From 7f958b2b43f7e418f87c9ff7f17508bcd8d0f1ea Mon Sep 17 00:00:00 2001 From: BulbyVR <26726264+TheDrawingCoder-Gamer@users.noreply.github.com> Date: Thu, 30 Nov 2023 13:29:39 -0500 Subject: [PATCH 01/95] i am trying, ok? --- linux/cc/ILayer.cc | 2 +- linux/cc/ILayer.hh | 2 +- linux/cc/ScreenInfo.cc | 2 +- script/build.py | 15 +- wayland/CMakeLists.txt | 43 + .../cc/AppX11.cc => wayland/cc/AppWayland.cc | 0 wayland/cc/AppWayland.hh | 35 + .../cc/ClipboardWayland.cc | 0 .../cc/KeyX11.cc => wayland/cc/KeyWayland.cc | 0 .../cc/KeyX11.hh => wayland/cc/KeyWayland.hh | 0 wayland/cc/LayerGLWayland.cc | 123 ++ .../cc/LayerRasterWayland.cc | 0 .../cc/MouseButtonWayland.cc | 0 .../cc/MouseButtonWayland.hh | 0 wayland/cc/WindowManagerWayland.cc | 330 +++ wayland/cc/WindowManagerWayland.hh | 96 + wayland/cc/WindowWayland.cc | 585 +++++ wayland/cc/WindowWayland.hh | 84 + wayland/cc/xdg-shell.c | 172 ++ wayland/cc/xdg-shell.h | 1884 +++++++++++++++++ wayland/java/WindowWayland.java | 235 ++ {linux => x11}/CMakeLists.txt | 4 +- x11/cc/AppX11.cc | 126 ++ {linux => x11}/cc/AppX11.hh | 0 x11/cc/ClipboardX11.cc | 125 ++ x11/cc/KeyX11.cc | 178 ++ x11/cc/KeyX11.hh | 14 + linux/cc/LayerGL.cc => x11/cc/LayerGLX11.cc | 0 x11/cc/LayerRasterX11.cc | 123 ++ x11/cc/MouseButtonX11.cc | 25 + x11/cc/MouseButtonX11.hh | 12 + {linux => x11}/cc/WindowManagerX11.cc | 0 {linux => x11}/cc/WindowManagerX11.hh | 0 {linux => x11}/cc/WindowX11.cc | 0 {linux => x11}/cc/WindowX11.hh | 0 {linux => x11}/cc/WindowX11MotifHints.hh | 0 {linux => x11}/java/WindowX11.java | 0 37 files changed, 4206 insertions(+), 9 deletions(-) create mode 100644 wayland/CMakeLists.txt rename linux/cc/AppX11.cc => wayland/cc/AppWayland.cc (100%) create mode 100644 wayland/cc/AppWayland.hh rename linux/cc/ClipboardX11.cc => wayland/cc/ClipboardWayland.cc (100%) rename linux/cc/KeyX11.cc => wayland/cc/KeyWayland.cc (100%) rename linux/cc/KeyX11.hh => wayland/cc/KeyWayland.hh (100%) create mode 100644 wayland/cc/LayerGLWayland.cc rename linux/cc/LayerRaster.cc => wayland/cc/LayerRasterWayland.cc (100%) rename linux/cc/MouseButtonX11.cc => wayland/cc/MouseButtonWayland.cc (100%) rename linux/cc/MouseButtonX11.hh => wayland/cc/MouseButtonWayland.hh (100%) create mode 100644 wayland/cc/WindowManagerWayland.cc create mode 100644 wayland/cc/WindowManagerWayland.hh create mode 100644 wayland/cc/WindowWayland.cc create mode 100644 wayland/cc/WindowWayland.hh create mode 100644 wayland/cc/xdg-shell.c create mode 100644 wayland/cc/xdg-shell.h create mode 100644 wayland/java/WindowWayland.java rename {linux => x11}/CMakeLists.txt (91%) create mode 100644 x11/cc/AppX11.cc rename {linux => x11}/cc/AppX11.hh (100%) create mode 100644 x11/cc/ClipboardX11.cc create mode 100644 x11/cc/KeyX11.cc create mode 100644 x11/cc/KeyX11.hh rename linux/cc/LayerGL.cc => x11/cc/LayerGLX11.cc (100%) create mode 100644 x11/cc/LayerRasterX11.cc create mode 100644 x11/cc/MouseButtonX11.cc create mode 100644 x11/cc/MouseButtonX11.hh rename {linux => x11}/cc/WindowManagerX11.cc (100%) rename {linux => x11}/cc/WindowManagerX11.hh (100%) rename {linux => x11}/cc/WindowX11.cc (100%) rename {linux => x11}/cc/WindowX11.hh (100%) rename {linux => x11}/cc/WindowX11MotifHints.hh (100%) rename {linux => x11}/java/WindowX11.java (100%) diff --git a/linux/cc/ILayer.cc b/linux/cc/ILayer.cc index 4f7d2d1e..6d4d4264 100644 --- a/linux/cc/ILayer.cc +++ b/linux/cc/ILayer.cc @@ -10,4 +10,4 @@ void jwm::ILayer::makeCurrent() { } void jwm::ILayer::makeCurrentForced() { _ourCurrentLayer = this; -} \ No newline at end of file +} diff --git a/linux/cc/ILayer.hh b/linux/cc/ILayer.hh index 5a22d209..81a32c2b 100644 --- a/linux/cc/ILayer.hh +++ b/linux/cc/ILayer.hh @@ -20,4 +20,4 @@ public: static ILayer* _ourCurrentLayer; }; -} // namespace jwm \ No newline at end of file +} // namespace jwm diff --git a/linux/cc/ScreenInfo.cc b/linux/cc/ScreenInfo.cc index ddcab4c6..f3aa1dee 100644 --- a/linux/cc/ScreenInfo.cc +++ b/linux/cc/ScreenInfo.cc @@ -4,4 +4,4 @@ jobject jwm::ScreenInfo::asJavaObject(JNIEnv* env) const { return jwm::classes::Screen::make(env, id, isPrimary, bounds, bounds, jwm::app.getScale()); -} \ No newline at end of file +} diff --git a/script/build.py b/script/build.py index 56e4cc3b..80882614 100755 --- a/script/build.py +++ b/script/build.py @@ -1,8 +1,8 @@ #! /usr/bin/env python3 import argparse, build_utils, common, glob, os, platform, subprocess, sys -def build_native(): - os.chdir(common.basedir + "/" + build_utils.system) +def build_native_system(system): + os.chdir(common.basedir + "/" + system) subprocess.check_call(["cmake", "-DCMAKE_BUILD_TYPE=Release", "-B", "build", @@ -24,10 +24,17 @@ def build_native(): build_utils.copy_newer('build/jwm_x64.dll', '../target/classes/jwm_x64.dll') return 0 - +def build_system(): + cur_system = build_utils.system; + if cur_system == "linux": + build_native_system("x11") + build_native_system("wayland") + else: + build_native_system(cur_system) + return 0 def build_java(): os.chdir(common.basedir) - sources = build_utils.files("linux/java/**/*.java", "macos/java/**/*.java", "shared/java/**/*.java", "windows/java/**/*.java",) + sources = build_utils.files("x11/java/**/*.java", "macos/java/**/*.java", "shared/java/**/*.java", "windows/java/**/*.java",) build_utils.javac(sources, "target/classes", classpath=common.deps_compile()) return 0 diff --git a/wayland/CMakeLists.txt b/wayland/CMakeLists.txt new file mode 100644 index 00000000..75249cdd --- /dev/null +++ b/wayland/CMakeLists.txt @@ -0,0 +1,43 @@ +cmake_minimum_required(VERSION 3.9) + +# prefer the newer GL library (GLVND) +cmake_policy(SET CMP0072 NEW) + +project(jwm LANGUAGES CXX) +set(CMAKE_CXX_STANDARD 14) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +if(NOT JWM_ARCH) + if ("${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "arm64") + set(JWM_ARCH "arm64") + else() + set(JWM_ARCH "x64") + endif() +endif() + +find_package(wayland-client REQUIRED) +find_package(decor REQUIRED) +find_package(wayland-cursor REQUIRED) +find_package(OpenGL REQUIRED) + +file(GLOB SOURCES_CXX ${CMAKE_CURRENT_LIST_DIR}/../shared/cc/*.cc ${CMAKE_CURRENT_LIST_DIR}/cc/*.cc) +file(GLOB SOURCES_CXX_IMPL ${CMAKE_CURRENT_LIST_DIR}/../shared/cc/impl/*.cc) +add_library(jwm SHARED ${SOURCES_OBJC} ${SOURCES_CXX} ${SOURCES_CXX_IMPL}) + +set(JAVA_HOME $ENV{JAVA_HOME}) +if (NOT JAVA_HOME) + file(GLOB JAVA_HOMES "/usr/lib/jvm/java-*") + if (JAVA_HOMES) + list(GET JAVA_HOMES 0 JAVA_HOME) + message(STATUS "Java home found automatically at ${JAVA_HOME}. Set JAVA_HOME environment variable to override.") + else() + message(FATAL_ERROR "Java home not found! Please set JAVA_HOME environment variable.") + endif() +endif() + +target_include_directories(jwm PRIVATE ${CMAKE_CURRENT_LIST_DIR}/../shared/cc ../shared/linux ${JAVA_HOME}/include ${JAVA_HOME}/include/linux) +set_target_properties(jwm PROPERTIES OUTPUT_NAME "jwm_${JWM_ARCH}") + + +target_link_libraries(jwm PRIVATE wayland-client::wayland-client, decor::decor, wayland-cursor::wayland-cursor) +target_link_libraries(jwm PRIVATE OpenGL::GL) diff --git a/linux/cc/AppX11.cc b/wayland/cc/AppWayland.cc similarity index 100% rename from linux/cc/AppX11.cc rename to wayland/cc/AppWayland.cc diff --git a/wayland/cc/AppWayland.hh b/wayland/cc/AppWayland.hh new file mode 100644 index 00000000..d6953aae --- /dev/null +++ b/wayland/cc/AppWayland.hh @@ -0,0 +1,35 @@ +#pragma once + +#include "WindowManagerX11.hh" +#include +#include "Types.hh" +#include +#include "impl/Library.hh" +#include "ScreenInfo.hh" + +namespace jwm { + extern class AppX11 { + public: + + void init(JNIEnv* jniEnv); + void start(); + void terminate(); + + WindowManagerX11& getWindowManager() { + return wm; + } + + JNIEnv* getJniEnv() { + return _jniEnv; + } + + const std::vector& getScreens(); + + float getScale(); + + JNIEnv* _jniEnv; + WindowManagerX11 wm; + std::vector _screens; + + } app; +} diff --git a/linux/cc/ClipboardX11.cc b/wayland/cc/ClipboardWayland.cc similarity index 100% rename from linux/cc/ClipboardX11.cc rename to wayland/cc/ClipboardWayland.cc diff --git a/linux/cc/KeyX11.cc b/wayland/cc/KeyWayland.cc similarity index 100% rename from linux/cc/KeyX11.cc rename to wayland/cc/KeyWayland.cc diff --git a/linux/cc/KeyX11.hh b/wayland/cc/KeyWayland.hh similarity index 100% rename from linux/cc/KeyX11.hh rename to wayland/cc/KeyWayland.hh diff --git a/wayland/cc/LayerGLWayland.cc b/wayland/cc/LayerGLWayland.cc new file mode 100644 index 00000000..aa78e4ee --- /dev/null +++ b/wayland/cc/LayerGLWayland.cc @@ -0,0 +1,123 @@ +#include +#include +#include +#include "impl/Library.hh" +#include "impl/RefCounted.hh" +#include "WindowX11.hh" +#include + +namespace jwm { + + class LayerGL: public RefCounted, public ILayer { + public: + WindowX11* fWindow; + GLXContext _context = nullptr; + using glXSwapIntervalEXT_t = void (*)(Display*, GLXDrawable, int); + glXSwapIntervalEXT_t _glXSwapIntervalEXT; + + LayerGL() = default; + virtual ~LayerGL() = default; + + void attach(WindowX11* window) { + if (window->_windowManager.getVisualInfo() == nullptr) { + throw std::runtime_error("layer not supported"); + } + + fWindow = jwm::ref(window); + fWindow->setLayer(this); + + if (_context == nullptr) { + _context = glXCreateContext(window->_windowManager.getDisplay(), + window->_windowManager.getVisualInfo(), + nullptr, + true); + + } + + makeCurrentForced(); + + _glXSwapIntervalEXT = reinterpret_cast(glXGetProcAddress(reinterpret_cast("glXSwapIntervalEXT"))); + setVsyncMode(VSYNC_ADAPTIVE); + } + + void setVsyncMode(VSync v) override { + + if (_glXSwapIntervalEXT) { + _glXSwapIntervalEXT(fWindow->_windowManager.getDisplay(), + fWindow->_x11Window, + v); + } + } + + void resize(int width, int height) { + glClearStencil(0); + glClearColor(0, 0, 0, 255); + glStencilMask(0xffffffff); + glClear(GL_STENCIL_BUFFER_BIT | GL_COLOR_BUFFER_BIT); + + glViewport(0, 0, width, height); + } + + void swapBuffers() { + glXSwapBuffers(fWindow->_windowManager.getDisplay(), fWindow->_x11Window); + } + + void close() override { + glXDestroyContext(fWindow->_windowManager.getDisplay(), _context); + jwm::unref(&fWindow); + } + + void makeCurrentForced() override { + ILayer::makeCurrentForced(); + glXMakeCurrent(fWindow->_windowManager.getDisplay(), + fWindow->_x11Window, + _context); + } + }; + +} // namespace jwm + +// JNI + +extern "C" JNIEXPORT jlong JNICALL Java_io_github_humbleui_jwm_LayerGL__1nMake + (JNIEnv* env, jclass jclass) { + jwm::LayerGL* instance = new jwm::LayerGL(); + return reinterpret_cast(instance); +} + +extern "C" JNIEXPORT void JNICALL Java_io_github_humbleui_jwm_LayerGL__1nAttach + (JNIEnv* env, jobject obj, jobject windowObj) { + try { + jwm::LayerGL* instance = reinterpret_cast(jwm::classes::Native::fromJava(env, obj)); + jwm::WindowX11* window = reinterpret_cast(jwm::classes::Native::fromJava(env, windowObj)); + instance->attach(window); + } catch (const std::exception& e) { + jwm::classes::Throwable::throwLayerNotSupportedException(env, "Failed to init OpenGL"); + } +} + +extern "C" JNIEXPORT void JNICALL Java_io_github_humbleui_jwm_LayerGL__1nReconfigure + (JNIEnv* env, jobject obj, jint width, jint height) { +} + +extern "C" JNIEXPORT void JNICALL Java_io_github_humbleui_jwm_LayerGL__1nResize + (JNIEnv* env, jobject obj, jint width, jint height) { + jwm::LayerGL* instance = reinterpret_cast(jwm::classes::Native::fromJava(env, obj)); + instance->resize(width, height); +} + +extern "C" JNIEXPORT void JNICALL Java_io_github_humbleui_jwm_LayerGL__1nMakeCurrent + (JNIEnv* env, jobject obj) { +} + +extern "C" JNIEXPORT void JNICALL Java_io_github_humbleui_jwm_LayerGL__1nSwapBuffers + (JNIEnv* env, jobject obj) { + jwm::LayerGL* instance = reinterpret_cast(jwm::classes::Native::fromJava(env, obj)); + instance->swapBuffers(); +} + +extern "C" JNIEXPORT void JNICALL Java_io_github_humbleui_jwm_LayerGL__1nClose + (JNIEnv* env, jobject obj) { + jwm::LayerGL* instance = reinterpret_cast(jwm::classes::Native::fromJava(env, obj)); + instance->close(); +} diff --git a/linux/cc/LayerRaster.cc b/wayland/cc/LayerRasterWayland.cc similarity index 100% rename from linux/cc/LayerRaster.cc rename to wayland/cc/LayerRasterWayland.cc diff --git a/linux/cc/MouseButtonX11.cc b/wayland/cc/MouseButtonWayland.cc similarity index 100% rename from linux/cc/MouseButtonX11.cc rename to wayland/cc/MouseButtonWayland.cc diff --git a/linux/cc/MouseButtonX11.hh b/wayland/cc/MouseButtonWayland.hh similarity index 100% rename from linux/cc/MouseButtonX11.hh rename to wayland/cc/MouseButtonWayland.hh diff --git a/wayland/cc/WindowManagerWayland.cc b/wayland/cc/WindowManagerWayland.cc new file mode 100644 index 00000000..e8f799d3 --- /dev/null +++ b/wayland/cc/WindowManagerWayland.cc @@ -0,0 +1,330 @@ +#include "WindowManagerWayland.hh" +#include "WindowWayland.hh" +#include +#include +#include +#include +#include "AppWayland.hh" +#include +#include +#include +#include "KeyWayland.hh" +#include "MouseButtonWayland.hh" +#include "StringUTF16.hh" +#include +#include +#include "Log.hh" + +using namespace jwm; + + +WindowManagerWayland::WindowManagerWayland(): + display(wl_display_connect(nullptr)) { + registry = wl_display_get_registry(display); + wl_registry_listener registry_listener = { + .global = registryGlobalHandler, + .global_remove = registryGlobalHandlerRemove + }; + wl_registry_add_listener(registry, ®istry_listener, nullptr); + + wl_display_roundtrip(display); + + if (!(shm && xdgShell && compositor && deviceManager && seat)) { + // ??? + // Bad. Means our compositor no supportie : ( + throw std::system_error(1, std::system_category); + } + + { + wl_cursor_theme* cursor_theme = wl_cursor_theme_load(nullptr, 24, shm); + // TODO: what about if missing : ( + auto loadCursor = [&](const char* name) { + wl_cursor* cursor = wl_cursor_theme_get_cursor(cursor_theme, name); + wl_cursor_image* cursorImage = cursor->images[0]; + return wl_cursor_image_get_buffer(cursorImage); + } + + _cursors[static_cast(jwm::MouseCursor::ARROW )] = loadCursor("default"); + _cursors[static_cast(jwm::MouseCursor::CROSSHAIR )] = loadCursor("crosshair"); + _cursors[static_cast(jwm::MouseCursor::HELP )] = loadCursor("help"); + _cursors[static_cast(jwm::MouseCursor::POINTING_HAND )] = loadCursor("pointer"); + _cursors[static_cast(jwm::MouseCursor::IBEAM )] = loadCursor("text"); + _cursors[static_cast(jwm::MouseCursor::NOT_ALLOWED )] = loadCursor("not-allowed"); + _cursors[static_cast(jwm::MouseCursor::WAIT )] = loadCursor("watch"); + _cursors[static_cast(jwm::MouseCursor::RESIZE_NS )] = loadCursor("ns-resize"); + _cursors[static_cast(jwm::MouseCursor::RESIZE_WE )] = loadCursor("ew-resize"); + _cursors[static_cast(jwm::MouseCursor::RESIZE_NESW )] = loadCursor("nesw-resize"); + _cursors[static_cast(jwm::MouseCursor::RESIZE_NWSE )] = loadCursor("nwse-resize"); + } + +} + + + + +void WindowManagerWayland::runLoop() { + _runLoop = true; + XEvent ev; + + // buffer to read into; really only needs to be two characters long due to the notifyBool fast path, but longer doesn't hurt + char buf[100]; + // initialize a pipe to write to whenever this loop needs to process something new + int pipes[2]; + if (pipe(pipes)) { + printf("Failed to open pipe\n"); + return; + } + + notifyFD = pipes[1]; + fcntl(pipes[1], F_SETFL, O_NONBLOCK); // make sure notifyLoop doesn't block + // two polled items - the X11 event queue, and our event queue + struct pollfd ps[] = {{.fd=XConnectionNumber(display), .events=POLLIN}, {.fd=pipes[0], .events=POLLIN}}; + + while (_runLoop) { + while (XPending(display)) { + XNextEvent(display, &ev); + _processXEvent(ev); + if (jwm::classes::Throwable::exceptionThrown(app.getJniEnv())) + _runLoop = false; + } + _processCallbacks(); + + // block until the next X11 or our event + if (poll(&ps[0], 2, -1) < 0) { + printf("Error during poll\n"); + break; + } + + // clear pipe + if (ps[1].revents & POLLIN) { + while (read(pipes[0], buf, sizeof(buf)) == sizeof(buf)) { } + } + // clear fast path boolean; done after clearing the pipe so that, during event execution, new notifyLoop calls can still function + notifyBool.store(false); + // The events causing a notifyLoop anywhere between poll() end and here will be processed in all cases, as that's the next thing that happens + } + + notifyFD = -1; + close(pipes[0]); + close(pipes[1]); +} + +void WindowManagerWayland::notifyLoop() { + if (notifyFD==-1) return; + // fast notifyBool path to not make system calls when not necessary + if (!notifyBool.exchange(true)) { + char dummy[1] = {0}; + int unused = write(notifyFD, dummy, 1); // this really shouldn't fail, but if it does, the pipe should either be full (good), or dead (bad, but not our business) + } +} + +void WindowManagerWayland::_processCallbacks() { + { + // process ui thread callbacks + std::unique_lock lock(_taskQueueLock); + + while (!_taskQueue.empty()) { + auto callback = std::move(_taskQueue.front()); + _taskQueue.pop(); + lock.unlock(); + callback(); + lock.lock(); + } + } + { + // copy window list in case one closes any other, invalidating some iterator in _nativeWindowToMy + std::vector copy; + for (auto& p : _nativeWindowToMy) { + copy.push_back(p.second); + } + // process redraw requests + for (auto p : copy) { + if (p->isRedrawRequested()) { + p->unsetRedrawRequest(); + if (p->_layer) { + p->_layer->makeCurrent(); + } + p->dispatch(classes::EventFrame::kInstance); + } + } + } +} + +void WindowManagerWayland::registryHandleGlobal(void* data, wl_registry *registry, + uint32_t name, const char* interface, uint32_t version) { + if (strcmp(interface, "wl_compositor") == 0) { + compositor = wl_registry_bind(registry, name, + &wl_compositor_interface, 3); + } else if (strcmp(interface, "wl_shm") == 0) { + shm = wl_registry_bind(registry, name, + &wl_shm_interface, 1); + } else if (strcmp(interface, "zxdg_shell_v6") == 0) { + xdgShell = wl_registry_bind(registry, name, + &zxdg_shell_v6_interface, 1); + } else if (strcmp(interface, "wl_data_device_manager") == 0) { + deviceManager = wl_registry_bind(registry, name, + &wl_data_device_manager_interface, 1); + } else if (strcmp(interface, "wl_seat") == 0) { + seat = wl_registry_bind(registry, name, + &wl_seat_interface, 1); + } +} +void WindowManagerWayland::registryHandleGlobalRemove(void* data, wl_registry *registry, uint32_t name) { + // i do nothing : ) +} +std::vector WindowManagerWayland::getClipboardFormats() { + auto owner = XGetSelectionOwner(display, _atoms.CLIPBOARD); + if (owner == None) + { + return {}; + } + + assert(("create at least one window in order to use clipboard" && !_nativeWindowToMy.empty())); + + auto nativeHandle = _nativeWindowToMy.begin()->first; + assert(nativeHandle); + + XConvertSelection(display, + _atoms.CLIPBOARD, + _atoms.TARGETS, + _atoms.JWM_CLIPBOARD, + nativeHandle, + CurrentTime); + + XEvent ev; + + // fetch mime types + std::vector result; + + // using lambda here in order to break 2 loops + [&]{ + while (_runLoop) { + while (XPending(display)) { + XNextEvent(display, &ev); + if (ev.type == SelectionNotify) { + int format; + unsigned long count, lengthInBytes; + Atom type; + Atom* properties; + XGetWindowProperty(display, nativeHandle, _atoms.JWM_CLIPBOARD, 0, 1024 * sizeof(Atom), false, XA_ATOM, &type, &format, &count, &lengthInBytes, reinterpret_cast(&properties)); + + for (unsigned long i = 0; i < count; ++i) { + char* str = XGetAtomName(display, properties[i]); + if (str) { + std::string s = str; + // include only mime types + if (s.find('/') != std::string::npos) { + result.push_back(s); + } else if (s == "UTF8_STRING") { + // HACK: treat UTF8_STRING as text/plain under the hood + // avoid duplicates + std::string textPlain = "text/plain"; + if (std::find(result.begin(), result.end(), textPlain) != result.end()) { + result.push_back(textPlain); + } + } + XFree(str); + } + } + + XFree(properties); + return; + } else { + _processXEvent(ev); + } + + } + _processCallbacks(); + } + }(); + + // fetching data + + XDeleteProperty(display, nativeHandle, _atoms.JWM_CLIPBOARD); + return result; +} + +jwm::ByteBuf WindowManagerWayland::getClipboardContents(const std::string& type) { + auto nativeHandle = _nativeWindowToMy.begin()->first; + + XConvertSelection(display, + _atoms.CLIPBOARD, + XInternAtom(display, type.c_str(), false), + _atoms.JWM_CLIPBOARD, + nativeHandle, + CurrentTime); + XEvent ev; + while (_runLoop) { + while (XPending(display)) { + XNextEvent(display, &ev); + switch (ev.type) + { + case SelectionNotify: { + if (ev.xselection.property == None) { + return {}; + } + + Atom da, incr, type; + int di; + unsigned long size, length, count; + unsigned char* propRet = NULL; + + XGetWindowProperty(display, nativeHandle, _atoms.JWM_CLIPBOARD, 0, 0, False, AnyPropertyType, + &type, &di, &length, &size, &propRet); + XFree(propRet); + + // Clipboard data is too large and INCR mechanism not implemented + ByteBuf result; + if (type != _atoms.INCR) + { + XGetWindowProperty(display, nativeHandle, _atoms.JWM_CLIPBOARD, 0, size, False, AnyPropertyType, + &da, &di, &length, &count, &propRet); + + result = ByteBuf{ propRet, propRet + length }; + XFree(propRet); + return result; + } + XDeleteProperty(display, nativeHandle, _atoms.JWM_CLIPBOARD); + return result; + } + default: + _processXEvent(ev); + } + } + _processCallbacks(); + } + + XDeleteProperty(display, nativeHandle, _atoms.JWM_CLIPBOARD); + return {}; +} + +void WindowManagerWayland::registerWindow(WindowWayland* window) { + _nativeWindowToMy[window->_waylandWindow] = window; +} + +void WindowManagerWayland::unregisterWindow(WindowX11* window) { + auto it = _nativeWindowToMy.find(window->_waylandWindow); + if (it != _nativeWindowToMy.end()) { + _nativeWindowToMy.erase(it); + } +} + +void WindowManagerWayland::terminate() { + _runLoop = false; + notifyLoop(); +} + +void WindowManagerWayland::setClipboardContents(std::map&& c) { + assert(("create at least one window in order to use clipboard" && !_nativeWindowToMy.empty())); + _myClipboardContents = c; + ::Window window = _nativeWindowToMy.begin()->first; + XSetSelectionOwner(display, XA_PRIMARY, window, CurrentTime); + XSetSelectionOwner(display, _atoms.CLIPBOARD, window, CurrentTime); +} + +void WindowManagerWayland::enqueueTask(const std::function& task) { + std::unique_lock lock(_taskQueueLock); + _taskQueue.push(task); + _taskQueueNotify.notify_one(); + notifyLoop(); +} diff --git a/wayland/cc/WindowManagerWayland.hh b/wayland/cc/WindowManagerWayland.hh new file mode 100644 index 00000000..a5c30afc --- /dev/null +++ b/wayland/cc/WindowManagerWayland.hh @@ -0,0 +1,96 @@ +#pragma once + +#include +#include +#include +#include +#include +#include "Types.hh" +#include +#include +#include +#include +#include "MouseCursor.hh" +#include +#include +#include "xdg_shell.h" + +namespace jwm { + class WindowWayland; + class WindowManagerWayland { + public: + WindowManagerWayland(); + + void runLoop(); + void terminate(); + + void registerWindow(WindowWayland* window); + void unregisterWindow(WindowWayland* window); + + // XVisualInfo* pickVisual(); + // static int _xerrorhandler(Display* dsp, XErrorEvent* error); + // void _xi2IterateDevices(); + + // XVisualInfo* getVisualInfo() const { return x11VisualInfo; } + // XSetWindowAttributes& getSWA() { return x11SWA; } + // XIM getIM() const { return _im; } + /* + int getX11VisualDepth() const { + if (x11VisualInfo) { + return x11VisualInfo->depth; + } + return DefaultDepth(display, 0); + } + Visual* getX11Visual() const { + if (x11VisualInfo) { + return x11VisualInfo->visual; + } + return DefaultVisual(display, 0); + } + */ + void enqueueTask(const std::function& task); + + void registryHandleGlobal(void* data, wl_registry *registry, + uint32_t name, const char* interface, uint32_t version); + void registryHandleGlobalRemove(void* data, wl_registry *registry, + uint32_t name); + + + + ByteBuf getClipboardContents(const std::string& type); + std::vector getClipboardFormats(); + + wl_display* display = nullptr + wl_registry* registry = nullptr + wl_shm* shm = nullptr; + zxdg_shell_v6* xdgShell = nullptr; + wl_compositor* compositor = nullptr; + wl_data_device_manager* deviceManager = nullptr; + wl_seat* seat = nullptr; + + // XVisualInfo* x11VisualInfo; + // XSetWindowAttributes x11SWA; + bool _runLoop; + int notifyFD = -1; + std::atomic_bool notifyBool{false}; + int lastMousePosX = 0; + int lastMousePosY = 0; + void mouseUpdate(WindowWayland* myWindow); + + std::map<::Window, WindowWayland*> _nativeWindowToMy; + std::map _myClipboardContents; + + + wl_surface* cursorSurface; + // Is holding all cursors in memory a good idea? + wl_buffer _cursors[static_cast(jwm::MouseCursor::COUNT)]; + + + std::mutex _taskQueueLock; + std::condition_variable _taskQueueNotify; + std::queue> _taskQueue; + + + void setClipboardContents(std::map&& c); + }; +} diff --git a/wayland/cc/WindowWayland.cc b/wayland/cc/WindowWayland.cc new file mode 100644 index 00000000..4d1c525f --- /dev/null +++ b/wayland/cc/WindowWayland.cc @@ -0,0 +1,585 @@ +#include "WindowWayland.hh" +#include +#include +#include +#include "AppWayland.hh" +#include "impl/Library.hh" +#include "impl/JNILocal.hh" +#include + +using namespace jwm; + + +WindowWayland::WindowWayland(JNIEnv* env, WindowManagerWayland& windowManager): + jwm::Window(env), + _windowManager(windowManager) +{ +} + +WindowWayland::~WindowWayland() { + close(); +} + +void WindowWayland::setTitle(const std::string& title) { + XChangeProperty(_windowManager.getDisplay(), + _x11Window, + _windowManager.getAtoms()._NET_WM_NAME, + _windowManager.getAtoms().UTF8_STRING, + 8, + PropModeReplace, + reinterpret_cast(title.c_str()), + title.length()); +} + +void WindowX11::setTitlebarVisible(bool isVisible) { + MotifHints motifHints = {0}; + + motifHints.flags = MOTIF_HINTS_DECORATIONS; + motifHints.decorations = int(isVisible); + + XChangeProperty(_windowManager.getDisplay(), + _x11Window, + _windowManager.getAtoms()._MOTIF_WM_HINTS, + _windowManager.getAtoms()._MOTIF_WM_HINTS, + 32, + PropModeReplace, + (unsigned char*) &motifHints, + 5); +} + +void WindowX11::close() { + if (_x11Window) { + _windowManager.unregisterWindow(this); + XDestroyWindow(_windowManager.display, _x11Window); + _x11Window = 0; + } +} +void WindowX11::_xSendEventToWM(Atom atom, long a, long b, long c, long d, long e) const { + XEvent event = { 0 }; + event.type = ClientMessage; + event.xclient.window = _x11Window; + event.xclient.format = 32; // data is 32-bit longs + event.xclient.message_type = atom; + event.xclient.data.l[0] = a; + event.xclient.data.l[1] = b; + event.xclient.data.l[2] = c; + event.xclient.data.l[3] = d; + event.xclient.data.l[4] = e; + + XSendEvent(_windowManager.display, + DefaultRootWindow(_windowManager.display), + False, + SubstructureNotifyMask | SubstructureRedirectMask, + &event); +} +unsigned long WindowX11::_xGetWindowProperty(Atom property, Atom type, unsigned char** value) const { + Atom actualType; + int actualFormat; + unsigned long itemCount, bytesAfter; + + XGetWindowProperty(_windowManager.display, + _x11Window, + property, + 0, + std::numeric_limits::max(), + false, + type, + &actualType, + &actualFormat, + &itemCount, + &bytesAfter, + value); + + return itemCount; +} + +void WindowX11::maximize() { + XWindowAttributes wa; + XGetWindowAttributes(_windowManager.display, _x11Window, &wa); + + if (wa.map_state == IsViewable) { + _xSendEventToWM(_windowManager._atoms._NET_WM_STATE, + 1, + _windowManager._atoms._NET_WM_STATE_MAXIMIZED_HORZ, + _windowManager._atoms._NET_WM_STATE_MAXIMIZED_VERT, + 0, + 0); + } else { + Atom* states = nullptr; + unsigned long count = _xGetWindowProperty(_windowManager._atoms._NET_WM_STATE, + XA_ATOM, + reinterpret_cast(&states)); + + + Atom missing[2] = { + _windowManager._atoms._NET_WM_STATE_MAXIMIZED_VERT, + _windowManager._atoms._NET_WM_STATE_MAXIMIZED_HORZ + }; + unsigned long missingCount = 2; + + for (unsigned long i = 0; i < count; i++) + { + for (unsigned long j = 0; j < missingCount; j++) + { + if (states[i] == missing[j]) + { + missing[j] = missing[missingCount - 1]; + missingCount--; + } + } + } + + if (states) + XFree(states); + + if (!missingCount) + return; + + XChangeProperty(_windowManager.display, + _x11Window, + _windowManager._atoms._NET_WM_STATE, + XA_ATOM, + 32, + PropModeAppend, + (unsigned char*) missing, + missingCount); + } + XFlush(_windowManager.display); +} + +void WindowX11::minimize() { + XIconifyWindow(_windowManager.display, _x11Window, 0); +} + +void WindowX11::restore() { + if (_windowManager._atoms._NET_WM_STATE && + _windowManager._atoms._NET_WM_STATE_MAXIMIZED_VERT && + _windowManager._atoms._NET_WM_STATE_MAXIMIZED_HORZ) { + _xSendEventToWM(_windowManager._atoms._NET_WM_STATE, + 0, + _windowManager._atoms._NET_WM_STATE_MAXIMIZED_VERT, + _windowManager._atoms._NET_WM_STATE_MAXIMIZED_HORZ, + 1, + 0); + } +} + +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(&data)); + if (data!=nullptr) { + left = data[0]; + top = data[2]; + right = data[1]; + bottom = data[3]; + XFree(data); + } else { + XWindowAttributes xwa; + XGetWindowAttributes(_windowManager.display, _x11Window, &xwa); + left = xwa.x; + top = xwa.y; + right = 0; + bottom = 0; + } +} + +void WindowX11::getContentPosition(int& posX, int& posY) { + int x, y; + ::Window child; + XTranslateCoordinates(_windowManager.display, + _x11Window, + XRootWindow(_windowManager.display, 0), + 0, 0, + &x, &y, + &child); + posX = x; + posY = y; + +} + +int WindowX11::getLeft() { + int x, y; + getContentPosition(x, y); + return x; +} + +int WindowX11::getTop() { + int x, y; + getContentPosition(x, y); + return y; +} + +int WindowX11::getWidth() { + return _width; +} + +int WindowX11::getHeight() { + return _height; +} + +float WindowX11::getScale() { + return jwm::app.getScale(); +} + +bool WindowX11::init() +{ + _x11Window = XCreateWindow(_windowManager.getDisplay(), + _windowManager.getScreen()->root, + 0, 0, + 800, 500, + 0, + _windowManager.getX11VisualDepth(), + InputOutput, + _windowManager.getX11Visual(), + CWColormap | CWEventMask | CWCursor, + &_windowManager.getSWA() + ); + + // tell X11 we want to handle close button + XSetWMProtocols(_windowManager.getDisplay(), + _x11Window, + &_windowManager.getAtoms().WM_DELETE_WINDOW, + WindowManagerX11::Atoms::PROTOCOL_COUNT); + + // IC + { + _ic = XCreateIC(_windowManager.getIM(), + XNInputStyle, + XIMPreeditNothing | XIMStatusNothing, + XNClientWindow, + _x11Window, + nullptr); + + XSetICFocus(_ic); + } + + + + // XSync + { + XSyncValue value; + XSyncIntToValue(&value, 0); + _xsyncRequestCounter.counter = XSyncCreateCounter(_windowManager.getDisplay(), value); + XChangeProperty(_windowManager.getDisplay(), + _x11Window, + _windowManager.getAtoms()._NET_WM_SYNC_REQUEST_COUNTER, + XA_CARDINAL, + 32, + PropModeReplace, + (const unsigned char*)&_xsyncRequestCounter.counter, 1); + + } + _windowManager.registerWindow(this); + return true; +} + +void WindowX11::move(int left, int top) { + _posX = left; + _posY = top; + if (_visible) + XMoveWindow(_windowManager.display, _x11Window, left, top); +} + +void WindowX11::resize(int width, int height) { + _width = width; + _height = height; + if (_visible) { + XResizeWindow(_windowManager.display, _x11Window, width, height); + jwm::JNILocal eventWindowResize(app.getJniEnv(), classes::EventWindowResize::make(app.getJniEnv(), width, height, width, height)); + dispatch(eventWindowResize.get()); + } +} + +void WindowX11::setVisible(bool isVisible) { + if (_visible != isVisible) { + _visible = isVisible; + if (_visible) { + XMapWindow(_windowManager.getDisplay(), _x11Window); + if (_posX > 0 && _posY > 0) + move(_posX, _posY); + if (_width > 0 && _height > 0) + resize(_width, _height); + } else { + XUnmapWindow(_windowManager.getDisplay(), _x11Window); + } + } +} + +const ScreenInfo& WindowX11::getScreen() { + // in X11, there's no straightforward way to get screen of window. + // instead, we should do it manually using center point of the window and calculating which monitor this point + // belongs to. + + int centerX = getLeft() + getWidth() / 2; + int centerY = getTop() + getHeight() / 2; + for (auto& screen : jwm::app.getScreens()) { + if (screen.bounds.isPointInside(centerX, centerY)) { + return screen; + } + } + return *jwm::app.getScreens().begin(); +} + +void jwm::WindowX11::setCursor(jwm::MouseCursor cursor) { + if (auto x11Cursor = _windowManager._cursors[static_cast(cursor)]) { + XDefineCursor(_windowManager.display, _x11Window, x11Cursor); + } else { + XUndefineCursor(_windowManager.display, _x11Window); + } +} + +// JNI + +extern "C" JNIEXPORT jlong JNICALL Java_io_github_humbleui_jwm_WindowX11__1nMake + (JNIEnv* env, jclass jclass) { + std::unique_ptr instance = std::make_unique(env, jwm::app.getWindowManager()); + if (instance->init()) { + return reinterpret_cast(instance.release()); + } + return 0; +} + +extern "C" JNIEXPORT void JNICALL Java_io_github_humbleui_jwm_WindowX11__1nSetVisible + (JNIEnv* env, jobject obj, jboolean isVisible) { + + reinterpret_cast(jwm::classes::Native::fromJava(env, obj))->setVisible(isVisible); +} + +extern "C" JNIEXPORT jobject JNICALL Java_io_github_humbleui_jwm_WindowX11__1nGetWindowRect + (JNIEnv* env, jobject obj) { + jwm::WindowX11* instance = reinterpret_cast(jwm::classes::Native::fromJava(env, obj)); + int left, top, right, bottom; + instance->getDecorations(left, top, right, bottom); + int x, y; + instance->getContentPosition(x, y); + return jwm::classes::IRect::toJavaXYWH( + env, + x-left, + y-top, + instance->getWidth()+left+right, + instance->getHeight()+top+bottom + ); +} + +extern "C" JNIEXPORT jobject JNICALL Java_io_github_humbleui_jwm_WindowX11__1nGetContentRect + (JNIEnv* env, jobject obj) { + jwm::WindowX11* instance = reinterpret_cast(jwm::classes::Native::fromJava(env, obj)); + int left, top, right, bottom; + instance->getDecorations(left, top, right, bottom); + return jwm::classes::IRect::toJavaXYWH( + env, + left, + top, + instance->getWidth(), + instance->getHeight() + ); +} + +extern "C" JNIEXPORT void JNICALL Java_io_github_humbleui_jwm_WindowX11__1nSetWindowPosition + (JNIEnv* env, jobject obj, int left, int top) { + jwm::WindowX11* instance = reinterpret_cast(jwm::classes::Native::fromJava(env, obj)); + instance->move(left, top); +} + +extern "C" JNIEXPORT void JNICALL Java_io_github_humbleui_jwm_WindowX11__1nSetWindowSize + (JNIEnv* env, jobject obj, int width, int height) { + jwm::WindowX11* instance = reinterpret_cast(jwm::classes::Native::fromJava(env, obj)); + // TODO https://github.com/HumbleUI/JWM/issues/109 + instance->resize(width, height); +} + +extern "C" JNIEXPORT void JNICALL Java_io_github_humbleui_jwm_WindowX11__1nSetContentSize + (JNIEnv* env, jobject obj, int width, int height) { + jwm::WindowX11* instance = reinterpret_cast(jwm::classes::Native::fromJava(env, obj)); + instance->resize(width, height); +} + +extern "C" JNIEXPORT jobject JNICALL Java_io_github_humbleui_jwm_WindowX11__1nGetScreen + (JNIEnv* env, jobject obj) { + jwm::WindowX11* instance = reinterpret_cast(jwm::classes::Native::fromJava(env, obj)); + return instance->getScreen().asJavaObject(env); +} + +extern "C" JNIEXPORT void JNICALL Java_io_github_humbleui_jwm_WindowX11__1nRequestFrame + (JNIEnv* env, jobject obj) { + jwm::WindowX11* instance = reinterpret_cast(jwm::classes::Native::fromJava(env, obj)); + instance->requestRedraw(); +} + +extern "C" JNIEXPORT void JNICALL Java_io_github_humbleui_jwm_WindowX11__1nMaximize + (JNIEnv* env, jobject obj) { + reinterpret_cast(jwm::classes::Native::fromJava(env, obj))->maximize(); +} + + +extern "C" JNIEXPORT void JNICALL Java_io_github_humbleui_jwm_WindowX11__1nMinimize + (JNIEnv* env, jobject obj) { + reinterpret_cast(jwm::classes::Native::fromJava(env, obj))->minimize(); +} + + +extern "C" JNIEXPORT void JNICALL Java_io_github_humbleui_jwm_WindowX11__1nRestore + (JNIEnv* env, jobject obj) { + reinterpret_cast(jwm::classes::Native::fromJava(env, obj))->restore(); +} + + +extern "C" JNIEXPORT void JNICALL Java_io_github_humbleui_jwm_WindowX11__1nClose + (JNIEnv* env, jobject obj) { + jwm::WindowX11* instance = reinterpret_cast(jwm::classes::Native::fromJava(env, obj)); + instance->close(); +} +extern "C" JNIEXPORT void JNICALL Java_io_github_humbleui_jwm_WindowX11__1nSetTitle + (JNIEnv* env, jobject obj, jbyteArray title) { + jwm::WindowX11* instance = reinterpret_cast(jwm::classes::Native::fromJava(env, obj)); + + jbyte* bytes = env->GetByteArrayElements(title, nullptr); + std::string titleS = { bytes, bytes + env->GetArrayLength(title) }; + env->ReleaseByteArrayElements(title, bytes, 0); + + instance->setTitle(titleS); +} + +extern "C" JNIEXPORT void JNICALL Java_io_github_humbleui_jwm_WindowX11__1nSetTitlebarVisible + (JNIEnv* env, jobject obj, jboolean isVisible) { + jwm::WindowX11* instance = reinterpret_cast(jwm::classes::Native::fromJava(env, obj)); + instance->setTitlebarVisible(isVisible); +} + +extern "C" JNIEXPORT void JNICALL Java_io_github_humbleui_jwm_WindowX11__1nSetMouseCursor + (JNIEnv* env, jobject obj, jint idx) { + jwm::WindowX11* instance = reinterpret_cast(jwm::classes::Native::fromJava(env, obj)); + + instance->setCursor(static_cast(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::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::classes::Native::fromJava(env, obj)); + return instance->isFullScreen(); +} diff --git a/wayland/cc/WindowWayland.hh b/wayland/cc/WindowWayland.hh new file mode 100644 index 00000000..e47e27a0 --- /dev/null +++ b/wayland/cc/WindowWayland.hh @@ -0,0 +1,84 @@ +#pragma once + +#include +#include +#include "Window.hh" +#include "WindowManagerWayland.hh" +#include "ILayer.hh" +#include "ScreenInfo.hh" + +namespace jwm { + class WindowWayland: public jwm::Window { + public: + WindowWayland(JNIEnv* env, WindowManagerWayland& windowManager); + ~WindowWayland() override; + + void getDecorations(int& left, int& top, int& right, int& bottom); + void getContentPosition(int& posX, int& posY); + void setVisible(bool isVisible); + void close(); + bool init(); + int getLeft(); + int getTop(); + int getWidth(); + int getHeight(); + float getScale(); + void move(int left, int top); + void resize(int width, int height); + void requestRedraw() { + _isRedrawRequested = true; + _windowManager.notifyLoop(); + } + void unsetRedrawRequest() { + _isRedrawRequested = false; + } + bool isRedrawRequested() { + return _isRedrawRequested; + } + void setTitle(const std::string& title); + void setTitlebarVisible(bool isVisible); + + void maximize(); + void minimize(); + void restore(); + + void setFullScreen(bool isFullScreen); + bool isFullScreen(); + + XIC getIC() const { + return _ic; + } + void setCursor(jwm::MouseCursor cursor); + void setLayer(ILayer* layer) { + _layer = layer; + } + void _xSendEventToWM(Atom atom, long a, long b, long c, long d, long e) const; + unsigned long _xGetWindowProperty(Atom property, Atom type, unsigned char** value) const; + + const ScreenInfo& getScreen(); + + /** + * _NET_WM_SYNC_REQUEST (resize flicker fix) update request counter + */ + struct { + uint32_t lo = 0; + uint32_t hi = 0; + XID counter; + } _xsyncRequestCounter; + + int _posX = -1; + int _posY = -1; + int _width = -1; + int _height = -1; + int _WM_ADD = 1L; + int _WM_REMOVE = 0L; + bool _visible = false; + + bool _isRedrawRequested = false; + + WindowManagerWayland& _windowManager; + ILayer* _layer = nullptr; + ::Window _waylandWindow = 0; + XIC _ic; + }; +} diff --git a/wayland/cc/xdg-shell.c b/wayland/cc/xdg-shell.c new file mode 100644 index 00000000..68ea631f --- /dev/null +++ b/wayland/cc/xdg-shell.c @@ -0,0 +1,172 @@ +/* Generated by wayland-scanner 1.22.0 */ + +/* + * Copyright © 2008-2013 Kristian Høgsberg + * Copyright © 2013 Rafael Antognolli + * Copyright © 2013 Jasper St. Pierre + * Copyright © 2010-2013 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include +#include +#include "wayland-util.h" + +#ifndef __has_attribute +# define __has_attribute(x) 0 /* Compatibility with non-clang compilers. */ +#endif + +#if (__has_attribute(visibility) || defined(__GNUC__) && __GNUC__ >= 4) +#define WL_PRIVATE __attribute__ ((visibility("hidden"))) +#else +#define WL_PRIVATE +#endif + +extern const struct wl_interface wl_output_interface; +extern const struct wl_interface wl_seat_interface; +extern const struct wl_interface wl_surface_interface; +extern const struct wl_interface zxdg_popup_v6_interface; +extern const struct wl_interface zxdg_positioner_v6_interface; +extern const struct wl_interface zxdg_surface_v6_interface; +extern const struct wl_interface zxdg_toplevel_v6_interface; + +static const struct wl_interface *xdg_shell_unstable_v6_types[] = { + NULL, + NULL, + NULL, + NULL, + &zxdg_positioner_v6_interface, + &zxdg_surface_v6_interface, + &wl_surface_interface, + &zxdg_toplevel_v6_interface, + &zxdg_popup_v6_interface, + &zxdg_surface_v6_interface, + &zxdg_positioner_v6_interface, + &zxdg_toplevel_v6_interface, + &wl_seat_interface, + NULL, + NULL, + NULL, + &wl_seat_interface, + NULL, + &wl_seat_interface, + NULL, + NULL, + &wl_output_interface, + &wl_seat_interface, + NULL, +}; + +static const struct wl_message zxdg_shell_v6_requests[] = { + { "destroy", "", xdg_shell_unstable_v6_types + 0 }, + { "create_positioner", "n", xdg_shell_unstable_v6_types + 4 }, + { "get_xdg_surface", "no", xdg_shell_unstable_v6_types + 5 }, + { "pong", "u", xdg_shell_unstable_v6_types + 0 }, +}; + +static const struct wl_message zxdg_shell_v6_events[] = { + { "ping", "u", xdg_shell_unstable_v6_types + 0 }, +}; + +WL_PRIVATE const struct wl_interface zxdg_shell_v6_interface = { + "zxdg_shell_v6", 1, + 4, zxdg_shell_v6_requests, + 1, zxdg_shell_v6_events, +}; + +static const struct wl_message zxdg_positioner_v6_requests[] = { + { "destroy", "", xdg_shell_unstable_v6_types + 0 }, + { "set_size", "ii", xdg_shell_unstable_v6_types + 0 }, + { "set_anchor_rect", "iiii", xdg_shell_unstable_v6_types + 0 }, + { "set_anchor", "u", xdg_shell_unstable_v6_types + 0 }, + { "set_gravity", "u", xdg_shell_unstable_v6_types + 0 }, + { "set_constraint_adjustment", "u", xdg_shell_unstable_v6_types + 0 }, + { "set_offset", "ii", xdg_shell_unstable_v6_types + 0 }, +}; + +WL_PRIVATE const struct wl_interface zxdg_positioner_v6_interface = { + "zxdg_positioner_v6", 1, + 7, zxdg_positioner_v6_requests, + 0, NULL, +}; + +static const struct wl_message zxdg_surface_v6_requests[] = { + { "destroy", "", xdg_shell_unstable_v6_types + 0 }, + { "get_toplevel", "n", xdg_shell_unstable_v6_types + 7 }, + { "get_popup", "noo", xdg_shell_unstable_v6_types + 8 }, + { "set_window_geometry", "iiii", xdg_shell_unstable_v6_types + 0 }, + { "ack_configure", "u", xdg_shell_unstable_v6_types + 0 }, +}; + +static const struct wl_message zxdg_surface_v6_events[] = { + { "configure", "u", xdg_shell_unstable_v6_types + 0 }, +}; + +WL_PRIVATE const struct wl_interface zxdg_surface_v6_interface = { + "zxdg_surface_v6", 1, + 5, zxdg_surface_v6_requests, + 1, zxdg_surface_v6_events, +}; + +static const struct wl_message zxdg_toplevel_v6_requests[] = { + { "destroy", "", xdg_shell_unstable_v6_types + 0 }, + { "set_parent", "?o", xdg_shell_unstable_v6_types + 11 }, + { "set_title", "s", xdg_shell_unstable_v6_types + 0 }, + { "set_app_id", "s", xdg_shell_unstable_v6_types + 0 }, + { "show_window_menu", "ouii", xdg_shell_unstable_v6_types + 12 }, + { "move", "ou", xdg_shell_unstable_v6_types + 16 }, + { "resize", "ouu", xdg_shell_unstable_v6_types + 18 }, + { "set_max_size", "ii", xdg_shell_unstable_v6_types + 0 }, + { "set_min_size", "ii", xdg_shell_unstable_v6_types + 0 }, + { "set_maximized", "", xdg_shell_unstable_v6_types + 0 }, + { "unset_maximized", "", xdg_shell_unstable_v6_types + 0 }, + { "set_fullscreen", "?o", xdg_shell_unstable_v6_types + 21 }, + { "unset_fullscreen", "", xdg_shell_unstable_v6_types + 0 }, + { "set_minimized", "", xdg_shell_unstable_v6_types + 0 }, +}; + +static const struct wl_message zxdg_toplevel_v6_events[] = { + { "configure", "iia", xdg_shell_unstable_v6_types + 0 }, + { "close", "", xdg_shell_unstable_v6_types + 0 }, +}; + +WL_PRIVATE const struct wl_interface zxdg_toplevel_v6_interface = { + "zxdg_toplevel_v6", 1, + 14, zxdg_toplevel_v6_requests, + 2, zxdg_toplevel_v6_events, +}; + +static const struct wl_message zxdg_popup_v6_requests[] = { + { "destroy", "", xdg_shell_unstable_v6_types + 0 }, + { "grab", "ou", xdg_shell_unstable_v6_types + 22 }, +}; + +static const struct wl_message zxdg_popup_v6_events[] = { + { "configure", "iiii", xdg_shell_unstable_v6_types + 0 }, + { "popup_done", "", xdg_shell_unstable_v6_types + 0 }, +}; + +WL_PRIVATE const struct wl_interface zxdg_popup_v6_interface = { + "zxdg_popup_v6", 1, + 2, zxdg_popup_v6_requests, + 2, zxdg_popup_v6_events, +}; + diff --git a/wayland/cc/xdg-shell.h b/wayland/cc/xdg-shell.h new file mode 100644 index 00000000..3f4d6846 --- /dev/null +++ b/wayland/cc/xdg-shell.h @@ -0,0 +1,1884 @@ +/* Generated by wayland-scanner 1.22.0 */ + +#ifndef XDG_SHELL_UNSTABLE_V6_CLIENT_PROTOCOL_H +#define XDG_SHELL_UNSTABLE_V6_CLIENT_PROTOCOL_H + +#include +#include +#include "wayland-client.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @page page_xdg_shell_unstable_v6 The xdg_shell_unstable_v6 protocol + * @section page_ifaces_xdg_shell_unstable_v6 Interfaces + * - @subpage page_iface_zxdg_shell_v6 - create desktop-style surfaces + * - @subpage page_iface_zxdg_positioner_v6 - child surface positioner + * - @subpage page_iface_zxdg_surface_v6 - desktop user interface surface base interface + * - @subpage page_iface_zxdg_toplevel_v6 - toplevel surface + * - @subpage page_iface_zxdg_popup_v6 - short-lived, popup surfaces for menus + * @section page_copyright_xdg_shell_unstable_v6 Copyright + *
+ *
+ * Copyright © 2008-2013 Kristian Høgsberg
+ * Copyright © 2013      Rafael Antognolli
+ * Copyright © 2013      Jasper St. Pierre
+ * Copyright © 2010-2013 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ * 
+ */ +struct wl_output; +struct wl_seat; +struct wl_surface; +struct zxdg_popup_v6; +struct zxdg_positioner_v6; +struct zxdg_shell_v6; +struct zxdg_surface_v6; +struct zxdg_toplevel_v6; + +#ifndef ZXDG_SHELL_V6_INTERFACE +#define ZXDG_SHELL_V6_INTERFACE +/** + * @page page_iface_zxdg_shell_v6 zxdg_shell_v6 + * @section page_iface_zxdg_shell_v6_desc Description + * + * xdg_shell allows clients to turn a wl_surface into a "real window" + * which can be dragged, resized, stacked, and moved around by the + * user. Everything about this interface is suited towards traditional + * desktop environments. + * @section page_iface_zxdg_shell_v6_api API + * See @ref iface_zxdg_shell_v6. + */ +/** + * @defgroup iface_zxdg_shell_v6 The zxdg_shell_v6 interface + * + * xdg_shell allows clients to turn a wl_surface into a "real window" + * which can be dragged, resized, stacked, and moved around by the + * user. Everything about this interface is suited towards traditional + * desktop environments. + */ +extern const struct wl_interface zxdg_shell_v6_interface; +#endif +#ifndef ZXDG_POSITIONER_V6_INTERFACE +#define ZXDG_POSITIONER_V6_INTERFACE +/** + * @page page_iface_zxdg_positioner_v6 zxdg_positioner_v6 + * @section page_iface_zxdg_positioner_v6_desc Description + * + * The xdg_positioner provides a collection of rules for the placement of a + * child surface relative to a parent surface. Rules can be defined to ensure + * the child surface remains within the visible area's borders, and to + * specify how the child surface changes its position, such as sliding along + * an axis, or flipping around a rectangle. These positioner-created rules are + * constrained by the requirement that a child surface must intersect with or + * be at least partially adjacent to its parent surface. + * + * See the various requests for details about possible rules. + * + * At the time of the request, the compositor makes a copy of the rules + * specified by the xdg_positioner. Thus, after the request is complete the + * xdg_positioner object can be destroyed or reused; further changes to the + * object will have no effect on previous usages. + * + * For an xdg_positioner object to be considered complete, it must have a + * non-zero size set by set_size, and a non-zero anchor rectangle set by + * set_anchor_rect. Passing an incomplete xdg_positioner object when + * positioning a surface raises an error. + * @section page_iface_zxdg_positioner_v6_api API + * See @ref iface_zxdg_positioner_v6. + */ +/** + * @defgroup iface_zxdg_positioner_v6 The zxdg_positioner_v6 interface + * + * The xdg_positioner provides a collection of rules for the placement of a + * child surface relative to a parent surface. Rules can be defined to ensure + * the child surface remains within the visible area's borders, and to + * specify how the child surface changes its position, such as sliding along + * an axis, or flipping around a rectangle. These positioner-created rules are + * constrained by the requirement that a child surface must intersect with or + * be at least partially adjacent to its parent surface. + * + * See the various requests for details about possible rules. + * + * At the time of the request, the compositor makes a copy of the rules + * specified by the xdg_positioner. Thus, after the request is complete the + * xdg_positioner object can be destroyed or reused; further changes to the + * object will have no effect on previous usages. + * + * For an xdg_positioner object to be considered complete, it must have a + * non-zero size set by set_size, and a non-zero anchor rectangle set by + * set_anchor_rect. Passing an incomplete xdg_positioner object when + * positioning a surface raises an error. + */ +extern const struct wl_interface zxdg_positioner_v6_interface; +#endif +#ifndef ZXDG_SURFACE_V6_INTERFACE +#define ZXDG_SURFACE_V6_INTERFACE +/** + * @page page_iface_zxdg_surface_v6 zxdg_surface_v6 + * @section page_iface_zxdg_surface_v6_desc Description + * + * An interface that may be implemented by a wl_surface, for + * implementations that provide a desktop-style user interface. + * + * It provides a base set of functionality required to construct user + * interface elements requiring management by the compositor, such as + * toplevel windows, menus, etc. The types of functionality are split into + * xdg_surface roles. + * + * Creating an xdg_surface does not set the role for a wl_surface. In order + * to map an xdg_surface, the client must create a role-specific object + * using, e.g., get_toplevel, get_popup. The wl_surface for any given + * xdg_surface can have at most one role, and may not be assigned any role + * not based on xdg_surface. + * + * A role must be assigned before any other requests are made to the + * xdg_surface object. + * + * The client must call wl_surface.commit on the corresponding wl_surface + * for the xdg_surface state to take effect. + * + * Creating an xdg_surface from a wl_surface which has a buffer attached or + * committed is a client error, and any attempts by a client to attach or + * manipulate a buffer prior to the first xdg_surface.configure call must + * also be treated as errors. + * + * For a surface to be mapped by the compositor, the following conditions + * must be met: (1) the client has assigned an xdg_surface based role to the + * surface, (2) the client has set and committed the xdg_surface state and + * the role dependent state to the surface and (3) the client has committed a + * buffer to the surface. + * @section page_iface_zxdg_surface_v6_api API + * See @ref iface_zxdg_surface_v6. + */ +/** + * @defgroup iface_zxdg_surface_v6 The zxdg_surface_v6 interface + * + * An interface that may be implemented by a wl_surface, for + * implementations that provide a desktop-style user interface. + * + * It provides a base set of functionality required to construct user + * interface elements requiring management by the compositor, such as + * toplevel windows, menus, etc. The types of functionality are split into + * xdg_surface roles. + * + * Creating an xdg_surface does not set the role for a wl_surface. In order + * to map an xdg_surface, the client must create a role-specific object + * using, e.g., get_toplevel, get_popup. The wl_surface for any given + * xdg_surface can have at most one role, and may not be assigned any role + * not based on xdg_surface. + * + * A role must be assigned before any other requests are made to the + * xdg_surface object. + * + * The client must call wl_surface.commit on the corresponding wl_surface + * for the xdg_surface state to take effect. + * + * Creating an xdg_surface from a wl_surface which has a buffer attached or + * committed is a client error, and any attempts by a client to attach or + * manipulate a buffer prior to the first xdg_surface.configure call must + * also be treated as errors. + * + * For a surface to be mapped by the compositor, the following conditions + * must be met: (1) the client has assigned an xdg_surface based role to the + * surface, (2) the client has set and committed the xdg_surface state and + * the role dependent state to the surface and (3) the client has committed a + * buffer to the surface. + */ +extern const struct wl_interface zxdg_surface_v6_interface; +#endif +#ifndef ZXDG_TOPLEVEL_V6_INTERFACE +#define ZXDG_TOPLEVEL_V6_INTERFACE +/** + * @page page_iface_zxdg_toplevel_v6 zxdg_toplevel_v6 + * @section page_iface_zxdg_toplevel_v6_desc Description + * + * This interface defines an xdg_surface role which allows a surface to, + * among other things, set window-like properties such as maximize, + * fullscreen, and minimize, set application-specific metadata like title and + * id, and well as trigger user interactive operations such as interactive + * resize and move. + * @section page_iface_zxdg_toplevel_v6_api API + * See @ref iface_zxdg_toplevel_v6. + */ +/** + * @defgroup iface_zxdg_toplevel_v6 The zxdg_toplevel_v6 interface + * + * This interface defines an xdg_surface role which allows a surface to, + * among other things, set window-like properties such as maximize, + * fullscreen, and minimize, set application-specific metadata like title and + * id, and well as trigger user interactive operations such as interactive + * resize and move. + */ +extern const struct wl_interface zxdg_toplevel_v6_interface; +#endif +#ifndef ZXDG_POPUP_V6_INTERFACE +#define ZXDG_POPUP_V6_INTERFACE +/** + * @page page_iface_zxdg_popup_v6 zxdg_popup_v6 + * @section page_iface_zxdg_popup_v6_desc Description + * + * A popup surface is a short-lived, temporary surface. It can be used to + * implement for example menus, popovers, tooltips and other similar user + * interface concepts. + * + * A popup can be made to take an explicit grab. See xdg_popup.grab for + * details. + * + * When the popup is dismissed, a popup_done event will be sent out, and at + * the same time the surface will be unmapped. See the xdg_popup.popup_done + * event for details. + * + * Explicitly destroying the xdg_popup object will also dismiss the popup and + * unmap the surface. Clients that want to dismiss the popup when another + * surface of their own is clicked should dismiss the popup using the destroy + * request. + * + * The parent surface must have either the xdg_toplevel or xdg_popup surface + * role. + * + * A newly created xdg_popup will be stacked on top of all previously created + * xdg_popup surfaces associated with the same xdg_toplevel. + * + * The parent of an xdg_popup must be mapped (see the xdg_surface + * description) before the xdg_popup itself. + * + * The x and y arguments passed when creating the popup object specify + * where the top left of the popup should be placed, relative to the + * local surface coordinates of the parent surface. See + * xdg_surface.get_popup. An xdg_popup must intersect with or be at least + * partially adjacent to its parent surface. + * + * The client must call wl_surface.commit on the corresponding wl_surface + * for the xdg_popup state to take effect. + * @section page_iface_zxdg_popup_v6_api API + * See @ref iface_zxdg_popup_v6. + */ +/** + * @defgroup iface_zxdg_popup_v6 The zxdg_popup_v6 interface + * + * A popup surface is a short-lived, temporary surface. It can be used to + * implement for example menus, popovers, tooltips and other similar user + * interface concepts. + * + * A popup can be made to take an explicit grab. See xdg_popup.grab for + * details. + * + * When the popup is dismissed, a popup_done event will be sent out, and at + * the same time the surface will be unmapped. See the xdg_popup.popup_done + * event for details. + * + * Explicitly destroying the xdg_popup object will also dismiss the popup and + * unmap the surface. Clients that want to dismiss the popup when another + * surface of their own is clicked should dismiss the popup using the destroy + * request. + * + * The parent surface must have either the xdg_toplevel or xdg_popup surface + * role. + * + * A newly created xdg_popup will be stacked on top of all previously created + * xdg_popup surfaces associated with the same xdg_toplevel. + * + * The parent of an xdg_popup must be mapped (see the xdg_surface + * description) before the xdg_popup itself. + * + * The x and y arguments passed when creating the popup object specify + * where the top left of the popup should be placed, relative to the + * local surface coordinates of the parent surface. See + * xdg_surface.get_popup. An xdg_popup must intersect with or be at least + * partially adjacent to its parent surface. + * + * The client must call wl_surface.commit on the corresponding wl_surface + * for the xdg_popup state to take effect. + */ +extern const struct wl_interface zxdg_popup_v6_interface; +#endif + +#ifndef ZXDG_SHELL_V6_ERROR_ENUM +#define ZXDG_SHELL_V6_ERROR_ENUM +enum zxdg_shell_v6_error { + /** + * given wl_surface has another role + */ + ZXDG_SHELL_V6_ERROR_ROLE = 0, + /** + * xdg_shell was destroyed before children + */ + ZXDG_SHELL_V6_ERROR_DEFUNCT_SURFACES = 1, + /** + * the client tried to map or destroy a non-topmost popup + */ + ZXDG_SHELL_V6_ERROR_NOT_THE_TOPMOST_POPUP = 2, + /** + * the client specified an invalid popup parent surface + */ + ZXDG_SHELL_V6_ERROR_INVALID_POPUP_PARENT = 3, + /** + * the client provided an invalid surface state + */ + ZXDG_SHELL_V6_ERROR_INVALID_SURFACE_STATE = 4, + /** + * the client provided an invalid positioner + */ + ZXDG_SHELL_V6_ERROR_INVALID_POSITIONER = 5, +}; +#endif /* ZXDG_SHELL_V6_ERROR_ENUM */ + +/** + * @ingroup iface_zxdg_shell_v6 + * @struct zxdg_shell_v6_listener + */ +struct zxdg_shell_v6_listener { + /** + * check if the client is alive + * + * The ping event asks the client if it's still alive. Pass the + * serial specified in the event back to the compositor by sending + * a "pong" request back with the specified serial. See + * xdg_shell.ping. + * + * Compositors can use this to determine if the client is still + * alive. It's unspecified what will happen if the client doesn't + * respond to the ping request, or in what timeframe. Clients + * should try to respond in a reasonable amount of time. + * + * A compositor is free to ping in any way it wants, but a client + * must always respond to any xdg_shell object it created. + * @param serial pass this to the pong request + */ + void (*ping)(void *data, + struct zxdg_shell_v6 *zxdg_shell_v6, + uint32_t serial); +}; + +/** + * @ingroup iface_zxdg_shell_v6 + */ +static inline int +zxdg_shell_v6_add_listener(struct zxdg_shell_v6 *zxdg_shell_v6, + const struct zxdg_shell_v6_listener *listener, void *data) +{ + return wl_proxy_add_listener((struct wl_proxy *) zxdg_shell_v6, + (void (**)(void)) listener, data); +} + +#define ZXDG_SHELL_V6_DESTROY 0 +#define ZXDG_SHELL_V6_CREATE_POSITIONER 1 +#define ZXDG_SHELL_V6_GET_XDG_SURFACE 2 +#define ZXDG_SHELL_V6_PONG 3 + +/** + * @ingroup iface_zxdg_shell_v6 + */ +#define ZXDG_SHELL_V6_PING_SINCE_VERSION 1 + +/** + * @ingroup iface_zxdg_shell_v6 + */ +#define ZXDG_SHELL_V6_DESTROY_SINCE_VERSION 1 +/** + * @ingroup iface_zxdg_shell_v6 + */ +#define ZXDG_SHELL_V6_CREATE_POSITIONER_SINCE_VERSION 1 +/** + * @ingroup iface_zxdg_shell_v6 + */ +#define ZXDG_SHELL_V6_GET_XDG_SURFACE_SINCE_VERSION 1 +/** + * @ingroup iface_zxdg_shell_v6 + */ +#define ZXDG_SHELL_V6_PONG_SINCE_VERSION 1 + +/** @ingroup iface_zxdg_shell_v6 */ +static inline void +zxdg_shell_v6_set_user_data(struct zxdg_shell_v6 *zxdg_shell_v6, void *user_data) +{ + wl_proxy_set_user_data((struct wl_proxy *) zxdg_shell_v6, user_data); +} + +/** @ingroup iface_zxdg_shell_v6 */ +static inline void * +zxdg_shell_v6_get_user_data(struct zxdg_shell_v6 *zxdg_shell_v6) +{ + return wl_proxy_get_user_data((struct wl_proxy *) zxdg_shell_v6); +} + +static inline uint32_t +zxdg_shell_v6_get_version(struct zxdg_shell_v6 *zxdg_shell_v6) +{ + return wl_proxy_get_version((struct wl_proxy *) zxdg_shell_v6); +} + +/** + * @ingroup iface_zxdg_shell_v6 + * + * Destroy this xdg_shell object. + * + * Destroying a bound xdg_shell object while there are surfaces + * still alive created by this xdg_shell object instance is illegal + * and will result in a protocol error. + */ +static inline void +zxdg_shell_v6_destroy(struct zxdg_shell_v6 *zxdg_shell_v6) +{ + wl_proxy_marshal_flags((struct wl_proxy *) zxdg_shell_v6, + ZXDG_SHELL_V6_DESTROY, NULL, wl_proxy_get_version((struct wl_proxy *) zxdg_shell_v6), WL_MARSHAL_FLAG_DESTROY); +} + +/** + * @ingroup iface_zxdg_shell_v6 + * + * Create a positioner object. A positioner object is used to position + * surfaces relative to some parent surface. See the interface description + * and xdg_surface.get_popup for details. + */ +static inline struct zxdg_positioner_v6 * +zxdg_shell_v6_create_positioner(struct zxdg_shell_v6 *zxdg_shell_v6) +{ + struct wl_proxy *id; + + id = wl_proxy_marshal_flags((struct wl_proxy *) zxdg_shell_v6, + ZXDG_SHELL_V6_CREATE_POSITIONER, &zxdg_positioner_v6_interface, wl_proxy_get_version((struct wl_proxy *) zxdg_shell_v6), 0, NULL); + + return (struct zxdg_positioner_v6 *) id; +} + +/** + * @ingroup iface_zxdg_shell_v6 + * + * This creates an xdg_surface for the given surface. While xdg_surface + * itself is not a role, the corresponding surface may only be assigned + * a role extending xdg_surface, such as xdg_toplevel or xdg_popup. + * + * This creates an xdg_surface for the given surface. An xdg_surface is + * used as basis to define a role to a given surface, such as xdg_toplevel + * or xdg_popup. It also manages functionality shared between xdg_surface + * based surface roles. + * + * See the documentation of xdg_surface for more details about what an + * xdg_surface is and how it is used. + */ +static inline struct zxdg_surface_v6 * +zxdg_shell_v6_get_xdg_surface(struct zxdg_shell_v6 *zxdg_shell_v6, struct wl_surface *surface) +{ + struct wl_proxy *id; + + id = wl_proxy_marshal_flags((struct wl_proxy *) zxdg_shell_v6, + ZXDG_SHELL_V6_GET_XDG_SURFACE, &zxdg_surface_v6_interface, wl_proxy_get_version((struct wl_proxy *) zxdg_shell_v6), 0, NULL, surface); + + return (struct zxdg_surface_v6 *) id; +} + +/** + * @ingroup iface_zxdg_shell_v6 + * + * A client must respond to a ping event with a pong request or + * the client may be deemed unresponsive. See xdg_shell.ping. + */ +static inline void +zxdg_shell_v6_pong(struct zxdg_shell_v6 *zxdg_shell_v6, uint32_t serial) +{ + wl_proxy_marshal_flags((struct wl_proxy *) zxdg_shell_v6, + ZXDG_SHELL_V6_PONG, NULL, wl_proxy_get_version((struct wl_proxy *) zxdg_shell_v6), 0, serial); +} + +#ifndef ZXDG_POSITIONER_V6_ERROR_ENUM +#define ZXDG_POSITIONER_V6_ERROR_ENUM +enum zxdg_positioner_v6_error { + /** + * invalid input provided + */ + ZXDG_POSITIONER_V6_ERROR_INVALID_INPUT = 0, +}; +#endif /* ZXDG_POSITIONER_V6_ERROR_ENUM */ + +#ifndef ZXDG_POSITIONER_V6_ANCHOR_ENUM +#define ZXDG_POSITIONER_V6_ANCHOR_ENUM +enum zxdg_positioner_v6_anchor { + /** + * the center of the anchor rectangle + */ + ZXDG_POSITIONER_V6_ANCHOR_NONE = 0, + /** + * the top edge of the anchor rectangle + */ + ZXDG_POSITIONER_V6_ANCHOR_TOP = 1, + /** + * the bottom edge of the anchor rectangle + */ + ZXDG_POSITIONER_V6_ANCHOR_BOTTOM = 2, + /** + * the left edge of the anchor rectangle + */ + ZXDG_POSITIONER_V6_ANCHOR_LEFT = 4, + /** + * the right edge of the anchor rectangle + */ + ZXDG_POSITIONER_V6_ANCHOR_RIGHT = 8, +}; +#endif /* ZXDG_POSITIONER_V6_ANCHOR_ENUM */ + +#ifndef ZXDG_POSITIONER_V6_GRAVITY_ENUM +#define ZXDG_POSITIONER_V6_GRAVITY_ENUM +enum zxdg_positioner_v6_gravity { + /** + * center over the anchor edge + */ + ZXDG_POSITIONER_V6_GRAVITY_NONE = 0, + /** + * position above the anchor edge + */ + ZXDG_POSITIONER_V6_GRAVITY_TOP = 1, + /** + * position below the anchor edge + */ + ZXDG_POSITIONER_V6_GRAVITY_BOTTOM = 2, + /** + * position to the left of the anchor edge + */ + ZXDG_POSITIONER_V6_GRAVITY_LEFT = 4, + /** + * position to the right of the anchor edge + */ + ZXDG_POSITIONER_V6_GRAVITY_RIGHT = 8, +}; +#endif /* ZXDG_POSITIONER_V6_GRAVITY_ENUM */ + +#ifndef ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_ENUM +#define ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_ENUM +/** + * @ingroup iface_zxdg_positioner_v6 + * constraint adjustments + * + * The constraint adjustment value define ways the compositor will adjust + * the position of the surface, if the unadjusted position would result + * in the surface being partly constrained. + * + * Whether a surface is considered 'constrained' is left to the compositor + * to determine. For example, the surface may be partly outside the + * compositor's defined 'work area', thus necessitating the child surface's + * position be adjusted until it is entirely inside the work area. + * + * The adjustments can be combined, according to a defined precedence: 1) + * Flip, 2) Slide, 3) Resize. + */ +enum zxdg_positioner_v6_constraint_adjustment { + /** + * don't move the child surface when constrained + * + * Don't alter the surface position even if it is constrained on + * some axis, for example partially outside the edge of a monitor. + */ + ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_NONE = 0, + /** + * move along the x axis until unconstrained + * + * Slide the surface along the x axis until it is no longer + * constrained. + * + * First try to slide towards the direction of the gravity on the x + * axis until either the edge in the opposite direction of the + * gravity is unconstrained or the edge in the direction of the + * gravity is constrained. + * + * Then try to slide towards the opposite direction of the gravity + * on the x axis until either the edge in the direction of the + * gravity is unconstrained or the edge in the opposite direction + * of the gravity is constrained. + */ + ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_SLIDE_X = 1, + /** + * move along the y axis until unconstrained + * + * Slide the surface along the y axis until it is no longer + * constrained. + * + * First try to slide towards the direction of the gravity on the y + * axis until either the edge in the opposite direction of the + * gravity is unconstrained or the edge in the direction of the + * gravity is constrained. + * + * Then try to slide towards the opposite direction of the gravity + * on the y axis until either the edge in the direction of the + * gravity is unconstrained or the edge in the opposite direction + * of the gravity is constrained. + */ + ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_SLIDE_Y = 2, + /** + * invert the anchor and gravity on the x axis + * + * Invert the anchor and gravity on the x axis if the surface is + * constrained on the x axis. For example, if the left edge of the + * surface is constrained, the gravity is 'left' and the anchor is + * 'left', change the gravity to 'right' and the anchor to 'right'. + * + * If the adjusted position also ends up being constrained, the + * resulting position of the flip_x adjustment will be the one + * before the adjustment. + */ + ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_FLIP_X = 4, + /** + * invert the anchor and gravity on the y axis + * + * Invert the anchor and gravity on the y axis if the surface is + * constrained on the y axis. For example, if the bottom edge of + * the surface is constrained, the gravity is 'bottom' and the + * anchor is 'bottom', change the gravity to 'top' and the anchor + * to 'top'. + * + * If the adjusted position also ends up being constrained, the + * resulting position of the flip_y adjustment will be the one + * before the adjustment. + */ + ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_FLIP_Y = 8, + /** + * horizontally resize the surface + * + * Resize the surface horizontally so that it is completely + * unconstrained. + */ + ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_RESIZE_X = 16, + /** + * vertically resize the surface + * + * Resize the surface vertically so that it is completely + * unconstrained. + */ + ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_RESIZE_Y = 32, +}; +#endif /* ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_ENUM */ + +#define ZXDG_POSITIONER_V6_DESTROY 0 +#define ZXDG_POSITIONER_V6_SET_SIZE 1 +#define ZXDG_POSITIONER_V6_SET_ANCHOR_RECT 2 +#define ZXDG_POSITIONER_V6_SET_ANCHOR 3 +#define ZXDG_POSITIONER_V6_SET_GRAVITY 4 +#define ZXDG_POSITIONER_V6_SET_CONSTRAINT_ADJUSTMENT 5 +#define ZXDG_POSITIONER_V6_SET_OFFSET 6 + + +/** + * @ingroup iface_zxdg_positioner_v6 + */ +#define ZXDG_POSITIONER_V6_DESTROY_SINCE_VERSION 1 +/** + * @ingroup iface_zxdg_positioner_v6 + */ +#define ZXDG_POSITIONER_V6_SET_SIZE_SINCE_VERSION 1 +/** + * @ingroup iface_zxdg_positioner_v6 + */ +#define ZXDG_POSITIONER_V6_SET_ANCHOR_RECT_SINCE_VERSION 1 +/** + * @ingroup iface_zxdg_positioner_v6 + */ +#define ZXDG_POSITIONER_V6_SET_ANCHOR_SINCE_VERSION 1 +/** + * @ingroup iface_zxdg_positioner_v6 + */ +#define ZXDG_POSITIONER_V6_SET_GRAVITY_SINCE_VERSION 1 +/** + * @ingroup iface_zxdg_positioner_v6 + */ +#define ZXDG_POSITIONER_V6_SET_CONSTRAINT_ADJUSTMENT_SINCE_VERSION 1 +/** + * @ingroup iface_zxdg_positioner_v6 + */ +#define ZXDG_POSITIONER_V6_SET_OFFSET_SINCE_VERSION 1 + +/** @ingroup iface_zxdg_positioner_v6 */ +static inline void +zxdg_positioner_v6_set_user_data(struct zxdg_positioner_v6 *zxdg_positioner_v6, void *user_data) +{ + wl_proxy_set_user_data((struct wl_proxy *) zxdg_positioner_v6, user_data); +} + +/** @ingroup iface_zxdg_positioner_v6 */ +static inline void * +zxdg_positioner_v6_get_user_data(struct zxdg_positioner_v6 *zxdg_positioner_v6) +{ + return wl_proxy_get_user_data((struct wl_proxy *) zxdg_positioner_v6); +} + +static inline uint32_t +zxdg_positioner_v6_get_version(struct zxdg_positioner_v6 *zxdg_positioner_v6) +{ + return wl_proxy_get_version((struct wl_proxy *) zxdg_positioner_v6); +} + +/** + * @ingroup iface_zxdg_positioner_v6 + * + * Notify the compositor that the xdg_positioner will no longer be used. + */ +static inline void +zxdg_positioner_v6_destroy(struct zxdg_positioner_v6 *zxdg_positioner_v6) +{ + wl_proxy_marshal_flags((struct wl_proxy *) zxdg_positioner_v6, + ZXDG_POSITIONER_V6_DESTROY, NULL, wl_proxy_get_version((struct wl_proxy *) zxdg_positioner_v6), WL_MARSHAL_FLAG_DESTROY); +} + +/** + * @ingroup iface_zxdg_positioner_v6 + * + * Set the size of the surface that is to be positioned with the positioner + * object. The size is in surface-local coordinates and corresponds to the + * window geometry. See xdg_surface.set_window_geometry. + * + * If a zero or negative size is set the invalid_input error is raised. + */ +static inline void +zxdg_positioner_v6_set_size(struct zxdg_positioner_v6 *zxdg_positioner_v6, int32_t width, int32_t height) +{ + wl_proxy_marshal_flags((struct wl_proxy *) zxdg_positioner_v6, + ZXDG_POSITIONER_V6_SET_SIZE, NULL, wl_proxy_get_version((struct wl_proxy *) zxdg_positioner_v6), 0, width, height); +} + +/** + * @ingroup iface_zxdg_positioner_v6 + * + * Specify the anchor rectangle within the parent surface that the child + * surface will be placed relative to. The rectangle is relative to the + * window geometry as defined by xdg_surface.set_window_geometry of the + * parent surface. The rectangle must be at least 1x1 large. + * + * When the xdg_positioner object is used to position a child surface, the + * anchor rectangle may not extend outside the window geometry of the + * positioned child's parent surface. + * + * If a zero or negative size is set the invalid_input error is raised. + */ +static inline void +zxdg_positioner_v6_set_anchor_rect(struct zxdg_positioner_v6 *zxdg_positioner_v6, int32_t x, int32_t y, int32_t width, int32_t height) +{ + wl_proxy_marshal_flags((struct wl_proxy *) zxdg_positioner_v6, + ZXDG_POSITIONER_V6_SET_ANCHOR_RECT, NULL, wl_proxy_get_version((struct wl_proxy *) zxdg_positioner_v6), 0, x, y, width, height); +} + +/** + * @ingroup iface_zxdg_positioner_v6 + * + * Defines a set of edges for the anchor rectangle. These are used to + * derive an anchor point that the child surface will be positioned + * relative to. If two orthogonal edges are specified (e.g. 'top' and + * 'left'), then the anchor point will be the intersection of the edges + * (e.g. the top left position of the rectangle); otherwise, the derived + * anchor point will be centered on the specified edge, or in the center of + * the anchor rectangle if no edge is specified. + * + * If two parallel anchor edges are specified (e.g. 'left' and 'right'), + * the invalid_input error is raised. + */ +static inline void +zxdg_positioner_v6_set_anchor(struct zxdg_positioner_v6 *zxdg_positioner_v6, uint32_t anchor) +{ + wl_proxy_marshal_flags((struct wl_proxy *) zxdg_positioner_v6, + ZXDG_POSITIONER_V6_SET_ANCHOR, NULL, wl_proxy_get_version((struct wl_proxy *) zxdg_positioner_v6), 0, anchor); +} + +/** + * @ingroup iface_zxdg_positioner_v6 + * + * Defines in what direction a surface should be positioned, relative to + * the anchor point of the parent surface. If two orthogonal gravities are + * specified (e.g. 'bottom' and 'right'), then the child surface will be + * placed in the specified direction; otherwise, the child surface will be + * centered over the anchor point on any axis that had no gravity + * specified. + * + * If two parallel gravities are specified (e.g. 'left' and 'right'), the + * invalid_input error is raised. + */ +static inline void +zxdg_positioner_v6_set_gravity(struct zxdg_positioner_v6 *zxdg_positioner_v6, uint32_t gravity) +{ + wl_proxy_marshal_flags((struct wl_proxy *) zxdg_positioner_v6, + ZXDG_POSITIONER_V6_SET_GRAVITY, NULL, wl_proxy_get_version((struct wl_proxy *) zxdg_positioner_v6), 0, gravity); +} + +/** + * @ingroup iface_zxdg_positioner_v6 + * + * Specify how the window should be positioned if the originally intended + * position caused the surface to be constrained, meaning at least + * partially outside positioning boundaries set by the compositor. The + * adjustment is set by constructing a bitmask describing the adjustment to + * be made when the surface is constrained on that axis. + * + * If no bit for one axis is set, the compositor will assume that the child + * surface should not change its position on that axis when constrained. + * + * If more than one bit for one axis is set, the order of how adjustments + * are applied is specified in the corresponding adjustment descriptions. + * + * The default adjustment is none. + */ +static inline void +zxdg_positioner_v6_set_constraint_adjustment(struct zxdg_positioner_v6 *zxdg_positioner_v6, uint32_t constraint_adjustment) +{ + wl_proxy_marshal_flags((struct wl_proxy *) zxdg_positioner_v6, + ZXDG_POSITIONER_V6_SET_CONSTRAINT_ADJUSTMENT, NULL, wl_proxy_get_version((struct wl_proxy *) zxdg_positioner_v6), 0, constraint_adjustment); +} + +/** + * @ingroup iface_zxdg_positioner_v6 + * + * Specify the surface position offset relative to the position of the + * anchor on the anchor rectangle and the anchor on the surface. For + * example if the anchor of the anchor rectangle is at (x, y), the surface + * has the gravity bottom|right, and the offset is (ox, oy), the calculated + * surface position will be (x + ox, y + oy). The offset position of the + * surface is the one used for constraint testing. See + * set_constraint_adjustment. + * + * An example use case is placing a popup menu on top of a user interface + * element, while aligning the user interface element of the parent surface + * with some user interface element placed somewhere in the popup surface. + */ +static inline void +zxdg_positioner_v6_set_offset(struct zxdg_positioner_v6 *zxdg_positioner_v6, int32_t x, int32_t y) +{ + wl_proxy_marshal_flags((struct wl_proxy *) zxdg_positioner_v6, + ZXDG_POSITIONER_V6_SET_OFFSET, NULL, wl_proxy_get_version((struct wl_proxy *) zxdg_positioner_v6), 0, x, y); +} + +#ifndef ZXDG_SURFACE_V6_ERROR_ENUM +#define ZXDG_SURFACE_V6_ERROR_ENUM +enum zxdg_surface_v6_error { + ZXDG_SURFACE_V6_ERROR_NOT_CONSTRUCTED = 1, + ZXDG_SURFACE_V6_ERROR_ALREADY_CONSTRUCTED = 2, + ZXDG_SURFACE_V6_ERROR_UNCONFIGURED_BUFFER = 3, +}; +#endif /* ZXDG_SURFACE_V6_ERROR_ENUM */ + +/** + * @ingroup iface_zxdg_surface_v6 + * @struct zxdg_surface_v6_listener + */ +struct zxdg_surface_v6_listener { + /** + * suggest a surface change + * + * The configure event marks the end of a configure sequence. A + * configure sequence is a set of one or more events configuring + * the state of the xdg_surface, including the final + * xdg_surface.configure event. + * + * Where applicable, xdg_surface surface roles will during a + * configure sequence extend this event as a latched state sent as + * events before the xdg_surface.configure event. Such events + * should be considered to make up a set of atomically applied + * configuration states, where the xdg_surface.configure commits + * the accumulated state. + * + * Clients should arrange their surface for the new states, and + * then send an ack_configure request with the serial sent in this + * configure event at some point before committing the new surface. + * + * If the client receives multiple configure events before it can + * respond to one, it is free to discard all but the last event it + * received. + * @param serial serial of the configure event + */ + void (*configure)(void *data, + struct zxdg_surface_v6 *zxdg_surface_v6, + uint32_t serial); +}; + +/** + * @ingroup iface_zxdg_surface_v6 + */ +static inline int +zxdg_surface_v6_add_listener(struct zxdg_surface_v6 *zxdg_surface_v6, + const struct zxdg_surface_v6_listener *listener, void *data) +{ + return wl_proxy_add_listener((struct wl_proxy *) zxdg_surface_v6, + (void (**)(void)) listener, data); +} + +#define ZXDG_SURFACE_V6_DESTROY 0 +#define ZXDG_SURFACE_V6_GET_TOPLEVEL 1 +#define ZXDG_SURFACE_V6_GET_POPUP 2 +#define ZXDG_SURFACE_V6_SET_WINDOW_GEOMETRY 3 +#define ZXDG_SURFACE_V6_ACK_CONFIGURE 4 + +/** + * @ingroup iface_zxdg_surface_v6 + */ +#define ZXDG_SURFACE_V6_CONFIGURE_SINCE_VERSION 1 + +/** + * @ingroup iface_zxdg_surface_v6 + */ +#define ZXDG_SURFACE_V6_DESTROY_SINCE_VERSION 1 +/** + * @ingroup iface_zxdg_surface_v6 + */ +#define ZXDG_SURFACE_V6_GET_TOPLEVEL_SINCE_VERSION 1 +/** + * @ingroup iface_zxdg_surface_v6 + */ +#define ZXDG_SURFACE_V6_GET_POPUP_SINCE_VERSION 1 +/** + * @ingroup iface_zxdg_surface_v6 + */ +#define ZXDG_SURFACE_V6_SET_WINDOW_GEOMETRY_SINCE_VERSION 1 +/** + * @ingroup iface_zxdg_surface_v6 + */ +#define ZXDG_SURFACE_V6_ACK_CONFIGURE_SINCE_VERSION 1 + +/** @ingroup iface_zxdg_surface_v6 */ +static inline void +zxdg_surface_v6_set_user_data(struct zxdg_surface_v6 *zxdg_surface_v6, void *user_data) +{ + wl_proxy_set_user_data((struct wl_proxy *) zxdg_surface_v6, user_data); +} + +/** @ingroup iface_zxdg_surface_v6 */ +static inline void * +zxdg_surface_v6_get_user_data(struct zxdg_surface_v6 *zxdg_surface_v6) +{ + return wl_proxy_get_user_data((struct wl_proxy *) zxdg_surface_v6); +} + +static inline uint32_t +zxdg_surface_v6_get_version(struct zxdg_surface_v6 *zxdg_surface_v6) +{ + return wl_proxy_get_version((struct wl_proxy *) zxdg_surface_v6); +} + +/** + * @ingroup iface_zxdg_surface_v6 + * + * Destroy the xdg_surface object. An xdg_surface must only be destroyed + * after its role object has been destroyed. If the role object still + * exists when this request is issued, the zxdg_shell_v6.defunct_surfaces + * is raised. + */ +static inline void +zxdg_surface_v6_destroy(struct zxdg_surface_v6 *zxdg_surface_v6) +{ + wl_proxy_marshal_flags((struct wl_proxy *) zxdg_surface_v6, + ZXDG_SURFACE_V6_DESTROY, NULL, wl_proxy_get_version((struct wl_proxy *) zxdg_surface_v6), WL_MARSHAL_FLAG_DESTROY); +} + +/** + * @ingroup iface_zxdg_surface_v6 + * + * This creates an xdg_toplevel object for the given xdg_surface and gives + * the associated wl_surface the xdg_toplevel role. If the surface already + * had a role, the zxdg_shell_v6.role error is raised. + * + * See the documentation of xdg_toplevel for more details about what an + * xdg_toplevel is and how it is used. + */ +static inline struct zxdg_toplevel_v6 * +zxdg_surface_v6_get_toplevel(struct zxdg_surface_v6 *zxdg_surface_v6) +{ + struct wl_proxy *id; + + id = wl_proxy_marshal_flags((struct wl_proxy *) zxdg_surface_v6, + ZXDG_SURFACE_V6_GET_TOPLEVEL, &zxdg_toplevel_v6_interface, wl_proxy_get_version((struct wl_proxy *) zxdg_surface_v6), 0, NULL); + + return (struct zxdg_toplevel_v6 *) id; +} + +/** + * @ingroup iface_zxdg_surface_v6 + * + * This creates an xdg_popup object for the given xdg_surface and gives the + * associated wl_surface the xdg_popup role. If the surface already + * had a role, the zxdg_shell_v6.role error is raised. + * + * See the documentation of xdg_popup for more details about what an + * xdg_popup is and how it is used. + */ +static inline struct zxdg_popup_v6 * +zxdg_surface_v6_get_popup(struct zxdg_surface_v6 *zxdg_surface_v6, struct zxdg_surface_v6 *parent, struct zxdg_positioner_v6 *positioner) +{ + struct wl_proxy *id; + + id = wl_proxy_marshal_flags((struct wl_proxy *) zxdg_surface_v6, + ZXDG_SURFACE_V6_GET_POPUP, &zxdg_popup_v6_interface, wl_proxy_get_version((struct wl_proxy *) zxdg_surface_v6), 0, NULL, parent, positioner); + + return (struct zxdg_popup_v6 *) id; +} + +/** + * @ingroup iface_zxdg_surface_v6 + * + * The window geometry of a surface is its "visible bounds" from the + * user's perspective. Client-side decorations often have invisible + * portions like drop-shadows which should be ignored for the + * purposes of aligning, placing and constraining windows. + * + * The window geometry is double buffered, and will be applied at the + * time wl_surface.commit of the corresponding wl_surface is called. + * + * Once the window geometry of the surface is set, it is not possible to + * unset it, and it will remain the same until set_window_geometry is + * called again, even if a new subsurface or buffer is attached. + * + * If never set, the value is the full bounds of the surface, + * including any subsurfaces. This updates dynamically on every + * commit. This unset is meant for extremely simple clients. + * + * The arguments are given in the surface-local coordinate space of + * the wl_surface associated with this xdg_surface. + * + * The width and height must be greater than zero. Setting an invalid size + * will raise an error. When applied, the effective window geometry will be + * the set window geometry clamped to the bounding rectangle of the + * combined geometry of the surface of the xdg_surface and the associated + * subsurfaces. + */ +static inline void +zxdg_surface_v6_set_window_geometry(struct zxdg_surface_v6 *zxdg_surface_v6, int32_t x, int32_t y, int32_t width, int32_t height) +{ + wl_proxy_marshal_flags((struct wl_proxy *) zxdg_surface_v6, + ZXDG_SURFACE_V6_SET_WINDOW_GEOMETRY, NULL, wl_proxy_get_version((struct wl_proxy *) zxdg_surface_v6), 0, x, y, width, height); +} + +/** + * @ingroup iface_zxdg_surface_v6 + * + * When a configure event is received, if a client commits the + * surface in response to the configure event, then the client + * must make an ack_configure request sometime before the commit + * request, passing along the serial of the configure event. + * + * For instance, for toplevel surfaces the compositor might use this + * information to move a surface to the top left only when the client has + * drawn itself for the maximized or fullscreen state. + * + * If the client receives multiple configure events before it + * can respond to one, it only has to ack the last configure event. + * + * A client is not required to commit immediately after sending + * an ack_configure request - it may even ack_configure several times + * before its next surface commit. + * + * A client may send multiple ack_configure requests before committing, but + * only the last request sent before a commit indicates which configure + * event the client really is responding to. + * + * If an invalid serial is used, the zxdg_shell_v6.invalid_surface_state + * error is raised. + */ +static inline void +zxdg_surface_v6_ack_configure(struct zxdg_surface_v6 *zxdg_surface_v6, uint32_t serial) +{ + wl_proxy_marshal_flags((struct wl_proxy *) zxdg_surface_v6, + ZXDG_SURFACE_V6_ACK_CONFIGURE, NULL, wl_proxy_get_version((struct wl_proxy *) zxdg_surface_v6), 0, serial); +} + +#ifndef ZXDG_TOPLEVEL_V6_RESIZE_EDGE_ENUM +#define ZXDG_TOPLEVEL_V6_RESIZE_EDGE_ENUM +/** + * @ingroup iface_zxdg_toplevel_v6 + * edge values for resizing + * + * These values are used to indicate which edge of a surface + * is being dragged in a resize operation. + */ +enum zxdg_toplevel_v6_resize_edge { + ZXDG_TOPLEVEL_V6_RESIZE_EDGE_NONE = 0, + ZXDG_TOPLEVEL_V6_RESIZE_EDGE_TOP = 1, + ZXDG_TOPLEVEL_V6_RESIZE_EDGE_BOTTOM = 2, + ZXDG_TOPLEVEL_V6_RESIZE_EDGE_LEFT = 4, + ZXDG_TOPLEVEL_V6_RESIZE_EDGE_TOP_LEFT = 5, + ZXDG_TOPLEVEL_V6_RESIZE_EDGE_BOTTOM_LEFT = 6, + ZXDG_TOPLEVEL_V6_RESIZE_EDGE_RIGHT = 8, + ZXDG_TOPLEVEL_V6_RESIZE_EDGE_TOP_RIGHT = 9, + ZXDG_TOPLEVEL_V6_RESIZE_EDGE_BOTTOM_RIGHT = 10, +}; +#endif /* ZXDG_TOPLEVEL_V6_RESIZE_EDGE_ENUM */ + +#ifndef ZXDG_TOPLEVEL_V6_STATE_ENUM +#define ZXDG_TOPLEVEL_V6_STATE_ENUM +/** + * @ingroup iface_zxdg_toplevel_v6 + * types of state on the surface + * + * The different state values used on the surface. This is designed for + * state values like maximized, fullscreen. It is paired with the + * configure event to ensure that both the client and the compositor + * setting the state can be synchronized. + * + * States set in this way are double-buffered. They will get applied on + * the next commit. + */ +enum zxdg_toplevel_v6_state { + /** + * the surface is maximized + * the surface is maximized + * + * The surface is maximized. The window geometry specified in the + * configure event must be obeyed by the client. If the window + * geometry is not obyed, the zxdg_shell_v6.invalid_surface_state + * error is raised. + */ + ZXDG_TOPLEVEL_V6_STATE_MAXIMIZED = 1, + /** + * the surface is fullscreen + * the surface is fullscreen + * + * The surface is fullscreen. See set_fullscreen for more + * information. + */ + ZXDG_TOPLEVEL_V6_STATE_FULLSCREEN = 2, + /** + * the surface is being resized + * the surface is being resized + * + * The surface is being resized. The window geometry specified in + * the configure event is a maximum; the client cannot resize + * beyond it. If the client attempts to resize above it, the + * zxdg_shell_v6.invalid_surface_state error is raised. Clients + * that have aspect ratio or cell sizing configuration can use a + * smaller size, however. + */ + ZXDG_TOPLEVEL_V6_STATE_RESIZING = 3, + /** + * the surface is now activated + * the surface is now activated + * + * Client window decorations should be painted as if the window + * is active. Do not assume this means that the window actually has + * keyboard or pointer focus. + */ + ZXDG_TOPLEVEL_V6_STATE_ACTIVATED = 4, +}; +#endif /* ZXDG_TOPLEVEL_V6_STATE_ENUM */ + +/** + * @ingroup iface_zxdg_toplevel_v6 + * @struct zxdg_toplevel_v6_listener + */ +struct zxdg_toplevel_v6_listener { + /** + * suggest a surface change + * + * This configure event asks the client to resize its toplevel + * surface or to change its state. The configured state should not + * be applied immediately. See xdg_surface.configure for details. + * + * The width and height arguments specify a hint to the window + * about how its surface should be resized in window geometry + * coordinates. See set_window_geometry. + * + * If the width or height arguments are zero, it means the client + * should decide its own window dimension. This may happen when the + * compositor needs to configure the state of the surface but + * doesn't have any information about any previous or expected + * dimension. + * + * The states listed in the event specify how the width/height + * arguments should be interpreted, and possibly how it should be + * drawn. + * + * Clients must send an ack_configure in response to this event. + * See xdg_surface.configure and xdg_surface.ack_configure for + * details. + */ + void (*configure)(void *data, + struct zxdg_toplevel_v6 *zxdg_toplevel_v6, + int32_t width, + int32_t height, + struct wl_array *states); + /** + * surface wants to be closed + * + * The close event is sent by the compositor when the user wants + * the surface to be closed. This should be equivalent to the user + * clicking the close button in client-side decorations, if your + * application has any. + * + * This is only a request that the user intends to close the + * window. The client may choose to ignore this request, or show a + * dialog to ask the user to save their data, etc. + */ + void (*close)(void *data, + struct zxdg_toplevel_v6 *zxdg_toplevel_v6); +}; + +/** + * @ingroup iface_zxdg_toplevel_v6 + */ +static inline int +zxdg_toplevel_v6_add_listener(struct zxdg_toplevel_v6 *zxdg_toplevel_v6, + const struct zxdg_toplevel_v6_listener *listener, void *data) +{ + return wl_proxy_add_listener((struct wl_proxy *) zxdg_toplevel_v6, + (void (**)(void)) listener, data); +} + +#define ZXDG_TOPLEVEL_V6_DESTROY 0 +#define ZXDG_TOPLEVEL_V6_SET_PARENT 1 +#define ZXDG_TOPLEVEL_V6_SET_TITLE 2 +#define ZXDG_TOPLEVEL_V6_SET_APP_ID 3 +#define ZXDG_TOPLEVEL_V6_SHOW_WINDOW_MENU 4 +#define ZXDG_TOPLEVEL_V6_MOVE 5 +#define ZXDG_TOPLEVEL_V6_RESIZE 6 +#define ZXDG_TOPLEVEL_V6_SET_MAX_SIZE 7 +#define ZXDG_TOPLEVEL_V6_SET_MIN_SIZE 8 +#define ZXDG_TOPLEVEL_V6_SET_MAXIMIZED 9 +#define ZXDG_TOPLEVEL_V6_UNSET_MAXIMIZED 10 +#define ZXDG_TOPLEVEL_V6_SET_FULLSCREEN 11 +#define ZXDG_TOPLEVEL_V6_UNSET_FULLSCREEN 12 +#define ZXDG_TOPLEVEL_V6_SET_MINIMIZED 13 + +/** + * @ingroup iface_zxdg_toplevel_v6 + */ +#define ZXDG_TOPLEVEL_V6_CONFIGURE_SINCE_VERSION 1 +/** + * @ingroup iface_zxdg_toplevel_v6 + */ +#define ZXDG_TOPLEVEL_V6_CLOSE_SINCE_VERSION 1 + +/** + * @ingroup iface_zxdg_toplevel_v6 + */ +#define ZXDG_TOPLEVEL_V6_DESTROY_SINCE_VERSION 1 +/** + * @ingroup iface_zxdg_toplevel_v6 + */ +#define ZXDG_TOPLEVEL_V6_SET_PARENT_SINCE_VERSION 1 +/** + * @ingroup iface_zxdg_toplevel_v6 + */ +#define ZXDG_TOPLEVEL_V6_SET_TITLE_SINCE_VERSION 1 +/** + * @ingroup iface_zxdg_toplevel_v6 + */ +#define ZXDG_TOPLEVEL_V6_SET_APP_ID_SINCE_VERSION 1 +/** + * @ingroup iface_zxdg_toplevel_v6 + */ +#define ZXDG_TOPLEVEL_V6_SHOW_WINDOW_MENU_SINCE_VERSION 1 +/** + * @ingroup iface_zxdg_toplevel_v6 + */ +#define ZXDG_TOPLEVEL_V6_MOVE_SINCE_VERSION 1 +/** + * @ingroup iface_zxdg_toplevel_v6 + */ +#define ZXDG_TOPLEVEL_V6_RESIZE_SINCE_VERSION 1 +/** + * @ingroup iface_zxdg_toplevel_v6 + */ +#define ZXDG_TOPLEVEL_V6_SET_MAX_SIZE_SINCE_VERSION 1 +/** + * @ingroup iface_zxdg_toplevel_v6 + */ +#define ZXDG_TOPLEVEL_V6_SET_MIN_SIZE_SINCE_VERSION 1 +/** + * @ingroup iface_zxdg_toplevel_v6 + */ +#define ZXDG_TOPLEVEL_V6_SET_MAXIMIZED_SINCE_VERSION 1 +/** + * @ingroup iface_zxdg_toplevel_v6 + */ +#define ZXDG_TOPLEVEL_V6_UNSET_MAXIMIZED_SINCE_VERSION 1 +/** + * @ingroup iface_zxdg_toplevel_v6 + */ +#define ZXDG_TOPLEVEL_V6_SET_FULLSCREEN_SINCE_VERSION 1 +/** + * @ingroup iface_zxdg_toplevel_v6 + */ +#define ZXDG_TOPLEVEL_V6_UNSET_FULLSCREEN_SINCE_VERSION 1 +/** + * @ingroup iface_zxdg_toplevel_v6 + */ +#define ZXDG_TOPLEVEL_V6_SET_MINIMIZED_SINCE_VERSION 1 + +/** @ingroup iface_zxdg_toplevel_v6 */ +static inline void +zxdg_toplevel_v6_set_user_data(struct zxdg_toplevel_v6 *zxdg_toplevel_v6, void *user_data) +{ + wl_proxy_set_user_data((struct wl_proxy *) zxdg_toplevel_v6, user_data); +} + +/** @ingroup iface_zxdg_toplevel_v6 */ +static inline void * +zxdg_toplevel_v6_get_user_data(struct zxdg_toplevel_v6 *zxdg_toplevel_v6) +{ + return wl_proxy_get_user_data((struct wl_proxy *) zxdg_toplevel_v6); +} + +static inline uint32_t +zxdg_toplevel_v6_get_version(struct zxdg_toplevel_v6 *zxdg_toplevel_v6) +{ + return wl_proxy_get_version((struct wl_proxy *) zxdg_toplevel_v6); +} + +/** + * @ingroup iface_zxdg_toplevel_v6 + * + * Unmap and destroy the window. The window will be effectively + * hidden from the user's point of view, and all state like + * maximization, fullscreen, and so on, will be lost. + */ +static inline void +zxdg_toplevel_v6_destroy(struct zxdg_toplevel_v6 *zxdg_toplevel_v6) +{ + wl_proxy_marshal_flags((struct wl_proxy *) zxdg_toplevel_v6, + ZXDG_TOPLEVEL_V6_DESTROY, NULL, wl_proxy_get_version((struct wl_proxy *) zxdg_toplevel_v6), WL_MARSHAL_FLAG_DESTROY); +} + +/** + * @ingroup iface_zxdg_toplevel_v6 + * + * Set the "parent" of this surface. This window should be stacked + * above a parent. The parent surface must be mapped as long as this + * surface is mapped. + * + * Parent windows should be set on dialogs, toolboxes, or other + * "auxiliary" surfaces, so that the parent is raised when the dialog + * is raised. + */ +static inline void +zxdg_toplevel_v6_set_parent(struct zxdg_toplevel_v6 *zxdg_toplevel_v6, struct zxdg_toplevel_v6 *parent) +{ + wl_proxy_marshal_flags((struct wl_proxy *) zxdg_toplevel_v6, + ZXDG_TOPLEVEL_V6_SET_PARENT, NULL, wl_proxy_get_version((struct wl_proxy *) zxdg_toplevel_v6), 0, parent); +} + +/** + * @ingroup iface_zxdg_toplevel_v6 + * + * Set a short title for the surface. + * + * This string may be used to identify the surface in a task bar, + * window list, or other user interface elements provided by the + * compositor. + * + * The string must be encoded in UTF-8. + */ +static inline void +zxdg_toplevel_v6_set_title(struct zxdg_toplevel_v6 *zxdg_toplevel_v6, const char *title) +{ + wl_proxy_marshal_flags((struct wl_proxy *) zxdg_toplevel_v6, + ZXDG_TOPLEVEL_V6_SET_TITLE, NULL, wl_proxy_get_version((struct wl_proxy *) zxdg_toplevel_v6), 0, title); +} + +/** + * @ingroup iface_zxdg_toplevel_v6 + * + * Set an application identifier for the surface. + * + * The app ID identifies the general class of applications to which + * the surface belongs. The compositor can use this to group multiple + * surfaces together, or to determine how to launch a new application. + * + * For D-Bus activatable applications, the app ID is used as the D-Bus + * service name. + * + * The compositor shell will try to group application surfaces together + * by their app ID. As a best practice, it is suggested to select app + * ID's that match the basename of the application's .desktop file. + * For example, "org.freedesktop.FooViewer" where the .desktop file is + * "org.freedesktop.FooViewer.desktop". + * + * See the desktop-entry specification [0] for more details on + * application identifiers and how they relate to well-known D-Bus + * names and .desktop files. + * + * [0] http://standards.freedesktop.org/desktop-entry-spec/ + */ +static inline void +zxdg_toplevel_v6_set_app_id(struct zxdg_toplevel_v6 *zxdg_toplevel_v6, const char *app_id) +{ + wl_proxy_marshal_flags((struct wl_proxy *) zxdg_toplevel_v6, + ZXDG_TOPLEVEL_V6_SET_APP_ID, NULL, wl_proxy_get_version((struct wl_proxy *) zxdg_toplevel_v6), 0, app_id); +} + +/** + * @ingroup iface_zxdg_toplevel_v6 + * + * Clients implementing client-side decorations might want to show + * a context menu when right-clicking on the decorations, giving the + * user a menu that they can use to maximize or minimize the window. + * + * This request asks the compositor to pop up such a window menu at + * the given position, relative to the local surface coordinates of + * the parent surface. There are no guarantees as to what menu items + * the window menu contains. + * + * This request must be used in response to some sort of user action + * like a button press, key press, or touch down event. + */ +static inline void +zxdg_toplevel_v6_show_window_menu(struct zxdg_toplevel_v6 *zxdg_toplevel_v6, struct wl_seat *seat, uint32_t serial, int32_t x, int32_t y) +{ + wl_proxy_marshal_flags((struct wl_proxy *) zxdg_toplevel_v6, + ZXDG_TOPLEVEL_V6_SHOW_WINDOW_MENU, NULL, wl_proxy_get_version((struct wl_proxy *) zxdg_toplevel_v6), 0, seat, serial, x, y); +} + +/** + * @ingroup iface_zxdg_toplevel_v6 + * + * Start an interactive, user-driven move of the surface. + * + * This request must be used in response to some sort of user action + * like a button press, key press, or touch down event. The passed + * serial is used to determine the type of interactive move (touch, + * pointer, etc). + * + * The server may ignore move requests depending on the state of + * the surface (e.g. fullscreen or maximized), or if the passed serial + * is no longer valid. + * + * If triggered, the surface will lose the focus of the device + * (wl_pointer, wl_touch, etc) used for the move. It is up to the + * compositor to visually indicate that the move is taking place, such as + * updating a pointer cursor, during the move. There is no guarantee + * that the device focus will return when the move is completed. + */ +static inline void +zxdg_toplevel_v6_move(struct zxdg_toplevel_v6 *zxdg_toplevel_v6, struct wl_seat *seat, uint32_t serial) +{ + wl_proxy_marshal_flags((struct wl_proxy *) zxdg_toplevel_v6, + ZXDG_TOPLEVEL_V6_MOVE, NULL, wl_proxy_get_version((struct wl_proxy *) zxdg_toplevel_v6), 0, seat, serial); +} + +/** + * @ingroup iface_zxdg_toplevel_v6 + * + * Start a user-driven, interactive resize of the surface. + * + * This request must be used in response to some sort of user action + * like a button press, key press, or touch down event. The passed + * serial is used to determine the type of interactive resize (touch, + * pointer, etc). + * + * The server may ignore resize requests depending on the state of + * the surface (e.g. fullscreen or maximized). + * + * If triggered, the client will receive configure events with the + * "resize" state enum value and the expected sizes. See the "resize" + * enum value for more details about what is required. The client + * must also acknowledge configure events using "ack_configure". After + * the resize is completed, the client will receive another "configure" + * event without the resize state. + * + * If triggered, the surface also will lose the focus of the device + * (wl_pointer, wl_touch, etc) used for the resize. It is up to the + * compositor to visually indicate that the resize is taking place, + * such as updating a pointer cursor, during the resize. There is no + * guarantee that the device focus will return when the resize is + * completed. + * + * The edges parameter specifies how the surface should be resized, + * and is one of the values of the resize_edge enum. The compositor + * may use this information to update the surface position for + * example when dragging the top left corner. The compositor may also + * use this information to adapt its behavior, e.g. choose an + * appropriate cursor image. + */ +static inline void +zxdg_toplevel_v6_resize(struct zxdg_toplevel_v6 *zxdg_toplevel_v6, struct wl_seat *seat, uint32_t serial, uint32_t edges) +{ + wl_proxy_marshal_flags((struct wl_proxy *) zxdg_toplevel_v6, + ZXDG_TOPLEVEL_V6_RESIZE, NULL, wl_proxy_get_version((struct wl_proxy *) zxdg_toplevel_v6), 0, seat, serial, edges); +} + +/** + * @ingroup iface_zxdg_toplevel_v6 + * + * Set a maximum size for the window. + * + * The client can specify a maximum size so that the compositor does + * not try to configure the window beyond this size. + * + * The width and height arguments are in window geometry coordinates. + * See xdg_surface.set_window_geometry. + * + * Values set in this way are double-buffered. They will get applied + * on the next commit. + * + * The compositor can use this information to allow or disallow + * different states like maximize or fullscreen and draw accurate + * animations. + * + * Similarly, a tiling window manager may use this information to + * place and resize client windows in a more effective way. + * + * The client should not rely on the compositor to obey the maximum + * size. The compositor may decide to ignore the values set by the + * client and request a larger size. + * + * If never set, or a value of zero in the request, means that the + * client has no expected maximum size in the given dimension. + * As a result, a client wishing to reset the maximum size + * to an unspecified state can use zero for width and height in the + * request. + * + * Requesting a maximum size to be smaller than the minimum size of + * a surface is illegal and will result in a protocol error. + * + * The width and height must be greater than or equal to zero. Using + * strictly negative values for width and height will result in the + * zxdg_shell_v6.invalid_surface_state error being raised. + */ +static inline void +zxdg_toplevel_v6_set_max_size(struct zxdg_toplevel_v6 *zxdg_toplevel_v6, int32_t width, int32_t height) +{ + wl_proxy_marshal_flags((struct wl_proxy *) zxdg_toplevel_v6, + ZXDG_TOPLEVEL_V6_SET_MAX_SIZE, NULL, wl_proxy_get_version((struct wl_proxy *) zxdg_toplevel_v6), 0, width, height); +} + +/** + * @ingroup iface_zxdg_toplevel_v6 + * + * Set a minimum size for the window. + * + * The client can specify a minimum size so that the compositor does + * not try to configure the window below this size. + * + * The width and height arguments are in window geometry coordinates. + * See xdg_surface.set_window_geometry. + * + * Values set in this way are double-buffered. They will get applied + * on the next commit. + * + * The compositor can use this information to allow or disallow + * different states like maximize or fullscreen and draw accurate + * animations. + * + * Similarly, a tiling window manager may use this information to + * place and resize client windows in a more effective way. + * + * The client should not rely on the compositor to obey the minimum + * size. The compositor may decide to ignore the values set by the + * client and request a smaller size. + * + * If never set, or a value of zero in the request, means that the + * client has no expected minimum size in the given dimension. + * As a result, a client wishing to reset the minimum size + * to an unspecified state can use zero for width and height in the + * request. + * + * Requesting a minimum size to be larger than the maximum size of + * a surface is illegal and will result in a protocol error. + * + * The width and height must be greater than or equal to zero. Using + * strictly negative values for width and height will result in the + * zxdg_shell_v6.invalid_surface_state error being raised. + */ +static inline void +zxdg_toplevel_v6_set_min_size(struct zxdg_toplevel_v6 *zxdg_toplevel_v6, int32_t width, int32_t height) +{ + wl_proxy_marshal_flags((struct wl_proxy *) zxdg_toplevel_v6, + ZXDG_TOPLEVEL_V6_SET_MIN_SIZE, NULL, wl_proxy_get_version((struct wl_proxy *) zxdg_toplevel_v6), 0, width, height); +} + +/** + * @ingroup iface_zxdg_toplevel_v6 + * + * Maximize the surface. + * + * After requesting that the surface should be maximized, the compositor + * will respond by emitting a configure event with the "maximized" state + * and the required window geometry. The client should then update its + * content, drawing it in a maximized state, i.e. without shadow or other + * decoration outside of the window geometry. The client must also + * acknowledge the configure when committing the new content (see + * ack_configure). + * + * It is up to the compositor to decide how and where to maximize the + * surface, for example which output and what region of the screen should + * be used. + * + * If the surface was already maximized, the compositor will still emit + * a configure event with the "maximized" state. + * + * Note that unrelated compositor side state changes may cause + * configure events to be emitted at any time, meaning trying to + * match this request to a specific future configure event is + * futile. + */ +static inline void +zxdg_toplevel_v6_set_maximized(struct zxdg_toplevel_v6 *zxdg_toplevel_v6) +{ + wl_proxy_marshal_flags((struct wl_proxy *) zxdg_toplevel_v6, + ZXDG_TOPLEVEL_V6_SET_MAXIMIZED, NULL, wl_proxy_get_version((struct wl_proxy *) zxdg_toplevel_v6), 0); +} + +/** + * @ingroup iface_zxdg_toplevel_v6 + * + * Unmaximize the surface. + * + * After requesting that the surface should be unmaximized, the compositor + * will respond by emitting a configure event without the "maximized" + * state. If available, the compositor will include the window geometry + * dimensions the window had prior to being maximized in the configure + * request. The client must then update its content, drawing it in a + * regular state, i.e. potentially with shadow, etc. The client must also + * acknowledge the configure when committing the new content (see + * ack_configure). + * + * It is up to the compositor to position the surface after it was + * unmaximized; usually the position the surface had before maximizing, if + * applicable. + * + * If the surface was already not maximized, the compositor will still + * emit a configure event without the "maximized" state. + * + * Note that unrelated changes in the state of compositor may cause + * configure events to be emitted by the compositor between processing + * this request and emitting corresponding configure event, so trying + * to match the request with the event is futile. + */ +static inline void +zxdg_toplevel_v6_unset_maximized(struct zxdg_toplevel_v6 *zxdg_toplevel_v6) +{ + wl_proxy_marshal_flags((struct wl_proxy *) zxdg_toplevel_v6, + ZXDG_TOPLEVEL_V6_UNSET_MAXIMIZED, NULL, wl_proxy_get_version((struct wl_proxy *) zxdg_toplevel_v6), 0); +} + +/** + * @ingroup iface_zxdg_toplevel_v6 + * + * Make the surface fullscreen. + * + * You can specify an output that you would prefer to be fullscreen. + * If this value is NULL, it's up to the compositor to choose which + * display will be used to map this surface. + * + * If the surface doesn't cover the whole output, the compositor will + * position the surface in the center of the output and compensate with + * black borders filling the rest of the output. + */ +static inline void +zxdg_toplevel_v6_set_fullscreen(struct zxdg_toplevel_v6 *zxdg_toplevel_v6, struct wl_output *output) +{ + wl_proxy_marshal_flags((struct wl_proxy *) zxdg_toplevel_v6, + ZXDG_TOPLEVEL_V6_SET_FULLSCREEN, NULL, wl_proxy_get_version((struct wl_proxy *) zxdg_toplevel_v6), 0, output); +} + +/** + * @ingroup iface_zxdg_toplevel_v6 + */ +static inline void +zxdg_toplevel_v6_unset_fullscreen(struct zxdg_toplevel_v6 *zxdg_toplevel_v6) +{ + wl_proxy_marshal_flags((struct wl_proxy *) zxdg_toplevel_v6, + ZXDG_TOPLEVEL_V6_UNSET_FULLSCREEN, NULL, wl_proxy_get_version((struct wl_proxy *) zxdg_toplevel_v6), 0); +} + +/** + * @ingroup iface_zxdg_toplevel_v6 + * + * Request that the compositor minimize your surface. There is no + * way to know if the surface is currently minimized, nor is there + * any way to unset minimization on this surface. + * + * If you are looking to throttle redrawing when minimized, please + * instead use the wl_surface.frame event for this, as this will + * also work with live previews on windows in Alt-Tab, Expose or + * similar compositor features. + */ +static inline void +zxdg_toplevel_v6_set_minimized(struct zxdg_toplevel_v6 *zxdg_toplevel_v6) +{ + wl_proxy_marshal_flags((struct wl_proxy *) zxdg_toplevel_v6, + ZXDG_TOPLEVEL_V6_SET_MINIMIZED, NULL, wl_proxy_get_version((struct wl_proxy *) zxdg_toplevel_v6), 0); +} + +#ifndef ZXDG_POPUP_V6_ERROR_ENUM +#define ZXDG_POPUP_V6_ERROR_ENUM +enum zxdg_popup_v6_error { + /** + * tried to grab after being mapped + */ + ZXDG_POPUP_V6_ERROR_INVALID_GRAB = 0, +}; +#endif /* ZXDG_POPUP_V6_ERROR_ENUM */ + +/** + * @ingroup iface_zxdg_popup_v6 + * @struct zxdg_popup_v6_listener + */ +struct zxdg_popup_v6_listener { + /** + * configure the popup surface + * + * This event asks the popup surface to configure itself given + * the configuration. The configured state should not be applied + * immediately. See xdg_surface.configure for details. + * + * The x and y arguments represent the position the popup was + * placed at given the xdg_positioner rule, relative to the upper + * left corner of the window geometry of the parent surface. + * @param x x position relative to parent surface window geometry + * @param y y position relative to parent surface window geometry + * @param width window geometry width + * @param height window geometry height + */ + void (*configure)(void *data, + struct zxdg_popup_v6 *zxdg_popup_v6, + int32_t x, + int32_t y, + int32_t width, + int32_t height); + /** + * popup interaction is done + * + * The popup_done event is sent out when a popup is dismissed by + * the compositor. The client should destroy the xdg_popup object + * at this point. + */ + void (*popup_done)(void *data, + struct zxdg_popup_v6 *zxdg_popup_v6); +}; + +/** + * @ingroup iface_zxdg_popup_v6 + */ +static inline int +zxdg_popup_v6_add_listener(struct zxdg_popup_v6 *zxdg_popup_v6, + const struct zxdg_popup_v6_listener *listener, void *data) +{ + return wl_proxy_add_listener((struct wl_proxy *) zxdg_popup_v6, + (void (**)(void)) listener, data); +} + +#define ZXDG_POPUP_V6_DESTROY 0 +#define ZXDG_POPUP_V6_GRAB 1 + +/** + * @ingroup iface_zxdg_popup_v6 + */ +#define ZXDG_POPUP_V6_CONFIGURE_SINCE_VERSION 1 +/** + * @ingroup iface_zxdg_popup_v6 + */ +#define ZXDG_POPUP_V6_POPUP_DONE_SINCE_VERSION 1 + +/** + * @ingroup iface_zxdg_popup_v6 + */ +#define ZXDG_POPUP_V6_DESTROY_SINCE_VERSION 1 +/** + * @ingroup iface_zxdg_popup_v6 + */ +#define ZXDG_POPUP_V6_GRAB_SINCE_VERSION 1 + +/** @ingroup iface_zxdg_popup_v6 */ +static inline void +zxdg_popup_v6_set_user_data(struct zxdg_popup_v6 *zxdg_popup_v6, void *user_data) +{ + wl_proxy_set_user_data((struct wl_proxy *) zxdg_popup_v6, user_data); +} + +/** @ingroup iface_zxdg_popup_v6 */ +static inline void * +zxdg_popup_v6_get_user_data(struct zxdg_popup_v6 *zxdg_popup_v6) +{ + return wl_proxy_get_user_data((struct wl_proxy *) zxdg_popup_v6); +} + +static inline uint32_t +zxdg_popup_v6_get_version(struct zxdg_popup_v6 *zxdg_popup_v6) +{ + return wl_proxy_get_version((struct wl_proxy *) zxdg_popup_v6); +} + +/** + * @ingroup iface_zxdg_popup_v6 + * + * This destroys the popup. Explicitly destroying the xdg_popup + * object will also dismiss the popup, and unmap the surface. + * + * If this xdg_popup is not the "topmost" popup, a protocol error + * will be sent. + */ +static inline void +zxdg_popup_v6_destroy(struct zxdg_popup_v6 *zxdg_popup_v6) +{ + wl_proxy_marshal_flags((struct wl_proxy *) zxdg_popup_v6, + ZXDG_POPUP_V6_DESTROY, NULL, wl_proxy_get_version((struct wl_proxy *) zxdg_popup_v6), WL_MARSHAL_FLAG_DESTROY); +} + +/** + * @ingroup iface_zxdg_popup_v6 + * + * This request makes the created popup take an explicit grab. An explicit + * grab will be dismissed when the user dismisses the popup, or when the + * client destroys the xdg_popup. This can be done by the user clicking + * outside the surface, using the keyboard, or even locking the screen + * through closing the lid or a timeout. + * + * If the compositor denies the grab, the popup will be immediately + * dismissed. + * + * This request must be used in response to some sort of user action like a + * button press, key press, or touch down event. The serial number of the + * event should be passed as 'serial'. + * + * The parent of a grabbing popup must either be an xdg_toplevel surface or + * another xdg_popup with an explicit grab. If the parent is another + * xdg_popup it means that the popups are nested, with this popup now being + * the topmost popup. + * + * Nested popups must be destroyed in the reverse order they were created + * in, e.g. the only popup you are allowed to destroy at all times is the + * topmost one. + * + * When compositors choose to dismiss a popup, they may dismiss every + * nested grabbing popup as well. When a compositor dismisses popups, it + * will follow the same dismissing order as required from the client. + * + * The parent of a grabbing popup must either be another xdg_popup with an + * active explicit grab, or an xdg_popup or xdg_toplevel, if there are no + * explicit grabs already taken. + * + * If the topmost grabbing popup is destroyed, the grab will be returned to + * the parent of the popup, if that parent previously had an explicit grab. + * + * If the parent is a grabbing popup which has already been dismissed, this + * popup will be immediately dismissed. If the parent is a popup that did + * not take an explicit grab, an error will be raised. + * + * During a popup grab, the client owning the grab will receive pointer + * and touch events for all their surfaces as normal (similar to an + * "owner-events" grab in X11 parlance), while the top most grabbing popup + * will always have keyboard focus. + */ +static inline void +zxdg_popup_v6_grab(struct zxdg_popup_v6 *zxdg_popup_v6, struct wl_seat *seat, uint32_t serial) +{ + wl_proxy_marshal_flags((struct wl_proxy *) zxdg_popup_v6, + ZXDG_POPUP_V6_GRAB, NULL, wl_proxy_get_version((struct wl_proxy *) zxdg_popup_v6), 0, seat, serial); +} + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/wayland/java/WindowWayland.java b/wayland/java/WindowWayland.java new file mode 100644 index 00000000..0fa0ce65 --- /dev/null +++ b/wayland/java/WindowWayland.java @@ -0,0 +1,235 @@ +package io.github.humbleui.jwm; + +import java.io.*; +import java.util.concurrent.*; +import java.util.function.*; +import lombok.*; +import org.jetbrains.annotations.*; +import io.github.humbleui.jwm.*; +import io.github.humbleui.jwm.impl.*; +import io.github.humbleui.types.*; + +public class WindowWayland extends Window { + @ApiStatus.Internal + public WindowWayland() { + super(_nMake()); + } + + @Override + public Window setTextInputEnabled(boolean enabled) { + assert _onUIThread() : "Should be run on UI thread"; + // TODO: impl me + return this; + } + + @Override + public void unmarkText() { + assert _onUIThread() : "Should be run on UI thread"; + // TODO: impl me! + } + + @Override + public IRect getWindowRect() { + assert _onUIThread() : "Should be run on UI thread"; + return _nGetWindowRect(); + } + + @Override + public IRect getContentRect() { + assert _onUIThread() : "Should be run on UI thread"; + return _nGetContentRect(); + } + + @Override + public Window setWindowPosition(int left, int top) { + assert _onUIThread() : "Should be run on UI thread"; + // _nSetWindowPosition(left, top); + // Unsupported under wayland rn + return this; + } + + @Override + public Window setWindowSize(int width, int height) { + assert _onUIThread() : "Should be run on UI thread"; + // _nSetWindowSize(width, height); + // Possibly unsupported? Hinting is possibly supported + return this; + } + + @Override + public Window setContentSize(int width, int height) { + assert _onUIThread() : "Should be run on UI thread"; + // _nSetContentSize(width, height); + // Possibly unsupported + return this; + } + + @Override + public Window setTitle(String title) { + assert _onUIThread() : "Should be run on UI thread"; + try { + _nSetTitle(title.getBytes("UTF-8")); + } catch (UnsupportedEncodingException ignored) {} + return this; + } + + @Override + public Window setIcon(File icon) { + // TODO #95 + return this; + } + + @Override + public Window setTitlebarVisible(boolean value) { + _nSetTitlebarVisible(value); + return this; + } + + @Override + public Window setVisible(boolean isVisible) { + assert _onUIThread() : "Should be run on UI thread"; + _nSetVisible(isVisible); + return super.setVisible(true); + } + + @Override + public Window hideMouseCursorUntilMoved(boolean value) { + // TODO impl me! + return this; + } + + @Override + public Window lockMouseCursor(boolean value) { + // TODO impl me! + return this; + } + + @Override + public Window setOpacity(float opacity) { + // TODO: impl me! + return this; + } + + @Override + public float getOpacity(){ + throw new UnsupportedOperationException("impl me!"); + } + + @Override + public Screen getScreen() { + assert _onUIThread() : "Should be run on UI thread"; + return _nGetScreen(); + } + + @Override + public void requestFrame() { + if (!isClosed()) { + App.runOnUIThread(() -> { + if (!isClosed()) { + _nRequestFrame(); + } + }); + } + } + + @Override + public void close() { + assert _onUIThread() && !isClosed(); + _nClose(); + super.close(); + } + + @Override + public Window maximize() { + _nMaximize(); + return this; + } + + @Override + public Window minimize() { + _nMinimize(); + return this; + } + + @Override + public Window focus() { + assert _onUIThread() : "Should be run on UI thread"; + // TODO implement + return this; + } + + @Override + public Window bringToFront() { + assert _onUIThread() : "Should be run on UI thread"; + // TODO implement + return this; + } + + @Override + public boolean isFront() { + assert _onUIThread() : "Should be run on UI thread"; + // TODO: impl me + return false; + } + + @Override + public ZOrder getZOrder() { + assert _onUIThread() : "Should be run on UI thread"; + return ZOrder.NORMAL; + } + + @Override + public Window setZOrder(ZOrder order) { + assert _onUIThread() : "Should be run on UI thread"; + // TODO implement + return this; + } + + @Override + public float getProgressBar() { + throw new UnsupportedOperationException("impl me!"); + } + + @Override + public Window setProgressBar(float progress) { + throw new UnsupportedOperationException("impl me!"); + } + + @Override + public Window restore() { + _nRestore(); + return this; + } + + @Override + public Window setFullScreen(boolean value) { + assert _onUIThread() : "Should be run on UI thread"; + _nSetFullScreen(value); + return this; + } + + @Override + public boolean isFullScreen() { + assert _onUIThread() : "Should be run on UI thread"; + return _nIsFullScreen(); + } + + @ApiStatus.Internal public static native long _nMake(); + @ApiStatus.Internal public native void _nSetVisible(boolean isVisible); + @ApiStatus.Internal public native IRect _nGetWindowRect(); + @ApiStatus.Internal public native IRect _nGetContentRect(); + // @ApiStatus.Internal public native void _nSetWindowPosition(int left, int top); + // @ApiStatus.Internal public native void _nSetWindowSize(int width, int height); + @ApiStatus.Internal public native void _nSetMouseCursor(int cursorId); + // @ApiStatus.Internal public native void _nSetContentSize(int width, int height); + @ApiStatus.Internal public native Screen _nGetScreen(); + @ApiStatus.Internal public native void _nRequestFrame(); + @ApiStatus.Internal public native void _nClose(); + @ApiStatus.Internal public native void _nMaximize(); + @ApiStatus.Internal public native void _nMinimize(); + @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(); +} diff --git a/linux/CMakeLists.txt b/x11/CMakeLists.txt similarity index 91% rename from linux/CMakeLists.txt rename to x11/CMakeLists.txt index ef67fc66..e50d48e5 100644 --- a/linux/CMakeLists.txt +++ b/x11/CMakeLists.txt @@ -33,9 +33,9 @@ if (NOT JAVA_HOME) endif() endif() -target_include_directories(jwm PRIVATE ${CMAKE_CURRENT_LIST_DIR}/../shared/cc ${JAVA_HOME}/include ${JAVA_HOME}/include/linux) +target_include_directories(jwm PRIVATE ${CMAKE_CURRENT_LIST_DIR}/../shared/cc ../linux/cc ${JAVA_HOME}/include ${JAVA_HOME}/include/linux) set_target_properties(jwm PROPERTIES OUTPUT_NAME "jwm_${JWM_ARCH}") target_link_libraries(jwm PRIVATE X11::X11 X11::Xrandr X11::Xcursor X11::Xi) -target_link_libraries(jwm PRIVATE OpenGL::GL) \ No newline at end of file +target_link_libraries(jwm PRIVATE OpenGL::GL) diff --git a/x11/cc/AppX11.cc b/x11/cc/AppX11.cc new file mode 100644 index 00000000..9a1f03ec --- /dev/null +++ b/x11/cc/AppX11.cc @@ -0,0 +1,126 @@ +#include +#include "AppX11.hh" +#include +#include +#include +#include +#include +#include + +jwm::AppX11 jwm::app; + + +float jwm::AppX11::getScale() { + char *resourceString = XResourceManagerString(wm.display); + XrmDatabase db; + XrmValue value; + char *type = NULL; + + static struct once { + once() { + XrmInitialize(); + } + } once; + + if (resourceString) { + db = XrmGetStringDatabase(resourceString); + if (XrmGetResource(db, "Xft.dpi", "String", &type, &value)) { + if (value.addr) { + return atof(value.addr) / 96.f; + } + } + } + + return 1.f; +} + +void jwm::AppX11::init(JNIEnv* jniEnv) { + _jniEnv = jniEnv; +} + +void jwm::AppX11::start() { + wm.runLoop(); +} + +void jwm::AppX11::terminate() { + wm.terminate(); +} + +const std::vector& jwm::AppX11::getScreens() { + if (_screens.empty()) { + Display* display = getWindowManager().getDisplay(); + XRRScreenResources* resources = XRRGetScreenResources(display, getWindowManager().getRootWindow()); + RROutput primaryOutput = XRRGetOutputPrimary(display, getWindowManager().getRootWindow()); + int count = resources->ncrtc; + int primaryIdx = 0; + + float dpi = jwm::app.getScale(); + + for (int i = 0; i < count; ++i) { + XRRCrtcInfo* info = XRRGetCrtcInfo(display, resources, resources->crtcs[i]); + // skip empty monitors + if (info->width != 0) { + for (int j = 0; j < info->noutput; ++j) { + RROutput output = info->outputs[j]; + if (output == primaryOutput) { + primaryIdx = _screens.size(); + break; + } + } + + auto bounds = jwm::IRect::makeXYWH(info->x, info->y, info->width, info->height); + + ScreenInfo myScreenInfo = { + long(info->outputs[0]), + bounds, + false + }; + _screens.push_back(myScreenInfo); + } + + XRRFreeCrtcInfo(info); + } + XRRFreeScreenResources(resources); + _screens[primaryIdx].isPrimary = true; + } + return _screens; +} + +// JNI + +extern "C" JNIEXPORT void JNICALL Java_io_github_humbleui_jwm_App__1nStart(JNIEnv* env, jclass jclass, jobject launcher) { + jwm::app.init(env); + jwm::classes::Runnable::run(env, launcher); + jwm::app.start(); +} + +extern "C" JNIEXPORT void JNICALL Java_io_github_humbleui_jwm_App__1nTerminate(JNIEnv* env, jclass jclass) { + jwm::app.terminate(); +} + +extern"C" JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void*) { + return JNI_VERSION_1_2; +} + + +extern "C" JNIEXPORT void JNICALL Java_io_github_humbleui_jwm_App__1nRunOnUIThread + (JNIEnv* env, jclass cls, jobject callback) { + jobject callbackRef = env->NewGlobalRef(callback); + jwm::app.getWindowManager().enqueueTask([callbackRef] { + jwm::classes::Runnable::run(jwm::app.getJniEnv(), callbackRef); + jwm::app.getJniEnv()->DeleteGlobalRef(callbackRef); + }); +} + + +extern "C" JNIEXPORT jobjectArray JNICALL Java_io_github_humbleui_jwm_App__1nGetScreens(JNIEnv* env, jobject cls) noexcept { + auto& screens = jwm::app.getScreens(); + jobjectArray array = env->NewObjectArray(screens.size(), jwm::classes::Screen::kCls, 0); + float scale = jwm::app.getScale(); + size_t index = 0; + for (auto& screen : screens) { + env->SetObjectArrayElement(array, index++, screen.asJavaObject(env)); + } + + return array; +} diff --git a/linux/cc/AppX11.hh b/x11/cc/AppX11.hh similarity index 100% rename from linux/cc/AppX11.hh rename to x11/cc/AppX11.hh diff --git a/x11/cc/ClipboardX11.cc b/x11/cc/ClipboardX11.cc new file mode 100644 index 00000000..fd8c23d7 --- /dev/null +++ b/x11/cc/ClipboardX11.cc @@ -0,0 +1,125 @@ +#include +#include +#include +#include +#include "AppX11.hh" +#include + +namespace jwm { + class ClipboardX11 { + public: + static ClipboardX11& inst() { + static ClipboardX11 s; + return s; + } + + jobjectArray getFormats(JNIEnv* env) const { + auto formats = jwm::app.getWindowManager().getClipboardFormats(); + if (formats.empty()) { + return nullptr; + } + + std::vector formatObjs; + + for (auto& format : formats) { + auto js = StringUTF16(format.c_str()).toJString(env); + formatObjs.push_back(classes::Clipboard::registerFormat(env, js.get())); + } + jobjectArray jniFormats = env->NewObjectArray(static_cast(formats.size()), classes::ClipboardFormat::kCls, nullptr); + + // fill java array + for (jsize i = 0; i < static_cast(formatObjs.size()); ++i) { + env->SetObjectArrayElement(jniFormats, i, formatObjs[i]); + } + + return jniFormats; + } + + jobject get(JNIEnv* env, jobjectArray formats) { + jsize formatsSize = env->GetArrayLength(formats); + for (jsize i = 0; i < formatsSize; ++i) { + jobject format = env->GetObjectArrayElement(formats, i); + if (format) { + jwm::StringUTF16 formatId = jwm::StringUTF16::makeFromJString(env, classes::ClipboardFormat::getFormatId(env, format)); + + + ByteBuf contents; + // HACK: prefer UTF8_STRING over text/plain and convert it to utf16 + if (formatId == "text/plain") { + contents = app.getWindowManager().getClipboardContents("UTF8_STRING"); + } + // TODO add another formats + if (contents.empty()) { + return nullptr; + } + JNILocal data(env, env->NewByteArray(static_cast(contents.size()))); + jbyte* bytes = env->GetByteArrayElements(data.get(), nullptr); + std::memcpy(bytes, contents.data(), contents.size()); + + env->ReleaseByteArrayElements(data.get(), bytes, 0); + + + JNILocal entry(env, classes::ClipboardEntry::make(env, format, data.get())); + return env->NewGlobalRef(entry.get()); + } + } + classes::Throwable::exceptionThrown(env); + return nullptr; + } + + + void set(JNIEnv* env, jobjectArray entries) { + jsize size = env->GetArrayLength(entries); + std::map contents; + for (jsize i = 0; i < size; ++i) { + jobject entry = env->GetObjectArrayElement(entries, i); + + if (entry) { + jobject format = classes::ClipboardEntry::getFormat(env, entry); + jbyteArray data = classes::ClipboardEntry::getData(env, entry); + jsize dataSize = env->GetArrayLength(data); + + StringUTF16 formatId = StringUTF16::makeFromJString(env, classes::ClipboardFormat::getFormatId(env, format)); + + ByteBuf resultBuffer; + { + jbyte* dataBytes = env->GetByteArrayElements(data, nullptr); + resultBuffer.insert(resultBuffer.end(), dataBytes, dataBytes + dataSize); + env->ReleaseByteArrayElements(data, dataBytes, JNI_ABORT); + } + + contents[formatId.toAscii()] = std::move(resultBuffer); + } + } + jwm::app.getWindowManager().setClipboardContents(std::move(contents)); + } + }; +} + + +// JNI + +extern "C" JNIEXPORT void JNICALL Java_io_github_humbleui_jwm_Clipboard__1nSet + (JNIEnv* env, jclass jclass, jobjectArray entries) { + return jwm::ClipboardX11::inst().set(env, entries); +} + +extern "C" JNIEXPORT jobject JNICALL Java_io_github_humbleui_jwm_Clipboard__1nGet + (JNIEnv* env, jclass jclass, jobjectArray formats) { + return jwm::ClipboardX11::inst().get(env, formats); +} + +extern "C" JNIEXPORT jobjectArray JNICALL Java_io_github_humbleui_jwm_Clipboard__1nGetFormats + (JNIEnv* env, jclass jclass) { + return jwm::ClipboardX11::inst().getFormats(env); +} + +extern "C" JNIEXPORT void JNICALL Java_io_github_humbleui_jwm_Clipboard__1nClear + (JNIEnv* env, jclass jclass) { + +} + +extern "C" JNIEXPORT jboolean JNICALL Java_io_github_humbleui_jwm_Clipboard__1nRegisterFormat + (JNIEnv* env, jclass jclass, jstring formatId) { + return true; +} \ No newline at end of file diff --git a/x11/cc/KeyX11.cc b/x11/cc/KeyX11.cc new file mode 100644 index 00000000..9a638c69 --- /dev/null +++ b/x11/cc/KeyX11.cc @@ -0,0 +1,178 @@ +#include "KeyX11.hh" +#include +#include +#include +#include +#include "KeyModifier.hh" + + +bool gKeyStates[(size_t) jwm::Key::_KEY_COUNT] = {0}; + +bool jwm::KeyX11::getKeyState(jwm::Key key) { + return gKeyStates[(size_t) key]; +} + +void jwm::KeyX11::setKeyState(jwm::Key key, bool isDown) { + gKeyStates[(size_t) key] = isDown; +} + +int jwm::KeyX11::getModifiers() { + int m = 0; + + if (getKeyState(jwm::Key::SHIFT )) m |= (int)jwm::KeyModifier::SHIFT; + if (getKeyState(jwm::Key::CONTROL )) m |= (int)jwm::KeyModifier::CONTROL; + if (getKeyState(jwm::Key::ALT )) m |= (int)jwm::KeyModifier::ALT; + if (getKeyState(jwm::Key::LINUX_META )) m |= (int)jwm::KeyModifier::LINUX_META; + if (getKeyState(jwm::Key::LINUX_SUPER)) m |= (int)jwm::KeyModifier::LINUX_SUPER; + + return m; +} + +int jwm::KeyX11::getModifiersFromMask(int mask) { + int m = getModifiers(); + + if (mask & ShiftMask ) m |= (int)jwm::KeyModifier::SHIFT; + if (mask & ControlMask) m |= (int)jwm::KeyModifier::CONTROL; + if (mask & Mod1Mask ) m |= (int)jwm::KeyModifier::ALT; + + return m; +} + +jwm::Key jwm::KeyX11::fromNative(uint32_t v) { + switch (v) { + // Modifiers + case XK_Caps_Lock: return Key::CAPS_LOCK; + case XK_Shift_R: + case XK_Shift_L: return Key::SHIFT; + case XK_Control_R: + case XK_Control_L: return Key::CONTROL; + case XK_Alt_R: + case XK_Alt_L: return Key::ALT; + // Key::WIN_LOGO + case XK_Super_L: + case XK_Super_R: return Key::LINUX_SUPER; + case XK_Meta_L: + case XK_Meta_R: return Key::LINUX_META; + // Key::MAC_COMMAND + // Key::MAC_OPTION + // Key::MAC_FN + + // Rest of the keys + case XK_Return: return Key::ENTER; + case XK_BackSpace: return Key::BACKSPACE; + case XK_Tab: return Key::TAB; + case XK_Cancel: return Key::CANCEL; + case XK_Clear: return Key::CLEAR; + case XK_Pause: return Key::PAUSE; + case XK_Escape: return Key::ESCAPE; + case XK_space: return Key::SPACE; + case XK_Page_Up: return Key::PAGE_UP; + case XK_Page_Down: return Key::PAGE_DOWN; + case XK_End: return Key::END; + case XK_Home: return Key::HOME; + case XK_Left: return Key::LEFT; + case XK_Up: return Key::UP; + case XK_Right: return Key::RIGHT; + case XK_Down: return Key::DOWN; + case XK_comma: return Key::COMMA; + case XK_minus: return Key::MINUS; + case XK_period: return Key::PERIOD; + case XK_slash: return Key::SLASH; + case XK_0: return Key::DIGIT0; + case XK_1: return Key::DIGIT1; + case XK_2: return Key::DIGIT2; + case XK_3: return Key::DIGIT3; + case XK_4: return Key::DIGIT4; + case XK_5: return Key::DIGIT5; + case XK_6: return Key::DIGIT6; + case XK_7: return Key::DIGIT7; + case XK_8: return Key::DIGIT8; + case XK_9: return Key::DIGIT9; + case XK_semicolon: return Key::SEMICOLON; + case XK_equal: return Key::EQUALS; + case XK_a: return Key::A; + case XK_b: return Key::B; + case XK_c: return Key::C; + case XK_d: return Key::D; + case XK_e: return Key::E; + case XK_f: return Key::F; + case XK_g: return Key::G; + case XK_h: return Key::H; + case XK_i: return Key::I; + case XK_j: return Key::J; + case XK_k: return Key::K; + case XK_l: return Key::L; + case XK_m: return Key::M; + case XK_n: return Key::N; + case XK_o: return Key::O; + case XK_p: return Key::P; + case XK_q: return Key::Q; + case XK_r: return Key::R; + case XK_s: return Key::S; + case XK_t: return Key::T; + case XK_u: return Key::U; + case XK_v: return Key::V; + case XK_w: return Key::W; + case XK_x: return Key::X; + case XK_y: return Key::Y; + case XK_z: return Key::Z; + case XK_bracketleft: return Key::OPEN_BRACKET; + case XK_backslash: return Key::BACK_SLASH; + case XK_bracketright: return Key::CLOSE_BRACKET; + case XK_KP_0: return Key::DIGIT0; + case XK_KP_1: return Key::DIGIT1; + case XK_KP_2: return Key::DIGIT2; + case XK_KP_3: return Key::DIGIT3; + case XK_KP_4: return Key::DIGIT4; + case XK_KP_5: return Key::DIGIT5; + case XK_KP_6: return Key::DIGIT6; + case XK_KP_7: return Key::DIGIT7; + case XK_KP_8: return Key::DIGIT8; + case XK_KP_9: return Key::DIGIT9; + case XK_multiply: return Key::MULTIPLY; + case XK_KP_Add: return Key::ADD; + case XK_KP_Separator: return Key::SEPARATOR; + case XK_KP_Subtract: return Key::MINUS; + case XK_KP_Decimal: return Key::PERIOD; + case XK_KP_Divide: return Key::SLASH; + case XK_KP_Delete: return Key::DEL; + case XK_Delete: return Key::DEL; + case XK_Num_Lock: return Key::NUM_LOCK; + case XK_Scroll_Lock: return Key::SCROLL_LOCK; + case XK_F1: return Key::F1; + case XK_F2: return Key::F2; + case XK_F3: return Key::F3; + case XK_F4: return Key::F4; + case XK_F5: return Key::F5; + case XK_F6: return Key::F6; + case XK_F7: return Key::F7; + case XK_F8: return Key::F8; + case XK_F9: return Key::F9; + case XK_F10: return Key::F10; + case XK_F11: return Key::F11; + case XK_F12: return Key::F12; + case XK_F13: return Key::F13; + case XK_F14: return Key::F14; + case XK_F15: return Key::F15; + case XK_F16: return Key::F16; + case XK_F17: return Key::F17; + case XK_F18: return Key::F18; + case XK_F19: return Key::F19; + case XK_F20: return Key::F20; + case XK_F21: return Key::F21; + case XK_F22: return Key::F22; + case XK_F23: return Key::F23; + case XK_F24: return Key::F24; + case XK_Print: return Key::PRINTSCREEN; + case XK_Insert: return Key::INSERT; + case XK_Help: return Key::HELP; + case XK_grave: return Key::BACK_QUOTE; + case XK_quoteright: return Key::QUOTE; + case XK_Menu: return Key::MENU; + // Key::KANA + // Key::VOLUME_UP + // Key::VOLUME_DOWN + // Key::MUTE + default: return Key::UNDEFINED; + } +} \ No newline at end of file diff --git a/x11/cc/KeyX11.hh b/x11/cc/KeyX11.hh new file mode 100644 index 00000000..ce03b165 --- /dev/null +++ b/x11/cc/KeyX11.hh @@ -0,0 +1,14 @@ +#pragma once + +#include +#include "Key.hh" + +namespace jwm { + namespace KeyX11 { + jwm::Key fromNative(uint32_t v); + bool getKeyState(jwm::Key key); + void setKeyState(jwm::Key key, bool isDown); + int getModifiers(); + int getModifiersFromMask(int mask); + } +} \ No newline at end of file diff --git a/linux/cc/LayerGL.cc b/x11/cc/LayerGLX11.cc similarity index 100% rename from linux/cc/LayerGL.cc rename to x11/cc/LayerGLX11.cc diff --git a/x11/cc/LayerRasterX11.cc b/x11/cc/LayerRasterX11.cc new file mode 100644 index 00000000..2ec70c85 --- /dev/null +++ b/x11/cc/LayerRasterX11.cc @@ -0,0 +1,123 @@ +// JNI + +#include +#include "impl/Library.hh" +#include "impl/RefCounted.hh" +#include "WindowX11.hh" + +namespace jwm { + class LayerRaster: public RefCounted, public ILayer { + public: + WindowX11* fWindow; + size_t _width = 0, _height = 0; + XImage* _xImage = nullptr; + GC _graphicsContext; + VSync _vsync = VSYNC_ENABLED; + + /** + * Using raw pointer here because XImage frees this buffer on XDestroyImage. + */ + uint8_t* _imageData = nullptr; + + LayerRaster() = default; + virtual ~LayerRaster() = default; + + void attach(WindowX11* window) { + fWindow = jwm::ref(window); + fWindow->setLayer(this); + + Display* d = fWindow->_windowManager.getDisplay(); + _graphicsContext = DefaultGC(d, DefaultScreen(d)); + } + + void resize(int width, int height) { + Display* d = fWindow->_windowManager.getDisplay(); + _width = width; + _height = height; + if (_xImage) { + XDestroyImage(_xImage); + } + _imageData = new uint8_t[width * height * sizeof(uint32_t)]; + _xImage = XCreateImage(d, CopyFromParent, DefaultDepth(d, DefaultScreen(d)), ZPixmap, 0, (char*)_imageData, width, height, 32, 0); + XInitImage(_xImage); + _xImage->byte_order = _xImage->bitmap_bit_order = LSBFirst; + } + + const void* getPixelsPtr() const { + return _imageData; + } + + int getRowBytes() const { + return _width * sizeof(uint32_t); + } + + void swapBuffers() { + XPutImage(fWindow->_windowManager.getDisplay(), fWindow->_x11Window, _graphicsContext, _xImage, 0, 0, 0, 0, _width, _height); + } + + void close() override { + if (_xImage) { + XDestroyImage(_xImage); + _xImage = nullptr; + } + jwm::unref(&fWindow); + } + + void makeCurrentForced() override { + ILayer::makeCurrentForced(); + } + + void setVsyncMode(VSync v) override { + _vsync = v; + } + }; +} + + +extern "C" JNIEXPORT jlong JNICALL Java_io_github_humbleui_jwm_LayerRaster__1nMake + (JNIEnv* env, jclass jclass) { + jwm::LayerRaster* instance = new jwm::LayerRaster; + return reinterpret_cast(instance); +} + +extern "C" JNIEXPORT void JNICALL Java_io_github_humbleui_jwm_LayerRaster__1nAttach + (JNIEnv* env, jobject obj, jobject windowObj) { + jwm::LayerRaster* instance = reinterpret_cast(jwm::classes::Native::fromJava(env, obj)); + jwm::WindowX11* window = reinterpret_cast(jwm::classes::Native::fromJava(env, windowObj)); + instance->attach(window); +} + +extern "C" JNIEXPORT void JNICALL Java_io_github_humbleui_jwm_LayerRaster__1nReconfigure + (JNIEnv* env, jobject obj) { + +} + +extern "C" JNIEXPORT void JNICALL Java_io_github_humbleui_jwm_LayerRaster__1nResize + (JNIEnv* env, jobject obj, jint width, jint height) { + jwm::LayerRaster* instance = reinterpret_cast(jwm::classes::Native::fromJava(env, obj)); + instance->resize(width, height); +} + +extern "C" JNIEXPORT void JNICALL Java_io_github_humbleui_jwm_LayerRaster__1nSwapBuffers + (JNIEnv* env, jobject obj) { + jwm::LayerRaster* instance = reinterpret_cast(jwm::classes::Native::fromJava(env, obj)); + instance->swapBuffers(); +} + +extern "C" JNIEXPORT void JNICALL Java_io_github_humbleui_jwm_LayerRaster__1nClose + (JNIEnv* env, jobject obj) { + jwm::LayerRaster* instance = reinterpret_cast(jwm::classes::Native::fromJava(env, obj)); + instance->close(); +} + +extern "C" JNIEXPORT jlong JNICALL Java_io_github_humbleui_jwm_LayerRaster__1nGetPixelsPtr + (JNIEnv* env, jobject obj) { + jwm::LayerRaster* instance = reinterpret_cast(jwm::classes::Native::fromJava(env, obj)); + return reinterpret_cast(instance->getPixelsPtr()); +} + +extern "C" JNIEXPORT jlong JNICALL Java_io_github_humbleui_jwm_LayerRaster__1nGetRowBytes + (JNIEnv* env, jobject obj) { + jwm::LayerRaster* instance = reinterpret_cast(jwm::classes::Native::fromJava(env, obj)); + return static_cast(instance->getRowBytes()); +} \ No newline at end of file diff --git a/x11/cc/MouseButtonX11.cc b/x11/cc/MouseButtonX11.cc new file mode 100644 index 00000000..c1aa2c36 --- /dev/null +++ b/x11/cc/MouseButtonX11.cc @@ -0,0 +1,25 @@ +#include "MouseButtonX11.hh" + + +jwm::MouseButton jwm::MouseButtonX11::fromNative(uint32_t v) { + switch (v) { + case 1: return jwm::MouseButton::PRIMARY; + case 2: return jwm::MouseButton::MIDDLE; + case 3: return jwm::MouseButton::SECONDARY; + case 8: return jwm::MouseButton::BACK; + case 9: return jwm::MouseButton::FORWARD; + } + return jwm::MouseButton::PRIMARY; +} + +bool jwm::MouseButtonX11::isButton(uint32_t v) { + return v < 4 || v > 7; // mouse wheel buttons +} + +int jwm::MouseButtonX11::fromNativeMask(unsigned v) { + int res = 0; + if (v & 0x100) res |= int(jwm::MouseButton::PRIMARY); + if (v & 0x400) res |= int(jwm::MouseButton::SECONDARY); + if (v & 0x200) res |= int(jwm::MouseButton::MIDDLE); + return res; +} \ No newline at end of file diff --git a/x11/cc/MouseButtonX11.hh b/x11/cc/MouseButtonX11.hh new file mode 100644 index 00000000..898edda7 --- /dev/null +++ b/x11/cc/MouseButtonX11.hh @@ -0,0 +1,12 @@ +#pragma once + +#include +#include "MouseButton.hh" + +namespace jwm { + namespace MouseButtonX11 { + MouseButton fromNative(uint32_t v); + int fromNativeMask(unsigned v); + bool isButton(uint32_t v); + } +} \ No newline at end of file diff --git a/linux/cc/WindowManagerX11.cc b/x11/cc/WindowManagerX11.cc similarity index 100% rename from linux/cc/WindowManagerX11.cc rename to x11/cc/WindowManagerX11.cc diff --git a/linux/cc/WindowManagerX11.hh b/x11/cc/WindowManagerX11.hh similarity index 100% rename from linux/cc/WindowManagerX11.hh rename to x11/cc/WindowManagerX11.hh diff --git a/linux/cc/WindowX11.cc b/x11/cc/WindowX11.cc similarity index 100% rename from linux/cc/WindowX11.cc rename to x11/cc/WindowX11.cc diff --git a/linux/cc/WindowX11.hh b/x11/cc/WindowX11.hh similarity index 100% rename from linux/cc/WindowX11.hh rename to x11/cc/WindowX11.hh diff --git a/linux/cc/WindowX11MotifHints.hh b/x11/cc/WindowX11MotifHints.hh similarity index 100% rename from linux/cc/WindowX11MotifHints.hh rename to x11/cc/WindowX11MotifHints.hh diff --git a/linux/java/WindowX11.java b/x11/java/WindowX11.java similarity index 100% rename from linux/java/WindowX11.java rename to x11/java/WindowX11.java From f3ec23129814be60baacbb86bb5dff6541e5528b Mon Sep 17 00:00:00 2001 From: BulbyVR <26726264+TheDrawingCoder-Gamer@users.noreply.github.com> Date: Thu, 30 Nov 2023 21:15:32 -0500 Subject: [PATCH 02/95] we've all sinned. some worse than others. --- macos/java/WindowMac.java | 5 + script/build.py | 2 +- shared/cc/StringUTF16.cc | 2 +- shared/cc/StringUTF16.hh | 1 + shared/java/Window.java | 1 + wayland/CMakeLists.txt | 15 +- wayland/cc/AppWayland.cc | 92 +- wayland/cc/AppWayland.hh | 14 +- wayland/cc/ClipboardWayland.cc | 28 +- wayland/cc/KeyWayland.cc | 23 +- wayland/cc/KeyWayland.hh | 4 +- wayland/cc/LayerGLWayland.cc | 76 +- wayland/cc/LayerRasterWayland.cc | 53 +- wayland/cc/MouseButtonWayland.cc | 22 +- wayland/cc/MouseButtonWayland.hh | 4 +- wayland/cc/ScreenInfo.cc | 5 + {linux => wayland}/cc/ScreenInfo.hh | 0 wayland/cc/ShmPool.cc | 92 ++ wayland/cc/ShmPool.hh | 25 + wayland/cc/WindowManagerWayland.cc | 232 ++- wayland/cc/WindowManagerWayland.hh | 26 +- wayland/cc/WindowWayland.cc | 492 ++---- wayland/cc/WindowWayland.hh | 9 +- wayland/cc/xdg-shell.c | 172 -- wayland/cc/xdg-shell.cc | 183 +++ wayland/cc/xdg-shell.h | 1884 ---------------------- wayland/cc/xdg-shell.hh | 2307 +++++++++++++++++++++++++++ wayland/java/WindowWayland.java | 17 +- windows/java/WindowWin32.java | 5 +- x11/CMakeLists.txt | 6 +- {linux => x11}/cc/ScreenInfo.cc | 1 - x11/cc/ScreenInfo.hh | 13 + x11/cc/WindowX11.cc | 8 +- x11/cc/WindowX11.hh | 2 +- x11/java/WindowX11.java | 6 + 35 files changed, 3075 insertions(+), 2752 deletions(-) create mode 100644 wayland/cc/ScreenInfo.cc rename {linux => wayland}/cc/ScreenInfo.hh (100%) create mode 100644 wayland/cc/ShmPool.cc create mode 100644 wayland/cc/ShmPool.hh delete mode 100644 wayland/cc/xdg-shell.c create mode 100644 wayland/cc/xdg-shell.cc delete mode 100644 wayland/cc/xdg-shell.h create mode 100644 wayland/cc/xdg-shell.hh rename {linux => x11}/cc/ScreenInfo.cc (99%) create mode 100644 x11/cc/ScreenInfo.hh diff --git a/macos/java/WindowMac.java b/macos/java/WindowMac.java index 645be065..297736fa 100644 --- a/macos/java/WindowMac.java +++ b/macos/java/WindowMac.java @@ -228,6 +228,11 @@ public Window restore() { return this; } + @Override + public float getScale() { + return this.getScreen().getScale(); + } + @Override public Window setFullScreen(boolean value) { assert _onUIThread() : "Should be run on UI thread"; diff --git a/script/build.py b/script/build.py index 80882614..ec528e66 100755 --- a/script/build.py +++ b/script/build.py @@ -24,7 +24,7 @@ def build_native_system(system): build_utils.copy_newer('build/jwm_x64.dll', '../target/classes/jwm_x64.dll') return 0 -def build_system(): +def build_native(): cur_system = build_utils.system; if cur_system == "linux": build_native_system("x11") diff --git a/shared/cc/StringUTF16.cc b/shared/cc/StringUTF16.cc index ac8522a4..a3b54b56 100644 --- a/shared/cc/StringUTF16.cc +++ b/shared/cc/StringUTF16.cc @@ -89,4 +89,4 @@ std::string jwm::StringUTF16::toAscii() const { result[i++] = static_cast(c); } return result; -} \ No newline at end of file +} diff --git a/shared/cc/StringUTF16.hh b/shared/cc/StringUTF16.hh index 84cde0e7..cf58976a 100644 --- a/shared/cc/StringUTF16.hh +++ b/shared/cc/StringUTF16.hh @@ -3,6 +3,7 @@ #include #include #include +#include namespace jwm { diff --git a/shared/java/Window.java b/shared/java/Window.java index dc78cb15..d156b949 100644 --- a/shared/java/Window.java +++ b/shared/java/Window.java @@ -68,6 +68,7 @@ public Window setLayer(@Nullable Layer layer) { return this; } + public abstract float getScale(); /** *

Enables complex text input on this window.

*

Passed value `true` or `false` enables or disables complex text input and IME on this window respectively.

diff --git a/wayland/CMakeLists.txt b/wayland/CMakeLists.txt index 75249cdd..8af35a72 100644 --- a/wayland/CMakeLists.txt +++ b/wayland/CMakeLists.txt @@ -15,12 +15,8 @@ if(NOT JWM_ARCH) endif() endif() -find_package(wayland-client REQUIRED) -find_package(decor REQUIRED) -find_package(wayland-cursor REQUIRED) -find_package(OpenGL REQUIRED) - -file(GLOB SOURCES_CXX ${CMAKE_CURRENT_LIST_DIR}/../shared/cc/*.cc ${CMAKE_CURRENT_LIST_DIR}/cc/*.cc) +file(GLOB SOURCES_CXX ${CMAKE_CURRENT_LIST_DIR}/../shared/cc/*.cc + ${CMAKE_CURRENT_LIST_DIR}/cc/*.cc ${CMAKE_CURRENT_LIST_DIR}/../linux/cc/*.cc) file(GLOB SOURCES_CXX_IMPL ${CMAKE_CURRENT_LIST_DIR}/../shared/cc/impl/*.cc) add_library(jwm SHARED ${SOURCES_OBJC} ${SOURCES_CXX} ${SOURCES_CXX_IMPL}) @@ -35,9 +31,10 @@ if (NOT JAVA_HOME) endif() endif() -target_include_directories(jwm PRIVATE ${CMAKE_CURRENT_LIST_DIR}/../shared/cc ../shared/linux ${JAVA_HOME}/include ${JAVA_HOME}/include/linux) +target_include_directories(jwm PRIVATE ${CMAKE_CURRENT_LIST_DIR}/../shared/cc ${CMAKE_CURRENT_LIST_DIR}/../linux/cc ${JAVA_HOME}/include ${JAVA_HOME}/include/linux) set_target_properties(jwm PROPERTIES OUTPUT_NAME "jwm_${JWM_ARCH}") -target_link_libraries(jwm PRIVATE wayland-client::wayland-client, decor::decor, wayland-cursor::wayland-cursor) -target_link_libraries(jwm PRIVATE OpenGL::GL) +target_link_libraries(jwm PRIVATE wayland-client, decor, + wayland-cursor, xkbcommon) +target_link_libraries(jwm PRIVATE EGL, wayland-egl) diff --git a/wayland/cc/AppWayland.cc b/wayland/cc/AppWayland.cc index 9a1f03ec..dc075681 100644 --- a/wayland/cc/AppWayland.cc +++ b/wayland/cc/AppWayland.cc @@ -1,91 +1,23 @@ #include -#include "AppX11.hh" -#include +#include "AppWayland.hh" #include -#include -#include -#include -#include +#include -jwm::AppX11 jwm::app; +jwm::AppWayland jwm::app; -float jwm::AppX11::getScale() { - char *resourceString = XResourceManagerString(wm.display); - XrmDatabase db; - XrmValue value; - char *type = NULL; - - static struct once { - once() { - XrmInitialize(); - } - } once; - - if (resourceString) { - db = XrmGetStringDatabase(resourceString); - if (XrmGetResource(db, "Xft.dpi", "String", &type, &value)) { - if (value.addr) { - return atof(value.addr) / 96.f; - } - } - } - - return 1.f; -} - -void jwm::AppX11::init(JNIEnv* jniEnv) { +void jwm::AppWayland::init(JNIEnv* jniEnv) { _jniEnv = jniEnv; } -void jwm::AppX11::start() { +void jwm::AppWayland::start() { wm.runLoop(); } -void jwm::AppX11::terminate() { +void jwm::AppWayland::terminate() { wm.terminate(); } -const std::vector& jwm::AppX11::getScreens() { - if (_screens.empty()) { - Display* display = getWindowManager().getDisplay(); - XRRScreenResources* resources = XRRGetScreenResources(display, getWindowManager().getRootWindow()); - RROutput primaryOutput = XRRGetOutputPrimary(display, getWindowManager().getRootWindow()); - int count = resources->ncrtc; - int primaryIdx = 0; - - float dpi = jwm::app.getScale(); - - for (int i = 0; i < count; ++i) { - XRRCrtcInfo* info = XRRGetCrtcInfo(display, resources, resources->crtcs[i]); - // skip empty monitors - if (info->width != 0) { - for (int j = 0; j < info->noutput; ++j) { - RROutput output = info->outputs[j]; - if (output == primaryOutput) { - primaryIdx = _screens.size(); - break; - } - } - - auto bounds = jwm::IRect::makeXYWH(info->x, info->y, info->width, info->height); - - ScreenInfo myScreenInfo = { - long(info->outputs[0]), - bounds, - false - }; - _screens.push_back(myScreenInfo); - } - - XRRFreeCrtcInfo(info); - } - XRRFreeScreenResources(resources); - _screens[primaryIdx].isPrimary = true; - } - return _screens; -} - // JNI extern "C" JNIEXPORT void JNICALL Java_io_github_humbleui_jwm_App__1nStart(JNIEnv* env, jclass jclass, jobject launcher) { @@ -112,15 +44,9 @@ extern "C" JNIEXPORT void JNICALL Java_io_github_humbleui_jwm_App__1nRunOnUIThre }); } - -extern "C" JNIEXPORT jobjectArray JNICALL Java_io_github_humbleui_jwm_App__1nGetScreens(JNIEnv* env, jobject cls) noexcept { - auto& screens = jwm::app.getScreens(); - jobjectArray array = env->NewObjectArray(screens.size(), jwm::classes::Screen::kCls, 0); - float scale = jwm::app.getScale(); - size_t index = 0; - for (auto& screen : screens) { - env->SetObjectArrayElement(array, index++, screen.asJavaObject(env)); - } +// how awful +extern "C" JNIEXPORT jobjectArray JNICALL Java_io_github_humbleui_jwm_App__1nGetScreens(JNIEnv env*, jobject cls) noexcept { + jobjectArray array = env->NewObjectArray(0, jwm::classes::Screen::kCls, 0); return array; } diff --git a/wayland/cc/AppWayland.hh b/wayland/cc/AppWayland.hh index d6953aae..d0907b8c 100644 --- a/wayland/cc/AppWayland.hh +++ b/wayland/cc/AppWayland.hh @@ -1,6 +1,6 @@ #pragma once -#include "WindowManagerX11.hh" +#include "WindowManagerWayland.hh" #include #include "Types.hh" #include @@ -8,14 +8,14 @@ #include "ScreenInfo.hh" namespace jwm { - extern class AppX11 { + extern class AppWayland { public: void init(JNIEnv* jniEnv); void start(); void terminate(); - WindowManagerX11& getWindowManager() { + WindowManagerWayland& getWindowManager() { return wm; } @@ -23,13 +23,7 @@ namespace jwm { return _jniEnv; } - const std::vector& getScreens(); - - float getScale(); - JNIEnv* _jniEnv; - WindowManagerX11 wm; - std::vector _screens; - + WindowManagerWayland wm; } app; } diff --git a/wayland/cc/ClipboardWayland.cc b/wayland/cc/ClipboardWayland.cc index fd8c23d7..c32b1b51 100644 --- a/wayland/cc/ClipboardWayland.cc +++ b/wayland/cc/ClipboardWayland.cc @@ -2,18 +2,19 @@ #include #include #include -#include "AppX11.hh" +#include "AppWayland.hh" #include namespace jwm { - class ClipboardX11 { + class ClipboardWayland { public: - static ClipboardX11& inst() { - static ClipboardX11 s; + static ClipboardWayland& inst() { + static ClipboardWayland s; return s; } jobjectArray getFormats(JNIEnv* env) const { + /* auto formats = jwm::app.getWindowManager().getClipboardFormats(); if (formats.empty()) { return nullptr; @@ -31,11 +32,14 @@ namespace jwm { for (jsize i = 0; i < static_cast(formatObjs.size()); ++i) { env->SetObjectArrayElement(jniFormats, i, formatObjs[i]); } - - return jniFormats; + */ + // impl me : ) + jobjectArray formats = env->NewObjectArray(0, classes::ClipboardFormat::kCls, nullptr); + return format; } jobject get(JNIEnv* env, jobjectArray formats) { + /* jsize formatsSize = env->GetArrayLength(formats); for (jsize i = 0; i < formatsSize; ++i) { jobject format = env->GetObjectArrayElement(formats, i); @@ -63,12 +67,15 @@ namespace jwm { return env->NewGlobalRef(entry.get()); } } + */ + // impl me : ) classes::Throwable::exceptionThrown(env); return nullptr; } void set(JNIEnv* env, jobjectArray entries) { + /* jsize size = env->GetArrayLength(entries); std::map contents; for (jsize i = 0; i < size; ++i) { @@ -92,6 +99,7 @@ namespace jwm { } } jwm::app.getWindowManager().setClipboardContents(std::move(contents)); + */ // impl me : ) } }; } @@ -101,17 +109,17 @@ namespace jwm { extern "C" JNIEXPORT void JNICALL Java_io_github_humbleui_jwm_Clipboard__1nSet (JNIEnv* env, jclass jclass, jobjectArray entries) { - return jwm::ClipboardX11::inst().set(env, entries); + return jwm::ClipboardWayland::inst().set(env, entries); } extern "C" JNIEXPORT jobject JNICALL Java_io_github_humbleui_jwm_Clipboard__1nGet (JNIEnv* env, jclass jclass, jobjectArray formats) { - return jwm::ClipboardX11::inst().get(env, formats); + return jwm::ClipboardWayland::inst().get(env, formats); } extern "C" JNIEXPORT jobjectArray JNICALL Java_io_github_humbleui_jwm_Clipboard__1nGetFormats (JNIEnv* env, jclass jclass) { - return jwm::ClipboardX11::inst().getFormats(env); + return jwm::ClipboardWayland::inst().getFormats(env); } extern "C" JNIEXPORT void JNICALL Java_io_github_humbleui_jwm_Clipboard__1nClear @@ -122,4 +130,4 @@ extern "C" JNIEXPORT void JNICALL Java_io_github_humbleui_jwm_Clipboard__1nClear extern "C" JNIEXPORT jboolean JNICALL Java_io_github_humbleui_jwm_Clipboard__1nRegisterFormat (JNIEnv* env, jclass jclass, jstring formatId) { return true; -} \ No newline at end of file +} diff --git a/wayland/cc/KeyWayland.cc b/wayland/cc/KeyWayland.cc index 9a638c69..026e31c6 100644 --- a/wayland/cc/KeyWayland.cc +++ b/wayland/cc/KeyWayland.cc @@ -1,22 +1,17 @@ -#include "KeyX11.hh" -#include -#include -#include -#include +#include "KeyWayland.hh" #include "KeyModifier.hh" - bool gKeyStates[(size_t) jwm::Key::_KEY_COUNT] = {0}; -bool jwm::KeyX11::getKeyState(jwm::Key key) { +bool jwm::KeyWayland::getKeyState(jwm::Key key) { return gKeyStates[(size_t) key]; } -void jwm::KeyX11::setKeyState(jwm::Key key, bool isDown) { +void jwm::KeyWayland::setKeyState(jwm::Key key, bool isDown) { gKeyStates[(size_t) key] = isDown; } -int jwm::KeyX11::getModifiers() { +int jwm::KeyWayland::getModifiers() { int m = 0; if (getKeyState(jwm::Key::SHIFT )) m |= (int)jwm::KeyModifier::SHIFT; @@ -28,7 +23,7 @@ int jwm::KeyX11::getModifiers() { return m; } -int jwm::KeyX11::getModifiersFromMask(int mask) { +int jwm::KeyWayland::getModifiersFromMask(int mask) { int m = getModifiers(); if (mask & ShiftMask ) m |= (int)jwm::KeyModifier::SHIFT; @@ -38,7 +33,7 @@ int jwm::KeyX11::getModifiersFromMask(int mask) { return m; } -jwm::Key jwm::KeyX11::fromNative(uint32_t v) { +jwm::Key jwm::KeyWayland::fromNative(uint32_t v) { /* switch (v) { // Modifiers case XK_Caps_Lock: return Key::CAPS_LOCK; @@ -174,5 +169,7 @@ jwm::Key jwm::KeyX11::fromNative(uint32_t v) { // Key::VOLUME_DOWN // Key::MUTE default: return Key::UNDEFINED; - } -} \ No newline at end of file + } */ + // IMPL ME! + return Key::UNDEFINED: +} diff --git a/wayland/cc/KeyWayland.hh b/wayland/cc/KeyWayland.hh index ce03b165..bcbf02d8 100644 --- a/wayland/cc/KeyWayland.hh +++ b/wayland/cc/KeyWayland.hh @@ -4,11 +4,11 @@ #include "Key.hh" namespace jwm { - namespace KeyX11 { + namespace KeyWayland { jwm::Key fromNative(uint32_t v); bool getKeyState(jwm::Key key); void setKeyState(jwm::Key key, bool isDown); int getModifiers(); int getModifiersFromMask(int mask); } -} \ No newline at end of file +} diff --git a/wayland/cc/LayerGLWayland.cc b/wayland/cc/LayerGLWayland.cc index aa78e4ee..e8b96960 100644 --- a/wayland/cc/LayerGLWayland.cc +++ b/wayland/cc/LayerGLWayland.cc @@ -3,50 +3,59 @@ #include #include "impl/Library.hh" #include "impl/RefCounted.hh" -#include "WindowX11.hh" -#include +#include "WindowWayland.hh" +#include +#include namespace jwm { class LayerGL: public RefCounted, public ILayer { public: - WindowX11* fWindow; - GLXContext _context = nullptr; - using glXSwapIntervalEXT_t = void (*)(Display*, GLXDrawable, int); - glXSwapIntervalEXT_t _glXSwapIntervalEXT; + WindowWayland* fWindow; + wl_egl_window* _eglWindow = nullptr; + EGLContext _context = nullptr; + EGLDisplay _display = nullptr; + EGLSurface _surface = nullptr; - LayerGL() = default; - virtual ~LayerGL() = default; - - void attach(WindowX11* window) { - if (window->_windowManager.getVisualInfo() == nullptr) { - throw std::runtime_error("layer not supported"); - } + LayerGLWayland() = default; + virtual ~LayerGLWayland() = default; + void attach(WindowWayland* window) { fWindow = jwm::ref(window); fWindow->setLayer(this); + if (_display == nullptr) { + _display = eglGetPlatformDisplayEXT(EGL_PLATFORM_WAYLAND_KHR, window->_windowManager.display, EGL_NONE); + + eglInitialize(_display, nullptr, nullptr); + } if (_context == nullptr) { - _context = glXCreateContext(window->_windowManager.getDisplay(), - window->_windowManager.getVisualInfo(), - nullptr, - true); - + EGLint attrList[] = { + EGL_ALPHA_SIZE, 8, + EGL_BLUE_SIZE, 8, + EGL_GREEN_SIZE, 8, + EGL_RED_SIZE, 8, + EGL_NONE + }; + EGLConfig config; + EGLint numConfig; + eglChooseConfig(_display, attrList, &config, 1, &numConfig); + // :troll: + _context = eglCreateContext(_display, + config, + EGL_NO_CONTEXT, + nullptr); + _eglWindow = wl_egl_window_create(window->_waylandWindow, window->getWidth(), window->getHeight()); + + _surface = eglCreatePlatformWindowSurface(_display, config, _eglWindow, nullptr); } makeCurrentForced(); - _glXSwapIntervalEXT = reinterpret_cast(glXGetProcAddress(reinterpret_cast("glXSwapIntervalEXT"))); - setVsyncMode(VSYNC_ADAPTIVE); } void setVsyncMode(VSync v) override { - - if (_glXSwapIntervalEXT) { - _glXSwapIntervalEXT(fWindow->_windowManager.getDisplay(), - fWindow->_x11Window, - v); - } + // vsync? what vsync? } void resize(int width, int height) { @@ -55,22 +64,25 @@ namespace jwm { glStencilMask(0xffffffff); glClear(GL_STENCIL_BUFFER_BIT | GL_COLOR_BUFFER_BIT); - glViewport(0, 0, width, height); + // ??? + // glViewport(0, 0, width, height); + wl_egl_window_resize(_eglWindow, width, height, 0, 0); } void swapBuffers() { - glXSwapBuffers(fWindow->_windowManager.getDisplay(), fWindow->_x11Window); + eglSwapBuffers(fWindow->_windowManager.getDisplay(), _surface); } void close() override { - glXDestroyContext(fWindow->_windowManager.getDisplay(), _context); + eglDestroyContext(_display, _context); jwm::unref(&fWindow); } void makeCurrentForced() override { ILayer::makeCurrentForced(); - glXMakeCurrent(fWindow->_windowManager.getDisplay(), - fWindow->_x11Window, + eglMakeCurrent(_display, + _surface, + _surface, _context); } }; @@ -89,7 +101,7 @@ extern "C" JNIEXPORT void JNICALL Java_io_github_humbleui_jwm_LayerGL__1nAttach (JNIEnv* env, jobject obj, jobject windowObj) { try { jwm::LayerGL* instance = reinterpret_cast(jwm::classes::Native::fromJava(env, obj)); - jwm::WindowX11* window = reinterpret_cast(jwm::classes::Native::fromJava(env, windowObj)); + jwm::WindowWayland* window = reinterpret_cast(jwm::classes::Native::fromJava(env, windowObj)); instance->attach(window); } catch (const std::exception& e) { jwm::classes::Throwable::throwLayerNotSupportedException(env, "Failed to init OpenGL"); diff --git a/wayland/cc/LayerRasterWayland.cc b/wayland/cc/LayerRasterWayland.cc index 2ec70c85..f656709b 100644 --- a/wayland/cc/LayerRasterWayland.cc +++ b/wayland/cc/LayerRasterWayland.cc @@ -3,44 +3,40 @@ #include #include "impl/Library.hh" #include "impl/RefCounted.hh" -#include "WindowX11.hh" - +#include "WindowWayland.hh" +#include "ShmPool.hh" namespace jwm { class LayerRaster: public RefCounted, public ILayer { public: - WindowX11* fWindow; + WindowWayland* fWindow; size_t _width = 0, _height = 0; - XImage* _xImage = nullptr; - GC _graphicsContext; - VSync _vsync = VSYNC_ENABLED; - - /** - * Using raw pointer here because XImage frees this buffer on XDestroyImage. - */ + wl_buffer* _buffer = nullptr; uint8_t* _imageData = nullptr; + ShmPool _pool = nullptr; + VSync _vsync = VSYNC_ENABLED; LayerRaster() = default; virtual ~LayerRaster() = default; - void attach(WindowX11* window) { + void attach(WindowWayland* window) { fWindow = jwm::ref(window); fWindow->setLayer(this); - - Display* d = fWindow->_windowManager.getDisplay(); - _graphicsContext = DefaultGC(d, DefaultScreen(d)); } void resize(int width, int height) { - Display* d = fWindow->_windowManager.getDisplay(); + wl_display* d = fWindow->_windowManager.display; _width = width; _height = height; - if (_xImage) { - XDestroyImage(_xImage); + int bufSize = width * height * sizeof(uint32_t) * 2; + if (!_pool) { + _pool = new ShmPool(fWindow->_windowManager->shm, bufSize); } - _imageData = new uint8_t[width * height * sizeof(uint32_t)]; - _xImage = XCreateImage(d, CopyFromParent, DefaultDepth(d, DefaultScreen(d)), ZPixmap, 0, (char*)_imageData, width, height, 32, 0); - XInitImage(_xImage); - _xImage->byte_order = _xImage->bitmap_bit_order = LSBFirst; + _pool.grow(bufSize); + // LSBFirst means Little endian : ) + auto buf = _pool.createBuffer(0, width, height, width * sizeof(uint32_t), WL_SHM_FORMAT_ABRG8888); + + _buffer = buf.first; + _imageData = buf.second; } const void* getPixelsPtr() const { @@ -48,18 +44,21 @@ namespace jwm { } int getRowBytes() const { + return _width * sizeof(uint32_t); } void swapBuffers() { - XPutImage(fWindow->_windowManager.getDisplay(), fWindow->_x11Window, _graphicsContext, _xImage, 0, 0, 0, 0, _width, _height); + // : ) + wl_surface_damage_buffer(fWindow->_waylandWindow, 0, 0, UINT32_MAX, UINT32_MAX); } void close() override { - if (_xImage) { - XDestroyImage(_xImage); - _xImage = nullptr; + if (_buffer) { + wl_buffer_destroy(_buffer); + _buffer = nullptr; } + destroy _pool; jwm::unref(&fWindow); } @@ -83,7 +82,7 @@ extern "C" JNIEXPORT jlong JNICALL Java_io_github_humbleui_jwm_LayerRaster__1nMa extern "C" JNIEXPORT void JNICALL Java_io_github_humbleui_jwm_LayerRaster__1nAttach (JNIEnv* env, jobject obj, jobject windowObj) { jwm::LayerRaster* instance = reinterpret_cast(jwm::classes::Native::fromJava(env, obj)); - jwm::WindowX11* window = reinterpret_cast(jwm::classes::Native::fromJava(env, windowObj)); + jwm::WindowWayland* window = reinterpret_cast(jwm::classes::Native::fromJava(env, windowObj)); instance->attach(window); } @@ -120,4 +119,4 @@ extern "C" JNIEXPORT jlong JNICALL Java_io_github_humbleui_jwm_LayerRaster__1nGe (JNIEnv* env, jobject obj) { jwm::LayerRaster* instance = reinterpret_cast(jwm::classes::Native::fromJava(env, obj)); return static_cast(instance->getRowBytes()); -} \ No newline at end of file +} diff --git a/wayland/cc/MouseButtonWayland.cc b/wayland/cc/MouseButtonWayland.cc index c1aa2c36..d2fcd79d 100644 --- a/wayland/cc/MouseButtonWayland.cc +++ b/wayland/cc/MouseButtonWayland.cc @@ -1,25 +1,25 @@ -#include "MouseButtonX11.hh" +#include "MouseButtonWayland.hh" -jwm::MouseButton jwm::MouseButtonX11::fromNative(uint32_t v) { +jwm::MouseButton jwm::MouseButtonWayland::fromNative(uint32_t v) { switch (v) { - case 1: return jwm::MouseButton::PRIMARY; - case 2: return jwm::MouseButton::MIDDLE; - case 3: return jwm::MouseButton::SECONDARY; - case 8: return jwm::MouseButton::BACK; - case 9: return jwm::MouseButton::FORWARD; + case 0x110: return jwm::MouseButton::PRIMARY; + case 0x112: return jwm::MouseButton::MIDDLE; + case 0x111: return jwm::MouseButton::SECONDARY; + case 0x116: return jwm::MouseButton::BACK; + case 0x115: return jwm::MouseButton::FORWARD; } return jwm::MouseButton::PRIMARY; } -bool jwm::MouseButtonX11::isButton(uint32_t v) { - return v < 4 || v > 7; // mouse wheel buttons +bool jwm::MouseButtonWayland::isButton(uint32_t v) { + return v >= 0x110 && v <= 0x116; // mouse wheel buttons } -int jwm::MouseButtonX11::fromNativeMask(unsigned v) { +int jwm::MouseButtonWayland::fromNativeMask(unsigned v) { int res = 0; if (v & 0x100) res |= int(jwm::MouseButton::PRIMARY); if (v & 0x400) res |= int(jwm::MouseButton::SECONDARY); if (v & 0x200) res |= int(jwm::MouseButton::MIDDLE); return res; -} \ No newline at end of file +} diff --git a/wayland/cc/MouseButtonWayland.hh b/wayland/cc/MouseButtonWayland.hh index 898edda7..1cf9f960 100644 --- a/wayland/cc/MouseButtonWayland.hh +++ b/wayland/cc/MouseButtonWayland.hh @@ -4,9 +4,9 @@ #include "MouseButton.hh" namespace jwm { - namespace MouseButtonX11 { + namespace MouseButtonWayland { MouseButton fromNative(uint32_t v); int fromNativeMask(unsigned v); bool isButton(uint32_t v); } -} \ No newline at end of file +} diff --git a/wayland/cc/ScreenInfo.cc b/wayland/cc/ScreenInfo.cc new file mode 100644 index 00000000..a7392e40 --- /dev/null +++ b/wayland/cc/ScreenInfo.cc @@ -0,0 +1,5 @@ +#include "ScreenInfo.hh" + +jobject jwm::ScreenInfo::asJavaObject(JNIEnv* env) const { + return jwm::classes::Screen::make(env, id, isPrimary, bounds, bounds, jwm::app.getScale()); +} diff --git a/linux/cc/ScreenInfo.hh b/wayland/cc/ScreenInfo.hh similarity index 100% rename from linux/cc/ScreenInfo.hh rename to wayland/cc/ScreenInfo.hh diff --git a/wayland/cc/ShmPool.cc b/wayland/cc/ShmPool.cc new file mode 100644 index 00000000..e2a3a334 --- /dev/null +++ b/wayland/cc/ShmPool.cc @@ -0,0 +1,92 @@ +#include "ShmPool.hh" +#include +#include +#include +#include +#include + +using namespace jwm; + +static void randname(char *buf) +{ + struct timespec ts; + clock_gettime(CLOCK_REALTIME, &ts); + long r = ts.tv_nsec; + for (int i = 0; i < 6; ++i) { + buf[i] = 'A'+(r&15)+(r&16)*2; + r >>= 5; + } +} +ShmPool::ShmPool(wl_shm* shm, size_t size): + _size(size) { + _fd = _allocateShmFile(size); + if (_fd < 0) { + // why : ( + throw std::system_error(EIO, "Couldn't allocate buffer"); + } + _pool = wl_shm_create_pool(shm, _fd, size); + _rawData = mmap(nullptr, size, + PROT_READ | PROT_WRITE, MAP_SHARED, _fd, 0); + + } +ShmPool::~ShmPool() { + wl_shm_pool_destroy(_pool); + close(_fd); +} + +void ShmPool::grow(size_t size) { + if (size <= _size) + return; + int ret; + do { + ret = ftruncate(_fd, size); + } while (ret < 0 && errno == EINTR); + if (ret < 0) { + // AAHHHHH! + throw std::system_error(EIO, "Couldn't grow buffer"); + } + uint8_t* newData = mmap(nullptr, size, + PROT_READ | PROT_WRITE, MAP_SHARED, _fd, 0); + // do I need to memcpy??? lets say no :troll: + // TODO: error checking :troll: + munmap(_rawData, _size); + _rawData = newData; + _size = size; + wl_shm_pool_resize(_pool, size); +} + +int ShmPool::_createShmFile() { + int retries = 100; + do { + char name[] = "/wl_shm-XXXXXX"; + randname(name + sizeof(name) - 7); + --retries; + int fd = shm_open(name, O_RDWR | O_CREAT | O_EXCL, 0600); + if (fd >= 0) { + shm_unlink(name); + return fd; + } + } while (retries > 0 && errno == EEXIST); + return -1; +} + +int ShmPool::_allocateShmFile(size_t size) { + int fd = _createShmFile(); + if (fd < 0) + return -1; + int ret; + do { + ret = ftruncate(fd, size); + } while (ret < 0 && errno == EINTR); + if (ret < 0) { + close(fd); + return -1; + } + return fd; +} + +std::pair createBuffer(int offset, int width, int height, int stride, uint32_t format) { + wl_buffer* buffer = wl_shm_pool_create_buffer(_pool, offset, width, height, stride, format); + uint32_t* data = &_rawData[offset]; + return std::pair(buffer, data); +} diff --git a/wayland/cc/ShmPool.hh b/wayland/cc/ShmPool.hh new file mode 100644 index 00000000..ed67e1a5 --- /dev/null +++ b/wayland/cc/ShmPool.hh @@ -0,0 +1,25 @@ +#pragma once + +#include +#include + +namespace jwm { + class ShmPool { + public: + ShmPool(wl_shm* shm, size_t size); + ~ShmPool(); + + size_t _size; + int _fd; + wl_shm_pool* _pool; + uint8_t* _rawData; + + // grows current file to at least this size + void grow(size_t size); + + std::pair createBuffer(int offset, int width, int height, int stride, uint32_t format); + + int _createShmFile(); + int _allocateShmFile(size_t size); + }; +} diff --git a/wayland/cc/WindowManagerWayland.cc b/wayland/cc/WindowManagerWayland.cc index e8f799d3..a86f4650 100644 --- a/wayland/cc/WindowManagerWayland.cc +++ b/wayland/cc/WindowManagerWayland.cc @@ -14,6 +14,8 @@ #include #include #include "Log.hh" +#include "xdg-shell.hh" +#include using namespace jwm; @@ -35,13 +37,15 @@ WindowManagerWayland::WindowManagerWayland(): throw std::system_error(1, std::system_category); } + + { wl_cursor_theme* cursor_theme = wl_cursor_theme_load(nullptr, 24, shm); // TODO: what about if missing : ( auto loadCursor = [&](const char* name) { wl_cursor* cursor = wl_cursor_theme_get_cursor(cursor_theme, name); wl_cursor_image* cursorImage = cursor->images[0]; - return wl_cursor_image_get_buffer(cursorImage); + return cursorImage; } _cursors[static_cast(jwm::MouseCursor::ARROW )] = loadCursor("default"); @@ -55,8 +59,28 @@ WindowManagerWayland::WindowManagerWayland(): _cursors[static_cast(jwm::MouseCursor::RESIZE_WE )] = loadCursor("ew-resize"); _cursors[static_cast(jwm::MouseCursor::RESIZE_NESW )] = loadCursor("nesw-resize"); _cursors[static_cast(jwm::MouseCursor::RESIZE_NWSE )] = loadCursor("nwse-resize"); + + cursorSurface = wl_compositor_create_surface(compositor); + + wl_surface_attach(cursorSurface, + wl_cursor_get_image_buffer(_cursors[static_cast(jwm::MouseCursor::ARROW)]), 0, 0); + wl_surface_commit(cursorSurface); + } + + { + pointer = wl_seat_get_pointer(seat); + wl_pointer_listener pointerListener = { + .enter = pointerHandleEnter, + .leave = pointerHandleLeave, + .motion = pointerHandleMotion, + .button = pointerHandleButton, + .axis = pointerHandleAxis + }; + wl_pointer_add_listener(pointer, &pointerListener, nullptr); } + + } @@ -64,58 +88,11 @@ WindowManagerWayland::WindowManagerWayland(): void WindowManagerWayland::runLoop() { _runLoop = true; - XEvent ev; - - // buffer to read into; really only needs to be two characters long due to the notifyBool fast path, but longer doesn't hurt - char buf[100]; - // initialize a pipe to write to whenever this loop needs to process something new - int pipes[2]; - if (pipe(pipes)) { - printf("Failed to open pipe\n"); - return; - } - - notifyFD = pipes[1]; - fcntl(pipes[1], F_SETFL, O_NONBLOCK); // make sure notifyLoop doesn't block - // two polled items - the X11 event queue, and our event queue - struct pollfd ps[] = {{.fd=XConnectionNumber(display), .events=POLLIN}, {.fd=pipes[0], .events=POLLIN}}; + // who be out here running they loop while (_runLoop) { - while (XPending(display)) { - XNextEvent(display, &ev); - _processXEvent(ev); - if (jwm::classes::Throwable::exceptionThrown(app.getJniEnv())) - _runLoop = false; - } - _processCallbacks(); - - // block until the next X11 or our event - if (poll(&ps[0], 2, -1) < 0) { - printf("Error during poll\n"); - break; - } - - // clear pipe - if (ps[1].revents & POLLIN) { - while (read(pipes[0], buf, sizeof(buf)) == sizeof(buf)) { } - } - // clear fast path boolean; done after clearing the pipe so that, during event execution, new notifyLoop calls can still function - notifyBool.store(false); - // The events causing a notifyLoop anywhere between poll() end and here will be processed in all cases, as that's the next thing that happens } - notifyFD = -1; - close(pipes[0]); - close(pipes[1]); -} - -void WindowManagerWayland::notifyLoop() { - if (notifyFD==-1) return; - // fast notifyBool path to not make system calls when not necessary - if (!notifyBool.exchange(true)) { - char dummy[1] = {0}; - int unused = write(notifyFD, dummy, 1); // this really shouldn't fail, but if it does, the pipe should either be full (good), or dead (bad, but not our business) - } } void WindowManagerWayland::_processCallbacks() { @@ -133,7 +110,7 @@ void WindowManagerWayland::_processCallbacks() { } { // copy window list in case one closes any other, invalidating some iterator in _nativeWindowToMy - std::vector copy; + std::vector copy; for (auto& p : _nativeWindowToMy) { copy.push_back(p.second); } @@ -153,37 +130,123 @@ void WindowManagerWayland::_processCallbacks() { void WindowManagerWayland::registryHandleGlobal(void* data, wl_registry *registry, uint32_t name, const char* interface, uint32_t version) { if (strcmp(interface, "wl_compositor") == 0) { - compositor = wl_registry_bind(registry, name, + compositor = (wl_compositor*)wl_registry_bind(registry, name, &wl_compositor_interface, 3); } else if (strcmp(interface, "wl_shm") == 0) { - shm = wl_registry_bind(registry, name, + shm = (wl_shm*)wl_registry_bind(registry, name, &wl_shm_interface, 1); - } else if (strcmp(interface, "zxdg_shell_v6") == 0) { - xdgShell = wl_registry_bind(registry, name, - &zxdg_shell_v6_interface, 1); + } else if (strcmp(interface, "xdg_wm_base") == 0) { + xdgShell = (xdg_wm_base*)wl_registry_bind(registry, name, + &xdg_wm_base_interface, 1); } else if (strcmp(interface, "wl_data_device_manager") == 0) { - deviceManager = wl_registry_bind(registry, name, + deviceManager = (wl_data_device_manager*)wl_registry_bind(registry, name, &wl_data_device_manager_interface, 1); } else if (strcmp(interface, "wl_seat") == 0) { - seat = wl_registry_bind(registry, name, + seat = (wl_seat*)wl_registry_bind(registry, name, &wl_seat_interface, 1); } } void WindowManagerWayland::registryHandleGlobalRemove(void* data, wl_registry *registry, uint32_t name) { // i do nothing : ) } -std::vector WindowManagerWayland::getClipboardFormats() { - auto owner = XGetSelectionOwner(display, _atoms.CLIPBOARD); - if (owner == None) - { - return {}; + +void WindowManagerWayland::pointerHandleEnter(void* data, wl_pointer* pointer, uint32_t serial, + wl_surface *surface, wl_fixed_t surface_x, wl_fixed_t surface_y) { + wl_cursor_image* image = _cursors[static_cast(jwm::MouseCursor::ARROW)]; + wl_pointer_set_cursor(cursor, serial, cursorSurface, image->hotspot_x, image->hotspot_y); + focusedSurface = surface; +} +void WindowManagerWayland::pointerHandleLeave(void* data, wl_pointer* pointer, uint32_t serial, + wl_surface *surface) { + focusedSurface = nullptr; + // ??? + mouseMask = 0; +} +void WindowManagerWayland::pointerHandleMotion(void* data, wl_pointer* pointer, + uint32_t time, wl_fixed_t surface_x, wl_fixed_t surface_y) { + lastMousePosX = surface_x; + lastMousePosY = surface_y; + if (focusedSurface) { + ::WindowWayland* window = _nativeWindowToMy.find(focusedSurface)->second; + mouseUpdate(window, wl_fixed_to_int(surface_x), wl_fixed_to_int(surface_y), mouseMask); } - assert(("create at least one window in order to use clipboard" && !_nativeWindowToMy.empty())); - - auto nativeHandle = _nativeWindowToMy.begin()->first; - assert(nativeHandle); - +} +void WindowManagerWayland::pointerHandleButton(void* data, wl_pointer* pointer, + uint32_t serial, uint32_t time, uint32_t button, uint32_t state) { + using namespace classes; + if (state == 0) { + // release + switch (button) { + // primary + case 0x110: + mouseMask &= ~0x100; + break; + // secondary + case 0x111: + mouseMask &= ~0x400; + break; + // middle + case 0x112: + mouseMask &= ~0x200; + break; + default: + break; + } + + if (MouseButtonWayland::isButton(button) && focusedSurface) { + jwm::JNILocal eventButton( + app.getJniEnv(), + EventMouseButton::make( + app.getJniEnv(), + MouseButtonWayland::fromNative(button), + false, + lastMousePosX, + lastMousePosY, + jwm::KeyWayland::getModifiers() + ) + ); + WindowWayland* window = _nativeWindowToMy.find(focusedSurface)->second; + window->dispatch(eventButton.get()); + } + } else { + // down + switch (button) { + // primary + case 0x110: + mouseMask |= 0x100; + break; + // secondary + case 0x111: + mouseMask |= 0x400; + break; + // middle + case 0x112: + mouseMask |= 0x200; + break; + default: + break; + } + + if (MouseButtonWayland::isButton(button) && focusedSurface) { + jwm::JNILocal eventButton( + app.getJniEnv(), + EventMouseButton::make( + app.getJniEnv(), + MouseButtonWayland::fromNative(button), + true, + lastMousePosX, + lastMousePosY, + jwm::KeyWayland::getModifiers() + ) + ); + WindowWayland* window = _nativeWindowToMy.find(focusedSurface)->second; + window->dispatch(eventButton.get()); + } + } +} +std::vector WindowManagerWayland::getClipboardFormats() { + /* XConvertSelection(display, _atoms.CLIPBOARD, _atoms.TARGETS, @@ -241,12 +304,32 @@ std::vector WindowManagerWayland::getClipboardFormats() { // fetching data XDeleteProperty(display, nativeHandle, _atoms.JWM_CLIPBOARD); + */ + std::vector result; return result; } - +void WindowManagerWayland::mouseUpdate(WindowWayland* myWindow, uint32_t x, uint32_t y, uint32_t mask) { + using namespace classes; + + // impl me : ) + int movementX = 0, movementY = 0; + jwm::JNILocal eventMove( + app.getJniEnv(), + EventMouseMove::make(app.getJniEnv(), + x, + y, + movementX, + movementY, + jwm::MouseButtonWayland::fromNativeMask(mask), + // impl me! + jwm::KeyWayland::getModifiersFromMask(0) + ) + ); + myWindow->dispatch(eventMove.get()); +} jwm::ByteBuf WindowManagerWayland::getClipboardContents(const std::string& type) { auto nativeHandle = _nativeWindowToMy.begin()->first; - + /* XConvertSelection(display, _atoms.CLIPBOARD, XInternAtom(display, type.c_str(), false), @@ -295,6 +378,7 @@ jwm::ByteBuf WindowManagerWayland::getClipboardContents(const std::string& type) } XDeleteProperty(display, nativeHandle, _atoms.JWM_CLIPBOARD); + */ return {}; } @@ -302,7 +386,7 @@ void WindowManagerWayland::registerWindow(WindowWayland* window) { _nativeWindowToMy[window->_waylandWindow] = window; } -void WindowManagerWayland::unregisterWindow(WindowX11* window) { +void WindowManagerWayland::unregisterWindow(WindowWayland* window) { auto it = _nativeWindowToMy.find(window->_waylandWindow); if (it != _nativeWindowToMy.end()) { _nativeWindowToMy.erase(it); @@ -311,15 +395,15 @@ void WindowManagerWayland::unregisterWindow(WindowX11* window) { void WindowManagerWayland::terminate() { _runLoop = false; - notifyLoop(); } void WindowManagerWayland::setClipboardContents(std::map&& c) { assert(("create at least one window in order to use clipboard" && !_nativeWindowToMy.empty())); _myClipboardContents = c; - ::Window window = _nativeWindowToMy.begin()->first; - XSetSelectionOwner(display, XA_PRIMARY, window, CurrentTime); - XSetSelectionOwner(display, _atoms.CLIPBOARD, window, CurrentTime); + // impl me : ) + auto window = _nativeWindowToMy.begin()->first; + // XSetSelectionOwner(display, XA_PRIMARY, window, CurrentTime); + // XSetSelectionOwner(display, _atoms.CLIPBOARD, window, CurrentTime); } void WindowManagerWayland::enqueueTask(const std::function& task) { diff --git a/wayland/cc/WindowManagerWayland.hh b/wayland/cc/WindowManagerWayland.hh index a5c30afc..1dfe03ec 100644 --- a/wayland/cc/WindowManagerWayland.hh +++ b/wayland/cc/WindowManagerWayland.hh @@ -13,7 +13,7 @@ #include "MouseCursor.hh" #include #include -#include "xdg_shell.h" +#include "xdg-shell.hh" namespace jwm { class WindowWayland; @@ -55,6 +55,18 @@ namespace jwm { void registryHandleGlobalRemove(void* data, wl_registry *registry, uint32_t name); + void pointerHandleEnter(void* data, wl_pointer *pointer, + uint32_t serial, wl_surface *surface, wl_fixed_t surface_x, wl_fixed_t surface_y); + void pointerHandleLeave(void* data, wl_pointer *pointer, + uint32_t serial, wl_surface* surface); + void pointerHandleMotion(void* data, wl_pointer *pointer, + uint32_t time, wl_fixed_t surface_x, wl_fixed_t surface_y); + void pointerHandleButton(void* data, wl_pointer *pointer, + uint32_t serial, uint32_t time, uint32_t button, + uint32_t state); + void pointerHandleAxis(void* data, wl_pointer *pointer, + uint32_t time, uint32_t axis, wl_fixed_t value); + ByteBuf getClipboardContents(const std::string& type); @@ -63,10 +75,12 @@ namespace jwm { wl_display* display = nullptr wl_registry* registry = nullptr wl_shm* shm = nullptr; - zxdg_shell_v6* xdgShell = nullptr; + xdg_wm_base* xdgShell = nullptr; wl_compositor* compositor = nullptr; wl_data_device_manager* deviceManager = nullptr; wl_seat* seat = nullptr; + wl_pointer* pointer = nullptr; + // XVisualInfo* x11VisualInfo; // XSetWindowAttributes x11SWA; @@ -75,15 +89,17 @@ namespace jwm { std::atomic_bool notifyBool{false}; int lastMousePosX = 0; int lastMousePosY = 0; - void mouseUpdate(WindowWayland* myWindow); + int mouseMask = 0; + void mouseUpdate(WindowWayland* myWindow, uint32_t x, uint32_t y, uint32_t mask); - std::map<::Window, WindowWayland*> _nativeWindowToMy; + std::map _nativeWindowToMy; std::map _myClipboardContents; wl_surface* cursorSurface; + wl_surface* focusedSurface = nullptr; // Is holding all cursors in memory a good idea? - wl_buffer _cursors[static_cast(jwm::MouseCursor::COUNT)]; + wl_cursor_image* _cursors[static_cast(jwm::MouseCursor::COUNT)]; std::mutex _taskQueueLock; diff --git a/wayland/cc/WindowWayland.cc b/wayland/cc/WindowWayland.cc index 4d1c525f..cc64c8cd 100644 --- a/wayland/cc/WindowWayland.cc +++ b/wayland/cc/WindowWayland.cc @@ -21,395 +21,95 @@ WindowWayland::~WindowWayland() { } void WindowWayland::setTitle(const std::string& title) { - XChangeProperty(_windowManager.getDisplay(), - _x11Window, - _windowManager.getAtoms()._NET_WM_NAME, - _windowManager.getAtoms().UTF8_STRING, - 8, - PropModeReplace, - reinterpret_cast(title.c_str()), - title.length()); -} - -void WindowX11::setTitlebarVisible(bool isVisible) { - MotifHints motifHints = {0}; - - motifHints.flags = MOTIF_HINTS_DECORATIONS; - motifHints.decorations = int(isVisible); - - XChangeProperty(_windowManager.getDisplay(), - _x11Window, - _windowManager.getAtoms()._MOTIF_WM_HINTS, - _windowManager.getAtoms()._MOTIF_WM_HINTS, - 32, - PropModeReplace, - (unsigned char*) &motifHints, - 5); -} - -void WindowX11::close() { - if (_x11Window) { - _windowManager.unregisterWindow(this); - XDestroyWindow(_windowManager.display, _x11Window); - _x11Window = 0; - } + // impl me : ) } -void WindowX11::_xSendEventToWM(Atom atom, long a, long b, long c, long d, long e) const { - XEvent event = { 0 }; - event.type = ClientMessage; - event.xclient.window = _x11Window; - event.xclient.format = 32; // data is 32-bit longs - event.xclient.message_type = atom; - event.xclient.data.l[0] = a; - event.xclient.data.l[1] = b; - event.xclient.data.l[2] = c; - event.xclient.data.l[3] = d; - event.xclient.data.l[4] = e; - - XSendEvent(_windowManager.display, - DefaultRootWindow(_windowManager.display), - False, - SubstructureNotifyMask | SubstructureRedirectMask, - &event); -} -unsigned long WindowX11::_xGetWindowProperty(Atom property, Atom type, unsigned char** value) const { - Atom actualType; - int actualFormat; - unsigned long itemCount, bytesAfter; - - XGetWindowProperty(_windowManager.display, - _x11Window, - property, - 0, - std::numeric_limits::max(), - false, - type, - &actualType, - &actualFormat, - &itemCount, - &bytesAfter, - value); - - return itemCount; -} - -void WindowX11::maximize() { - XWindowAttributes wa; - XGetWindowAttributes(_windowManager.display, _x11Window, &wa); - - if (wa.map_state == IsViewable) { - _xSendEventToWM(_windowManager._atoms._NET_WM_STATE, - 1, - _windowManager._atoms._NET_WM_STATE_MAXIMIZED_HORZ, - _windowManager._atoms._NET_WM_STATE_MAXIMIZED_VERT, - 0, - 0); - } else { - Atom* states = nullptr; - unsigned long count = _xGetWindowProperty(_windowManager._atoms._NET_WM_STATE, - XA_ATOM, - reinterpret_cast(&states)); - - - Atom missing[2] = { - _windowManager._atoms._NET_WM_STATE_MAXIMIZED_VERT, - _windowManager._atoms._NET_WM_STATE_MAXIMIZED_HORZ - }; - unsigned long missingCount = 2; - - for (unsigned long i = 0; i < count; i++) - { - for (unsigned long j = 0; j < missingCount; j++) - { - if (states[i] == missing[j]) - { - missing[j] = missing[missingCount - 1]; - missingCount--; - } - } - } - if (states) - XFree(states); - - if (!missingCount) - return; +void WindowWayland::setTitlebarVisible(bool isVisible) { + // impl me : ) +} - XChangeProperty(_windowManager.display, - _x11Window, - _windowManager._atoms._NET_WM_STATE, - XA_ATOM, - 32, - PropModeAppend, - (unsigned char*) missing, - missingCount); +void WindowWayland::close() { + if (_waylandWindow) { + _windowManager.unregisterWindow(this); + wl_surface_destroy(_waylandWindow); } - XFlush(_windowManager.display); } - -void WindowX11::minimize() { - XIconifyWindow(_windowManager.display, _x11Window, 0); +void WindowWayland::maximize() { + // impl me :) } -void WindowX11::restore() { - if (_windowManager._atoms._NET_WM_STATE && - _windowManager._atoms._NET_WM_STATE_MAXIMIZED_VERT && - _windowManager._atoms._NET_WM_STATE_MAXIMIZED_HORZ) { - _xSendEventToWM(_windowManager._atoms._NET_WM_STATE, - 0, - _windowManager._atoms._NET_WM_STATE_MAXIMIZED_VERT, - _windowManager._atoms._NET_WM_STATE_MAXIMIZED_HORZ, - 1, - 0); - } +void WindowWayland::minimize() { + // impl me : ) } -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); - } - } +void WindowWayland::restore() { + // impl me } -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); - } +void WindowWayland::setFullScreen(bool isFullScreen) { + // impl me : ) +} - return retval; +bool WindowWayland::isFullScreen() { + // impl me : ) + return false; } -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(&data)); - if (data!=nullptr) { - left = data[0]; - top = data[2]; - right = data[1]; - bottom = data[3]; - XFree(data); - } else { - XWindowAttributes xwa; - XGetWindowAttributes(_windowManager.display, _x11Window, &xwa); - left = xwa.x; - top = xwa.y; - right = 0; - bottom = 0; - } +void WindowWayland::getDecorations(int& left, int& top, int& right, int& bottom) { + // impl me : ) } -void WindowX11::getContentPosition(int& posX, int& posY) { - int x, y; - ::Window child; - XTranslateCoordinates(_windowManager.display, - _x11Window, - XRootWindow(_windowManager.display, 0), - 0, 0, - &x, &y, - &child); - posX = x; - posY = y; +void WindowWayland::getContentPosition(int& posX, int& posY) { + posX = 0; + posY = 0; } -int WindowX11::getLeft() { +int WindowWayland::getLeft() { int x, y; getContentPosition(x, y); return x; } -int WindowX11::getTop() { +int WindowWayland::getTop() { int x, y; getContentPosition(x, y); return y; } -int WindowX11::getWidth() { +int WindowWayland::getWidth() { return _width; } -int WindowX11::getHeight() { +int WindowWayland::getHeight() { return _height; } -float WindowX11::getScale() { +float WindowWayland::getScale() { + // TODO: use surface scaling return jwm::app.getScale(); } -bool WindowX11::init() +bool WindowWayland::init() { - _x11Window = XCreateWindow(_windowManager.getDisplay(), - _windowManager.getScreen()->root, - 0, 0, - 800, 500, - 0, - _windowManager.getX11VisualDepth(), - InputOutput, - _windowManager.getX11Visual(), - CWColormap | CWEventMask | CWCursor, - &_windowManager.getSWA() - ); - - // tell X11 we want to handle close button - XSetWMProtocols(_windowManager.getDisplay(), - _x11Window, - &_windowManager.getAtoms().WM_DELETE_WINDOW, - WindowManagerX11::Atoms::PROTOCOL_COUNT); - - // IC - { - _ic = XCreateIC(_windowManager.getIM(), - XNInputStyle, - XIMPreeditNothing | XIMStatusNothing, - XNClientWindow, - _x11Window, - nullptr); - - XSetICFocus(_ic); - } - - + _waylandWindow = wl_compositor_create_surface(_windowManager.compositor); + + xdgSurface = xdg_wm_base_get_xdg_surface(_windowManager.xdgShell, _waylandWindow); + xdgTopLevel = xdg_surface_get_toplevel(xdgSurface); - // XSync - { - XSyncValue value; - XSyncIntToValue(&value, 0); - _xsyncRequestCounter.counter = XSyncCreateCounter(_windowManager.getDisplay(), value); - XChangeProperty(_windowManager.getDisplay(), - _x11Window, - _windowManager.getAtoms()._NET_WM_SYNC_REQUEST_COUNTER, - XA_CARDINAL, - 32, - PropModeReplace, - (const unsigned char*)&_xsyncRequestCounter.counter, 1); - - } _windowManager.registerWindow(this); return true; } -void WindowX11::move(int left, int top) { - _posX = left; - _posY = top; - if (_visible) - XMoveWindow(_windowManager.display, _x11Window, left, top); +void WindowWayland::move(int left, int top) { + // NO HAVING FUN! } -void WindowX11::resize(int width, int height) { - _width = width; - _height = height; - if (_visible) { - XResizeWindow(_windowManager.display, _x11Window, width, height); - jwm::JNILocal eventWindowResize(app.getJniEnv(), classes::EventWindowResize::make(app.getJniEnv(), width, height, width, height)); - dispatch(eventWindowResize.get()); - } +void WindowWayland::resize(int width, int height) { + // BOO! } -void WindowX11::setVisible(bool isVisible) { +void WindowWayland::setVisible(bool isVisible) { if (_visible != isVisible) { _visible = isVisible; if (_visible) { @@ -424,49 +124,41 @@ void WindowX11::setVisible(bool isVisible) { } } -const ScreenInfo& WindowX11::getScreen() { - // in X11, there's no straightforward way to get screen of window. - // instead, we should do it manually using center point of the window and calculating which monitor this point - // belongs to. - - int centerX = getLeft() + getWidth() / 2; - int centerY = getTop() + getHeight() / 2; - for (auto& screen : jwm::app.getScreens()) { - if (screen.bounds.isPointInside(centerX, centerY)) { - return screen; - } - } - return *jwm::app.getScreens().begin(); -} - -void jwm::WindowX11::setCursor(jwm::MouseCursor cursor) { - if (auto x11Cursor = _windowManager._cursors[static_cast(cursor)]) { - XDefineCursor(_windowManager.display, _x11Window, x11Cursor); +void jwm::WindowWayland::setCursor(jwm::MouseCursor cursor) { + if (auto wayCursor = _windowManager._cursors[static_cast(cursor)]) { + wl_surface_attach(_windowManager.cursorSurface, + wl_cursor_image_get_buffer(wayCursor->images[0]), + 0, 0); + wl_surface_commit(_windowManager.cursorSurface); + // TODO: hotspots? } else { - XUndefineCursor(_windowManager.display, _x11Window); + auto wayCursor = _windowManager.cursors[static_cast(jwm::MouseCursor::ARROW)]; + wl_surface_attach(_windowManager.cursorSurface, + wl_cursor_image_get_buffer(wayCursor->images[0]), 0, 0); + wl_surface_commit(_windowManager.cursorSurface); } } // JNI -extern "C" JNIEXPORT jlong JNICALL Java_io_github_humbleui_jwm_WindowX11__1nMake +extern "C" JNIEXPORT jlong JNICALL Java_io_github_humbleui_jwm_WindowWayland__1nMake (JNIEnv* env, jclass jclass) { - std::unique_ptr instance = std::make_unique(env, jwm::app.getWindowManager()); + std::unique_ptr instance = std::make_unique(env, jwm::app.getWindowManager()); if (instance->init()) { return reinterpret_cast(instance.release()); } return 0; } -extern "C" JNIEXPORT void JNICALL Java_io_github_humbleui_jwm_WindowX11__1nSetVisible +extern "C" JNIEXPORT void JNICALL Java_io_github_humbleui_jwm_WindowWayland__1nSetVisible (JNIEnv* env, jobject obj, jboolean isVisible) { - reinterpret_cast(jwm::classes::Native::fromJava(env, obj))->setVisible(isVisible); + reinterpret_cast(jwm::classes::Native::fromJava(env, obj))->setVisible(isVisible); } -extern "C" JNIEXPORT jobject JNICALL Java_io_github_humbleui_jwm_WindowX11__1nGetWindowRect +extern "C" JNIEXPORT jobject JNICALL Java_io_github_humbleui_jwm_WindowWayland__1nGetWindowRect (JNIEnv* env, jobject obj) { - jwm::WindowX11* instance = reinterpret_cast(jwm::classes::Native::fromJava(env, obj)); + jwm::WindowWayland* instance = reinterpret_cast(jwm::classes::Native::fromJava(env, obj)); int left, top, right, bottom; instance->getDecorations(left, top, right, bottom); int x, y; @@ -480,9 +172,9 @@ extern "C" JNIEXPORT jobject JNICALL Java_io_github_humbleui_jwm_WindowX11__1nGe ); } -extern "C" JNIEXPORT jobject JNICALL Java_io_github_humbleui_jwm_WindowX11__1nGetContentRect +extern "C" JNIEXPORT jobject JNICALL Java_io_github_humbleui_jwm_WindowWayland__1nGetContentRect (JNIEnv* env, jobject obj) { - jwm::WindowX11* instance = reinterpret_cast(jwm::classes::Native::fromJava(env, obj)); + jwm::WindowWayland* instance = reinterpret_cast(jwm::classes::Native::fromJava(env, obj)); int left, top, right, bottom; instance->getDecorations(left, top, right, bottom); return jwm::classes::IRect::toJavaXYWH( @@ -494,63 +186,57 @@ extern "C" JNIEXPORT jobject JNICALL Java_io_github_humbleui_jwm_WindowX11__1nGe ); } -extern "C" JNIEXPORT void JNICALL Java_io_github_humbleui_jwm_WindowX11__1nSetWindowPosition +extern "C" JNIEXPORT void JNICALL Java_io_github_humbleui_jwm_WindowWayland__1nSetWindowPosition (JNIEnv* env, jobject obj, int left, int top) { - jwm::WindowX11* instance = reinterpret_cast(jwm::classes::Native::fromJava(env, obj)); + jwm::WindowWayland* instance = reinterpret_cast(jwm::classes::Native::fromJava(env, obj)); instance->move(left, top); } -extern "C" JNIEXPORT void JNICALL Java_io_github_humbleui_jwm_WindowX11__1nSetWindowSize +extern "C" JNIEXPORT void JNICALL Java_io_github_humbleui_jwm_WindowWayland__1nSetWindowSize (JNIEnv* env, jobject obj, int width, int height) { - jwm::WindowX11* instance = reinterpret_cast(jwm::classes::Native::fromJava(env, obj)); + jwm::WindowWayland* instance = reinterpret_cast(jwm::classes::Native::fromJava(env, obj)); // TODO https://github.com/HumbleUI/JWM/issues/109 instance->resize(width, height); } -extern "C" JNIEXPORT void JNICALL Java_io_github_humbleui_jwm_WindowX11__1nSetContentSize +extern "C" JNIEXPORT void JNICALL Java_io_github_humbleui_jwm_WindowWayland__1nSetContentSize (JNIEnv* env, jobject obj, int width, int height) { - jwm::WindowX11* instance = reinterpret_cast(jwm::classes::Native::fromJava(env, obj)); + jwm::WindowWayland* instance = reinterpret_cast(jwm::classes::Native::fromJava(env, obj)); instance->resize(width, height); } -extern "C" JNIEXPORT jobject JNICALL Java_io_github_humbleui_jwm_WindowX11__1nGetScreen +extern "C" JNIEXPORT void JNICALL Java_io_github_humbleui_jwm_WindowWayland__1nRequestFrame (JNIEnv* env, jobject obj) { - jwm::WindowX11* instance = reinterpret_cast(jwm::classes::Native::fromJava(env, obj)); - return instance->getScreen().asJavaObject(env); -} - -extern "C" JNIEXPORT void JNICALL Java_io_github_humbleui_jwm_WindowX11__1nRequestFrame - (JNIEnv* env, jobject obj) { - jwm::WindowX11* instance = reinterpret_cast(jwm::classes::Native::fromJava(env, obj)); + jwm::WindowWayland* instance = reinterpret_cast(jwm::classes::Native::fromJava(env, obj)); instance->requestRedraw(); } -extern "C" JNIEXPORT void JNICALL Java_io_github_humbleui_jwm_WindowX11__1nMaximize +extern "C" JNIEXPORT void JNICALL Java_io_github_humbleui_jwm_WindowWayland__1nMaximize (JNIEnv* env, jobject obj) { - reinterpret_cast(jwm::classes::Native::fromJava(env, obj))->maximize(); + reinterpret_cast(jwm::classes::Native::fromJava(env, obj))->maximize(); } -extern "C" JNIEXPORT void JNICALL Java_io_github_humbleui_jwm_WindowX11__1nMinimize +extern "C" JNIEXPORT void JNICALL Java_io_github_humbleui_jwm_WindowWayland__1nMinimize (JNIEnv* env, jobject obj) { - reinterpret_cast(jwm::classes::Native::fromJava(env, obj))->minimize(); + reinterpret_cast(jwm::classes::Native::fromJava(env, obj))->minimize(); } -extern "C" JNIEXPORT void JNICALL Java_io_github_humbleui_jwm_WindowX11__1nRestore +extern "C" JNIEXPORT void JNICALL Java_io_github_humbleui_jwm_WindowWayland__1nRestore (JNIEnv* env, jobject obj) { - reinterpret_cast(jwm::classes::Native::fromJava(env, obj))->restore(); + reinterpret_cast(jwm::classes::Native::fromJava(env, obj))->restore(); } -extern "C" JNIEXPORT void JNICALL Java_io_github_humbleui_jwm_WindowX11__1nClose +extern "C" JNIEXPORT void JNICALL Java_io_github_humbleui_jwm_WindowWayland__1nClose (JNIEnv* env, jobject obj) { - jwm::WindowX11* instance = reinterpret_cast(jwm::classes::Native::fromJava(env, obj)); + jwm::WindowWayland* instance = reinterpret_cast(jwm::classes::Native::fromJava(env, obj)); instance->close(); } -extern "C" JNIEXPORT void JNICALL Java_io_github_humbleui_jwm_WindowX11__1nSetTitle +extern "C" JNIEXPORT void JNICALL Java_io_github_humbleui_jwm_WindowWayland__1nSetTitle (JNIEnv* env, jobject obj, jbyteArray title) { - jwm::WindowX11* instance = reinterpret_cast(jwm::classes::Native::fromJava(env, obj)); + jwm::WindowWayland* instance = reinterpret_cast(jwm::classes::Native::fromJava(env, obj)); jbyte* bytes = env->GetByteArrayElements(title, nullptr); std::string titleS = { bytes, bytes + env->GetArrayLength(title) }; @@ -559,27 +245,33 @@ extern "C" JNIEXPORT void JNICALL Java_io_github_humbleui_jwm_WindowX11__1nSetTi instance->setTitle(titleS); } -extern "C" JNIEXPORT void JNICALL Java_io_github_humbleui_jwm_WindowX11__1nSetTitlebarVisible +extern "C" JNIEXPORT void JNICALL Java_io_github_humbleui_jwm_WindowWayland__1nSetTitlebarVisible (JNIEnv* env, jobject obj, jboolean isVisible) { - jwm::WindowX11* instance = reinterpret_cast(jwm::classes::Native::fromJava(env, obj)); + jwm::WindowWayland* instance = reinterpret_cast(jwm::classes::Native::fromJava(env, obj)); instance->setTitlebarVisible(isVisible); } -extern "C" JNIEXPORT void JNICALL Java_io_github_humbleui_jwm_WindowX11__1nSetMouseCursor +extern "C" JNIEXPORT void JNICALL Java_io_github_humbleui_jwm_WindowWayland__1nSetMouseCursor (JNIEnv* env, jobject obj, jint idx) { - jwm::WindowX11* instance = reinterpret_cast(jwm::classes::Native::fromJava(env, obj)); + jwm::WindowWayland* instance = reinterpret_cast(jwm::classes::Native::fromJava(env, obj)); instance->setCursor(static_cast(idx)); } -extern "C" JNIEXPORT void JNICALL Java_io_github_humbleui_jwm_WindowX11__1nSetFullScreen +extern "C" JNIEXPORT void JNICALL Java_io_github_humbleui_jwm_WindowWayland__1nSetFullScreen (JNIEnv* env, jobject obj, jboolean isFullScreen) { - jwm::WindowX11* instance = reinterpret_cast(jwm::classes::Native::fromJava(env, obj)); + jwm::WindowWayland* instance = reinterpret_cast(jwm::classes::Native::fromJava(env, obj)); instance->setFullScreen(isFullScreen); } -extern "C" JNIEXPORT jboolean JNICALL Java_io_github_humbleui_jwm_WindowX11__1nIsFullScreen +extern "C" JNIEXPORT jboolean JNICALL Java_io_github_humbleui_jwm_WindowWayland__1nIsFullScreen (JNIEnv* env, jobject obj) { - jwm::WindowX11* instance = reinterpret_cast(jwm::classes::Native::fromJava(env, obj)); + jwm::WindowWayland* instance = reinterpret_cast(jwm::classes::Native::fromJava(env, obj)); return instance->isFullScreen(); } + +extern "C" JNIEXPORT jfloat JNICALL Java_io_github_humbleui_jwm_WindowWayland__1nGetScale + (JNIEnv* env, jobject obj) { + jwm::WindowWayland* instance = reinterpret_cast(jwm::classes::Native::fromJava(env, obj)); + return instance->getScale(); +} diff --git a/wayland/cc/WindowWayland.hh b/wayland/cc/WindowWayland.hh index e47e27a0..21fa1867 100644 --- a/wayland/cc/WindowWayland.hh +++ b/wayland/cc/WindowWayland.hh @@ -4,9 +4,9 @@ #include #include "Window.hh" #include "WindowManagerWayland.hh" -#include "ILayer.hh" +#include #include "ScreenInfo.hh" - +#include "xdg-shell.hh" namespace jwm { class WindowWayland: public jwm::Window { public: @@ -78,7 +78,8 @@ namespace jwm { WindowManagerWayland& _windowManager; ILayer* _layer = nullptr; - ::Window _waylandWindow = 0; - XIC _ic; + wl_surface* _waylandWindow = nullptr; + xdg_surface* xdgSurface = nullptr; + xdg_toplevel* xdgToplevel = nullptr; }; } diff --git a/wayland/cc/xdg-shell.c b/wayland/cc/xdg-shell.c deleted file mode 100644 index 68ea631f..00000000 --- a/wayland/cc/xdg-shell.c +++ /dev/null @@ -1,172 +0,0 @@ -/* Generated by wayland-scanner 1.22.0 */ - -/* - * Copyright © 2008-2013 Kristian Høgsberg - * Copyright © 2013 Rafael Antognolli - * Copyright © 2013 Jasper St. Pierre - * Copyright © 2010-2013 Intel Corporation - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - */ - -#include -#include -#include "wayland-util.h" - -#ifndef __has_attribute -# define __has_attribute(x) 0 /* Compatibility with non-clang compilers. */ -#endif - -#if (__has_attribute(visibility) || defined(__GNUC__) && __GNUC__ >= 4) -#define WL_PRIVATE __attribute__ ((visibility("hidden"))) -#else -#define WL_PRIVATE -#endif - -extern const struct wl_interface wl_output_interface; -extern const struct wl_interface wl_seat_interface; -extern const struct wl_interface wl_surface_interface; -extern const struct wl_interface zxdg_popup_v6_interface; -extern const struct wl_interface zxdg_positioner_v6_interface; -extern const struct wl_interface zxdg_surface_v6_interface; -extern const struct wl_interface zxdg_toplevel_v6_interface; - -static const struct wl_interface *xdg_shell_unstable_v6_types[] = { - NULL, - NULL, - NULL, - NULL, - &zxdg_positioner_v6_interface, - &zxdg_surface_v6_interface, - &wl_surface_interface, - &zxdg_toplevel_v6_interface, - &zxdg_popup_v6_interface, - &zxdg_surface_v6_interface, - &zxdg_positioner_v6_interface, - &zxdg_toplevel_v6_interface, - &wl_seat_interface, - NULL, - NULL, - NULL, - &wl_seat_interface, - NULL, - &wl_seat_interface, - NULL, - NULL, - &wl_output_interface, - &wl_seat_interface, - NULL, -}; - -static const struct wl_message zxdg_shell_v6_requests[] = { - { "destroy", "", xdg_shell_unstable_v6_types + 0 }, - { "create_positioner", "n", xdg_shell_unstable_v6_types + 4 }, - { "get_xdg_surface", "no", xdg_shell_unstable_v6_types + 5 }, - { "pong", "u", xdg_shell_unstable_v6_types + 0 }, -}; - -static const struct wl_message zxdg_shell_v6_events[] = { - { "ping", "u", xdg_shell_unstable_v6_types + 0 }, -}; - -WL_PRIVATE const struct wl_interface zxdg_shell_v6_interface = { - "zxdg_shell_v6", 1, - 4, zxdg_shell_v6_requests, - 1, zxdg_shell_v6_events, -}; - -static const struct wl_message zxdg_positioner_v6_requests[] = { - { "destroy", "", xdg_shell_unstable_v6_types + 0 }, - { "set_size", "ii", xdg_shell_unstable_v6_types + 0 }, - { "set_anchor_rect", "iiii", xdg_shell_unstable_v6_types + 0 }, - { "set_anchor", "u", xdg_shell_unstable_v6_types + 0 }, - { "set_gravity", "u", xdg_shell_unstable_v6_types + 0 }, - { "set_constraint_adjustment", "u", xdg_shell_unstable_v6_types + 0 }, - { "set_offset", "ii", xdg_shell_unstable_v6_types + 0 }, -}; - -WL_PRIVATE const struct wl_interface zxdg_positioner_v6_interface = { - "zxdg_positioner_v6", 1, - 7, zxdg_positioner_v6_requests, - 0, NULL, -}; - -static const struct wl_message zxdg_surface_v6_requests[] = { - { "destroy", "", xdg_shell_unstable_v6_types + 0 }, - { "get_toplevel", "n", xdg_shell_unstable_v6_types + 7 }, - { "get_popup", "noo", xdg_shell_unstable_v6_types + 8 }, - { "set_window_geometry", "iiii", xdg_shell_unstable_v6_types + 0 }, - { "ack_configure", "u", xdg_shell_unstable_v6_types + 0 }, -}; - -static const struct wl_message zxdg_surface_v6_events[] = { - { "configure", "u", xdg_shell_unstable_v6_types + 0 }, -}; - -WL_PRIVATE const struct wl_interface zxdg_surface_v6_interface = { - "zxdg_surface_v6", 1, - 5, zxdg_surface_v6_requests, - 1, zxdg_surface_v6_events, -}; - -static const struct wl_message zxdg_toplevel_v6_requests[] = { - { "destroy", "", xdg_shell_unstable_v6_types + 0 }, - { "set_parent", "?o", xdg_shell_unstable_v6_types + 11 }, - { "set_title", "s", xdg_shell_unstable_v6_types + 0 }, - { "set_app_id", "s", xdg_shell_unstable_v6_types + 0 }, - { "show_window_menu", "ouii", xdg_shell_unstable_v6_types + 12 }, - { "move", "ou", xdg_shell_unstable_v6_types + 16 }, - { "resize", "ouu", xdg_shell_unstable_v6_types + 18 }, - { "set_max_size", "ii", xdg_shell_unstable_v6_types + 0 }, - { "set_min_size", "ii", xdg_shell_unstable_v6_types + 0 }, - { "set_maximized", "", xdg_shell_unstable_v6_types + 0 }, - { "unset_maximized", "", xdg_shell_unstable_v6_types + 0 }, - { "set_fullscreen", "?o", xdg_shell_unstable_v6_types + 21 }, - { "unset_fullscreen", "", xdg_shell_unstable_v6_types + 0 }, - { "set_minimized", "", xdg_shell_unstable_v6_types + 0 }, -}; - -static const struct wl_message zxdg_toplevel_v6_events[] = { - { "configure", "iia", xdg_shell_unstable_v6_types + 0 }, - { "close", "", xdg_shell_unstable_v6_types + 0 }, -}; - -WL_PRIVATE const struct wl_interface zxdg_toplevel_v6_interface = { - "zxdg_toplevel_v6", 1, - 14, zxdg_toplevel_v6_requests, - 2, zxdg_toplevel_v6_events, -}; - -static const struct wl_message zxdg_popup_v6_requests[] = { - { "destroy", "", xdg_shell_unstable_v6_types + 0 }, - { "grab", "ou", xdg_shell_unstable_v6_types + 22 }, -}; - -static const struct wl_message zxdg_popup_v6_events[] = { - { "configure", "iiii", xdg_shell_unstable_v6_types + 0 }, - { "popup_done", "", xdg_shell_unstable_v6_types + 0 }, -}; - -WL_PRIVATE const struct wl_interface zxdg_popup_v6_interface = { - "zxdg_popup_v6", 1, - 2, zxdg_popup_v6_requests, - 2, zxdg_popup_v6_events, -}; - diff --git a/wayland/cc/xdg-shell.cc b/wayland/cc/xdg-shell.cc new file mode 100644 index 00000000..03826cdc --- /dev/null +++ b/wayland/cc/xdg-shell.cc @@ -0,0 +1,183 @@ +/* Generated by wayland-scanner 1.22.0 */ + +/* + * Copyright © 2008-2013 Kristian Høgsberg + * Copyright © 2013 Rafael Antognolli + * Copyright © 2013 Jasper St. Pierre + * Copyright © 2010-2013 Intel Corporation + * Copyright © 2015-2017 Samsung Electronics Co., Ltd + * Copyright © 2015-2017 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include +#include +#include "wayland-util.h" + +#ifndef __has_attribute +# define __has_attribute(x) 0 /* Compatibility with non-clang compilers. */ +#endif + +#if (__has_attribute(visibility) || defined(__GNUC__) && __GNUC__ >= 4) +#define WL_PRIVATE __attribute__ ((visibility("hidden"))) +#else +#define WL_PRIVATE +#endif + +extern const struct wl_interface wl_output_interface; +extern const struct wl_interface wl_seat_interface; +extern const struct wl_interface wl_surface_interface; +extern const struct wl_interface xdg_popup_interface; +extern const struct wl_interface xdg_positioner_interface; +extern const struct wl_interface xdg_surface_interface; +extern const struct wl_interface xdg_toplevel_interface; + +static const struct wl_interface *xdg_shell_types[] = { + NULL, + NULL, + NULL, + NULL, + &xdg_positioner_interface, + &xdg_surface_interface, + &wl_surface_interface, + &xdg_toplevel_interface, + &xdg_popup_interface, + &xdg_surface_interface, + &xdg_positioner_interface, + &xdg_toplevel_interface, + &wl_seat_interface, + NULL, + NULL, + NULL, + &wl_seat_interface, + NULL, + &wl_seat_interface, + NULL, + NULL, + &wl_output_interface, + &wl_seat_interface, + NULL, + &xdg_positioner_interface, + NULL, +}; + +static const struct wl_message xdg_wm_base_requests[] = { + { "destroy", "", xdg_shell_types + 0 }, + { "create_positioner", "n", xdg_shell_types + 4 }, + { "get_xdg_surface", "no", xdg_shell_types + 5 }, + { "pong", "u", xdg_shell_types + 0 }, +}; + +static const struct wl_message xdg_wm_base_events[] = { + { "ping", "u", xdg_shell_types + 0 }, +}; + +WL_PRIVATE const struct wl_interface xdg_wm_base_interface = { + "xdg_wm_base", 6, + 4, xdg_wm_base_requests, + 1, xdg_wm_base_events, +}; + +static const struct wl_message xdg_positioner_requests[] = { + { "destroy", "", xdg_shell_types + 0 }, + { "set_size", "ii", xdg_shell_types + 0 }, + { "set_anchor_rect", "iiii", xdg_shell_types + 0 }, + { "set_anchor", "u", xdg_shell_types + 0 }, + { "set_gravity", "u", xdg_shell_types + 0 }, + { "set_constraint_adjustment", "u", xdg_shell_types + 0 }, + { "set_offset", "ii", xdg_shell_types + 0 }, + { "set_reactive", "3", xdg_shell_types + 0 }, + { "set_parent_size", "3ii", xdg_shell_types + 0 }, + { "set_parent_configure", "3u", xdg_shell_types + 0 }, +}; + +WL_PRIVATE const struct wl_interface xdg_positioner_interface = { + "xdg_positioner", 6, + 10, xdg_positioner_requests, + 0, NULL, +}; + +static const struct wl_message xdg_surface_requests[] = { + { "destroy", "", xdg_shell_types + 0 }, + { "get_toplevel", "n", xdg_shell_types + 7 }, + { "get_popup", "n?oo", xdg_shell_types + 8 }, + { "set_window_geometry", "iiii", xdg_shell_types + 0 }, + { "ack_configure", "u", xdg_shell_types + 0 }, +}; + +static const struct wl_message xdg_surface_events[] = { + { "configure", "u", xdg_shell_types + 0 }, +}; + +WL_PRIVATE const struct wl_interface xdg_surface_interface = { + "xdg_surface", 6, + 5, xdg_surface_requests, + 1, xdg_surface_events, +}; + +static const struct wl_message xdg_toplevel_requests[] = { + { "destroy", "", xdg_shell_types + 0 }, + { "set_parent", "?o", xdg_shell_types + 11 }, + { "set_title", "s", xdg_shell_types + 0 }, + { "set_app_id", "s", xdg_shell_types + 0 }, + { "show_window_menu", "ouii", xdg_shell_types + 12 }, + { "move", "ou", xdg_shell_types + 16 }, + { "resize", "ouu", xdg_shell_types + 18 }, + { "set_max_size", "ii", xdg_shell_types + 0 }, + { "set_min_size", "ii", xdg_shell_types + 0 }, + { "set_maximized", "", xdg_shell_types + 0 }, + { "unset_maximized", "", xdg_shell_types + 0 }, + { "set_fullscreen", "?o", xdg_shell_types + 21 }, + { "unset_fullscreen", "", xdg_shell_types + 0 }, + { "set_minimized", "", xdg_shell_types + 0 }, +}; + +static const struct wl_message xdg_toplevel_events[] = { + { "configure", "iia", xdg_shell_types + 0 }, + { "close", "", xdg_shell_types + 0 }, + { "configure_bounds", "4ii", xdg_shell_types + 0 }, + { "wm_capabilities", "5a", xdg_shell_types + 0 }, +}; + +WL_PRIVATE const struct wl_interface xdg_toplevel_interface = { + "xdg_toplevel", 6, + 14, xdg_toplevel_requests, + 4, xdg_toplevel_events, +}; + +static const struct wl_message xdg_popup_requests[] = { + { "destroy", "", xdg_shell_types + 0 }, + { "grab", "ou", xdg_shell_types + 22 }, + { "reposition", "3ou", xdg_shell_types + 24 }, +}; + +static const struct wl_message xdg_popup_events[] = { + { "configure", "iiii", xdg_shell_types + 0 }, + { "popup_done", "", xdg_shell_types + 0 }, + { "repositioned", "3u", xdg_shell_types + 0 }, +}; + +WL_PRIVATE const struct wl_interface xdg_popup_interface = { + "xdg_popup", 6, + 3, xdg_popup_requests, + 3, xdg_popup_events, +}; + diff --git a/wayland/cc/xdg-shell.h b/wayland/cc/xdg-shell.h deleted file mode 100644 index 3f4d6846..00000000 --- a/wayland/cc/xdg-shell.h +++ /dev/null @@ -1,1884 +0,0 @@ -/* Generated by wayland-scanner 1.22.0 */ - -#ifndef XDG_SHELL_UNSTABLE_V6_CLIENT_PROTOCOL_H -#define XDG_SHELL_UNSTABLE_V6_CLIENT_PROTOCOL_H - -#include -#include -#include "wayland-client.h" - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * @page page_xdg_shell_unstable_v6 The xdg_shell_unstable_v6 protocol - * @section page_ifaces_xdg_shell_unstable_v6 Interfaces - * - @subpage page_iface_zxdg_shell_v6 - create desktop-style surfaces - * - @subpage page_iface_zxdg_positioner_v6 - child surface positioner - * - @subpage page_iface_zxdg_surface_v6 - desktop user interface surface base interface - * - @subpage page_iface_zxdg_toplevel_v6 - toplevel surface - * - @subpage page_iface_zxdg_popup_v6 - short-lived, popup surfaces for menus - * @section page_copyright_xdg_shell_unstable_v6 Copyright - *
- *
- * Copyright © 2008-2013 Kristian Høgsberg
- * Copyright © 2013      Rafael Antognolli
- * Copyright © 2013      Jasper St. Pierre
- * Copyright © 2010-2013 Intel Corporation
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice (including the next
- * paragraph) shall be included in all copies or substantial portions of the
- * Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- * 
- */ -struct wl_output; -struct wl_seat; -struct wl_surface; -struct zxdg_popup_v6; -struct zxdg_positioner_v6; -struct zxdg_shell_v6; -struct zxdg_surface_v6; -struct zxdg_toplevel_v6; - -#ifndef ZXDG_SHELL_V6_INTERFACE -#define ZXDG_SHELL_V6_INTERFACE -/** - * @page page_iface_zxdg_shell_v6 zxdg_shell_v6 - * @section page_iface_zxdg_shell_v6_desc Description - * - * xdg_shell allows clients to turn a wl_surface into a "real window" - * which can be dragged, resized, stacked, and moved around by the - * user. Everything about this interface is suited towards traditional - * desktop environments. - * @section page_iface_zxdg_shell_v6_api API - * See @ref iface_zxdg_shell_v6. - */ -/** - * @defgroup iface_zxdg_shell_v6 The zxdg_shell_v6 interface - * - * xdg_shell allows clients to turn a wl_surface into a "real window" - * which can be dragged, resized, stacked, and moved around by the - * user. Everything about this interface is suited towards traditional - * desktop environments. - */ -extern const struct wl_interface zxdg_shell_v6_interface; -#endif -#ifndef ZXDG_POSITIONER_V6_INTERFACE -#define ZXDG_POSITIONER_V6_INTERFACE -/** - * @page page_iface_zxdg_positioner_v6 zxdg_positioner_v6 - * @section page_iface_zxdg_positioner_v6_desc Description - * - * The xdg_positioner provides a collection of rules for the placement of a - * child surface relative to a parent surface. Rules can be defined to ensure - * the child surface remains within the visible area's borders, and to - * specify how the child surface changes its position, such as sliding along - * an axis, or flipping around a rectangle. These positioner-created rules are - * constrained by the requirement that a child surface must intersect with or - * be at least partially adjacent to its parent surface. - * - * See the various requests for details about possible rules. - * - * At the time of the request, the compositor makes a copy of the rules - * specified by the xdg_positioner. Thus, after the request is complete the - * xdg_positioner object can be destroyed or reused; further changes to the - * object will have no effect on previous usages. - * - * For an xdg_positioner object to be considered complete, it must have a - * non-zero size set by set_size, and a non-zero anchor rectangle set by - * set_anchor_rect. Passing an incomplete xdg_positioner object when - * positioning a surface raises an error. - * @section page_iface_zxdg_positioner_v6_api API - * See @ref iface_zxdg_positioner_v6. - */ -/** - * @defgroup iface_zxdg_positioner_v6 The zxdg_positioner_v6 interface - * - * The xdg_positioner provides a collection of rules for the placement of a - * child surface relative to a parent surface. Rules can be defined to ensure - * the child surface remains within the visible area's borders, and to - * specify how the child surface changes its position, such as sliding along - * an axis, or flipping around a rectangle. These positioner-created rules are - * constrained by the requirement that a child surface must intersect with or - * be at least partially adjacent to its parent surface. - * - * See the various requests for details about possible rules. - * - * At the time of the request, the compositor makes a copy of the rules - * specified by the xdg_positioner. Thus, after the request is complete the - * xdg_positioner object can be destroyed or reused; further changes to the - * object will have no effect on previous usages. - * - * For an xdg_positioner object to be considered complete, it must have a - * non-zero size set by set_size, and a non-zero anchor rectangle set by - * set_anchor_rect. Passing an incomplete xdg_positioner object when - * positioning a surface raises an error. - */ -extern const struct wl_interface zxdg_positioner_v6_interface; -#endif -#ifndef ZXDG_SURFACE_V6_INTERFACE -#define ZXDG_SURFACE_V6_INTERFACE -/** - * @page page_iface_zxdg_surface_v6 zxdg_surface_v6 - * @section page_iface_zxdg_surface_v6_desc Description - * - * An interface that may be implemented by a wl_surface, for - * implementations that provide a desktop-style user interface. - * - * It provides a base set of functionality required to construct user - * interface elements requiring management by the compositor, such as - * toplevel windows, menus, etc. The types of functionality are split into - * xdg_surface roles. - * - * Creating an xdg_surface does not set the role for a wl_surface. In order - * to map an xdg_surface, the client must create a role-specific object - * using, e.g., get_toplevel, get_popup. The wl_surface for any given - * xdg_surface can have at most one role, and may not be assigned any role - * not based on xdg_surface. - * - * A role must be assigned before any other requests are made to the - * xdg_surface object. - * - * The client must call wl_surface.commit on the corresponding wl_surface - * for the xdg_surface state to take effect. - * - * Creating an xdg_surface from a wl_surface which has a buffer attached or - * committed is a client error, and any attempts by a client to attach or - * manipulate a buffer prior to the first xdg_surface.configure call must - * also be treated as errors. - * - * For a surface to be mapped by the compositor, the following conditions - * must be met: (1) the client has assigned an xdg_surface based role to the - * surface, (2) the client has set and committed the xdg_surface state and - * the role dependent state to the surface and (3) the client has committed a - * buffer to the surface. - * @section page_iface_zxdg_surface_v6_api API - * See @ref iface_zxdg_surface_v6. - */ -/** - * @defgroup iface_zxdg_surface_v6 The zxdg_surface_v6 interface - * - * An interface that may be implemented by a wl_surface, for - * implementations that provide a desktop-style user interface. - * - * It provides a base set of functionality required to construct user - * interface elements requiring management by the compositor, such as - * toplevel windows, menus, etc. The types of functionality are split into - * xdg_surface roles. - * - * Creating an xdg_surface does not set the role for a wl_surface. In order - * to map an xdg_surface, the client must create a role-specific object - * using, e.g., get_toplevel, get_popup. The wl_surface for any given - * xdg_surface can have at most one role, and may not be assigned any role - * not based on xdg_surface. - * - * A role must be assigned before any other requests are made to the - * xdg_surface object. - * - * The client must call wl_surface.commit on the corresponding wl_surface - * for the xdg_surface state to take effect. - * - * Creating an xdg_surface from a wl_surface which has a buffer attached or - * committed is a client error, and any attempts by a client to attach or - * manipulate a buffer prior to the first xdg_surface.configure call must - * also be treated as errors. - * - * For a surface to be mapped by the compositor, the following conditions - * must be met: (1) the client has assigned an xdg_surface based role to the - * surface, (2) the client has set and committed the xdg_surface state and - * the role dependent state to the surface and (3) the client has committed a - * buffer to the surface. - */ -extern const struct wl_interface zxdg_surface_v6_interface; -#endif -#ifndef ZXDG_TOPLEVEL_V6_INTERFACE -#define ZXDG_TOPLEVEL_V6_INTERFACE -/** - * @page page_iface_zxdg_toplevel_v6 zxdg_toplevel_v6 - * @section page_iface_zxdg_toplevel_v6_desc Description - * - * This interface defines an xdg_surface role which allows a surface to, - * among other things, set window-like properties such as maximize, - * fullscreen, and minimize, set application-specific metadata like title and - * id, and well as trigger user interactive operations such as interactive - * resize and move. - * @section page_iface_zxdg_toplevel_v6_api API - * See @ref iface_zxdg_toplevel_v6. - */ -/** - * @defgroup iface_zxdg_toplevel_v6 The zxdg_toplevel_v6 interface - * - * This interface defines an xdg_surface role which allows a surface to, - * among other things, set window-like properties such as maximize, - * fullscreen, and minimize, set application-specific metadata like title and - * id, and well as trigger user interactive operations such as interactive - * resize and move. - */ -extern const struct wl_interface zxdg_toplevel_v6_interface; -#endif -#ifndef ZXDG_POPUP_V6_INTERFACE -#define ZXDG_POPUP_V6_INTERFACE -/** - * @page page_iface_zxdg_popup_v6 zxdg_popup_v6 - * @section page_iface_zxdg_popup_v6_desc Description - * - * A popup surface is a short-lived, temporary surface. It can be used to - * implement for example menus, popovers, tooltips and other similar user - * interface concepts. - * - * A popup can be made to take an explicit grab. See xdg_popup.grab for - * details. - * - * When the popup is dismissed, a popup_done event will be sent out, and at - * the same time the surface will be unmapped. See the xdg_popup.popup_done - * event for details. - * - * Explicitly destroying the xdg_popup object will also dismiss the popup and - * unmap the surface. Clients that want to dismiss the popup when another - * surface of their own is clicked should dismiss the popup using the destroy - * request. - * - * The parent surface must have either the xdg_toplevel or xdg_popup surface - * role. - * - * A newly created xdg_popup will be stacked on top of all previously created - * xdg_popup surfaces associated with the same xdg_toplevel. - * - * The parent of an xdg_popup must be mapped (see the xdg_surface - * description) before the xdg_popup itself. - * - * The x and y arguments passed when creating the popup object specify - * where the top left of the popup should be placed, relative to the - * local surface coordinates of the parent surface. See - * xdg_surface.get_popup. An xdg_popup must intersect with or be at least - * partially adjacent to its parent surface. - * - * The client must call wl_surface.commit on the corresponding wl_surface - * for the xdg_popup state to take effect. - * @section page_iface_zxdg_popup_v6_api API - * See @ref iface_zxdg_popup_v6. - */ -/** - * @defgroup iface_zxdg_popup_v6 The zxdg_popup_v6 interface - * - * A popup surface is a short-lived, temporary surface. It can be used to - * implement for example menus, popovers, tooltips and other similar user - * interface concepts. - * - * A popup can be made to take an explicit grab. See xdg_popup.grab for - * details. - * - * When the popup is dismissed, a popup_done event will be sent out, and at - * the same time the surface will be unmapped. See the xdg_popup.popup_done - * event for details. - * - * Explicitly destroying the xdg_popup object will also dismiss the popup and - * unmap the surface. Clients that want to dismiss the popup when another - * surface of their own is clicked should dismiss the popup using the destroy - * request. - * - * The parent surface must have either the xdg_toplevel or xdg_popup surface - * role. - * - * A newly created xdg_popup will be stacked on top of all previously created - * xdg_popup surfaces associated with the same xdg_toplevel. - * - * The parent of an xdg_popup must be mapped (see the xdg_surface - * description) before the xdg_popup itself. - * - * The x and y arguments passed when creating the popup object specify - * where the top left of the popup should be placed, relative to the - * local surface coordinates of the parent surface. See - * xdg_surface.get_popup. An xdg_popup must intersect with or be at least - * partially adjacent to its parent surface. - * - * The client must call wl_surface.commit on the corresponding wl_surface - * for the xdg_popup state to take effect. - */ -extern const struct wl_interface zxdg_popup_v6_interface; -#endif - -#ifndef ZXDG_SHELL_V6_ERROR_ENUM -#define ZXDG_SHELL_V6_ERROR_ENUM -enum zxdg_shell_v6_error { - /** - * given wl_surface has another role - */ - ZXDG_SHELL_V6_ERROR_ROLE = 0, - /** - * xdg_shell was destroyed before children - */ - ZXDG_SHELL_V6_ERROR_DEFUNCT_SURFACES = 1, - /** - * the client tried to map or destroy a non-topmost popup - */ - ZXDG_SHELL_V6_ERROR_NOT_THE_TOPMOST_POPUP = 2, - /** - * the client specified an invalid popup parent surface - */ - ZXDG_SHELL_V6_ERROR_INVALID_POPUP_PARENT = 3, - /** - * the client provided an invalid surface state - */ - ZXDG_SHELL_V6_ERROR_INVALID_SURFACE_STATE = 4, - /** - * the client provided an invalid positioner - */ - ZXDG_SHELL_V6_ERROR_INVALID_POSITIONER = 5, -}; -#endif /* ZXDG_SHELL_V6_ERROR_ENUM */ - -/** - * @ingroup iface_zxdg_shell_v6 - * @struct zxdg_shell_v6_listener - */ -struct zxdg_shell_v6_listener { - /** - * check if the client is alive - * - * The ping event asks the client if it's still alive. Pass the - * serial specified in the event back to the compositor by sending - * a "pong" request back with the specified serial. See - * xdg_shell.ping. - * - * Compositors can use this to determine if the client is still - * alive. It's unspecified what will happen if the client doesn't - * respond to the ping request, or in what timeframe. Clients - * should try to respond in a reasonable amount of time. - * - * A compositor is free to ping in any way it wants, but a client - * must always respond to any xdg_shell object it created. - * @param serial pass this to the pong request - */ - void (*ping)(void *data, - struct zxdg_shell_v6 *zxdg_shell_v6, - uint32_t serial); -}; - -/** - * @ingroup iface_zxdg_shell_v6 - */ -static inline int -zxdg_shell_v6_add_listener(struct zxdg_shell_v6 *zxdg_shell_v6, - const struct zxdg_shell_v6_listener *listener, void *data) -{ - return wl_proxy_add_listener((struct wl_proxy *) zxdg_shell_v6, - (void (**)(void)) listener, data); -} - -#define ZXDG_SHELL_V6_DESTROY 0 -#define ZXDG_SHELL_V6_CREATE_POSITIONER 1 -#define ZXDG_SHELL_V6_GET_XDG_SURFACE 2 -#define ZXDG_SHELL_V6_PONG 3 - -/** - * @ingroup iface_zxdg_shell_v6 - */ -#define ZXDG_SHELL_V6_PING_SINCE_VERSION 1 - -/** - * @ingroup iface_zxdg_shell_v6 - */ -#define ZXDG_SHELL_V6_DESTROY_SINCE_VERSION 1 -/** - * @ingroup iface_zxdg_shell_v6 - */ -#define ZXDG_SHELL_V6_CREATE_POSITIONER_SINCE_VERSION 1 -/** - * @ingroup iface_zxdg_shell_v6 - */ -#define ZXDG_SHELL_V6_GET_XDG_SURFACE_SINCE_VERSION 1 -/** - * @ingroup iface_zxdg_shell_v6 - */ -#define ZXDG_SHELL_V6_PONG_SINCE_VERSION 1 - -/** @ingroup iface_zxdg_shell_v6 */ -static inline void -zxdg_shell_v6_set_user_data(struct zxdg_shell_v6 *zxdg_shell_v6, void *user_data) -{ - wl_proxy_set_user_data((struct wl_proxy *) zxdg_shell_v6, user_data); -} - -/** @ingroup iface_zxdg_shell_v6 */ -static inline void * -zxdg_shell_v6_get_user_data(struct zxdg_shell_v6 *zxdg_shell_v6) -{ - return wl_proxy_get_user_data((struct wl_proxy *) zxdg_shell_v6); -} - -static inline uint32_t -zxdg_shell_v6_get_version(struct zxdg_shell_v6 *zxdg_shell_v6) -{ - return wl_proxy_get_version((struct wl_proxy *) zxdg_shell_v6); -} - -/** - * @ingroup iface_zxdg_shell_v6 - * - * Destroy this xdg_shell object. - * - * Destroying a bound xdg_shell object while there are surfaces - * still alive created by this xdg_shell object instance is illegal - * and will result in a protocol error. - */ -static inline void -zxdg_shell_v6_destroy(struct zxdg_shell_v6 *zxdg_shell_v6) -{ - wl_proxy_marshal_flags((struct wl_proxy *) zxdg_shell_v6, - ZXDG_SHELL_V6_DESTROY, NULL, wl_proxy_get_version((struct wl_proxy *) zxdg_shell_v6), WL_MARSHAL_FLAG_DESTROY); -} - -/** - * @ingroup iface_zxdg_shell_v6 - * - * Create a positioner object. A positioner object is used to position - * surfaces relative to some parent surface. See the interface description - * and xdg_surface.get_popup for details. - */ -static inline struct zxdg_positioner_v6 * -zxdg_shell_v6_create_positioner(struct zxdg_shell_v6 *zxdg_shell_v6) -{ - struct wl_proxy *id; - - id = wl_proxy_marshal_flags((struct wl_proxy *) zxdg_shell_v6, - ZXDG_SHELL_V6_CREATE_POSITIONER, &zxdg_positioner_v6_interface, wl_proxy_get_version((struct wl_proxy *) zxdg_shell_v6), 0, NULL); - - return (struct zxdg_positioner_v6 *) id; -} - -/** - * @ingroup iface_zxdg_shell_v6 - * - * This creates an xdg_surface for the given surface. While xdg_surface - * itself is not a role, the corresponding surface may only be assigned - * a role extending xdg_surface, such as xdg_toplevel or xdg_popup. - * - * This creates an xdg_surface for the given surface. An xdg_surface is - * used as basis to define a role to a given surface, such as xdg_toplevel - * or xdg_popup. It also manages functionality shared between xdg_surface - * based surface roles. - * - * See the documentation of xdg_surface for more details about what an - * xdg_surface is and how it is used. - */ -static inline struct zxdg_surface_v6 * -zxdg_shell_v6_get_xdg_surface(struct zxdg_shell_v6 *zxdg_shell_v6, struct wl_surface *surface) -{ - struct wl_proxy *id; - - id = wl_proxy_marshal_flags((struct wl_proxy *) zxdg_shell_v6, - ZXDG_SHELL_V6_GET_XDG_SURFACE, &zxdg_surface_v6_interface, wl_proxy_get_version((struct wl_proxy *) zxdg_shell_v6), 0, NULL, surface); - - return (struct zxdg_surface_v6 *) id; -} - -/** - * @ingroup iface_zxdg_shell_v6 - * - * A client must respond to a ping event with a pong request or - * the client may be deemed unresponsive. See xdg_shell.ping. - */ -static inline void -zxdg_shell_v6_pong(struct zxdg_shell_v6 *zxdg_shell_v6, uint32_t serial) -{ - wl_proxy_marshal_flags((struct wl_proxy *) zxdg_shell_v6, - ZXDG_SHELL_V6_PONG, NULL, wl_proxy_get_version((struct wl_proxy *) zxdg_shell_v6), 0, serial); -} - -#ifndef ZXDG_POSITIONER_V6_ERROR_ENUM -#define ZXDG_POSITIONER_V6_ERROR_ENUM -enum zxdg_positioner_v6_error { - /** - * invalid input provided - */ - ZXDG_POSITIONER_V6_ERROR_INVALID_INPUT = 0, -}; -#endif /* ZXDG_POSITIONER_V6_ERROR_ENUM */ - -#ifndef ZXDG_POSITIONER_V6_ANCHOR_ENUM -#define ZXDG_POSITIONER_V6_ANCHOR_ENUM -enum zxdg_positioner_v6_anchor { - /** - * the center of the anchor rectangle - */ - ZXDG_POSITIONER_V6_ANCHOR_NONE = 0, - /** - * the top edge of the anchor rectangle - */ - ZXDG_POSITIONER_V6_ANCHOR_TOP = 1, - /** - * the bottom edge of the anchor rectangle - */ - ZXDG_POSITIONER_V6_ANCHOR_BOTTOM = 2, - /** - * the left edge of the anchor rectangle - */ - ZXDG_POSITIONER_V6_ANCHOR_LEFT = 4, - /** - * the right edge of the anchor rectangle - */ - ZXDG_POSITIONER_V6_ANCHOR_RIGHT = 8, -}; -#endif /* ZXDG_POSITIONER_V6_ANCHOR_ENUM */ - -#ifndef ZXDG_POSITIONER_V6_GRAVITY_ENUM -#define ZXDG_POSITIONER_V6_GRAVITY_ENUM -enum zxdg_positioner_v6_gravity { - /** - * center over the anchor edge - */ - ZXDG_POSITIONER_V6_GRAVITY_NONE = 0, - /** - * position above the anchor edge - */ - ZXDG_POSITIONER_V6_GRAVITY_TOP = 1, - /** - * position below the anchor edge - */ - ZXDG_POSITIONER_V6_GRAVITY_BOTTOM = 2, - /** - * position to the left of the anchor edge - */ - ZXDG_POSITIONER_V6_GRAVITY_LEFT = 4, - /** - * position to the right of the anchor edge - */ - ZXDG_POSITIONER_V6_GRAVITY_RIGHT = 8, -}; -#endif /* ZXDG_POSITIONER_V6_GRAVITY_ENUM */ - -#ifndef ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_ENUM -#define ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_ENUM -/** - * @ingroup iface_zxdg_positioner_v6 - * constraint adjustments - * - * The constraint adjustment value define ways the compositor will adjust - * the position of the surface, if the unadjusted position would result - * in the surface being partly constrained. - * - * Whether a surface is considered 'constrained' is left to the compositor - * to determine. For example, the surface may be partly outside the - * compositor's defined 'work area', thus necessitating the child surface's - * position be adjusted until it is entirely inside the work area. - * - * The adjustments can be combined, according to a defined precedence: 1) - * Flip, 2) Slide, 3) Resize. - */ -enum zxdg_positioner_v6_constraint_adjustment { - /** - * don't move the child surface when constrained - * - * Don't alter the surface position even if it is constrained on - * some axis, for example partially outside the edge of a monitor. - */ - ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_NONE = 0, - /** - * move along the x axis until unconstrained - * - * Slide the surface along the x axis until it is no longer - * constrained. - * - * First try to slide towards the direction of the gravity on the x - * axis until either the edge in the opposite direction of the - * gravity is unconstrained or the edge in the direction of the - * gravity is constrained. - * - * Then try to slide towards the opposite direction of the gravity - * on the x axis until either the edge in the direction of the - * gravity is unconstrained or the edge in the opposite direction - * of the gravity is constrained. - */ - ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_SLIDE_X = 1, - /** - * move along the y axis until unconstrained - * - * Slide the surface along the y axis until it is no longer - * constrained. - * - * First try to slide towards the direction of the gravity on the y - * axis until either the edge in the opposite direction of the - * gravity is unconstrained or the edge in the direction of the - * gravity is constrained. - * - * Then try to slide towards the opposite direction of the gravity - * on the y axis until either the edge in the direction of the - * gravity is unconstrained or the edge in the opposite direction - * of the gravity is constrained. - */ - ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_SLIDE_Y = 2, - /** - * invert the anchor and gravity on the x axis - * - * Invert the anchor and gravity on the x axis if the surface is - * constrained on the x axis. For example, if the left edge of the - * surface is constrained, the gravity is 'left' and the anchor is - * 'left', change the gravity to 'right' and the anchor to 'right'. - * - * If the adjusted position also ends up being constrained, the - * resulting position of the flip_x adjustment will be the one - * before the adjustment. - */ - ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_FLIP_X = 4, - /** - * invert the anchor and gravity on the y axis - * - * Invert the anchor and gravity on the y axis if the surface is - * constrained on the y axis. For example, if the bottom edge of - * the surface is constrained, the gravity is 'bottom' and the - * anchor is 'bottom', change the gravity to 'top' and the anchor - * to 'top'. - * - * If the adjusted position also ends up being constrained, the - * resulting position of the flip_y adjustment will be the one - * before the adjustment. - */ - ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_FLIP_Y = 8, - /** - * horizontally resize the surface - * - * Resize the surface horizontally so that it is completely - * unconstrained. - */ - ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_RESIZE_X = 16, - /** - * vertically resize the surface - * - * Resize the surface vertically so that it is completely - * unconstrained. - */ - ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_RESIZE_Y = 32, -}; -#endif /* ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_ENUM */ - -#define ZXDG_POSITIONER_V6_DESTROY 0 -#define ZXDG_POSITIONER_V6_SET_SIZE 1 -#define ZXDG_POSITIONER_V6_SET_ANCHOR_RECT 2 -#define ZXDG_POSITIONER_V6_SET_ANCHOR 3 -#define ZXDG_POSITIONER_V6_SET_GRAVITY 4 -#define ZXDG_POSITIONER_V6_SET_CONSTRAINT_ADJUSTMENT 5 -#define ZXDG_POSITIONER_V6_SET_OFFSET 6 - - -/** - * @ingroup iface_zxdg_positioner_v6 - */ -#define ZXDG_POSITIONER_V6_DESTROY_SINCE_VERSION 1 -/** - * @ingroup iface_zxdg_positioner_v6 - */ -#define ZXDG_POSITIONER_V6_SET_SIZE_SINCE_VERSION 1 -/** - * @ingroup iface_zxdg_positioner_v6 - */ -#define ZXDG_POSITIONER_V6_SET_ANCHOR_RECT_SINCE_VERSION 1 -/** - * @ingroup iface_zxdg_positioner_v6 - */ -#define ZXDG_POSITIONER_V6_SET_ANCHOR_SINCE_VERSION 1 -/** - * @ingroup iface_zxdg_positioner_v6 - */ -#define ZXDG_POSITIONER_V6_SET_GRAVITY_SINCE_VERSION 1 -/** - * @ingroup iface_zxdg_positioner_v6 - */ -#define ZXDG_POSITIONER_V6_SET_CONSTRAINT_ADJUSTMENT_SINCE_VERSION 1 -/** - * @ingroup iface_zxdg_positioner_v6 - */ -#define ZXDG_POSITIONER_V6_SET_OFFSET_SINCE_VERSION 1 - -/** @ingroup iface_zxdg_positioner_v6 */ -static inline void -zxdg_positioner_v6_set_user_data(struct zxdg_positioner_v6 *zxdg_positioner_v6, void *user_data) -{ - wl_proxy_set_user_data((struct wl_proxy *) zxdg_positioner_v6, user_data); -} - -/** @ingroup iface_zxdg_positioner_v6 */ -static inline void * -zxdg_positioner_v6_get_user_data(struct zxdg_positioner_v6 *zxdg_positioner_v6) -{ - return wl_proxy_get_user_data((struct wl_proxy *) zxdg_positioner_v6); -} - -static inline uint32_t -zxdg_positioner_v6_get_version(struct zxdg_positioner_v6 *zxdg_positioner_v6) -{ - return wl_proxy_get_version((struct wl_proxy *) zxdg_positioner_v6); -} - -/** - * @ingroup iface_zxdg_positioner_v6 - * - * Notify the compositor that the xdg_positioner will no longer be used. - */ -static inline void -zxdg_positioner_v6_destroy(struct zxdg_positioner_v6 *zxdg_positioner_v6) -{ - wl_proxy_marshal_flags((struct wl_proxy *) zxdg_positioner_v6, - ZXDG_POSITIONER_V6_DESTROY, NULL, wl_proxy_get_version((struct wl_proxy *) zxdg_positioner_v6), WL_MARSHAL_FLAG_DESTROY); -} - -/** - * @ingroup iface_zxdg_positioner_v6 - * - * Set the size of the surface that is to be positioned with the positioner - * object. The size is in surface-local coordinates and corresponds to the - * window geometry. See xdg_surface.set_window_geometry. - * - * If a zero or negative size is set the invalid_input error is raised. - */ -static inline void -zxdg_positioner_v6_set_size(struct zxdg_positioner_v6 *zxdg_positioner_v6, int32_t width, int32_t height) -{ - wl_proxy_marshal_flags((struct wl_proxy *) zxdg_positioner_v6, - ZXDG_POSITIONER_V6_SET_SIZE, NULL, wl_proxy_get_version((struct wl_proxy *) zxdg_positioner_v6), 0, width, height); -} - -/** - * @ingroup iface_zxdg_positioner_v6 - * - * Specify the anchor rectangle within the parent surface that the child - * surface will be placed relative to. The rectangle is relative to the - * window geometry as defined by xdg_surface.set_window_geometry of the - * parent surface. The rectangle must be at least 1x1 large. - * - * When the xdg_positioner object is used to position a child surface, the - * anchor rectangle may not extend outside the window geometry of the - * positioned child's parent surface. - * - * If a zero or negative size is set the invalid_input error is raised. - */ -static inline void -zxdg_positioner_v6_set_anchor_rect(struct zxdg_positioner_v6 *zxdg_positioner_v6, int32_t x, int32_t y, int32_t width, int32_t height) -{ - wl_proxy_marshal_flags((struct wl_proxy *) zxdg_positioner_v6, - ZXDG_POSITIONER_V6_SET_ANCHOR_RECT, NULL, wl_proxy_get_version((struct wl_proxy *) zxdg_positioner_v6), 0, x, y, width, height); -} - -/** - * @ingroup iface_zxdg_positioner_v6 - * - * Defines a set of edges for the anchor rectangle. These are used to - * derive an anchor point that the child surface will be positioned - * relative to. If two orthogonal edges are specified (e.g. 'top' and - * 'left'), then the anchor point will be the intersection of the edges - * (e.g. the top left position of the rectangle); otherwise, the derived - * anchor point will be centered on the specified edge, or in the center of - * the anchor rectangle if no edge is specified. - * - * If two parallel anchor edges are specified (e.g. 'left' and 'right'), - * the invalid_input error is raised. - */ -static inline void -zxdg_positioner_v6_set_anchor(struct zxdg_positioner_v6 *zxdg_positioner_v6, uint32_t anchor) -{ - wl_proxy_marshal_flags((struct wl_proxy *) zxdg_positioner_v6, - ZXDG_POSITIONER_V6_SET_ANCHOR, NULL, wl_proxy_get_version((struct wl_proxy *) zxdg_positioner_v6), 0, anchor); -} - -/** - * @ingroup iface_zxdg_positioner_v6 - * - * Defines in what direction a surface should be positioned, relative to - * the anchor point of the parent surface. If two orthogonal gravities are - * specified (e.g. 'bottom' and 'right'), then the child surface will be - * placed in the specified direction; otherwise, the child surface will be - * centered over the anchor point on any axis that had no gravity - * specified. - * - * If two parallel gravities are specified (e.g. 'left' and 'right'), the - * invalid_input error is raised. - */ -static inline void -zxdg_positioner_v6_set_gravity(struct zxdg_positioner_v6 *zxdg_positioner_v6, uint32_t gravity) -{ - wl_proxy_marshal_flags((struct wl_proxy *) zxdg_positioner_v6, - ZXDG_POSITIONER_V6_SET_GRAVITY, NULL, wl_proxy_get_version((struct wl_proxy *) zxdg_positioner_v6), 0, gravity); -} - -/** - * @ingroup iface_zxdg_positioner_v6 - * - * Specify how the window should be positioned if the originally intended - * position caused the surface to be constrained, meaning at least - * partially outside positioning boundaries set by the compositor. The - * adjustment is set by constructing a bitmask describing the adjustment to - * be made when the surface is constrained on that axis. - * - * If no bit for one axis is set, the compositor will assume that the child - * surface should not change its position on that axis when constrained. - * - * If more than one bit for one axis is set, the order of how adjustments - * are applied is specified in the corresponding adjustment descriptions. - * - * The default adjustment is none. - */ -static inline void -zxdg_positioner_v6_set_constraint_adjustment(struct zxdg_positioner_v6 *zxdg_positioner_v6, uint32_t constraint_adjustment) -{ - wl_proxy_marshal_flags((struct wl_proxy *) zxdg_positioner_v6, - ZXDG_POSITIONER_V6_SET_CONSTRAINT_ADJUSTMENT, NULL, wl_proxy_get_version((struct wl_proxy *) zxdg_positioner_v6), 0, constraint_adjustment); -} - -/** - * @ingroup iface_zxdg_positioner_v6 - * - * Specify the surface position offset relative to the position of the - * anchor on the anchor rectangle and the anchor on the surface. For - * example if the anchor of the anchor rectangle is at (x, y), the surface - * has the gravity bottom|right, and the offset is (ox, oy), the calculated - * surface position will be (x + ox, y + oy). The offset position of the - * surface is the one used for constraint testing. See - * set_constraint_adjustment. - * - * An example use case is placing a popup menu on top of a user interface - * element, while aligning the user interface element of the parent surface - * with some user interface element placed somewhere in the popup surface. - */ -static inline void -zxdg_positioner_v6_set_offset(struct zxdg_positioner_v6 *zxdg_positioner_v6, int32_t x, int32_t y) -{ - wl_proxy_marshal_flags((struct wl_proxy *) zxdg_positioner_v6, - ZXDG_POSITIONER_V6_SET_OFFSET, NULL, wl_proxy_get_version((struct wl_proxy *) zxdg_positioner_v6), 0, x, y); -} - -#ifndef ZXDG_SURFACE_V6_ERROR_ENUM -#define ZXDG_SURFACE_V6_ERROR_ENUM -enum zxdg_surface_v6_error { - ZXDG_SURFACE_V6_ERROR_NOT_CONSTRUCTED = 1, - ZXDG_SURFACE_V6_ERROR_ALREADY_CONSTRUCTED = 2, - ZXDG_SURFACE_V6_ERROR_UNCONFIGURED_BUFFER = 3, -}; -#endif /* ZXDG_SURFACE_V6_ERROR_ENUM */ - -/** - * @ingroup iface_zxdg_surface_v6 - * @struct zxdg_surface_v6_listener - */ -struct zxdg_surface_v6_listener { - /** - * suggest a surface change - * - * The configure event marks the end of a configure sequence. A - * configure sequence is a set of one or more events configuring - * the state of the xdg_surface, including the final - * xdg_surface.configure event. - * - * Where applicable, xdg_surface surface roles will during a - * configure sequence extend this event as a latched state sent as - * events before the xdg_surface.configure event. Such events - * should be considered to make up a set of atomically applied - * configuration states, where the xdg_surface.configure commits - * the accumulated state. - * - * Clients should arrange their surface for the new states, and - * then send an ack_configure request with the serial sent in this - * configure event at some point before committing the new surface. - * - * If the client receives multiple configure events before it can - * respond to one, it is free to discard all but the last event it - * received. - * @param serial serial of the configure event - */ - void (*configure)(void *data, - struct zxdg_surface_v6 *zxdg_surface_v6, - uint32_t serial); -}; - -/** - * @ingroup iface_zxdg_surface_v6 - */ -static inline int -zxdg_surface_v6_add_listener(struct zxdg_surface_v6 *zxdg_surface_v6, - const struct zxdg_surface_v6_listener *listener, void *data) -{ - return wl_proxy_add_listener((struct wl_proxy *) zxdg_surface_v6, - (void (**)(void)) listener, data); -} - -#define ZXDG_SURFACE_V6_DESTROY 0 -#define ZXDG_SURFACE_V6_GET_TOPLEVEL 1 -#define ZXDG_SURFACE_V6_GET_POPUP 2 -#define ZXDG_SURFACE_V6_SET_WINDOW_GEOMETRY 3 -#define ZXDG_SURFACE_V6_ACK_CONFIGURE 4 - -/** - * @ingroup iface_zxdg_surface_v6 - */ -#define ZXDG_SURFACE_V6_CONFIGURE_SINCE_VERSION 1 - -/** - * @ingroup iface_zxdg_surface_v6 - */ -#define ZXDG_SURFACE_V6_DESTROY_SINCE_VERSION 1 -/** - * @ingroup iface_zxdg_surface_v6 - */ -#define ZXDG_SURFACE_V6_GET_TOPLEVEL_SINCE_VERSION 1 -/** - * @ingroup iface_zxdg_surface_v6 - */ -#define ZXDG_SURFACE_V6_GET_POPUP_SINCE_VERSION 1 -/** - * @ingroup iface_zxdg_surface_v6 - */ -#define ZXDG_SURFACE_V6_SET_WINDOW_GEOMETRY_SINCE_VERSION 1 -/** - * @ingroup iface_zxdg_surface_v6 - */ -#define ZXDG_SURFACE_V6_ACK_CONFIGURE_SINCE_VERSION 1 - -/** @ingroup iface_zxdg_surface_v6 */ -static inline void -zxdg_surface_v6_set_user_data(struct zxdg_surface_v6 *zxdg_surface_v6, void *user_data) -{ - wl_proxy_set_user_data((struct wl_proxy *) zxdg_surface_v6, user_data); -} - -/** @ingroup iface_zxdg_surface_v6 */ -static inline void * -zxdg_surface_v6_get_user_data(struct zxdg_surface_v6 *zxdg_surface_v6) -{ - return wl_proxy_get_user_data((struct wl_proxy *) zxdg_surface_v6); -} - -static inline uint32_t -zxdg_surface_v6_get_version(struct zxdg_surface_v6 *zxdg_surface_v6) -{ - return wl_proxy_get_version((struct wl_proxy *) zxdg_surface_v6); -} - -/** - * @ingroup iface_zxdg_surface_v6 - * - * Destroy the xdg_surface object. An xdg_surface must only be destroyed - * after its role object has been destroyed. If the role object still - * exists when this request is issued, the zxdg_shell_v6.defunct_surfaces - * is raised. - */ -static inline void -zxdg_surface_v6_destroy(struct zxdg_surface_v6 *zxdg_surface_v6) -{ - wl_proxy_marshal_flags((struct wl_proxy *) zxdg_surface_v6, - ZXDG_SURFACE_V6_DESTROY, NULL, wl_proxy_get_version((struct wl_proxy *) zxdg_surface_v6), WL_MARSHAL_FLAG_DESTROY); -} - -/** - * @ingroup iface_zxdg_surface_v6 - * - * This creates an xdg_toplevel object for the given xdg_surface and gives - * the associated wl_surface the xdg_toplevel role. If the surface already - * had a role, the zxdg_shell_v6.role error is raised. - * - * See the documentation of xdg_toplevel for more details about what an - * xdg_toplevel is and how it is used. - */ -static inline struct zxdg_toplevel_v6 * -zxdg_surface_v6_get_toplevel(struct zxdg_surface_v6 *zxdg_surface_v6) -{ - struct wl_proxy *id; - - id = wl_proxy_marshal_flags((struct wl_proxy *) zxdg_surface_v6, - ZXDG_SURFACE_V6_GET_TOPLEVEL, &zxdg_toplevel_v6_interface, wl_proxy_get_version((struct wl_proxy *) zxdg_surface_v6), 0, NULL); - - return (struct zxdg_toplevel_v6 *) id; -} - -/** - * @ingroup iface_zxdg_surface_v6 - * - * This creates an xdg_popup object for the given xdg_surface and gives the - * associated wl_surface the xdg_popup role. If the surface already - * had a role, the zxdg_shell_v6.role error is raised. - * - * See the documentation of xdg_popup for more details about what an - * xdg_popup is and how it is used. - */ -static inline struct zxdg_popup_v6 * -zxdg_surface_v6_get_popup(struct zxdg_surface_v6 *zxdg_surface_v6, struct zxdg_surface_v6 *parent, struct zxdg_positioner_v6 *positioner) -{ - struct wl_proxy *id; - - id = wl_proxy_marshal_flags((struct wl_proxy *) zxdg_surface_v6, - ZXDG_SURFACE_V6_GET_POPUP, &zxdg_popup_v6_interface, wl_proxy_get_version((struct wl_proxy *) zxdg_surface_v6), 0, NULL, parent, positioner); - - return (struct zxdg_popup_v6 *) id; -} - -/** - * @ingroup iface_zxdg_surface_v6 - * - * The window geometry of a surface is its "visible bounds" from the - * user's perspective. Client-side decorations often have invisible - * portions like drop-shadows which should be ignored for the - * purposes of aligning, placing and constraining windows. - * - * The window geometry is double buffered, and will be applied at the - * time wl_surface.commit of the corresponding wl_surface is called. - * - * Once the window geometry of the surface is set, it is not possible to - * unset it, and it will remain the same until set_window_geometry is - * called again, even if a new subsurface or buffer is attached. - * - * If never set, the value is the full bounds of the surface, - * including any subsurfaces. This updates dynamically on every - * commit. This unset is meant for extremely simple clients. - * - * The arguments are given in the surface-local coordinate space of - * the wl_surface associated with this xdg_surface. - * - * The width and height must be greater than zero. Setting an invalid size - * will raise an error. When applied, the effective window geometry will be - * the set window geometry clamped to the bounding rectangle of the - * combined geometry of the surface of the xdg_surface and the associated - * subsurfaces. - */ -static inline void -zxdg_surface_v6_set_window_geometry(struct zxdg_surface_v6 *zxdg_surface_v6, int32_t x, int32_t y, int32_t width, int32_t height) -{ - wl_proxy_marshal_flags((struct wl_proxy *) zxdg_surface_v6, - ZXDG_SURFACE_V6_SET_WINDOW_GEOMETRY, NULL, wl_proxy_get_version((struct wl_proxy *) zxdg_surface_v6), 0, x, y, width, height); -} - -/** - * @ingroup iface_zxdg_surface_v6 - * - * When a configure event is received, if a client commits the - * surface in response to the configure event, then the client - * must make an ack_configure request sometime before the commit - * request, passing along the serial of the configure event. - * - * For instance, for toplevel surfaces the compositor might use this - * information to move a surface to the top left only when the client has - * drawn itself for the maximized or fullscreen state. - * - * If the client receives multiple configure events before it - * can respond to one, it only has to ack the last configure event. - * - * A client is not required to commit immediately after sending - * an ack_configure request - it may even ack_configure several times - * before its next surface commit. - * - * A client may send multiple ack_configure requests before committing, but - * only the last request sent before a commit indicates which configure - * event the client really is responding to. - * - * If an invalid serial is used, the zxdg_shell_v6.invalid_surface_state - * error is raised. - */ -static inline void -zxdg_surface_v6_ack_configure(struct zxdg_surface_v6 *zxdg_surface_v6, uint32_t serial) -{ - wl_proxy_marshal_flags((struct wl_proxy *) zxdg_surface_v6, - ZXDG_SURFACE_V6_ACK_CONFIGURE, NULL, wl_proxy_get_version((struct wl_proxy *) zxdg_surface_v6), 0, serial); -} - -#ifndef ZXDG_TOPLEVEL_V6_RESIZE_EDGE_ENUM -#define ZXDG_TOPLEVEL_V6_RESIZE_EDGE_ENUM -/** - * @ingroup iface_zxdg_toplevel_v6 - * edge values for resizing - * - * These values are used to indicate which edge of a surface - * is being dragged in a resize operation. - */ -enum zxdg_toplevel_v6_resize_edge { - ZXDG_TOPLEVEL_V6_RESIZE_EDGE_NONE = 0, - ZXDG_TOPLEVEL_V6_RESIZE_EDGE_TOP = 1, - ZXDG_TOPLEVEL_V6_RESIZE_EDGE_BOTTOM = 2, - ZXDG_TOPLEVEL_V6_RESIZE_EDGE_LEFT = 4, - ZXDG_TOPLEVEL_V6_RESIZE_EDGE_TOP_LEFT = 5, - ZXDG_TOPLEVEL_V6_RESIZE_EDGE_BOTTOM_LEFT = 6, - ZXDG_TOPLEVEL_V6_RESIZE_EDGE_RIGHT = 8, - ZXDG_TOPLEVEL_V6_RESIZE_EDGE_TOP_RIGHT = 9, - ZXDG_TOPLEVEL_V6_RESIZE_EDGE_BOTTOM_RIGHT = 10, -}; -#endif /* ZXDG_TOPLEVEL_V6_RESIZE_EDGE_ENUM */ - -#ifndef ZXDG_TOPLEVEL_V6_STATE_ENUM -#define ZXDG_TOPLEVEL_V6_STATE_ENUM -/** - * @ingroup iface_zxdg_toplevel_v6 - * types of state on the surface - * - * The different state values used on the surface. This is designed for - * state values like maximized, fullscreen. It is paired with the - * configure event to ensure that both the client and the compositor - * setting the state can be synchronized. - * - * States set in this way are double-buffered. They will get applied on - * the next commit. - */ -enum zxdg_toplevel_v6_state { - /** - * the surface is maximized - * the surface is maximized - * - * The surface is maximized. The window geometry specified in the - * configure event must be obeyed by the client. If the window - * geometry is not obyed, the zxdg_shell_v6.invalid_surface_state - * error is raised. - */ - ZXDG_TOPLEVEL_V6_STATE_MAXIMIZED = 1, - /** - * the surface is fullscreen - * the surface is fullscreen - * - * The surface is fullscreen. See set_fullscreen for more - * information. - */ - ZXDG_TOPLEVEL_V6_STATE_FULLSCREEN = 2, - /** - * the surface is being resized - * the surface is being resized - * - * The surface is being resized. The window geometry specified in - * the configure event is a maximum; the client cannot resize - * beyond it. If the client attempts to resize above it, the - * zxdg_shell_v6.invalid_surface_state error is raised. Clients - * that have aspect ratio or cell sizing configuration can use a - * smaller size, however. - */ - ZXDG_TOPLEVEL_V6_STATE_RESIZING = 3, - /** - * the surface is now activated - * the surface is now activated - * - * Client window decorations should be painted as if the window - * is active. Do not assume this means that the window actually has - * keyboard or pointer focus. - */ - ZXDG_TOPLEVEL_V6_STATE_ACTIVATED = 4, -}; -#endif /* ZXDG_TOPLEVEL_V6_STATE_ENUM */ - -/** - * @ingroup iface_zxdg_toplevel_v6 - * @struct zxdg_toplevel_v6_listener - */ -struct zxdg_toplevel_v6_listener { - /** - * suggest a surface change - * - * This configure event asks the client to resize its toplevel - * surface or to change its state. The configured state should not - * be applied immediately. See xdg_surface.configure for details. - * - * The width and height arguments specify a hint to the window - * about how its surface should be resized in window geometry - * coordinates. See set_window_geometry. - * - * If the width or height arguments are zero, it means the client - * should decide its own window dimension. This may happen when the - * compositor needs to configure the state of the surface but - * doesn't have any information about any previous or expected - * dimension. - * - * The states listed in the event specify how the width/height - * arguments should be interpreted, and possibly how it should be - * drawn. - * - * Clients must send an ack_configure in response to this event. - * See xdg_surface.configure and xdg_surface.ack_configure for - * details. - */ - void (*configure)(void *data, - struct zxdg_toplevel_v6 *zxdg_toplevel_v6, - int32_t width, - int32_t height, - struct wl_array *states); - /** - * surface wants to be closed - * - * The close event is sent by the compositor when the user wants - * the surface to be closed. This should be equivalent to the user - * clicking the close button in client-side decorations, if your - * application has any. - * - * This is only a request that the user intends to close the - * window. The client may choose to ignore this request, or show a - * dialog to ask the user to save their data, etc. - */ - void (*close)(void *data, - struct zxdg_toplevel_v6 *zxdg_toplevel_v6); -}; - -/** - * @ingroup iface_zxdg_toplevel_v6 - */ -static inline int -zxdg_toplevel_v6_add_listener(struct zxdg_toplevel_v6 *zxdg_toplevel_v6, - const struct zxdg_toplevel_v6_listener *listener, void *data) -{ - return wl_proxy_add_listener((struct wl_proxy *) zxdg_toplevel_v6, - (void (**)(void)) listener, data); -} - -#define ZXDG_TOPLEVEL_V6_DESTROY 0 -#define ZXDG_TOPLEVEL_V6_SET_PARENT 1 -#define ZXDG_TOPLEVEL_V6_SET_TITLE 2 -#define ZXDG_TOPLEVEL_V6_SET_APP_ID 3 -#define ZXDG_TOPLEVEL_V6_SHOW_WINDOW_MENU 4 -#define ZXDG_TOPLEVEL_V6_MOVE 5 -#define ZXDG_TOPLEVEL_V6_RESIZE 6 -#define ZXDG_TOPLEVEL_V6_SET_MAX_SIZE 7 -#define ZXDG_TOPLEVEL_V6_SET_MIN_SIZE 8 -#define ZXDG_TOPLEVEL_V6_SET_MAXIMIZED 9 -#define ZXDG_TOPLEVEL_V6_UNSET_MAXIMIZED 10 -#define ZXDG_TOPLEVEL_V6_SET_FULLSCREEN 11 -#define ZXDG_TOPLEVEL_V6_UNSET_FULLSCREEN 12 -#define ZXDG_TOPLEVEL_V6_SET_MINIMIZED 13 - -/** - * @ingroup iface_zxdg_toplevel_v6 - */ -#define ZXDG_TOPLEVEL_V6_CONFIGURE_SINCE_VERSION 1 -/** - * @ingroup iface_zxdg_toplevel_v6 - */ -#define ZXDG_TOPLEVEL_V6_CLOSE_SINCE_VERSION 1 - -/** - * @ingroup iface_zxdg_toplevel_v6 - */ -#define ZXDG_TOPLEVEL_V6_DESTROY_SINCE_VERSION 1 -/** - * @ingroup iface_zxdg_toplevel_v6 - */ -#define ZXDG_TOPLEVEL_V6_SET_PARENT_SINCE_VERSION 1 -/** - * @ingroup iface_zxdg_toplevel_v6 - */ -#define ZXDG_TOPLEVEL_V6_SET_TITLE_SINCE_VERSION 1 -/** - * @ingroup iface_zxdg_toplevel_v6 - */ -#define ZXDG_TOPLEVEL_V6_SET_APP_ID_SINCE_VERSION 1 -/** - * @ingroup iface_zxdg_toplevel_v6 - */ -#define ZXDG_TOPLEVEL_V6_SHOW_WINDOW_MENU_SINCE_VERSION 1 -/** - * @ingroup iface_zxdg_toplevel_v6 - */ -#define ZXDG_TOPLEVEL_V6_MOVE_SINCE_VERSION 1 -/** - * @ingroup iface_zxdg_toplevel_v6 - */ -#define ZXDG_TOPLEVEL_V6_RESIZE_SINCE_VERSION 1 -/** - * @ingroup iface_zxdg_toplevel_v6 - */ -#define ZXDG_TOPLEVEL_V6_SET_MAX_SIZE_SINCE_VERSION 1 -/** - * @ingroup iface_zxdg_toplevel_v6 - */ -#define ZXDG_TOPLEVEL_V6_SET_MIN_SIZE_SINCE_VERSION 1 -/** - * @ingroup iface_zxdg_toplevel_v6 - */ -#define ZXDG_TOPLEVEL_V6_SET_MAXIMIZED_SINCE_VERSION 1 -/** - * @ingroup iface_zxdg_toplevel_v6 - */ -#define ZXDG_TOPLEVEL_V6_UNSET_MAXIMIZED_SINCE_VERSION 1 -/** - * @ingroup iface_zxdg_toplevel_v6 - */ -#define ZXDG_TOPLEVEL_V6_SET_FULLSCREEN_SINCE_VERSION 1 -/** - * @ingroup iface_zxdg_toplevel_v6 - */ -#define ZXDG_TOPLEVEL_V6_UNSET_FULLSCREEN_SINCE_VERSION 1 -/** - * @ingroup iface_zxdg_toplevel_v6 - */ -#define ZXDG_TOPLEVEL_V6_SET_MINIMIZED_SINCE_VERSION 1 - -/** @ingroup iface_zxdg_toplevel_v6 */ -static inline void -zxdg_toplevel_v6_set_user_data(struct zxdg_toplevel_v6 *zxdg_toplevel_v6, void *user_data) -{ - wl_proxy_set_user_data((struct wl_proxy *) zxdg_toplevel_v6, user_data); -} - -/** @ingroup iface_zxdg_toplevel_v6 */ -static inline void * -zxdg_toplevel_v6_get_user_data(struct zxdg_toplevel_v6 *zxdg_toplevel_v6) -{ - return wl_proxy_get_user_data((struct wl_proxy *) zxdg_toplevel_v6); -} - -static inline uint32_t -zxdg_toplevel_v6_get_version(struct zxdg_toplevel_v6 *zxdg_toplevel_v6) -{ - return wl_proxy_get_version((struct wl_proxy *) zxdg_toplevel_v6); -} - -/** - * @ingroup iface_zxdg_toplevel_v6 - * - * Unmap and destroy the window. The window will be effectively - * hidden from the user's point of view, and all state like - * maximization, fullscreen, and so on, will be lost. - */ -static inline void -zxdg_toplevel_v6_destroy(struct zxdg_toplevel_v6 *zxdg_toplevel_v6) -{ - wl_proxy_marshal_flags((struct wl_proxy *) zxdg_toplevel_v6, - ZXDG_TOPLEVEL_V6_DESTROY, NULL, wl_proxy_get_version((struct wl_proxy *) zxdg_toplevel_v6), WL_MARSHAL_FLAG_DESTROY); -} - -/** - * @ingroup iface_zxdg_toplevel_v6 - * - * Set the "parent" of this surface. This window should be stacked - * above a parent. The parent surface must be mapped as long as this - * surface is mapped. - * - * Parent windows should be set on dialogs, toolboxes, or other - * "auxiliary" surfaces, so that the parent is raised when the dialog - * is raised. - */ -static inline void -zxdg_toplevel_v6_set_parent(struct zxdg_toplevel_v6 *zxdg_toplevel_v6, struct zxdg_toplevel_v6 *parent) -{ - wl_proxy_marshal_flags((struct wl_proxy *) zxdg_toplevel_v6, - ZXDG_TOPLEVEL_V6_SET_PARENT, NULL, wl_proxy_get_version((struct wl_proxy *) zxdg_toplevel_v6), 0, parent); -} - -/** - * @ingroup iface_zxdg_toplevel_v6 - * - * Set a short title for the surface. - * - * This string may be used to identify the surface in a task bar, - * window list, or other user interface elements provided by the - * compositor. - * - * The string must be encoded in UTF-8. - */ -static inline void -zxdg_toplevel_v6_set_title(struct zxdg_toplevel_v6 *zxdg_toplevel_v6, const char *title) -{ - wl_proxy_marshal_flags((struct wl_proxy *) zxdg_toplevel_v6, - ZXDG_TOPLEVEL_V6_SET_TITLE, NULL, wl_proxy_get_version((struct wl_proxy *) zxdg_toplevel_v6), 0, title); -} - -/** - * @ingroup iface_zxdg_toplevel_v6 - * - * Set an application identifier for the surface. - * - * The app ID identifies the general class of applications to which - * the surface belongs. The compositor can use this to group multiple - * surfaces together, or to determine how to launch a new application. - * - * For D-Bus activatable applications, the app ID is used as the D-Bus - * service name. - * - * The compositor shell will try to group application surfaces together - * by their app ID. As a best practice, it is suggested to select app - * ID's that match the basename of the application's .desktop file. - * For example, "org.freedesktop.FooViewer" where the .desktop file is - * "org.freedesktop.FooViewer.desktop". - * - * See the desktop-entry specification [0] for more details on - * application identifiers and how they relate to well-known D-Bus - * names and .desktop files. - * - * [0] http://standards.freedesktop.org/desktop-entry-spec/ - */ -static inline void -zxdg_toplevel_v6_set_app_id(struct zxdg_toplevel_v6 *zxdg_toplevel_v6, const char *app_id) -{ - wl_proxy_marshal_flags((struct wl_proxy *) zxdg_toplevel_v6, - ZXDG_TOPLEVEL_V6_SET_APP_ID, NULL, wl_proxy_get_version((struct wl_proxy *) zxdg_toplevel_v6), 0, app_id); -} - -/** - * @ingroup iface_zxdg_toplevel_v6 - * - * Clients implementing client-side decorations might want to show - * a context menu when right-clicking on the decorations, giving the - * user a menu that they can use to maximize or minimize the window. - * - * This request asks the compositor to pop up such a window menu at - * the given position, relative to the local surface coordinates of - * the parent surface. There are no guarantees as to what menu items - * the window menu contains. - * - * This request must be used in response to some sort of user action - * like a button press, key press, or touch down event. - */ -static inline void -zxdg_toplevel_v6_show_window_menu(struct zxdg_toplevel_v6 *zxdg_toplevel_v6, struct wl_seat *seat, uint32_t serial, int32_t x, int32_t y) -{ - wl_proxy_marshal_flags((struct wl_proxy *) zxdg_toplevel_v6, - ZXDG_TOPLEVEL_V6_SHOW_WINDOW_MENU, NULL, wl_proxy_get_version((struct wl_proxy *) zxdg_toplevel_v6), 0, seat, serial, x, y); -} - -/** - * @ingroup iface_zxdg_toplevel_v6 - * - * Start an interactive, user-driven move of the surface. - * - * This request must be used in response to some sort of user action - * like a button press, key press, or touch down event. The passed - * serial is used to determine the type of interactive move (touch, - * pointer, etc). - * - * The server may ignore move requests depending on the state of - * the surface (e.g. fullscreen or maximized), or if the passed serial - * is no longer valid. - * - * If triggered, the surface will lose the focus of the device - * (wl_pointer, wl_touch, etc) used for the move. It is up to the - * compositor to visually indicate that the move is taking place, such as - * updating a pointer cursor, during the move. There is no guarantee - * that the device focus will return when the move is completed. - */ -static inline void -zxdg_toplevel_v6_move(struct zxdg_toplevel_v6 *zxdg_toplevel_v6, struct wl_seat *seat, uint32_t serial) -{ - wl_proxy_marshal_flags((struct wl_proxy *) zxdg_toplevel_v6, - ZXDG_TOPLEVEL_V6_MOVE, NULL, wl_proxy_get_version((struct wl_proxy *) zxdg_toplevel_v6), 0, seat, serial); -} - -/** - * @ingroup iface_zxdg_toplevel_v6 - * - * Start a user-driven, interactive resize of the surface. - * - * This request must be used in response to some sort of user action - * like a button press, key press, or touch down event. The passed - * serial is used to determine the type of interactive resize (touch, - * pointer, etc). - * - * The server may ignore resize requests depending on the state of - * the surface (e.g. fullscreen or maximized). - * - * If triggered, the client will receive configure events with the - * "resize" state enum value and the expected sizes. See the "resize" - * enum value for more details about what is required. The client - * must also acknowledge configure events using "ack_configure". After - * the resize is completed, the client will receive another "configure" - * event without the resize state. - * - * If triggered, the surface also will lose the focus of the device - * (wl_pointer, wl_touch, etc) used for the resize. It is up to the - * compositor to visually indicate that the resize is taking place, - * such as updating a pointer cursor, during the resize. There is no - * guarantee that the device focus will return when the resize is - * completed. - * - * The edges parameter specifies how the surface should be resized, - * and is one of the values of the resize_edge enum. The compositor - * may use this information to update the surface position for - * example when dragging the top left corner. The compositor may also - * use this information to adapt its behavior, e.g. choose an - * appropriate cursor image. - */ -static inline void -zxdg_toplevel_v6_resize(struct zxdg_toplevel_v6 *zxdg_toplevel_v6, struct wl_seat *seat, uint32_t serial, uint32_t edges) -{ - wl_proxy_marshal_flags((struct wl_proxy *) zxdg_toplevel_v6, - ZXDG_TOPLEVEL_V6_RESIZE, NULL, wl_proxy_get_version((struct wl_proxy *) zxdg_toplevel_v6), 0, seat, serial, edges); -} - -/** - * @ingroup iface_zxdg_toplevel_v6 - * - * Set a maximum size for the window. - * - * The client can specify a maximum size so that the compositor does - * not try to configure the window beyond this size. - * - * The width and height arguments are in window geometry coordinates. - * See xdg_surface.set_window_geometry. - * - * Values set in this way are double-buffered. They will get applied - * on the next commit. - * - * The compositor can use this information to allow or disallow - * different states like maximize or fullscreen and draw accurate - * animations. - * - * Similarly, a tiling window manager may use this information to - * place and resize client windows in a more effective way. - * - * The client should not rely on the compositor to obey the maximum - * size. The compositor may decide to ignore the values set by the - * client and request a larger size. - * - * If never set, or a value of zero in the request, means that the - * client has no expected maximum size in the given dimension. - * As a result, a client wishing to reset the maximum size - * to an unspecified state can use zero for width and height in the - * request. - * - * Requesting a maximum size to be smaller than the minimum size of - * a surface is illegal and will result in a protocol error. - * - * The width and height must be greater than or equal to zero. Using - * strictly negative values for width and height will result in the - * zxdg_shell_v6.invalid_surface_state error being raised. - */ -static inline void -zxdg_toplevel_v6_set_max_size(struct zxdg_toplevel_v6 *zxdg_toplevel_v6, int32_t width, int32_t height) -{ - wl_proxy_marshal_flags((struct wl_proxy *) zxdg_toplevel_v6, - ZXDG_TOPLEVEL_V6_SET_MAX_SIZE, NULL, wl_proxy_get_version((struct wl_proxy *) zxdg_toplevel_v6), 0, width, height); -} - -/** - * @ingroup iface_zxdg_toplevel_v6 - * - * Set a minimum size for the window. - * - * The client can specify a minimum size so that the compositor does - * not try to configure the window below this size. - * - * The width and height arguments are in window geometry coordinates. - * See xdg_surface.set_window_geometry. - * - * Values set in this way are double-buffered. They will get applied - * on the next commit. - * - * The compositor can use this information to allow or disallow - * different states like maximize or fullscreen and draw accurate - * animations. - * - * Similarly, a tiling window manager may use this information to - * place and resize client windows in a more effective way. - * - * The client should not rely on the compositor to obey the minimum - * size. The compositor may decide to ignore the values set by the - * client and request a smaller size. - * - * If never set, or a value of zero in the request, means that the - * client has no expected minimum size in the given dimension. - * As a result, a client wishing to reset the minimum size - * to an unspecified state can use zero for width and height in the - * request. - * - * Requesting a minimum size to be larger than the maximum size of - * a surface is illegal and will result in a protocol error. - * - * The width and height must be greater than or equal to zero. Using - * strictly negative values for width and height will result in the - * zxdg_shell_v6.invalid_surface_state error being raised. - */ -static inline void -zxdg_toplevel_v6_set_min_size(struct zxdg_toplevel_v6 *zxdg_toplevel_v6, int32_t width, int32_t height) -{ - wl_proxy_marshal_flags((struct wl_proxy *) zxdg_toplevel_v6, - ZXDG_TOPLEVEL_V6_SET_MIN_SIZE, NULL, wl_proxy_get_version((struct wl_proxy *) zxdg_toplevel_v6), 0, width, height); -} - -/** - * @ingroup iface_zxdg_toplevel_v6 - * - * Maximize the surface. - * - * After requesting that the surface should be maximized, the compositor - * will respond by emitting a configure event with the "maximized" state - * and the required window geometry. The client should then update its - * content, drawing it in a maximized state, i.e. without shadow or other - * decoration outside of the window geometry. The client must also - * acknowledge the configure when committing the new content (see - * ack_configure). - * - * It is up to the compositor to decide how and where to maximize the - * surface, for example which output and what region of the screen should - * be used. - * - * If the surface was already maximized, the compositor will still emit - * a configure event with the "maximized" state. - * - * Note that unrelated compositor side state changes may cause - * configure events to be emitted at any time, meaning trying to - * match this request to a specific future configure event is - * futile. - */ -static inline void -zxdg_toplevel_v6_set_maximized(struct zxdg_toplevel_v6 *zxdg_toplevel_v6) -{ - wl_proxy_marshal_flags((struct wl_proxy *) zxdg_toplevel_v6, - ZXDG_TOPLEVEL_V6_SET_MAXIMIZED, NULL, wl_proxy_get_version((struct wl_proxy *) zxdg_toplevel_v6), 0); -} - -/** - * @ingroup iface_zxdg_toplevel_v6 - * - * Unmaximize the surface. - * - * After requesting that the surface should be unmaximized, the compositor - * will respond by emitting a configure event without the "maximized" - * state. If available, the compositor will include the window geometry - * dimensions the window had prior to being maximized in the configure - * request. The client must then update its content, drawing it in a - * regular state, i.e. potentially with shadow, etc. The client must also - * acknowledge the configure when committing the new content (see - * ack_configure). - * - * It is up to the compositor to position the surface after it was - * unmaximized; usually the position the surface had before maximizing, if - * applicable. - * - * If the surface was already not maximized, the compositor will still - * emit a configure event without the "maximized" state. - * - * Note that unrelated changes in the state of compositor may cause - * configure events to be emitted by the compositor between processing - * this request and emitting corresponding configure event, so trying - * to match the request with the event is futile. - */ -static inline void -zxdg_toplevel_v6_unset_maximized(struct zxdg_toplevel_v6 *zxdg_toplevel_v6) -{ - wl_proxy_marshal_flags((struct wl_proxy *) zxdg_toplevel_v6, - ZXDG_TOPLEVEL_V6_UNSET_MAXIMIZED, NULL, wl_proxy_get_version((struct wl_proxy *) zxdg_toplevel_v6), 0); -} - -/** - * @ingroup iface_zxdg_toplevel_v6 - * - * Make the surface fullscreen. - * - * You can specify an output that you would prefer to be fullscreen. - * If this value is NULL, it's up to the compositor to choose which - * display will be used to map this surface. - * - * If the surface doesn't cover the whole output, the compositor will - * position the surface in the center of the output and compensate with - * black borders filling the rest of the output. - */ -static inline void -zxdg_toplevel_v6_set_fullscreen(struct zxdg_toplevel_v6 *zxdg_toplevel_v6, struct wl_output *output) -{ - wl_proxy_marshal_flags((struct wl_proxy *) zxdg_toplevel_v6, - ZXDG_TOPLEVEL_V6_SET_FULLSCREEN, NULL, wl_proxy_get_version((struct wl_proxy *) zxdg_toplevel_v6), 0, output); -} - -/** - * @ingroup iface_zxdg_toplevel_v6 - */ -static inline void -zxdg_toplevel_v6_unset_fullscreen(struct zxdg_toplevel_v6 *zxdg_toplevel_v6) -{ - wl_proxy_marshal_flags((struct wl_proxy *) zxdg_toplevel_v6, - ZXDG_TOPLEVEL_V6_UNSET_FULLSCREEN, NULL, wl_proxy_get_version((struct wl_proxy *) zxdg_toplevel_v6), 0); -} - -/** - * @ingroup iface_zxdg_toplevel_v6 - * - * Request that the compositor minimize your surface. There is no - * way to know if the surface is currently minimized, nor is there - * any way to unset minimization on this surface. - * - * If you are looking to throttle redrawing when minimized, please - * instead use the wl_surface.frame event for this, as this will - * also work with live previews on windows in Alt-Tab, Expose or - * similar compositor features. - */ -static inline void -zxdg_toplevel_v6_set_minimized(struct zxdg_toplevel_v6 *zxdg_toplevel_v6) -{ - wl_proxy_marshal_flags((struct wl_proxy *) zxdg_toplevel_v6, - ZXDG_TOPLEVEL_V6_SET_MINIMIZED, NULL, wl_proxy_get_version((struct wl_proxy *) zxdg_toplevel_v6), 0); -} - -#ifndef ZXDG_POPUP_V6_ERROR_ENUM -#define ZXDG_POPUP_V6_ERROR_ENUM -enum zxdg_popup_v6_error { - /** - * tried to grab after being mapped - */ - ZXDG_POPUP_V6_ERROR_INVALID_GRAB = 0, -}; -#endif /* ZXDG_POPUP_V6_ERROR_ENUM */ - -/** - * @ingroup iface_zxdg_popup_v6 - * @struct zxdg_popup_v6_listener - */ -struct zxdg_popup_v6_listener { - /** - * configure the popup surface - * - * This event asks the popup surface to configure itself given - * the configuration. The configured state should not be applied - * immediately. See xdg_surface.configure for details. - * - * The x and y arguments represent the position the popup was - * placed at given the xdg_positioner rule, relative to the upper - * left corner of the window geometry of the parent surface. - * @param x x position relative to parent surface window geometry - * @param y y position relative to parent surface window geometry - * @param width window geometry width - * @param height window geometry height - */ - void (*configure)(void *data, - struct zxdg_popup_v6 *zxdg_popup_v6, - int32_t x, - int32_t y, - int32_t width, - int32_t height); - /** - * popup interaction is done - * - * The popup_done event is sent out when a popup is dismissed by - * the compositor. The client should destroy the xdg_popup object - * at this point. - */ - void (*popup_done)(void *data, - struct zxdg_popup_v6 *zxdg_popup_v6); -}; - -/** - * @ingroup iface_zxdg_popup_v6 - */ -static inline int -zxdg_popup_v6_add_listener(struct zxdg_popup_v6 *zxdg_popup_v6, - const struct zxdg_popup_v6_listener *listener, void *data) -{ - return wl_proxy_add_listener((struct wl_proxy *) zxdg_popup_v6, - (void (**)(void)) listener, data); -} - -#define ZXDG_POPUP_V6_DESTROY 0 -#define ZXDG_POPUP_V6_GRAB 1 - -/** - * @ingroup iface_zxdg_popup_v6 - */ -#define ZXDG_POPUP_V6_CONFIGURE_SINCE_VERSION 1 -/** - * @ingroup iface_zxdg_popup_v6 - */ -#define ZXDG_POPUP_V6_POPUP_DONE_SINCE_VERSION 1 - -/** - * @ingroup iface_zxdg_popup_v6 - */ -#define ZXDG_POPUP_V6_DESTROY_SINCE_VERSION 1 -/** - * @ingroup iface_zxdg_popup_v6 - */ -#define ZXDG_POPUP_V6_GRAB_SINCE_VERSION 1 - -/** @ingroup iface_zxdg_popup_v6 */ -static inline void -zxdg_popup_v6_set_user_data(struct zxdg_popup_v6 *zxdg_popup_v6, void *user_data) -{ - wl_proxy_set_user_data((struct wl_proxy *) zxdg_popup_v6, user_data); -} - -/** @ingroup iface_zxdg_popup_v6 */ -static inline void * -zxdg_popup_v6_get_user_data(struct zxdg_popup_v6 *zxdg_popup_v6) -{ - return wl_proxy_get_user_data((struct wl_proxy *) zxdg_popup_v6); -} - -static inline uint32_t -zxdg_popup_v6_get_version(struct zxdg_popup_v6 *zxdg_popup_v6) -{ - return wl_proxy_get_version((struct wl_proxy *) zxdg_popup_v6); -} - -/** - * @ingroup iface_zxdg_popup_v6 - * - * This destroys the popup. Explicitly destroying the xdg_popup - * object will also dismiss the popup, and unmap the surface. - * - * If this xdg_popup is not the "topmost" popup, a protocol error - * will be sent. - */ -static inline void -zxdg_popup_v6_destroy(struct zxdg_popup_v6 *zxdg_popup_v6) -{ - wl_proxy_marshal_flags((struct wl_proxy *) zxdg_popup_v6, - ZXDG_POPUP_V6_DESTROY, NULL, wl_proxy_get_version((struct wl_proxy *) zxdg_popup_v6), WL_MARSHAL_FLAG_DESTROY); -} - -/** - * @ingroup iface_zxdg_popup_v6 - * - * This request makes the created popup take an explicit grab. An explicit - * grab will be dismissed when the user dismisses the popup, or when the - * client destroys the xdg_popup. This can be done by the user clicking - * outside the surface, using the keyboard, or even locking the screen - * through closing the lid or a timeout. - * - * If the compositor denies the grab, the popup will be immediately - * dismissed. - * - * This request must be used in response to some sort of user action like a - * button press, key press, or touch down event. The serial number of the - * event should be passed as 'serial'. - * - * The parent of a grabbing popup must either be an xdg_toplevel surface or - * another xdg_popup with an explicit grab. If the parent is another - * xdg_popup it means that the popups are nested, with this popup now being - * the topmost popup. - * - * Nested popups must be destroyed in the reverse order they were created - * in, e.g. the only popup you are allowed to destroy at all times is the - * topmost one. - * - * When compositors choose to dismiss a popup, they may dismiss every - * nested grabbing popup as well. When a compositor dismisses popups, it - * will follow the same dismissing order as required from the client. - * - * The parent of a grabbing popup must either be another xdg_popup with an - * active explicit grab, or an xdg_popup or xdg_toplevel, if there are no - * explicit grabs already taken. - * - * If the topmost grabbing popup is destroyed, the grab will be returned to - * the parent of the popup, if that parent previously had an explicit grab. - * - * If the parent is a grabbing popup which has already been dismissed, this - * popup will be immediately dismissed. If the parent is a popup that did - * not take an explicit grab, an error will be raised. - * - * During a popup grab, the client owning the grab will receive pointer - * and touch events for all their surfaces as normal (similar to an - * "owner-events" grab in X11 parlance), while the top most grabbing popup - * will always have keyboard focus. - */ -static inline void -zxdg_popup_v6_grab(struct zxdg_popup_v6 *zxdg_popup_v6, struct wl_seat *seat, uint32_t serial) -{ - wl_proxy_marshal_flags((struct wl_proxy *) zxdg_popup_v6, - ZXDG_POPUP_V6_GRAB, NULL, wl_proxy_get_version((struct wl_proxy *) zxdg_popup_v6), 0, seat, serial); -} - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/wayland/cc/xdg-shell.hh b/wayland/cc/xdg-shell.hh new file mode 100644 index 00000000..ea75d476 --- /dev/null +++ b/wayland/cc/xdg-shell.hh @@ -0,0 +1,2307 @@ +/* Generated by wayland-scanner 1.22.0 */ + +#ifndef XDG_SHELL_CLIENT_PROTOCOL_H +#define XDG_SHELL_CLIENT_PROTOCOL_H + +#include +#include +#include "wayland-client.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @page page_xdg_shell The xdg_shell protocol + * @section page_ifaces_xdg_shell Interfaces + * - @subpage page_iface_xdg_wm_base - create desktop-style surfaces + * - @subpage page_iface_xdg_positioner - child surface positioner + * - @subpage page_iface_xdg_surface - desktop user interface surface base interface + * - @subpage page_iface_xdg_toplevel - toplevel surface + * - @subpage page_iface_xdg_popup - short-lived, popup surfaces for menus + * @section page_copyright_xdg_shell Copyright + *
+ *
+ * Copyright © 2008-2013 Kristian Høgsberg
+ * Copyright © 2013      Rafael Antognolli
+ * Copyright © 2013      Jasper St. Pierre
+ * Copyright © 2010-2013 Intel Corporation
+ * Copyright © 2015-2017 Samsung Electronics Co., Ltd
+ * Copyright © 2015-2017 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ * 
+ */ +struct wl_output; +struct wl_seat; +struct wl_surface; +struct xdg_popup; +struct xdg_positioner; +struct xdg_surface; +struct xdg_toplevel; +struct xdg_wm_base; + +#ifndef XDG_WM_BASE_INTERFACE +#define XDG_WM_BASE_INTERFACE +/** + * @page page_iface_xdg_wm_base xdg_wm_base + * @section page_iface_xdg_wm_base_desc Description + * + * The xdg_wm_base interface is exposed as a global object enabling clients + * to turn their wl_surfaces into windows in a desktop environment. It + * defines the basic functionality needed for clients and the compositor to + * create windows that can be dragged, resized, maximized, etc, as well as + * creating transient windows such as popup menus. + * @section page_iface_xdg_wm_base_api API + * See @ref iface_xdg_wm_base. + */ +/** + * @defgroup iface_xdg_wm_base The xdg_wm_base interface + * + * The xdg_wm_base interface is exposed as a global object enabling clients + * to turn their wl_surfaces into windows in a desktop environment. It + * defines the basic functionality needed for clients and the compositor to + * create windows that can be dragged, resized, maximized, etc, as well as + * creating transient windows such as popup menus. + */ +extern const struct wl_interface xdg_wm_base_interface; +#endif +#ifndef XDG_POSITIONER_INTERFACE +#define XDG_POSITIONER_INTERFACE +/** + * @page page_iface_xdg_positioner xdg_positioner + * @section page_iface_xdg_positioner_desc Description + * + * The xdg_positioner provides a collection of rules for the placement of a + * child surface relative to a parent surface. Rules can be defined to ensure + * the child surface remains within the visible area's borders, and to + * specify how the child surface changes its position, such as sliding along + * an axis, or flipping around a rectangle. These positioner-created rules are + * constrained by the requirement that a child surface must intersect with or + * be at least partially adjacent to its parent surface. + * + * See the various requests for details about possible rules. + * + * At the time of the request, the compositor makes a copy of the rules + * specified by the xdg_positioner. Thus, after the request is complete the + * xdg_positioner object can be destroyed or reused; further changes to the + * object will have no effect on previous usages. + * + * For an xdg_positioner object to be considered complete, it must have a + * non-zero size set by set_size, and a non-zero anchor rectangle set by + * set_anchor_rect. Passing an incomplete xdg_positioner object when + * positioning a surface raises an invalid_positioner error. + * @section page_iface_xdg_positioner_api API + * See @ref iface_xdg_positioner. + */ +/** + * @defgroup iface_xdg_positioner The xdg_positioner interface + * + * The xdg_positioner provides a collection of rules for the placement of a + * child surface relative to a parent surface. Rules can be defined to ensure + * the child surface remains within the visible area's borders, and to + * specify how the child surface changes its position, such as sliding along + * an axis, or flipping around a rectangle. These positioner-created rules are + * constrained by the requirement that a child surface must intersect with or + * be at least partially adjacent to its parent surface. + * + * See the various requests for details about possible rules. + * + * At the time of the request, the compositor makes a copy of the rules + * specified by the xdg_positioner. Thus, after the request is complete the + * xdg_positioner object can be destroyed or reused; further changes to the + * object will have no effect on previous usages. + * + * For an xdg_positioner object to be considered complete, it must have a + * non-zero size set by set_size, and a non-zero anchor rectangle set by + * set_anchor_rect. Passing an incomplete xdg_positioner object when + * positioning a surface raises an invalid_positioner error. + */ +extern const struct wl_interface xdg_positioner_interface; +#endif +#ifndef XDG_SURFACE_INTERFACE +#define XDG_SURFACE_INTERFACE +/** + * @page page_iface_xdg_surface xdg_surface + * @section page_iface_xdg_surface_desc Description + * + * An interface that may be implemented by a wl_surface, for + * implementations that provide a desktop-style user interface. + * + * It provides a base set of functionality required to construct user + * interface elements requiring management by the compositor, such as + * toplevel windows, menus, etc. The types of functionality are split into + * xdg_surface roles. + * + * Creating an xdg_surface does not set the role for a wl_surface. In order + * to map an xdg_surface, the client must create a role-specific object + * using, e.g., get_toplevel, get_popup. The wl_surface for any given + * xdg_surface can have at most one role, and may not be assigned any role + * not based on xdg_surface. + * + * A role must be assigned before any other requests are made to the + * xdg_surface object. + * + * The client must call wl_surface.commit on the corresponding wl_surface + * for the xdg_surface state to take effect. + * + * Creating an xdg_surface from a wl_surface which has a buffer attached or + * committed is a client error, and any attempts by a client to attach or + * manipulate a buffer prior to the first xdg_surface.configure call must + * also be treated as errors. + * + * After creating a role-specific object and setting it up, the client must + * perform an initial commit without any buffer attached. The compositor + * will reply with initial wl_surface state such as + * wl_surface.preferred_buffer_scale followed by an xdg_surface.configure + * event. The client must acknowledge it and is then allowed to attach a + * buffer to map the surface. + * + * Mapping an xdg_surface-based role surface is defined as making it + * possible for the surface to be shown by the compositor. Note that + * a mapped surface is not guaranteed to be visible once it is mapped. + * + * For an xdg_surface to be mapped by the compositor, the following + * conditions must be met: + * (1) the client has assigned an xdg_surface-based role to the surface + * (2) the client has set and committed the xdg_surface state and the + * role-dependent state to the surface + * (3) the client has committed a buffer to the surface + * + * A newly-unmapped surface is considered to have met condition (1) out + * of the 3 required conditions for mapping a surface if its role surface + * has not been destroyed, i.e. the client must perform the initial commit + * again before attaching a buffer. + * @section page_iface_xdg_surface_api API + * See @ref iface_xdg_surface. + */ +/** + * @defgroup iface_xdg_surface The xdg_surface interface + * + * An interface that may be implemented by a wl_surface, for + * implementations that provide a desktop-style user interface. + * + * It provides a base set of functionality required to construct user + * interface elements requiring management by the compositor, such as + * toplevel windows, menus, etc. The types of functionality are split into + * xdg_surface roles. + * + * Creating an xdg_surface does not set the role for a wl_surface. In order + * to map an xdg_surface, the client must create a role-specific object + * using, e.g., get_toplevel, get_popup. The wl_surface for any given + * xdg_surface can have at most one role, and may not be assigned any role + * not based on xdg_surface. + * + * A role must be assigned before any other requests are made to the + * xdg_surface object. + * + * The client must call wl_surface.commit on the corresponding wl_surface + * for the xdg_surface state to take effect. + * + * Creating an xdg_surface from a wl_surface which has a buffer attached or + * committed is a client error, and any attempts by a client to attach or + * manipulate a buffer prior to the first xdg_surface.configure call must + * also be treated as errors. + * + * After creating a role-specific object and setting it up, the client must + * perform an initial commit without any buffer attached. The compositor + * will reply with initial wl_surface state such as + * wl_surface.preferred_buffer_scale followed by an xdg_surface.configure + * event. The client must acknowledge it and is then allowed to attach a + * buffer to map the surface. + * + * Mapping an xdg_surface-based role surface is defined as making it + * possible for the surface to be shown by the compositor. Note that + * a mapped surface is not guaranteed to be visible once it is mapped. + * + * For an xdg_surface to be mapped by the compositor, the following + * conditions must be met: + * (1) the client has assigned an xdg_surface-based role to the surface + * (2) the client has set and committed the xdg_surface state and the + * role-dependent state to the surface + * (3) the client has committed a buffer to the surface + * + * A newly-unmapped surface is considered to have met condition (1) out + * of the 3 required conditions for mapping a surface if its role surface + * has not been destroyed, i.e. the client must perform the initial commit + * again before attaching a buffer. + */ +extern const struct wl_interface xdg_surface_interface; +#endif +#ifndef XDG_TOPLEVEL_INTERFACE +#define XDG_TOPLEVEL_INTERFACE +/** + * @page page_iface_xdg_toplevel xdg_toplevel + * @section page_iface_xdg_toplevel_desc Description + * + * This interface defines an xdg_surface role which allows a surface to, + * among other things, set window-like properties such as maximize, + * fullscreen, and minimize, set application-specific metadata like title and + * id, and well as trigger user interactive operations such as interactive + * resize and move. + * + * Unmapping an xdg_toplevel means that the surface cannot be shown + * by the compositor until it is explicitly mapped again. + * All active operations (e.g., move, resize) are canceled and all + * attributes (e.g. title, state, stacking, ...) are discarded for + * an xdg_toplevel surface when it is unmapped. The xdg_toplevel returns to + * the state it had right after xdg_surface.get_toplevel. The client + * can re-map the toplevel by perfoming a commit without any buffer + * attached, waiting for a configure event and handling it as usual (see + * xdg_surface description). + * + * Attaching a null buffer to a toplevel unmaps the surface. + * @section page_iface_xdg_toplevel_api API + * See @ref iface_xdg_toplevel. + */ +/** + * @defgroup iface_xdg_toplevel The xdg_toplevel interface + * + * This interface defines an xdg_surface role which allows a surface to, + * among other things, set window-like properties such as maximize, + * fullscreen, and minimize, set application-specific metadata like title and + * id, and well as trigger user interactive operations such as interactive + * resize and move. + * + * Unmapping an xdg_toplevel means that the surface cannot be shown + * by the compositor until it is explicitly mapped again. + * All active operations (e.g., move, resize) are canceled and all + * attributes (e.g. title, state, stacking, ...) are discarded for + * an xdg_toplevel surface when it is unmapped. The xdg_toplevel returns to + * the state it had right after xdg_surface.get_toplevel. The client + * can re-map the toplevel by perfoming a commit without any buffer + * attached, waiting for a configure event and handling it as usual (see + * xdg_surface description). + * + * Attaching a null buffer to a toplevel unmaps the surface. + */ +extern const struct wl_interface xdg_toplevel_interface; +#endif +#ifndef XDG_POPUP_INTERFACE +#define XDG_POPUP_INTERFACE +/** + * @page page_iface_xdg_popup xdg_popup + * @section page_iface_xdg_popup_desc Description + * + * A popup surface is a short-lived, temporary surface. It can be used to + * implement for example menus, popovers, tooltips and other similar user + * interface concepts. + * + * A popup can be made to take an explicit grab. See xdg_popup.grab for + * details. + * + * When the popup is dismissed, a popup_done event will be sent out, and at + * the same time the surface will be unmapped. See the xdg_popup.popup_done + * event for details. + * + * Explicitly destroying the xdg_popup object will also dismiss the popup and + * unmap the surface. Clients that want to dismiss the popup when another + * surface of their own is clicked should dismiss the popup using the destroy + * request. + * + * A newly created xdg_popup will be stacked on top of all previously created + * xdg_popup surfaces associated with the same xdg_toplevel. + * + * The parent of an xdg_popup must be mapped (see the xdg_surface + * description) before the xdg_popup itself. + * + * The client must call wl_surface.commit on the corresponding wl_surface + * for the xdg_popup state to take effect. + * @section page_iface_xdg_popup_api API + * See @ref iface_xdg_popup. + */ +/** + * @defgroup iface_xdg_popup The xdg_popup interface + * + * A popup surface is a short-lived, temporary surface. It can be used to + * implement for example menus, popovers, tooltips and other similar user + * interface concepts. + * + * A popup can be made to take an explicit grab. See xdg_popup.grab for + * details. + * + * When the popup is dismissed, a popup_done event will be sent out, and at + * the same time the surface will be unmapped. See the xdg_popup.popup_done + * event for details. + * + * Explicitly destroying the xdg_popup object will also dismiss the popup and + * unmap the surface. Clients that want to dismiss the popup when another + * surface of their own is clicked should dismiss the popup using the destroy + * request. + * + * A newly created xdg_popup will be stacked on top of all previously created + * xdg_popup surfaces associated with the same xdg_toplevel. + * + * The parent of an xdg_popup must be mapped (see the xdg_surface + * description) before the xdg_popup itself. + * + * The client must call wl_surface.commit on the corresponding wl_surface + * for the xdg_popup state to take effect. + */ +extern const struct wl_interface xdg_popup_interface; +#endif + +#ifndef XDG_WM_BASE_ERROR_ENUM +#define XDG_WM_BASE_ERROR_ENUM +enum xdg_wm_base_error { + /** + * given wl_surface has another role + */ + XDG_WM_BASE_ERROR_ROLE = 0, + /** + * xdg_wm_base was destroyed before children + */ + XDG_WM_BASE_ERROR_DEFUNCT_SURFACES = 1, + /** + * the client tried to map or destroy a non-topmost popup + */ + XDG_WM_BASE_ERROR_NOT_THE_TOPMOST_POPUP = 2, + /** + * the client specified an invalid popup parent surface + */ + XDG_WM_BASE_ERROR_INVALID_POPUP_PARENT = 3, + /** + * the client provided an invalid surface state + */ + XDG_WM_BASE_ERROR_INVALID_SURFACE_STATE = 4, + /** + * the client provided an invalid positioner + */ + XDG_WM_BASE_ERROR_INVALID_POSITIONER = 5, + /** + * the client didn’t respond to a ping event in time + */ + XDG_WM_BASE_ERROR_UNRESPONSIVE = 6, +}; +#endif /* XDG_WM_BASE_ERROR_ENUM */ + +/** + * @ingroup iface_xdg_wm_base + * @struct xdg_wm_base_listener + */ +struct xdg_wm_base_listener { + /** + * check if the client is alive + * + * The ping event asks the client if it's still alive. Pass the + * serial specified in the event back to the compositor by sending + * a "pong" request back with the specified serial. See + * xdg_wm_base.pong. + * + * Compositors can use this to determine if the client is still + * alive. It's unspecified what will happen if the client doesn't + * respond to the ping request, or in what timeframe. Clients + * should try to respond in a reasonable amount of time. The + * “unresponsive” error is provided for compositors that wish + * to disconnect unresponsive clients. + * + * A compositor is free to ping in any way it wants, but a client + * must always respond to any xdg_wm_base object it created. + * @param serial pass this to the pong request + */ + void (*ping)(void *data, + struct xdg_wm_base *xdg_wm_base, + uint32_t serial); +}; + +/** + * @ingroup iface_xdg_wm_base + */ +static inline int +xdg_wm_base_add_listener(struct xdg_wm_base *xdg_wm_base, + const struct xdg_wm_base_listener *listener, void *data) +{ + return wl_proxy_add_listener((struct wl_proxy *) xdg_wm_base, + (void (**)(void)) listener, data); +} + +#define XDG_WM_BASE_DESTROY 0 +#define XDG_WM_BASE_CREATE_POSITIONER 1 +#define XDG_WM_BASE_GET_XDG_SURFACE 2 +#define XDG_WM_BASE_PONG 3 + +/** + * @ingroup iface_xdg_wm_base + */ +#define XDG_WM_BASE_PING_SINCE_VERSION 1 + +/** + * @ingroup iface_xdg_wm_base + */ +#define XDG_WM_BASE_DESTROY_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_wm_base + */ +#define XDG_WM_BASE_CREATE_POSITIONER_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_wm_base + */ +#define XDG_WM_BASE_GET_XDG_SURFACE_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_wm_base + */ +#define XDG_WM_BASE_PONG_SINCE_VERSION 1 + +/** @ingroup iface_xdg_wm_base */ +static inline void +xdg_wm_base_set_user_data(struct xdg_wm_base *xdg_wm_base, void *user_data) +{ + wl_proxy_set_user_data((struct wl_proxy *) xdg_wm_base, user_data); +} + +/** @ingroup iface_xdg_wm_base */ +static inline void * +xdg_wm_base_get_user_data(struct xdg_wm_base *xdg_wm_base) +{ + return wl_proxy_get_user_data((struct wl_proxy *) xdg_wm_base); +} + +static inline uint32_t +xdg_wm_base_get_version(struct xdg_wm_base *xdg_wm_base) +{ + return wl_proxy_get_version((struct wl_proxy *) xdg_wm_base); +} + +/** + * @ingroup iface_xdg_wm_base + * + * Destroy this xdg_wm_base object. + * + * Destroying a bound xdg_wm_base object while there are surfaces + * still alive created by this xdg_wm_base object instance is illegal + * and will result in a defunct_surfaces error. + */ +static inline void +xdg_wm_base_destroy(struct xdg_wm_base *xdg_wm_base) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_wm_base, + XDG_WM_BASE_DESTROY, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_wm_base), WL_MARSHAL_FLAG_DESTROY); +} + +/** + * @ingroup iface_xdg_wm_base + * + * Create a positioner object. A positioner object is used to position + * surfaces relative to some parent surface. See the interface description + * and xdg_surface.get_popup for details. + */ +static inline struct xdg_positioner * +xdg_wm_base_create_positioner(struct xdg_wm_base *xdg_wm_base) +{ + struct wl_proxy *id; + + id = wl_proxy_marshal_flags((struct wl_proxy *) xdg_wm_base, + XDG_WM_BASE_CREATE_POSITIONER, &xdg_positioner_interface, wl_proxy_get_version((struct wl_proxy *) xdg_wm_base), 0, NULL); + + return (struct xdg_positioner *) id; +} + +/** + * @ingroup iface_xdg_wm_base + * + * This creates an xdg_surface for the given surface. While xdg_surface + * itself is not a role, the corresponding surface may only be assigned + * a role extending xdg_surface, such as xdg_toplevel or xdg_popup. It is + * illegal to create an xdg_surface for a wl_surface which already has an + * assigned role and this will result in a role error. + * + * This creates an xdg_surface for the given surface. An xdg_surface is + * used as basis to define a role to a given surface, such as xdg_toplevel + * or xdg_popup. It also manages functionality shared between xdg_surface + * based surface roles. + * + * See the documentation of xdg_surface for more details about what an + * xdg_surface is and how it is used. + */ +static inline struct xdg_surface * +xdg_wm_base_get_xdg_surface(struct xdg_wm_base *xdg_wm_base, struct wl_surface *surface) +{ + struct wl_proxy *id; + + id = wl_proxy_marshal_flags((struct wl_proxy *) xdg_wm_base, + XDG_WM_BASE_GET_XDG_SURFACE, &xdg_surface_interface, wl_proxy_get_version((struct wl_proxy *) xdg_wm_base), 0, NULL, surface); + + return (struct xdg_surface *) id; +} + +/** + * @ingroup iface_xdg_wm_base + * + * A client must respond to a ping event with a pong request or + * the client may be deemed unresponsive. See xdg_wm_base.ping + * and xdg_wm_base.error.unresponsive. + */ +static inline void +xdg_wm_base_pong(struct xdg_wm_base *xdg_wm_base, uint32_t serial) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_wm_base, + XDG_WM_BASE_PONG, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_wm_base), 0, serial); +} + +#ifndef XDG_POSITIONER_ERROR_ENUM +#define XDG_POSITIONER_ERROR_ENUM +enum xdg_positioner_error { + /** + * invalid input provided + */ + XDG_POSITIONER_ERROR_INVALID_INPUT = 0, +}; +#endif /* XDG_POSITIONER_ERROR_ENUM */ + +#ifndef XDG_POSITIONER_ANCHOR_ENUM +#define XDG_POSITIONER_ANCHOR_ENUM +enum xdg_positioner_anchor { + XDG_POSITIONER_ANCHOR_NONE = 0, + XDG_POSITIONER_ANCHOR_TOP = 1, + XDG_POSITIONER_ANCHOR_BOTTOM = 2, + XDG_POSITIONER_ANCHOR_LEFT = 3, + XDG_POSITIONER_ANCHOR_RIGHT = 4, + XDG_POSITIONER_ANCHOR_TOP_LEFT = 5, + XDG_POSITIONER_ANCHOR_BOTTOM_LEFT = 6, + XDG_POSITIONER_ANCHOR_TOP_RIGHT = 7, + XDG_POSITIONER_ANCHOR_BOTTOM_RIGHT = 8, +}; +#endif /* XDG_POSITIONER_ANCHOR_ENUM */ + +#ifndef XDG_POSITIONER_GRAVITY_ENUM +#define XDG_POSITIONER_GRAVITY_ENUM +enum xdg_positioner_gravity { + XDG_POSITIONER_GRAVITY_NONE = 0, + XDG_POSITIONER_GRAVITY_TOP = 1, + XDG_POSITIONER_GRAVITY_BOTTOM = 2, + XDG_POSITIONER_GRAVITY_LEFT = 3, + XDG_POSITIONER_GRAVITY_RIGHT = 4, + XDG_POSITIONER_GRAVITY_TOP_LEFT = 5, + XDG_POSITIONER_GRAVITY_BOTTOM_LEFT = 6, + XDG_POSITIONER_GRAVITY_TOP_RIGHT = 7, + XDG_POSITIONER_GRAVITY_BOTTOM_RIGHT = 8, +}; +#endif /* XDG_POSITIONER_GRAVITY_ENUM */ + +#ifndef XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_ENUM +#define XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_ENUM +/** + * @ingroup iface_xdg_positioner + * constraint adjustments + * + * The constraint adjustment value define ways the compositor will adjust + * the position of the surface, if the unadjusted position would result + * in the surface being partly constrained. + * + * Whether a surface is considered 'constrained' is left to the compositor + * to determine. For example, the surface may be partly outside the + * compositor's defined 'work area', thus necessitating the child surface's + * position be adjusted until it is entirely inside the work area. + * + * The adjustments can be combined, according to a defined precedence: 1) + * Flip, 2) Slide, 3) Resize. + */ +enum xdg_positioner_constraint_adjustment { + /** + * don't move the child surface when constrained + * + * Don't alter the surface position even if it is constrained on + * some axis, for example partially outside the edge of an output. + */ + XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_NONE = 0, + /** + * move along the x axis until unconstrained + * + * Slide the surface along the x axis until it is no longer + * constrained. + * + * First try to slide towards the direction of the gravity on the x + * axis until either the edge in the opposite direction of the + * gravity is unconstrained or the edge in the direction of the + * gravity is constrained. + * + * Then try to slide towards the opposite direction of the gravity + * on the x axis until either the edge in the direction of the + * gravity is unconstrained or the edge in the opposite direction + * of the gravity is constrained. + */ + XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_X = 1, + /** + * move along the y axis until unconstrained + * + * Slide the surface along the y axis until it is no longer + * constrained. + * + * First try to slide towards the direction of the gravity on the y + * axis until either the edge in the opposite direction of the + * gravity is unconstrained or the edge in the direction of the + * gravity is constrained. + * + * Then try to slide towards the opposite direction of the gravity + * on the y axis until either the edge in the direction of the + * gravity is unconstrained or the edge in the opposite direction + * of the gravity is constrained. + */ + XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_Y = 2, + /** + * invert the anchor and gravity on the x axis + * + * Invert the anchor and gravity on the x axis if the surface is + * constrained on the x axis. For example, if the left edge of the + * surface is constrained, the gravity is 'left' and the anchor is + * 'left', change the gravity to 'right' and the anchor to 'right'. + * + * If the adjusted position also ends up being constrained, the + * resulting position of the flip_x adjustment will be the one + * before the adjustment. + */ + XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_FLIP_X = 4, + /** + * invert the anchor and gravity on the y axis + * + * Invert the anchor and gravity on the y axis if the surface is + * constrained on the y axis. For example, if the bottom edge of + * the surface is constrained, the gravity is 'bottom' and the + * anchor is 'bottom', change the gravity to 'top' and the anchor + * to 'top'. + * + * The adjusted position is calculated given the original anchor + * rectangle and offset, but with the new flipped anchor and + * gravity values. + * + * If the adjusted position also ends up being constrained, the + * resulting position of the flip_y adjustment will be the one + * before the adjustment. + */ + XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_FLIP_Y = 8, + /** + * horizontally resize the surface + * + * Resize the surface horizontally so that it is completely + * unconstrained. + */ + XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_RESIZE_X = 16, + /** + * vertically resize the surface + * + * Resize the surface vertically so that it is completely + * unconstrained. + */ + XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_RESIZE_Y = 32, +}; +#endif /* XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_ENUM */ + +#define XDG_POSITIONER_DESTROY 0 +#define XDG_POSITIONER_SET_SIZE 1 +#define XDG_POSITIONER_SET_ANCHOR_RECT 2 +#define XDG_POSITIONER_SET_ANCHOR 3 +#define XDG_POSITIONER_SET_GRAVITY 4 +#define XDG_POSITIONER_SET_CONSTRAINT_ADJUSTMENT 5 +#define XDG_POSITIONER_SET_OFFSET 6 +#define XDG_POSITIONER_SET_REACTIVE 7 +#define XDG_POSITIONER_SET_PARENT_SIZE 8 +#define XDG_POSITIONER_SET_PARENT_CONFIGURE 9 + + +/** + * @ingroup iface_xdg_positioner + */ +#define XDG_POSITIONER_DESTROY_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_positioner + */ +#define XDG_POSITIONER_SET_SIZE_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_positioner + */ +#define XDG_POSITIONER_SET_ANCHOR_RECT_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_positioner + */ +#define XDG_POSITIONER_SET_ANCHOR_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_positioner + */ +#define XDG_POSITIONER_SET_GRAVITY_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_positioner + */ +#define XDG_POSITIONER_SET_CONSTRAINT_ADJUSTMENT_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_positioner + */ +#define XDG_POSITIONER_SET_OFFSET_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_positioner + */ +#define XDG_POSITIONER_SET_REACTIVE_SINCE_VERSION 3 +/** + * @ingroup iface_xdg_positioner + */ +#define XDG_POSITIONER_SET_PARENT_SIZE_SINCE_VERSION 3 +/** + * @ingroup iface_xdg_positioner + */ +#define XDG_POSITIONER_SET_PARENT_CONFIGURE_SINCE_VERSION 3 + +/** @ingroup iface_xdg_positioner */ +static inline void +xdg_positioner_set_user_data(struct xdg_positioner *xdg_positioner, void *user_data) +{ + wl_proxy_set_user_data((struct wl_proxy *) xdg_positioner, user_data); +} + +/** @ingroup iface_xdg_positioner */ +static inline void * +xdg_positioner_get_user_data(struct xdg_positioner *xdg_positioner) +{ + return wl_proxy_get_user_data((struct wl_proxy *) xdg_positioner); +} + +static inline uint32_t +xdg_positioner_get_version(struct xdg_positioner *xdg_positioner) +{ + return wl_proxy_get_version((struct wl_proxy *) xdg_positioner); +} + +/** + * @ingroup iface_xdg_positioner + * + * Notify the compositor that the xdg_positioner will no longer be used. + */ +static inline void +xdg_positioner_destroy(struct xdg_positioner *xdg_positioner) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_positioner, + XDG_POSITIONER_DESTROY, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_positioner), WL_MARSHAL_FLAG_DESTROY); +} + +/** + * @ingroup iface_xdg_positioner + * + * Set the size of the surface that is to be positioned with the positioner + * object. The size is in surface-local coordinates and corresponds to the + * window geometry. See xdg_surface.set_window_geometry. + * + * If a zero or negative size is set the invalid_input error is raised. + */ +static inline void +xdg_positioner_set_size(struct xdg_positioner *xdg_positioner, int32_t width, int32_t height) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_positioner, + XDG_POSITIONER_SET_SIZE, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_positioner), 0, width, height); +} + +/** + * @ingroup iface_xdg_positioner + * + * Specify the anchor rectangle within the parent surface that the child + * surface will be placed relative to. The rectangle is relative to the + * window geometry as defined by xdg_surface.set_window_geometry of the + * parent surface. + * + * When the xdg_positioner object is used to position a child surface, the + * anchor rectangle may not extend outside the window geometry of the + * positioned child's parent surface. + * + * If a negative size is set the invalid_input error is raised. + */ +static inline void +xdg_positioner_set_anchor_rect(struct xdg_positioner *xdg_positioner, int32_t x, int32_t y, int32_t width, int32_t height) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_positioner, + XDG_POSITIONER_SET_ANCHOR_RECT, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_positioner), 0, x, y, width, height); +} + +/** + * @ingroup iface_xdg_positioner + * + * Defines the anchor point for the anchor rectangle. The specified anchor + * is used derive an anchor point that the child surface will be + * positioned relative to. If a corner anchor is set (e.g. 'top_left' or + * 'bottom_right'), the anchor point will be at the specified corner; + * otherwise, the derived anchor point will be centered on the specified + * edge, or in the center of the anchor rectangle if no edge is specified. + */ +static inline void +xdg_positioner_set_anchor(struct xdg_positioner *xdg_positioner, uint32_t anchor) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_positioner, + XDG_POSITIONER_SET_ANCHOR, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_positioner), 0, anchor); +} + +/** + * @ingroup iface_xdg_positioner + * + * Defines in what direction a surface should be positioned, relative to + * the anchor point of the parent surface. If a corner gravity is + * specified (e.g. 'bottom_right' or 'top_left'), then the child surface + * will be placed towards the specified gravity; otherwise, the child + * surface will be centered over the anchor point on any axis that had no + * gravity specified. If the gravity is not in the ‘gravity’ enum, an + * invalid_input error is raised. + */ +static inline void +xdg_positioner_set_gravity(struct xdg_positioner *xdg_positioner, uint32_t gravity) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_positioner, + XDG_POSITIONER_SET_GRAVITY, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_positioner), 0, gravity); +} + +/** + * @ingroup iface_xdg_positioner + * + * Specify how the window should be positioned if the originally intended + * position caused the surface to be constrained, meaning at least + * partially outside positioning boundaries set by the compositor. The + * adjustment is set by constructing a bitmask describing the adjustment to + * be made when the surface is constrained on that axis. + * + * If no bit for one axis is set, the compositor will assume that the child + * surface should not change its position on that axis when constrained. + * + * If more than one bit for one axis is set, the order of how adjustments + * are applied is specified in the corresponding adjustment descriptions. + * + * The default adjustment is none. + */ +static inline void +xdg_positioner_set_constraint_adjustment(struct xdg_positioner *xdg_positioner, uint32_t constraint_adjustment) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_positioner, + XDG_POSITIONER_SET_CONSTRAINT_ADJUSTMENT, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_positioner), 0, constraint_adjustment); +} + +/** + * @ingroup iface_xdg_positioner + * + * Specify the surface position offset relative to the position of the + * anchor on the anchor rectangle and the anchor on the surface. For + * example if the anchor of the anchor rectangle is at (x, y), the surface + * has the gravity bottom|right, and the offset is (ox, oy), the calculated + * surface position will be (x + ox, y + oy). The offset position of the + * surface is the one used for constraint testing. See + * set_constraint_adjustment. + * + * An example use case is placing a popup menu on top of a user interface + * element, while aligning the user interface element of the parent surface + * with some user interface element placed somewhere in the popup surface. + */ +static inline void +xdg_positioner_set_offset(struct xdg_positioner *xdg_positioner, int32_t x, int32_t y) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_positioner, + XDG_POSITIONER_SET_OFFSET, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_positioner), 0, x, y); +} + +/** + * @ingroup iface_xdg_positioner + * + * When set reactive, the surface is reconstrained if the conditions used + * for constraining changed, e.g. the parent window moved. + * + * If the conditions changed and the popup was reconstrained, an + * xdg_popup.configure event is sent with updated geometry, followed by an + * xdg_surface.configure event. + */ +static inline void +xdg_positioner_set_reactive(struct xdg_positioner *xdg_positioner) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_positioner, + XDG_POSITIONER_SET_REACTIVE, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_positioner), 0); +} + +/** + * @ingroup iface_xdg_positioner + * + * Set the parent window geometry the compositor should use when + * positioning the popup. The compositor may use this information to + * determine the future state the popup should be constrained using. If + * this doesn't match the dimension of the parent the popup is eventually + * positioned against, the behavior is undefined. + * + * The arguments are given in the surface-local coordinate space. + */ +static inline void +xdg_positioner_set_parent_size(struct xdg_positioner *xdg_positioner, int32_t parent_width, int32_t parent_height) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_positioner, + XDG_POSITIONER_SET_PARENT_SIZE, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_positioner), 0, parent_width, parent_height); +} + +/** + * @ingroup iface_xdg_positioner + * + * Set the serial of an xdg_surface.configure event this positioner will be + * used in response to. The compositor may use this information together + * with set_parent_size to determine what future state the popup should be + * constrained using. + */ +static inline void +xdg_positioner_set_parent_configure(struct xdg_positioner *xdg_positioner, uint32_t serial) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_positioner, + XDG_POSITIONER_SET_PARENT_CONFIGURE, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_positioner), 0, serial); +} + +#ifndef XDG_SURFACE_ERROR_ENUM +#define XDG_SURFACE_ERROR_ENUM +enum xdg_surface_error { + /** + * Surface was not fully constructed + */ + XDG_SURFACE_ERROR_NOT_CONSTRUCTED = 1, + /** + * Surface was already constructed + */ + XDG_SURFACE_ERROR_ALREADY_CONSTRUCTED = 2, + /** + * Attaching a buffer to an unconfigured surface + */ + XDG_SURFACE_ERROR_UNCONFIGURED_BUFFER = 3, + /** + * Invalid serial number when acking a configure event + */ + XDG_SURFACE_ERROR_INVALID_SERIAL = 4, + /** + * Width or height was zero or negative + */ + XDG_SURFACE_ERROR_INVALID_SIZE = 5, + /** + * Surface was destroyed before its role object + */ + XDG_SURFACE_ERROR_DEFUNCT_ROLE_OBJECT = 6, +}; +#endif /* XDG_SURFACE_ERROR_ENUM */ + +/** + * @ingroup iface_xdg_surface + * @struct xdg_surface_listener + */ +struct xdg_surface_listener { + /** + * suggest a surface change + * + * The configure event marks the end of a configure sequence. A + * configure sequence is a set of one or more events configuring + * the state of the xdg_surface, including the final + * xdg_surface.configure event. + * + * Where applicable, xdg_surface surface roles will during a + * configure sequence extend this event as a latched state sent as + * events before the xdg_surface.configure event. Such events + * should be considered to make up a set of atomically applied + * configuration states, where the xdg_surface.configure commits + * the accumulated state. + * + * Clients should arrange their surface for the new states, and + * then send an ack_configure request with the serial sent in this + * configure event at some point before committing the new surface. + * + * If the client receives multiple configure events before it can + * respond to one, it is free to discard all but the last event it + * received. + * @param serial serial of the configure event + */ + void (*configure)(void *data, + struct xdg_surface *xdg_surface, + uint32_t serial); +}; + +/** + * @ingroup iface_xdg_surface + */ +static inline int +xdg_surface_add_listener(struct xdg_surface *xdg_surface, + const struct xdg_surface_listener *listener, void *data) +{ + return wl_proxy_add_listener((struct wl_proxy *) xdg_surface, + (void (**)(void)) listener, data); +} + +#define XDG_SURFACE_DESTROY 0 +#define XDG_SURFACE_GET_TOPLEVEL 1 +#define XDG_SURFACE_GET_POPUP 2 +#define XDG_SURFACE_SET_WINDOW_GEOMETRY 3 +#define XDG_SURFACE_ACK_CONFIGURE 4 + +/** + * @ingroup iface_xdg_surface + */ +#define XDG_SURFACE_CONFIGURE_SINCE_VERSION 1 + +/** + * @ingroup iface_xdg_surface + */ +#define XDG_SURFACE_DESTROY_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_surface + */ +#define XDG_SURFACE_GET_TOPLEVEL_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_surface + */ +#define XDG_SURFACE_GET_POPUP_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_surface + */ +#define XDG_SURFACE_SET_WINDOW_GEOMETRY_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_surface + */ +#define XDG_SURFACE_ACK_CONFIGURE_SINCE_VERSION 1 + +/** @ingroup iface_xdg_surface */ +static inline void +xdg_surface_set_user_data(struct xdg_surface *xdg_surface, void *user_data) +{ + wl_proxy_set_user_data((struct wl_proxy *) xdg_surface, user_data); +} + +/** @ingroup iface_xdg_surface */ +static inline void * +xdg_surface_get_user_data(struct xdg_surface *xdg_surface) +{ + return wl_proxy_get_user_data((struct wl_proxy *) xdg_surface); +} + +static inline uint32_t +xdg_surface_get_version(struct xdg_surface *xdg_surface) +{ + return wl_proxy_get_version((struct wl_proxy *) xdg_surface); +} + +/** + * @ingroup iface_xdg_surface + * + * Destroy the xdg_surface object. An xdg_surface must only be destroyed + * after its role object has been destroyed, otherwise + * a defunct_role_object error is raised. + */ +static inline void +xdg_surface_destroy(struct xdg_surface *xdg_surface) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_surface, + XDG_SURFACE_DESTROY, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_surface), WL_MARSHAL_FLAG_DESTROY); +} + +/** + * @ingroup iface_xdg_surface + * + * This creates an xdg_toplevel object for the given xdg_surface and gives + * the associated wl_surface the xdg_toplevel role. + * + * See the documentation of xdg_toplevel for more details about what an + * xdg_toplevel is and how it is used. + */ +static inline struct xdg_toplevel * +xdg_surface_get_toplevel(struct xdg_surface *xdg_surface) +{ + struct wl_proxy *id; + + id = wl_proxy_marshal_flags((struct wl_proxy *) xdg_surface, + XDG_SURFACE_GET_TOPLEVEL, &xdg_toplevel_interface, wl_proxy_get_version((struct wl_proxy *) xdg_surface), 0, NULL); + + return (struct xdg_toplevel *) id; +} + +/** + * @ingroup iface_xdg_surface + * + * This creates an xdg_popup object for the given xdg_surface and gives + * the associated wl_surface the xdg_popup role. + * + * If null is passed as a parent, a parent surface must be specified using + * some other protocol, before committing the initial state. + * + * See the documentation of xdg_popup for more details about what an + * xdg_popup is and how it is used. + */ +static inline struct xdg_popup * +xdg_surface_get_popup(struct xdg_surface *xdg_surface, struct xdg_surface *parent, struct xdg_positioner *positioner) +{ + struct wl_proxy *id; + + id = wl_proxy_marshal_flags((struct wl_proxy *) xdg_surface, + XDG_SURFACE_GET_POPUP, &xdg_popup_interface, wl_proxy_get_version((struct wl_proxy *) xdg_surface), 0, NULL, parent, positioner); + + return (struct xdg_popup *) id; +} + +/** + * @ingroup iface_xdg_surface + * + * The window geometry of a surface is its "visible bounds" from the + * user's perspective. Client-side decorations often have invisible + * portions like drop-shadows which should be ignored for the + * purposes of aligning, placing and constraining windows. + * + * The window geometry is double buffered, and will be applied at the + * time wl_surface.commit of the corresponding wl_surface is called. + * + * When maintaining a position, the compositor should treat the (x, y) + * coordinate of the window geometry as the top left corner of the window. + * A client changing the (x, y) window geometry coordinate should in + * general not alter the position of the window. + * + * Once the window geometry of the surface is set, it is not possible to + * unset it, and it will remain the same until set_window_geometry is + * called again, even if a new subsurface or buffer is attached. + * + * If never set, the value is the full bounds of the surface, + * including any subsurfaces. This updates dynamically on every + * commit. This unset is meant for extremely simple clients. + * + * The arguments are given in the surface-local coordinate space of + * the wl_surface associated with this xdg_surface, and may extend outside + * of the wl_surface itself to mark parts of the subsurface tree as part of + * the window geometry. + * + * When applied, the effective window geometry will be the set window + * geometry clamped to the bounding rectangle of the combined + * geometry of the surface of the xdg_surface and the associated + * subsurfaces. + * + * The effective geometry will not be recalculated unless a new call to + * set_window_geometry is done and the new pending surface state is + * subsequently applied. + * + * The width and height of the effective window geometry must be + * greater than zero. Setting an invalid size will raise an + * invalid_size error. + */ +static inline void +xdg_surface_set_window_geometry(struct xdg_surface *xdg_surface, int32_t x, int32_t y, int32_t width, int32_t height) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_surface, + XDG_SURFACE_SET_WINDOW_GEOMETRY, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_surface), 0, x, y, width, height); +} + +/** + * @ingroup iface_xdg_surface + * + * When a configure event is received, if a client commits the + * surface in response to the configure event, then the client + * must make an ack_configure request sometime before the commit + * request, passing along the serial of the configure event. + * + * For instance, for toplevel surfaces the compositor might use this + * information to move a surface to the top left only when the client has + * drawn itself for the maximized or fullscreen state. + * + * If the client receives multiple configure events before it + * can respond to one, it only has to ack the last configure event. + * Acking a configure event that was never sent raises an invalid_serial + * error. + * + * A client is not required to commit immediately after sending + * an ack_configure request - it may even ack_configure several times + * before its next surface commit. + * + * A client may send multiple ack_configure requests before committing, but + * only the last request sent before a commit indicates which configure + * event the client really is responding to. + * + * Sending an ack_configure request consumes the serial number sent with + * the request, as well as serial numbers sent by all configure events + * sent on this xdg_surface prior to the configure event referenced by + * the committed serial. + * + * It is an error to issue multiple ack_configure requests referencing a + * serial from the same configure event, or to issue an ack_configure + * request referencing a serial from a configure event issued before the + * event identified by the last ack_configure request for the same + * xdg_surface. Doing so will raise an invalid_serial error. + */ +static inline void +xdg_surface_ack_configure(struct xdg_surface *xdg_surface, uint32_t serial) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_surface, + XDG_SURFACE_ACK_CONFIGURE, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_surface), 0, serial); +} + +#ifndef XDG_TOPLEVEL_ERROR_ENUM +#define XDG_TOPLEVEL_ERROR_ENUM +enum xdg_toplevel_error { + /** + * provided value is not a valid variant of the resize_edge enum + */ + XDG_TOPLEVEL_ERROR_INVALID_RESIZE_EDGE = 0, + /** + * invalid parent toplevel + */ + XDG_TOPLEVEL_ERROR_INVALID_PARENT = 1, + /** + * client provided an invalid min or max size + */ + XDG_TOPLEVEL_ERROR_INVALID_SIZE = 2, +}; +#endif /* XDG_TOPLEVEL_ERROR_ENUM */ + +#ifndef XDG_TOPLEVEL_RESIZE_EDGE_ENUM +#define XDG_TOPLEVEL_RESIZE_EDGE_ENUM +/** + * @ingroup iface_xdg_toplevel + * edge values for resizing + * + * These values are used to indicate which edge of a surface + * is being dragged in a resize operation. + */ +enum xdg_toplevel_resize_edge { + XDG_TOPLEVEL_RESIZE_EDGE_NONE = 0, + XDG_TOPLEVEL_RESIZE_EDGE_TOP = 1, + XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM = 2, + XDG_TOPLEVEL_RESIZE_EDGE_LEFT = 4, + XDG_TOPLEVEL_RESIZE_EDGE_TOP_LEFT = 5, + XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_LEFT = 6, + XDG_TOPLEVEL_RESIZE_EDGE_RIGHT = 8, + XDG_TOPLEVEL_RESIZE_EDGE_TOP_RIGHT = 9, + XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_RIGHT = 10, +}; +#endif /* XDG_TOPLEVEL_RESIZE_EDGE_ENUM */ + +#ifndef XDG_TOPLEVEL_STATE_ENUM +#define XDG_TOPLEVEL_STATE_ENUM +/** + * @ingroup iface_xdg_toplevel + * types of state on the surface + * + * The different state values used on the surface. This is designed for + * state values like maximized, fullscreen. It is paired with the + * configure event to ensure that both the client and the compositor + * setting the state can be synchronized. + * + * States set in this way are double-buffered. They will get applied on + * the next commit. + */ +enum xdg_toplevel_state { + /** + * the surface is maximized + * the surface is maximized + * + * The surface is maximized. The window geometry specified in the + * configure event must be obeyed by the client, or the + * xdg_wm_base.invalid_surface_state error is raised. + * + * The client should draw without shadow or other decoration + * outside of the window geometry. + */ + XDG_TOPLEVEL_STATE_MAXIMIZED = 1, + /** + * the surface is fullscreen + * the surface is fullscreen + * + * The surface is fullscreen. The window geometry specified in + * the configure event is a maximum; the client cannot resize + * beyond it. For a surface to cover the whole fullscreened area, + * the geometry dimensions must be obeyed by the client. For more + * details, see xdg_toplevel.set_fullscreen. + */ + XDG_TOPLEVEL_STATE_FULLSCREEN = 2, + /** + * the surface is being resized + * the surface is being resized + * + * The surface is being resized. The window geometry specified in + * the configure event is a maximum; the client cannot resize + * beyond it. Clients that have aspect ratio or cell sizing + * configuration can use a smaller size, however. + */ + XDG_TOPLEVEL_STATE_RESIZING = 3, + /** + * the surface is now activated + * the surface is now activated + * + * Client window decorations should be painted as if the window + * is active. Do not assume this means that the window actually has + * keyboard or pointer focus. + */ + XDG_TOPLEVEL_STATE_ACTIVATED = 4, + /** + * the surface’s left edge is tiled + * + * The window is currently in a tiled layout and the left edge is + * considered to be adjacent to another part of the tiling grid. + * @since 2 + */ + XDG_TOPLEVEL_STATE_TILED_LEFT = 5, + /** + * the surface’s right edge is tiled + * + * The window is currently in a tiled layout and the right edge + * is considered to be adjacent to another part of the tiling grid. + * @since 2 + */ + XDG_TOPLEVEL_STATE_TILED_RIGHT = 6, + /** + * the surface’s top edge is tiled + * + * The window is currently in a tiled layout and the top edge is + * considered to be adjacent to another part of the tiling grid. + * @since 2 + */ + XDG_TOPLEVEL_STATE_TILED_TOP = 7, + /** + * the surface’s bottom edge is tiled + * + * The window is currently in a tiled layout and the bottom edge + * is considered to be adjacent to another part of the tiling grid. + * @since 2 + */ + XDG_TOPLEVEL_STATE_TILED_BOTTOM = 8, + /** + * surface repaint is suspended + * + * The surface is currently not ordinarily being repainted; for + * example because its content is occluded by another window, or + * its outputs are switched off due to screen locking. + * @since 6 + */ + XDG_TOPLEVEL_STATE_SUSPENDED = 9, +}; +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_STATE_TILED_LEFT_SINCE_VERSION 2 +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_STATE_TILED_RIGHT_SINCE_VERSION 2 +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_STATE_TILED_TOP_SINCE_VERSION 2 +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_STATE_TILED_BOTTOM_SINCE_VERSION 2 +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_STATE_SUSPENDED_SINCE_VERSION 6 +#endif /* XDG_TOPLEVEL_STATE_ENUM */ + +#ifndef XDG_TOPLEVEL_WM_CAPABILITIES_ENUM +#define XDG_TOPLEVEL_WM_CAPABILITIES_ENUM +enum xdg_toplevel_wm_capabilities { + /** + * show_window_menu is available + */ + XDG_TOPLEVEL_WM_CAPABILITIES_WINDOW_MENU = 1, + /** + * set_maximized and unset_maximized are available + */ + XDG_TOPLEVEL_WM_CAPABILITIES_MAXIMIZE = 2, + /** + * set_fullscreen and unset_fullscreen are available + */ + XDG_TOPLEVEL_WM_CAPABILITIES_FULLSCREEN = 3, + /** + * set_minimized is available + */ + XDG_TOPLEVEL_WM_CAPABILITIES_MINIMIZE = 4, +}; +#endif /* XDG_TOPLEVEL_WM_CAPABILITIES_ENUM */ + +/** + * @ingroup iface_xdg_toplevel + * @struct xdg_toplevel_listener + */ +struct xdg_toplevel_listener { + /** + * suggest a surface change + * + * This configure event asks the client to resize its toplevel + * surface or to change its state. The configured state should not + * be applied immediately. See xdg_surface.configure for details. + * + * The width and height arguments specify a hint to the window + * about how its surface should be resized in window geometry + * coordinates. See set_window_geometry. + * + * If the width or height arguments are zero, it means the client + * should decide its own window dimension. This may happen when the + * compositor needs to configure the state of the surface but + * doesn't have any information about any previous or expected + * dimension. + * + * The states listed in the event specify how the width/height + * arguments should be interpreted, and possibly how it should be + * drawn. + * + * Clients must send an ack_configure in response to this event. + * See xdg_surface.configure and xdg_surface.ack_configure for + * details. + */ + void (*configure)(void *data, + struct xdg_toplevel *xdg_toplevel, + int32_t width, + int32_t height, + struct wl_array *states); + /** + * surface wants to be closed + * + * The close event is sent by the compositor when the user wants + * the surface to be closed. This should be equivalent to the user + * clicking the close button in client-side decorations, if your + * application has any. + * + * This is only a request that the user intends to close the + * window. The client may choose to ignore this request, or show a + * dialog to ask the user to save their data, etc. + */ + void (*close)(void *data, + struct xdg_toplevel *xdg_toplevel); + /** + * recommended window geometry bounds + * + * The configure_bounds event may be sent prior to a + * xdg_toplevel.configure event to communicate the bounds a window + * geometry size is recommended to constrain to. + * + * The passed width and height are in surface coordinate space. If + * width and height are 0, it means bounds is unknown and + * equivalent to as if no configure_bounds event was ever sent for + * this surface. + * + * The bounds can for example correspond to the size of a monitor + * excluding any panels or other shell components, so that a + * surface isn't created in a way that it cannot fit. + * + * The bounds may change at any point, and in such a case, a new + * xdg_toplevel.configure_bounds will be sent, followed by + * xdg_toplevel.configure and xdg_surface.configure. + * @since 4 + */ + void (*configure_bounds)(void *data, + struct xdg_toplevel *xdg_toplevel, + int32_t width, + int32_t height); + /** + * compositor capabilities + * + * This event advertises the capabilities supported by the + * compositor. If a capability isn't supported, clients should hide + * or disable the UI elements that expose this functionality. For + * instance, if the compositor doesn't advertise support for + * minimized toplevels, a button triggering the set_minimized + * request should not be displayed. + * + * The compositor will ignore requests it doesn't support. For + * instance, a compositor which doesn't advertise support for + * minimized will ignore set_minimized requests. + * + * Compositors must send this event once before the first + * xdg_surface.configure event. When the capabilities change, + * compositors must send this event again and then send an + * xdg_surface.configure event. + * + * The configured state should not be applied immediately. See + * xdg_surface.configure for details. + * + * The capabilities are sent as an array of 32-bit unsigned + * integers in native endianness. + * @param capabilities array of 32-bit capabilities + * @since 5 + */ + void (*wm_capabilities)(void *data, + struct xdg_toplevel *xdg_toplevel, + struct wl_array *capabilities); +}; + +/** + * @ingroup iface_xdg_toplevel + */ +static inline int +xdg_toplevel_add_listener(struct xdg_toplevel *xdg_toplevel, + const struct xdg_toplevel_listener *listener, void *data) +{ + return wl_proxy_add_listener((struct wl_proxy *) xdg_toplevel, + (void (**)(void)) listener, data); +} + +#define XDG_TOPLEVEL_DESTROY 0 +#define XDG_TOPLEVEL_SET_PARENT 1 +#define XDG_TOPLEVEL_SET_TITLE 2 +#define XDG_TOPLEVEL_SET_APP_ID 3 +#define XDG_TOPLEVEL_SHOW_WINDOW_MENU 4 +#define XDG_TOPLEVEL_MOVE 5 +#define XDG_TOPLEVEL_RESIZE 6 +#define XDG_TOPLEVEL_SET_MAX_SIZE 7 +#define XDG_TOPLEVEL_SET_MIN_SIZE 8 +#define XDG_TOPLEVEL_SET_MAXIMIZED 9 +#define XDG_TOPLEVEL_UNSET_MAXIMIZED 10 +#define XDG_TOPLEVEL_SET_FULLSCREEN 11 +#define XDG_TOPLEVEL_UNSET_FULLSCREEN 12 +#define XDG_TOPLEVEL_SET_MINIMIZED 13 + +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_CONFIGURE_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_CLOSE_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_CONFIGURE_BOUNDS_SINCE_VERSION 4 +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_WM_CAPABILITIES_SINCE_VERSION 5 + +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_DESTROY_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_SET_PARENT_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_SET_TITLE_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_SET_APP_ID_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_SHOW_WINDOW_MENU_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_MOVE_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_RESIZE_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_SET_MAX_SIZE_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_SET_MIN_SIZE_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_SET_MAXIMIZED_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_UNSET_MAXIMIZED_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_SET_FULLSCREEN_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_UNSET_FULLSCREEN_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_SET_MINIMIZED_SINCE_VERSION 1 + +/** @ingroup iface_xdg_toplevel */ +static inline void +xdg_toplevel_set_user_data(struct xdg_toplevel *xdg_toplevel, void *user_data) +{ + wl_proxy_set_user_data((struct wl_proxy *) xdg_toplevel, user_data); +} + +/** @ingroup iface_xdg_toplevel */ +static inline void * +xdg_toplevel_get_user_data(struct xdg_toplevel *xdg_toplevel) +{ + return wl_proxy_get_user_data((struct wl_proxy *) xdg_toplevel); +} + +static inline uint32_t +xdg_toplevel_get_version(struct xdg_toplevel *xdg_toplevel) +{ + return wl_proxy_get_version((struct wl_proxy *) xdg_toplevel); +} + +/** + * @ingroup iface_xdg_toplevel + * + * This request destroys the role surface and unmaps the surface; + * see "Unmapping" behavior in interface section for details. + */ +static inline void +xdg_toplevel_destroy(struct xdg_toplevel *xdg_toplevel) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_toplevel, + XDG_TOPLEVEL_DESTROY, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_toplevel), WL_MARSHAL_FLAG_DESTROY); +} + +/** + * @ingroup iface_xdg_toplevel + * + * Set the "parent" of this surface. This surface should be stacked + * above the parent surface and all other ancestor surfaces. + * + * Parent surfaces should be set on dialogs, toolboxes, or other + * "auxiliary" surfaces, so that the parent is raised when the dialog + * is raised. + * + * Setting a null parent for a child surface unsets its parent. Setting + * a null parent for a surface which currently has no parent is a no-op. + * + * Only mapped surfaces can have child surfaces. Setting a parent which + * is not mapped is equivalent to setting a null parent. If a surface + * becomes unmapped, its children's parent is set to the parent of + * the now-unmapped surface. If the now-unmapped surface has no parent, + * its children's parent is unset. If the now-unmapped surface becomes + * mapped again, its parent-child relationship is not restored. + * + * The parent toplevel must not be one of the child toplevel's + * descendants, and the parent must be different from the child toplevel, + * otherwise the invalid_parent protocol error is raised. + */ +static inline void +xdg_toplevel_set_parent(struct xdg_toplevel *xdg_toplevel, struct xdg_toplevel *parent) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_toplevel, + XDG_TOPLEVEL_SET_PARENT, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_toplevel), 0, parent); +} + +/** + * @ingroup iface_xdg_toplevel + * + * Set a short title for the surface. + * + * This string may be used to identify the surface in a task bar, + * window list, or other user interface elements provided by the + * compositor. + * + * The string must be encoded in UTF-8. + */ +static inline void +xdg_toplevel_set_title(struct xdg_toplevel *xdg_toplevel, const char *title) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_toplevel, + XDG_TOPLEVEL_SET_TITLE, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_toplevel), 0, title); +} + +/** + * @ingroup iface_xdg_toplevel + * + * Set an application identifier for the surface. + * + * The app ID identifies the general class of applications to which + * the surface belongs. The compositor can use this to group multiple + * surfaces together, or to determine how to launch a new application. + * + * For D-Bus activatable applications, the app ID is used as the D-Bus + * service name. + * + * The compositor shell will try to group application surfaces together + * by their app ID. As a best practice, it is suggested to select app + * ID's that match the basename of the application's .desktop file. + * For example, "org.freedesktop.FooViewer" where the .desktop file is + * "org.freedesktop.FooViewer.desktop". + * + * Like other properties, a set_app_id request can be sent after the + * xdg_toplevel has been mapped to update the property. + * + * See the desktop-entry specification [0] for more details on + * application identifiers and how they relate to well-known D-Bus + * names and .desktop files. + * + * [0] https://standards.freedesktop.org/desktop-entry-spec/ + */ +static inline void +xdg_toplevel_set_app_id(struct xdg_toplevel *xdg_toplevel, const char *app_id) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_toplevel, + XDG_TOPLEVEL_SET_APP_ID, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_toplevel), 0, app_id); +} + +/** + * @ingroup iface_xdg_toplevel + * + * Clients implementing client-side decorations might want to show + * a context menu when right-clicking on the decorations, giving the + * user a menu that they can use to maximize or minimize the window. + * + * This request asks the compositor to pop up such a window menu at + * the given position, relative to the local surface coordinates of + * the parent surface. There are no guarantees as to what menu items + * the window menu contains, or even if a window menu will be drawn + * at all. + * + * This request must be used in response to some sort of user action + * like a button press, key press, or touch down event. + */ +static inline void +xdg_toplevel_show_window_menu(struct xdg_toplevel *xdg_toplevel, struct wl_seat *seat, uint32_t serial, int32_t x, int32_t y) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_toplevel, + XDG_TOPLEVEL_SHOW_WINDOW_MENU, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_toplevel), 0, seat, serial, x, y); +} + +/** + * @ingroup iface_xdg_toplevel + * + * Start an interactive, user-driven move of the surface. + * + * This request must be used in response to some sort of user action + * like a button press, key press, or touch down event. The passed + * serial is used to determine the type of interactive move (touch, + * pointer, etc). + * + * The server may ignore move requests depending on the state of + * the surface (e.g. fullscreen or maximized), or if the passed serial + * is no longer valid. + * + * If triggered, the surface will lose the focus of the device + * (wl_pointer, wl_touch, etc) used for the move. It is up to the + * compositor to visually indicate that the move is taking place, such as + * updating a pointer cursor, during the move. There is no guarantee + * that the device focus will return when the move is completed. + */ +static inline void +xdg_toplevel_move(struct xdg_toplevel *xdg_toplevel, struct wl_seat *seat, uint32_t serial) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_toplevel, + XDG_TOPLEVEL_MOVE, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_toplevel), 0, seat, serial); +} + +/** + * @ingroup iface_xdg_toplevel + * + * Start a user-driven, interactive resize of the surface. + * + * This request must be used in response to some sort of user action + * like a button press, key press, or touch down event. The passed + * serial is used to determine the type of interactive resize (touch, + * pointer, etc). + * + * The server may ignore resize requests depending on the state of + * the surface (e.g. fullscreen or maximized). + * + * If triggered, the client will receive configure events with the + * "resize" state enum value and the expected sizes. See the "resize" + * enum value for more details about what is required. The client + * must also acknowledge configure events using "ack_configure". After + * the resize is completed, the client will receive another "configure" + * event without the resize state. + * + * If triggered, the surface also will lose the focus of the device + * (wl_pointer, wl_touch, etc) used for the resize. It is up to the + * compositor to visually indicate that the resize is taking place, + * such as updating a pointer cursor, during the resize. There is no + * guarantee that the device focus will return when the resize is + * completed. + * + * The edges parameter specifies how the surface should be resized, and + * is one of the values of the resize_edge enum. Values not matching + * a variant of the enum will cause the invalid_resize_edge protocol error. + * The compositor may use this information to update the surface position + * for example when dragging the top left corner. The compositor may also + * use this information to adapt its behavior, e.g. choose an appropriate + * cursor image. + */ +static inline void +xdg_toplevel_resize(struct xdg_toplevel *xdg_toplevel, struct wl_seat *seat, uint32_t serial, uint32_t edges) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_toplevel, + XDG_TOPLEVEL_RESIZE, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_toplevel), 0, seat, serial, edges); +} + +/** + * @ingroup iface_xdg_toplevel + * + * Set a maximum size for the window. + * + * The client can specify a maximum size so that the compositor does + * not try to configure the window beyond this size. + * + * The width and height arguments are in window geometry coordinates. + * See xdg_surface.set_window_geometry. + * + * Values set in this way are double-buffered. They will get applied + * on the next commit. + * + * The compositor can use this information to allow or disallow + * different states like maximize or fullscreen and draw accurate + * animations. + * + * Similarly, a tiling window manager may use this information to + * place and resize client windows in a more effective way. + * + * The client should not rely on the compositor to obey the maximum + * size. The compositor may decide to ignore the values set by the + * client and request a larger size. + * + * If never set, or a value of zero in the request, means that the + * client has no expected maximum size in the given dimension. + * As a result, a client wishing to reset the maximum size + * to an unspecified state can use zero for width and height in the + * request. + * + * Requesting a maximum size to be smaller than the minimum size of + * a surface is illegal and will result in an invalid_size error. + * + * The width and height must be greater than or equal to zero. Using + * strictly negative values for width or height will result in a + * invalid_size error. + */ +static inline void +xdg_toplevel_set_max_size(struct xdg_toplevel *xdg_toplevel, int32_t width, int32_t height) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_toplevel, + XDG_TOPLEVEL_SET_MAX_SIZE, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_toplevel), 0, width, height); +} + +/** + * @ingroup iface_xdg_toplevel + * + * Set a minimum size for the window. + * + * The client can specify a minimum size so that the compositor does + * not try to configure the window below this size. + * + * The width and height arguments are in window geometry coordinates. + * See xdg_surface.set_window_geometry. + * + * Values set in this way are double-buffered. They will get applied + * on the next commit. + * + * The compositor can use this information to allow or disallow + * different states like maximize or fullscreen and draw accurate + * animations. + * + * Similarly, a tiling window manager may use this information to + * place and resize client windows in a more effective way. + * + * The client should not rely on the compositor to obey the minimum + * size. The compositor may decide to ignore the values set by the + * client and request a smaller size. + * + * If never set, or a value of zero in the request, means that the + * client has no expected minimum size in the given dimension. + * As a result, a client wishing to reset the minimum size + * to an unspecified state can use zero for width and height in the + * request. + * + * Requesting a minimum size to be larger than the maximum size of + * a surface is illegal and will result in an invalid_size error. + * + * The width and height must be greater than or equal to zero. Using + * strictly negative values for width and height will result in a + * invalid_size error. + */ +static inline void +xdg_toplevel_set_min_size(struct xdg_toplevel *xdg_toplevel, int32_t width, int32_t height) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_toplevel, + XDG_TOPLEVEL_SET_MIN_SIZE, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_toplevel), 0, width, height); +} + +/** + * @ingroup iface_xdg_toplevel + * + * Maximize the surface. + * + * After requesting that the surface should be maximized, the compositor + * will respond by emitting a configure event. Whether this configure + * actually sets the window maximized is subject to compositor policies. + * The client must then update its content, drawing in the configured + * state. The client must also acknowledge the configure when committing + * the new content (see ack_configure). + * + * It is up to the compositor to decide how and where to maximize the + * surface, for example which output and what region of the screen should + * be used. + * + * If the surface was already maximized, the compositor will still emit + * a configure event with the "maximized" state. + * + * If the surface is in a fullscreen state, this request has no direct + * effect. It may alter the state the surface is returned to when + * unmaximized unless overridden by the compositor. + */ +static inline void +xdg_toplevel_set_maximized(struct xdg_toplevel *xdg_toplevel) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_toplevel, + XDG_TOPLEVEL_SET_MAXIMIZED, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_toplevel), 0); +} + +/** + * @ingroup iface_xdg_toplevel + * + * Unmaximize the surface. + * + * After requesting that the surface should be unmaximized, the compositor + * will respond by emitting a configure event. Whether this actually + * un-maximizes the window is subject to compositor policies. + * If available and applicable, the compositor will include the window + * geometry dimensions the window had prior to being maximized in the + * configure event. The client must then update its content, drawing it in + * the configured state. The client must also acknowledge the configure + * when committing the new content (see ack_configure). + * + * It is up to the compositor to position the surface after it was + * unmaximized; usually the position the surface had before maximizing, if + * applicable. + * + * If the surface was already not maximized, the compositor will still + * emit a configure event without the "maximized" state. + * + * If the surface is in a fullscreen state, this request has no direct + * effect. It may alter the state the surface is returned to when + * unmaximized unless overridden by the compositor. + */ +static inline void +xdg_toplevel_unset_maximized(struct xdg_toplevel *xdg_toplevel) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_toplevel, + XDG_TOPLEVEL_UNSET_MAXIMIZED, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_toplevel), 0); +} + +/** + * @ingroup iface_xdg_toplevel + * + * Make the surface fullscreen. + * + * After requesting that the surface should be fullscreened, the + * compositor will respond by emitting a configure event. Whether the + * client is actually put into a fullscreen state is subject to compositor + * policies. The client must also acknowledge the configure when + * committing the new content (see ack_configure). + * + * The output passed by the request indicates the client's preference as + * to which display it should be set fullscreen on. If this value is NULL, + * it's up to the compositor to choose which display will be used to map + * this surface. + * + * If the surface doesn't cover the whole output, the compositor will + * position the surface in the center of the output and compensate with + * with border fill covering the rest of the output. The content of the + * border fill is undefined, but should be assumed to be in some way that + * attempts to blend into the surrounding area (e.g. solid black). + * + * If the fullscreened surface is not opaque, the compositor must make + * sure that other screen content not part of the same surface tree (made + * up of subsurfaces, popups or similarly coupled surfaces) are not + * visible below the fullscreened surface. + */ +static inline void +xdg_toplevel_set_fullscreen(struct xdg_toplevel *xdg_toplevel, struct wl_output *output) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_toplevel, + XDG_TOPLEVEL_SET_FULLSCREEN, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_toplevel), 0, output); +} + +/** + * @ingroup iface_xdg_toplevel + * + * Make the surface no longer fullscreen. + * + * After requesting that the surface should be unfullscreened, the + * compositor will respond by emitting a configure event. + * Whether this actually removes the fullscreen state of the client is + * subject to compositor policies. + * + * Making a surface unfullscreen sets states for the surface based on the following: + * * the state(s) it may have had before becoming fullscreen + * * any state(s) decided by the compositor + * * any state(s) requested by the client while the surface was fullscreen + * + * The compositor may include the previous window geometry dimensions in + * the configure event, if applicable. + * + * The client must also acknowledge the configure when committing the new + * content (see ack_configure). + */ +static inline void +xdg_toplevel_unset_fullscreen(struct xdg_toplevel *xdg_toplevel) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_toplevel, + XDG_TOPLEVEL_UNSET_FULLSCREEN, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_toplevel), 0); +} + +/** + * @ingroup iface_xdg_toplevel + * + * Request that the compositor minimize your surface. There is no + * way to know if the surface is currently minimized, nor is there + * any way to unset minimization on this surface. + * + * If you are looking to throttle redrawing when minimized, please + * instead use the wl_surface.frame event for this, as this will + * also work with live previews on windows in Alt-Tab, Expose or + * similar compositor features. + */ +static inline void +xdg_toplevel_set_minimized(struct xdg_toplevel *xdg_toplevel) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_toplevel, + XDG_TOPLEVEL_SET_MINIMIZED, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_toplevel), 0); +} + +#ifndef XDG_POPUP_ERROR_ENUM +#define XDG_POPUP_ERROR_ENUM +enum xdg_popup_error { + /** + * tried to grab after being mapped + */ + XDG_POPUP_ERROR_INVALID_GRAB = 0, +}; +#endif /* XDG_POPUP_ERROR_ENUM */ + +/** + * @ingroup iface_xdg_popup + * @struct xdg_popup_listener + */ +struct xdg_popup_listener { + /** + * configure the popup surface + * + * This event asks the popup surface to configure itself given + * the configuration. The configured state should not be applied + * immediately. See xdg_surface.configure for details. + * + * The x and y arguments represent the position the popup was + * placed at given the xdg_positioner rule, relative to the upper + * left corner of the window geometry of the parent surface. + * + * For version 2 or older, the configure event for an xdg_popup is + * only ever sent once for the initial configuration. Starting with + * version 3, it may be sent again if the popup is setup with an + * xdg_positioner with set_reactive requested, or in response to + * xdg_popup.reposition requests. + * @param x x position relative to parent surface window geometry + * @param y y position relative to parent surface window geometry + * @param width window geometry width + * @param height window geometry height + */ + void (*configure)(void *data, + struct xdg_popup *xdg_popup, + int32_t x, + int32_t y, + int32_t width, + int32_t height); + /** + * popup interaction is done + * + * The popup_done event is sent out when a popup is dismissed by + * the compositor. The client should destroy the xdg_popup object + * at this point. + */ + void (*popup_done)(void *data, + struct xdg_popup *xdg_popup); + /** + * signal the completion of a repositioned request + * + * The repositioned event is sent as part of a popup + * configuration sequence, together with xdg_popup.configure and + * lastly xdg_surface.configure to notify the completion of a + * reposition request. + * + * The repositioned event is to notify about the completion of a + * xdg_popup.reposition request. The token argument is the token + * passed in the xdg_popup.reposition request. + * + * Immediately after this event is emitted, xdg_popup.configure and + * xdg_surface.configure will be sent with the updated size and + * position, as well as a new configure serial. + * + * The client should optionally update the content of the popup, + * but must acknowledge the new popup configuration for the new + * position to take effect. See xdg_surface.ack_configure for + * details. + * @param token reposition request token + * @since 3 + */ + void (*repositioned)(void *data, + struct xdg_popup *xdg_popup, + uint32_t token); +}; + +/** + * @ingroup iface_xdg_popup + */ +static inline int +xdg_popup_add_listener(struct xdg_popup *xdg_popup, + const struct xdg_popup_listener *listener, void *data) +{ + return wl_proxy_add_listener((struct wl_proxy *) xdg_popup, + (void (**)(void)) listener, data); +} + +#define XDG_POPUP_DESTROY 0 +#define XDG_POPUP_GRAB 1 +#define XDG_POPUP_REPOSITION 2 + +/** + * @ingroup iface_xdg_popup + */ +#define XDG_POPUP_CONFIGURE_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_popup + */ +#define XDG_POPUP_POPUP_DONE_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_popup + */ +#define XDG_POPUP_REPOSITIONED_SINCE_VERSION 3 + +/** + * @ingroup iface_xdg_popup + */ +#define XDG_POPUP_DESTROY_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_popup + */ +#define XDG_POPUP_GRAB_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_popup + */ +#define XDG_POPUP_REPOSITION_SINCE_VERSION 3 + +/** @ingroup iface_xdg_popup */ +static inline void +xdg_popup_set_user_data(struct xdg_popup *xdg_popup, void *user_data) +{ + wl_proxy_set_user_data((struct wl_proxy *) xdg_popup, user_data); +} + +/** @ingroup iface_xdg_popup */ +static inline void * +xdg_popup_get_user_data(struct xdg_popup *xdg_popup) +{ + return wl_proxy_get_user_data((struct wl_proxy *) xdg_popup); +} + +static inline uint32_t +xdg_popup_get_version(struct xdg_popup *xdg_popup) +{ + return wl_proxy_get_version((struct wl_proxy *) xdg_popup); +} + +/** + * @ingroup iface_xdg_popup + * + * This destroys the popup. Explicitly destroying the xdg_popup + * object will also dismiss the popup, and unmap the surface. + * + * If this xdg_popup is not the "topmost" popup, the + * xdg_wm_base.not_the_topmost_popup protocol error will be sent. + */ +static inline void +xdg_popup_destroy(struct xdg_popup *xdg_popup) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_popup, + XDG_POPUP_DESTROY, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_popup), WL_MARSHAL_FLAG_DESTROY); +} + +/** + * @ingroup iface_xdg_popup + * + * This request makes the created popup take an explicit grab. An explicit + * grab will be dismissed when the user dismisses the popup, or when the + * client destroys the xdg_popup. This can be done by the user clicking + * outside the surface, using the keyboard, or even locking the screen + * through closing the lid or a timeout. + * + * If the compositor denies the grab, the popup will be immediately + * dismissed. + * + * This request must be used in response to some sort of user action like a + * button press, key press, or touch down event. The serial number of the + * event should be passed as 'serial'. + * + * The parent of a grabbing popup must either be an xdg_toplevel surface or + * another xdg_popup with an explicit grab. If the parent is another + * xdg_popup it means that the popups are nested, with this popup now being + * the topmost popup. + * + * Nested popups must be destroyed in the reverse order they were created + * in, e.g. the only popup you are allowed to destroy at all times is the + * topmost one. + * + * When compositors choose to dismiss a popup, they may dismiss every + * nested grabbing popup as well. When a compositor dismisses popups, it + * will follow the same dismissing order as required from the client. + * + * If the topmost grabbing popup is destroyed, the grab will be returned to + * the parent of the popup, if that parent previously had an explicit grab. + * + * If the parent is a grabbing popup which has already been dismissed, this + * popup will be immediately dismissed. If the parent is a popup that did + * not take an explicit grab, an error will be raised. + * + * During a popup grab, the client owning the grab will receive pointer + * and touch events for all their surfaces as normal (similar to an + * "owner-events" grab in X11 parlance), while the top most grabbing popup + * will always have keyboard focus. + */ +static inline void +xdg_popup_grab(struct xdg_popup *xdg_popup, struct wl_seat *seat, uint32_t serial) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_popup, + XDG_POPUP_GRAB, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_popup), 0, seat, serial); +} + +/** + * @ingroup iface_xdg_popup + * + * Reposition an already-mapped popup. The popup will be placed given the + * details in the passed xdg_positioner object, and a + * xdg_popup.repositioned followed by xdg_popup.configure and + * xdg_surface.configure will be emitted in response. Any parameters set + * by the previous positioner will be discarded. + * + * The passed token will be sent in the corresponding + * xdg_popup.repositioned event. The new popup position will not take + * effect until the corresponding configure event is acknowledged by the + * client. See xdg_popup.repositioned for details. The token itself is + * opaque, and has no other special meaning. + * + * If multiple reposition requests are sent, the compositor may skip all + * but the last one. + * + * If the popup is repositioned in response to a configure event for its + * parent, the client should send an xdg_positioner.set_parent_configure + * and possibly an xdg_positioner.set_parent_size request to allow the + * compositor to properly constrain the popup. + * + * If the popup is repositioned together with a parent that is being + * resized, but not in response to a configure event, the client should + * send an xdg_positioner.set_parent_size request. + */ +static inline void +xdg_popup_reposition(struct xdg_popup *xdg_popup, struct xdg_positioner *positioner, uint32_t token) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_popup, + XDG_POPUP_REPOSITION, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_popup), 0, positioner, token); +} + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/wayland/java/WindowWayland.java b/wayland/java/WindowWayland.java index 0fa0ce65..53832795 100644 --- a/wayland/java/WindowWayland.java +++ b/wayland/java/WindowWayland.java @@ -31,13 +31,15 @@ public void unmarkText() { @Override public IRect getWindowRect() { assert _onUIThread() : "Should be run on UI thread"; - return _nGetWindowRect(); + // very very bad! + return IRect.makeXYWH(0, 0, 0, 0); } @Override public IRect getContentRect() { assert _onUIThread() : "Should be run on UI thread"; - return _nGetContentRect(); + // stop! + return IRect.makeXYWH(0, 0, 0, 0); } @Override @@ -118,7 +120,7 @@ public float getOpacity(){ @Override public Screen getScreen() { assert _onUIThread() : "Should be run on UI thread"; - return _nGetScreen(); + return new Screen(0, false, IRect.makeXYWH(0, 0, 0, 0), IRect.makeXYWH(0, 0, 0, 0), 1); } @Override @@ -214,6 +216,12 @@ public boolean isFullScreen() { return _nIsFullScreen(); } + @Override + public float getScale() { + assert _onUIThread() : "Should be run on UI Thread"; + return _nGetScale(); + } + @ApiStatus.Internal public static native long _nMake(); @ApiStatus.Internal public native void _nSetVisible(boolean isVisible); @ApiStatus.Internal public native IRect _nGetWindowRect(); @@ -222,7 +230,7 @@ public boolean isFullScreen() { // @ApiStatus.Internal public native void _nSetWindowSize(int width, int height); @ApiStatus.Internal public native void _nSetMouseCursor(int cursorId); // @ApiStatus.Internal public native void _nSetContentSize(int width, int height); - @ApiStatus.Internal public native Screen _nGetScreen(); + // @ApiStatus.Internal public native Screen _nGetScreen(); @ApiStatus.Internal public native void _nRequestFrame(); @ApiStatus.Internal public native void _nClose(); @ApiStatus.Internal public native void _nMaximize(); @@ -232,4 +240,5 @@ public boolean isFullScreen() { @ApiStatus.Internal public native void _nSetTitlebarVisible(boolean isVisible); @ApiStatus.Internal public native void _nSetFullScreen(boolean isFullScreen); @ApiStatus.Internal public native boolean _nIsFullScreen(); + @ApiStatus.Internal public native float _nGetScale(); } diff --git a/windows/java/WindowWin32.java b/windows/java/WindowWin32.java index 1379268e..496d97e7 100644 --- a/windows/java/WindowWin32.java +++ b/windows/java/WindowWin32.java @@ -172,7 +172,10 @@ public Window bringToFront() { _nBringToFront(); return this; } - + + public float getScale() { + return this.getScreen().getScale(); + } @Override public boolean isFront() { assert _onUIThread() : "Should be run on UI thread"; diff --git a/x11/CMakeLists.txt b/x11/CMakeLists.txt index e50d48e5..2157bd4b 100644 --- a/x11/CMakeLists.txt +++ b/x11/CMakeLists.txt @@ -18,7 +18,9 @@ endif() find_package(X11 REQUIRED) find_package(OpenGL REQUIRED) -file(GLOB SOURCES_CXX ${CMAKE_CURRENT_LIST_DIR}/../shared/cc/*.cc ${CMAKE_CURRENT_LIST_DIR}/cc/*.cc) +file(GLOB SOURCES_CXX ${CMAKE_CURRENT_LIST_DIR}/../shared/cc/*.cc + ${CMAKE_CURRENT_LIST_DIR}/cc/*.cc + ${CMAKE_CURRENT_LIST_DIR}/../linux/cc/*.cc) file(GLOB SOURCES_CXX_IMPL ${CMAKE_CURRENT_LIST_DIR}/../shared/cc/impl/*.cc) add_library(jwm SHARED ${SOURCES_OBJC} ${SOURCES_CXX} ${SOURCES_CXX_IMPL}) @@ -33,7 +35,7 @@ if (NOT JAVA_HOME) endif() endif() -target_include_directories(jwm PRIVATE ${CMAKE_CURRENT_LIST_DIR}/../shared/cc ../linux/cc ${JAVA_HOME}/include ${JAVA_HOME}/include/linux) +target_include_directories(jwm PRIVATE ${CMAKE_CURRENT_LIST_DIR}/../shared/cc ${CMAKE_CURRENT_LIST_DIR}/../linux/cc ${JAVA_HOME}/include ${JAVA_HOME}/include/linux) set_target_properties(jwm PROPERTIES OUTPUT_NAME "jwm_${JWM_ARCH}") diff --git a/linux/cc/ScreenInfo.cc b/x11/cc/ScreenInfo.cc similarity index 99% rename from linux/cc/ScreenInfo.cc rename to x11/cc/ScreenInfo.cc index f3aa1dee..f1e56dc2 100644 --- a/linux/cc/ScreenInfo.cc +++ b/x11/cc/ScreenInfo.cc @@ -1,7 +1,6 @@ #include "ScreenInfo.hh" #include "AppX11.hh" - jobject jwm::ScreenInfo::asJavaObject(JNIEnv* env) const { return jwm::classes::Screen::make(env, id, isPrimary, bounds, bounds, jwm::app.getScale()); } diff --git a/x11/cc/ScreenInfo.hh b/x11/cc/ScreenInfo.hh new file mode 100644 index 00000000..947890a3 --- /dev/null +++ b/x11/cc/ScreenInfo.hh @@ -0,0 +1,13 @@ +#pragma once + +#include + +namespace jwm +{ + struct ScreenInfo { + long id; + IRect bounds; + bool isPrimary; + jobject asJavaObject(JNIEnv* env) const; + }; +} // namespace jwm diff --git a/x11/cc/WindowX11.cc b/x11/cc/WindowX11.cc index c73fba04..4c2605f2 100644 --- a/x11/cc/WindowX11.cc +++ b/x11/cc/WindowX11.cc @@ -338,9 +338,6 @@ int WindowX11::getHeight() { return _height; } -float WindowX11::getScale() { - return jwm::app.getScale(); -} bool WindowX11::init() { @@ -585,3 +582,8 @@ extern "C" JNIEXPORT jboolean JNICALL Java_io_github_humbleui_jwm_WindowX11__1nI jwm::WindowX11* instance = reinterpret_cast(jwm::classes::Native::fromJava(env, obj)); return instance->isFullScreen(); } + +extern "C" JNIEXPORT jfloat JNICALL Java_io_github_humbleui_jwm_WindowX11__1nGetScale + (JNIEnv* env, jobject obj) { + return jwm::app.getScale(); +} diff --git a/x11/cc/WindowX11.hh b/x11/cc/WindowX11.hh index 55f5befe..e2fc7ea0 100644 --- a/x11/cc/WindowX11.hh +++ b/x11/cc/WindowX11.hh @@ -24,7 +24,7 @@ namespace jwm { int getTop(); int getWidth(); int getHeight(); - float getScale(); + void move(int left, int top); void resize(int width, int height); void requestRedraw() { diff --git a/x11/java/WindowX11.java b/x11/java/WindowX11.java index d60e0502..bc865777 100644 --- a/x11/java/WindowX11.java +++ b/x11/java/WindowX11.java @@ -211,6 +211,11 @@ public boolean isFullScreen() { return _nIsFullScreen(); } + @Override + public float getScale() { + return ; + } + @ApiStatus.Internal public static native long _nMake(); @ApiStatus.Internal public native void _nSetVisible(boolean isVisible); @ApiStatus.Internal public native IRect _nGetWindowRect(); @@ -229,4 +234,5 @@ public boolean isFullScreen() { @ApiStatus.Internal public native void _nSetTitlebarVisible(boolean isVisible); @ApiStatus.Internal public native void _nSetFullScreen(boolean isFullScreen); @ApiStatus.Internal public native boolean _nIsFullScreen(); + @ApiStatus.Internal public native float _nGetScale(); } From a10db21651ee0097d6af862dbbede917ec269625 Mon Sep 17 00:00:00 2001 From: BulbyVR <26726264+TheDrawingCoder-Gamer@users.noreply.github.com> Date: Sat, 2 Dec 2023 20:11:39 -0500 Subject: [PATCH 03/95] OH MY, SO MANY CHANGES --- linux/cc/ILayer.hh | 1 + script/build.py | 11 ++- script/clean.py | 8 +- script/run.py | 14 ++- shared/java/Layer.java | 2 +- shared/java/LayerGL.java | 2 +- shared/java/Platform.java | 10 +- shared/java/impl/Library.java | 5 +- wayland/CMakeLists.txt | 20 ++-- wayland/cc/AppWayland.cc | 2 +- wayland/cc/ClipboardWayland.cc | 2 +- wayland/cc/KeyWayland.cc | 16 ++-- wayland/cc/LayerGLWayland.cc | 16 +++- wayland/cc/LayerRasterWayland.cc | 15 +-- wayland/cc/ScreenInfo.cc | 4 +- wayland/cc/ShmPool.cc | 13 +-- wayland/cc/WindowManagerWayland.cc | 128 +++++++++++++++++--------- wayland/cc/WindowManagerWayland.hh | 20 ++-- wayland/cc/WindowWayland.cc | 143 +++++++++++++++++++++-------- wayland/cc/WindowWayland.hh | 39 +++++--- wayland/java/WindowWayland.java | 8 +- x11/CMakeLists.txt | 4 +- x11/cc/KeyX11.cc | 2 +- x11/cc/LayerGLX11.cc | 4 +- x11/cc/LayerRasterX11.cc | 4 +- x11/java/WindowX11.java | 3 +- 26 files changed, 328 insertions(+), 168 deletions(-) diff --git a/linux/cc/ILayer.hh b/linux/cc/ILayer.hh index 81a32c2b..f21346a4 100644 --- a/linux/cc/ILayer.hh +++ b/linux/cc/ILayer.hh @@ -15,6 +15,7 @@ public: virtual void makeCurrentForced(); virtual void setVsyncMode(VSync v) = 0; virtual void close() = 0; + virtual void resize(int width, int height); static ILayer* _ourCurrentLayer; diff --git a/script/build.py b/script/build.py index ec528e66..9954d826 100755 --- a/script/build.py +++ b/script/build.py @@ -19,6 +19,9 @@ def build_native_system(system): if os.path.exists('build/libjwm_x64.so'): build_utils.copy_newer('build/libjwm_x64.so', '../target/classes/libjwm_x64.so') + + if os.path.exists('build/libjwm_x64_wayland.so'): + build_utils.copy_newer('build/libjwm_x64_wayland.so', '../target/classes/libjwm_x64_wayland.so') if os.path.exists('build/jwm_x64.dll'): build_utils.copy_newer('build/jwm_x64.dll', '../target/classes/jwm_x64.dll') @@ -34,8 +37,12 @@ def build_native(): return 0 def build_java(): os.chdir(common.basedir) - sources = build_utils.files("x11/java/**/*.java", "macos/java/**/*.java", "shared/java/**/*.java", "windows/java/**/*.java",) - build_utils.javac(sources, "target/classes", classpath=common.deps_compile()) + sources = build_utils.files("x11/java/**/*.java", + "macos/java/**/*.java", + "shared/java/**/*.java", + "windows/java/**/*.java", + "wayland/java/**/*.java") + build_utils.javac(sources, "target/classes", classpath=common.deps_compile(), release="16") return 0 def main(): diff --git a/script/clean.py b/script/clean.py index 77a3870b..db328a9b 100755 --- a/script/clean.py +++ b/script/clean.py @@ -4,9 +4,13 @@ def main(): os.chdir(common.basedir) build_utils.rmdir("target") - build_utils.rmdir(build_utils.system + "/build") + if build_utils.system == "linux": + build_utils.rmdir("wayland/build") + build_utils.rmdir("x11/build") + else: + build_utils.rmdir(build_utils.system + "/build") build_utils.rmdir("examples/dashboard/target") return 0 if __name__ == '__main__': - sys.exit(main()) \ No newline at end of file + sys.exit(main()) diff --git a/script/run.py b/script/run.py index d4a282ef..98f3c0ee 100755 --- a/script/run.py +++ b/script/run.py @@ -32,9 +32,17 @@ def main(): ] else: classpath += [ - 'target/classes', - build_utils.system + '/build' - ] + 'target/classes' + ] + if build_utils.system == "linux": + classpath += [ + "wayland/build", + "x11/build" + ] + else: + classpath += [ + build_utils.system + '/build' + ] if args.skija_dir: classpath += [ diff --git a/shared/java/Layer.java b/shared/java/Layer.java index c3931abd..7099f9ba 100644 --- a/shared/java/Layer.java +++ b/shared/java/Layer.java @@ -47,4 +47,4 @@ default void swapBuffers() {} @Override default void close() {} -} \ No newline at end of file +} diff --git a/shared/java/LayerGL.java b/shared/java/LayerGL.java index 75aabb57..5162c70a 100644 --- a/shared/java/LayerGL.java +++ b/shared/java/LayerGL.java @@ -75,4 +75,4 @@ public void close() { @ApiStatus.Internal public native void _nResize(int width, int height); @ApiStatus.Internal public native void _nSwapBuffers(); @ApiStatus.Internal public native void _nClose(); -} \ No newline at end of file +} diff --git a/shared/java/Platform.java b/shared/java/Platform.java index 737fcaa8..cf923704 100644 --- a/shared/java/Platform.java +++ b/shared/java/Platform.java @@ -3,7 +3,8 @@ public enum Platform { WINDOWS, X11, - MACOS; + MACOS, + WAYLAND; public static final Platform CURRENT; static { @@ -13,8 +14,11 @@ public enum Platform { else if (os.contains("windows")) CURRENT = WINDOWS; else if (os.contains("nux") || os.contains("nix")) - CURRENT = X11; + if (System.getenv("WAYLAND_DISPLAY") != null) + CURRENT = WAYLAND; + else + CURRENT = X11; else throw new RuntimeException("Unsupported platform: " + os); } -} \ No newline at end of file +} diff --git a/shared/java/impl/Library.java b/shared/java/impl/Library.java index 7104fc59..43fd5cbc 100644 --- a/shared/java/impl/Library.java +++ b/shared/java/impl/Library.java @@ -46,7 +46,10 @@ public static synchronized void load() { } else if (Platform.CURRENT == Platform.X11) { File library = _extract("/", "libjwm_x64.so", tempDir); System.load(library.getAbsolutePath()); - } + } /*else if (Platform.CURRENT == Platform.WAYLAND) { + File library = _extract("/", "libjwm_x64_wayland.so", tempDir); + System.load(library.getAbsolutePath()); + }*/ if (tempDir.exists() && version == null) { Runtime.getRuntime().addShutdownHook(new Thread(() -> { diff --git a/wayland/CMakeLists.txt b/wayland/CMakeLists.txt index 8af35a72..95ca0ad2 100644 --- a/wayland/CMakeLists.txt +++ b/wayland/CMakeLists.txt @@ -4,7 +4,7 @@ cmake_minimum_required(VERSION 3.9) cmake_policy(SET CMP0072 NEW) project(jwm LANGUAGES CXX) -set(CMAKE_CXX_STANDARD 14) +set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) if(NOT JWM_ARCH) @@ -16,9 +16,16 @@ if(NOT JWM_ARCH) endif() file(GLOB SOURCES_CXX ${CMAKE_CURRENT_LIST_DIR}/../shared/cc/*.cc - ${CMAKE_CURRENT_LIST_DIR}/cc/*.cc ${CMAKE_CURRENT_LIST_DIR}/../linux/cc/*.cc) + ${CMAKE_CURRENT_LIST_DIR}/../linux/cc/*.cc + ${CMAKE_CURRENT_LIST_DIR}/cc/*.cc ) file(GLOB SOURCES_CXX_IMPL ${CMAKE_CURRENT_LIST_DIR}/../shared/cc/impl/*.cc) add_library(jwm SHARED ${SOURCES_OBJC} ${SOURCES_CXX} ${SOURCES_CXX_IMPL}) +find_library(WAYLAND_CLIENT_LIB wayland-client) +find_library(DECOR_LIB decor-0) +find_library(WAYLAND_CURSOR wayland-cursor) +find_library(XKBCOMMON xkbcommon) +find_library(EGL EGL) +find_library(WAYLAND_EGL wayland-egl) set(JAVA_HOME $ENV{JAVA_HOME}) if (NOT JAVA_HOME) @@ -32,9 +39,8 @@ if (NOT JAVA_HOME) endif() target_include_directories(jwm PRIVATE ${CMAKE_CURRENT_LIST_DIR}/../shared/cc ${CMAKE_CURRENT_LIST_DIR}/../linux/cc ${JAVA_HOME}/include ${JAVA_HOME}/include/linux) -set_target_properties(jwm PROPERTIES OUTPUT_NAME "jwm_${JWM_ARCH}") +set_target_properties(jwm PROPERTIES OUTPUT_NAME "jwm_${JWM_ARCH}_wayland") - -target_link_libraries(jwm PRIVATE wayland-client, decor, - wayland-cursor, xkbcommon) -target_link_libraries(jwm PRIVATE EGL, wayland-egl) +target_link_libraries(jwm PRIVATE ${WAYLAND_CLIENT_LIB} ${DECOR_LIB} + ${WAYLAND_CURSOR} ${XKBCOMMON}) +target_link_libraries(jwm PRIVATE ${EGL} ${WAYLAND_EGL}) diff --git a/wayland/cc/AppWayland.cc b/wayland/cc/AppWayland.cc index dc075681..326963c0 100644 --- a/wayland/cc/AppWayland.cc +++ b/wayland/cc/AppWayland.cc @@ -45,7 +45,7 @@ extern "C" JNIEXPORT void JNICALL Java_io_github_humbleui_jwm_App__1nRunOnUIThre } // how awful -extern "C" JNIEXPORT jobjectArray JNICALL Java_io_github_humbleui_jwm_App__1nGetScreens(JNIEnv env*, jobject cls) noexcept { +extern "C" JNIEXPORT jobjectArray JNICALL Java_io_github_humbleui_jwm_App__1nGetScreens(JNIEnv* env, jobject cls) noexcept { jobjectArray array = env->NewObjectArray(0, jwm::classes::Screen::kCls, 0); return array; diff --git a/wayland/cc/ClipboardWayland.cc b/wayland/cc/ClipboardWayland.cc index c32b1b51..0d02345a 100644 --- a/wayland/cc/ClipboardWayland.cc +++ b/wayland/cc/ClipboardWayland.cc @@ -35,7 +35,7 @@ namespace jwm { */ // impl me : ) jobjectArray formats = env->NewObjectArray(0, classes::ClipboardFormat::kCls, nullptr); - return format; + return formats; } jobject get(JNIEnv* env, jobjectArray formats) { diff --git a/wayland/cc/KeyWayland.cc b/wayland/cc/KeyWayland.cc index 026e31c6..7a16be98 100644 --- a/wayland/cc/KeyWayland.cc +++ b/wayland/cc/KeyWayland.cc @@ -1,14 +1,14 @@ #include "KeyWayland.hh" #include "KeyModifier.hh" -bool gKeyStates[(size_t) jwm::Key::_KEY_COUNT] = {0}; +bool gKeyStates[(std::size_t) jwm::Key::_KEY_COUNT] = {0}; bool jwm::KeyWayland::getKeyState(jwm::Key key) { - return gKeyStates[(size_t) key]; + return gKeyStates[(std::size_t) key]; } void jwm::KeyWayland::setKeyState(jwm::Key key, bool isDown) { - gKeyStates[(size_t) key] = isDown; + gKeyStates[(std::size_t) key] = isDown; } int jwm::KeyWayland::getModifiers() { @@ -25,10 +25,10 @@ int jwm::KeyWayland::getModifiers() { int jwm::KeyWayland::getModifiersFromMask(int mask) { int m = getModifiers(); - - if (mask & ShiftMask ) m |= (int)jwm::KeyModifier::SHIFT; - if (mask & ControlMask) m |= (int)jwm::KeyModifier::CONTROL; - if (mask & Mod1Mask ) m |= (int)jwm::KeyModifier::ALT; + // ??? + // if (mask & ShiftMask ) m |= (int)jwm::KeyModifier::SHIFT; + // if (mask & ControlMask) m |= (int)jwm::KeyModifier::CONTROL; + // if (mask & Mod1Mask ) m |= (int)jwm::KeyModifier::ALT; return m; } @@ -171,5 +171,5 @@ jwm::Key jwm::KeyWayland::fromNative(uint32_t v) { /* default: return Key::UNDEFINED; } */ // IMPL ME! - return Key::UNDEFINED: + return Key::UNDEFINED; } diff --git a/wayland/cc/LayerGLWayland.cc b/wayland/cc/LayerGLWayland.cc index e8b96960..8e3f2ee2 100644 --- a/wayland/cc/LayerGLWayland.cc +++ b/wayland/cc/LayerGLWayland.cc @@ -5,6 +5,7 @@ #include "impl/RefCounted.hh" #include "WindowWayland.hh" #include +#include #include namespace jwm { @@ -17,14 +18,19 @@ namespace jwm { EGLDisplay _display = nullptr; EGLSurface _surface = nullptr; - LayerGLWayland() = default; - virtual ~LayerGLWayland() = default; + LayerGL() = default; + virtual ~LayerGL() = default; void attach(WindowWayland* window) { fWindow = jwm::ref(window); + if (window->_layer) { + // HACK: close window and reopen + window->close(); + window->init(); + } fWindow->setLayer(this); if (_display == nullptr) { - _display = eglGetPlatformDisplayEXT(EGL_PLATFORM_WAYLAND_KHR, window->_windowManager.display, EGL_NONE); + _display = eglGetPlatformDisplay(EGL_PLATFORM_WAYLAND_KHR, window->_windowManager.display, nullptr); eglInitialize(_display, nullptr, nullptr); @@ -58,7 +64,7 @@ namespace jwm { // vsync? what vsync? } - void resize(int width, int height) { + void resize(int width, int height) override { glClearStencil(0); glClearColor(0, 0, 0, 255); glStencilMask(0xffffffff); @@ -70,7 +76,7 @@ namespace jwm { } void swapBuffers() { - eglSwapBuffers(fWindow->_windowManager.getDisplay(), _surface); + eglSwapBuffers(_display, _surface); } void close() override { diff --git a/wayland/cc/LayerRasterWayland.cc b/wayland/cc/LayerRasterWayland.cc index f656709b..0462940e 100644 --- a/wayland/cc/LayerRasterWayland.cc +++ b/wayland/cc/LayerRasterWayland.cc @@ -5,6 +5,8 @@ #include "impl/RefCounted.hh" #include "WindowWayland.hh" #include "ShmPool.hh" +#include + namespace jwm { class LayerRaster: public RefCounted, public ILayer { public: @@ -12,7 +14,7 @@ namespace jwm { size_t _width = 0, _height = 0; wl_buffer* _buffer = nullptr; uint8_t* _imageData = nullptr; - ShmPool _pool = nullptr; + ShmPool* _pool = nullptr; VSync _vsync = VSYNC_ENABLED; LayerRaster() = default; @@ -23,17 +25,17 @@ namespace jwm { fWindow->setLayer(this); } - void resize(int width, int height) { + void resize(int width, int height) override { wl_display* d = fWindow->_windowManager.display; _width = width; _height = height; int bufSize = width * height * sizeof(uint32_t) * 2; if (!_pool) { - _pool = new ShmPool(fWindow->_windowManager->shm, bufSize); + _pool = new ShmPool(fWindow->_windowManager.shm, bufSize); } - _pool.grow(bufSize); + _pool->grow(bufSize); // LSBFirst means Little endian : ) - auto buf = _pool.createBuffer(0, width, height, width * sizeof(uint32_t), WL_SHM_FORMAT_ABRG8888); + auto buf = _pool->createBuffer(0, width, height, width * sizeof(uint32_t), WL_SHM_FORMAT_ABGR8888); _buffer = buf.first; _imageData = buf.second; @@ -58,7 +60,8 @@ namespace jwm { wl_buffer_destroy(_buffer); _buffer = nullptr; } - destroy _pool; + // ??? + // destroy _pool; jwm::unref(&fWindow); } diff --git a/wayland/cc/ScreenInfo.cc b/wayland/cc/ScreenInfo.cc index a7392e40..3dddf554 100644 --- a/wayland/cc/ScreenInfo.cc +++ b/wayland/cc/ScreenInfo.cc @@ -1,5 +1,5 @@ #include "ScreenInfo.hh" - +#include "AppWayland.hh" jobject jwm::ScreenInfo::asJavaObject(JNIEnv* env) const { - return jwm::classes::Screen::make(env, id, isPrimary, bounds, bounds, jwm::app.getScale()); + return jwm::classes::Screen::make(env, id, isPrimary, bounds, bounds, 1.0f); } diff --git a/wayland/cc/ShmPool.cc b/wayland/cc/ShmPool.cc index e2a3a334..0f42ed50 100644 --- a/wayland/cc/ShmPool.cc +++ b/wayland/cc/ShmPool.cc @@ -4,6 +4,7 @@ #include #include #include +#include using namespace jwm; @@ -22,10 +23,10 @@ ShmPool::ShmPool(wl_shm* shm, size_t size): _fd = _allocateShmFile(size); if (_fd < 0) { // why : ( - throw std::system_error(EIO, "Couldn't allocate buffer"); + throw std::system_error(EIO, std::generic_category(), "Couldn't allocate buffer"); } _pool = wl_shm_create_pool(shm, _fd, size); - _rawData = mmap(nullptr, size, + _rawData = (uint8_t*)mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_SHARED, _fd, 0); } @@ -43,9 +44,9 @@ void ShmPool::grow(size_t size) { } while (ret < 0 && errno == EINTR); if (ret < 0) { // AAHHHHH! - throw std::system_error(EIO, "Couldn't grow buffer"); + throw std::system_error(EIO, std::generic_category(), "Couldn't grow buffer"); } - uint8_t* newData = mmap(nullptr, size, + uint8_t* newData = (uint8_t*)mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_SHARED, _fd, 0); // do I need to memcpy??? lets say no :troll: // TODO: error checking :troll: @@ -85,8 +86,8 @@ int ShmPool::_allocateShmFile(size_t size) { return fd; } -std::pair createBuffer(int offset, int width, int height, int stride, uint32_t format) { +std::pair ShmPool::createBuffer(int offset, int width, int height, int stride, uint32_t format) { wl_buffer* buffer = wl_shm_pool_create_buffer(_pool, offset, width, height, stride, format); - uint32_t* data = &_rawData[offset]; + uint8_t* data = &_rawData[offset]; return std::pair(buffer, data); } diff --git a/wayland/cc/WindowManagerWayland.cc b/wayland/cc/WindowManagerWayland.cc index a86f4650..2c629633 100644 --- a/wayland/cc/WindowManagerWayland.cc +++ b/wayland/cc/WindowManagerWayland.cc @@ -17,6 +17,7 @@ #include "xdg-shell.hh" #include + using namespace jwm; @@ -24,17 +25,17 @@ WindowManagerWayland::WindowManagerWayland(): display(wl_display_connect(nullptr)) { registry = wl_display_get_registry(display); wl_registry_listener registry_listener = { - .global = registryGlobalHandler, - .global_remove = registryGlobalHandlerRemove + .global = WindowManagerWayland::registryHandleGlobal, + .global_remove = WindowManagerWayland::registryHandleGlobalRemove }; - wl_registry_add_listener(registry, ®istry_listener, nullptr); + wl_registry_add_listener(registry, ®istry_listener, this); wl_display_roundtrip(display); if (!(shm && xdgShell && compositor && deviceManager && seat)) { // ??? // Bad. Means our compositor no supportie : ( - throw std::system_error(1, std::system_category); + throw std::system_error(ENOTSUP, std::generic_category(), "Unsupported compositor"); } @@ -46,7 +47,7 @@ WindowManagerWayland::WindowManagerWayland(): wl_cursor* cursor = wl_cursor_theme_get_cursor(cursor_theme, name); wl_cursor_image* cursorImage = cursor->images[0]; return cursorImage; - } + }; _cursors[static_cast(jwm::MouseCursor::ARROW )] = loadCursor("default"); _cursors[static_cast(jwm::MouseCursor::CROSSHAIR )] = loadCursor("crosshair"); @@ -63,20 +64,20 @@ WindowManagerWayland::WindowManagerWayland(): cursorSurface = wl_compositor_create_surface(compositor); wl_surface_attach(cursorSurface, - wl_cursor_get_image_buffer(_cursors[static_cast(jwm::MouseCursor::ARROW)]), 0, 0); + wl_cursor_image_get_buffer(_cursors[static_cast(jwm::MouseCursor::ARROW)]), 0, 0); wl_surface_commit(cursorSurface); } { pointer = wl_seat_get_pointer(seat); wl_pointer_listener pointerListener = { - .enter = pointerHandleEnter, - .leave = pointerHandleLeave, - .motion = pointerHandleMotion, - .button = pointerHandleButton, - .axis = pointerHandleAxis + .enter = WindowManagerWayland::pointerHandleEnter, + .leave = WindowManagerWayland::pointerHandleLeave, + .motion = WindowManagerWayland::pointerHandleMotion, + .button = WindowManagerWayland::pointerHandleButton, + .axis = WindowManagerWayland::pointerHandleAxis }; - wl_pointer_add_listener(pointer, &pointerListener, nullptr); + wl_pointer_add_listener(pointer, &pointerListener, this); } @@ -88,10 +89,37 @@ WindowManagerWayland::WindowManagerWayland(): void WindowManagerWayland::runLoop() { _runLoop = true; + int pipes[2]; + char buf[100]; + if (pipe(pipes)) { + printf("failed to open pipe\n"); + return; + } + notifyFD = pipes[1]; + fcntl(pipes[1], F_SETFL, O_NONBLOCK); // notifyLoop no blockie : ) + struct pollfd myPoll = {.fd=pipes[0], .events=POLLIN}; // who be out here running they loop while (_runLoop) { + if (jwm::classes::Throwable::exceptionThrown(app.getJniEnv())) + _runLoop = false; + _processCallbacks(); + + // block until event : ) + if (poll(&myPoll, 1, -1) < 0) { + printf("error with pipe\n"); + break; + } + + if (myPoll.revents & POLLIN) { + while (read(pipes[0], buf, sizeof(buf) == sizeof(buf))) {} + } + notifyBool.store(false); } + + notifyFD = -1; + close(pipes[0]); + close(pipes[1]); } @@ -110,7 +138,7 @@ void WindowManagerWayland::_processCallbacks() { } { // copy window list in case one closes any other, invalidating some iterator in _nativeWindowToMy - std::vector copy; + std::vector copy; for (auto& p : _nativeWindowToMy) { copy.push_back(p.second); } @@ -129,20 +157,21 @@ void WindowManagerWayland::_processCallbacks() { void WindowManagerWayland::registryHandleGlobal(void* data, wl_registry *registry, uint32_t name, const char* interface, uint32_t version) { + WindowManagerWayland* self = (WindowManagerWayland*)data; if (strcmp(interface, "wl_compositor") == 0) { - compositor = (wl_compositor*)wl_registry_bind(registry, name, + self->compositor = (wl_compositor*)wl_registry_bind(registry, name, &wl_compositor_interface, 3); } else if (strcmp(interface, "wl_shm") == 0) { - shm = (wl_shm*)wl_registry_bind(registry, name, + self->shm = (wl_shm*)wl_registry_bind(registry, name, &wl_shm_interface, 1); } else if (strcmp(interface, "xdg_wm_base") == 0) { - xdgShell = (xdg_wm_base*)wl_registry_bind(registry, name, - &xdg_wm_base_interface, 1); + self->xdgShell = (xdg_wm_base*)wl_registry_bind(registry, name, + &xdg_wm_base_interface, 2); } else if (strcmp(interface, "wl_data_device_manager") == 0) { - deviceManager = (wl_data_device_manager*)wl_registry_bind(registry, name, + self->deviceManager = (wl_data_device_manager*)wl_registry_bind(registry, name, &wl_data_device_manager_interface, 1); } else if (strcmp(interface, "wl_seat") == 0) { - seat = (wl_seat*)wl_registry_bind(registry, name, + self->seat = (wl_seat*)wl_registry_bind(registry, name, &wl_seat_interface, 1); } } @@ -152,61 +181,65 @@ void WindowManagerWayland::registryHandleGlobalRemove(void* data, wl_registry *r void WindowManagerWayland::pointerHandleEnter(void* data, wl_pointer* pointer, uint32_t serial, wl_surface *surface, wl_fixed_t surface_x, wl_fixed_t surface_y) { - wl_cursor_image* image = _cursors[static_cast(jwm::MouseCursor::ARROW)]; - wl_pointer_set_cursor(cursor, serial, cursorSurface, image->hotspot_x, image->hotspot_y); - focusedSurface = surface; + WindowManagerWayland* self = (WindowManagerWayland*)data; + wl_cursor_image* image = self->_cursors[static_cast(jwm::MouseCursor::ARROW)]; + wl_pointer_set_cursor(pointer, serial, self->cursorSurface, image->hotspot_x, image->hotspot_y); + self->focusedSurface = surface; } void WindowManagerWayland::pointerHandleLeave(void* data, wl_pointer* pointer, uint32_t serial, wl_surface *surface) { - focusedSurface = nullptr; + WindowManagerWayland* self = (WindowManagerWayland*)data; + self->focusedSurface = nullptr; // ??? - mouseMask = 0; + self->mouseMask = 0; } void WindowManagerWayland::pointerHandleMotion(void* data, wl_pointer* pointer, uint32_t time, wl_fixed_t surface_x, wl_fixed_t surface_y) { - lastMousePosX = surface_x; - lastMousePosY = surface_y; - if (focusedSurface) { - ::WindowWayland* window = _nativeWindowToMy.find(focusedSurface)->second; - mouseUpdate(window, wl_fixed_to_int(surface_x), wl_fixed_to_int(surface_y), mouseMask); + WindowManagerWayland* self = (WindowManagerWayland*)data; + self->lastMousePosX = surface_x; + self->lastMousePosY = surface_y; + if (self->focusedSurface) { + ::WindowWayland* window = self->_nativeWindowToMy.find(self->focusedSurface)->second; + self->mouseUpdate(window, wl_fixed_to_int(surface_x), wl_fixed_to_int(surface_y), self->mouseMask); } } void WindowManagerWayland::pointerHandleButton(void* data, wl_pointer* pointer, uint32_t serial, uint32_t time, uint32_t button, uint32_t state) { using namespace classes; + WindowManagerWayland* self = (WindowManagerWayland*)data; if (state == 0) { // release switch (button) { // primary case 0x110: - mouseMask &= ~0x100; + self->mouseMask &= ~0x100; break; // secondary case 0x111: - mouseMask &= ~0x400; + self->mouseMask &= ~0x400; break; // middle case 0x112: - mouseMask &= ~0x200; + self->mouseMask &= ~0x200; break; default: break; } - if (MouseButtonWayland::isButton(button) && focusedSurface) { + if (MouseButtonWayland::isButton(button) && self->focusedSurface) { jwm::JNILocal eventButton( app.getJniEnv(), EventMouseButton::make( app.getJniEnv(), MouseButtonWayland::fromNative(button), false, - lastMousePosX, - lastMousePosY, + self->lastMousePosX, + self->lastMousePosY, jwm::KeyWayland::getModifiers() ) ); - WindowWayland* window = _nativeWindowToMy.find(focusedSurface)->second; + WindowWayland* window = self->_nativeWindowToMy.find(self->focusedSurface)->second; window->dispatch(eventButton.get()); } } else { @@ -214,33 +247,33 @@ void WindowManagerWayland::pointerHandleButton(void* data, wl_pointer* pointer, switch (button) { // primary case 0x110: - mouseMask |= 0x100; + self->mouseMask |= 0x100; break; // secondary case 0x111: - mouseMask |= 0x400; + self->mouseMask |= 0x400; break; // middle case 0x112: - mouseMask |= 0x200; + self->mouseMask |= 0x200; break; default: break; } - if (MouseButtonWayland::isButton(button) && focusedSurface) { + if (MouseButtonWayland::isButton(button) && self->focusedSurface) { jwm::JNILocal eventButton( app.getJniEnv(), EventMouseButton::make( app.getJniEnv(), MouseButtonWayland::fromNative(button), true, - lastMousePosX, - lastMousePosY, + self->lastMousePosX, + self->lastMousePosY, jwm::KeyWayland::getModifiers() ) ); - WindowWayland* window = _nativeWindowToMy.find(focusedSurface)->second; + WindowWayland* window = self->_nativeWindowToMy.find(self->focusedSurface)->second; window->dispatch(eventButton.get()); } } @@ -412,3 +445,12 @@ void WindowManagerWayland::enqueueTask(const std::function& task) { _taskQueueNotify.notify_one(); notifyLoop(); } + +void WindowManagerWayland::notifyLoop() { + if (notifyFD==-1) return; + // fast notifyBool path to not make system calls when not necessary + if (!notifyBool.exchange(true)) { + char dummy[1] = {0}; + int unused = write(notifyFD, dummy, 1); // this really shouldn't fail, but if it does, the pipe should either be full (good), or dead (bad, but not our business) + } +} diff --git a/wayland/cc/WindowManagerWayland.hh b/wayland/cc/WindowManagerWayland.hh index 1dfe03ec..25cfed88 100644 --- a/wayland/cc/WindowManagerWayland.hh +++ b/wayland/cc/WindowManagerWayland.hh @@ -48,23 +48,25 @@ namespace jwm { return DefaultVisual(display, 0); } */ + void _processCallbacks(); + void notifyLoop(); void enqueueTask(const std::function& task); - void registryHandleGlobal(void* data, wl_registry *registry, + static void registryHandleGlobal(void* data, wl_registry *registry, uint32_t name, const char* interface, uint32_t version); - void registryHandleGlobalRemove(void* data, wl_registry *registry, + static void registryHandleGlobalRemove(void* data, wl_registry *registry, uint32_t name); - void pointerHandleEnter(void* data, wl_pointer *pointer, + static void pointerHandleEnter(void* data, wl_pointer *pointer, uint32_t serial, wl_surface *surface, wl_fixed_t surface_x, wl_fixed_t surface_y); - void pointerHandleLeave(void* data, wl_pointer *pointer, + static void pointerHandleLeave(void* data, wl_pointer *pointer, uint32_t serial, wl_surface* surface); - void pointerHandleMotion(void* data, wl_pointer *pointer, + static void pointerHandleMotion(void* data, wl_pointer *pointer, uint32_t time, wl_fixed_t surface_x, wl_fixed_t surface_y); - void pointerHandleButton(void* data, wl_pointer *pointer, + static void pointerHandleButton(void* data, wl_pointer *pointer, uint32_t serial, uint32_t time, uint32_t button, uint32_t state); - void pointerHandleAxis(void* data, wl_pointer *pointer, + static void pointerHandleAxis(void* data, wl_pointer *pointer, uint32_t time, uint32_t axis, wl_fixed_t value); @@ -72,8 +74,8 @@ namespace jwm { ByteBuf getClipboardContents(const std::string& type); std::vector getClipboardFormats(); - wl_display* display = nullptr - wl_registry* registry = nullptr + wl_display* display = nullptr; + wl_registry* registry = nullptr; wl_shm* shm = nullptr; xdg_wm_base* xdgShell = nullptr; wl_compositor* compositor = nullptr; diff --git a/wayland/cc/WindowWayland.cc b/wayland/cc/WindowWayland.cc index cc64c8cd..ecbd2a92 100644 --- a/wayland/cc/WindowWayland.cc +++ b/wayland/cc/WindowWayland.cc @@ -5,7 +5,6 @@ #include "AppWayland.hh" #include "impl/Library.hh" #include "impl/JNILocal.hh" -#include using namespace jwm; @@ -20,6 +19,7 @@ WindowWayland::~WindowWayland() { close(); } + void WindowWayland::setTitle(const std::string& title) { // impl me : ) } @@ -57,6 +57,11 @@ bool WindowWayland::isFullScreen() { void WindowWayland::getDecorations(int& left, int& top, int& right, int& bottom) { // impl me : ) + left = 0; + right = 0; + top = 0; + bottom = 0; + } void WindowWayland::getContentPosition(int& posX, int& posY) { @@ -86,40 +91,52 @@ int WindowWayland::getHeight() { } float WindowWayland::getScale() { - // TODO: use surface scaling - return jwm::app.getScale(); + return _scale; } bool WindowWayland::init() { _waylandWindow = wl_compositor_create_surface(_windowManager.compositor); - - xdgSurface = xdg_wm_base_get_xdg_surface(_windowManager.xdgShell, _waylandWindow); - xdgTopLevel = xdg_surface_get_toplevel(xdgSurface); + wl_surface_listener surfaceListener = { + .enter = WindowWayland::surfaceEnter, + .leave = WindowWayland::surfaceLeave, + .preferred_buffer_scale = WindowWayland::surfacePreferredBufferScale, + .preferred_buffer_transform = WindowWayland::surfacePreferredBufferTransform + }; + wl_surface_add_listener(_waylandWindow, &surfaceListener, this); + xdgSurface = xdg_wm_base_get_xdg_surface(_windowManager.xdgShell, _waylandWindow); + xdg_surface_listener xdgSurfaceListener = { + .configure = WindowWayland::xdgSurfaceConfigure + }; + xdg_surface_add_listener(xdgSurface, &xdgSurfaceListener, this); + + xdgToplevel = xdg_surface_get_toplevel(xdgSurface); + xdg_toplevel_listener xdgToplevelListener = { + .configure = WindowWayland::xdgToplevelConfigure, + .close = WindowWayland::xdgToplevelClose, + .configure_bounds = WindowWayland::xdgToplevelConfigureBounds, + .wm_capabilities = WindowWayland::xdgToplevelWmCapabilities + }; + xdg_toplevel_add_listener(xdgToplevel, &xdgToplevelListener, this); _windowManager.registerWindow(this); return true; } -void WindowWayland::move(int left, int top) { - // NO HAVING FUN! -} - -void WindowWayland::resize(int width, int height) { - // BOO! +// ??? +void WindowWayland::recreate() +{ + close(); + init(); } void WindowWayland::setVisible(bool isVisible) { if (_visible != isVisible) { _visible = isVisible; if (_visible) { - XMapWindow(_windowManager.getDisplay(), _x11Window); - if (_posX > 0 && _posY > 0) - move(_posX, _posY); - if (_width > 0 && _height > 0) - resize(_width, _height); + // impl me :troll: } else { - XUnmapWindow(_windowManager.getDisplay(), _x11Window); + // impl me :troll: } } } @@ -127,18 +144,84 @@ void WindowWayland::setVisible(bool isVisible) { void jwm::WindowWayland::setCursor(jwm::MouseCursor cursor) { if (auto wayCursor = _windowManager._cursors[static_cast(cursor)]) { wl_surface_attach(_windowManager.cursorSurface, - wl_cursor_image_get_buffer(wayCursor->images[0]), + wl_cursor_image_get_buffer(wayCursor), 0, 0); wl_surface_commit(_windowManager.cursorSurface); // TODO: hotspots? } else { - auto wayCursor = _windowManager.cursors[static_cast(jwm::MouseCursor::ARROW)]; + auto otherCursor = _windowManager._cursors[static_cast(jwm::MouseCursor::ARROW)]; wl_surface_attach(_windowManager.cursorSurface, - wl_cursor_image_get_buffer(wayCursor->images[0]), 0, 0); + wl_cursor_image_get_buffer(otherCursor), 0, 0); wl_surface_commit(_windowManager.cursorSurface); } } +// what do??? +void jwm::WindowWayland::surfaceEnter(void* data, wl_surface* surface, wl_output* output) {} +void jwm::WindowWayland::surfaceLeave(void* data, wl_surface* surface, wl_output* output) {} +void jwm::WindowWayland::surfacePreferredBufferScale(void* data, wl_surface* surface, int factor) { + WindowWayland* self = (WindowWayland*) data; + if (factor < 1) { + return; + } + self->_scale = factor; + // do I pinky promise here? + // yes : ) + if (self->_layer) { + self->_layer->resize(self->_width * factor, self->_height * factor); + } + wl_surface_set_buffer_scale(surface, factor); +} +void jwm::WindowWayland::surfacePreferredBufferTransform(void* data, wl_surface* surface, uint32_t transform) {} + +void jwm::WindowWayland::xdgSurfaceConfigure(void* data, xdg_surface* surface, uint32_t serial) { + WindowWayland* self = (WindowWayland*) data; + // Commit state + if (self->_newWidth > 0 || self->_newHeight > 0) { + int goodWidth = self->_width, goodHeight = self->_height; + if (self->_newWidth > 0) + goodWidth = self->_newWidth; + if (self->_newHeight > 0) + goodHeight = self->_newHeight; + self->_adaptSize(goodWidth, goodHeight); + } + self->_newWidth = -1; + self->_newHeight = -1; + xdg_surface_ack_configure(surface, serial); +} +void jwm::WindowWayland::xdgToplevelConfigure(void* data, xdg_toplevel* toplevel, int width, int height, wl_array* states) { + WindowWayland* self = (WindowWayland*) data; + if (width > 0) { + self->_newWidth = width; + } + if (height > 0) { + self->_newHeight = height; + } + // honestly idrc about the state +} +void jwm::WindowWayland::xdgToplevelClose(void* data, xdg_toplevel* toplevel) { + // ??? + // Request close EVENTUALLY:TM: +} +void jwm::WindowWayland::xdgToplevelConfigureBounds(void* data, xdg_toplevel* toplevel, int width, int height) { + WindowWayland* self = (WindowWayland*) data; + if (width > 0) { + self->_newWidth = width; + } + if (height > 0) { + self->_newHeight = height; + } +} +void jwm::WindowWayland::xdgToplevelWmCapabilities(void* data, xdg_toplevel* toplevel, wl_array* array) { + // impl me : ) +} +void jwm::WindowWayland::_adaptSize(int newWidth, int newHeight) { + _width = newWidth; + _height = newHeight; + if (_layer) { + _layer->resize(_width * _scale, _height * _scale); + } +} // JNI extern "C" JNIEXPORT jlong JNICALL Java_io_github_humbleui_jwm_WindowWayland__1nMake @@ -186,24 +269,6 @@ extern "C" JNIEXPORT jobject JNICALL Java_io_github_humbleui_jwm_WindowWayland__ ); } -extern "C" JNIEXPORT void JNICALL Java_io_github_humbleui_jwm_WindowWayland__1nSetWindowPosition - (JNIEnv* env, jobject obj, int left, int top) { - jwm::WindowWayland* instance = reinterpret_cast(jwm::classes::Native::fromJava(env, obj)); - instance->move(left, top); -} - -extern "C" JNIEXPORT void JNICALL Java_io_github_humbleui_jwm_WindowWayland__1nSetWindowSize - (JNIEnv* env, jobject obj, int width, int height) { - jwm::WindowWayland* instance = reinterpret_cast(jwm::classes::Native::fromJava(env, obj)); - // TODO https://github.com/HumbleUI/JWM/issues/109 - instance->resize(width, height); -} - -extern "C" JNIEXPORT void JNICALL Java_io_github_humbleui_jwm_WindowWayland__1nSetContentSize - (JNIEnv* env, jobject obj, int width, int height) { - jwm::WindowWayland* instance = reinterpret_cast(jwm::classes::Native::fromJava(env, obj)); - instance->resize(width, height); -} extern "C" JNIEXPORT void JNICALL Java_io_github_humbleui_jwm_WindowWayland__1nRequestFrame (JNIEnv* env, jobject obj) { diff --git a/wayland/cc/WindowWayland.hh b/wayland/cc/WindowWayland.hh index 21fa1867..c67e9add 100644 --- a/wayland/cc/WindowWayland.hh +++ b/wayland/cc/WindowWayland.hh @@ -7,6 +7,7 @@ #include #include "ScreenInfo.hh" #include "xdg-shell.hh" +#include namespace jwm { class WindowWayland: public jwm::Window { public: @@ -18,13 +19,12 @@ namespace jwm { void setVisible(bool isVisible); void close(); bool init(); + void recreate(); int getLeft(); int getTop(); int getWidth(); int getHeight(); float getScale(); - void move(int left, int top); - void resize(int width, int height); void requestRedraw() { _isRedrawRequested = true; _windowManager.notifyLoop(); @@ -45,33 +45,41 @@ namespace jwm { void setFullScreen(bool isFullScreen); bool isFullScreen(); - XIC getIC() const { - return _ic; - } void setCursor(jwm::MouseCursor cursor); void setLayer(ILayer* layer) { _layer = layer; } - void _xSendEventToWM(Atom atom, long a, long b, long c, long d, long e) const; - unsigned long _xGetWindowProperty(Atom property, Atom type, unsigned char** value) const; + const ScreenInfo& getScreen(); - /** - * _NET_WM_SYNC_REQUEST (resize flicker fix) update request counter - */ - struct { - uint32_t lo = 0; - uint32_t hi = 0; - XID counter; - } _xsyncRequestCounter; + static void surfaceEnter(void* data, wl_surface* surface, wl_output* output); + static void surfaceLeave(void* data, wl_surface* surface, wl_output* output); + static void surfacePreferredBufferScale(void* data, wl_surface* surface, int factor); + static void surfacePreferredBufferTransform(void* data, wl_surface* surface, uint32_t transform); + + static void xdgSurfaceConfigure(void* data, xdg_surface* surface, uint32_t serial); + + static void xdgToplevelConfigure(void* data, xdg_toplevel* toplevel, int width, int height, wl_array* states); + static void xdgToplevelClose(void* data, xdg_toplevel* toplevel); + static void xdgToplevelConfigureBounds(void* data, xdg_toplevel* toplevel, int width, int height); + static void xdgToplevelWmCapabilities(void* data, xdg_toplevel* toplevel, wl_array* capabilities); + + void _adaptSize(int newWidth, int newHeight); + int _posX = -1; int _posY = -1; int _width = -1; + int _newWidth = -1; + int _scale = 1; int _height = -1; + int _newHeight = -1; int _WM_ADD = 1L; int _WM_REMOVE = 0L; + bool _canMinimize = false; + bool _canMaximize = false; + bool _canFullscreen = false; bool _visible = false; bool _isRedrawRequested = false; @@ -81,5 +89,6 @@ namespace jwm { wl_surface* _waylandWindow = nullptr; xdg_surface* xdgSurface = nullptr; xdg_toplevel* xdgToplevel = nullptr; + libdecor_frame* _frame = nullptr; }; } diff --git a/wayland/java/WindowWayland.java b/wayland/java/WindowWayland.java index 53832795..056f7f45 100644 --- a/wayland/java/WindowWayland.java +++ b/wayland/java/WindowWayland.java @@ -45,16 +45,14 @@ public IRect getContentRect() { @Override public Window setWindowPosition(int left, int top) { assert _onUIThread() : "Should be run on UI thread"; - // _nSetWindowPosition(left, top); - // Unsupported under wayland rn + // no : ) return this; } @Override public Window setWindowSize(int width, int height) { assert _onUIThread() : "Should be run on UI thread"; - // _nSetWindowSize(width, height); - // Possibly unsupported? Hinting is possibly supported + // no : ) return this; } @@ -120,7 +118,7 @@ public float getOpacity(){ @Override public Screen getScreen() { assert _onUIThread() : "Should be run on UI thread"; - return new Screen(0, false, IRect.makeXYWH(0, 0, 0, 0), IRect.makeXYWH(0, 0, 0, 0), 1); + return new Screen(0, false, IRect.makeXYWH(0, 0, 0, 0), IRect.makeXYWH(0, 0, 0, 0), getScale()); } @Override diff --git a/x11/CMakeLists.txt b/x11/CMakeLists.txt index 2157bd4b..36bfff99 100644 --- a/x11/CMakeLists.txt +++ b/x11/CMakeLists.txt @@ -19,8 +19,8 @@ find_package(X11 REQUIRED) find_package(OpenGL REQUIRED) file(GLOB SOURCES_CXX ${CMAKE_CURRENT_LIST_DIR}/../shared/cc/*.cc - ${CMAKE_CURRENT_LIST_DIR}/cc/*.cc - ${CMAKE_CURRENT_LIST_DIR}/../linux/cc/*.cc) + ${CMAKE_CURRENT_LIST_DIR}/../linux/cc/*.cc + ${CMAKE_CURRENT_LIST_DIR}/cc/*.cc) file(GLOB SOURCES_CXX_IMPL ${CMAKE_CURRENT_LIST_DIR}/../shared/cc/impl/*.cc) add_library(jwm SHARED ${SOURCES_OBJC} ${SOURCES_CXX} ${SOURCES_CXX_IMPL}) diff --git a/x11/cc/KeyX11.cc b/x11/cc/KeyX11.cc index 9a638c69..30045e30 100644 --- a/x11/cc/KeyX11.cc +++ b/x11/cc/KeyX11.cc @@ -175,4 +175,4 @@ jwm::Key jwm::KeyX11::fromNative(uint32_t v) { // Key::MUTE default: return Key::UNDEFINED; } -} \ No newline at end of file +} diff --git a/x11/cc/LayerGLX11.cc b/x11/cc/LayerGLX11.cc index 4beb64eb..1b4b8e02 100644 --- a/x11/cc/LayerGLX11.cc +++ b/x11/cc/LayerGLX11.cc @@ -49,7 +49,7 @@ namespace jwm { } } - void resize(int width, int height) { + void resize(int width, int height) override { glClearStencil(0); glClearColor(0, 0, 0, 255); glStencilMask(0xffffffff); @@ -120,4 +120,4 @@ extern "C" JNIEXPORT void JNICALL Java_io_github_humbleui_jwm_LayerGL__1nClose (JNIEnv* env, jobject obj) { jwm::LayerGL* instance = reinterpret_cast(jwm::classes::Native::fromJava(env, obj)); instance->close(); -} \ No newline at end of file +} diff --git a/x11/cc/LayerRasterX11.cc b/x11/cc/LayerRasterX11.cc index 2ec70c85..cc22ea88 100644 --- a/x11/cc/LayerRasterX11.cc +++ b/x11/cc/LayerRasterX11.cc @@ -30,7 +30,7 @@ namespace jwm { _graphicsContext = DefaultGC(d, DefaultScreen(d)); } - void resize(int width, int height) { + void resize(int width, int height) override { Display* d = fWindow->_windowManager.getDisplay(); _width = width; _height = height; @@ -120,4 +120,4 @@ extern "C" JNIEXPORT jlong JNICALL Java_io_github_humbleui_jwm_LayerRaster__1nGe (JNIEnv* env, jobject obj) { jwm::LayerRaster* instance = reinterpret_cast(jwm::classes::Native::fromJava(env, obj)); return static_cast(instance->getRowBytes()); -} \ No newline at end of file +} diff --git a/x11/java/WindowX11.java b/x11/java/WindowX11.java index bc865777..89d584c9 100644 --- a/x11/java/WindowX11.java +++ b/x11/java/WindowX11.java @@ -213,7 +213,8 @@ public boolean isFullScreen() { @Override public float getScale() { - return ; + assert _onUIThread() : "Should be run on UI thread"; + return _nGetScale(); } @ApiStatus.Internal public static native long _nMake(); From 4378e8c8492e4fc39d17dee9ba7be73d09cc25b2 Mon Sep 17 00:00:00 2001 From: BulbyVR <26726264+TheDrawingCoder-Gamer@users.noreply.github.com> Date: Sun, 3 Dec 2023 11:44:52 -0500 Subject: [PATCH 04/95] It's trying, at least --- examples/dashboard/java/Example.java | 7 ++++--- examples/dashboard/java/PanelRendering.java | 2 ++ linux/cc/ILayer.hh | 2 +- script/common.py | 4 ++-- script/run.py | 3 ++- shared/java/App.java | 2 ++ shared/java/impl/Library.java | 4 ++-- wayland/CMakeLists.txt | 7 +++++++ wayland/cc/LayerGLWayland.cc | 4 +++- wayland/cc/WindowManagerWayland.cc | 6 +++--- wayland/cc/WindowWayland.cc | 2 ++ wayland/cc/{xdg-shell.cc => xdg-shell.c} | 1 - wayland/java/WindowWayland.java | 4 ++-- 13 files changed, 32 insertions(+), 16 deletions(-) rename wayland/cc/{xdg-shell.cc => xdg-shell.c} (99%) diff --git a/examples/dashboard/java/Example.java b/examples/dashboard/java/Example.java index f8a2ee20..3df7cac5 100644 --- a/examples/dashboard/java/Example.java +++ b/examples/dashboard/java/Example.java @@ -53,10 +53,11 @@ public Example() { panelTheme = new PanelTheme(window); panelTouch = new PanelTouch(window); - var scale = window.getScreen().getScale(); + var scale = window.getScale(); int count = App._windows.size() - 1; - Screen screen = App.getScreens()[(count / 5) % App.getScreens().length]; - IRect bounds = screen.getWorkArea(); + // Screen screen = App.getScreens()[(count / 5) % App.getScreens().length]; + // IRect bounds = screen.getWorkArea(); + IRect bounds = new IRect(0, 0, 100, 100); window.setTitle("JWM Window #" + count); if (window instanceof WindowMac windowMac) { diff --git a/examples/dashboard/java/PanelRendering.java b/examples/dashboard/java/PanelRendering.java index 5ab882b4..4d215ccf 100644 --- a/examples/dashboard/java/PanelRendering.java +++ b/examples/dashboard/java/PanelRendering.java @@ -34,6 +34,8 @@ else if (Platform.CURRENT == Platform.WINDOWS) layers = new String[] { "LayerD3D12Skija", "LayerGLSkija", "SkijaLayerRaster" }; else if (Platform.CURRENT == Platform.X11) layers = new String[] { "LayerGLSkija", "LayerRasterSkija" }; + else if (Platform.CURRENT == Platform.WAYLAND) + layers = new String[] { "LayerGLSkija", "LayerRasterSkija" }; for (var layerName: layers) layersStatus.put(layerName, UNKNOWN); diff --git a/linux/cc/ILayer.hh b/linux/cc/ILayer.hh index f21346a4..5b8ee030 100644 --- a/linux/cc/ILayer.hh +++ b/linux/cc/ILayer.hh @@ -15,7 +15,7 @@ public: virtual void makeCurrentForced(); virtual void setVsyncMode(VSync v) = 0; virtual void close() = 0; - virtual void resize(int width, int height); + virtual void resize(int width, int height) = 0; static ILayer* _ourCurrentLayer; diff --git a/script/common.py b/script/common.py index a95ad202..36a5f31a 100644 --- a/script/common.py +++ b/script/common.py @@ -12,7 +12,7 @@ def deps_compile(): (args, _) = parser.parse_known_args() deps = [ - build_utils.fetch_maven('org.projectlombok', 'lombok', '1.18.22'), + build_utils.fetch_maven('org.projectlombok', 'lombok', '1.18.30'), build_utils.fetch_maven('org.jetbrains', 'annotations', '20.1.0') ] @@ -38,4 +38,4 @@ def deps_compile(): return deps -version = build_utils.get_arg("version") or build_utils.parse_ref() or build_utils.parse_sha() or "0.0.0-SNAPSHOT" \ No newline at end of file +version = build_utils.get_arg("version") or build_utils.parse_ref() or build_utils.parse_sha() or "0.0.0-SNAPSHOT" diff --git a/script/run.py b/script/run.py index 98f3c0ee..e90cbe7e 100755 --- a/script/run.py +++ b/script/run.py @@ -10,9 +10,10 @@ def main(): parser.add_argument('--skija-shared-jar', default=None) parser.add_argument('--skija-platform-jar', default=None) parser.add_argument('--types-dir', default=None) + parser.add_argument('--just-run', action='store_true') args = parser.parse_args() - if not args.jwm_version: + if not args.jwm_version and not args.just_run: build.main() if args.skija_dir: diff --git a/shared/java/App.java b/shared/java/App.java index 268ddc60..7a1c5a76 100644 --- a/shared/java/App.java +++ b/shared/java/App.java @@ -52,6 +52,8 @@ else if (Platform.CURRENT == Platform.MACOS) window = new WindowMac(); else if (Platform.CURRENT == Platform.X11) window = new WindowX11(); + else if (Platform.CURRENT == Platform.WAYLAND) + window = new WindowWayland(); else throw new RuntimeException("Unsupported platform: " + Platform.CURRENT); _windows.add(window); diff --git a/shared/java/impl/Library.java b/shared/java/impl/Library.java index 43fd5cbc..29a5da5d 100644 --- a/shared/java/impl/Library.java +++ b/shared/java/impl/Library.java @@ -46,10 +46,10 @@ public static synchronized void load() { } else if (Platform.CURRENT == Platform.X11) { File library = _extract("/", "libjwm_x64.so", tempDir); System.load(library.getAbsolutePath()); - } /*else if (Platform.CURRENT == Platform.WAYLAND) { + } else if (Platform.CURRENT == Platform.WAYLAND) { File library = _extract("/", "libjwm_x64_wayland.so", tempDir); System.load(library.getAbsolutePath()); - }*/ + } if (tempDir.exists() && version == null) { Runtime.getRuntime().addShutdownHook(new Thread(() -> { diff --git a/wayland/CMakeLists.txt b/wayland/CMakeLists.txt index 95ca0ad2..074e87d6 100644 --- a/wayland/CMakeLists.txt +++ b/wayland/CMakeLists.txt @@ -4,6 +4,7 @@ cmake_minimum_required(VERSION 3.9) cmake_policy(SET CMP0072 NEW) project(jwm LANGUAGES CXX) +project(xdgShell LANGUAGES C) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) @@ -19,6 +20,8 @@ file(GLOB SOURCES_CXX ${CMAKE_CURRENT_LIST_DIR}/../shared/cc/*.cc ${CMAKE_CURRENT_LIST_DIR}/../linux/cc/*.cc ${CMAKE_CURRENT_LIST_DIR}/cc/*.cc ) file(GLOB SOURCES_CXX_IMPL ${CMAKE_CURRENT_LIST_DIR}/../shared/cc/impl/*.cc) +file(GLOB SOURCES_C ${CMAKE_CURRENT_LIST_DIR}/cc/*.c) +add_library(xdgShell STATIC ${SOURCES_C}) add_library(jwm SHARED ${SOURCES_OBJC} ${SOURCES_CXX} ${SOURCES_CXX_IMPL}) find_library(WAYLAND_CLIENT_LIB wayland-client) find_library(DECOR_LIB decor-0) @@ -26,6 +29,7 @@ find_library(WAYLAND_CURSOR wayland-cursor) find_library(XKBCOMMON xkbcommon) find_library(EGL EGL) find_library(WAYLAND_EGL wayland-egl) +find_package(OpenGL REQUIRED) set(JAVA_HOME $ENV{JAVA_HOME}) if (NOT JAVA_HOME) @@ -41,6 +45,9 @@ endif() target_include_directories(jwm PRIVATE ${CMAKE_CURRENT_LIST_DIR}/../shared/cc ${CMAKE_CURRENT_LIST_DIR}/../linux/cc ${JAVA_HOME}/include ${JAVA_HOME}/include/linux) set_target_properties(jwm PROPERTIES OUTPUT_NAME "jwm_${JWM_ARCH}_wayland") +target_link_libraries(jwm PUBLIC xdgShell) target_link_libraries(jwm PRIVATE ${WAYLAND_CLIENT_LIB} ${DECOR_LIB} ${WAYLAND_CURSOR} ${XKBCOMMON}) target_link_libraries(jwm PRIVATE ${EGL} ${WAYLAND_EGL}) +target_link_libraries(jwm PRIVATE OpenGL::GL) + diff --git a/wayland/cc/LayerGLWayland.cc b/wayland/cc/LayerGLWayland.cc index 8e3f2ee2..e8f8cbcb 100644 --- a/wayland/cc/LayerGLWayland.cc +++ b/wayland/cc/LayerGLWayland.cc @@ -72,7 +72,9 @@ namespace jwm { // ??? // glViewport(0, 0, width, height); - wl_egl_window_resize(_eglWindow, width, height, 0, 0); + // God is dead if _eglWindow is null + if (_eglWindow) + wl_egl_window_resize(_eglWindow, width, height, 0, 0); } void swapBuffers() { diff --git a/wayland/cc/WindowManagerWayland.cc b/wayland/cc/WindowManagerWayland.cc index 2c629633..5789cb24 100644 --- a/wayland/cc/WindowManagerWayland.cc +++ b/wayland/cc/WindowManagerWayland.cc @@ -75,7 +75,7 @@ WindowManagerWayland::WindowManagerWayland(): .leave = WindowManagerWayland::pointerHandleLeave, .motion = WindowManagerWayland::pointerHandleMotion, .button = WindowManagerWayland::pointerHandleButton, - .axis = WindowManagerWayland::pointerHandleAxis + //.axis = WindowManagerWayland::pointerHandleAxis }; wl_pointer_add_listener(pointer, &pointerListener, this); } @@ -112,7 +112,7 @@ void WindowManagerWayland::runLoop() { } if (myPoll.revents & POLLIN) { - while (read(pipes[0], buf, sizeof(buf) == sizeof(buf))) {} + while (read(pipes[0], buf, sizeof(buf)) == sizeof(buf)) {} } notifyBool.store(false); } @@ -166,7 +166,7 @@ void WindowManagerWayland::registryHandleGlobal(void* data, wl_registry *registr &wl_shm_interface, 1); } else if (strcmp(interface, "xdg_wm_base") == 0) { self->xdgShell = (xdg_wm_base*)wl_registry_bind(registry, name, - &xdg_wm_base_interface, 2); + &xdg_wm_base_interface, 1); } else if (strcmp(interface, "wl_data_device_manager") == 0) { self->deviceManager = (wl_data_device_manager*)wl_registry_bind(registry, name, &wl_data_device_manager_interface, 1); diff --git a/wayland/cc/WindowWayland.cc b/wayland/cc/WindowWayland.cc index ecbd2a92..c28d52fc 100644 --- a/wayland/cc/WindowWayland.cc +++ b/wayland/cc/WindowWayland.cc @@ -100,8 +100,10 @@ bool WindowWayland::init() wl_surface_listener surfaceListener = { .enter = WindowWayland::surfaceEnter, .leave = WindowWayland::surfaceLeave, + #ifdef HAVE_WAYLAND_1_22 .preferred_buffer_scale = WindowWayland::surfacePreferredBufferScale, .preferred_buffer_transform = WindowWayland::surfacePreferredBufferTransform + #endif }; wl_surface_add_listener(_waylandWindow, &surfaceListener, this); diff --git a/wayland/cc/xdg-shell.cc b/wayland/cc/xdg-shell.c similarity index 99% rename from wayland/cc/xdg-shell.cc rename to wayland/cc/xdg-shell.c index 03826cdc..747c222a 100644 --- a/wayland/cc/xdg-shell.cc +++ b/wayland/cc/xdg-shell.c @@ -180,4 +180,3 @@ WL_PRIVATE const struct wl_interface xdg_popup_interface = { 3, xdg_popup_requests, 3, xdg_popup_events, }; - diff --git a/wayland/java/WindowWayland.java b/wayland/java/WindowWayland.java index 056f7f45..5c09dd50 100644 --- a/wayland/java/WindowWayland.java +++ b/wayland/java/WindowWayland.java @@ -32,14 +32,14 @@ public void unmarkText() { public IRect getWindowRect() { assert _onUIThread() : "Should be run on UI thread"; // very very bad! - return IRect.makeXYWH(0, 0, 0, 0); + return IRect.makeXYWH(0, 0, 100, 100); } @Override public IRect getContentRect() { assert _onUIThread() : "Should be run on UI thread"; // stop! - return IRect.makeXYWH(0, 0, 0, 0); + return IRect.makeXYWH(0, 0, 100, 100); } @Override From ff1d84a219262059b457c723f2aac80fc932b0ec Mon Sep 17 00:00:00 2001 From: BulbyVR <26726264+TheDrawingCoder-Gamer@users.noreply.github.com> Date: Mon, 4 Dec 2023 09:33:52 -0500 Subject: [PATCH 05/95] Try work on event loop? --- wayland/cc/LayerGLWayland.cc | 5 +-- wayland/cc/LayerRasterWayland.cc | 29 +++++++++++++---- wayland/cc/WindowManagerWayland.cc | 38 ++++++++++++++++------ wayland/cc/WindowManagerWayland.hh | 1 + wayland/cc/WindowWayland.cc | 51 ++++++++++++++++++++++++++---- wayland/cc/WindowWayland.hh | 10 ++++++ 6 files changed, 110 insertions(+), 24 deletions(-) diff --git a/wayland/cc/LayerGLWayland.cc b/wayland/cc/LayerGLWayland.cc index e8f8cbcb..c2d07114 100644 --- a/wayland/cc/LayerGLWayland.cc +++ b/wayland/cc/LayerGLWayland.cc @@ -22,6 +22,7 @@ namespace jwm { virtual ~LayerGL() = default; void attach(WindowWayland* window) { + eglBindAPI(EGL_OPENGL_API); fWindow = jwm::ref(window); if (window->_layer) { // HACK: close window and reopen @@ -52,7 +53,7 @@ namespace jwm { EGL_NO_CONTEXT, nullptr); _eglWindow = wl_egl_window_create(window->_waylandWindow, window->getWidth(), window->getHeight()); - + // TODO: closed windows? _surface = eglCreatePlatformWindowSurface(_display, config, _eglWindow, nullptr); } @@ -71,7 +72,7 @@ namespace jwm { glClear(GL_STENCIL_BUFFER_BIT | GL_COLOR_BUFFER_BIT); // ??? - // glViewport(0, 0, width, height); + glViewport(0, 0, width, height); // God is dead if _eglWindow is null if (_eglWindow) wl_egl_window_resize(_eglWindow, width, height, 0, 0); diff --git a/wayland/cc/LayerRasterWayland.cc b/wayland/cc/LayerRasterWayland.cc index 0462940e..9bec31a2 100644 --- a/wayland/cc/LayerRasterWayland.cc +++ b/wayland/cc/LayerRasterWayland.cc @@ -23,22 +23,35 @@ namespace jwm { void attach(WindowWayland* window) { fWindow = jwm::ref(window); fWindow->setLayer(this); + // a default size : ) + resize(100, 100); } void resize(int width, int height) override { wl_display* d = fWindow->_windowManager.display; _width = width; _height = height; - int bufSize = width * height * sizeof(uint32_t) * 2; - if (!_pool) { - _pool = new ShmPool(fWindow->_windowManager.shm, bufSize); + int stride = width * sizeof(uint32_t); + int bufSize = stride * height * 2; + // TODO: better pool impl + if (_pool) { + delete _pool; } + _pool = new ShmPool(fWindow->_windowManager.shm, bufSize); + if (_buffer) { + wl_buffer_destroy(_buffer); + _imageData = nullptr; + } + _pool->grow(bufSize); // LSBFirst means Little endian : ) - auto buf = _pool->createBuffer(0, width, height, width * sizeof(uint32_t), WL_SHM_FORMAT_ABGR8888); - + // This will highly likely cause issues : ) + auto buf = _pool->createBuffer(0, width, height, stride, WL_SHM_FORMAT_ABGR8888); + _buffer = buf.first; _imageData = buf.second; + + } const void* getPixelsPtr() const { @@ -52,7 +65,8 @@ namespace jwm { void swapBuffers() { // : ) - wl_surface_damage_buffer(fWindow->_waylandWindow, 0, 0, UINT32_MAX, UINT32_MAX); + if (fWindow->_waylandWindow) + wl_surface_damage_buffer(fWindow->_waylandWindow, 0, 0, UINT32_MAX, UINT32_MAX); } void close() override { @@ -61,7 +75,7 @@ namespace jwm { _buffer = nullptr; } // ??? - // destroy _pool; + delete _pool; jwm::unref(&fWindow); } @@ -70,6 +84,7 @@ namespace jwm { } void setVsyncMode(VSync v) override { + // srsly, why do I need this _vsync = v; } }; diff --git a/wayland/cc/WindowManagerWayland.cc b/wayland/cc/WindowManagerWayland.cc index 5789cb24..a7f9e5c2 100644 --- a/wayland/cc/WindowManagerWayland.cc +++ b/wayland/cc/WindowManagerWayland.cc @@ -32,14 +32,20 @@ WindowManagerWayland::WindowManagerWayland(): wl_display_roundtrip(display); + + if (!(shm && xdgShell && compositor && deviceManager && seat)) { // ??? // Bad. Means our compositor no supportie : ( throw std::system_error(ENOTSUP, std::generic_category(), "Unsupported compositor"); } - - - + + xdg_wm_base_listener xdgListener = { + .ping = WindowManagerWayland::xdgWmBasePing + }; + // frankly `this` is not needed here, but it needs a pointer anyway and it's + // good to have consistentcy. + xdg_wm_base_add_listener(xdgShell, &xdgListener, this); { wl_cursor_theme* cursor_theme = wl_cursor_theme_load(nullptr, 24, shm); // TODO: what about if missing : ( @@ -98,22 +104,29 @@ void WindowManagerWayland::runLoop() { } notifyFD = pipes[1]; fcntl(pipes[1], F_SETFL, O_NONBLOCK); // notifyLoop no blockie : ) - struct pollfd myPoll = {.fd=pipes[0], .events=POLLIN}; + struct pollfd ps[] = {{.fd=wl_display_get_fd(display), .events=POLLIN}, {.fd=pipes[0], .events=POLLIN}}; // who be out here running they loop while (_runLoop) { + printf("gi huys\n"); + wl_display_flush(display); if (jwm::classes::Throwable::exceptionThrown(app.getJniEnv())) _runLoop = false; + printf("try guys\n"); _processCallbacks(); // block until event : ) - if (poll(&myPoll, 1, -1) < 0) { + if (poll(&ps[0], 2, -1) < 0) { printf("error with pipe\n"); break; } - - if (myPoll.revents & POLLIN) { - while (read(pipes[0], buf, sizeof(buf)) == sizeof(buf)) {} + printf("why guys\n"); + if (ps[1].revents & POLLIN) { + while (read(pipes[0], buf, sizeof(buf)) == sizeof(buf)) { } + } + if (ps[0].revents & POLLIN) { + wl_display_dispatch(display); } + printf("hi guys? %i\n", _runLoop); notifyBool.store(false); } @@ -159,8 +172,9 @@ void WindowManagerWayland::registryHandleGlobal(void* data, wl_registry *registr uint32_t name, const char* interface, uint32_t version) { WindowManagerWayland* self = (WindowManagerWayland*)data; if (strcmp(interface, "wl_compositor") == 0) { + // EGL apparently requires at least a version of 4 here : ) self->compositor = (wl_compositor*)wl_registry_bind(registry, name, - &wl_compositor_interface, 3); + &wl_compositor_interface, 4); } else if (strcmp(interface, "wl_shm") == 0) { self->shm = (wl_shm*)wl_registry_bind(registry, name, &wl_shm_interface, 1); @@ -278,6 +292,9 @@ void WindowManagerWayland::pointerHandleButton(void* data, wl_pointer* pointer, } } } +void WindowManagerWayland::xdgWmBasePing(void* data, xdg_wm_base* base, uint32_t serial) { + xdg_wm_base_pong(base, serial); +} std::vector WindowManagerWayland::getClipboardFormats() { /* XConvertSelection(display, @@ -447,10 +464,13 @@ void WindowManagerWayland::enqueueTask(const std::function& task) { } void WindowManagerWayland::notifyLoop() { + // maybe just do nothing? + /* if (notifyFD==-1) return; // fast notifyBool path to not make system calls when not necessary if (!notifyBool.exchange(true)) { char dummy[1] = {0}; int unused = write(notifyFD, dummy, 1); // this really shouldn't fail, but if it does, the pipe should either be full (good), or dead (bad, but not our business) } + */ } diff --git a/wayland/cc/WindowManagerWayland.hh b/wayland/cc/WindowManagerWayland.hh index 25cfed88..dae2c083 100644 --- a/wayland/cc/WindowManagerWayland.hh +++ b/wayland/cc/WindowManagerWayland.hh @@ -69,6 +69,7 @@ namespace jwm { static void pointerHandleAxis(void* data, wl_pointer *pointer, uint32_t time, uint32_t axis, wl_fixed_t value); + static void xdgWmBasePing(void* data, xdg_wm_base* base, uint32_t serial); ByteBuf getClipboardContents(const std::string& type); diff --git a/wayland/cc/WindowWayland.cc b/wayland/cc/WindowWayland.cc index c28d52fc..911592b6 100644 --- a/wayland/cc/WindowWayland.cc +++ b/wayland/cc/WindowWayland.cc @@ -28,11 +28,22 @@ void WindowWayland::setTitlebarVisible(bool isVisible) { // impl me : ) } +// Closing is like... the exact same as being visible. WTH void WindowWayland::close() { if (_waylandWindow) { _windowManager.unregisterWindow(this); wl_surface_destroy(_waylandWindow); } + _waylandWindow = nullptr; + if (xdgSurface) { + xdg_surface_destroy(xdgSurface); + } + xdgSurface = nullptr; + if (xdgToplevel) { + xdg_toplevel_destroy(xdgToplevel); + } + xdgToplevel = nullptr; + } void WindowWayland::maximize() { // impl me :) @@ -93,8 +104,10 @@ int WindowWayland::getHeight() { float WindowWayland::getScale() { return _scale; } - -bool WindowWayland::init() +bool WindowWayland::init() { + return true; +} +void WindowWayland::show() { _waylandWindow = wl_compositor_create_surface(_windowManager.compositor); wl_surface_listener surfaceListener = { @@ -122,7 +135,6 @@ bool WindowWayland::init() }; xdg_toplevel_add_listener(xdgToplevel, &xdgToplevelListener, this); _windowManager.registerWindow(this); - return true; } // ??? @@ -136,9 +148,9 @@ void WindowWayland::setVisible(bool isVisible) { if (_visible != isVisible) { _visible = isVisible; if (_visible) { - // impl me :troll: + show(); } else { - // impl me :troll: + close(); } } } @@ -159,7 +171,17 @@ void jwm::WindowWayland::setCursor(jwm::MouseCursor cursor) { } // what do??? -void jwm::WindowWayland::surfaceEnter(void* data, wl_surface* surface, wl_output* output) {} +void jwm::WindowWayland::surfaceEnter(void* data, wl_surface* surface, wl_output* output) { + wl_output_listener listener = { + .geometry = WindowWayland::outputGeometry, + .mode = WindowWayland::outputMode, + .done = WindowWayland::outputDone, + .scale = WindowWayland::outputScale, + .name = WindowWayland::outputName, + .description = WindowWayland::outputDescription + }; + wl_output_add_listener(output, &listener, data); +} void jwm::WindowWayland::surfaceLeave(void* data, wl_surface* surface, wl_output* output) {} void jwm::WindowWayland::surfacePreferredBufferScale(void* data, wl_surface* surface, int factor) { WindowWayland* self = (WindowWayland*) data; @@ -217,6 +239,23 @@ void jwm::WindowWayland::xdgToplevelConfigureBounds(void* data, xdg_toplevel* to void jwm::WindowWayland::xdgToplevelWmCapabilities(void* data, xdg_toplevel* toplevel, wl_array* array) { // impl me : ) } +void jwm::WindowWayland::outputGeometry(void* data, wl_output* output, int x, int y, int pWidth, int pHeight, + int subpixel, const char* make, const char* model, int transform) {} +void jwm::WindowWayland::outputMode(void* data, wl_output* output, uint32_t flags, int width, int height, + int refresh) {} +void jwm::WindowWayland::outputDone(void* data, wl_output* output) {} +void jwm::WindowWayland::outputScale(void* data, wl_output* output, int factor) { + WindowWayland* self = reinterpret_cast(data); + self->_scale = factor; + if (self->_layer) { + self->_layer->resize(self->_width * factor, self->_height * factor); + if (self->_waylandWindow) { + wl_surface_set_buffer_scale(self->_waylandWindow, factor); + } + } +} +void jwm::WindowWayland::outputName(void* data, wl_output* output, const char* name) {} +void jwm::WindowWayland::outputDescription(void* data, wl_output* output, const char* desc) {} void jwm::WindowWayland::_adaptSize(int newWidth, int newHeight) { _width = newWidth; _height = newHeight; diff --git a/wayland/cc/WindowWayland.hh b/wayland/cc/WindowWayland.hh index c67e9add..9658dfc7 100644 --- a/wayland/cc/WindowWayland.hh +++ b/wayland/cc/WindowWayland.hh @@ -19,6 +19,7 @@ namespace jwm { void setVisible(bool isVisible); void close(); bool init(); + void show(); void recreate(); int getLeft(); int getTop(); @@ -65,6 +66,15 @@ namespace jwm { static void xdgToplevelConfigureBounds(void* data, xdg_toplevel* toplevel, int width, int height); static void xdgToplevelWmCapabilities(void* data, xdg_toplevel* toplevel, wl_array* capabilities); + static void outputGeometry(void* data, wl_output* output, int x, int y, int physWidth, int physHeight, + int subpixelOrient, const char* make, const char* model, int transform); + static void outputMode(void* data, wl_output* output, uint32_t flags, int width, int height, int refresh); + static void outputDone(void* data, wl_output* output); + // YEAH THAT'S WHAT I'VE BEEN WAITING FOR + static void outputScale(void* data, wl_output* output, int factor); + static void outputName(void* data, wl_output* output, const char* name); + static void outputDescription(void* data, wl_output* output, const char* desc); + void _adaptSize(int newWidth, int newHeight); From 26823f21fde8e791bc9beae9f62afbdf45fb02b1 Mon Sep 17 00:00:00 2001 From: BulbyVR <26726264+TheDrawingCoder-Gamer@users.noreply.github.com> Date: Mon, 4 Dec 2023 10:28:25 -0500 Subject: [PATCH 06/95] Try attach buffer (xdg didn't like that) --- wayland/cc/ILayerWayland.hh | 10 ++++++++++ wayland/cc/LayerGLWayland.cc | 20 +++++++++++++------- wayland/cc/LayerRasterWayland.cc | 17 +++++++++++++---- wayland/cc/WindowManagerWayland.cc | 6 ------ wayland/cc/WindowWayland.cc | 5 +++++ wayland/cc/WindowWayland.hh | 8 +++++--- 6 files changed, 46 insertions(+), 20 deletions(-) create mode 100644 wayland/cc/ILayerWayland.hh diff --git a/wayland/cc/ILayerWayland.hh b/wayland/cc/ILayerWayland.hh new file mode 100644 index 00000000..45a0dafa --- /dev/null +++ b/wayland/cc/ILayerWayland.hh @@ -0,0 +1,10 @@ +#pragma once + +#include + +namespace jwm { + class ILayerWayland: public ILayer { + public: + virtual void attachBuffer() = 0; + }; +} diff --git a/wayland/cc/LayerGLWayland.cc b/wayland/cc/LayerGLWayland.cc index c2d07114..bf0e6ab9 100644 --- a/wayland/cc/LayerGLWayland.cc +++ b/wayland/cc/LayerGLWayland.cc @@ -7,16 +7,18 @@ #include #include #include +#include "ILayerWayland.hh" namespace jwm { - class LayerGL: public RefCounted, public ILayer { + class LayerGL: public RefCounted, public ILayerWayland { public: WindowWayland* fWindow; wl_egl_window* _eglWindow = nullptr; EGLContext _context = nullptr; EGLDisplay _display = nullptr; EGLSurface _surface = nullptr; + EGLConfig _config = nullptr; LayerGL() = default; virtual ~LayerGL() = default; @@ -44,17 +46,13 @@ namespace jwm { EGL_RED_SIZE, 8, EGL_NONE }; - EGLConfig config; EGLint numConfig; - eglChooseConfig(_display, attrList, &config, 1, &numConfig); + eglChooseConfig(_display, attrList, &_config, 1, &numConfig); // :troll: _context = eglCreateContext(_display, - config, + _config, EGL_NO_CONTEXT, nullptr); - _eglWindow = wl_egl_window_create(window->_waylandWindow, window->getWidth(), window->getHeight()); - // TODO: closed windows? - _surface = eglCreatePlatformWindowSurface(_display, config, _eglWindow, nullptr); } makeCurrentForced(); @@ -94,6 +92,14 @@ namespace jwm { _surface, _context); } + void attachBuffer() override { + if (fWindow && fWindow->_waylandWindow) { + if (!_eglWindow) { + _eglWindow = wl_egl_window_create(fWindow->_waylandWindow, fWindow->getWidth(), fWindow->getHeight()); + _surface = eglCreatePlatformWindowSurface(_display, _config, _eglWindow, nullptr); + } + } + } }; } // namespace jwm diff --git a/wayland/cc/LayerRasterWayland.cc b/wayland/cc/LayerRasterWayland.cc index 9bec31a2..431d6464 100644 --- a/wayland/cc/LayerRasterWayland.cc +++ b/wayland/cc/LayerRasterWayland.cc @@ -8,7 +8,7 @@ #include namespace jwm { - class LayerRaster: public RefCounted, public ILayer { + class LayerRaster: public RefCounted, public ILayerWayland { public: WindowWayland* fWindow; size_t _width = 0, _height = 0; @@ -50,8 +50,6 @@ namespace jwm { _buffer = buf.first; _imageData = buf.second; - - } const void* getPixelsPtr() const { @@ -65,8 +63,10 @@ namespace jwm { void swapBuffers() { // : ) - if (fWindow->_waylandWindow) + if (fWindow->_waylandWindow) { wl_surface_damage_buffer(fWindow->_waylandWindow, 0, 0, UINT32_MAX, UINT32_MAX); + wl_surface_commit(fWindow->_waylandWindow); + } } void close() override { @@ -87,6 +87,15 @@ namespace jwm { // srsly, why do I need this _vsync = v; } + + void attachBuffer() override { + if (fWindow) { + if (fWindow->_waylandWindow) { + wl_surface_attach(fWindow->_waylandWindow, _buffer, 0, 0); + wl_surface_commit(fWindow->_waylandWindow); + } + } + } }; } diff --git a/wayland/cc/WindowManagerWayland.cc b/wayland/cc/WindowManagerWayland.cc index a7f9e5c2..6b65294d 100644 --- a/wayland/cc/WindowManagerWayland.cc +++ b/wayland/cc/WindowManagerWayland.cc @@ -107,11 +107,9 @@ void WindowManagerWayland::runLoop() { struct pollfd ps[] = {{.fd=wl_display_get_fd(display), .events=POLLIN}, {.fd=pipes[0], .events=POLLIN}}; // who be out here running they loop while (_runLoop) { - printf("gi huys\n"); wl_display_flush(display); if (jwm::classes::Throwable::exceptionThrown(app.getJniEnv())) _runLoop = false; - printf("try guys\n"); _processCallbacks(); // block until event : ) @@ -119,14 +117,12 @@ void WindowManagerWayland::runLoop() { printf("error with pipe\n"); break; } - printf("why guys\n"); if (ps[1].revents & POLLIN) { while (read(pipes[0], buf, sizeof(buf)) == sizeof(buf)) { } } if (ps[0].revents & POLLIN) { wl_display_dispatch(display); } - printf("hi guys? %i\n", _runLoop); notifyBool.store(false); } @@ -465,12 +461,10 @@ void WindowManagerWayland::enqueueTask(const std::function& task) { void WindowManagerWayland::notifyLoop() { // maybe just do nothing? - /* if (notifyFD==-1) return; // fast notifyBool path to not make system calls when not necessary if (!notifyBool.exchange(true)) { char dummy[1] = {0}; int unused = write(notifyFD, dummy, 1); // this really shouldn't fail, but if it does, the pipe should either be full (good), or dead (bad, but not our business) } - */ } diff --git a/wayland/cc/WindowWayland.cc b/wayland/cc/WindowWayland.cc index 911592b6..c280afde 100644 --- a/wayland/cc/WindowWayland.cc +++ b/wayland/cc/WindowWayland.cc @@ -135,6 +135,10 @@ void WindowWayland::show() }; xdg_toplevel_add_listener(xdgToplevel, &xdgToplevelListener, this); _windowManager.registerWindow(this); + + wl_display_roundtrip(_windowManager.display); + if (_layer) + _layer->attachBuffer(); } // ??? @@ -200,6 +204,7 @@ void jwm::WindowWayland::surfacePreferredBufferTransform(void* data, wl_surface* void jwm::WindowWayland::xdgSurfaceConfigure(void* data, xdg_surface* surface, uint32_t serial) { WindowWayland* self = (WindowWayland*) data; + printf("hi guys"); // Commit state if (self->_newWidth > 0 || self->_newHeight > 0) { int goodWidth = self->_width, goodHeight = self->_height; diff --git a/wayland/cc/WindowWayland.hh b/wayland/cc/WindowWayland.hh index 9658dfc7..6bdbf174 100644 --- a/wayland/cc/WindowWayland.hh +++ b/wayland/cc/WindowWayland.hh @@ -4,7 +4,7 @@ #include #include "Window.hh" #include "WindowManagerWayland.hh" -#include +#include "ILayerWayland.hh" #include "ScreenInfo.hh" #include "xdg-shell.hh" #include @@ -47,8 +47,10 @@ namespace jwm { bool isFullScreen(); void setCursor(jwm::MouseCursor cursor); - void setLayer(ILayer* layer) { + void setLayer(ILayerWayland* layer) { _layer = layer; + if (_visible) + _layer->attachBuffer(); } @@ -95,7 +97,7 @@ namespace jwm { bool _isRedrawRequested = false; WindowManagerWayland& _windowManager; - ILayer* _layer = nullptr; + ILayerWayland* _layer = nullptr; wl_surface* _waylandWindow = nullptr; xdg_surface* xdgSurface = nullptr; xdg_toplevel* xdgToplevel = nullptr; From 2831f685f1ae7be765da5e52d206634de4e72a6f Mon Sep 17 00:00:00 2001 From: BulbyVR <26726264+TheDrawingCoder-Gamer@users.noreply.github.com> Date: Mon, 4 Dec 2023 10:33:17 -0500 Subject: [PATCH 07/95] It now segfaults --- wayland/cc/WindowWayland.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/wayland/cc/WindowWayland.cc b/wayland/cc/WindowWayland.cc index c280afde..efcebca0 100644 --- a/wayland/cc/WindowWayland.cc +++ b/wayland/cc/WindowWayland.cc @@ -136,9 +136,6 @@ void WindowWayland::show() xdg_toplevel_add_listener(xdgToplevel, &xdgToplevelListener, this); _windowManager.registerWindow(this); - wl_display_roundtrip(_windowManager.display); - if (_layer) - _layer->attachBuffer(); } // ??? @@ -217,6 +214,9 @@ void jwm::WindowWayland::xdgSurfaceConfigure(void* data, xdg_surface* surface, u self->_newWidth = -1; self->_newHeight = -1; xdg_surface_ack_configure(surface, serial); + if (self->_layer) { + self->_layer->attachBuffer(); + } } void jwm::WindowWayland::xdgToplevelConfigure(void* data, xdg_toplevel* toplevel, int width, int height, wl_array* states) { WindowWayland* self = (WindowWayland*) data; From cc7f89258609713e7c9f2afc66d8b85db84adf6c Mon Sep 17 00:00:00 2001 From: BulbyVR <26726264+TheDrawingCoder-Gamer@users.noreply.github.com> Date: Tue, 5 Dec 2023 20:10:32 -0500 Subject: [PATCH 08/95] ok i'm trying --- examples/dashboard/java/PanelRendering.java | 2 +- wayland/cc/LayerRasterWayland.cc | 22 ++++++--- wayland/cc/ShmPool.cc | 7 ++- wayland/cc/ShmPool.hh | 1 + wayland/cc/WindowManagerWayland.cc | 55 ++++++++++++++++----- wayland/cc/WindowWayland.cc | 23 +++++---- wayland/cc/WindowWayland.hh | 2 - 7 files changed, 78 insertions(+), 34 deletions(-) diff --git a/examples/dashboard/java/PanelRendering.java b/examples/dashboard/java/PanelRendering.java index 4d215ccf..a40b4a0a 100644 --- a/examples/dashboard/java/PanelRendering.java +++ b/examples/dashboard/java/PanelRendering.java @@ -35,7 +35,7 @@ else if (Platform.CURRENT == Platform.WINDOWS) else if (Platform.CURRENT == Platform.X11) layers = new String[] { "LayerGLSkija", "LayerRasterSkija" }; else if (Platform.CURRENT == Platform.WAYLAND) - layers = new String[] { "LayerGLSkija", "LayerRasterSkija" }; + layers = new String[] { "LayerRasterSkija" }; for (var layerName: layers) layersStatus.put(layerName, UNKNOWN); diff --git a/wayland/cc/LayerRasterWayland.cc b/wayland/cc/LayerRasterWayland.cc index 431d6464..8e603f1c 100644 --- a/wayland/cc/LayerRasterWayland.cc +++ b/wayland/cc/LayerRasterWayland.cc @@ -16,6 +16,7 @@ namespace jwm { uint8_t* _imageData = nullptr; ShmPool* _pool = nullptr; VSync _vsync = VSYNC_ENABLED; + bool _attached = false; LayerRaster() = default; virtual ~LayerRaster() = default; @@ -35,21 +36,26 @@ namespace jwm { int bufSize = stride * height * 2; // TODO: better pool impl if (_pool) { - delete _pool; + _pool->close(); } _pool = new ShmPool(fWindow->_windowManager.shm, bufSize); + if (fWindow->_waylandWindow) { + wl_surface_attach(fWindow->_waylandWindow, nullptr, 0, 0); + } if (_buffer) { wl_buffer_destroy(_buffer); _imageData = nullptr; } - _pool->grow(bufSize); // LSBFirst means Little endian : ) // This will highly likely cause issues : ) - auto buf = _pool->createBuffer(0, width, height, stride, WL_SHM_FORMAT_ABGR8888); + auto buf = _pool->createBuffer(0, width, height, stride, WL_SHM_FORMAT_ARGB8888); _buffer = buf.first; _imageData = buf.second; + if (_attached) { + attachBuffer(); + } } const void* getPixelsPtr() const { @@ -63,8 +69,8 @@ namespace jwm { void swapBuffers() { // : ) - if (fWindow->_waylandWindow) { - wl_surface_damage_buffer(fWindow->_waylandWindow, 0, 0, UINT32_MAX, UINT32_MAX); + if (fWindow->_waylandWindow && _attached) { + wl_surface_damage_buffer(fWindow->_waylandWindow, 0, 0, INT32_MAX, INT32_MAX); wl_surface_commit(fWindow->_waylandWindow); } } @@ -75,7 +81,9 @@ namespace jwm { _buffer = nullptr; } // ??? - delete _pool; + if (_pool) { + _pool->close(); + } jwm::unref(&fWindow); } @@ -93,10 +101,12 @@ namespace jwm { if (fWindow->_waylandWindow) { wl_surface_attach(fWindow->_waylandWindow, _buffer, 0, 0); wl_surface_commit(fWindow->_waylandWindow); + _attached = true; } } } }; + } diff --git a/wayland/cc/ShmPool.cc b/wayland/cc/ShmPool.cc index 0f42ed50..b6c3a0de 100644 --- a/wayland/cc/ShmPool.cc +++ b/wayland/cc/ShmPool.cc @@ -31,8 +31,11 @@ ShmPool::ShmPool(wl_shm* shm, size_t size): } ShmPool::~ShmPool() { + +} +void ShmPool::close() { wl_shm_pool_destroy(_pool); - close(_fd); + ::close(_fd); } void ShmPool::grow(size_t size) { @@ -80,7 +83,7 @@ int ShmPool::_allocateShmFile(size_t size) { ret = ftruncate(fd, size); } while (ret < 0 && errno == EINTR); if (ret < 0) { - close(fd); + ::close(fd); return -1; } return fd; diff --git a/wayland/cc/ShmPool.hh b/wayland/cc/ShmPool.hh index ed67e1a5..402660b6 100644 --- a/wayland/cc/ShmPool.hh +++ b/wayland/cc/ShmPool.hh @@ -21,5 +21,6 @@ namespace jwm { int _createShmFile(); int _allocateShmFile(size_t size); + void close(); }; } diff --git a/wayland/cc/WindowManagerWayland.cc b/wayland/cc/WindowManagerWayland.cc index 6b65294d..5354ce88 100644 --- a/wayland/cc/WindowManagerWayland.cc +++ b/wayland/cc/WindowManagerWayland.cc @@ -16,7 +16,7 @@ #include "Log.hh" #include "xdg-shell.hh" #include - +#include using namespace jwm; @@ -40,12 +40,13 @@ WindowManagerWayland::WindowManagerWayland(): throw std::system_error(ENOTSUP, std::generic_category(), "Unsupported compositor"); } - xdg_wm_base_listener xdgListener = { + struct xdg_wm_base_listener xdgListener = { .ping = WindowManagerWayland::xdgWmBasePing }; + // frankly `this` is not needed here, but it needs a pointer anyway and it's - // good to have consistentcy. - xdg_wm_base_add_listener(xdgShell, &xdgListener, this); + // good to have consistentcy. + // xdg_wm_base_add_listener(xdgShell, &xdgListener, this); { wl_cursor_theme* cursor_theme = wl_cursor_theme_load(nullptr, 24, shm); // TODO: what about if missing : ( @@ -76,12 +77,12 @@ WindowManagerWayland::WindowManagerWayland(): { pointer = wl_seat_get_pointer(seat); - wl_pointer_listener pointerListener = { + struct wl_pointer_listener pointerListener = { .enter = WindowManagerWayland::pointerHandleEnter, .leave = WindowManagerWayland::pointerHandleLeave, .motion = WindowManagerWayland::pointerHandleMotion, .button = WindowManagerWayland::pointerHandleButton, - //.axis = WindowManagerWayland::pointerHandleAxis + .axis = WindowManagerWayland::pointerHandleAxis }; wl_pointer_add_listener(pointer, &pointerListener, this); } @@ -104,26 +105,56 @@ void WindowManagerWayland::runLoop() { } notifyFD = pipes[1]; fcntl(pipes[1], F_SETFL, O_NONBLOCK); // notifyLoop no blockie : ) + struct pollfd wayland_out = {.fd=wl_display_get_fd(display),.events=POLLOUT}; struct pollfd ps[] = {{.fd=wl_display_get_fd(display), .events=POLLIN}, {.fd=pipes[0], .events=POLLIN}}; // who be out here running they loop while (_runLoop) { - wl_display_flush(display); + printf(": (\n"); if (jwm::classes::Throwable::exceptionThrown(app.getJniEnv())) _runLoop = false; _processCallbacks(); + while(wl_display_prepare_read(display) != 0) + wl_display_dispatch_pending(display); + // adapted from Waylock + while (true) { + int res = wl_display_flush(display); + if (res >= 0) + break; + + switch (errno) { + case EPIPE: + wl_display_read_events(display); + throw std::system_error(errno, std::generic_category(), "connection to wayland server unexpectedly terminated"); + break; + case EAGAIN: + if (poll(&wayland_out, 1, -1) < 0) { + throw std::system_error(EPIPE, std::generic_category(), "poll failed"); + } + break; + default: + throw std::system_error(errno, std::generic_category(), "failed to flush requests"); + break; + } + } // block until event : ) if (poll(&ps[0], 2, -1) < 0) { printf("error with pipe\n"); break; } + printf(": |\n"); if (ps[1].revents & POLLIN) { while (read(pipes[0], buf, sizeof(buf)) == sizeof(buf)) { } } if (ps[0].revents & POLLIN) { - wl_display_dispatch(display); + // WHY IN THE WORLD IS THIS CRASHING + wl_display_read_events(display); + } else { + wl_display_cancel_read(display); } + wl_display_dispatch_pending(display); notifyBool.store(false); + printf(": )\n"); } notifyFD = -1; @@ -155,7 +186,7 @@ void WindowManagerWayland::_processCallbacks() { for (auto p : copy) { if (p->isRedrawRequested()) { p->unsetRedrawRequest(); - if (p->_layer) { + if (p->_layer && p->_visible) { p->_layer->makeCurrent(); } p->dispatch(classes::EventFrame::kInstance); @@ -166,7 +197,7 @@ void WindowManagerWayland::_processCallbacks() { void WindowManagerWayland::registryHandleGlobal(void* data, wl_registry *registry, uint32_t name, const char* interface, uint32_t version) { - WindowManagerWayland* self = (WindowManagerWayland*)data; + WindowManagerWayland* self = reinterpret_cast(data); if (strcmp(interface, "wl_compositor") == 0) { // EGL apparently requires at least a version of 4 here : ) self->compositor = (wl_compositor*)wl_registry_bind(registry, name, @@ -176,7 +207,7 @@ void WindowManagerWayland::registryHandleGlobal(void* data, wl_registry *registr &wl_shm_interface, 1); } else if (strcmp(interface, "xdg_wm_base") == 0) { self->xdgShell = (xdg_wm_base*)wl_registry_bind(registry, name, - &xdg_wm_base_interface, 1); + &xdg_wm_base_interface, 2); } else if (strcmp(interface, "wl_data_device_manager") == 0) { self->deviceManager = (wl_data_device_manager*)wl_registry_bind(registry, name, &wl_data_device_manager_interface, 1); @@ -288,6 +319,8 @@ void WindowManagerWayland::pointerHandleButton(void* data, wl_pointer* pointer, } } } +void WindowManagerWayland::pointerHandleAxis(void* data, wl_pointer* pointer, + uint32_t time, uint32_t axis, wl_fixed_t value) {} void WindowManagerWayland::xdgWmBasePing(void* data, xdg_wm_base* base, uint32_t serial) { xdg_wm_base_pong(base, serial); } diff --git a/wayland/cc/WindowWayland.cc b/wayland/cc/WindowWayland.cc index efcebca0..300ad76a 100644 --- a/wayland/cc/WindowWayland.cc +++ b/wayland/cc/WindowWayland.cc @@ -11,7 +11,10 @@ using namespace jwm; WindowWayland::WindowWayland(JNIEnv* env, WindowManagerWayland& windowManager): jwm::Window(env), - _windowManager(windowManager) + _windowManager(windowManager), + // HACK: have a default height bc some compositors won't hecking tell me :sob: + _width(100), + _height(100) { } @@ -28,7 +31,7 @@ void WindowWayland::setTitlebarVisible(bool isVisible) { // impl me : ) } -// Closing is like... the exact same as being visible. WTH +// Closing is like... the exact same as hiding. WTH void WindowWayland::close() { if (_waylandWindow) { _windowManager.unregisterWindow(this); @@ -113,10 +116,8 @@ void WindowWayland::show() wl_surface_listener surfaceListener = { .enter = WindowWayland::surfaceEnter, .leave = WindowWayland::surfaceLeave, - #ifdef HAVE_WAYLAND_1_22 .preferred_buffer_scale = WindowWayland::surfacePreferredBufferScale, .preferred_buffer_transform = WindowWayland::surfacePreferredBufferTransform - #endif }; wl_surface_add_listener(_waylandWindow, &surfaceListener, this); @@ -133,9 +134,9 @@ void WindowWayland::show() .configure_bounds = WindowWayland::xdgToplevelConfigureBounds, .wm_capabilities = WindowWayland::xdgToplevelWmCapabilities }; - xdg_toplevel_add_listener(xdgToplevel, &xdgToplevelListener, this); + xdg_toplevel_add_listener(xdgToplevel, &xdgToplevelListener, this); _windowManager.registerWindow(this); - + wl_surface_commit(_waylandWindow); } // ??? @@ -173,6 +174,7 @@ void jwm::WindowWayland::setCursor(jwm::MouseCursor cursor) { // what do??? void jwm::WindowWayland::surfaceEnter(void* data, wl_surface* surface, wl_output* output) { + // doesn't crash : ) wl_output_listener listener = { .geometry = WindowWayland::outputGeometry, .mode = WindowWayland::outputMode, @@ -192,16 +194,13 @@ void jwm::WindowWayland::surfacePreferredBufferScale(void* data, wl_surface* sur self->_scale = factor; // do I pinky promise here? // yes : ) - if (self->_layer) { - self->_layer->resize(self->_width * factor, self->_height * factor); - } wl_surface_set_buffer_scale(surface, factor); + self->_adaptSize(self->_width, self->_height); } void jwm::WindowWayland::surfacePreferredBufferTransform(void* data, wl_surface* surface, uint32_t transform) {} void jwm::WindowWayland::xdgSurfaceConfigure(void* data, xdg_surface* surface, uint32_t serial) { WindowWayland* self = (WindowWayland*) data; - printf("hi guys"); // Commit state if (self->_newWidth > 0 || self->_newHeight > 0) { int goodWidth = self->_width, goodHeight = self->_height; @@ -229,8 +228,8 @@ void jwm::WindowWayland::xdgToplevelConfigure(void* data, xdg_toplevel* toplevel // honestly idrc about the state } void jwm::WindowWayland::xdgToplevelClose(void* data, xdg_toplevel* toplevel) { - // ??? - // Request close EVENTUALLY:TM: + WindowWayland* self = reinterpret_cast(data); + self->dispatch(classes::EventWindowCloseRequest::kInstance); } void jwm::WindowWayland::xdgToplevelConfigureBounds(void* data, xdg_toplevel* toplevel, int width, int height) { WindowWayland* self = (WindowWayland*) data; diff --git a/wayland/cc/WindowWayland.hh b/wayland/cc/WindowWayland.hh index 6bdbf174..53c22717 100644 --- a/wayland/cc/WindowWayland.hh +++ b/wayland/cc/WindowWayland.hh @@ -49,8 +49,6 @@ namespace jwm { void setCursor(jwm::MouseCursor cursor); void setLayer(ILayerWayland* layer) { _layer = layer; - if (_visible) - _layer->attachBuffer(); } From 9c35576e1d1afd8a24a50e9da3a80d3126380d2c Mon Sep 17 00:00:00 2001 From: BulbyVR <26726264+TheDrawingCoder-Gamer@users.noreply.github.com> Date: Tue, 5 Dec 2023 21:06:07 -0500 Subject: [PATCH 09/95] IT SURE DOES OPEN --- wayland/CMakeLists.txt | 4 +- wayland/cc/WindowManagerWayland.cc | 52 +++++++++-------- wayland/cc/WindowManagerWayland.hh | 7 ++- wayland/cc/WindowWayland.cc | 56 ++++++++++--------- wayland/cc/WindowWayland.hh | 9 ++- wayland/cc/{ => xdg-shell}/xdg-shell.c | 21 ++----- .../{xdg-shell.hh => xdg-shell/xdg-shell.h} | 0 7 files changed, 76 insertions(+), 73 deletions(-) rename wayland/cc/{ => xdg-shell}/xdg-shell.c (90%) rename wayland/cc/{xdg-shell.hh => xdg-shell/xdg-shell.h} (100%) diff --git a/wayland/CMakeLists.txt b/wayland/CMakeLists.txt index 074e87d6..d323fd85 100644 --- a/wayland/CMakeLists.txt +++ b/wayland/CMakeLists.txt @@ -20,8 +20,8 @@ file(GLOB SOURCES_CXX ${CMAKE_CURRENT_LIST_DIR}/../shared/cc/*.cc ${CMAKE_CURRENT_LIST_DIR}/../linux/cc/*.cc ${CMAKE_CURRENT_LIST_DIR}/cc/*.cc ) file(GLOB SOURCES_CXX_IMPL ${CMAKE_CURRENT_LIST_DIR}/../shared/cc/impl/*.cc) -file(GLOB SOURCES_C ${CMAKE_CURRENT_LIST_DIR}/cc/*.c) -add_library(xdgShell STATIC ${SOURCES_C}) +file(GLOB SOURCES_XDG ${CMAKE_CURRENT_LIST_DIR}/cc/xdg-shell/*.c) +add_library(xdgShell STATIC ${SOURCES_XDG}) add_library(jwm SHARED ${SOURCES_OBJC} ${SOURCES_CXX} ${SOURCES_CXX_IMPL}) find_library(WAYLAND_CLIENT_LIB wayland-client) find_library(DECOR_LIB decor-0) diff --git a/wayland/cc/WindowManagerWayland.cc b/wayland/cc/WindowManagerWayland.cc index 5354ce88..50b88e41 100644 --- a/wayland/cc/WindowManagerWayland.cc +++ b/wayland/cc/WindowManagerWayland.cc @@ -14,21 +14,30 @@ #include #include #include "Log.hh" -#include "xdg-shell.hh" #include #include using namespace jwm; +wl_registry_listener WindowManagerWayland::_registryListener = { + .global = WindowManagerWayland::registryHandleGlobal, + .global_remove = WindowManagerWayland::registryHandleGlobalRemove +}; +wl_pointer_listener WindowManagerWayland::_pointerListener = { + .enter = WindowManagerWayland::pointerHandleEnter, + .leave = WindowManagerWayland::pointerHandleLeave, + .motion = WindowManagerWayland::pointerHandleMotion, + .button = WindowManagerWayland::pointerHandleButton, + .axis = WindowManagerWayland::pointerHandleAxis +}; +xdg_wm_base_listener WindowManagerWayland::_xdgWmBaseListener = { + .ping = WindowManagerWayland::xdgWmBasePing +}; WindowManagerWayland::WindowManagerWayland(): display(wl_display_connect(nullptr)) { registry = wl_display_get_registry(display); - wl_registry_listener registry_listener = { - .global = WindowManagerWayland::registryHandleGlobal, - .global_remove = WindowManagerWayland::registryHandleGlobalRemove - }; - wl_registry_add_listener(registry, ®istry_listener, this); + wl_registry_add_listener(registry, &_registryListener, this); wl_display_roundtrip(display); @@ -40,13 +49,11 @@ WindowManagerWayland::WindowManagerWayland(): throw std::system_error(ENOTSUP, std::generic_category(), "Unsupported compositor"); } - struct xdg_wm_base_listener xdgListener = { - .ping = WindowManagerWayland::xdgWmBasePing - }; + // frankly `this` is not needed here, but it needs a pointer anyway and it's // good to have consistentcy. - // xdg_wm_base_add_listener(xdgShell, &xdgListener, this); + xdg_wm_base_add_listener(xdgShell, &_xdgWmBaseListener, this); { wl_cursor_theme* cursor_theme = wl_cursor_theme_load(nullptr, 24, shm); // TODO: what about if missing : ( @@ -77,14 +84,8 @@ WindowManagerWayland::WindowManagerWayland(): { pointer = wl_seat_get_pointer(seat); - struct wl_pointer_listener pointerListener = { - .enter = WindowManagerWayland::pointerHandleEnter, - .leave = WindowManagerWayland::pointerHandleLeave, - .motion = WindowManagerWayland::pointerHandleMotion, - .button = WindowManagerWayland::pointerHandleButton, - .axis = WindowManagerWayland::pointerHandleAxis - }; - wl_pointer_add_listener(pointer, &pointerListener, this); + + wl_pointer_add_listener(pointer, &_pointerListener, this); } @@ -109,7 +110,6 @@ void WindowManagerWayland::runLoop() { struct pollfd ps[] = {{.fd=wl_display_get_fd(display), .events=POLLIN}, {.fd=pipes[0], .events=POLLIN}}; // who be out here running they loop while (_runLoop) { - printf(": (\n"); if (jwm::classes::Throwable::exceptionThrown(app.getJniEnv())) _runLoop = false; _processCallbacks(); @@ -142,7 +142,6 @@ void WindowManagerWayland::runLoop() { printf("error with pipe\n"); break; } - printf(": |\n"); if (ps[1].revents & POLLIN) { while (read(pipes[0], buf, sizeof(buf)) == sizeof(buf)) { } } @@ -154,7 +153,6 @@ void WindowManagerWayland::runLoop() { } wl_display_dispatch_pending(display); notifyBool.store(false); - printf(": )\n"); } notifyFD = -1; @@ -198,20 +196,20 @@ void WindowManagerWayland::_processCallbacks() { void WindowManagerWayland::registryHandleGlobal(void* data, wl_registry *registry, uint32_t name, const char* interface, uint32_t version) { WindowManagerWayland* self = reinterpret_cast(data); - if (strcmp(interface, "wl_compositor") == 0) { + if (strcmp(interface, wl_compositor_interface.name) == 0) { // EGL apparently requires at least a version of 4 here : ) self->compositor = (wl_compositor*)wl_registry_bind(registry, name, &wl_compositor_interface, 4); - } else if (strcmp(interface, "wl_shm") == 0) { + } else if (strcmp(interface, wl_shm_interface.name) == 0) { self->shm = (wl_shm*)wl_registry_bind(registry, name, &wl_shm_interface, 1); - } else if (strcmp(interface, "xdg_wm_base") == 0) { + } else if (strcmp(interface, xdg_wm_base_interface.name) == 0) { self->xdgShell = (xdg_wm_base*)wl_registry_bind(registry, name, - &xdg_wm_base_interface, 2); - } else if (strcmp(interface, "wl_data_device_manager") == 0) { + &xdg_wm_base_interface, 1); + } else if (strcmp(interface, wl_data_device_manager_interface.name) == 0) { self->deviceManager = (wl_data_device_manager*)wl_registry_bind(registry, name, &wl_data_device_manager_interface, 1); - } else if (strcmp(interface, "wl_seat") == 0) { + } else if (strcmp(interface, wl_seat_interface.name) == 0) { self->seat = (wl_seat*)wl_registry_bind(registry, name, &wl_seat_interface, 1); } diff --git a/wayland/cc/WindowManagerWayland.hh b/wayland/cc/WindowManagerWayland.hh index dae2c083..a5ecf0ec 100644 --- a/wayland/cc/WindowManagerWayland.hh +++ b/wayland/cc/WindowManagerWayland.hh @@ -13,7 +13,7 @@ #include "MouseCursor.hh" #include #include -#include "xdg-shell.hh" +#include "xdg-shell/xdg-shell.h" namespace jwm { class WindowWayland; @@ -52,11 +52,15 @@ namespace jwm { void notifyLoop(); void enqueueTask(const std::function& task); + static wl_registry_listener _registryListener; + static void registryHandleGlobal(void* data, wl_registry *registry, uint32_t name, const char* interface, uint32_t version); static void registryHandleGlobalRemove(void* data, wl_registry *registry, uint32_t name); + static wl_pointer_listener _pointerListener; + static void pointerHandleEnter(void* data, wl_pointer *pointer, uint32_t serial, wl_surface *surface, wl_fixed_t surface_x, wl_fixed_t surface_y); static void pointerHandleLeave(void* data, wl_pointer *pointer, @@ -69,6 +73,7 @@ namespace jwm { static void pointerHandleAxis(void* data, wl_pointer *pointer, uint32_t time, uint32_t axis, wl_fixed_t value); + static xdg_wm_base_listener _xdgWmBaseListener; static void xdgWmBasePing(void* data, xdg_wm_base* base, uint32_t serial); diff --git a/wayland/cc/WindowWayland.cc b/wayland/cc/WindowWayland.cc index 300ad76a..ab21dfc4 100644 --- a/wayland/cc/WindowWayland.cc +++ b/wayland/cc/WindowWayland.cc @@ -9,6 +9,31 @@ using namespace jwm; +wl_surface_listener WindowWayland::_surfaceListener = { + .enter = WindowWayland::surfaceEnter, + .leave = WindowWayland::surfaceLeave, + .preferred_buffer_scale = WindowWayland::surfacePreferredBufferScale, + .preferred_buffer_transform = WindowWayland::surfacePreferredBufferTransform +}; +xdg_surface_listener WindowWayland::_xdgSurfaceListener = { + .configure = WindowWayland::xdgSurfaceConfigure +}; + +xdg_toplevel_listener WindowWayland::_xdgToplevelListener = { + .configure = WindowWayland::xdgToplevelConfigure, + .close = WindowWayland::xdgToplevelClose, + .configure_bounds = WindowWayland::xdgToplevelConfigureBounds, + .wm_capabilities = WindowWayland::xdgToplevelWmCapabilities +}; + +wl_output_listener WindowWayland::_outputListener = { + .geometry = WindowWayland::outputGeometry, + .mode = WindowWayland::outputMode, + .done = WindowWayland::outputDone, + .scale = WindowWayland::outputScale, + .name = WindowWayland::outputName, + .description = WindowWayland::outputDescription +}; WindowWayland::WindowWayland(JNIEnv* env, WindowManagerWayland& windowManager): jwm::Window(env), _windowManager(windowManager), @@ -113,28 +138,13 @@ bool WindowWayland::init() { void WindowWayland::show() { _waylandWindow = wl_compositor_create_surface(_windowManager.compositor); - wl_surface_listener surfaceListener = { - .enter = WindowWayland::surfaceEnter, - .leave = WindowWayland::surfaceLeave, - .preferred_buffer_scale = WindowWayland::surfacePreferredBufferScale, - .preferred_buffer_transform = WindowWayland::surfacePreferredBufferTransform - }; - wl_surface_add_listener(_waylandWindow, &surfaceListener, this); + wl_surface_add_listener(_waylandWindow, &_surfaceListener, this); xdgSurface = xdg_wm_base_get_xdg_surface(_windowManager.xdgShell, _waylandWindow); - xdg_surface_listener xdgSurfaceListener = { - .configure = WindowWayland::xdgSurfaceConfigure - }; - xdg_surface_add_listener(xdgSurface, &xdgSurfaceListener, this); + xdg_surface_add_listener(xdgSurface, &_xdgSurfaceListener, this); xdgToplevel = xdg_surface_get_toplevel(xdgSurface); - xdg_toplevel_listener xdgToplevelListener = { - .configure = WindowWayland::xdgToplevelConfigure, - .close = WindowWayland::xdgToplevelClose, - .configure_bounds = WindowWayland::xdgToplevelConfigureBounds, - .wm_capabilities = WindowWayland::xdgToplevelWmCapabilities - }; - xdg_toplevel_add_listener(xdgToplevel, &xdgToplevelListener, this); + xdg_toplevel_add_listener(xdgToplevel, &_xdgToplevelListener, this); _windowManager.registerWindow(this); wl_surface_commit(_waylandWindow); } @@ -175,15 +185,7 @@ void jwm::WindowWayland::setCursor(jwm::MouseCursor cursor) { // what do??? void jwm::WindowWayland::surfaceEnter(void* data, wl_surface* surface, wl_output* output) { // doesn't crash : ) - wl_output_listener listener = { - .geometry = WindowWayland::outputGeometry, - .mode = WindowWayland::outputMode, - .done = WindowWayland::outputDone, - .scale = WindowWayland::outputScale, - .name = WindowWayland::outputName, - .description = WindowWayland::outputDescription - }; - wl_output_add_listener(output, &listener, data); + wl_output_add_listener(output, &_outputListener, data); } void jwm::WindowWayland::surfaceLeave(void* data, wl_surface* surface, wl_output* output) {} void jwm::WindowWayland::surfacePreferredBufferScale(void* data, wl_surface* surface, int factor) { diff --git a/wayland/cc/WindowWayland.hh b/wayland/cc/WindowWayland.hh index 53c22717..80723915 100644 --- a/wayland/cc/WindowWayland.hh +++ b/wayland/cc/WindowWayland.hh @@ -6,7 +6,7 @@ #include "WindowManagerWayland.hh" #include "ILayerWayland.hh" #include "ScreenInfo.hh" -#include "xdg-shell.hh" +#include "xdg-shell/xdg-shell.h" #include namespace jwm { class WindowWayland: public jwm::Window { @@ -100,5 +100,12 @@ namespace jwm { xdg_surface* xdgSurface = nullptr; xdg_toplevel* xdgToplevel = nullptr; libdecor_frame* _frame = nullptr; + + static wl_surface_listener _surfaceListener; + static xdg_surface_listener _xdgSurfaceListener; + static xdg_toplevel_listener _xdgToplevelListener; + + static wl_output_listener _outputListener; + }; } diff --git a/wayland/cc/xdg-shell.c b/wayland/cc/xdg-shell/xdg-shell.c similarity index 90% rename from wayland/cc/xdg-shell.c rename to wayland/cc/xdg-shell/xdg-shell.c index 747c222a..a91fb6ca 100644 --- a/wayland/cc/xdg-shell.c +++ b/wayland/cc/xdg-shell/xdg-shell.c @@ -32,16 +32,6 @@ #include #include "wayland-util.h" -#ifndef __has_attribute -# define __has_attribute(x) 0 /* Compatibility with non-clang compilers. */ -#endif - -#if (__has_attribute(visibility) || defined(__GNUC__) && __GNUC__ >= 4) -#define WL_PRIVATE __attribute__ ((visibility("hidden"))) -#else -#define WL_PRIVATE -#endif - extern const struct wl_interface wl_output_interface; extern const struct wl_interface wl_seat_interface; extern const struct wl_interface wl_surface_interface; @@ -90,7 +80,7 @@ static const struct wl_message xdg_wm_base_events[] = { { "ping", "u", xdg_shell_types + 0 }, }; -WL_PRIVATE const struct wl_interface xdg_wm_base_interface = { +WL_EXPORT const struct wl_interface xdg_wm_base_interface = { "xdg_wm_base", 6, 4, xdg_wm_base_requests, 1, xdg_wm_base_events, @@ -109,7 +99,7 @@ static const struct wl_message xdg_positioner_requests[] = { { "set_parent_configure", "3u", xdg_shell_types + 0 }, }; -WL_PRIVATE const struct wl_interface xdg_positioner_interface = { +WL_EXPORT const struct wl_interface xdg_positioner_interface = { "xdg_positioner", 6, 10, xdg_positioner_requests, 0, NULL, @@ -127,7 +117,7 @@ static const struct wl_message xdg_surface_events[] = { { "configure", "u", xdg_shell_types + 0 }, }; -WL_PRIVATE const struct wl_interface xdg_surface_interface = { +WL_EXPORT const struct wl_interface xdg_surface_interface = { "xdg_surface", 6, 5, xdg_surface_requests, 1, xdg_surface_events, @@ -157,7 +147,7 @@ static const struct wl_message xdg_toplevel_events[] = { { "wm_capabilities", "5a", xdg_shell_types + 0 }, }; -WL_PRIVATE const struct wl_interface xdg_toplevel_interface = { +WL_EXPORT const struct wl_interface xdg_toplevel_interface = { "xdg_toplevel", 6, 14, xdg_toplevel_requests, 4, xdg_toplevel_events, @@ -175,8 +165,9 @@ static const struct wl_message xdg_popup_events[] = { { "repositioned", "3u", xdg_shell_types + 0 }, }; -WL_PRIVATE const struct wl_interface xdg_popup_interface = { +WL_EXPORT const struct wl_interface xdg_popup_interface = { "xdg_popup", 6, 3, xdg_popup_requests, 3, xdg_popup_events, }; + diff --git a/wayland/cc/xdg-shell.hh b/wayland/cc/xdg-shell/xdg-shell.h similarity index 100% rename from wayland/cc/xdg-shell.hh rename to wayland/cc/xdg-shell/xdg-shell.h From 46d015ee044cd6364138eece330ed46164716980 Mon Sep 17 00:00:00 2001 From: BulbyVR <26726264+TheDrawingCoder-Gamer@users.noreply.github.com> Date: Tue, 5 Dec 2023 21:07:19 -0500 Subject: [PATCH 10/95] Revert from public glue to private glue --- wayland/cc/xdg-shell/xdg-shell.c | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/wayland/cc/xdg-shell/xdg-shell.c b/wayland/cc/xdg-shell/xdg-shell.c index a91fb6ca..03826cdc 100644 --- a/wayland/cc/xdg-shell/xdg-shell.c +++ b/wayland/cc/xdg-shell/xdg-shell.c @@ -32,6 +32,16 @@ #include #include "wayland-util.h" +#ifndef __has_attribute +# define __has_attribute(x) 0 /* Compatibility with non-clang compilers. */ +#endif + +#if (__has_attribute(visibility) || defined(__GNUC__) && __GNUC__ >= 4) +#define WL_PRIVATE __attribute__ ((visibility("hidden"))) +#else +#define WL_PRIVATE +#endif + extern const struct wl_interface wl_output_interface; extern const struct wl_interface wl_seat_interface; extern const struct wl_interface wl_surface_interface; @@ -80,7 +90,7 @@ static const struct wl_message xdg_wm_base_events[] = { { "ping", "u", xdg_shell_types + 0 }, }; -WL_EXPORT const struct wl_interface xdg_wm_base_interface = { +WL_PRIVATE const struct wl_interface xdg_wm_base_interface = { "xdg_wm_base", 6, 4, xdg_wm_base_requests, 1, xdg_wm_base_events, @@ -99,7 +109,7 @@ static const struct wl_message xdg_positioner_requests[] = { { "set_parent_configure", "3u", xdg_shell_types + 0 }, }; -WL_EXPORT const struct wl_interface xdg_positioner_interface = { +WL_PRIVATE const struct wl_interface xdg_positioner_interface = { "xdg_positioner", 6, 10, xdg_positioner_requests, 0, NULL, @@ -117,7 +127,7 @@ static const struct wl_message xdg_surface_events[] = { { "configure", "u", xdg_shell_types + 0 }, }; -WL_EXPORT const struct wl_interface xdg_surface_interface = { +WL_PRIVATE const struct wl_interface xdg_surface_interface = { "xdg_surface", 6, 5, xdg_surface_requests, 1, xdg_surface_events, @@ -147,7 +157,7 @@ static const struct wl_message xdg_toplevel_events[] = { { "wm_capabilities", "5a", xdg_shell_types + 0 }, }; -WL_EXPORT const struct wl_interface xdg_toplevel_interface = { +WL_PRIVATE const struct wl_interface xdg_toplevel_interface = { "xdg_toplevel", 6, 14, xdg_toplevel_requests, 4, xdg_toplevel_events, @@ -165,7 +175,7 @@ static const struct wl_message xdg_popup_events[] = { { "repositioned", "3u", xdg_shell_types + 0 }, }; -WL_EXPORT const struct wl_interface xdg_popup_interface = { +WL_PRIVATE const struct wl_interface xdg_popup_interface = { "xdg_popup", 6, 3, xdg_popup_requests, 3, xdg_popup_events, From 84ec8e61586df8fd0a895546b56773c5b2a3bd6e Mon Sep 17 00:00:00 2001 From: BulbyVR <26726264+TheDrawingCoder-Gamer@users.noreply.github.com> Date: Tue, 5 Dec 2023 22:40:08 -0500 Subject: [PATCH 11/95] a whole lot of nothing --- examples/dashboard/java/PanelRendering.java | 2 +- wayland/cc/LayerGLWayland.cc | 6 +++++- wayland/cc/LayerRasterWayland.cc | 9 +++------ wayland/cc/ShmPool.cc | 3 ++- 4 files changed, 11 insertions(+), 9 deletions(-) diff --git a/examples/dashboard/java/PanelRendering.java b/examples/dashboard/java/PanelRendering.java index a40b4a0a..4d215ccf 100644 --- a/examples/dashboard/java/PanelRendering.java +++ b/examples/dashboard/java/PanelRendering.java @@ -35,7 +35,7 @@ else if (Platform.CURRENT == Platform.WINDOWS) else if (Platform.CURRENT == Platform.X11) layers = new String[] { "LayerGLSkija", "LayerRasterSkija" }; else if (Platform.CURRENT == Platform.WAYLAND) - layers = new String[] { "LayerRasterSkija" }; + layers = new String[] { "LayerGLSkija", "LayerRasterSkija" }; for (var layerName: layers) layersStatus.put(layerName, UNKNOWN); diff --git a/wayland/cc/LayerGLWayland.cc b/wayland/cc/LayerGLWayland.cc index bf0e6ab9..e013cc31 100644 --- a/wayland/cc/LayerGLWayland.cc +++ b/wayland/cc/LayerGLWayland.cc @@ -6,6 +6,7 @@ #include "WindowWayland.hh" #include #include +#include #include #include "ILayerWayland.hh" @@ -26,12 +27,15 @@ namespace jwm { void attach(WindowWayland* window) { eglBindAPI(EGL_OPENGL_API); fWindow = jwm::ref(window); + bool shouldReopen = false; if (window->_layer) { // HACK: close window and reopen window->close(); - window->init(); + shouldReopen = window->_visible; } fWindow->setLayer(this); + if (shouldReopen) + window->show(); if (_display == nullptr) { _display = eglGetPlatformDisplay(EGL_PLATFORM_WAYLAND_KHR, window->_windowManager.display, nullptr); diff --git a/wayland/cc/LayerRasterWayland.cc b/wayland/cc/LayerRasterWayland.cc index 8e603f1c..20124c97 100644 --- a/wayland/cc/LayerRasterWayland.cc +++ b/wayland/cc/LayerRasterWayland.cc @@ -15,7 +15,6 @@ namespace jwm { wl_buffer* _buffer = nullptr; uint8_t* _imageData = nullptr; ShmPool* _pool = nullptr; - VSync _vsync = VSYNC_ENABLED; bool _attached = false; LayerRaster() = default; @@ -36,6 +35,8 @@ namespace jwm { int bufSize = stride * height * 2; // TODO: better pool impl if (_pool) { + // TODO: don't mem leak : ) + // This memleaks - munmap causes skija to error out : / _pool->close(); } _pool = new ShmPool(fWindow->_windowManager.shm, bufSize); @@ -47,8 +48,7 @@ namespace jwm { _imageData = nullptr; } - // LSBFirst means Little endian : ) - // This will highly likely cause issues : ) + // : ) auto buf = _pool->createBuffer(0, width, height, stride, WL_SHM_FORMAT_ARGB8888); _buffer = buf.first; @@ -68,7 +68,6 @@ namespace jwm { } void swapBuffers() { - // : ) if (fWindow->_waylandWindow && _attached) { wl_surface_damage_buffer(fWindow->_waylandWindow, 0, 0, INT32_MAX, INT32_MAX); wl_surface_commit(fWindow->_waylandWindow); @@ -92,8 +91,6 @@ namespace jwm { } void setVsyncMode(VSync v) override { - // srsly, why do I need this - _vsync = v; } void attachBuffer() override { diff --git a/wayland/cc/ShmPool.cc b/wayland/cc/ShmPool.cc index b6c3a0de..353c1d3d 100644 --- a/wayland/cc/ShmPool.cc +++ b/wayland/cc/ShmPool.cc @@ -31,11 +31,12 @@ ShmPool::ShmPool(wl_shm* shm, size_t size): } ShmPool::~ShmPool() { - + close(); } void ShmPool::close() { wl_shm_pool_destroy(_pool); ::close(_fd); + // munmap(_rawData, _size); } void ShmPool::grow(size_t size) { From 9c543c09ba61a567cf4f4dea639e75504a505e76 Mon Sep 17 00:00:00 2001 From: BulbyVR <26726264+TheDrawingCoder-Gamer@users.noreply.github.com> Date: Thu, 7 Dec 2023 14:29:36 -0500 Subject: [PATCH 12/95] Start switch to libdecor (crashes) --- script/build_utils.py | 4 +- script/run.py | 3 +- shared/java/skija/LayerGLSkija.java | 2 +- wayland/CMakeLists.txt | 2 +- wayland/cc/ILayerWayland.hh | 1 + wayland/cc/LayerGLWayland.cc | 40 +++++++++++--- wayland/cc/LayerRasterWayland.cc | 8 +-- wayland/cc/Output.cc | 31 +++++++++++ wayland/cc/Output.hh | 24 ++++++++ wayland/cc/ShmPool.cc | 2 +- wayland/cc/WindowManagerWayland.cc | 85 ++++++++++++++++++++++------- wayland/cc/WindowManagerWayland.hh | 8 ++- wayland/cc/WindowWayland.cc | 72 +++++++++++++++++++++--- wayland/cc/WindowWayland.hh | 9 +++ 14 files changed, 242 insertions(+), 49 deletions(-) create mode 100644 wayland/cc/Output.cc create mode 100644 wayland/cc/Output.hh diff --git a/script/build_utils.py b/script/build_utils.py index f32d3383..1067db9f 100644 --- a/script/build_utils.py +++ b/script/build_utils.py @@ -121,7 +121,7 @@ def jar(target: str, *content: List[Tuple[str, str]]) -> str: @functools.lru_cache(maxsize=1) def lombok(): - return fetch_maven('org.projectlombok', 'lombok', '1.18.22') + return fetch_maven('org.projectlombok', 'lombok', '1.18.30') def delombok(dirs: List[str], target: str, classpath: List[str] = [], modulepath: List[str] = []): sources = files(*[dir + "/**/*.java" for dir in dirs]) @@ -248,4 +248,4 @@ def fetch(path, data = None): "stagedRepositoryIds":[repo_id] }}) print('Success! Just released', repo_id) - return 0 \ No newline at end of file + return 0 diff --git a/script/run.py b/script/run.py index e90cbe7e..4ba67860 100755 --- a/script/run.py +++ b/script/run.py @@ -47,8 +47,7 @@ def main(): if args.skija_dir: classpath += [ - skija_dir + '/platform/build', - skija_dir + '/platform/target/classes', + skija_dir + '/platform/target/' + build_utils.system + '-' + build_utils.arch + '/classes', ] elif args.skija_platform_jar: classpath += [ diff --git a/shared/java/skija/LayerGLSkija.java b/shared/java/skija/LayerGLSkija.java index 9862a675..b543cd2f 100644 --- a/shared/java/skija/LayerGLSkija.java +++ b/shared/java/skija/LayerGLSkija.java @@ -82,4 +82,4 @@ public void close() { super.close(); } -} \ No newline at end of file +} diff --git a/wayland/CMakeLists.txt b/wayland/CMakeLists.txt index d323fd85..04947745 100644 --- a/wayland/CMakeLists.txt +++ b/wayland/CMakeLists.txt @@ -29,8 +29,8 @@ find_library(WAYLAND_CURSOR wayland-cursor) find_library(XKBCOMMON xkbcommon) find_library(EGL EGL) find_library(WAYLAND_EGL wayland-egl) +# find_library(OPENGL_ES2 GLESv2) find_package(OpenGL REQUIRED) - set(JAVA_HOME $ENV{JAVA_HOME}) if (NOT JAVA_HOME) file(GLOB JAVA_HOMES "/usr/lib/jvm/java-*") diff --git a/wayland/cc/ILayerWayland.hh b/wayland/cc/ILayerWayland.hh index 45a0dafa..d05ee848 100644 --- a/wayland/cc/ILayerWayland.hh +++ b/wayland/cc/ILayerWayland.hh @@ -6,5 +6,6 @@ namespace jwm { class ILayerWayland: public ILayer { public: virtual void attachBuffer() = 0; + virtual void swapBuffers() = 0; }; } diff --git a/wayland/cc/LayerGLWayland.cc b/wayland/cc/LayerGLWayland.cc index e013cc31..27f6c93c 100644 --- a/wayland/cc/LayerGLWayland.cc +++ b/wayland/cc/LayerGLWayland.cc @@ -4,10 +4,11 @@ #include "impl/Library.hh" #include "impl/RefCounted.hh" #include "WindowWayland.hh" +#include #include #include +#include #include -#include #include "ILayerWayland.hh" namespace jwm { @@ -16,6 +17,7 @@ namespace jwm { public: WindowWayland* fWindow; wl_egl_window* _eglWindow = nullptr; + wl_region* _region = nullptr; EGLContext _context = nullptr; EGLDisplay _display = nullptr; EGLSurface _surface = nullptr; @@ -37,29 +39,41 @@ namespace jwm { if (shouldReopen) window->show(); if (_display == nullptr) { - _display = eglGetPlatformDisplay(EGL_PLATFORM_WAYLAND_KHR, window->_windowManager.display, nullptr); + _display = eglGetDisplay(window->_windowManager.display); eglInitialize(_display, nullptr, nullptr); } if (_context == nullptr) { EGLint attrList[] = { + EGL_SURFACE_TYPE, EGL_WINDOW_BIT, + EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT, EGL_ALPHA_SIZE, 8, EGL_BLUE_SIZE, 8, EGL_GREEN_SIZE, 8, EGL_RED_SIZE, 8, EGL_NONE }; + EGLint contextAttr[] = { + EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE, EGL_NONE + }; EGLint numConfig; - eglChooseConfig(_display, attrList, &_config, 1, &numConfig); + if ( ( eglGetConfigs(_display, nullptr, 0, &numConfig) != EGL_TRUE) || (numConfig == 0) ) { + throw std::runtime_error("No configuration"); + } + if ( ( eglChooseConfig(_display, attrList, &_config, 1, &numConfig) != EGL_TRUE) || (numConfig != 1)) { + throw std::runtime_error("No/Amibguous configuration"); + } // :troll: _context = eglCreateContext(_display, _config, EGL_NO_CONTEXT, - nullptr); + contextAttr); + if ( _context == EGL_NO_CONTEXT ) { + throw std::runtime_error("Couldn't make context"); + } } - makeCurrentForced(); } @@ -80,7 +94,7 @@ namespace jwm { wl_egl_window_resize(_eglWindow, width, height, 0, 0); } - void swapBuffers() { + void swapBuffers() override { eglSwapBuffers(_display, _surface); } @@ -99,8 +113,17 @@ namespace jwm { void attachBuffer() override { if (fWindow && fWindow->_waylandWindow) { if (!_eglWindow) { + // _region = wl_compositor_create_region(fWindow->_windowManager.compositor); + // wl_region_add(_region, 0, 0, fWindow->getWidth(), fWindow->getHeight()); + // wl_surface_set_opaque_region(fWindow->_waylandWindow, _region); + _eglWindow = wl_egl_window_create(fWindow->_waylandWindow, fWindow->getWidth(), fWindow->getHeight()); - _surface = eglCreatePlatformWindowSurface(_display, _config, _eglWindow, nullptr); + _surface = eglCreateWindowSurface(_display, _config, _eglWindow, nullptr); + + if ( _eglWindow == EGL_NO_SURFACE ) { + throw std::runtime_error("couldn't get surface"); + } + makeCurrentForced(); } } } @@ -120,9 +143,12 @@ extern "C" JNIEXPORT void JNICALL Java_io_github_humbleui_jwm_LayerGL__1nAttach (JNIEnv* env, jobject obj, jobject windowObj) { try { jwm::LayerGL* instance = reinterpret_cast(jwm::classes::Native::fromJava(env, obj)); + printf("instance good : )\n"); jwm::WindowWayland* window = reinterpret_cast(jwm::classes::Native::fromJava(env, windowObj)); + printf("window good : )\n"); instance->attach(window); } catch (const std::exception& e) { + printf("%s\n", e.what()); jwm::classes::Throwable::throwLayerNotSupportedException(env, "Failed to init OpenGL"); } } diff --git a/wayland/cc/LayerRasterWayland.cc b/wayland/cc/LayerRasterWayland.cc index 20124c97..4198d675 100644 --- a/wayland/cc/LayerRasterWayland.cc +++ b/wayland/cc/LayerRasterWayland.cc @@ -5,7 +5,7 @@ #include "impl/RefCounted.hh" #include "WindowWayland.hh" #include "ShmPool.hh" -#include +#include namespace jwm { class LayerRaster: public RefCounted, public ILayerWayland { @@ -47,9 +47,8 @@ namespace jwm { wl_buffer_destroy(_buffer); _imageData = nullptr; } - // : ) - auto buf = _pool->createBuffer(0, width, height, stride, WL_SHM_FORMAT_ARGB8888); + auto buf = _pool->createBuffer(0, width, height, stride, WL_SHM_FORMAT_XRGB8888); _buffer = buf.first; _imageData = buf.second; @@ -67,7 +66,7 @@ namespace jwm { return _width * sizeof(uint32_t); } - void swapBuffers() { + void swapBuffers() override { if (fWindow->_waylandWindow && _attached) { wl_surface_damage_buffer(fWindow->_waylandWindow, 0, 0, INT32_MAX, INT32_MAX); wl_surface_commit(fWindow->_waylandWindow); @@ -97,6 +96,7 @@ namespace jwm { if (fWindow) { if (fWindow->_waylandWindow) { wl_surface_attach(fWindow->_waylandWindow, _buffer, 0, 0); + wl_surface_damage_buffer(fWindow->_waylandWindow, 0, 0, INT32_MAX, INT32_MAX); wl_surface_commit(fWindow->_waylandWindow); _attached = true; } diff --git a/wayland/cc/Output.cc b/wayland/cc/Output.cc new file mode 100644 index 00000000..356aefe4 --- /dev/null +++ b/wayland/cc/Output.cc @@ -0,0 +1,31 @@ +#include "Output.hh" + +using namespace jwm; + +wl_output_listener Output::_outputListener = { + .geometry = Output::outputGeometry, + .mode = Output::outputMode, + .done = Output::outputDone, + .scale = Output::outputScale, + .name = Output::outputName, + .description = Output::outputDescription +}; +Output::Output(wl_output* output, uint32_t name): + _output(output), + _name(name) + { + wl_output_add_listener(output, &_outputListener, this); + } +void Output::outputGeometry(void* data, wl_output* output, int x, int y, int physWidth, int physHeight, + int subPixel, const char* make, const char* model, int transform) {} +void Output::outputMode(void* data, wl_output* output, uint32_t flags, int width, int height, int refresh) {} +void Output::outputDone(void* data, wl_output* output) {} +void Output::outputScale(void* data, wl_output* output, int factor) { + Output* self = reinterpret_cast(data); + self->scale = factor; +} +void Output::outputName(void* data, wl_output* output, const char* name) { + +} +void Output::outputDescription(void* data, wl_output* output, const char* desc) {} + diff --git a/wayland/cc/Output.hh b/wayland/cc/Output.hh new file mode 100644 index 00000000..1c6763b1 --- /dev/null +++ b/wayland/cc/Output.hh @@ -0,0 +1,24 @@ +#pragma once + +#include +#include + +namespace jwm { + class Output { + public: + Output(wl_output* output, uint32_t name); + + wl_output* _output; + uint32_t _name; + int scale = 1; + + static wl_output_listener _outputListener; + static void outputGeometry(void* data, wl_output* output, int x, int y, int physWidth, int physHeight, + int subPixel, const char* make, const char* model, int transform); + static void outputMode(void* data, wl_output* output, uint32_t flags, int width, int height, int refresh); + static void outputDone(void* data, wl_output* output); + static void outputScale(void* data, wl_output* output, int factor); + static void outputName(void* data, wl_output* output, const char* name); + static void outputDescription(void* data, wl_output* output, const char* desc); + }; +} diff --git a/wayland/cc/ShmPool.cc b/wayland/cc/ShmPool.cc index 353c1d3d..fa429540 100644 --- a/wayland/cc/ShmPool.cc +++ b/wayland/cc/ShmPool.cc @@ -25,9 +25,9 @@ ShmPool::ShmPool(wl_shm* shm, size_t size): // why : ( throw std::system_error(EIO, std::generic_category(), "Couldn't allocate buffer"); } - _pool = wl_shm_create_pool(shm, _fd, size); _rawData = (uint8_t*)mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_SHARED, _fd, 0); + _pool = wl_shm_create_pool(shm, _fd, size); } ShmPool::~ShmPool() { diff --git a/wayland/cc/WindowManagerWayland.cc b/wayland/cc/WindowManagerWayland.cc index 50b88e41..6ae31f55 100644 --- a/wayland/cc/WindowManagerWayland.cc +++ b/wayland/cc/WindowManagerWayland.cc @@ -16,6 +16,9 @@ #include "Log.hh" #include #include +#include "Output.hh" +#include +#include using namespace jwm; @@ -34,13 +37,16 @@ xdg_wm_base_listener WindowManagerWayland::_xdgWmBaseListener = { .ping = WindowManagerWayland::xdgWmBasePing }; +libdecor_interface WindowManagerWayland::_decorInterface = { + .error = WindowManagerWayland::libdecorError +}; WindowManagerWayland::WindowManagerWayland(): display(wl_display_connect(nullptr)) { registry = wl_display_get_registry(display); wl_registry_add_listener(registry, &_registryListener, this); wl_display_roundtrip(display); - + if (!(shm && xdgShell && compositor && deviceManager && seat)) { @@ -50,6 +56,7 @@ WindowManagerWayland::WindowManagerWayland(): } + decorCtx = libdecor_new(display, &_decorInterface); // frankly `this` is not needed here, but it needs a pointer anyway and it's // good to have consistentcy. @@ -107,7 +114,11 @@ void WindowManagerWayland::runLoop() { notifyFD = pipes[1]; fcntl(pipes[1], F_SETFL, O_NONBLOCK); // notifyLoop no blockie : ) struct pollfd wayland_out = {.fd=wl_display_get_fd(display),.events=POLLOUT}; - struct pollfd ps[] = {{.fd=wl_display_get_fd(display), .events=POLLIN}, {.fd=pipes[0], .events=POLLIN}}; + struct pollfd ps[] = { + {.fd=wl_display_get_fd(display), .events=POLLIN}, + {.fd=pipes[0], .events=POLLIN}, + {.fd=libdecor_get_fd(decorCtx), .events=POLLIN} + }; // who be out here running they loop while (_runLoop) { if (jwm::classes::Throwable::exceptionThrown(app.getJniEnv())) @@ -138,7 +149,7 @@ void WindowManagerWayland::runLoop() { } // block until event : ) - if (poll(&ps[0], 2, -1) < 0) { + if (poll(&ps[0], 3, -1) < 0) { printf("error with pipe\n"); break; } @@ -151,6 +162,9 @@ void WindowManagerWayland::runLoop() { } else { wl_display_cancel_read(display); } + if (ps[2].revents & POLLIN) { + libdecor_dispatch(decorCtx, -1); + } wl_display_dispatch_pending(display); notifyBool.store(false); } @@ -161,6 +175,11 @@ void WindowManagerWayland::runLoop() { } +void WindowManagerWayland::libdecorError(libdecor* context, enum libdecor_error error, const char* message) { + // ??? + fprintf(stderr, "Caught error (%d): %s\n", error, message); + throw std::runtime_error("lib decor error > : ("); +} void WindowManagerWayland::_processCallbacks() { { // process ui thread callbacks @@ -212,10 +231,22 @@ void WindowManagerWayland::registryHandleGlobal(void* data, wl_registry *registr } else if (strcmp(interface, wl_seat_interface.name) == 0) { self->seat = (wl_seat*)wl_registry_bind(registry, name, &wl_seat_interface, 1); + } else if (strcmp(interface, wl_output_interface.name) == 0) { + wl_output* output = (wl_output*)wl_registry_bind(registry, name, + &wl_output_interface, 2); + Output* good = new Output(output, name); + self->outputs.push_back(good); } } void WindowManagerWayland::registryHandleGlobalRemove(void* data, wl_registry *registry, uint32_t name) { - // i do nothing : ) + auto self = reinterpret_cast(data); + for (std::list::iterator it = self->outputs.begin(); it != self->outputs.end();) { + if ((*it)->_name == name) { + self->outputs.erase(it); + break; + } + ++it; + } } void WindowManagerWayland::pointerHandleEnter(void* data, wl_pointer* pointer, uint32_t serial, @@ -235,11 +266,11 @@ void WindowManagerWayland::pointerHandleLeave(void* data, wl_pointer* pointer, u void WindowManagerWayland::pointerHandleMotion(void* data, wl_pointer* pointer, uint32_t time, wl_fixed_t surface_x, wl_fixed_t surface_y) { WindowManagerWayland* self = (WindowManagerWayland*)data; - self->lastMousePosX = surface_x; - self->lastMousePosY = surface_y; if (self->focusedSurface) { - ::WindowWayland* window = self->_nativeWindowToMy.find(self->focusedSurface)->second; - self->mouseUpdate(window, wl_fixed_to_int(surface_x), wl_fixed_to_int(surface_y), self->mouseMask); + ::WindowWayland* window = reinterpret_cast<::WindowWayland*>(wl_surface_get_user_data(self->focusedSurface)); + // God is dead if window is null + if (window) + self->mouseUpdate(window, wl_fixed_to_int(surface_x), wl_fixed_to_int(surface_y), self->mouseMask); } } @@ -247,19 +278,20 @@ void WindowManagerWayland::pointerHandleButton(void* data, wl_pointer* pointer, uint32_t serial, uint32_t time, uint32_t button, uint32_t state) { using namespace classes; WindowManagerWayland* self = (WindowManagerWayland*)data; + if (!self->focusedSurface) return; if (state == 0) { // release switch (button) { // primary - case 0x110: + case BTN_LEFT: self->mouseMask &= ~0x100; break; // secondary - case 0x111: + case BTN_RIGHT: self->mouseMask &= ~0x400; break; // middle - case 0x112: + case BTN_MIDDLE: self->mouseMask &= ~0x200; break; default: @@ -278,22 +310,23 @@ void WindowManagerWayland::pointerHandleButton(void* data, wl_pointer* pointer, jwm::KeyWayland::getModifiers() ) ); - WindowWayland* window = self->_nativeWindowToMy.find(self->focusedSurface)->second; - window->dispatch(eventButton.get()); + WindowWayland* window = reinterpret_cast(wl_surface_get_user_data(self->focusedSurface)); + if (window) + window->dispatch(eventButton.get()); } } else { // down switch (button) { // primary - case 0x110: + case BTN_LEFT: self->mouseMask |= 0x100; break; // secondary - case 0x111: + case BTN_RIGHT: self->mouseMask |= 0x400; break; // middle - case 0x112: + case BTN_MIDDLE: self->mouseMask |= 0x200; break; default: @@ -312,8 +345,10 @@ void WindowManagerWayland::pointerHandleButton(void* data, wl_pointer* pointer, jwm::KeyWayland::getModifiers() ) ); - WindowWayland* window = self->_nativeWindowToMy.find(self->focusedSurface)->second; - window->dispatch(eventButton.get()); + // me when this stuff is NULL : ( + WindowWayland* window = reinterpret_cast(wl_surface_get_user_data(self->focusedSurface)); + if (window) + window->dispatch(eventButton.get()); } } } @@ -387,9 +422,14 @@ std::vector WindowManagerWayland::getClipboardFormats() { } void WindowManagerWayland::mouseUpdate(WindowWayland* myWindow, uint32_t x, uint32_t y, uint32_t mask) { using namespace classes; - + if (!myWindow) + return; // impl me : ) + if (lastMousePosX == x && lastMousePosY == y) return; + lastMousePosX = x; + lastMousePosY = y; int movementX = 0, movementY = 0; + printf("mouse update: %i %i\n", x, y); jwm::JNILocal eventMove( app.getJniEnv(), EventMouseMove::make(app.getJniEnv(), @@ -399,10 +439,13 @@ void WindowManagerWayland::mouseUpdate(WindowWayland* myWindow, uint32_t x, uint movementY, jwm::MouseButtonWayland::fromNativeMask(mask), // impl me! - jwm::KeyWayland::getModifiersFromMask(0) + jwm::KeyWayland::getModifiers() ) ); - myWindow->dispatch(eventMove.get()); + auto foo = eventMove.get(); + printf("??? %x\n", foo); + myWindow->dispatch(foo); + printf("??????\n"); } jwm::ByteBuf WindowManagerWayland::getClipboardContents(const std::string& type) { auto nativeHandle = _nativeWindowToMy.begin()->first; diff --git a/wayland/cc/WindowManagerWayland.hh b/wayland/cc/WindowManagerWayland.hh index a5ecf0ec..4349347f 100644 --- a/wayland/cc/WindowManagerWayland.hh +++ b/wayland/cc/WindowManagerWayland.hh @@ -14,6 +14,9 @@ #include #include #include "xdg-shell/xdg-shell.h" +#include +#include "Output.hh" +#include namespace jwm { class WindowWayland; @@ -76,6 +79,8 @@ namespace jwm { static xdg_wm_base_listener _xdgWmBaseListener; static void xdgWmBasePing(void* data, xdg_wm_base* base, uint32_t serial); + static libdecor_interface _decorInterface; + static void libdecorError(libdecor* context, enum libdecor_error error, const char* message); ByteBuf getClipboardContents(const std::string& type); std::vector getClipboardFormats(); @@ -88,7 +93,8 @@ namespace jwm { wl_data_device_manager* deviceManager = nullptr; wl_seat* seat = nullptr; wl_pointer* pointer = nullptr; - + libdecor* decorCtx = nullptr; + std::list outputs; // XVisualInfo* x11VisualInfo; // XSetWindowAttributes x11SWA; diff --git a/wayland/cc/WindowWayland.cc b/wayland/cc/WindowWayland.cc index ab21dfc4..1dec4e34 100644 --- a/wayland/cc/WindowWayland.cc +++ b/wayland/cc/WindowWayland.cc @@ -34,12 +34,16 @@ wl_output_listener WindowWayland::_outputListener = { .name = WindowWayland::outputName, .description = WindowWayland::outputDescription }; +libdecor_frame_interface WindowWayland::_libdecorFrameInterface = { + .configure = WindowWayland::decorFrameConfigure, + .close = WindowWayland::decorFrameClose, + .commit = WindowWayland::decorFrameCommit, + .dismiss_popup = WindowWayland::decorFrameDismissPopup +}; + WindowWayland::WindowWayland(JNIEnv* env, WindowManagerWayland& windowManager): jwm::Window(env), - _windowManager(windowManager), - // HACK: have a default height bc some compositors won't hecking tell me :sob: - _width(100), - _height(100) + _windowManager(windowManager) { } @@ -139,12 +143,16 @@ void WindowWayland::show() { _waylandWindow = wl_compositor_create_surface(_windowManager.compositor); wl_surface_add_listener(_waylandWindow, &_surfaceListener, this); + // unsure if listener data and user data are the same, so i do this for safety : ) + wl_surface_set_user_data(_waylandWindow, this); + + // xdgSurface = xdg_wm_base_get_xdg_surface(_windowManager.xdgShell, _waylandWindow); + // xdg_surface_add_listener(xdgSurface, &_xdgSurfaceListener, this); - xdgSurface = xdg_wm_base_get_xdg_surface(_windowManager.xdgShell, _waylandWindow); - xdg_surface_add_listener(xdgSurface, &_xdgSurfaceListener, this); + // xdgToplevel = xdg_surface_get_toplevel(xdgSurface); + // xdg_toplevel_add_listener(xdgToplevel, &_xdgToplevelListener, this); - xdgToplevel = xdg_surface_get_toplevel(xdgSurface); - xdg_toplevel_add_listener(xdgToplevel, &_xdgToplevelListener, this); + _frame = libdecor_decorate(_windowManager.decorCtx, _waylandWindow, &_libdecorFrameInterface, this); _windowManager.registerWindow(this); wl_surface_commit(_waylandWindow); } @@ -185,7 +193,18 @@ void jwm::WindowWayland::setCursor(jwm::MouseCursor cursor) { // what do??? void jwm::WindowWayland::surfaceEnter(void* data, wl_surface* surface, wl_output* output) { // doesn't crash : ) - wl_output_add_listener(output, &_outputListener, data); + WindowWayland* self = reinterpret_cast(data); + + Output* good; + + for (auto o : self->_windowManager.outputs) { + if (o->_output == output) { + self->_scale = o->scale; + wl_surface_set_buffer_scale(surface, o->scale); + self->_adaptSize(self->_width, self->_height); + break; + } + } } void jwm::WindowWayland::surfaceLeave(void* data, wl_surface* surface, wl_output* output) {} void jwm::WindowWayland::surfacePreferredBufferScale(void* data, wl_surface* surface, int factor) { @@ -262,6 +281,41 @@ void jwm::WindowWayland::outputScale(void* data, wl_output* output, int factor) } void jwm::WindowWayland::outputName(void* data, wl_output* output, const char* name) {} void jwm::WindowWayland::outputDescription(void* data, wl_output* output, const char* desc) {} + +void jwm::WindowWayland::decorFrameConfigure(libdecor_frame* frame, libdecor_configuration* configuration, + void *userData) { + auto self = reinterpret_cast(userData); + int width = 0, height = 0; + + libdecor_configuration_get_content_size(configuration, frame, &width, &height); + + width = (width == 0) ? self->_floatingWidth : width; + height = (height == 0) ? self->_floatingHeight : height; + + libdecor_state* state = libdecor_state_new(width, height); + libdecor_frame_commit(frame, state, configuration); + libdecor_state_free(state); + + if (libdecor_frame_is_floating(frame)) { + self->_floatingWidth = width; + self->_floatingHeight = height; + } + + if (self->_layer) { + self->_layer->attachBuffer(); + } + self->_adaptSize(width, height); +} +void jwm::WindowWayland::decorFrameClose(libdecor_frame* frame, void* userData) { + WindowWayland* self = reinterpret_cast(userData); + self->dispatch(classes::EventWindowCloseRequest::kInstance); +} +void jwm::WindowWayland::decorFrameCommit(libdecor_frame* frame, void* userData) { + WindowWayland* self = reinterpret_cast(userData); + if (self->_layer) + self->dispatch(classes::EventFrame::kInstance); +} +void jwm::WindowWayland::decorFrameDismissPopup(libdecor_frame* frame, const char* seatName, void* userData) {} void jwm::WindowWayland::_adaptSize(int newWidth, int newHeight) { _width = newWidth; _height = newHeight; diff --git a/wayland/cc/WindowWayland.hh b/wayland/cc/WindowWayland.hh index 80723915..db9dff9a 100644 --- a/wayland/cc/WindowWayland.hh +++ b/wayland/cc/WindowWayland.hh @@ -75,6 +75,12 @@ namespace jwm { static void outputName(void* data, wl_output* output, const char* name); static void outputDescription(void* data, wl_output* output, const char* desc); + + static void decorFrameConfigure(libdecor_frame* frame, libdecor_configuration* config, void* userData); + static void decorFrameClose(libdecor_frame* frame, void* userData); + static void decorFrameCommit(libdecor_frame* frame, void* userData); + static void decorFrameDismissPopup(libdecor_frame* frame, const char* seatName, void* userData); + void _adaptSize(int newWidth, int newHeight); @@ -87,6 +93,8 @@ namespace jwm { int _newHeight = -1; int _WM_ADD = 1L; int _WM_REMOVE = 0L; + int _floatingWidth = 400; + int _floatingHeight = 400; bool _canMinimize = false; bool _canMaximize = false; bool _canFullscreen = false; @@ -106,6 +114,7 @@ namespace jwm { static xdg_toplevel_listener _xdgToplevelListener; static wl_output_listener _outputListener; + static libdecor_frame_interface _libdecorFrameInterface; }; } From 7aa2820d97abafa76c8743f5fff0a3029b11d6c3 Mon Sep 17 00:00:00 2001 From: BulbyVR <26726264+TheDrawingCoder-Gamer@users.noreply.github.com> Date: Thu, 7 Dec 2023 18:24:35 -0500 Subject: [PATCH 13/95] Doesn't crash (doesn't render well) --- wayland/cc/LayerGLWayland.cc | 32 ++++++++--------- wayland/cc/WindowManagerWayland.cc | 56 ++++++++---------------------- wayland/cc/WindowManagerWayland.hh | 2 ++ wayland/cc/WindowWayland.cc | 42 +++++++++++++--------- wayland/cc/WindowWayland.hh | 4 ++- 5 files changed, 60 insertions(+), 76 deletions(-) diff --git a/wayland/cc/LayerGLWayland.cc b/wayland/cc/LayerGLWayland.cc index 27f6c93c..9d1cf492 100644 --- a/wayland/cc/LayerGLWayland.cc +++ b/wayland/cc/LayerGLWayland.cc @@ -27,28 +27,21 @@ namespace jwm { virtual ~LayerGL() = default; void attach(WindowWayland* window) { - eglBindAPI(EGL_OPENGL_API); fWindow = jwm::ref(window); - bool shouldReopen = false; - if (window->_layer) { - // HACK: close window and reopen - window->close(); - shouldReopen = window->_visible; - } fWindow->setLayer(this); - if (shouldReopen) - window->show(); if (_display == nullptr) { _display = eglGetDisplay(window->_windowManager.display); eglInitialize(_display, nullptr, nullptr); } + if ( eglBindAPI(EGL_OPENGL_API) == EGL_FALSE) { + throw new std::runtime_error("Cannot bind EGL Api"); + } if (_context == nullptr) { EGLint attrList[] = { EGL_SURFACE_TYPE, EGL_WINDOW_BIT, EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT, - EGL_ALPHA_SIZE, 8, EGL_BLUE_SIZE, 8, EGL_GREEN_SIZE, 8, EGL_RED_SIZE, 8, @@ -74,7 +67,7 @@ namespace jwm { } } - + makeCurrentForced(); } void setVsyncMode(VSync v) override { @@ -88,7 +81,7 @@ namespace jwm { glClear(GL_STENCIL_BUFFER_BIT | GL_COLOR_BUFFER_BIT); // ??? - glViewport(0, 0, width, height); + // glViewport(0, 0, width, height); // God is dead if _eglWindow is null if (_eglWindow) wl_egl_window_resize(_eglWindow, width, height, 0, 0); @@ -99,7 +92,16 @@ namespace jwm { } void close() override { + eglMakeCurrent(_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + if (_surface) { + eglDestroySurface(_display, _surface); + } + if (_eglWindow) { + wl_egl_window_destroy(_eglWindow); + } eglDestroyContext(_display, _context); + eglTerminate(_display); + jwm::unref(&fWindow); } @@ -116,15 +118,15 @@ namespace jwm { // _region = wl_compositor_create_region(fWindow->_windowManager.compositor); // wl_region_add(_region, 0, 0, fWindow->getWidth(), fWindow->getHeight()); // wl_surface_set_opaque_region(fWindow->_waylandWindow, _region); - + printf("%i %i\n", fWindow->getWidth(), fWindow->getHeight()); _eglWindow = wl_egl_window_create(fWindow->_waylandWindow, fWindow->getWidth(), fWindow->getHeight()); _surface = eglCreateWindowSurface(_display, _config, _eglWindow, nullptr); if ( _eglWindow == EGL_NO_SURFACE ) { throw std::runtime_error("couldn't get surface"); } - makeCurrentForced(); } + makeCurrentForced(); } } }; @@ -143,9 +145,7 @@ extern "C" JNIEXPORT void JNICALL Java_io_github_humbleui_jwm_LayerGL__1nAttach (JNIEnv* env, jobject obj, jobject windowObj) { try { jwm::LayerGL* instance = reinterpret_cast(jwm::classes::Native::fromJava(env, obj)); - printf("instance good : )\n"); jwm::WindowWayland* window = reinterpret_cast(jwm::classes::Native::fromJava(env, windowObj)); - printf("window good : )\n"); instance->attach(window); } catch (const std::exception& e) { printf("%s\n", e.what()); diff --git a/wayland/cc/WindowManagerWayland.cc b/wayland/cc/WindowManagerWayland.cc index 6ae31f55..2df5df50 100644 --- a/wayland/cc/WindowManagerWayland.cc +++ b/wayland/cc/WindowManagerWayland.cc @@ -115,41 +115,16 @@ void WindowManagerWayland::runLoop() { fcntl(pipes[1], F_SETFL, O_NONBLOCK); // notifyLoop no blockie : ) struct pollfd wayland_out = {.fd=wl_display_get_fd(display),.events=POLLOUT}; struct pollfd ps[] = { - {.fd=wl_display_get_fd(display), .events=POLLIN}, + {.fd=libdecor_get_fd(decorCtx), .events=POLLIN}, {.fd=pipes[0], .events=POLLIN}, - {.fd=libdecor_get_fd(decorCtx), .events=POLLIN} }; // who be out here running they loop while (_runLoop) { if (jwm::classes::Throwable::exceptionThrown(app.getJniEnv())) _runLoop = false; _processCallbacks(); - while(wl_display_prepare_read(display) != 0) - wl_display_dispatch_pending(display); - // adapted from Waylock - while (true) { - int res = wl_display_flush(display); - if (res >= 0) - break; - - switch (errno) { - case EPIPE: - wl_display_read_events(display); - throw std::system_error(errno, std::generic_category(), "connection to wayland server unexpectedly terminated"); - break; - case EAGAIN: - if (poll(&wayland_out, 1, -1) < 0) { - throw std::system_error(EPIPE, std::generic_category(), "poll failed"); - } - break; - default: - throw std::system_error(errno, std::generic_category(), "failed to flush requests"); - break; - } - - } // block until event : ) - if (poll(&ps[0], 3, -1) < 0) { + if (poll(&ps[0], 2, -1) < 0) { printf("error with pipe\n"); break; } @@ -157,15 +132,8 @@ void WindowManagerWayland::runLoop() { while (read(pipes[0], buf, sizeof(buf)) == sizeof(buf)) { } } if (ps[0].revents & POLLIN) { - // WHY IN THE WORLD IS THIS CRASHING - wl_display_read_events(display); - } else { - wl_display_cancel_read(display); - } - if (ps[2].revents & POLLIN) { libdecor_dispatch(decorCtx, -1); } - wl_display_dispatch_pending(display); notifyBool.store(false); } @@ -248,13 +216,20 @@ void WindowManagerWayland::registryHandleGlobalRemove(void* data, wl_registry *r ++it; } } - +WindowWayland* WindowManagerWayland::getWindowForNative(wl_surface* surface) { + WindowWayland* myWindow = nullptr; + auto it = _nativeWindowToMy.find(surface); + if (it != _nativeWindowToMy.end()) + myWindow = it->second; + return myWindow; +} void WindowManagerWayland::pointerHandleEnter(void* data, wl_pointer* pointer, uint32_t serial, wl_surface *surface, wl_fixed_t surface_x, wl_fixed_t surface_y) { WindowManagerWayland* self = (WindowManagerWayland*)data; wl_cursor_image* image = self->_cursors[static_cast(jwm::MouseCursor::ARROW)]; wl_pointer_set_cursor(pointer, serial, self->cursorSurface, image->hotspot_x, image->hotspot_y); - self->focusedSurface = surface; + if (self->getWindowForNative(surface)) + self->focusedSurface = surface; } void WindowManagerWayland::pointerHandleLeave(void* data, wl_pointer* pointer, uint32_t serial, wl_surface *surface) { @@ -267,7 +242,7 @@ void WindowManagerWayland::pointerHandleMotion(void* data, wl_pointer* pointer, uint32_t time, wl_fixed_t surface_x, wl_fixed_t surface_y) { WindowManagerWayland* self = (WindowManagerWayland*)data; if (self->focusedSurface) { - ::WindowWayland* window = reinterpret_cast<::WindowWayland*>(wl_surface_get_user_data(self->focusedSurface)); + ::WindowWayland* window = self->getWindowForNative(self->focusedSurface); // God is dead if window is null if (window) self->mouseUpdate(window, wl_fixed_to_int(surface_x), wl_fixed_to_int(surface_y), self->mouseMask); @@ -310,7 +285,7 @@ void WindowManagerWayland::pointerHandleButton(void* data, wl_pointer* pointer, jwm::KeyWayland::getModifiers() ) ); - WindowWayland* window = reinterpret_cast(wl_surface_get_user_data(self->focusedSurface)); + WindowWayland* window = self->getWindowForNative(self->focusedSurface); if (window) window->dispatch(eventButton.get()); } @@ -346,7 +321,7 @@ void WindowManagerWayland::pointerHandleButton(void* data, wl_pointer* pointer, ) ); // me when this stuff is NULL : ( - WindowWayland* window = reinterpret_cast(wl_surface_get_user_data(self->focusedSurface)); + WindowWayland* window = self->getWindowForNative(self->focusedSurface); if (window) window->dispatch(eventButton.get()); } @@ -429,7 +404,6 @@ void WindowManagerWayland::mouseUpdate(WindowWayland* myWindow, uint32_t x, uint lastMousePosX = x; lastMousePosY = y; int movementX = 0, movementY = 0; - printf("mouse update: %i %i\n", x, y); jwm::JNILocal eventMove( app.getJniEnv(), EventMouseMove::make(app.getJniEnv(), @@ -443,9 +417,7 @@ void WindowManagerWayland::mouseUpdate(WindowWayland* myWindow, uint32_t x, uint ) ); auto foo = eventMove.get(); - printf("??? %x\n", foo); myWindow->dispatch(foo); - printf("??????\n"); } jwm::ByteBuf WindowManagerWayland::getClipboardContents(const std::string& type) { auto nativeHandle = _nativeWindowToMy.begin()->first; diff --git a/wayland/cc/WindowManagerWayland.hh b/wayland/cc/WindowManagerWayland.hh index 4349347f..eb872036 100644 --- a/wayland/cc/WindowManagerWayland.hh +++ b/wayland/cc/WindowManagerWayland.hh @@ -55,6 +55,8 @@ namespace jwm { void notifyLoop(); void enqueueTask(const std::function& task); + WindowWayland* getWindowForNative(wl_surface* surface); + static wl_registry_listener _registryListener; static void registryHandleGlobal(void* data, wl_registry *registry, diff --git a/wayland/cc/WindowWayland.cc b/wayland/cc/WindowWayland.cc index 1dec4e34..a9dbae2b 100644 --- a/wayland/cc/WindowWayland.cc +++ b/wayland/cc/WindowWayland.cc @@ -75,7 +75,11 @@ void WindowWayland::close() { xdg_toplevel_destroy(xdgToplevel); } xdgToplevel = nullptr; - + if (_frame) { + libdecor_frame_unref(_frame); + } + _frame = nullptr; + _configured = false; } void WindowWayland::maximize() { // impl me :) @@ -126,11 +130,11 @@ int WindowWayland::getTop() { } int WindowWayland::getWidth() { - return _width; + return _width <= 0 ? _floatingWidth : _width; } int WindowWayland::getHeight() { - return _height; + return _height <= 0 ? _floatingHeight : _height; } float WindowWayland::getScale() { @@ -152,16 +156,11 @@ void WindowWayland::show() // xdgToplevel = xdg_surface_get_toplevel(xdgSurface); // xdg_toplevel_add_listener(xdgToplevel, &_xdgToplevelListener, this); - _frame = libdecor_decorate(_windowManager.decorCtx, _waylandWindow, &_libdecorFrameInterface, this); _windowManager.registerWindow(this); - wl_surface_commit(_waylandWindow); -} - -// ??? -void WindowWayland::recreate() -{ - close(); - init(); + _frame = libdecor_decorate(_windowManager.decorCtx, _waylandWindow, &_libdecorFrameInterface, this); + _configured = false; + libdecor_frame_map(_frame); + libdecor_dispatch(_windowManager.decorCtx, -1); } void WindowWayland::setVisible(bool isVisible) { @@ -195,8 +194,6 @@ void jwm::WindowWayland::surfaceEnter(void* data, wl_surface* surface, wl_output // doesn't crash : ) WindowWayland* self = reinterpret_cast(data); - Output* good; - for (auto o : self->_windowManager.outputs) { if (o->_output == output) { self->_scale = o->scale; @@ -282,6 +279,12 @@ void jwm::WindowWayland::outputScale(void* data, wl_output* output, int factor) void jwm::WindowWayland::outputName(void* data, wl_output* output, const char* name) {} void jwm::WindowWayland::outputDescription(void* data, wl_output* output, const char* desc) {} +wl_callback_listener jwm::WindowWayland::_frameCallback = { + .done = [](void* data, wl_callback* cb, uint32_t cb_data) { + auto self = reinterpret_cast(data); + self->_adaptSize(self->_newWidth, self->_newHeight); + } +}; void jwm::WindowWayland::decorFrameConfigure(libdecor_frame* frame, libdecor_configuration* configuration, void *userData) { auto self = reinterpret_cast(userData); @@ -301,10 +304,15 @@ void jwm::WindowWayland::decorFrameConfigure(libdecor_frame* frame, libdecor_con self->_floatingHeight = height; } - if (self->_layer) { + if (!self->_configured && self->_layer) { self->_layer->attachBuffer(); } - self->_adaptSize(width, height); + self->_newWidth = width; + self->_newHeight = height; + wl_callback* callback = wl_surface_frame(self->_waylandWindow); + // Throttle frame + wl_callback_add_listener(callback, &_frameCallback, self); + self->_configured = true; } void jwm::WindowWayland::decorFrameClose(libdecor_frame* frame, void* userData) { WindowWayland* self = reinterpret_cast(userData); @@ -313,7 +321,7 @@ void jwm::WindowWayland::decorFrameClose(libdecor_frame* frame, void* userData) void jwm::WindowWayland::decorFrameCommit(libdecor_frame* frame, void* userData) { WindowWayland* self = reinterpret_cast(userData); if (self->_layer) - self->dispatch(classes::EventFrame::kInstance); + self->_layer->swapBuffers(); } void jwm::WindowWayland::decorFrameDismissPopup(libdecor_frame* frame, const char* seatName, void* userData) {} void jwm::WindowWayland::_adaptSize(int newWidth, int newHeight) { diff --git a/wayland/cc/WindowWayland.hh b/wayland/cc/WindowWayland.hh index db9dff9a..2ded4bd4 100644 --- a/wayland/cc/WindowWayland.hh +++ b/wayland/cc/WindowWayland.hh @@ -20,7 +20,6 @@ namespace jwm { void close(); bool init(); void show(); - void recreate(); int getLeft(); int getTop(); int getWidth(); @@ -99,6 +98,7 @@ namespace jwm { bool _canMaximize = false; bool _canFullscreen = false; bool _visible = false; + bool _configured = false; bool _isRedrawRequested = false; @@ -116,5 +116,7 @@ namespace jwm { static wl_output_listener _outputListener; static libdecor_frame_interface _libdecorFrameInterface; + static wl_callback_listener _frameCallback; + }; } From e42583de4bf7bfd43f652441b13820c9b9dffac8 Mon Sep 17 00:00:00 2001 From: BulbyVR <26726264+TheDrawingCoder-Gamer@users.noreply.github.com> Date: Thu, 7 Dec 2023 22:40:45 -0500 Subject: [PATCH 14/95] start input --- wayland/cc/KeyWayland.cc | 281 ++++++++++++++--------------- wayland/cc/KeyWayland.hh | 7 +- wayland/cc/LayerGLWayland.cc | 2 +- wayland/cc/LayerRasterWayland.cc | 2 - wayland/cc/Output.cc | 15 +- wayland/cc/Output.hh | 5 + wayland/cc/ScreenInfo.cc | 2 +- wayland/cc/ScreenInfo.hh | 1 + wayland/cc/WindowManagerWayland.cc | 204 ++++++++++++++------- wayland/cc/WindowManagerWayland.hh | 24 +++ wayland/cc/WindowWayland.cc | 59 +++++- wayland/cc/WindowWayland.hh | 4 +- wayland/java/WindowWayland.java | 15 +- 13 files changed, 393 insertions(+), 228 deletions(-) diff --git a/wayland/cc/KeyWayland.cc b/wayland/cc/KeyWayland.cc index 7a16be98..9c21f662 100644 --- a/wayland/cc/KeyWayland.cc +++ b/wayland/cc/KeyWayland.cc @@ -1,28 +1,23 @@ #include "KeyWayland.hh" #include "KeyModifier.hh" +#include +#include -bool gKeyStates[(std::size_t) jwm::Key::_KEY_COUNT] = {0}; +int jwm::KeyWayland::getModifiers(xkb_state* state) { -bool jwm::KeyWayland::getKeyState(jwm::Key key) { - return gKeyStates[(std::size_t) key]; -} - -void jwm::KeyWayland::setKeyState(jwm::Key key, bool isDown) { - gKeyStates[(std::size_t) key] = isDown; -} - -int jwm::KeyWayland::getModifiers() { int m = 0; - - if (getKeyState(jwm::Key::SHIFT )) m |= (int)jwm::KeyModifier::SHIFT; - if (getKeyState(jwm::Key::CONTROL )) m |= (int)jwm::KeyModifier::CONTROL; - if (getKeyState(jwm::Key::ALT )) m |= (int)jwm::KeyModifier::ALT; - if (getKeyState(jwm::Key::LINUX_META )) m |= (int)jwm::KeyModifier::LINUX_META; - if (getKeyState(jwm::Key::LINUX_SUPER)) m |= (int)jwm::KeyModifier::LINUX_SUPER; + + if (!state) + return 0; + if (xkb_state_mod_name_is_active(state, XKB_MOD_NAME_SHIFT, XKB_STATE_MODS_EFFECTIVE)) m |= (int)jwm::KeyModifier::SHIFT; + if (xkb_state_mod_name_is_active(state, XKB_MOD_NAME_CTRL , XKB_STATE_MODS_EFFECTIVE)) m |= (int)jwm::KeyModifier::CONTROL; + if (xkb_state_mod_name_is_active(state, XKB_MOD_NAME_ALT , XKB_STATE_MODS_EFFECTIVE)) m |= (int)jwm::KeyModifier::ALT; + if (xkb_state_mod_name_is_active(state, XKB_MOD_NAME_LOGO , XKB_STATE_MODS_EFFECTIVE)) m |= (int)jwm::KeyModifier::LINUX_META; + if (xkb_state_mod_name_is_active(state, "Super" , XKB_STATE_MODS_EFFECTIVE)) m |= (int)jwm::KeyModifier::LINUX_SUPER; return m; } - +/* int jwm::KeyWayland::getModifiersFromMask(int mask) { int m = getModifiers(); // ??? @@ -31,145 +26,143 @@ int jwm::KeyWayland::getModifiersFromMask(int mask) { // if (mask & Mod1Mask ) m |= (int)jwm::KeyModifier::ALT; return m; -} +}*/ -jwm::Key jwm::KeyWayland::fromNative(uint32_t v) { /* +jwm::Key jwm::KeyWayland::fromNative(uint32_t v) { switch (v) { // Modifiers - case XK_Caps_Lock: return Key::CAPS_LOCK; - case XK_Shift_R: - case XK_Shift_L: return Key::SHIFT; - case XK_Control_R: - case XK_Control_L: return Key::CONTROL; - case XK_Alt_R: - case XK_Alt_L: return Key::ALT; + case XKB_KEY_Caps_Lock: return Key::CAPS_LOCK; + case XKB_KEY_Shift_R: + case XKB_KEY_Shift_L: return Key::SHIFT; + case XKB_KEY_Control_R: + case XKB_KEY_Control_L: return Key::CONTROL; + case XKB_KEY_Alt_R: + case XKB_KEY_Alt_L: return Key::ALT; // Key::WIN_LOGO - case XK_Super_L: - case XK_Super_R: return Key::LINUX_SUPER; - case XK_Meta_L: - case XK_Meta_R: return Key::LINUX_META; + case XKB_KEY_Super_L: + case XKB_KEY_Super_R: return Key::LINUX_SUPER; + case XKB_KEY_Meta_L: + case XKB_KEY_Meta_R: return Key::LINUX_META; // Key::MAC_COMMAND // Key::MAC_OPTION // Key::MAC_FN // Rest of the keys - case XK_Return: return Key::ENTER; - case XK_BackSpace: return Key::BACKSPACE; - case XK_Tab: return Key::TAB; - case XK_Cancel: return Key::CANCEL; - case XK_Clear: return Key::CLEAR; - case XK_Pause: return Key::PAUSE; - case XK_Escape: return Key::ESCAPE; - case XK_space: return Key::SPACE; - case XK_Page_Up: return Key::PAGE_UP; - case XK_Page_Down: return Key::PAGE_DOWN; - case XK_End: return Key::END; - case XK_Home: return Key::HOME; - case XK_Left: return Key::LEFT; - case XK_Up: return Key::UP; - case XK_Right: return Key::RIGHT; - case XK_Down: return Key::DOWN; - case XK_comma: return Key::COMMA; - case XK_minus: return Key::MINUS; - case XK_period: return Key::PERIOD; - case XK_slash: return Key::SLASH; - case XK_0: return Key::DIGIT0; - case XK_1: return Key::DIGIT1; - case XK_2: return Key::DIGIT2; - case XK_3: return Key::DIGIT3; - case XK_4: return Key::DIGIT4; - case XK_5: return Key::DIGIT5; - case XK_6: return Key::DIGIT6; - case XK_7: return Key::DIGIT7; - case XK_8: return Key::DIGIT8; - case XK_9: return Key::DIGIT9; - case XK_semicolon: return Key::SEMICOLON; - case XK_equal: return Key::EQUALS; - case XK_a: return Key::A; - case XK_b: return Key::B; - case XK_c: return Key::C; - case XK_d: return Key::D; - case XK_e: return Key::E; - case XK_f: return Key::F; - case XK_g: return Key::G; - case XK_h: return Key::H; - case XK_i: return Key::I; - case XK_j: return Key::J; - case XK_k: return Key::K; - case XK_l: return Key::L; - case XK_m: return Key::M; - case XK_n: return Key::N; - case XK_o: return Key::O; - case XK_p: return Key::P; - case XK_q: return Key::Q; - case XK_r: return Key::R; - case XK_s: return Key::S; - case XK_t: return Key::T; - case XK_u: return Key::U; - case XK_v: return Key::V; - case XK_w: return Key::W; - case XK_x: return Key::X; - case XK_y: return Key::Y; - case XK_z: return Key::Z; - case XK_bracketleft: return Key::OPEN_BRACKET; - case XK_backslash: return Key::BACK_SLASH; - case XK_bracketright: return Key::CLOSE_BRACKET; - case XK_KP_0: return Key::DIGIT0; - case XK_KP_1: return Key::DIGIT1; - case XK_KP_2: return Key::DIGIT2; - case XK_KP_3: return Key::DIGIT3; - case XK_KP_4: return Key::DIGIT4; - case XK_KP_5: return Key::DIGIT5; - case XK_KP_6: return Key::DIGIT6; - case XK_KP_7: return Key::DIGIT7; - case XK_KP_8: return Key::DIGIT8; - case XK_KP_9: return Key::DIGIT9; - case XK_multiply: return Key::MULTIPLY; - case XK_KP_Add: return Key::ADD; - case XK_KP_Separator: return Key::SEPARATOR; - case XK_KP_Subtract: return Key::MINUS; - case XK_KP_Decimal: return Key::PERIOD; - case XK_KP_Divide: return Key::SLASH; - case XK_KP_Delete: return Key::DEL; - case XK_Delete: return Key::DEL; - case XK_Num_Lock: return Key::NUM_LOCK; - case XK_Scroll_Lock: return Key::SCROLL_LOCK; - case XK_F1: return Key::F1; - case XK_F2: return Key::F2; - case XK_F3: return Key::F3; - case XK_F4: return Key::F4; - case XK_F5: return Key::F5; - case XK_F6: return Key::F6; - case XK_F7: return Key::F7; - case XK_F8: return Key::F8; - case XK_F9: return Key::F9; - case XK_F10: return Key::F10; - case XK_F11: return Key::F11; - case XK_F12: return Key::F12; - case XK_F13: return Key::F13; - case XK_F14: return Key::F14; - case XK_F15: return Key::F15; - case XK_F16: return Key::F16; - case XK_F17: return Key::F17; - case XK_F18: return Key::F18; - case XK_F19: return Key::F19; - case XK_F20: return Key::F20; - case XK_F21: return Key::F21; - case XK_F22: return Key::F22; - case XK_F23: return Key::F23; - case XK_F24: return Key::F24; - case XK_Print: return Key::PRINTSCREEN; - case XK_Insert: return Key::INSERT; - case XK_Help: return Key::HELP; - case XK_grave: return Key::BACK_QUOTE; - case XK_quoteright: return Key::QUOTE; - case XK_Menu: return Key::MENU; + case XKB_KEY_Return: return Key::ENTER; + case XKB_KEY_BackSpace: return Key::BACKSPACE; + case XKB_KEY_Tab: return Key::TAB; + case XKB_KEY_Cancel: return Key::CANCEL; + case XKB_KEY_Clear: return Key::CLEAR; + case XKB_KEY_Pause: return Key::PAUSE; + case XKB_KEY_Escape: return Key::ESCAPE; + case XKB_KEY_space: return Key::SPACE; + case XKB_KEY_Page_Up: return Key::PAGE_UP; + case XKB_KEY_Page_Down: return Key::PAGE_DOWN; + case XKB_KEY_End: return Key::END; + case XKB_KEY_Home: return Key::HOME; + case XKB_KEY_Left: return Key::LEFT; + case XKB_KEY_Up: return Key::UP; + case XKB_KEY_Right: return Key::RIGHT; + case XKB_KEY_Down: return Key::DOWN; + case XKB_KEY_comma: return Key::COMMA; + case XKB_KEY_minus: return Key::MINUS; + case XKB_KEY_period: return Key::PERIOD; + case XKB_KEY_slash: return Key::SLASH; + case XKB_KEY_0: return Key::DIGIT0; + case XKB_KEY_1: return Key::DIGIT1; + case XKB_KEY_2: return Key::DIGIT2; + case XKB_KEY_3: return Key::DIGIT3; + case XKB_KEY_4: return Key::DIGIT4; + case XKB_KEY_5: return Key::DIGIT5; + case XKB_KEY_6: return Key::DIGIT6; + case XKB_KEY_7: return Key::DIGIT7; + case XKB_KEY_8: return Key::DIGIT8; + case XKB_KEY_9: return Key::DIGIT9; + case XKB_KEY_semicolon: return Key::SEMICOLON; + case XKB_KEY_equal: return Key::EQUALS; + case XKB_KEY_a: return Key::A; + case XKB_KEY_b: return Key::B; + case XKB_KEY_c: return Key::C; + case XKB_KEY_d: return Key::D; + case XKB_KEY_e: return Key::E; + case XKB_KEY_f: return Key::F; + case XKB_KEY_g: return Key::G; + case XKB_KEY_h: return Key::H; + case XKB_KEY_i: return Key::I; + case XKB_KEY_j: return Key::J; + case XKB_KEY_k: return Key::K; + case XKB_KEY_l: return Key::L; + case XKB_KEY_m: return Key::M; + case XKB_KEY_n: return Key::N; + case XKB_KEY_o: return Key::O; + case XKB_KEY_p: return Key::P; + case XKB_KEY_q: return Key::Q; + case XKB_KEY_r: return Key::R; + case XKB_KEY_s: return Key::S; + case XKB_KEY_t: return Key::T; + case XKB_KEY_u: return Key::U; + case XKB_KEY_v: return Key::V; + case XKB_KEY_w: return Key::W; + case XKB_KEY_x: return Key::X; + case XKB_KEY_y: return Key::Y; + case XKB_KEY_z: return Key::Z; + case XKB_KEY_bracketleft: return Key::OPEN_BRACKET; + case XKB_KEY_backslash: return Key::BACK_SLASH; + case XKB_KEY_bracketright: return Key::CLOSE_BRACKET; + case XKB_KEY_KP_0: return Key::DIGIT0; + case XKB_KEY_KP_1: return Key::DIGIT1; + case XKB_KEY_KP_2: return Key::DIGIT2; + case XKB_KEY_KP_3: return Key::DIGIT3; + case XKB_KEY_KP_4: return Key::DIGIT4; + case XKB_KEY_KP_5: return Key::DIGIT5; + case XKB_KEY_KP_6: return Key::DIGIT6; + case XKB_KEY_KP_7: return Key::DIGIT7; + case XKB_KEY_KP_8: return Key::DIGIT8; + case XKB_KEY_KP_9: return Key::DIGIT9; + case XKB_KEY_multiply: return Key::MULTIPLY; + case XKB_KEY_KP_Add: return Key::ADD; + case XKB_KEY_KP_Separator: return Key::SEPARATOR; + case XKB_KEY_KP_Subtract: return Key::MINUS; + case XKB_KEY_KP_Decimal: return Key::PERIOD; + case XKB_KEY_KP_Divide: return Key::SLASH; + case XKB_KEY_KP_Delete: return Key::DEL; + case XKB_KEY_Delete: return Key::DEL; + case XKB_KEY_Num_Lock: return Key::NUM_LOCK; + case XKB_KEY_Scroll_Lock: return Key::SCROLL_LOCK; + case XKB_KEY_F1: return Key::F1; + case XKB_KEY_F2: return Key::F2; + case XKB_KEY_F3: return Key::F3; + case XKB_KEY_F4: return Key::F4; + case XKB_KEY_F5: return Key::F5; + case XKB_KEY_F6: return Key::F6; + case XKB_KEY_F7: return Key::F7; + case XKB_KEY_F8: return Key::F8; + case XKB_KEY_F9: return Key::F9; + case XKB_KEY_F10: return Key::F10; + case XKB_KEY_F11: return Key::F11; + case XKB_KEY_F12: return Key::F12; + case XKB_KEY_F13: return Key::F13; + case XKB_KEY_F14: return Key::F14; + case XKB_KEY_F15: return Key::F15; + case XKB_KEY_F16: return Key::F16; + case XKB_KEY_F17: return Key::F17; + case XKB_KEY_F18: return Key::F18; + case XKB_KEY_F19: return Key::F19; + case XKB_KEY_F20: return Key::F20; + case XKB_KEY_F21: return Key::F21; + case XKB_KEY_F22: return Key::F22; + case XKB_KEY_F23: return Key::F23; + case XKB_KEY_F24: return Key::F24; + case XKB_KEY_Print: return Key::PRINTSCREEN; + case XKB_KEY_Insert: return Key::INSERT; + case XKB_KEY_Help: return Key::HELP; + case XKB_KEY_grave: return Key::BACK_QUOTE; + case XKB_KEY_quoteright: return Key::QUOTE; + case XKB_KEY_Menu: return Key::MENU; // Key::KANA // Key::VOLUME_UP // Key::VOLUME_DOWN // Key::MUTE default: return Key::UNDEFINED; - } */ - // IMPL ME! - return Key::UNDEFINED; + } } diff --git a/wayland/cc/KeyWayland.hh b/wayland/cc/KeyWayland.hh index bcbf02d8..03009cc1 100644 --- a/wayland/cc/KeyWayland.hh +++ b/wayland/cc/KeyWayland.hh @@ -2,13 +2,12 @@ #include #include "Key.hh" +#include namespace jwm { namespace KeyWayland { jwm::Key fromNative(uint32_t v); - bool getKeyState(jwm::Key key); - void setKeyState(jwm::Key key, bool isDown); - int getModifiers(); - int getModifiersFromMask(int mask); + int getModifiers(xkb_state* state); + // int getModifiersFromMask(int mask); } } diff --git a/wayland/cc/LayerGLWayland.cc b/wayland/cc/LayerGLWayland.cc index 9d1cf492..44ba6e90 100644 --- a/wayland/cc/LayerGLWayland.cc +++ b/wayland/cc/LayerGLWayland.cc @@ -81,7 +81,7 @@ namespace jwm { glClear(GL_STENCIL_BUFFER_BIT | GL_COLOR_BUFFER_BIT); // ??? - // glViewport(0, 0, width, height); + glViewport(0, 0, width, height); // God is dead if _eglWindow is null if (_eglWindow) wl_egl_window_resize(_eglWindow, width, height, 0, 0); diff --git a/wayland/cc/LayerRasterWayland.cc b/wayland/cc/LayerRasterWayland.cc index 4198d675..590ad999 100644 --- a/wayland/cc/LayerRasterWayland.cc +++ b/wayland/cc/LayerRasterWayland.cc @@ -23,8 +23,6 @@ namespace jwm { void attach(WindowWayland* window) { fWindow = jwm::ref(window); fWindow->setLayer(this); - // a default size : ) - resize(100, 100); } void resize(int width, int height) override { diff --git a/wayland/cc/Output.cc b/wayland/cc/Output.cc index 356aefe4..91590ce5 100644 --- a/wayland/cc/Output.cc +++ b/wayland/cc/Output.cc @@ -16,9 +16,22 @@ Output::Output(wl_output* output, uint32_t name): { wl_output_add_listener(output, &_outputListener, this); } + +ScreenInfo Output::getScreenInfo() const { + return { + .id = _name, + .bounds = jwm::IRect::makeXYWH(0, 0, width, height), + .isPrimary = false, + .scale = scale + }; +} void Output::outputGeometry(void* data, wl_output* output, int x, int y, int physWidth, int physHeight, int subPixel, const char* make, const char* model, int transform) {} -void Output::outputMode(void* data, wl_output* output, uint32_t flags, int width, int height, int refresh) {} +void Output::outputMode(void* data, wl_output* output, uint32_t flags, int width, int height, int refresh) { + Output* self = reinterpret_cast(data); + self->width = width; + self->height = height; +} void Output::outputDone(void* data, wl_output* output) {} void Output::outputScale(void* data, wl_output* output, int factor) { Output* self = reinterpret_cast(data); diff --git a/wayland/cc/Output.hh b/wayland/cc/Output.hh index 1c6763b1..cf8c9381 100644 --- a/wayland/cc/Output.hh +++ b/wayland/cc/Output.hh @@ -2,6 +2,7 @@ #include #include +#include "ScreenInfo.hh" namespace jwm { class Output { @@ -11,6 +12,10 @@ namespace jwm { wl_output* _output; uint32_t _name; int scale = 1; + int width = 0; + int height = 0; + + ScreenInfo getScreenInfo() const; static wl_output_listener _outputListener; static void outputGeometry(void* data, wl_output* output, int x, int y, int physWidth, int physHeight, diff --git a/wayland/cc/ScreenInfo.cc b/wayland/cc/ScreenInfo.cc index 3dddf554..0dc625b2 100644 --- a/wayland/cc/ScreenInfo.cc +++ b/wayland/cc/ScreenInfo.cc @@ -1,5 +1,5 @@ #include "ScreenInfo.hh" #include "AppWayland.hh" jobject jwm::ScreenInfo::asJavaObject(JNIEnv* env) const { - return jwm::classes::Screen::make(env, id, isPrimary, bounds, bounds, 1.0f); + return jwm::classes::Screen::make(env, id, isPrimary, bounds, bounds, scale); } diff --git a/wayland/cc/ScreenInfo.hh b/wayland/cc/ScreenInfo.hh index 947890a3..99d3eb9d 100644 --- a/wayland/cc/ScreenInfo.hh +++ b/wayland/cc/ScreenInfo.hh @@ -8,6 +8,7 @@ namespace jwm long id; IRect bounds; bool isPrimary; + int scale; jobject asJavaObject(JNIEnv* env) const; }; } // namespace jwm diff --git a/wayland/cc/WindowManagerWayland.cc b/wayland/cc/WindowManagerWayland.cc index 2df5df50..7e3a6f23 100644 --- a/wayland/cc/WindowManagerWayland.cc +++ b/wayland/cc/WindowManagerWayland.cc @@ -19,6 +19,9 @@ #include "Output.hh" #include #include +#include +#include +#include using namespace jwm; @@ -40,6 +43,18 @@ xdg_wm_base_listener WindowManagerWayland::_xdgWmBaseListener = { libdecor_interface WindowManagerWayland::_decorInterface = { .error = WindowManagerWayland::libdecorError }; +wl_keyboard_listener WindowManagerWayland::_keyboardListener = { + .keymap = WindowManagerWayland::keyboardKeymap, + .enter = WindowManagerWayland::keyboardEnter, + .leave = WindowManagerWayland::keyboardLeave, + .key = WindowManagerWayland::keyboardKey, + .modifiers = WindowManagerWayland::keyboardModifiers, + .repeat_info = WindowManagerWayland::keyboardRepeatInfo +}; +wl_seat_listener WindowManagerWayland::_seatListener = { + .capabilities = WindowManagerWayland::seatCapabilities, + .name = WindowManagerWayland::seatName +}; WindowManagerWayland::WindowManagerWayland(): display(wl_display_connect(nullptr)) { registry = wl_display_get_registry(display); @@ -55,9 +70,13 @@ WindowManagerWayland::WindowManagerWayland(): throw std::system_error(ENOTSUP, std::generic_category(), "Unsupported compositor"); } - + // ???: Moving this after libdecor_new causes input to not work + wl_seat_add_listener(seat, &_seatListener, this); + decorCtx = libdecor_new(display, &_decorInterface); + + wl_display_roundtrip(display); // frankly `this` is not needed here, but it needs a pointer anyway and it's // good to have consistentcy. xdg_wm_base_add_listener(xdgShell, &_xdgWmBaseListener, this); @@ -89,13 +108,6 @@ WindowManagerWayland::WindowManagerWayland(): wl_surface_commit(cursorSurface); } - { - pointer = wl_seat_get_pointer(seat); - - wl_pointer_add_listener(pointer, &_pointerListener, this); - } - - } @@ -245,7 +257,9 @@ void WindowManagerWayland::pointerHandleMotion(void* data, wl_pointer* pointer, ::WindowWayland* window = self->getWindowForNative(self->focusedSurface); // God is dead if window is null if (window) - self->mouseUpdate(window, wl_fixed_to_int(surface_x), wl_fixed_to_int(surface_y), self->mouseMask); + self->mouseUpdate(window, + wl_fixed_to_int(surface_x) * window->_scale, + wl_fixed_to_int(surface_y) * window->_scale, self->mouseMask); } } @@ -282,7 +296,7 @@ void WindowManagerWayland::pointerHandleButton(void* data, wl_pointer* pointer, false, self->lastMousePosX, self->lastMousePosY, - jwm::KeyWayland::getModifiers() + jwm::KeyWayland::getModifiers(self->_xkbState) ) ); WindowWayland* window = self->getWindowForNative(self->focusedSurface); @@ -317,7 +331,7 @@ void WindowManagerWayland::pointerHandleButton(void* data, wl_pointer* pointer, true, self->lastMousePosX, self->lastMousePosY, - jwm::KeyWayland::getModifiers() + jwm::KeyWayland::getModifiers(self->_xkbState) ) ); // me when this stuff is NULL : ( @@ -329,6 +343,122 @@ void WindowManagerWayland::pointerHandleButton(void* data, wl_pointer* pointer, } void WindowManagerWayland::pointerHandleAxis(void* data, wl_pointer* pointer, uint32_t time, uint32_t axis, wl_fixed_t value) {} +void WindowManagerWayland::keyboardKeymap(void* data, wl_keyboard* keyboard, uint32_t format, + int32_t fd, uint32_t size) { + auto self = reinterpret_cast(data); + + if (format != WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1) { + close(fd); + fprintf(stderr, "no xkb keymap\n"); + return; + } + + char* map_str = reinterpret_cast(mmap(nullptr, size, PROT_READ, MAP_PRIVATE, fd, 0)); + if (map_str == MAP_FAILED) { + close(fd); + fprintf(stderr, "keymap mmap failed: %s", strerror(errno)); + return; + } + + xkb_keymap* keymap = xkb_keymap_new_from_string( + self->_xkbContext, map_str, + XKB_KEYMAP_FORMAT_TEXT_V1, + XKB_KEYMAP_COMPILE_NO_FLAGS + ); + munmap(map_str, size); + close(fd); + + if (!keymap) { + return; + } + self->_xkbState = xkb_state_new(keymap); + + xkb_keymap_unref(keymap); +} +void WindowManagerWayland::keyboardEnter(void* data, wl_keyboard* keyboard, uint32_t serial, wl_surface* surface, + wl_array *keys) { + auto self = reinterpret_cast(data); + self->keyboardFocus = surface; +} +void WindowManagerWayland::keyboardLeave(void* data, wl_keyboard* keyboard, uint32_t serial, wl_surface* surface) { + auto self = reinterpret_cast(data); + self->keyboardFocus = nullptr; +} + +void WindowManagerWayland::keyboardKey(void* data, wl_keyboard* keyboard, uint32_t serial, uint32_t time, + uint32_t key, uint32_t state) +{ + auto self = reinterpret_cast(data); + if (!self->_xkbState) return; + const xkb_keysym_t *syms; + + if (xkb_state_key_get_syms(self->_xkbState, key + 8, &syms) != 1) + return; + jwm::Key jwmKey = KeyWayland::fromNative(syms[0]); + + // TODO: while unlikely, it could be possible that you can be entering text even if the + // pointer hasn't entered + if (self->focusedSurface) { + jwm::KeyLocation location; + JNILocal keyEvent( + app.getJniEnv(), + classes::EventKey::make( + app.getJniEnv(), + jwmKey, + state == WL_KEYBOARD_KEY_STATE_PRESSED, + KeyWayland::getModifiers(self->_xkbState), + location + ) + ); + auto window = self->getWindowForNative(self->keyboardFocus); + if (window) + window->dispatch(keyEvent.get()); + } + +} +void WindowManagerWayland::keyboardModifiers(void* data, wl_keyboard* keyboard, + uint32_t serial, uint32_t mods_depressed, uint32_t mods_latched, uint32_t mods_locked, + uint32_t group) { + auto self = reinterpret_cast(data); + if (!self->_xkbState) return; + xkb_state_update_mask(self->_xkbState, + mods_depressed, mods_latched, mods_locked, + 0, 0, group); +} + +void WindowManagerWayland::keyboardRepeatInfo(void* data, wl_keyboard* keyboard, + int32_t rate, int32_t delay) { + // TODO: The client (for some godforsaken reason) is expected to handle repeating characters +} + +void WindowManagerWayland::seatCapabilities(void* data, wl_seat* seat, uint32_t capabilities) { + auto self = reinterpret_cast(data); + printf("%i", capabilities & WL_SEAT_CAPABILITY_POINTER); + if ((capabilities & WL_SEAT_CAPABILITY_POINTER) && + !self->pointer) { + self->pointer = wl_seat_get_pointer(seat); + wl_pointer_add_listener(self->pointer, &_pointerListener, self); + } else if (!(capabilities & WL_SEAT_CAPABILITY_POINTER) && self->pointer) { + wl_pointer_release(self->pointer); + self->pointer = nullptr; + } + + if ((capabilities & WL_SEAT_CAPABILITY_KEYBOARD) && + !self->keyboard) { + printf("got da keyboard\n"); + self->keyboard = wl_seat_get_keyboard(seat); + self->_xkbContext = xkb_context_new(XKB_CONTEXT_NO_FLAGS); + wl_keyboard_add_listener(self->keyboard, &_keyboardListener, self); + } else if (!(capabilities & WL_SEAT_CAPABILITY_KEYBOARD) && + self->keyboard) { + xkb_context_unref(self->_xkbContext); + wl_keyboard_release(self->keyboard); + self->keyboard = nullptr; + } +} +void WindowManagerWayland::seatName(void* data, wl_seat* seat, const char* name) { + // who cares +} void WindowManagerWayland::xdgWmBasePing(void* data, xdg_wm_base* base, uint32_t serial) { xdg_wm_base_pong(base, serial); } @@ -413,7 +543,7 @@ void WindowManagerWayland::mouseUpdate(WindowWayland* myWindow, uint32_t x, uint movementY, jwm::MouseButtonWayland::fromNativeMask(mask), // impl me! - jwm::KeyWayland::getModifiers() + jwm::KeyWayland::getModifiers(_xkbState) ) ); auto foo = eventMove.get(); @@ -421,56 +551,6 @@ void WindowManagerWayland::mouseUpdate(WindowWayland* myWindow, uint32_t x, uint } jwm::ByteBuf WindowManagerWayland::getClipboardContents(const std::string& type) { auto nativeHandle = _nativeWindowToMy.begin()->first; - /* - XConvertSelection(display, - _atoms.CLIPBOARD, - XInternAtom(display, type.c_str(), false), - _atoms.JWM_CLIPBOARD, - nativeHandle, - CurrentTime); - XEvent ev; - while (_runLoop) { - while (XPending(display)) { - XNextEvent(display, &ev); - switch (ev.type) - { - case SelectionNotify: { - if (ev.xselection.property == None) { - return {}; - } - - Atom da, incr, type; - int di; - unsigned long size, length, count; - unsigned char* propRet = NULL; - - XGetWindowProperty(display, nativeHandle, _atoms.JWM_CLIPBOARD, 0, 0, False, AnyPropertyType, - &type, &di, &length, &size, &propRet); - XFree(propRet); - - // Clipboard data is too large and INCR mechanism not implemented - ByteBuf result; - if (type != _atoms.INCR) - { - XGetWindowProperty(display, nativeHandle, _atoms.JWM_CLIPBOARD, 0, size, False, AnyPropertyType, - &da, &di, &length, &count, &propRet); - - result = ByteBuf{ propRet, propRet + length }; - XFree(propRet); - return result; - } - XDeleteProperty(display, nativeHandle, _atoms.JWM_CLIPBOARD); - return result; - } - default: - _processXEvent(ev); - } - } - _processCallbacks(); - } - - XDeleteProperty(display, nativeHandle, _atoms.JWM_CLIPBOARD); - */ return {}; } diff --git a/wayland/cc/WindowManagerWayland.hh b/wayland/cc/WindowManagerWayland.hh index eb872036..3b470ea2 100644 --- a/wayland/cc/WindowManagerWayland.hh +++ b/wayland/cc/WindowManagerWayland.hh @@ -17,6 +17,7 @@ #include #include "Output.hh" #include +#include namespace jwm { class WindowWayland; @@ -84,6 +85,25 @@ namespace jwm { static libdecor_interface _decorInterface; static void libdecorError(libdecor* context, enum libdecor_error error, const char* message); + static wl_keyboard_listener _keyboardListener; + static void keyboardKeymap(void* data, wl_keyboard* keyboard, + uint32_t format, int32_t fd, uint32_t size); + static void keyboardEnter(void* data, wl_keyboard* keyboard, + uint32_t serial, wl_surface* surface, wl_array* keys); + static void keyboardLeave(void* data, wl_keyboard* keyboard, uint32_t serial, + wl_surface* surface); + static void keyboardKey(void* data, wl_keyboard* keyboard, uint32_t serial, + uint32_t time, uint32_t key, uint32_t state); + static void keyboardModifiers(void* data, wl_keyboard* keyboard, uint32_t serial, + uint32_t modsDepressed, uint32_t modsLatched, uint32_t modLocked, + uint32_t group); + static void keyboardRepeatInfo(void* data, wl_keyboard* keyboard, int32_t rate, int32_t delay); + + static wl_seat_listener _seatListener; + + static void seatCapabilities(void* data, wl_seat* seat, uint32_t capabilities); + static void seatName(void* data, wl_seat* seat, const char* name); + ByteBuf getClipboardContents(const std::string& type); std::vector getClipboardFormats(); @@ -95,7 +115,10 @@ namespace jwm { wl_data_device_manager* deviceManager = nullptr; wl_seat* seat = nullptr; wl_pointer* pointer = nullptr; + wl_keyboard* keyboard = nullptr; libdecor* decorCtx = nullptr; + xkb_context* _xkbContext = nullptr; + xkb_state* _xkbState = nullptr; std::list outputs; // XVisualInfo* x11VisualInfo; @@ -114,6 +137,7 @@ namespace jwm { wl_surface* cursorSurface; wl_surface* focusedSurface = nullptr; + wl_surface* keyboardFocus = nullptr; // Is holding all cursors in memory a good idea? wl_cursor_image* _cursors[static_cast(jwm::MouseCursor::COUNT)]; diff --git a/wayland/cc/WindowWayland.cc b/wayland/cc/WindowWayland.cc index a9dbae2b..f95916f4 100644 --- a/wayland/cc/WindowWayland.cc +++ b/wayland/cc/WindowWayland.cc @@ -117,6 +117,21 @@ void WindowWayland::getContentPosition(int& posX, int& posY) { } +bool WindowWayland::resize(int width, int height) { + // Width and height are in absolute pixel units, and wayland will + // complain if you try to set a width/height that isn't a multiple of scale. + if ((width % _scale) != 0 || (height % _scale) != 0) + return false; + _width = width / _scale; + _height = height / _scale; + if (_visible) { + // HACK: while I could just dispatch here and save some cpu time, + // it's easier to just lease it out + _adaptSize(_width, _height); + } + return true; +} + int WindowWayland::getLeft() { int x, y; getContentPosition(x, y); @@ -130,11 +145,11 @@ int WindowWayland::getTop() { } int WindowWayland::getWidth() { - return _width <= 0 ? _floatingWidth : _width; + return (_width <= 0 ? _floatingWidth : _width) * _scale; } int WindowWayland::getHeight() { - return _height <= 0 ? _floatingHeight : _height; + return (_height <= 0 ? _floatingHeight : _height) * _scale; } float WindowWayland::getScale() { @@ -163,6 +178,18 @@ void WindowWayland::show() libdecor_dispatch(_windowManager.decorCtx, -1); } +ScreenInfo WindowWayland::getScreen() { + if (_output) { + return _output->getScreenInfo(); + } else { + return { + .id = -1, + .bounds = jwm::IRect::makeXYWH(0, 0, _width, _height), + .isPrimary = false, + .scale = _scale + }; + } +} void WindowWayland::setVisible(bool isVisible) { if (_visible != isVisible) { _visible = isVisible; @@ -196,6 +223,7 @@ void jwm::WindowWayland::surfaceEnter(void* data, wl_surface* surface, wl_output for (auto o : self->_windowManager.outputs) { if (o->_output == output) { + self->_output = o; self->_scale = o->scale; wl_surface_set_buffer_scale(surface, o->scale); self->_adaptSize(self->_width, self->_height); @@ -325,11 +353,26 @@ void jwm::WindowWayland::decorFrameCommit(libdecor_frame* frame, void* userData) } void jwm::WindowWayland::decorFrameDismissPopup(libdecor_frame* frame, const char* seatName, void* userData) {} void jwm::WindowWayland::_adaptSize(int newWidth, int newHeight) { + using namespace classes; _width = newWidth; _height = newHeight; + int scaledWidth = _width * _scale; + int scaledHeight = _height * _scale; if (_layer) { - _layer->resize(_width * _scale, _height * _scale); + _layer->resize(scaledWidth, scaledHeight); } + + jwm::JNILocal eventWindowResize( + app.getJniEnv(), + EventWindowResize::make( + app.getJniEnv(), + scaledWidth, + scaledHeight, + scaledWidth, + scaledHeight + ) + ); + dispatch(eventWindowResize.get()); } // JNI @@ -348,6 +391,16 @@ extern "C" JNIEXPORT void JNICALL Java_io_github_humbleui_jwm_WindowWayland__1nS reinterpret_cast(jwm::classes::Native::fromJava(env, obj))->setVisible(isVisible); } +extern "C" JNIEXPORT void JNICALL Java_io_github_humbleui_jwm_WindowWayland__1nSetContentSize + (JNIEnv* env, jobject obj, jint width, jint height) { + reinterpret_cast(jwm::classes::Native::fromJava(env, obj))->resize(width, height); +} +extern "C" JNIEXPORT jobject JNICALL Java_io_github_humbleui_jwm_WindowWayland__1nGetScreen + (JNIEnv* env, jobject obj) { + jwm::WindowWayland* instance = reinterpret_cast(jwm::classes::Native::fromJava(env, obj)); + return instance->getScreen().asJavaObject(env); +} + extern "C" JNIEXPORT jobject JNICALL Java_io_github_humbleui_jwm_WindowWayland__1nGetWindowRect (JNIEnv* env, jobject obj) { jwm::WindowWayland* instance = reinterpret_cast(jwm::classes::Native::fromJava(env, obj)); diff --git a/wayland/cc/WindowWayland.hh b/wayland/cc/WindowWayland.hh index 2ded4bd4..c7b6f679 100644 --- a/wayland/cc/WindowWayland.hh +++ b/wayland/cc/WindowWayland.hh @@ -17,6 +17,7 @@ namespace jwm { void getDecorations(int& left, int& top, int& right, int& bottom); void getContentPosition(int& posX, int& posY); void setVisible(bool isVisible); + bool resize(int width, int height); void close(); bool init(); void show(); @@ -51,7 +52,7 @@ namespace jwm { } - const ScreenInfo& getScreen(); + ScreenInfo getScreen(); static void surfaceEnter(void* data, wl_surface* surface, wl_output* output); static void surfaceLeave(void* data, wl_surface* surface, wl_output* output); @@ -108,6 +109,7 @@ namespace jwm { xdg_surface* xdgSurface = nullptr; xdg_toplevel* xdgToplevel = nullptr; libdecor_frame* _frame = nullptr; + Output* _output = nullptr; static wl_surface_listener _surfaceListener; static xdg_surface_listener _xdgSurfaceListener; diff --git a/wayland/java/WindowWayland.java b/wayland/java/WindowWayland.java index 5c09dd50..666bc04e 100644 --- a/wayland/java/WindowWayland.java +++ b/wayland/java/WindowWayland.java @@ -31,15 +31,13 @@ public void unmarkText() { @Override public IRect getWindowRect() { assert _onUIThread() : "Should be run on UI thread"; - // very very bad! - return IRect.makeXYWH(0, 0, 100, 100); + return _nGetWindowRect(); } @Override public IRect getContentRect() { assert _onUIThread() : "Should be run on UI thread"; - // stop! - return IRect.makeXYWH(0, 0, 100, 100); + return _nGetContentRect(); } @Override @@ -59,8 +57,7 @@ public Window setWindowSize(int width, int height) { @Override public Window setContentSize(int width, int height) { assert _onUIThread() : "Should be run on UI thread"; - // _nSetContentSize(width, height); - // Possibly unsupported + _nSetContentSize(width, height); return this; } @@ -118,7 +115,7 @@ public float getOpacity(){ @Override public Screen getScreen() { assert _onUIThread() : "Should be run on UI thread"; - return new Screen(0, false, IRect.makeXYWH(0, 0, 0, 0), IRect.makeXYWH(0, 0, 0, 0), getScale()); + return _nGetScreen(); } @Override @@ -227,8 +224,8 @@ public float getScale() { // @ApiStatus.Internal public native void _nSetWindowPosition(int left, int top); // @ApiStatus.Internal public native void _nSetWindowSize(int width, int height); @ApiStatus.Internal public native void _nSetMouseCursor(int cursorId); - // @ApiStatus.Internal public native void _nSetContentSize(int width, int height); - // @ApiStatus.Internal public native Screen _nGetScreen(); + @ApiStatus.Internal public native void _nSetContentSize(int width, int height); + @ApiStatus.Internal public native Screen _nGetScreen(); @ApiStatus.Internal public native void _nRequestFrame(); @ApiStatus.Internal public native void _nClose(); @ApiStatus.Internal public native void _nMaximize(); From db4c4d5fe3151b2abddaf36f91a2be2e5cf7f138 Mon Sep 17 00:00:00 2001 From: BulbyVR <26726264+TheDrawingCoder-Gamer@users.noreply.github.com> Date: Thu, 7 Dec 2023 23:52:36 -0500 Subject: [PATCH 15/95] remove xdg-shell --- wayland/CMakeLists.txt | 3 - wayland/cc/LayerRasterWayland.cc | 4 + wayland/cc/WindowManagerWayland.cc | 41 +- wayland/cc/WindowManagerWayland.hh | 4 - wayland/cc/WindowWayland.cc | 85 +- wayland/cc/WindowWayland.hh | 12 - wayland/cc/xdg-shell/xdg-shell.c | 183 --- wayland/cc/xdg-shell/xdg-shell.h | 2307 ---------------------------- 8 files changed, 39 insertions(+), 2600 deletions(-) delete mode 100644 wayland/cc/xdg-shell/xdg-shell.c delete mode 100644 wayland/cc/xdg-shell/xdg-shell.h diff --git a/wayland/CMakeLists.txt b/wayland/CMakeLists.txt index 04947745..a7ead2f8 100644 --- a/wayland/CMakeLists.txt +++ b/wayland/CMakeLists.txt @@ -20,8 +20,6 @@ file(GLOB SOURCES_CXX ${CMAKE_CURRENT_LIST_DIR}/../shared/cc/*.cc ${CMAKE_CURRENT_LIST_DIR}/../linux/cc/*.cc ${CMAKE_CURRENT_LIST_DIR}/cc/*.cc ) file(GLOB SOURCES_CXX_IMPL ${CMAKE_CURRENT_LIST_DIR}/../shared/cc/impl/*.cc) -file(GLOB SOURCES_XDG ${CMAKE_CURRENT_LIST_DIR}/cc/xdg-shell/*.c) -add_library(xdgShell STATIC ${SOURCES_XDG}) add_library(jwm SHARED ${SOURCES_OBJC} ${SOURCES_CXX} ${SOURCES_CXX_IMPL}) find_library(WAYLAND_CLIENT_LIB wayland-client) find_library(DECOR_LIB decor-0) @@ -45,7 +43,6 @@ endif() target_include_directories(jwm PRIVATE ${CMAKE_CURRENT_LIST_DIR}/../shared/cc ${CMAKE_CURRENT_LIST_DIR}/../linux/cc ${JAVA_HOME}/include ${JAVA_HOME}/include/linux) set_target_properties(jwm PROPERTIES OUTPUT_NAME "jwm_${JWM_ARCH}_wayland") -target_link_libraries(jwm PUBLIC xdgShell) target_link_libraries(jwm PRIVATE ${WAYLAND_CLIENT_LIB} ${DECOR_LIB} ${WAYLAND_CURSOR} ${XKBCOMMON}) target_link_libraries(jwm PRIVATE ${EGL} ${WAYLAND_EGL}) diff --git a/wayland/cc/LayerRasterWayland.cc b/wayland/cc/LayerRasterWayland.cc index 590ad999..1e1d620c 100644 --- a/wayland/cc/LayerRasterWayland.cc +++ b/wayland/cc/LayerRasterWayland.cc @@ -23,9 +23,12 @@ namespace jwm { void attach(WindowWayland* window) { fWindow = jwm::ref(window); fWindow->setLayer(this); + // must have a default size for pointer initing : ) + resize(400, 400); } void resize(int width, int height) override { + printf("???\n"); wl_display* d = fWindow->_windowManager.display; _width = width; _height = height; @@ -53,6 +56,7 @@ namespace jwm { if (_attached) { attachBuffer(); } + makeCurrentForced(); } const void* getPixelsPtr() const { diff --git a/wayland/cc/WindowManagerWayland.cc b/wayland/cc/WindowManagerWayland.cc index 7e3a6f23..a69e9126 100644 --- a/wayland/cc/WindowManagerWayland.cc +++ b/wayland/cc/WindowManagerWayland.cc @@ -36,9 +36,6 @@ wl_pointer_listener WindowManagerWayland::_pointerListener = { .button = WindowManagerWayland::pointerHandleButton, .axis = WindowManagerWayland::pointerHandleAxis }; -xdg_wm_base_listener WindowManagerWayland::_xdgWmBaseListener = { - .ping = WindowManagerWayland::xdgWmBasePing -}; libdecor_interface WindowManagerWayland::_decorInterface = { .error = WindowManagerWayland::libdecorError @@ -64,7 +61,7 @@ WindowManagerWayland::WindowManagerWayland(): - if (!(shm && xdgShell && compositor && deviceManager && seat)) { + if (!(shm && compositor && deviceManager && seat)) { // ??? // Bad. Means our compositor no supportie : ( throw std::system_error(ENOTSUP, std::generic_category(), "Unsupported compositor"); @@ -77,9 +74,6 @@ WindowManagerWayland::WindowManagerWayland(): wl_display_roundtrip(display); - // frankly `this` is not needed here, but it needs a pointer anyway and it's - // good to have consistentcy. - xdg_wm_base_add_listener(xdgShell, &_xdgWmBaseListener, this); { wl_cursor_theme* cursor_theme = wl_cursor_theme_load(nullptr, 24, shm); // TODO: what about if missing : ( @@ -202,9 +196,6 @@ void WindowManagerWayland::registryHandleGlobal(void* data, wl_registry *registr } else if (strcmp(interface, wl_shm_interface.name) == 0) { self->shm = (wl_shm*)wl_registry_bind(registry, name, &wl_shm_interface, 1); - } else if (strcmp(interface, xdg_wm_base_interface.name) == 0) { - self->xdgShell = (xdg_wm_base*)wl_registry_bind(registry, name, - &xdg_wm_base_interface, 1); } else if (strcmp(interface, wl_data_device_manager_interface.name) == 0) { self->deviceManager = (wl_data_device_manager*)wl_registry_bind(registry, name, &wl_data_device_manager_interface, 1); @@ -395,10 +386,9 @@ void WindowManagerWayland::keyboardKey(void* data, wl_keyboard* keyboard, uint32 if (xkb_state_key_get_syms(self->_xkbState, key + 8, &syms) != 1) return; jwm::Key jwmKey = KeyWayland::fromNative(syms[0]); - // TODO: while unlikely, it could be possible that you can be entering text even if the // pointer hasn't entered - if (self->focusedSurface) { + if (self->keyboardFocus && jwmKey != jwm::Key::UNDEFINED) { jwm::KeyLocation location; JNILocal keyEvent( app.getJniEnv(), @@ -414,6 +404,30 @@ void WindowManagerWayland::keyboardKey(void* data, wl_keyboard* keyboard, uint32 if (window) window->dispatch(keyEvent.get()); } + if (state != WL_KEYBOARD_KEY_STATE_PRESSED) return; + + char textBuffer[0x40]; + int count = xkb_state_key_get_utf8(self->_xkbState, key + 8, textBuffer, sizeof(textBuffer)-1); + // ??? + if (count >= sizeof(textBuffer) - 1) { + return; + } + if (count > 0) { + // ignore sinful control symbols + if (textBuffer[0] != 127 && textBuffer[0] > 0x1f) { + JNIEnv* env = app.getJniEnv(); + + jwm::StringUTF16 converted = reinterpret_cast(textBuffer); + jwm::JNILocal jtext = converted.toJString(env); + + jwm::JNILocal eventTextInput(env, classes::EventTextInput::make(env, jtext.get())); + + + auto window = self->getWindowForNative(self->keyboardFocus); + if (window) + window->dispatch(eventTextInput.get()); + } + } } void WindowManagerWayland::keyboardModifiers(void* data, wl_keyboard* keyboard, @@ -459,9 +473,6 @@ void WindowManagerWayland::seatCapabilities(void* data, wl_seat* seat, uint32_t void WindowManagerWayland::seatName(void* data, wl_seat* seat, const char* name) { // who cares } -void WindowManagerWayland::xdgWmBasePing(void* data, xdg_wm_base* base, uint32_t serial) { - xdg_wm_base_pong(base, serial); -} std::vector WindowManagerWayland::getClipboardFormats() { /* XConvertSelection(display, diff --git a/wayland/cc/WindowManagerWayland.hh b/wayland/cc/WindowManagerWayland.hh index 3b470ea2..c3baf58b 100644 --- a/wayland/cc/WindowManagerWayland.hh +++ b/wayland/cc/WindowManagerWayland.hh @@ -13,7 +13,6 @@ #include "MouseCursor.hh" #include #include -#include "xdg-shell/xdg-shell.h" #include #include "Output.hh" #include @@ -79,8 +78,6 @@ namespace jwm { static void pointerHandleAxis(void* data, wl_pointer *pointer, uint32_t time, uint32_t axis, wl_fixed_t value); - static xdg_wm_base_listener _xdgWmBaseListener; - static void xdgWmBasePing(void* data, xdg_wm_base* base, uint32_t serial); static libdecor_interface _decorInterface; static void libdecorError(libdecor* context, enum libdecor_error error, const char* message); @@ -110,7 +107,6 @@ namespace jwm { wl_display* display = nullptr; wl_registry* registry = nullptr; wl_shm* shm = nullptr; - xdg_wm_base* xdgShell = nullptr; wl_compositor* compositor = nullptr; wl_data_device_manager* deviceManager = nullptr; wl_seat* seat = nullptr; diff --git a/wayland/cc/WindowWayland.cc b/wayland/cc/WindowWayland.cc index f95916f4..eab791a5 100644 --- a/wayland/cc/WindowWayland.cc +++ b/wayland/cc/WindowWayland.cc @@ -15,16 +15,7 @@ wl_surface_listener WindowWayland::_surfaceListener = { .preferred_buffer_scale = WindowWayland::surfacePreferredBufferScale, .preferred_buffer_transform = WindowWayland::surfacePreferredBufferTransform }; -xdg_surface_listener WindowWayland::_xdgSurfaceListener = { - .configure = WindowWayland::xdgSurfaceConfigure -}; -xdg_toplevel_listener WindowWayland::_xdgToplevelListener = { - .configure = WindowWayland::xdgToplevelConfigure, - .close = WindowWayland::xdgToplevelClose, - .configure_bounds = WindowWayland::xdgToplevelConfigureBounds, - .wm_capabilities = WindowWayland::xdgToplevelWmCapabilities -}; wl_output_listener WindowWayland::_outputListener = { .geometry = WindowWayland::outputGeometry, @@ -67,14 +58,6 @@ void WindowWayland::close() { wl_surface_destroy(_waylandWindow); } _waylandWindow = nullptr; - if (xdgSurface) { - xdg_surface_destroy(xdgSurface); - } - xdgSurface = nullptr; - if (xdgToplevel) { - xdg_toplevel_destroy(xdgToplevel); - } - xdgToplevel = nullptr; if (_frame) { libdecor_frame_unref(_frame); } @@ -125,8 +108,6 @@ bool WindowWayland::resize(int width, int height) { _width = width / _scale; _height = height / _scale; if (_visible) { - // HACK: while I could just dispatch here and save some cpu time, - // it's easier to just lease it out _adaptSize(_width, _height); } return true; @@ -176,6 +157,8 @@ void WindowWayland::show() _configured = false; libdecor_frame_map(_frame); libdecor_dispatch(_windowManager.decorCtx, -1); + if (_width > 0 && _height > 0) + resize(_width * _scale, _height * _scale); } ScreenInfo WindowWayland::getScreen() { @@ -245,50 +228,6 @@ void jwm::WindowWayland::surfacePreferredBufferScale(void* data, wl_surface* sur } void jwm::WindowWayland::surfacePreferredBufferTransform(void* data, wl_surface* surface, uint32_t transform) {} -void jwm::WindowWayland::xdgSurfaceConfigure(void* data, xdg_surface* surface, uint32_t serial) { - WindowWayland* self = (WindowWayland*) data; - // Commit state - if (self->_newWidth > 0 || self->_newHeight > 0) { - int goodWidth = self->_width, goodHeight = self->_height; - if (self->_newWidth > 0) - goodWidth = self->_newWidth; - if (self->_newHeight > 0) - goodHeight = self->_newHeight; - self->_adaptSize(goodWidth, goodHeight); - } - self->_newWidth = -1; - self->_newHeight = -1; - xdg_surface_ack_configure(surface, serial); - if (self->_layer) { - self->_layer->attachBuffer(); - } -} -void jwm::WindowWayland::xdgToplevelConfigure(void* data, xdg_toplevel* toplevel, int width, int height, wl_array* states) { - WindowWayland* self = (WindowWayland*) data; - if (width > 0) { - self->_newWidth = width; - } - if (height > 0) { - self->_newHeight = height; - } - // honestly idrc about the state -} -void jwm::WindowWayland::xdgToplevelClose(void* data, xdg_toplevel* toplevel) { - WindowWayland* self = reinterpret_cast(data); - self->dispatch(classes::EventWindowCloseRequest::kInstance); -} -void jwm::WindowWayland::xdgToplevelConfigureBounds(void* data, xdg_toplevel* toplevel, int width, int height) { - WindowWayland* self = (WindowWayland*) data; - if (width > 0) { - self->_newWidth = width; - } - if (height > 0) { - self->_newHeight = height; - } -} -void jwm::WindowWayland::xdgToplevelWmCapabilities(void* data, xdg_toplevel* toplevel, wl_array* array) { - // impl me : ) -} void jwm::WindowWayland::outputGeometry(void* data, wl_output* output, int x, int y, int pWidth, int pHeight, int subpixel, const char* make, const char* model, int transform) {} void jwm::WindowWayland::outputMode(void* data, wl_output* output, uint32_t flags, int width, int height, @@ -297,12 +236,7 @@ void jwm::WindowWayland::outputDone(void* data, wl_output* output) {} void jwm::WindowWayland::outputScale(void* data, wl_output* output, int factor) { WindowWayland* self = reinterpret_cast(data); self->_scale = factor; - if (self->_layer) { - self->_layer->resize(self->_width * factor, self->_height * factor); - if (self->_waylandWindow) { - wl_surface_set_buffer_scale(self->_waylandWindow, factor); - } - } + self->dispatch(classes::EventWindowScreenChange::kInstance); } void jwm::WindowWayland::outputName(void* data, wl_output* output, const char* name) {} void jwm::WindowWayland::outputDescription(void* data, wl_output* output, const char* desc) {} @@ -320,16 +254,18 @@ void jwm::WindowWayland::decorFrameConfigure(libdecor_frame* frame, libdecor_con libdecor_configuration_get_content_size(configuration, frame, &width, &height); - width = (width == 0) ? self->_floatingWidth : width; - height = (height == 0) ? self->_floatingHeight : height; + width = (width <= 0) ? self->_floatingWidth : width; + height = (height <= 0) ? self->_floatingHeight : height; libdecor_state* state = libdecor_state_new(width, height); libdecor_frame_commit(frame, state, configuration); libdecor_state_free(state); if (libdecor_frame_is_floating(frame)) { - self->_floatingWidth = width; - self->_floatingHeight = height; + if (width > 0) + self->_floatingWidth = width; + if (height > 0) + self->_floatingHeight = height; } if (!self->_configured && self->_layer) { @@ -358,9 +294,6 @@ void jwm::WindowWayland::_adaptSize(int newWidth, int newHeight) { _height = newHeight; int scaledWidth = _width * _scale; int scaledHeight = _height * _scale; - if (_layer) { - _layer->resize(scaledWidth, scaledHeight); - } jwm::JNILocal eventWindowResize( app.getJniEnv(), diff --git a/wayland/cc/WindowWayland.hh b/wayland/cc/WindowWayland.hh index c7b6f679..e04c366d 100644 --- a/wayland/cc/WindowWayland.hh +++ b/wayland/cc/WindowWayland.hh @@ -6,7 +6,6 @@ #include "WindowManagerWayland.hh" #include "ILayerWayland.hh" #include "ScreenInfo.hh" -#include "xdg-shell/xdg-shell.h" #include namespace jwm { class WindowWayland: public jwm::Window { @@ -59,13 +58,6 @@ namespace jwm { static void surfacePreferredBufferScale(void* data, wl_surface* surface, int factor); static void surfacePreferredBufferTransform(void* data, wl_surface* surface, uint32_t transform); - static void xdgSurfaceConfigure(void* data, xdg_surface* surface, uint32_t serial); - - static void xdgToplevelConfigure(void* data, xdg_toplevel* toplevel, int width, int height, wl_array* states); - static void xdgToplevelClose(void* data, xdg_toplevel* toplevel); - static void xdgToplevelConfigureBounds(void* data, xdg_toplevel* toplevel, int width, int height); - static void xdgToplevelWmCapabilities(void* data, xdg_toplevel* toplevel, wl_array* capabilities); - static void outputGeometry(void* data, wl_output* output, int x, int y, int physWidth, int physHeight, int subpixelOrient, const char* make, const char* model, int transform); static void outputMode(void* data, wl_output* output, uint32_t flags, int width, int height, int refresh); @@ -106,14 +98,10 @@ namespace jwm { WindowManagerWayland& _windowManager; ILayerWayland* _layer = nullptr; wl_surface* _waylandWindow = nullptr; - xdg_surface* xdgSurface = nullptr; - xdg_toplevel* xdgToplevel = nullptr; libdecor_frame* _frame = nullptr; Output* _output = nullptr; static wl_surface_listener _surfaceListener; - static xdg_surface_listener _xdgSurfaceListener; - static xdg_toplevel_listener _xdgToplevelListener; static wl_output_listener _outputListener; static libdecor_frame_interface _libdecorFrameInterface; diff --git a/wayland/cc/xdg-shell/xdg-shell.c b/wayland/cc/xdg-shell/xdg-shell.c deleted file mode 100644 index 03826cdc..00000000 --- a/wayland/cc/xdg-shell/xdg-shell.c +++ /dev/null @@ -1,183 +0,0 @@ -/* Generated by wayland-scanner 1.22.0 */ - -/* - * Copyright © 2008-2013 Kristian Høgsberg - * Copyright © 2013 Rafael Antognolli - * Copyright © 2013 Jasper St. Pierre - * Copyright © 2010-2013 Intel Corporation - * Copyright © 2015-2017 Samsung Electronics Co., Ltd - * Copyright © 2015-2017 Red Hat Inc. - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - */ - -#include -#include -#include "wayland-util.h" - -#ifndef __has_attribute -# define __has_attribute(x) 0 /* Compatibility with non-clang compilers. */ -#endif - -#if (__has_attribute(visibility) || defined(__GNUC__) && __GNUC__ >= 4) -#define WL_PRIVATE __attribute__ ((visibility("hidden"))) -#else -#define WL_PRIVATE -#endif - -extern const struct wl_interface wl_output_interface; -extern const struct wl_interface wl_seat_interface; -extern const struct wl_interface wl_surface_interface; -extern const struct wl_interface xdg_popup_interface; -extern const struct wl_interface xdg_positioner_interface; -extern const struct wl_interface xdg_surface_interface; -extern const struct wl_interface xdg_toplevel_interface; - -static const struct wl_interface *xdg_shell_types[] = { - NULL, - NULL, - NULL, - NULL, - &xdg_positioner_interface, - &xdg_surface_interface, - &wl_surface_interface, - &xdg_toplevel_interface, - &xdg_popup_interface, - &xdg_surface_interface, - &xdg_positioner_interface, - &xdg_toplevel_interface, - &wl_seat_interface, - NULL, - NULL, - NULL, - &wl_seat_interface, - NULL, - &wl_seat_interface, - NULL, - NULL, - &wl_output_interface, - &wl_seat_interface, - NULL, - &xdg_positioner_interface, - NULL, -}; - -static const struct wl_message xdg_wm_base_requests[] = { - { "destroy", "", xdg_shell_types + 0 }, - { "create_positioner", "n", xdg_shell_types + 4 }, - { "get_xdg_surface", "no", xdg_shell_types + 5 }, - { "pong", "u", xdg_shell_types + 0 }, -}; - -static const struct wl_message xdg_wm_base_events[] = { - { "ping", "u", xdg_shell_types + 0 }, -}; - -WL_PRIVATE const struct wl_interface xdg_wm_base_interface = { - "xdg_wm_base", 6, - 4, xdg_wm_base_requests, - 1, xdg_wm_base_events, -}; - -static const struct wl_message xdg_positioner_requests[] = { - { "destroy", "", xdg_shell_types + 0 }, - { "set_size", "ii", xdg_shell_types + 0 }, - { "set_anchor_rect", "iiii", xdg_shell_types + 0 }, - { "set_anchor", "u", xdg_shell_types + 0 }, - { "set_gravity", "u", xdg_shell_types + 0 }, - { "set_constraint_adjustment", "u", xdg_shell_types + 0 }, - { "set_offset", "ii", xdg_shell_types + 0 }, - { "set_reactive", "3", xdg_shell_types + 0 }, - { "set_parent_size", "3ii", xdg_shell_types + 0 }, - { "set_parent_configure", "3u", xdg_shell_types + 0 }, -}; - -WL_PRIVATE const struct wl_interface xdg_positioner_interface = { - "xdg_positioner", 6, - 10, xdg_positioner_requests, - 0, NULL, -}; - -static const struct wl_message xdg_surface_requests[] = { - { "destroy", "", xdg_shell_types + 0 }, - { "get_toplevel", "n", xdg_shell_types + 7 }, - { "get_popup", "n?oo", xdg_shell_types + 8 }, - { "set_window_geometry", "iiii", xdg_shell_types + 0 }, - { "ack_configure", "u", xdg_shell_types + 0 }, -}; - -static const struct wl_message xdg_surface_events[] = { - { "configure", "u", xdg_shell_types + 0 }, -}; - -WL_PRIVATE const struct wl_interface xdg_surface_interface = { - "xdg_surface", 6, - 5, xdg_surface_requests, - 1, xdg_surface_events, -}; - -static const struct wl_message xdg_toplevel_requests[] = { - { "destroy", "", xdg_shell_types + 0 }, - { "set_parent", "?o", xdg_shell_types + 11 }, - { "set_title", "s", xdg_shell_types + 0 }, - { "set_app_id", "s", xdg_shell_types + 0 }, - { "show_window_menu", "ouii", xdg_shell_types + 12 }, - { "move", "ou", xdg_shell_types + 16 }, - { "resize", "ouu", xdg_shell_types + 18 }, - { "set_max_size", "ii", xdg_shell_types + 0 }, - { "set_min_size", "ii", xdg_shell_types + 0 }, - { "set_maximized", "", xdg_shell_types + 0 }, - { "unset_maximized", "", xdg_shell_types + 0 }, - { "set_fullscreen", "?o", xdg_shell_types + 21 }, - { "unset_fullscreen", "", xdg_shell_types + 0 }, - { "set_minimized", "", xdg_shell_types + 0 }, -}; - -static const struct wl_message xdg_toplevel_events[] = { - { "configure", "iia", xdg_shell_types + 0 }, - { "close", "", xdg_shell_types + 0 }, - { "configure_bounds", "4ii", xdg_shell_types + 0 }, - { "wm_capabilities", "5a", xdg_shell_types + 0 }, -}; - -WL_PRIVATE const struct wl_interface xdg_toplevel_interface = { - "xdg_toplevel", 6, - 14, xdg_toplevel_requests, - 4, xdg_toplevel_events, -}; - -static const struct wl_message xdg_popup_requests[] = { - { "destroy", "", xdg_shell_types + 0 }, - { "grab", "ou", xdg_shell_types + 22 }, - { "reposition", "3ou", xdg_shell_types + 24 }, -}; - -static const struct wl_message xdg_popup_events[] = { - { "configure", "iiii", xdg_shell_types + 0 }, - { "popup_done", "", xdg_shell_types + 0 }, - { "repositioned", "3u", xdg_shell_types + 0 }, -}; - -WL_PRIVATE const struct wl_interface xdg_popup_interface = { - "xdg_popup", 6, - 3, xdg_popup_requests, - 3, xdg_popup_events, -}; - diff --git a/wayland/cc/xdg-shell/xdg-shell.h b/wayland/cc/xdg-shell/xdg-shell.h deleted file mode 100644 index ea75d476..00000000 --- a/wayland/cc/xdg-shell/xdg-shell.h +++ /dev/null @@ -1,2307 +0,0 @@ -/* Generated by wayland-scanner 1.22.0 */ - -#ifndef XDG_SHELL_CLIENT_PROTOCOL_H -#define XDG_SHELL_CLIENT_PROTOCOL_H - -#include -#include -#include "wayland-client.h" - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * @page page_xdg_shell The xdg_shell protocol - * @section page_ifaces_xdg_shell Interfaces - * - @subpage page_iface_xdg_wm_base - create desktop-style surfaces - * - @subpage page_iface_xdg_positioner - child surface positioner - * - @subpage page_iface_xdg_surface - desktop user interface surface base interface - * - @subpage page_iface_xdg_toplevel - toplevel surface - * - @subpage page_iface_xdg_popup - short-lived, popup surfaces for menus - * @section page_copyright_xdg_shell Copyright - *
- *
- * Copyright © 2008-2013 Kristian Høgsberg
- * Copyright © 2013      Rafael Antognolli
- * Copyright © 2013      Jasper St. Pierre
- * Copyright © 2010-2013 Intel Corporation
- * Copyright © 2015-2017 Samsung Electronics Co., Ltd
- * Copyright © 2015-2017 Red Hat Inc.
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice (including the next
- * paragraph) shall be included in all copies or substantial portions of the
- * Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- * 
- */ -struct wl_output; -struct wl_seat; -struct wl_surface; -struct xdg_popup; -struct xdg_positioner; -struct xdg_surface; -struct xdg_toplevel; -struct xdg_wm_base; - -#ifndef XDG_WM_BASE_INTERFACE -#define XDG_WM_BASE_INTERFACE -/** - * @page page_iface_xdg_wm_base xdg_wm_base - * @section page_iface_xdg_wm_base_desc Description - * - * The xdg_wm_base interface is exposed as a global object enabling clients - * to turn their wl_surfaces into windows in a desktop environment. It - * defines the basic functionality needed for clients and the compositor to - * create windows that can be dragged, resized, maximized, etc, as well as - * creating transient windows such as popup menus. - * @section page_iface_xdg_wm_base_api API - * See @ref iface_xdg_wm_base. - */ -/** - * @defgroup iface_xdg_wm_base The xdg_wm_base interface - * - * The xdg_wm_base interface is exposed as a global object enabling clients - * to turn their wl_surfaces into windows in a desktop environment. It - * defines the basic functionality needed for clients and the compositor to - * create windows that can be dragged, resized, maximized, etc, as well as - * creating transient windows such as popup menus. - */ -extern const struct wl_interface xdg_wm_base_interface; -#endif -#ifndef XDG_POSITIONER_INTERFACE -#define XDG_POSITIONER_INTERFACE -/** - * @page page_iface_xdg_positioner xdg_positioner - * @section page_iface_xdg_positioner_desc Description - * - * The xdg_positioner provides a collection of rules for the placement of a - * child surface relative to a parent surface. Rules can be defined to ensure - * the child surface remains within the visible area's borders, and to - * specify how the child surface changes its position, such as sliding along - * an axis, or flipping around a rectangle. These positioner-created rules are - * constrained by the requirement that a child surface must intersect with or - * be at least partially adjacent to its parent surface. - * - * See the various requests for details about possible rules. - * - * At the time of the request, the compositor makes a copy of the rules - * specified by the xdg_positioner. Thus, after the request is complete the - * xdg_positioner object can be destroyed or reused; further changes to the - * object will have no effect on previous usages. - * - * For an xdg_positioner object to be considered complete, it must have a - * non-zero size set by set_size, and a non-zero anchor rectangle set by - * set_anchor_rect. Passing an incomplete xdg_positioner object when - * positioning a surface raises an invalid_positioner error. - * @section page_iface_xdg_positioner_api API - * See @ref iface_xdg_positioner. - */ -/** - * @defgroup iface_xdg_positioner The xdg_positioner interface - * - * The xdg_positioner provides a collection of rules for the placement of a - * child surface relative to a parent surface. Rules can be defined to ensure - * the child surface remains within the visible area's borders, and to - * specify how the child surface changes its position, such as sliding along - * an axis, or flipping around a rectangle. These positioner-created rules are - * constrained by the requirement that a child surface must intersect with or - * be at least partially adjacent to its parent surface. - * - * See the various requests for details about possible rules. - * - * At the time of the request, the compositor makes a copy of the rules - * specified by the xdg_positioner. Thus, after the request is complete the - * xdg_positioner object can be destroyed or reused; further changes to the - * object will have no effect on previous usages. - * - * For an xdg_positioner object to be considered complete, it must have a - * non-zero size set by set_size, and a non-zero anchor rectangle set by - * set_anchor_rect. Passing an incomplete xdg_positioner object when - * positioning a surface raises an invalid_positioner error. - */ -extern const struct wl_interface xdg_positioner_interface; -#endif -#ifndef XDG_SURFACE_INTERFACE -#define XDG_SURFACE_INTERFACE -/** - * @page page_iface_xdg_surface xdg_surface - * @section page_iface_xdg_surface_desc Description - * - * An interface that may be implemented by a wl_surface, for - * implementations that provide a desktop-style user interface. - * - * It provides a base set of functionality required to construct user - * interface elements requiring management by the compositor, such as - * toplevel windows, menus, etc. The types of functionality are split into - * xdg_surface roles. - * - * Creating an xdg_surface does not set the role for a wl_surface. In order - * to map an xdg_surface, the client must create a role-specific object - * using, e.g., get_toplevel, get_popup. The wl_surface for any given - * xdg_surface can have at most one role, and may not be assigned any role - * not based on xdg_surface. - * - * A role must be assigned before any other requests are made to the - * xdg_surface object. - * - * The client must call wl_surface.commit on the corresponding wl_surface - * for the xdg_surface state to take effect. - * - * Creating an xdg_surface from a wl_surface which has a buffer attached or - * committed is a client error, and any attempts by a client to attach or - * manipulate a buffer prior to the first xdg_surface.configure call must - * also be treated as errors. - * - * After creating a role-specific object and setting it up, the client must - * perform an initial commit without any buffer attached. The compositor - * will reply with initial wl_surface state such as - * wl_surface.preferred_buffer_scale followed by an xdg_surface.configure - * event. The client must acknowledge it and is then allowed to attach a - * buffer to map the surface. - * - * Mapping an xdg_surface-based role surface is defined as making it - * possible for the surface to be shown by the compositor. Note that - * a mapped surface is not guaranteed to be visible once it is mapped. - * - * For an xdg_surface to be mapped by the compositor, the following - * conditions must be met: - * (1) the client has assigned an xdg_surface-based role to the surface - * (2) the client has set and committed the xdg_surface state and the - * role-dependent state to the surface - * (3) the client has committed a buffer to the surface - * - * A newly-unmapped surface is considered to have met condition (1) out - * of the 3 required conditions for mapping a surface if its role surface - * has not been destroyed, i.e. the client must perform the initial commit - * again before attaching a buffer. - * @section page_iface_xdg_surface_api API - * See @ref iface_xdg_surface. - */ -/** - * @defgroup iface_xdg_surface The xdg_surface interface - * - * An interface that may be implemented by a wl_surface, for - * implementations that provide a desktop-style user interface. - * - * It provides a base set of functionality required to construct user - * interface elements requiring management by the compositor, such as - * toplevel windows, menus, etc. The types of functionality are split into - * xdg_surface roles. - * - * Creating an xdg_surface does not set the role for a wl_surface. In order - * to map an xdg_surface, the client must create a role-specific object - * using, e.g., get_toplevel, get_popup. The wl_surface for any given - * xdg_surface can have at most one role, and may not be assigned any role - * not based on xdg_surface. - * - * A role must be assigned before any other requests are made to the - * xdg_surface object. - * - * The client must call wl_surface.commit on the corresponding wl_surface - * for the xdg_surface state to take effect. - * - * Creating an xdg_surface from a wl_surface which has a buffer attached or - * committed is a client error, and any attempts by a client to attach or - * manipulate a buffer prior to the first xdg_surface.configure call must - * also be treated as errors. - * - * After creating a role-specific object and setting it up, the client must - * perform an initial commit without any buffer attached. The compositor - * will reply with initial wl_surface state such as - * wl_surface.preferred_buffer_scale followed by an xdg_surface.configure - * event. The client must acknowledge it and is then allowed to attach a - * buffer to map the surface. - * - * Mapping an xdg_surface-based role surface is defined as making it - * possible for the surface to be shown by the compositor. Note that - * a mapped surface is not guaranteed to be visible once it is mapped. - * - * For an xdg_surface to be mapped by the compositor, the following - * conditions must be met: - * (1) the client has assigned an xdg_surface-based role to the surface - * (2) the client has set and committed the xdg_surface state and the - * role-dependent state to the surface - * (3) the client has committed a buffer to the surface - * - * A newly-unmapped surface is considered to have met condition (1) out - * of the 3 required conditions for mapping a surface if its role surface - * has not been destroyed, i.e. the client must perform the initial commit - * again before attaching a buffer. - */ -extern const struct wl_interface xdg_surface_interface; -#endif -#ifndef XDG_TOPLEVEL_INTERFACE -#define XDG_TOPLEVEL_INTERFACE -/** - * @page page_iface_xdg_toplevel xdg_toplevel - * @section page_iface_xdg_toplevel_desc Description - * - * This interface defines an xdg_surface role which allows a surface to, - * among other things, set window-like properties such as maximize, - * fullscreen, and minimize, set application-specific metadata like title and - * id, and well as trigger user interactive operations such as interactive - * resize and move. - * - * Unmapping an xdg_toplevel means that the surface cannot be shown - * by the compositor until it is explicitly mapped again. - * All active operations (e.g., move, resize) are canceled and all - * attributes (e.g. title, state, stacking, ...) are discarded for - * an xdg_toplevel surface when it is unmapped. The xdg_toplevel returns to - * the state it had right after xdg_surface.get_toplevel. The client - * can re-map the toplevel by perfoming a commit without any buffer - * attached, waiting for a configure event and handling it as usual (see - * xdg_surface description). - * - * Attaching a null buffer to a toplevel unmaps the surface. - * @section page_iface_xdg_toplevel_api API - * See @ref iface_xdg_toplevel. - */ -/** - * @defgroup iface_xdg_toplevel The xdg_toplevel interface - * - * This interface defines an xdg_surface role which allows a surface to, - * among other things, set window-like properties such as maximize, - * fullscreen, and minimize, set application-specific metadata like title and - * id, and well as trigger user interactive operations such as interactive - * resize and move. - * - * Unmapping an xdg_toplevel means that the surface cannot be shown - * by the compositor until it is explicitly mapped again. - * All active operations (e.g., move, resize) are canceled and all - * attributes (e.g. title, state, stacking, ...) are discarded for - * an xdg_toplevel surface when it is unmapped. The xdg_toplevel returns to - * the state it had right after xdg_surface.get_toplevel. The client - * can re-map the toplevel by perfoming a commit without any buffer - * attached, waiting for a configure event and handling it as usual (see - * xdg_surface description). - * - * Attaching a null buffer to a toplevel unmaps the surface. - */ -extern const struct wl_interface xdg_toplevel_interface; -#endif -#ifndef XDG_POPUP_INTERFACE -#define XDG_POPUP_INTERFACE -/** - * @page page_iface_xdg_popup xdg_popup - * @section page_iface_xdg_popup_desc Description - * - * A popup surface is a short-lived, temporary surface. It can be used to - * implement for example menus, popovers, tooltips and other similar user - * interface concepts. - * - * A popup can be made to take an explicit grab. See xdg_popup.grab for - * details. - * - * When the popup is dismissed, a popup_done event will be sent out, and at - * the same time the surface will be unmapped. See the xdg_popup.popup_done - * event for details. - * - * Explicitly destroying the xdg_popup object will also dismiss the popup and - * unmap the surface. Clients that want to dismiss the popup when another - * surface of their own is clicked should dismiss the popup using the destroy - * request. - * - * A newly created xdg_popup will be stacked on top of all previously created - * xdg_popup surfaces associated with the same xdg_toplevel. - * - * The parent of an xdg_popup must be mapped (see the xdg_surface - * description) before the xdg_popup itself. - * - * The client must call wl_surface.commit on the corresponding wl_surface - * for the xdg_popup state to take effect. - * @section page_iface_xdg_popup_api API - * See @ref iface_xdg_popup. - */ -/** - * @defgroup iface_xdg_popup The xdg_popup interface - * - * A popup surface is a short-lived, temporary surface. It can be used to - * implement for example menus, popovers, tooltips and other similar user - * interface concepts. - * - * A popup can be made to take an explicit grab. See xdg_popup.grab for - * details. - * - * When the popup is dismissed, a popup_done event will be sent out, and at - * the same time the surface will be unmapped. See the xdg_popup.popup_done - * event for details. - * - * Explicitly destroying the xdg_popup object will also dismiss the popup and - * unmap the surface. Clients that want to dismiss the popup when another - * surface of their own is clicked should dismiss the popup using the destroy - * request. - * - * A newly created xdg_popup will be stacked on top of all previously created - * xdg_popup surfaces associated with the same xdg_toplevel. - * - * The parent of an xdg_popup must be mapped (see the xdg_surface - * description) before the xdg_popup itself. - * - * The client must call wl_surface.commit on the corresponding wl_surface - * for the xdg_popup state to take effect. - */ -extern const struct wl_interface xdg_popup_interface; -#endif - -#ifndef XDG_WM_BASE_ERROR_ENUM -#define XDG_WM_BASE_ERROR_ENUM -enum xdg_wm_base_error { - /** - * given wl_surface has another role - */ - XDG_WM_BASE_ERROR_ROLE = 0, - /** - * xdg_wm_base was destroyed before children - */ - XDG_WM_BASE_ERROR_DEFUNCT_SURFACES = 1, - /** - * the client tried to map or destroy a non-topmost popup - */ - XDG_WM_BASE_ERROR_NOT_THE_TOPMOST_POPUP = 2, - /** - * the client specified an invalid popup parent surface - */ - XDG_WM_BASE_ERROR_INVALID_POPUP_PARENT = 3, - /** - * the client provided an invalid surface state - */ - XDG_WM_BASE_ERROR_INVALID_SURFACE_STATE = 4, - /** - * the client provided an invalid positioner - */ - XDG_WM_BASE_ERROR_INVALID_POSITIONER = 5, - /** - * the client didn’t respond to a ping event in time - */ - XDG_WM_BASE_ERROR_UNRESPONSIVE = 6, -}; -#endif /* XDG_WM_BASE_ERROR_ENUM */ - -/** - * @ingroup iface_xdg_wm_base - * @struct xdg_wm_base_listener - */ -struct xdg_wm_base_listener { - /** - * check if the client is alive - * - * The ping event asks the client if it's still alive. Pass the - * serial specified in the event back to the compositor by sending - * a "pong" request back with the specified serial. See - * xdg_wm_base.pong. - * - * Compositors can use this to determine if the client is still - * alive. It's unspecified what will happen if the client doesn't - * respond to the ping request, or in what timeframe. Clients - * should try to respond in a reasonable amount of time. The - * “unresponsive” error is provided for compositors that wish - * to disconnect unresponsive clients. - * - * A compositor is free to ping in any way it wants, but a client - * must always respond to any xdg_wm_base object it created. - * @param serial pass this to the pong request - */ - void (*ping)(void *data, - struct xdg_wm_base *xdg_wm_base, - uint32_t serial); -}; - -/** - * @ingroup iface_xdg_wm_base - */ -static inline int -xdg_wm_base_add_listener(struct xdg_wm_base *xdg_wm_base, - const struct xdg_wm_base_listener *listener, void *data) -{ - return wl_proxy_add_listener((struct wl_proxy *) xdg_wm_base, - (void (**)(void)) listener, data); -} - -#define XDG_WM_BASE_DESTROY 0 -#define XDG_WM_BASE_CREATE_POSITIONER 1 -#define XDG_WM_BASE_GET_XDG_SURFACE 2 -#define XDG_WM_BASE_PONG 3 - -/** - * @ingroup iface_xdg_wm_base - */ -#define XDG_WM_BASE_PING_SINCE_VERSION 1 - -/** - * @ingroup iface_xdg_wm_base - */ -#define XDG_WM_BASE_DESTROY_SINCE_VERSION 1 -/** - * @ingroup iface_xdg_wm_base - */ -#define XDG_WM_BASE_CREATE_POSITIONER_SINCE_VERSION 1 -/** - * @ingroup iface_xdg_wm_base - */ -#define XDG_WM_BASE_GET_XDG_SURFACE_SINCE_VERSION 1 -/** - * @ingroup iface_xdg_wm_base - */ -#define XDG_WM_BASE_PONG_SINCE_VERSION 1 - -/** @ingroup iface_xdg_wm_base */ -static inline void -xdg_wm_base_set_user_data(struct xdg_wm_base *xdg_wm_base, void *user_data) -{ - wl_proxy_set_user_data((struct wl_proxy *) xdg_wm_base, user_data); -} - -/** @ingroup iface_xdg_wm_base */ -static inline void * -xdg_wm_base_get_user_data(struct xdg_wm_base *xdg_wm_base) -{ - return wl_proxy_get_user_data((struct wl_proxy *) xdg_wm_base); -} - -static inline uint32_t -xdg_wm_base_get_version(struct xdg_wm_base *xdg_wm_base) -{ - return wl_proxy_get_version((struct wl_proxy *) xdg_wm_base); -} - -/** - * @ingroup iface_xdg_wm_base - * - * Destroy this xdg_wm_base object. - * - * Destroying a bound xdg_wm_base object while there are surfaces - * still alive created by this xdg_wm_base object instance is illegal - * and will result in a defunct_surfaces error. - */ -static inline void -xdg_wm_base_destroy(struct xdg_wm_base *xdg_wm_base) -{ - wl_proxy_marshal_flags((struct wl_proxy *) xdg_wm_base, - XDG_WM_BASE_DESTROY, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_wm_base), WL_MARSHAL_FLAG_DESTROY); -} - -/** - * @ingroup iface_xdg_wm_base - * - * Create a positioner object. A positioner object is used to position - * surfaces relative to some parent surface. See the interface description - * and xdg_surface.get_popup for details. - */ -static inline struct xdg_positioner * -xdg_wm_base_create_positioner(struct xdg_wm_base *xdg_wm_base) -{ - struct wl_proxy *id; - - id = wl_proxy_marshal_flags((struct wl_proxy *) xdg_wm_base, - XDG_WM_BASE_CREATE_POSITIONER, &xdg_positioner_interface, wl_proxy_get_version((struct wl_proxy *) xdg_wm_base), 0, NULL); - - return (struct xdg_positioner *) id; -} - -/** - * @ingroup iface_xdg_wm_base - * - * This creates an xdg_surface for the given surface. While xdg_surface - * itself is not a role, the corresponding surface may only be assigned - * a role extending xdg_surface, such as xdg_toplevel or xdg_popup. It is - * illegal to create an xdg_surface for a wl_surface which already has an - * assigned role and this will result in a role error. - * - * This creates an xdg_surface for the given surface. An xdg_surface is - * used as basis to define a role to a given surface, such as xdg_toplevel - * or xdg_popup. It also manages functionality shared between xdg_surface - * based surface roles. - * - * See the documentation of xdg_surface for more details about what an - * xdg_surface is and how it is used. - */ -static inline struct xdg_surface * -xdg_wm_base_get_xdg_surface(struct xdg_wm_base *xdg_wm_base, struct wl_surface *surface) -{ - struct wl_proxy *id; - - id = wl_proxy_marshal_flags((struct wl_proxy *) xdg_wm_base, - XDG_WM_BASE_GET_XDG_SURFACE, &xdg_surface_interface, wl_proxy_get_version((struct wl_proxy *) xdg_wm_base), 0, NULL, surface); - - return (struct xdg_surface *) id; -} - -/** - * @ingroup iface_xdg_wm_base - * - * A client must respond to a ping event with a pong request or - * the client may be deemed unresponsive. See xdg_wm_base.ping - * and xdg_wm_base.error.unresponsive. - */ -static inline void -xdg_wm_base_pong(struct xdg_wm_base *xdg_wm_base, uint32_t serial) -{ - wl_proxy_marshal_flags((struct wl_proxy *) xdg_wm_base, - XDG_WM_BASE_PONG, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_wm_base), 0, serial); -} - -#ifndef XDG_POSITIONER_ERROR_ENUM -#define XDG_POSITIONER_ERROR_ENUM -enum xdg_positioner_error { - /** - * invalid input provided - */ - XDG_POSITIONER_ERROR_INVALID_INPUT = 0, -}; -#endif /* XDG_POSITIONER_ERROR_ENUM */ - -#ifndef XDG_POSITIONER_ANCHOR_ENUM -#define XDG_POSITIONER_ANCHOR_ENUM -enum xdg_positioner_anchor { - XDG_POSITIONER_ANCHOR_NONE = 0, - XDG_POSITIONER_ANCHOR_TOP = 1, - XDG_POSITIONER_ANCHOR_BOTTOM = 2, - XDG_POSITIONER_ANCHOR_LEFT = 3, - XDG_POSITIONER_ANCHOR_RIGHT = 4, - XDG_POSITIONER_ANCHOR_TOP_LEFT = 5, - XDG_POSITIONER_ANCHOR_BOTTOM_LEFT = 6, - XDG_POSITIONER_ANCHOR_TOP_RIGHT = 7, - XDG_POSITIONER_ANCHOR_BOTTOM_RIGHT = 8, -}; -#endif /* XDG_POSITIONER_ANCHOR_ENUM */ - -#ifndef XDG_POSITIONER_GRAVITY_ENUM -#define XDG_POSITIONER_GRAVITY_ENUM -enum xdg_positioner_gravity { - XDG_POSITIONER_GRAVITY_NONE = 0, - XDG_POSITIONER_GRAVITY_TOP = 1, - XDG_POSITIONER_GRAVITY_BOTTOM = 2, - XDG_POSITIONER_GRAVITY_LEFT = 3, - XDG_POSITIONER_GRAVITY_RIGHT = 4, - XDG_POSITIONER_GRAVITY_TOP_LEFT = 5, - XDG_POSITIONER_GRAVITY_BOTTOM_LEFT = 6, - XDG_POSITIONER_GRAVITY_TOP_RIGHT = 7, - XDG_POSITIONER_GRAVITY_BOTTOM_RIGHT = 8, -}; -#endif /* XDG_POSITIONER_GRAVITY_ENUM */ - -#ifndef XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_ENUM -#define XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_ENUM -/** - * @ingroup iface_xdg_positioner - * constraint adjustments - * - * The constraint adjustment value define ways the compositor will adjust - * the position of the surface, if the unadjusted position would result - * in the surface being partly constrained. - * - * Whether a surface is considered 'constrained' is left to the compositor - * to determine. For example, the surface may be partly outside the - * compositor's defined 'work area', thus necessitating the child surface's - * position be adjusted until it is entirely inside the work area. - * - * The adjustments can be combined, according to a defined precedence: 1) - * Flip, 2) Slide, 3) Resize. - */ -enum xdg_positioner_constraint_adjustment { - /** - * don't move the child surface when constrained - * - * Don't alter the surface position even if it is constrained on - * some axis, for example partially outside the edge of an output. - */ - XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_NONE = 0, - /** - * move along the x axis until unconstrained - * - * Slide the surface along the x axis until it is no longer - * constrained. - * - * First try to slide towards the direction of the gravity on the x - * axis until either the edge in the opposite direction of the - * gravity is unconstrained or the edge in the direction of the - * gravity is constrained. - * - * Then try to slide towards the opposite direction of the gravity - * on the x axis until either the edge in the direction of the - * gravity is unconstrained or the edge in the opposite direction - * of the gravity is constrained. - */ - XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_X = 1, - /** - * move along the y axis until unconstrained - * - * Slide the surface along the y axis until it is no longer - * constrained. - * - * First try to slide towards the direction of the gravity on the y - * axis until either the edge in the opposite direction of the - * gravity is unconstrained or the edge in the direction of the - * gravity is constrained. - * - * Then try to slide towards the opposite direction of the gravity - * on the y axis until either the edge in the direction of the - * gravity is unconstrained or the edge in the opposite direction - * of the gravity is constrained. - */ - XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_Y = 2, - /** - * invert the anchor and gravity on the x axis - * - * Invert the anchor and gravity on the x axis if the surface is - * constrained on the x axis. For example, if the left edge of the - * surface is constrained, the gravity is 'left' and the anchor is - * 'left', change the gravity to 'right' and the anchor to 'right'. - * - * If the adjusted position also ends up being constrained, the - * resulting position of the flip_x adjustment will be the one - * before the adjustment. - */ - XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_FLIP_X = 4, - /** - * invert the anchor and gravity on the y axis - * - * Invert the anchor and gravity on the y axis if the surface is - * constrained on the y axis. For example, if the bottom edge of - * the surface is constrained, the gravity is 'bottom' and the - * anchor is 'bottom', change the gravity to 'top' and the anchor - * to 'top'. - * - * The adjusted position is calculated given the original anchor - * rectangle and offset, but with the new flipped anchor and - * gravity values. - * - * If the adjusted position also ends up being constrained, the - * resulting position of the flip_y adjustment will be the one - * before the adjustment. - */ - XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_FLIP_Y = 8, - /** - * horizontally resize the surface - * - * Resize the surface horizontally so that it is completely - * unconstrained. - */ - XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_RESIZE_X = 16, - /** - * vertically resize the surface - * - * Resize the surface vertically so that it is completely - * unconstrained. - */ - XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_RESIZE_Y = 32, -}; -#endif /* XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_ENUM */ - -#define XDG_POSITIONER_DESTROY 0 -#define XDG_POSITIONER_SET_SIZE 1 -#define XDG_POSITIONER_SET_ANCHOR_RECT 2 -#define XDG_POSITIONER_SET_ANCHOR 3 -#define XDG_POSITIONER_SET_GRAVITY 4 -#define XDG_POSITIONER_SET_CONSTRAINT_ADJUSTMENT 5 -#define XDG_POSITIONER_SET_OFFSET 6 -#define XDG_POSITIONER_SET_REACTIVE 7 -#define XDG_POSITIONER_SET_PARENT_SIZE 8 -#define XDG_POSITIONER_SET_PARENT_CONFIGURE 9 - - -/** - * @ingroup iface_xdg_positioner - */ -#define XDG_POSITIONER_DESTROY_SINCE_VERSION 1 -/** - * @ingroup iface_xdg_positioner - */ -#define XDG_POSITIONER_SET_SIZE_SINCE_VERSION 1 -/** - * @ingroup iface_xdg_positioner - */ -#define XDG_POSITIONER_SET_ANCHOR_RECT_SINCE_VERSION 1 -/** - * @ingroup iface_xdg_positioner - */ -#define XDG_POSITIONER_SET_ANCHOR_SINCE_VERSION 1 -/** - * @ingroup iface_xdg_positioner - */ -#define XDG_POSITIONER_SET_GRAVITY_SINCE_VERSION 1 -/** - * @ingroup iface_xdg_positioner - */ -#define XDG_POSITIONER_SET_CONSTRAINT_ADJUSTMENT_SINCE_VERSION 1 -/** - * @ingroup iface_xdg_positioner - */ -#define XDG_POSITIONER_SET_OFFSET_SINCE_VERSION 1 -/** - * @ingroup iface_xdg_positioner - */ -#define XDG_POSITIONER_SET_REACTIVE_SINCE_VERSION 3 -/** - * @ingroup iface_xdg_positioner - */ -#define XDG_POSITIONER_SET_PARENT_SIZE_SINCE_VERSION 3 -/** - * @ingroup iface_xdg_positioner - */ -#define XDG_POSITIONER_SET_PARENT_CONFIGURE_SINCE_VERSION 3 - -/** @ingroup iface_xdg_positioner */ -static inline void -xdg_positioner_set_user_data(struct xdg_positioner *xdg_positioner, void *user_data) -{ - wl_proxy_set_user_data((struct wl_proxy *) xdg_positioner, user_data); -} - -/** @ingroup iface_xdg_positioner */ -static inline void * -xdg_positioner_get_user_data(struct xdg_positioner *xdg_positioner) -{ - return wl_proxy_get_user_data((struct wl_proxy *) xdg_positioner); -} - -static inline uint32_t -xdg_positioner_get_version(struct xdg_positioner *xdg_positioner) -{ - return wl_proxy_get_version((struct wl_proxy *) xdg_positioner); -} - -/** - * @ingroup iface_xdg_positioner - * - * Notify the compositor that the xdg_positioner will no longer be used. - */ -static inline void -xdg_positioner_destroy(struct xdg_positioner *xdg_positioner) -{ - wl_proxy_marshal_flags((struct wl_proxy *) xdg_positioner, - XDG_POSITIONER_DESTROY, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_positioner), WL_MARSHAL_FLAG_DESTROY); -} - -/** - * @ingroup iface_xdg_positioner - * - * Set the size of the surface that is to be positioned with the positioner - * object. The size is in surface-local coordinates and corresponds to the - * window geometry. See xdg_surface.set_window_geometry. - * - * If a zero or negative size is set the invalid_input error is raised. - */ -static inline void -xdg_positioner_set_size(struct xdg_positioner *xdg_positioner, int32_t width, int32_t height) -{ - wl_proxy_marshal_flags((struct wl_proxy *) xdg_positioner, - XDG_POSITIONER_SET_SIZE, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_positioner), 0, width, height); -} - -/** - * @ingroup iface_xdg_positioner - * - * Specify the anchor rectangle within the parent surface that the child - * surface will be placed relative to. The rectangle is relative to the - * window geometry as defined by xdg_surface.set_window_geometry of the - * parent surface. - * - * When the xdg_positioner object is used to position a child surface, the - * anchor rectangle may not extend outside the window geometry of the - * positioned child's parent surface. - * - * If a negative size is set the invalid_input error is raised. - */ -static inline void -xdg_positioner_set_anchor_rect(struct xdg_positioner *xdg_positioner, int32_t x, int32_t y, int32_t width, int32_t height) -{ - wl_proxy_marshal_flags((struct wl_proxy *) xdg_positioner, - XDG_POSITIONER_SET_ANCHOR_RECT, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_positioner), 0, x, y, width, height); -} - -/** - * @ingroup iface_xdg_positioner - * - * Defines the anchor point for the anchor rectangle. The specified anchor - * is used derive an anchor point that the child surface will be - * positioned relative to. If a corner anchor is set (e.g. 'top_left' or - * 'bottom_right'), the anchor point will be at the specified corner; - * otherwise, the derived anchor point will be centered on the specified - * edge, or in the center of the anchor rectangle if no edge is specified. - */ -static inline void -xdg_positioner_set_anchor(struct xdg_positioner *xdg_positioner, uint32_t anchor) -{ - wl_proxy_marshal_flags((struct wl_proxy *) xdg_positioner, - XDG_POSITIONER_SET_ANCHOR, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_positioner), 0, anchor); -} - -/** - * @ingroup iface_xdg_positioner - * - * Defines in what direction a surface should be positioned, relative to - * the anchor point of the parent surface. If a corner gravity is - * specified (e.g. 'bottom_right' or 'top_left'), then the child surface - * will be placed towards the specified gravity; otherwise, the child - * surface will be centered over the anchor point on any axis that had no - * gravity specified. If the gravity is not in the ‘gravity’ enum, an - * invalid_input error is raised. - */ -static inline void -xdg_positioner_set_gravity(struct xdg_positioner *xdg_positioner, uint32_t gravity) -{ - wl_proxy_marshal_flags((struct wl_proxy *) xdg_positioner, - XDG_POSITIONER_SET_GRAVITY, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_positioner), 0, gravity); -} - -/** - * @ingroup iface_xdg_positioner - * - * Specify how the window should be positioned if the originally intended - * position caused the surface to be constrained, meaning at least - * partially outside positioning boundaries set by the compositor. The - * adjustment is set by constructing a bitmask describing the adjustment to - * be made when the surface is constrained on that axis. - * - * If no bit for one axis is set, the compositor will assume that the child - * surface should not change its position on that axis when constrained. - * - * If more than one bit for one axis is set, the order of how adjustments - * are applied is specified in the corresponding adjustment descriptions. - * - * The default adjustment is none. - */ -static inline void -xdg_positioner_set_constraint_adjustment(struct xdg_positioner *xdg_positioner, uint32_t constraint_adjustment) -{ - wl_proxy_marshal_flags((struct wl_proxy *) xdg_positioner, - XDG_POSITIONER_SET_CONSTRAINT_ADJUSTMENT, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_positioner), 0, constraint_adjustment); -} - -/** - * @ingroup iface_xdg_positioner - * - * Specify the surface position offset relative to the position of the - * anchor on the anchor rectangle and the anchor on the surface. For - * example if the anchor of the anchor rectangle is at (x, y), the surface - * has the gravity bottom|right, and the offset is (ox, oy), the calculated - * surface position will be (x + ox, y + oy). The offset position of the - * surface is the one used for constraint testing. See - * set_constraint_adjustment. - * - * An example use case is placing a popup menu on top of a user interface - * element, while aligning the user interface element of the parent surface - * with some user interface element placed somewhere in the popup surface. - */ -static inline void -xdg_positioner_set_offset(struct xdg_positioner *xdg_positioner, int32_t x, int32_t y) -{ - wl_proxy_marshal_flags((struct wl_proxy *) xdg_positioner, - XDG_POSITIONER_SET_OFFSET, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_positioner), 0, x, y); -} - -/** - * @ingroup iface_xdg_positioner - * - * When set reactive, the surface is reconstrained if the conditions used - * for constraining changed, e.g. the parent window moved. - * - * If the conditions changed and the popup was reconstrained, an - * xdg_popup.configure event is sent with updated geometry, followed by an - * xdg_surface.configure event. - */ -static inline void -xdg_positioner_set_reactive(struct xdg_positioner *xdg_positioner) -{ - wl_proxy_marshal_flags((struct wl_proxy *) xdg_positioner, - XDG_POSITIONER_SET_REACTIVE, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_positioner), 0); -} - -/** - * @ingroup iface_xdg_positioner - * - * Set the parent window geometry the compositor should use when - * positioning the popup. The compositor may use this information to - * determine the future state the popup should be constrained using. If - * this doesn't match the dimension of the parent the popup is eventually - * positioned against, the behavior is undefined. - * - * The arguments are given in the surface-local coordinate space. - */ -static inline void -xdg_positioner_set_parent_size(struct xdg_positioner *xdg_positioner, int32_t parent_width, int32_t parent_height) -{ - wl_proxy_marshal_flags((struct wl_proxy *) xdg_positioner, - XDG_POSITIONER_SET_PARENT_SIZE, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_positioner), 0, parent_width, parent_height); -} - -/** - * @ingroup iface_xdg_positioner - * - * Set the serial of an xdg_surface.configure event this positioner will be - * used in response to. The compositor may use this information together - * with set_parent_size to determine what future state the popup should be - * constrained using. - */ -static inline void -xdg_positioner_set_parent_configure(struct xdg_positioner *xdg_positioner, uint32_t serial) -{ - wl_proxy_marshal_flags((struct wl_proxy *) xdg_positioner, - XDG_POSITIONER_SET_PARENT_CONFIGURE, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_positioner), 0, serial); -} - -#ifndef XDG_SURFACE_ERROR_ENUM -#define XDG_SURFACE_ERROR_ENUM -enum xdg_surface_error { - /** - * Surface was not fully constructed - */ - XDG_SURFACE_ERROR_NOT_CONSTRUCTED = 1, - /** - * Surface was already constructed - */ - XDG_SURFACE_ERROR_ALREADY_CONSTRUCTED = 2, - /** - * Attaching a buffer to an unconfigured surface - */ - XDG_SURFACE_ERROR_UNCONFIGURED_BUFFER = 3, - /** - * Invalid serial number when acking a configure event - */ - XDG_SURFACE_ERROR_INVALID_SERIAL = 4, - /** - * Width or height was zero or negative - */ - XDG_SURFACE_ERROR_INVALID_SIZE = 5, - /** - * Surface was destroyed before its role object - */ - XDG_SURFACE_ERROR_DEFUNCT_ROLE_OBJECT = 6, -}; -#endif /* XDG_SURFACE_ERROR_ENUM */ - -/** - * @ingroup iface_xdg_surface - * @struct xdg_surface_listener - */ -struct xdg_surface_listener { - /** - * suggest a surface change - * - * The configure event marks the end of a configure sequence. A - * configure sequence is a set of one or more events configuring - * the state of the xdg_surface, including the final - * xdg_surface.configure event. - * - * Where applicable, xdg_surface surface roles will during a - * configure sequence extend this event as a latched state sent as - * events before the xdg_surface.configure event. Such events - * should be considered to make up a set of atomically applied - * configuration states, where the xdg_surface.configure commits - * the accumulated state. - * - * Clients should arrange their surface for the new states, and - * then send an ack_configure request with the serial sent in this - * configure event at some point before committing the new surface. - * - * If the client receives multiple configure events before it can - * respond to one, it is free to discard all but the last event it - * received. - * @param serial serial of the configure event - */ - void (*configure)(void *data, - struct xdg_surface *xdg_surface, - uint32_t serial); -}; - -/** - * @ingroup iface_xdg_surface - */ -static inline int -xdg_surface_add_listener(struct xdg_surface *xdg_surface, - const struct xdg_surface_listener *listener, void *data) -{ - return wl_proxy_add_listener((struct wl_proxy *) xdg_surface, - (void (**)(void)) listener, data); -} - -#define XDG_SURFACE_DESTROY 0 -#define XDG_SURFACE_GET_TOPLEVEL 1 -#define XDG_SURFACE_GET_POPUP 2 -#define XDG_SURFACE_SET_WINDOW_GEOMETRY 3 -#define XDG_SURFACE_ACK_CONFIGURE 4 - -/** - * @ingroup iface_xdg_surface - */ -#define XDG_SURFACE_CONFIGURE_SINCE_VERSION 1 - -/** - * @ingroup iface_xdg_surface - */ -#define XDG_SURFACE_DESTROY_SINCE_VERSION 1 -/** - * @ingroup iface_xdg_surface - */ -#define XDG_SURFACE_GET_TOPLEVEL_SINCE_VERSION 1 -/** - * @ingroup iface_xdg_surface - */ -#define XDG_SURFACE_GET_POPUP_SINCE_VERSION 1 -/** - * @ingroup iface_xdg_surface - */ -#define XDG_SURFACE_SET_WINDOW_GEOMETRY_SINCE_VERSION 1 -/** - * @ingroup iface_xdg_surface - */ -#define XDG_SURFACE_ACK_CONFIGURE_SINCE_VERSION 1 - -/** @ingroup iface_xdg_surface */ -static inline void -xdg_surface_set_user_data(struct xdg_surface *xdg_surface, void *user_data) -{ - wl_proxy_set_user_data((struct wl_proxy *) xdg_surface, user_data); -} - -/** @ingroup iface_xdg_surface */ -static inline void * -xdg_surface_get_user_data(struct xdg_surface *xdg_surface) -{ - return wl_proxy_get_user_data((struct wl_proxy *) xdg_surface); -} - -static inline uint32_t -xdg_surface_get_version(struct xdg_surface *xdg_surface) -{ - return wl_proxy_get_version((struct wl_proxy *) xdg_surface); -} - -/** - * @ingroup iface_xdg_surface - * - * Destroy the xdg_surface object. An xdg_surface must only be destroyed - * after its role object has been destroyed, otherwise - * a defunct_role_object error is raised. - */ -static inline void -xdg_surface_destroy(struct xdg_surface *xdg_surface) -{ - wl_proxy_marshal_flags((struct wl_proxy *) xdg_surface, - XDG_SURFACE_DESTROY, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_surface), WL_MARSHAL_FLAG_DESTROY); -} - -/** - * @ingroup iface_xdg_surface - * - * This creates an xdg_toplevel object for the given xdg_surface and gives - * the associated wl_surface the xdg_toplevel role. - * - * See the documentation of xdg_toplevel for more details about what an - * xdg_toplevel is and how it is used. - */ -static inline struct xdg_toplevel * -xdg_surface_get_toplevel(struct xdg_surface *xdg_surface) -{ - struct wl_proxy *id; - - id = wl_proxy_marshal_flags((struct wl_proxy *) xdg_surface, - XDG_SURFACE_GET_TOPLEVEL, &xdg_toplevel_interface, wl_proxy_get_version((struct wl_proxy *) xdg_surface), 0, NULL); - - return (struct xdg_toplevel *) id; -} - -/** - * @ingroup iface_xdg_surface - * - * This creates an xdg_popup object for the given xdg_surface and gives - * the associated wl_surface the xdg_popup role. - * - * If null is passed as a parent, a parent surface must be specified using - * some other protocol, before committing the initial state. - * - * See the documentation of xdg_popup for more details about what an - * xdg_popup is and how it is used. - */ -static inline struct xdg_popup * -xdg_surface_get_popup(struct xdg_surface *xdg_surface, struct xdg_surface *parent, struct xdg_positioner *positioner) -{ - struct wl_proxy *id; - - id = wl_proxy_marshal_flags((struct wl_proxy *) xdg_surface, - XDG_SURFACE_GET_POPUP, &xdg_popup_interface, wl_proxy_get_version((struct wl_proxy *) xdg_surface), 0, NULL, parent, positioner); - - return (struct xdg_popup *) id; -} - -/** - * @ingroup iface_xdg_surface - * - * The window geometry of a surface is its "visible bounds" from the - * user's perspective. Client-side decorations often have invisible - * portions like drop-shadows which should be ignored for the - * purposes of aligning, placing and constraining windows. - * - * The window geometry is double buffered, and will be applied at the - * time wl_surface.commit of the corresponding wl_surface is called. - * - * When maintaining a position, the compositor should treat the (x, y) - * coordinate of the window geometry as the top left corner of the window. - * A client changing the (x, y) window geometry coordinate should in - * general not alter the position of the window. - * - * Once the window geometry of the surface is set, it is not possible to - * unset it, and it will remain the same until set_window_geometry is - * called again, even if a new subsurface or buffer is attached. - * - * If never set, the value is the full bounds of the surface, - * including any subsurfaces. This updates dynamically on every - * commit. This unset is meant for extremely simple clients. - * - * The arguments are given in the surface-local coordinate space of - * the wl_surface associated with this xdg_surface, and may extend outside - * of the wl_surface itself to mark parts of the subsurface tree as part of - * the window geometry. - * - * When applied, the effective window geometry will be the set window - * geometry clamped to the bounding rectangle of the combined - * geometry of the surface of the xdg_surface and the associated - * subsurfaces. - * - * The effective geometry will not be recalculated unless a new call to - * set_window_geometry is done and the new pending surface state is - * subsequently applied. - * - * The width and height of the effective window geometry must be - * greater than zero. Setting an invalid size will raise an - * invalid_size error. - */ -static inline void -xdg_surface_set_window_geometry(struct xdg_surface *xdg_surface, int32_t x, int32_t y, int32_t width, int32_t height) -{ - wl_proxy_marshal_flags((struct wl_proxy *) xdg_surface, - XDG_SURFACE_SET_WINDOW_GEOMETRY, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_surface), 0, x, y, width, height); -} - -/** - * @ingroup iface_xdg_surface - * - * When a configure event is received, if a client commits the - * surface in response to the configure event, then the client - * must make an ack_configure request sometime before the commit - * request, passing along the serial of the configure event. - * - * For instance, for toplevel surfaces the compositor might use this - * information to move a surface to the top left only when the client has - * drawn itself for the maximized or fullscreen state. - * - * If the client receives multiple configure events before it - * can respond to one, it only has to ack the last configure event. - * Acking a configure event that was never sent raises an invalid_serial - * error. - * - * A client is not required to commit immediately after sending - * an ack_configure request - it may even ack_configure several times - * before its next surface commit. - * - * A client may send multiple ack_configure requests before committing, but - * only the last request sent before a commit indicates which configure - * event the client really is responding to. - * - * Sending an ack_configure request consumes the serial number sent with - * the request, as well as serial numbers sent by all configure events - * sent on this xdg_surface prior to the configure event referenced by - * the committed serial. - * - * It is an error to issue multiple ack_configure requests referencing a - * serial from the same configure event, or to issue an ack_configure - * request referencing a serial from a configure event issued before the - * event identified by the last ack_configure request for the same - * xdg_surface. Doing so will raise an invalid_serial error. - */ -static inline void -xdg_surface_ack_configure(struct xdg_surface *xdg_surface, uint32_t serial) -{ - wl_proxy_marshal_flags((struct wl_proxy *) xdg_surface, - XDG_SURFACE_ACK_CONFIGURE, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_surface), 0, serial); -} - -#ifndef XDG_TOPLEVEL_ERROR_ENUM -#define XDG_TOPLEVEL_ERROR_ENUM -enum xdg_toplevel_error { - /** - * provided value is not a valid variant of the resize_edge enum - */ - XDG_TOPLEVEL_ERROR_INVALID_RESIZE_EDGE = 0, - /** - * invalid parent toplevel - */ - XDG_TOPLEVEL_ERROR_INVALID_PARENT = 1, - /** - * client provided an invalid min or max size - */ - XDG_TOPLEVEL_ERROR_INVALID_SIZE = 2, -}; -#endif /* XDG_TOPLEVEL_ERROR_ENUM */ - -#ifndef XDG_TOPLEVEL_RESIZE_EDGE_ENUM -#define XDG_TOPLEVEL_RESIZE_EDGE_ENUM -/** - * @ingroup iface_xdg_toplevel - * edge values for resizing - * - * These values are used to indicate which edge of a surface - * is being dragged in a resize operation. - */ -enum xdg_toplevel_resize_edge { - XDG_TOPLEVEL_RESIZE_EDGE_NONE = 0, - XDG_TOPLEVEL_RESIZE_EDGE_TOP = 1, - XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM = 2, - XDG_TOPLEVEL_RESIZE_EDGE_LEFT = 4, - XDG_TOPLEVEL_RESIZE_EDGE_TOP_LEFT = 5, - XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_LEFT = 6, - XDG_TOPLEVEL_RESIZE_EDGE_RIGHT = 8, - XDG_TOPLEVEL_RESIZE_EDGE_TOP_RIGHT = 9, - XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_RIGHT = 10, -}; -#endif /* XDG_TOPLEVEL_RESIZE_EDGE_ENUM */ - -#ifndef XDG_TOPLEVEL_STATE_ENUM -#define XDG_TOPLEVEL_STATE_ENUM -/** - * @ingroup iface_xdg_toplevel - * types of state on the surface - * - * The different state values used on the surface. This is designed for - * state values like maximized, fullscreen. It is paired with the - * configure event to ensure that both the client and the compositor - * setting the state can be synchronized. - * - * States set in this way are double-buffered. They will get applied on - * the next commit. - */ -enum xdg_toplevel_state { - /** - * the surface is maximized - * the surface is maximized - * - * The surface is maximized. The window geometry specified in the - * configure event must be obeyed by the client, or the - * xdg_wm_base.invalid_surface_state error is raised. - * - * The client should draw without shadow or other decoration - * outside of the window geometry. - */ - XDG_TOPLEVEL_STATE_MAXIMIZED = 1, - /** - * the surface is fullscreen - * the surface is fullscreen - * - * The surface is fullscreen. The window geometry specified in - * the configure event is a maximum; the client cannot resize - * beyond it. For a surface to cover the whole fullscreened area, - * the geometry dimensions must be obeyed by the client. For more - * details, see xdg_toplevel.set_fullscreen. - */ - XDG_TOPLEVEL_STATE_FULLSCREEN = 2, - /** - * the surface is being resized - * the surface is being resized - * - * The surface is being resized. The window geometry specified in - * the configure event is a maximum; the client cannot resize - * beyond it. Clients that have aspect ratio or cell sizing - * configuration can use a smaller size, however. - */ - XDG_TOPLEVEL_STATE_RESIZING = 3, - /** - * the surface is now activated - * the surface is now activated - * - * Client window decorations should be painted as if the window - * is active. Do not assume this means that the window actually has - * keyboard or pointer focus. - */ - XDG_TOPLEVEL_STATE_ACTIVATED = 4, - /** - * the surface’s left edge is tiled - * - * The window is currently in a tiled layout and the left edge is - * considered to be adjacent to another part of the tiling grid. - * @since 2 - */ - XDG_TOPLEVEL_STATE_TILED_LEFT = 5, - /** - * the surface’s right edge is tiled - * - * The window is currently in a tiled layout and the right edge - * is considered to be adjacent to another part of the tiling grid. - * @since 2 - */ - XDG_TOPLEVEL_STATE_TILED_RIGHT = 6, - /** - * the surface’s top edge is tiled - * - * The window is currently in a tiled layout and the top edge is - * considered to be adjacent to another part of the tiling grid. - * @since 2 - */ - XDG_TOPLEVEL_STATE_TILED_TOP = 7, - /** - * the surface’s bottom edge is tiled - * - * The window is currently in a tiled layout and the bottom edge - * is considered to be adjacent to another part of the tiling grid. - * @since 2 - */ - XDG_TOPLEVEL_STATE_TILED_BOTTOM = 8, - /** - * surface repaint is suspended - * - * The surface is currently not ordinarily being repainted; for - * example because its content is occluded by another window, or - * its outputs are switched off due to screen locking. - * @since 6 - */ - XDG_TOPLEVEL_STATE_SUSPENDED = 9, -}; -/** - * @ingroup iface_xdg_toplevel - */ -#define XDG_TOPLEVEL_STATE_TILED_LEFT_SINCE_VERSION 2 -/** - * @ingroup iface_xdg_toplevel - */ -#define XDG_TOPLEVEL_STATE_TILED_RIGHT_SINCE_VERSION 2 -/** - * @ingroup iface_xdg_toplevel - */ -#define XDG_TOPLEVEL_STATE_TILED_TOP_SINCE_VERSION 2 -/** - * @ingroup iface_xdg_toplevel - */ -#define XDG_TOPLEVEL_STATE_TILED_BOTTOM_SINCE_VERSION 2 -/** - * @ingroup iface_xdg_toplevel - */ -#define XDG_TOPLEVEL_STATE_SUSPENDED_SINCE_VERSION 6 -#endif /* XDG_TOPLEVEL_STATE_ENUM */ - -#ifndef XDG_TOPLEVEL_WM_CAPABILITIES_ENUM -#define XDG_TOPLEVEL_WM_CAPABILITIES_ENUM -enum xdg_toplevel_wm_capabilities { - /** - * show_window_menu is available - */ - XDG_TOPLEVEL_WM_CAPABILITIES_WINDOW_MENU = 1, - /** - * set_maximized and unset_maximized are available - */ - XDG_TOPLEVEL_WM_CAPABILITIES_MAXIMIZE = 2, - /** - * set_fullscreen and unset_fullscreen are available - */ - XDG_TOPLEVEL_WM_CAPABILITIES_FULLSCREEN = 3, - /** - * set_minimized is available - */ - XDG_TOPLEVEL_WM_CAPABILITIES_MINIMIZE = 4, -}; -#endif /* XDG_TOPLEVEL_WM_CAPABILITIES_ENUM */ - -/** - * @ingroup iface_xdg_toplevel - * @struct xdg_toplevel_listener - */ -struct xdg_toplevel_listener { - /** - * suggest a surface change - * - * This configure event asks the client to resize its toplevel - * surface or to change its state. The configured state should not - * be applied immediately. See xdg_surface.configure for details. - * - * The width and height arguments specify a hint to the window - * about how its surface should be resized in window geometry - * coordinates. See set_window_geometry. - * - * If the width or height arguments are zero, it means the client - * should decide its own window dimension. This may happen when the - * compositor needs to configure the state of the surface but - * doesn't have any information about any previous or expected - * dimension. - * - * The states listed in the event specify how the width/height - * arguments should be interpreted, and possibly how it should be - * drawn. - * - * Clients must send an ack_configure in response to this event. - * See xdg_surface.configure and xdg_surface.ack_configure for - * details. - */ - void (*configure)(void *data, - struct xdg_toplevel *xdg_toplevel, - int32_t width, - int32_t height, - struct wl_array *states); - /** - * surface wants to be closed - * - * The close event is sent by the compositor when the user wants - * the surface to be closed. This should be equivalent to the user - * clicking the close button in client-side decorations, if your - * application has any. - * - * This is only a request that the user intends to close the - * window. The client may choose to ignore this request, or show a - * dialog to ask the user to save their data, etc. - */ - void (*close)(void *data, - struct xdg_toplevel *xdg_toplevel); - /** - * recommended window geometry bounds - * - * The configure_bounds event may be sent prior to a - * xdg_toplevel.configure event to communicate the bounds a window - * geometry size is recommended to constrain to. - * - * The passed width and height are in surface coordinate space. If - * width and height are 0, it means bounds is unknown and - * equivalent to as if no configure_bounds event was ever sent for - * this surface. - * - * The bounds can for example correspond to the size of a monitor - * excluding any panels or other shell components, so that a - * surface isn't created in a way that it cannot fit. - * - * The bounds may change at any point, and in such a case, a new - * xdg_toplevel.configure_bounds will be sent, followed by - * xdg_toplevel.configure and xdg_surface.configure. - * @since 4 - */ - void (*configure_bounds)(void *data, - struct xdg_toplevel *xdg_toplevel, - int32_t width, - int32_t height); - /** - * compositor capabilities - * - * This event advertises the capabilities supported by the - * compositor. If a capability isn't supported, clients should hide - * or disable the UI elements that expose this functionality. For - * instance, if the compositor doesn't advertise support for - * minimized toplevels, a button triggering the set_minimized - * request should not be displayed. - * - * The compositor will ignore requests it doesn't support. For - * instance, a compositor which doesn't advertise support for - * minimized will ignore set_minimized requests. - * - * Compositors must send this event once before the first - * xdg_surface.configure event. When the capabilities change, - * compositors must send this event again and then send an - * xdg_surface.configure event. - * - * The configured state should not be applied immediately. See - * xdg_surface.configure for details. - * - * The capabilities are sent as an array of 32-bit unsigned - * integers in native endianness. - * @param capabilities array of 32-bit capabilities - * @since 5 - */ - void (*wm_capabilities)(void *data, - struct xdg_toplevel *xdg_toplevel, - struct wl_array *capabilities); -}; - -/** - * @ingroup iface_xdg_toplevel - */ -static inline int -xdg_toplevel_add_listener(struct xdg_toplevel *xdg_toplevel, - const struct xdg_toplevel_listener *listener, void *data) -{ - return wl_proxy_add_listener((struct wl_proxy *) xdg_toplevel, - (void (**)(void)) listener, data); -} - -#define XDG_TOPLEVEL_DESTROY 0 -#define XDG_TOPLEVEL_SET_PARENT 1 -#define XDG_TOPLEVEL_SET_TITLE 2 -#define XDG_TOPLEVEL_SET_APP_ID 3 -#define XDG_TOPLEVEL_SHOW_WINDOW_MENU 4 -#define XDG_TOPLEVEL_MOVE 5 -#define XDG_TOPLEVEL_RESIZE 6 -#define XDG_TOPLEVEL_SET_MAX_SIZE 7 -#define XDG_TOPLEVEL_SET_MIN_SIZE 8 -#define XDG_TOPLEVEL_SET_MAXIMIZED 9 -#define XDG_TOPLEVEL_UNSET_MAXIMIZED 10 -#define XDG_TOPLEVEL_SET_FULLSCREEN 11 -#define XDG_TOPLEVEL_UNSET_FULLSCREEN 12 -#define XDG_TOPLEVEL_SET_MINIMIZED 13 - -/** - * @ingroup iface_xdg_toplevel - */ -#define XDG_TOPLEVEL_CONFIGURE_SINCE_VERSION 1 -/** - * @ingroup iface_xdg_toplevel - */ -#define XDG_TOPLEVEL_CLOSE_SINCE_VERSION 1 -/** - * @ingroup iface_xdg_toplevel - */ -#define XDG_TOPLEVEL_CONFIGURE_BOUNDS_SINCE_VERSION 4 -/** - * @ingroup iface_xdg_toplevel - */ -#define XDG_TOPLEVEL_WM_CAPABILITIES_SINCE_VERSION 5 - -/** - * @ingroup iface_xdg_toplevel - */ -#define XDG_TOPLEVEL_DESTROY_SINCE_VERSION 1 -/** - * @ingroup iface_xdg_toplevel - */ -#define XDG_TOPLEVEL_SET_PARENT_SINCE_VERSION 1 -/** - * @ingroup iface_xdg_toplevel - */ -#define XDG_TOPLEVEL_SET_TITLE_SINCE_VERSION 1 -/** - * @ingroup iface_xdg_toplevel - */ -#define XDG_TOPLEVEL_SET_APP_ID_SINCE_VERSION 1 -/** - * @ingroup iface_xdg_toplevel - */ -#define XDG_TOPLEVEL_SHOW_WINDOW_MENU_SINCE_VERSION 1 -/** - * @ingroup iface_xdg_toplevel - */ -#define XDG_TOPLEVEL_MOVE_SINCE_VERSION 1 -/** - * @ingroup iface_xdg_toplevel - */ -#define XDG_TOPLEVEL_RESIZE_SINCE_VERSION 1 -/** - * @ingroup iface_xdg_toplevel - */ -#define XDG_TOPLEVEL_SET_MAX_SIZE_SINCE_VERSION 1 -/** - * @ingroup iface_xdg_toplevel - */ -#define XDG_TOPLEVEL_SET_MIN_SIZE_SINCE_VERSION 1 -/** - * @ingroup iface_xdg_toplevel - */ -#define XDG_TOPLEVEL_SET_MAXIMIZED_SINCE_VERSION 1 -/** - * @ingroup iface_xdg_toplevel - */ -#define XDG_TOPLEVEL_UNSET_MAXIMIZED_SINCE_VERSION 1 -/** - * @ingroup iface_xdg_toplevel - */ -#define XDG_TOPLEVEL_SET_FULLSCREEN_SINCE_VERSION 1 -/** - * @ingroup iface_xdg_toplevel - */ -#define XDG_TOPLEVEL_UNSET_FULLSCREEN_SINCE_VERSION 1 -/** - * @ingroup iface_xdg_toplevel - */ -#define XDG_TOPLEVEL_SET_MINIMIZED_SINCE_VERSION 1 - -/** @ingroup iface_xdg_toplevel */ -static inline void -xdg_toplevel_set_user_data(struct xdg_toplevel *xdg_toplevel, void *user_data) -{ - wl_proxy_set_user_data((struct wl_proxy *) xdg_toplevel, user_data); -} - -/** @ingroup iface_xdg_toplevel */ -static inline void * -xdg_toplevel_get_user_data(struct xdg_toplevel *xdg_toplevel) -{ - return wl_proxy_get_user_data((struct wl_proxy *) xdg_toplevel); -} - -static inline uint32_t -xdg_toplevel_get_version(struct xdg_toplevel *xdg_toplevel) -{ - return wl_proxy_get_version((struct wl_proxy *) xdg_toplevel); -} - -/** - * @ingroup iface_xdg_toplevel - * - * This request destroys the role surface and unmaps the surface; - * see "Unmapping" behavior in interface section for details. - */ -static inline void -xdg_toplevel_destroy(struct xdg_toplevel *xdg_toplevel) -{ - wl_proxy_marshal_flags((struct wl_proxy *) xdg_toplevel, - XDG_TOPLEVEL_DESTROY, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_toplevel), WL_MARSHAL_FLAG_DESTROY); -} - -/** - * @ingroup iface_xdg_toplevel - * - * Set the "parent" of this surface. This surface should be stacked - * above the parent surface and all other ancestor surfaces. - * - * Parent surfaces should be set on dialogs, toolboxes, or other - * "auxiliary" surfaces, so that the parent is raised when the dialog - * is raised. - * - * Setting a null parent for a child surface unsets its parent. Setting - * a null parent for a surface which currently has no parent is a no-op. - * - * Only mapped surfaces can have child surfaces. Setting a parent which - * is not mapped is equivalent to setting a null parent. If a surface - * becomes unmapped, its children's parent is set to the parent of - * the now-unmapped surface. If the now-unmapped surface has no parent, - * its children's parent is unset. If the now-unmapped surface becomes - * mapped again, its parent-child relationship is not restored. - * - * The parent toplevel must not be one of the child toplevel's - * descendants, and the parent must be different from the child toplevel, - * otherwise the invalid_parent protocol error is raised. - */ -static inline void -xdg_toplevel_set_parent(struct xdg_toplevel *xdg_toplevel, struct xdg_toplevel *parent) -{ - wl_proxy_marshal_flags((struct wl_proxy *) xdg_toplevel, - XDG_TOPLEVEL_SET_PARENT, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_toplevel), 0, parent); -} - -/** - * @ingroup iface_xdg_toplevel - * - * Set a short title for the surface. - * - * This string may be used to identify the surface in a task bar, - * window list, or other user interface elements provided by the - * compositor. - * - * The string must be encoded in UTF-8. - */ -static inline void -xdg_toplevel_set_title(struct xdg_toplevel *xdg_toplevel, const char *title) -{ - wl_proxy_marshal_flags((struct wl_proxy *) xdg_toplevel, - XDG_TOPLEVEL_SET_TITLE, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_toplevel), 0, title); -} - -/** - * @ingroup iface_xdg_toplevel - * - * Set an application identifier for the surface. - * - * The app ID identifies the general class of applications to which - * the surface belongs. The compositor can use this to group multiple - * surfaces together, or to determine how to launch a new application. - * - * For D-Bus activatable applications, the app ID is used as the D-Bus - * service name. - * - * The compositor shell will try to group application surfaces together - * by their app ID. As a best practice, it is suggested to select app - * ID's that match the basename of the application's .desktop file. - * For example, "org.freedesktop.FooViewer" where the .desktop file is - * "org.freedesktop.FooViewer.desktop". - * - * Like other properties, a set_app_id request can be sent after the - * xdg_toplevel has been mapped to update the property. - * - * See the desktop-entry specification [0] for more details on - * application identifiers and how they relate to well-known D-Bus - * names and .desktop files. - * - * [0] https://standards.freedesktop.org/desktop-entry-spec/ - */ -static inline void -xdg_toplevel_set_app_id(struct xdg_toplevel *xdg_toplevel, const char *app_id) -{ - wl_proxy_marshal_flags((struct wl_proxy *) xdg_toplevel, - XDG_TOPLEVEL_SET_APP_ID, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_toplevel), 0, app_id); -} - -/** - * @ingroup iface_xdg_toplevel - * - * Clients implementing client-side decorations might want to show - * a context menu when right-clicking on the decorations, giving the - * user a menu that they can use to maximize or minimize the window. - * - * This request asks the compositor to pop up such a window menu at - * the given position, relative to the local surface coordinates of - * the parent surface. There are no guarantees as to what menu items - * the window menu contains, or even if a window menu will be drawn - * at all. - * - * This request must be used in response to some sort of user action - * like a button press, key press, or touch down event. - */ -static inline void -xdg_toplevel_show_window_menu(struct xdg_toplevel *xdg_toplevel, struct wl_seat *seat, uint32_t serial, int32_t x, int32_t y) -{ - wl_proxy_marshal_flags((struct wl_proxy *) xdg_toplevel, - XDG_TOPLEVEL_SHOW_WINDOW_MENU, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_toplevel), 0, seat, serial, x, y); -} - -/** - * @ingroup iface_xdg_toplevel - * - * Start an interactive, user-driven move of the surface. - * - * This request must be used in response to some sort of user action - * like a button press, key press, or touch down event. The passed - * serial is used to determine the type of interactive move (touch, - * pointer, etc). - * - * The server may ignore move requests depending on the state of - * the surface (e.g. fullscreen or maximized), or if the passed serial - * is no longer valid. - * - * If triggered, the surface will lose the focus of the device - * (wl_pointer, wl_touch, etc) used for the move. It is up to the - * compositor to visually indicate that the move is taking place, such as - * updating a pointer cursor, during the move. There is no guarantee - * that the device focus will return when the move is completed. - */ -static inline void -xdg_toplevel_move(struct xdg_toplevel *xdg_toplevel, struct wl_seat *seat, uint32_t serial) -{ - wl_proxy_marshal_flags((struct wl_proxy *) xdg_toplevel, - XDG_TOPLEVEL_MOVE, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_toplevel), 0, seat, serial); -} - -/** - * @ingroup iface_xdg_toplevel - * - * Start a user-driven, interactive resize of the surface. - * - * This request must be used in response to some sort of user action - * like a button press, key press, or touch down event. The passed - * serial is used to determine the type of interactive resize (touch, - * pointer, etc). - * - * The server may ignore resize requests depending on the state of - * the surface (e.g. fullscreen or maximized). - * - * If triggered, the client will receive configure events with the - * "resize" state enum value and the expected sizes. See the "resize" - * enum value for more details about what is required. The client - * must also acknowledge configure events using "ack_configure". After - * the resize is completed, the client will receive another "configure" - * event without the resize state. - * - * If triggered, the surface also will lose the focus of the device - * (wl_pointer, wl_touch, etc) used for the resize. It is up to the - * compositor to visually indicate that the resize is taking place, - * such as updating a pointer cursor, during the resize. There is no - * guarantee that the device focus will return when the resize is - * completed. - * - * The edges parameter specifies how the surface should be resized, and - * is one of the values of the resize_edge enum. Values not matching - * a variant of the enum will cause the invalid_resize_edge protocol error. - * The compositor may use this information to update the surface position - * for example when dragging the top left corner. The compositor may also - * use this information to adapt its behavior, e.g. choose an appropriate - * cursor image. - */ -static inline void -xdg_toplevel_resize(struct xdg_toplevel *xdg_toplevel, struct wl_seat *seat, uint32_t serial, uint32_t edges) -{ - wl_proxy_marshal_flags((struct wl_proxy *) xdg_toplevel, - XDG_TOPLEVEL_RESIZE, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_toplevel), 0, seat, serial, edges); -} - -/** - * @ingroup iface_xdg_toplevel - * - * Set a maximum size for the window. - * - * The client can specify a maximum size so that the compositor does - * not try to configure the window beyond this size. - * - * The width and height arguments are in window geometry coordinates. - * See xdg_surface.set_window_geometry. - * - * Values set in this way are double-buffered. They will get applied - * on the next commit. - * - * The compositor can use this information to allow or disallow - * different states like maximize or fullscreen and draw accurate - * animations. - * - * Similarly, a tiling window manager may use this information to - * place and resize client windows in a more effective way. - * - * The client should not rely on the compositor to obey the maximum - * size. The compositor may decide to ignore the values set by the - * client and request a larger size. - * - * If never set, or a value of zero in the request, means that the - * client has no expected maximum size in the given dimension. - * As a result, a client wishing to reset the maximum size - * to an unspecified state can use zero for width and height in the - * request. - * - * Requesting a maximum size to be smaller than the minimum size of - * a surface is illegal and will result in an invalid_size error. - * - * The width and height must be greater than or equal to zero. Using - * strictly negative values for width or height will result in a - * invalid_size error. - */ -static inline void -xdg_toplevel_set_max_size(struct xdg_toplevel *xdg_toplevel, int32_t width, int32_t height) -{ - wl_proxy_marshal_flags((struct wl_proxy *) xdg_toplevel, - XDG_TOPLEVEL_SET_MAX_SIZE, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_toplevel), 0, width, height); -} - -/** - * @ingroup iface_xdg_toplevel - * - * Set a minimum size for the window. - * - * The client can specify a minimum size so that the compositor does - * not try to configure the window below this size. - * - * The width and height arguments are in window geometry coordinates. - * See xdg_surface.set_window_geometry. - * - * Values set in this way are double-buffered. They will get applied - * on the next commit. - * - * The compositor can use this information to allow or disallow - * different states like maximize or fullscreen and draw accurate - * animations. - * - * Similarly, a tiling window manager may use this information to - * place and resize client windows in a more effective way. - * - * The client should not rely on the compositor to obey the minimum - * size. The compositor may decide to ignore the values set by the - * client and request a smaller size. - * - * If never set, or a value of zero in the request, means that the - * client has no expected minimum size in the given dimension. - * As a result, a client wishing to reset the minimum size - * to an unspecified state can use zero for width and height in the - * request. - * - * Requesting a minimum size to be larger than the maximum size of - * a surface is illegal and will result in an invalid_size error. - * - * The width and height must be greater than or equal to zero. Using - * strictly negative values for width and height will result in a - * invalid_size error. - */ -static inline void -xdg_toplevel_set_min_size(struct xdg_toplevel *xdg_toplevel, int32_t width, int32_t height) -{ - wl_proxy_marshal_flags((struct wl_proxy *) xdg_toplevel, - XDG_TOPLEVEL_SET_MIN_SIZE, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_toplevel), 0, width, height); -} - -/** - * @ingroup iface_xdg_toplevel - * - * Maximize the surface. - * - * After requesting that the surface should be maximized, the compositor - * will respond by emitting a configure event. Whether this configure - * actually sets the window maximized is subject to compositor policies. - * The client must then update its content, drawing in the configured - * state. The client must also acknowledge the configure when committing - * the new content (see ack_configure). - * - * It is up to the compositor to decide how and where to maximize the - * surface, for example which output and what region of the screen should - * be used. - * - * If the surface was already maximized, the compositor will still emit - * a configure event with the "maximized" state. - * - * If the surface is in a fullscreen state, this request has no direct - * effect. It may alter the state the surface is returned to when - * unmaximized unless overridden by the compositor. - */ -static inline void -xdg_toplevel_set_maximized(struct xdg_toplevel *xdg_toplevel) -{ - wl_proxy_marshal_flags((struct wl_proxy *) xdg_toplevel, - XDG_TOPLEVEL_SET_MAXIMIZED, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_toplevel), 0); -} - -/** - * @ingroup iface_xdg_toplevel - * - * Unmaximize the surface. - * - * After requesting that the surface should be unmaximized, the compositor - * will respond by emitting a configure event. Whether this actually - * un-maximizes the window is subject to compositor policies. - * If available and applicable, the compositor will include the window - * geometry dimensions the window had prior to being maximized in the - * configure event. The client must then update its content, drawing it in - * the configured state. The client must also acknowledge the configure - * when committing the new content (see ack_configure). - * - * It is up to the compositor to position the surface after it was - * unmaximized; usually the position the surface had before maximizing, if - * applicable. - * - * If the surface was already not maximized, the compositor will still - * emit a configure event without the "maximized" state. - * - * If the surface is in a fullscreen state, this request has no direct - * effect. It may alter the state the surface is returned to when - * unmaximized unless overridden by the compositor. - */ -static inline void -xdg_toplevel_unset_maximized(struct xdg_toplevel *xdg_toplevel) -{ - wl_proxy_marshal_flags((struct wl_proxy *) xdg_toplevel, - XDG_TOPLEVEL_UNSET_MAXIMIZED, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_toplevel), 0); -} - -/** - * @ingroup iface_xdg_toplevel - * - * Make the surface fullscreen. - * - * After requesting that the surface should be fullscreened, the - * compositor will respond by emitting a configure event. Whether the - * client is actually put into a fullscreen state is subject to compositor - * policies. The client must also acknowledge the configure when - * committing the new content (see ack_configure). - * - * The output passed by the request indicates the client's preference as - * to which display it should be set fullscreen on. If this value is NULL, - * it's up to the compositor to choose which display will be used to map - * this surface. - * - * If the surface doesn't cover the whole output, the compositor will - * position the surface in the center of the output and compensate with - * with border fill covering the rest of the output. The content of the - * border fill is undefined, but should be assumed to be in some way that - * attempts to blend into the surrounding area (e.g. solid black). - * - * If the fullscreened surface is not opaque, the compositor must make - * sure that other screen content not part of the same surface tree (made - * up of subsurfaces, popups or similarly coupled surfaces) are not - * visible below the fullscreened surface. - */ -static inline void -xdg_toplevel_set_fullscreen(struct xdg_toplevel *xdg_toplevel, struct wl_output *output) -{ - wl_proxy_marshal_flags((struct wl_proxy *) xdg_toplevel, - XDG_TOPLEVEL_SET_FULLSCREEN, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_toplevel), 0, output); -} - -/** - * @ingroup iface_xdg_toplevel - * - * Make the surface no longer fullscreen. - * - * After requesting that the surface should be unfullscreened, the - * compositor will respond by emitting a configure event. - * Whether this actually removes the fullscreen state of the client is - * subject to compositor policies. - * - * Making a surface unfullscreen sets states for the surface based on the following: - * * the state(s) it may have had before becoming fullscreen - * * any state(s) decided by the compositor - * * any state(s) requested by the client while the surface was fullscreen - * - * The compositor may include the previous window geometry dimensions in - * the configure event, if applicable. - * - * The client must also acknowledge the configure when committing the new - * content (see ack_configure). - */ -static inline void -xdg_toplevel_unset_fullscreen(struct xdg_toplevel *xdg_toplevel) -{ - wl_proxy_marshal_flags((struct wl_proxy *) xdg_toplevel, - XDG_TOPLEVEL_UNSET_FULLSCREEN, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_toplevel), 0); -} - -/** - * @ingroup iface_xdg_toplevel - * - * Request that the compositor minimize your surface. There is no - * way to know if the surface is currently minimized, nor is there - * any way to unset minimization on this surface. - * - * If you are looking to throttle redrawing when minimized, please - * instead use the wl_surface.frame event for this, as this will - * also work with live previews on windows in Alt-Tab, Expose or - * similar compositor features. - */ -static inline void -xdg_toplevel_set_minimized(struct xdg_toplevel *xdg_toplevel) -{ - wl_proxy_marshal_flags((struct wl_proxy *) xdg_toplevel, - XDG_TOPLEVEL_SET_MINIMIZED, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_toplevel), 0); -} - -#ifndef XDG_POPUP_ERROR_ENUM -#define XDG_POPUP_ERROR_ENUM -enum xdg_popup_error { - /** - * tried to grab after being mapped - */ - XDG_POPUP_ERROR_INVALID_GRAB = 0, -}; -#endif /* XDG_POPUP_ERROR_ENUM */ - -/** - * @ingroup iface_xdg_popup - * @struct xdg_popup_listener - */ -struct xdg_popup_listener { - /** - * configure the popup surface - * - * This event asks the popup surface to configure itself given - * the configuration. The configured state should not be applied - * immediately. See xdg_surface.configure for details. - * - * The x and y arguments represent the position the popup was - * placed at given the xdg_positioner rule, relative to the upper - * left corner of the window geometry of the parent surface. - * - * For version 2 or older, the configure event for an xdg_popup is - * only ever sent once for the initial configuration. Starting with - * version 3, it may be sent again if the popup is setup with an - * xdg_positioner with set_reactive requested, or in response to - * xdg_popup.reposition requests. - * @param x x position relative to parent surface window geometry - * @param y y position relative to parent surface window geometry - * @param width window geometry width - * @param height window geometry height - */ - void (*configure)(void *data, - struct xdg_popup *xdg_popup, - int32_t x, - int32_t y, - int32_t width, - int32_t height); - /** - * popup interaction is done - * - * The popup_done event is sent out when a popup is dismissed by - * the compositor. The client should destroy the xdg_popup object - * at this point. - */ - void (*popup_done)(void *data, - struct xdg_popup *xdg_popup); - /** - * signal the completion of a repositioned request - * - * The repositioned event is sent as part of a popup - * configuration sequence, together with xdg_popup.configure and - * lastly xdg_surface.configure to notify the completion of a - * reposition request. - * - * The repositioned event is to notify about the completion of a - * xdg_popup.reposition request. The token argument is the token - * passed in the xdg_popup.reposition request. - * - * Immediately after this event is emitted, xdg_popup.configure and - * xdg_surface.configure will be sent with the updated size and - * position, as well as a new configure serial. - * - * The client should optionally update the content of the popup, - * but must acknowledge the new popup configuration for the new - * position to take effect. See xdg_surface.ack_configure for - * details. - * @param token reposition request token - * @since 3 - */ - void (*repositioned)(void *data, - struct xdg_popup *xdg_popup, - uint32_t token); -}; - -/** - * @ingroup iface_xdg_popup - */ -static inline int -xdg_popup_add_listener(struct xdg_popup *xdg_popup, - const struct xdg_popup_listener *listener, void *data) -{ - return wl_proxy_add_listener((struct wl_proxy *) xdg_popup, - (void (**)(void)) listener, data); -} - -#define XDG_POPUP_DESTROY 0 -#define XDG_POPUP_GRAB 1 -#define XDG_POPUP_REPOSITION 2 - -/** - * @ingroup iface_xdg_popup - */ -#define XDG_POPUP_CONFIGURE_SINCE_VERSION 1 -/** - * @ingroup iface_xdg_popup - */ -#define XDG_POPUP_POPUP_DONE_SINCE_VERSION 1 -/** - * @ingroup iface_xdg_popup - */ -#define XDG_POPUP_REPOSITIONED_SINCE_VERSION 3 - -/** - * @ingroup iface_xdg_popup - */ -#define XDG_POPUP_DESTROY_SINCE_VERSION 1 -/** - * @ingroup iface_xdg_popup - */ -#define XDG_POPUP_GRAB_SINCE_VERSION 1 -/** - * @ingroup iface_xdg_popup - */ -#define XDG_POPUP_REPOSITION_SINCE_VERSION 3 - -/** @ingroup iface_xdg_popup */ -static inline void -xdg_popup_set_user_data(struct xdg_popup *xdg_popup, void *user_data) -{ - wl_proxy_set_user_data((struct wl_proxy *) xdg_popup, user_data); -} - -/** @ingroup iface_xdg_popup */ -static inline void * -xdg_popup_get_user_data(struct xdg_popup *xdg_popup) -{ - return wl_proxy_get_user_data((struct wl_proxy *) xdg_popup); -} - -static inline uint32_t -xdg_popup_get_version(struct xdg_popup *xdg_popup) -{ - return wl_proxy_get_version((struct wl_proxy *) xdg_popup); -} - -/** - * @ingroup iface_xdg_popup - * - * This destroys the popup. Explicitly destroying the xdg_popup - * object will also dismiss the popup, and unmap the surface. - * - * If this xdg_popup is not the "topmost" popup, the - * xdg_wm_base.not_the_topmost_popup protocol error will be sent. - */ -static inline void -xdg_popup_destroy(struct xdg_popup *xdg_popup) -{ - wl_proxy_marshal_flags((struct wl_proxy *) xdg_popup, - XDG_POPUP_DESTROY, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_popup), WL_MARSHAL_FLAG_DESTROY); -} - -/** - * @ingroup iface_xdg_popup - * - * This request makes the created popup take an explicit grab. An explicit - * grab will be dismissed when the user dismisses the popup, or when the - * client destroys the xdg_popup. This can be done by the user clicking - * outside the surface, using the keyboard, or even locking the screen - * through closing the lid or a timeout. - * - * If the compositor denies the grab, the popup will be immediately - * dismissed. - * - * This request must be used in response to some sort of user action like a - * button press, key press, or touch down event. The serial number of the - * event should be passed as 'serial'. - * - * The parent of a grabbing popup must either be an xdg_toplevel surface or - * another xdg_popup with an explicit grab. If the parent is another - * xdg_popup it means that the popups are nested, with this popup now being - * the topmost popup. - * - * Nested popups must be destroyed in the reverse order they were created - * in, e.g. the only popup you are allowed to destroy at all times is the - * topmost one. - * - * When compositors choose to dismiss a popup, they may dismiss every - * nested grabbing popup as well. When a compositor dismisses popups, it - * will follow the same dismissing order as required from the client. - * - * If the topmost grabbing popup is destroyed, the grab will be returned to - * the parent of the popup, if that parent previously had an explicit grab. - * - * If the parent is a grabbing popup which has already been dismissed, this - * popup will be immediately dismissed. If the parent is a popup that did - * not take an explicit grab, an error will be raised. - * - * During a popup grab, the client owning the grab will receive pointer - * and touch events for all their surfaces as normal (similar to an - * "owner-events" grab in X11 parlance), while the top most grabbing popup - * will always have keyboard focus. - */ -static inline void -xdg_popup_grab(struct xdg_popup *xdg_popup, struct wl_seat *seat, uint32_t serial) -{ - wl_proxy_marshal_flags((struct wl_proxy *) xdg_popup, - XDG_POPUP_GRAB, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_popup), 0, seat, serial); -} - -/** - * @ingroup iface_xdg_popup - * - * Reposition an already-mapped popup. The popup will be placed given the - * details in the passed xdg_positioner object, and a - * xdg_popup.repositioned followed by xdg_popup.configure and - * xdg_surface.configure will be emitted in response. Any parameters set - * by the previous positioner will be discarded. - * - * The passed token will be sent in the corresponding - * xdg_popup.repositioned event. The new popup position will not take - * effect until the corresponding configure event is acknowledged by the - * client. See xdg_popup.repositioned for details. The token itself is - * opaque, and has no other special meaning. - * - * If multiple reposition requests are sent, the compositor may skip all - * but the last one. - * - * If the popup is repositioned in response to a configure event for its - * parent, the client should send an xdg_positioner.set_parent_configure - * and possibly an xdg_positioner.set_parent_size request to allow the - * compositor to properly constrain the popup. - * - * If the popup is repositioned together with a parent that is being - * resized, but not in response to a configure event, the client should - * send an xdg_positioner.set_parent_size request. - */ -static inline void -xdg_popup_reposition(struct xdg_popup *xdg_popup, struct xdg_positioner *positioner, uint32_t token) -{ - wl_proxy_marshal_flags((struct wl_proxy *) xdg_popup, - XDG_POPUP_REPOSITION, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_popup), 0, positioner, token); -} - -#ifdef __cplusplus -} -#endif - -#endif From 101d7c0bc2b464ba95f29617344da94a0ffa3e32 Mon Sep 17 00:00:00 2001 From: BulbyVR <26726264+TheDrawingCoder-Gamer@users.noreply.github.com> Date: Fri, 8 Dec 2023 15:32:40 -0500 Subject: [PATCH 16/95] screens, and other misc things --- examples/dashboard/java/Example.java | 6 +-- wayland/cc/AppWayland.cc | 10 +++- wayland/cc/LayerGLWayland.cc | 1 - wayland/cc/LayerRasterWayland.cc | 12 ++--- wayland/cc/WindowManagerWayland.cc | 10 +++- wayland/cc/WindowWayland.cc | 69 ++++++++++++++++++---------- wayland/cc/WindowWayland.hh | 13 ++++-- 7 files changed, 80 insertions(+), 41 deletions(-) diff --git a/examples/dashboard/java/Example.java b/examples/dashboard/java/Example.java index 3df7cac5..28150556 100644 --- a/examples/dashboard/java/Example.java +++ b/examples/dashboard/java/Example.java @@ -55,9 +55,9 @@ public Example() { var scale = window.getScale(); int count = App._windows.size() - 1; - // Screen screen = App.getScreens()[(count / 5) % App.getScreens().length]; - // IRect bounds = screen.getWorkArea(); - IRect bounds = new IRect(0, 0, 100, 100); + Screen screen = App.getScreens()[(count / 5) % App.getScreens().length]; + IRect bounds = screen.getWorkArea(); + // IRect bounds = new IRect(0, 0, 100, 100); window.setTitle("JWM Window #" + count); if (window instanceof WindowMac windowMac) { diff --git a/wayland/cc/AppWayland.cc b/wayland/cc/AppWayland.cc index 326963c0..6e5f68a9 100644 --- a/wayland/cc/AppWayland.cc +++ b/wayland/cc/AppWayland.cc @@ -46,7 +46,15 @@ extern "C" JNIEXPORT void JNICALL Java_io_github_humbleui_jwm_App__1nRunOnUIThre // how awful extern "C" JNIEXPORT jobjectArray JNICALL Java_io_github_humbleui_jwm_App__1nGetScreens(JNIEnv* env, jobject cls) noexcept { - jobjectArray array = env->NewObjectArray(0, jwm::classes::Screen::kCls, 0); + + + jobjectArray array = env->NewObjectArray(jwm::app.wm.outputs.size(), jwm::classes::Screen::kCls, 0); + size_t index = 0; + + for (auto& i : jwm::app.wm.outputs) { + env->SetObjectArrayElement(array, index++, i->getScreenInfo().asJavaObject(env)); + } + return array; } diff --git a/wayland/cc/LayerGLWayland.cc b/wayland/cc/LayerGLWayland.cc index 44ba6e90..474fa7a5 100644 --- a/wayland/cc/LayerGLWayland.cc +++ b/wayland/cc/LayerGLWayland.cc @@ -118,7 +118,6 @@ namespace jwm { // _region = wl_compositor_create_region(fWindow->_windowManager.compositor); // wl_region_add(_region, 0, 0, fWindow->getWidth(), fWindow->getHeight()); // wl_surface_set_opaque_region(fWindow->_waylandWindow, _region); - printf("%i %i\n", fWindow->getWidth(), fWindow->getHeight()); _eglWindow = wl_egl_window_create(fWindow->_waylandWindow, fWindow->getWidth(), fWindow->getHeight()); _surface = eglCreateWindowSurface(_display, _config, _eglWindow, nullptr); diff --git a/wayland/cc/LayerRasterWayland.cc b/wayland/cc/LayerRasterWayland.cc index 1e1d620c..8457d266 100644 --- a/wayland/cc/LayerRasterWayland.cc +++ b/wayland/cc/LayerRasterWayland.cc @@ -24,11 +24,10 @@ namespace jwm { fWindow = jwm::ref(window); fWindow->setLayer(this); // must have a default size for pointer initing : ) - resize(400, 400); + resize(window->getWidth(), window->getHeight()); } void resize(int width, int height) override { - printf("???\n"); wl_display* d = fWindow->_windowManager.display; _width = width; _height = height; @@ -41,9 +40,6 @@ namespace jwm { _pool->close(); } _pool = new ShmPool(fWindow->_windowManager.shm, bufSize); - if (fWindow->_waylandWindow) { - wl_surface_attach(fWindow->_waylandWindow, nullptr, 0, 0); - } if (_buffer) { wl_buffer_destroy(_buffer); _imageData = nullptr; @@ -56,7 +52,7 @@ namespace jwm { if (_attached) { attachBuffer(); } - makeCurrentForced(); + // makeCurrentForced(); } const void* getPixelsPtr() const { @@ -69,7 +65,7 @@ namespace jwm { } void swapBuffers() override { - if (fWindow->_waylandWindow && _attached) { + if (fWindow->_waylandWindow) { wl_surface_damage_buffer(fWindow->_waylandWindow, 0, 0, INT32_MAX, INT32_MAX); wl_surface_commit(fWindow->_waylandWindow); } @@ -97,8 +93,10 @@ namespace jwm { void attachBuffer() override { if (fWindow) { if (fWindow->_waylandWindow) { + printf("gataching \n"); wl_surface_attach(fWindow->_waylandWindow, _buffer, 0, 0); wl_surface_damage_buffer(fWindow->_waylandWindow, 0, 0, INT32_MAX, INT32_MAX); + wl_surface_set_buffer_scale(fWindow->_waylandWindow, fWindow->_scale); wl_surface_commit(fWindow->_waylandWindow); _attached = true; } diff --git a/wayland/cc/WindowManagerWayland.cc b/wayland/cc/WindowManagerWayland.cc index a69e9126..60238201 100644 --- a/wayland/cc/WindowManagerWayland.cc +++ b/wayland/cc/WindowManagerWayland.cc @@ -220,11 +220,19 @@ void WindowManagerWayland::registryHandleGlobalRemove(void* data, wl_registry *r } } WindowWayland* WindowManagerWayland::getWindowForNative(wl_surface* surface) { + // the tag makes it safe. Should:TM: be faster than searching a list every time + const char* const* tag = wl_proxy_get_tag((wl_proxy*) surface); + if (tag != &WindowWayland::_windowTag) { + return nullptr; + } + return reinterpret_cast(wl_surface_get_user_data(surface)); + /* WindowWayland* myWindow = nullptr; auto it = _nativeWindowToMy.find(surface); if (it != _nativeWindowToMy.end()) myWindow = it->second; return myWindow; + */ } void WindowManagerWayland::pointerHandleEnter(void* data, wl_pointer* pointer, uint32_t serial, wl_surface *surface, wl_fixed_t surface_x, wl_fixed_t surface_y) { @@ -447,7 +455,6 @@ void WindowManagerWayland::keyboardRepeatInfo(void* data, wl_keyboard* keyboard, void WindowManagerWayland::seatCapabilities(void* data, wl_seat* seat, uint32_t capabilities) { auto self = reinterpret_cast(data); - printf("%i", capabilities & WL_SEAT_CAPABILITY_POINTER); if ((capabilities & WL_SEAT_CAPABILITY_POINTER) && !self->pointer) { self->pointer = wl_seat_get_pointer(seat); @@ -459,7 +466,6 @@ void WindowManagerWayland::seatCapabilities(void* data, wl_seat* seat, uint32_t if ((capabilities & WL_SEAT_CAPABILITY_KEYBOARD) && !self->keyboard) { - printf("got da keyboard\n"); self->keyboard = wl_seat_get_keyboard(seat); self->_xkbContext = xkb_context_new(XKB_CONTEXT_NO_FLAGS); wl_keyboard_add_listener(self->keyboard, &_keyboardListener, self); diff --git a/wayland/cc/WindowWayland.cc b/wayland/cc/WindowWayland.cc index eab791a5..e300b8d2 100644 --- a/wayland/cc/WindowWayland.cc +++ b/wayland/cc/WindowWayland.cc @@ -32,6 +32,8 @@ libdecor_frame_interface WindowWayland::_libdecorFrameInterface = { .dismiss_popup = WindowWayland::decorFrameDismissPopup }; +const char* WindowWayland::_windowTag = "WindowWayland"; + WindowWayland::WindowWayland(JNIEnv* env, WindowManagerWayland& windowManager): jwm::Window(env), _windowManager(windowManager) @@ -44,11 +46,15 @@ WindowWayland::~WindowWayland() { void WindowWayland::setTitle(const std::string& title) { - // impl me : ) + _title = title.c_str(); + if (_frame) + libdecor_frame_set_title(_frame, _title); } void WindowWayland::setTitlebarVisible(bool isVisible) { - // impl me : ) + _titlebarVisible = isVisible; + if (_frame) + libdecor_frame_set_visibility(_frame, isVisible); } // Closing is like... the exact same as hiding. WTH @@ -85,6 +91,11 @@ bool WindowWayland::isFullScreen() { return false; } +void WindowWayland::setLayer(ILayerWayland* layer) { + _layer = layer; + if (_visible) + _layer->attachBuffer(); +} void WindowWayland::getDecorations(int& left, int& top, int& right, int& bottom) { // impl me : ) left = 0; @@ -101,6 +112,9 @@ void WindowWayland::getContentPosition(int& posX, int& posY) { } bool WindowWayland::resize(int width, int height) { + // don't allow size to be set if currently tiled + if (!_floating && !_visible) + return false; // Width and height are in absolute pixel units, and wayland will // complain if you try to set a width/height that isn't a multiple of scale. if ((width % _scale) != 0 || (height % _scale) != 0) @@ -145,12 +159,7 @@ void WindowWayland::show() wl_surface_add_listener(_waylandWindow, &_surfaceListener, this); // unsure if listener data and user data are the same, so i do this for safety : ) wl_surface_set_user_data(_waylandWindow, this); - - // xdgSurface = xdg_wm_base_get_xdg_surface(_windowManager.xdgShell, _waylandWindow); - // xdg_surface_add_listener(xdgSurface, &_xdgSurfaceListener, this); - - // xdgToplevel = xdg_surface_get_toplevel(xdgSurface); - // xdg_toplevel_add_listener(xdgToplevel, &_xdgToplevelListener, this); + wl_proxy_set_tag((wl_proxy*) _waylandWindow, &_windowTag); _windowManager.registerWindow(this); _frame = libdecor_decorate(_windowManager.decorCtx, _waylandWindow, &_libdecorFrameInterface, this); @@ -159,6 +168,8 @@ void WindowWayland::show() libdecor_dispatch(_windowManager.decorCtx, -1); if (_width > 0 && _height > 0) resize(_width * _scale, _height * _scale); + setTitle(_title); + setTitlebarVisible(_titlebarVisible); } ScreenInfo WindowWayland::getScreen() { @@ -240,20 +251,22 @@ void jwm::WindowWayland::outputScale(void* data, wl_output* output, int factor) } void jwm::WindowWayland::outputName(void* data, wl_output* output, const char* name) {} void jwm::WindowWayland::outputDescription(void* data, wl_output* output, const char* desc) {} - wl_callback_listener jwm::WindowWayland::_frameCallback = { .done = [](void* data, wl_callback* cb, uint32_t cb_data) { auto self = reinterpret_cast(data); self->_adaptSize(self->_newWidth, self->_newHeight); } }; + void jwm::WindowWayland::decorFrameConfigure(libdecor_frame* frame, libdecor_configuration* configuration, void *userData) { auto self = reinterpret_cast(userData); int width = 0, height = 0; - + libdecor_window_state winState; + libdecor_configuration_get_content_size(configuration, frame, &width, &height); + width = (width <= 0) ? self->_floatingWidth : width; height = (height <= 0) ? self->_floatingHeight : height; @@ -261,21 +274,32 @@ void jwm::WindowWayland::decorFrameConfigure(libdecor_frame* frame, libdecor_con libdecor_frame_commit(frame, state, configuration); libdecor_state_free(state); - if (libdecor_frame_is_floating(frame)) { - if (width > 0) - self->_floatingWidth = width; - if (height > 0) - self->_floatingHeight = height; + if (libdecor_configuration_get_window_state(configuration, &winState)) { + self->_active = (winState & LIBDECOR_WINDOW_STATE_ACTIVE) != 0; + self->_maximized = (winState & LIBDECOR_WINDOW_STATE_MAXIMIZED) != 0; + self->_fullscreen = (winState & LIBDECOR_WINDOW_STATE_FULLSCREEN) != 0; + self->_floating = libdecor_frame_is_floating(frame); } + if (self->_width != width || self->_height != height) { + if (libdecor_frame_is_floating(frame)) { + if (width > 0) + self->_floatingWidth = width; + if (height > 0) + self->_floatingHeight = height; + } + + self->_newWidth = width; + self->_newHeight = height; + // This flat out breaks window if it isn't throttled + wl_callback* callback = wl_surface_frame(self->_waylandWindow); + // Throttle frame + wl_callback_add_listener(callback, &_frameCallback, self); + } if (!self->_configured && self->_layer) { self->_layer->attachBuffer(); } - self->_newWidth = width; - self->_newHeight = height; - wl_callback* callback = wl_surface_frame(self->_waylandWindow); - // Throttle frame - wl_callback_add_listener(callback, &_frameCallback, self); + self->_configured = true; } void jwm::WindowWayland::decorFrameClose(libdecor_frame* frame, void* userData) { @@ -284,8 +308,7 @@ void jwm::WindowWayland::decorFrameClose(libdecor_frame* frame, void* userData) } void jwm::WindowWayland::decorFrameCommit(libdecor_frame* frame, void* userData) { WindowWayland* self = reinterpret_cast(userData); - if (self->_layer) - self->_layer->swapBuffers(); + self->requestRedraw(); } void jwm::WindowWayland::decorFrameDismissPopup(libdecor_frame* frame, const char* seatName, void* userData) {} void jwm::WindowWayland::_adaptSize(int newWidth, int newHeight) { @@ -399,7 +422,7 @@ extern "C" JNIEXPORT void JNICALL Java_io_github_humbleui_jwm_WindowWayland__1nS jwm::WindowWayland* instance = reinterpret_cast(jwm::classes::Native::fromJava(env, obj)); jbyte* bytes = env->GetByteArrayElements(title, nullptr); - std::string titleS = { bytes, bytes + env->GetArrayLength(title) }; + std::string titleS((const char*) bytes, env->GetArrayLength(title)); env->ReleaseByteArrayElements(title, bytes, 0); instance->setTitle(titleS); diff --git a/wayland/cc/WindowWayland.hh b/wayland/cc/WindowWayland.hh index e04c366d..8e2de85a 100644 --- a/wayland/cc/WindowWayland.hh +++ b/wayland/cc/WindowWayland.hh @@ -46,9 +46,7 @@ namespace jwm { bool isFullScreen(); void setCursor(jwm::MouseCursor cursor); - void setLayer(ILayerWayland* layer) { - _layer = layer; - } + void setLayer(ILayerWayland* layer); ScreenInfo getScreen(); @@ -92,8 +90,13 @@ namespace jwm { bool _canFullscreen = false; bool _visible = false; bool _configured = false; - + bool _active = false; + bool _maximized = false; + bool _fullscreen = false; + bool _floating = false; bool _isRedrawRequested = false; + const char* _title = ""; + bool _titlebarVisible = true; WindowManagerWayland& _windowManager; ILayerWayland* _layer = nullptr; @@ -108,5 +111,7 @@ namespace jwm { static wl_callback_listener _frameCallback; + static const char* _windowTag; + }; } From f7ba6380eeeed690087a351d10249b10e982b3b7 Mon Sep 17 00:00:00 2001 From: BulbyVR <26726264+TheDrawingCoder-Gamer@users.noreply.github.com> Date: Fri, 8 Dec 2023 17:03:40 -0500 Subject: [PATCH 17/95] title/titlebar :) --- wayland/CMakeLists.txt | 3 +-- wayland/cc/ShmPool.cc | 2 +- wayland/cc/WindowWayland.cc | 21 ++++++++++++++------- wayland/cc/WindowWayland.hh | 3 ++- wayland/java/WindowWayland.java | 9 ++++----- 5 files changed, 22 insertions(+), 16 deletions(-) diff --git a/wayland/CMakeLists.txt b/wayland/CMakeLists.txt index a7ead2f8..47a98501 100644 --- a/wayland/CMakeLists.txt +++ b/wayland/CMakeLists.txt @@ -4,8 +4,7 @@ cmake_minimum_required(VERSION 3.9) cmake_policy(SET CMP0072 NEW) project(jwm LANGUAGES CXX) -project(xdgShell LANGUAGES C) -set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD 14) set(CMAKE_CXX_STANDARD_REQUIRED ON) if(NOT JWM_ARCH) diff --git a/wayland/cc/ShmPool.cc b/wayland/cc/ShmPool.cc index fa429540..13bfe5ef 100644 --- a/wayland/cc/ShmPool.cc +++ b/wayland/cc/ShmPool.cc @@ -93,5 +93,5 @@ int ShmPool::_allocateShmFile(size_t size) { std::pair ShmPool::createBuffer(int offset, int width, int height, int stride, uint32_t format) { wl_buffer* buffer = wl_shm_pool_create_buffer(_pool, offset, width, height, stride, format); uint8_t* data = &_rawData[offset]; - return std::pair(buffer, data); + return std::pair(buffer, data); } diff --git a/wayland/cc/WindowWayland.cc b/wayland/cc/WindowWayland.cc index e300b8d2..c2398aff 100644 --- a/wayland/cc/WindowWayland.cc +++ b/wayland/cc/WindowWayland.cc @@ -5,6 +5,8 @@ #include "AppWayland.hh" #include "impl/Library.hh" #include "impl/JNILocal.hh" +#include +#include using namespace jwm; @@ -36,7 +38,9 @@ const char* WindowWayland::_windowTag = "WindowWayland"; WindowWayland::WindowWayland(JNIEnv* env, WindowManagerWayland& windowManager): jwm::Window(env), - _windowManager(windowManager) + _windowManager(windowManager), + _title("") + { } @@ -46,9 +50,9 @@ WindowWayland::~WindowWayland() { void WindowWayland::setTitle(const std::string& title) { - _title = title.c_str(); + _title = title; if (_frame) - libdecor_frame_set_title(_frame, _title); + libdecor_frame_set_title(_frame, _title.c_str()); } void WindowWayland::setTitlebarVisible(bool isVisible) { @@ -418,13 +422,16 @@ extern "C" JNIEXPORT void JNICALL Java_io_github_humbleui_jwm_WindowWayland__1nC instance->close(); } extern "C" JNIEXPORT void JNICALL Java_io_github_humbleui_jwm_WindowWayland__1nSetTitle - (JNIEnv* env, jobject obj, jbyteArray title) { + (JNIEnv* env, jobject obj, jstring title) { jwm::WindowWayland* instance = reinterpret_cast(jwm::classes::Native::fromJava(env, obj)); - jbyte* bytes = env->GetByteArrayElements(title, nullptr); - std::string titleS((const char*) bytes, env->GetArrayLength(title)); - env->ReleaseByteArrayElements(title, bytes, 0); + const jchar* bytes = env->GetStringChars(title, nullptr); + jsize length = env->GetStringLength(title); + std::u16string thingie = {reinterpret_cast(bytes), length}; + std::string titleS = std::wstring_convert< + std::codecvt_utf8_utf16, char16_t>{}.to_bytes(thingie); + env->ReleaseStringChars(title, bytes); instance->setTitle(titleS); } diff --git a/wayland/cc/WindowWayland.hh b/wayland/cc/WindowWayland.hh index 8e2de85a..fe119dda 100644 --- a/wayland/cc/WindowWayland.hh +++ b/wayland/cc/WindowWayland.hh @@ -7,6 +7,7 @@ #include "ILayerWayland.hh" #include "ScreenInfo.hh" #include +#include namespace jwm { class WindowWayland: public jwm::Window { public: @@ -95,7 +96,7 @@ namespace jwm { bool _fullscreen = false; bool _floating = false; bool _isRedrawRequested = false; - const char* _title = ""; + std::string _title; bool _titlebarVisible = true; WindowManagerWayland& _windowManager; diff --git a/wayland/java/WindowWayland.java b/wayland/java/WindowWayland.java index 666bc04e..74441875 100644 --- a/wayland/java/WindowWayland.java +++ b/wayland/java/WindowWayland.java @@ -50,7 +50,8 @@ public Window setWindowPosition(int left, int top) { @Override public Window setWindowSize(int width, int height) { assert _onUIThread() : "Should be run on UI thread"; - // no : ) + // TODO: don't assume bounds + setContentSize(width, height); return this; } @@ -64,9 +65,7 @@ public Window setContentSize(int width, int height) { @Override public Window setTitle(String title) { assert _onUIThread() : "Should be run on UI thread"; - try { - _nSetTitle(title.getBytes("UTF-8")); - } catch (UnsupportedEncodingException ignored) {} + _nSetTitle(title); return this; } @@ -231,7 +230,7 @@ public float getScale() { @ApiStatus.Internal public native void _nMaximize(); @ApiStatus.Internal public native void _nMinimize(); @ApiStatus.Internal public native void _nRestore(); - @ApiStatus.Internal public native Screen _nSetTitle(byte[] title); + @ApiStatus.Internal public native Screen _nSetTitle(String title); @ApiStatus.Internal public native void _nSetTitlebarVisible(boolean isVisible); @ApiStatus.Internal public native void _nSetFullScreen(boolean isFullScreen); @ApiStatus.Internal public native boolean _nIsFullScreen(); From 62a8da1bdd236692aad0ef01ff3bbba7371f4918 Mon Sep 17 00:00:00 2001 From: BulbyVR <26726264+TheDrawingCoder-Gamer@users.noreply.github.com> Date: Fri, 8 Dec 2023 17:38:40 -0500 Subject: [PATCH 18/95] Events for focus, maximize --- wayland/cc/WindowWayland.cc | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/wayland/cc/WindowWayland.cc b/wayland/cc/WindowWayland.cc index c2398aff..177d5934 100644 --- a/wayland/cc/WindowWayland.cc +++ b/wayland/cc/WindowWayland.cc @@ -279,9 +279,30 @@ void jwm::WindowWayland::decorFrameConfigure(libdecor_frame* frame, libdecor_con libdecor_state_free(state); if (libdecor_configuration_get_window_state(configuration, &winState)) { - self->_active = (winState & LIBDECOR_WINDOW_STATE_ACTIVE) != 0; - self->_maximized = (winState & LIBDECOR_WINDOW_STATE_MAXIMIZED) != 0; - self->_fullscreen = (winState & LIBDECOR_WINDOW_STATE_FULLSCREEN) != 0; + bool active = (winState & LIBDECOR_WINDOW_STATE_ACTIVE) != 0; + bool maximized = (winState & LIBDECOR_WINDOW_STATE_MAXIMIZED) != 0; + bool fullscreen = (winState & LIBDECOR_WINDOW_STATE_FULLSCREEN) != 0; + // Some compositors (like weston) don't actually tell me on focus in and focus out. + // Libdecor simply sends an active at the beginning and keeps chugging. + if (active != self->_active) + if (active) + self->dispatch(classes::EventWindowFocusIn::kInstance); + else + self->dispatch(classes::EventWindowFocusOut::kInstance); + self->_active = active; + if (maximized != self->_maximized) + if (maximized) + self->dispatch(classes::EventWindowMaximize::kInstance); + self->_maximized = maximized; + // ??? + /* + if (fullscreen != self->_fullscreen) + if (fullscreen) + self->dispatch(classes::EventWindowFullScreenEnter::kInstance); + else + self->dispatch(classes::EventWindowFullScreenLeave::kInstance); + */ + self->_fullscreen = fullscreen; self->_floating = libdecor_frame_is_floating(frame); } if (self->_width != width || self->_height != height) { From 863fab0328392454e6e5e63bf4455f7e67ee20e9 Mon Sep 17 00:00:00 2001 From: BulbyVR <26726264+TheDrawingCoder-Gamer@users.noreply.github.com> Date: Sat, 9 Dec 2023 01:14:15 -0500 Subject: [PATCH 19/95] Partially working Raster --- shared/java/Window.java | 2 +- wayland/cc/AppWayland.cc | 13 ++- wayland/cc/AppWayland.hh | 6 +- wayland/cc/Buffer.cc | 101 ++++++++++++++++++++++ wayland/cc/Buffer.hh | 33 ++++++++ wayland/cc/ILayerWayland.hh | 1 + wayland/cc/LayerGLWayland.cc | 26 ++++-- wayland/cc/LayerRasterWayland.cc | 107 ++++++++++++------------ wayland/cc/ShmPool.cc | 2 +- wayland/cc/WindowManagerWayland.cc | 130 +++++++++++++---------------- wayland/cc/WindowManagerWayland.hh | 11 ++- wayland/cc/WindowWayland.cc | 62 ++++++++------ wayland/cc/WindowWayland.hh | 3 + 13 files changed, 334 insertions(+), 163 deletions(-) create mode 100644 wayland/cc/Buffer.cc create mode 100644 wayland/cc/Buffer.hh diff --git a/shared/java/Window.java b/shared/java/Window.java index d156b949..7b5ce0a4 100644 --- a/shared/java/Window.java +++ b/shared/java/Window.java @@ -423,7 +423,7 @@ public void accept(Event e) { if (e instanceof EventWindowScreenChange) { accept(new EventWindowResize(this)); - } else if (e instanceof EventWindowResize && Platform.CURRENT != Platform.X11) { + } else if (e instanceof EventWindowResize && Platform.CURRENT != Platform.X11 && Platform.CURRENT != Platform.WAYLAND) { accept(EventFrame.INSTANCE); } } diff --git a/wayland/cc/AppWayland.cc b/wayland/cc/AppWayland.cc index 6e5f68a9..e32a2575 100644 --- a/wayland/cc/AppWayland.cc +++ b/wayland/cc/AppWayland.cc @@ -2,12 +2,13 @@ #include "AppWayland.hh" #include #include - +#include jwm::AppWayland jwm::app; void jwm::AppWayland::init(JNIEnv* jniEnv) { - _jniEnv = jniEnv; + jint rs = jniEnv->GetJavaVM(&_javaVM); + assert(rs == JNI_OK); } void jwm::AppWayland::start() { @@ -18,6 +19,14 @@ void jwm::AppWayland::terminate() { wm.terminate(); } +JNIEnv* jwm::AppWayland::getJniEnv() { + JNIEnv* env; + // no-op on an already attached thread, so fast? + // makes it thread-safe (?) + jint rs = _javaVM->AttachCurrentThread((void**)&env, nullptr); + assert(rs == JNI_OK); + return env; +} // JNI extern "C" JNIEXPORT void JNICALL Java_io_github_humbleui_jwm_App__1nStart(JNIEnv* env, jclass jclass, jobject launcher) { diff --git a/wayland/cc/AppWayland.hh b/wayland/cc/AppWayland.hh index d0907b8c..2b5ba6f6 100644 --- a/wayland/cc/AppWayland.hh +++ b/wayland/cc/AppWayland.hh @@ -19,11 +19,9 @@ namespace jwm { return wm; } - JNIEnv* getJniEnv() { - return _jniEnv; - } + JNIEnv* getJniEnv(); - JNIEnv* _jniEnv; + JavaVM* _javaVM; WindowManagerWayland wm; } app; } diff --git a/wayland/cc/Buffer.cc b/wayland/cc/Buffer.cc new file mode 100644 index 00000000..35644145 --- /dev/null +++ b/wayland/cc/Buffer.cc @@ -0,0 +1,101 @@ +#include "Buffer.hh" +#include +#include +#include +#include +#include +using namespace jwm; + +wl_buffer_listener Buffer::_bufferListener = { + .release = [](void* data, wl_buffer* wlbuffer) { + auto buffer = reinterpret_cast(data); + delete buffer; + } +}; +static void randname(char *buf) +{ + struct timespec ts; + clock_gettime(CLOCK_REALTIME, &ts); + long r = ts.tv_nsec; + for (int i = 0; i < 6; ++i) { + buf[i] = 'A'+(r&15)+(r&16)*2; + r >>= 5; + } +} +static int _createShmFile() { + int retries = 100; + do { + char name[] = "/wl_shm-XXXXXX"; + randname(name + sizeof(name) - 7); + --retries; + int fd = shm_open(name, O_RDWR | O_CREAT | O_EXCL, 0600); + if (fd >= 0) { + shm_unlink(name); + return fd; + } + } while (retries > 0 && errno == EEXIST); + return -1; +} + +static int _allocateShmFile(size_t size) { + int fd = _createShmFile(); + if (fd < 0) + return -1; + int ret; + do { + ret = ftruncate(fd, size); + } while (ret < 0 && errno == EINTR); + if (ret < 0) { + ::close(fd); + return -1; + } + return fd; +} +Buffer::Buffer(wl_buffer* buffer, + int width, + int height, + void *data, + size_t dataSize) : + _buffer(buffer), + _width(width), + _height(height), + _data(data), + _dataSize(dataSize) +{ + wl_buffer_add_listener(buffer, &_bufferListener, this); +} +Buffer::~Buffer() +{ + wl_buffer_destroy(_buffer); + munmap(_data, _dataSize); +} + +Buffer* Buffer::createShmBuffer(wl_shm* shm, int width, int height, uint32_t format) +{ + wl_shm_pool* pool; + int fd, size, stride; + void* data; + wl_buffer* buffer; + + stride = width * 4; + size = stride * height; + + fd = _allocateShmFile(size); + if (fd < 0) + return nullptr; + + data = mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + if (data == MAP_FAILED) { + close(fd); + return nullptr; + } + + pool = wl_shm_create_pool(shm, fd, size); + + buffer = wl_shm_pool_create_buffer(pool, 0, width, height, stride, format); + + wl_shm_pool_destroy(pool); + close(fd); + + return new Buffer(buffer, width, height, data, size); +} diff --git a/wayland/cc/Buffer.hh b/wayland/cc/Buffer.hh new file mode 100644 index 00000000..d6bfe08c --- /dev/null +++ b/wayland/cc/Buffer.hh @@ -0,0 +1,33 @@ +#include +#include +#include + +namespace jwm { + class Buffer { + public: + Buffer(wl_buffer* buffer, + int width, + int height, + void *data, + size_t dataSize); + ~Buffer(); + wl_buffer* _buffer; + wl_buffer* getBuffer() const { + return _buffer; + } + int _width; + int _height; + void* _data; + void* getData() const { + return _data; + } + size_t _dataSize; + size_t getSize() { + return _dataSize; + } + + static wl_buffer_listener _bufferListener; + + static Buffer* createShmBuffer(wl_shm* shm, int width, int height, uint32_t format); + }; +} diff --git a/wayland/cc/ILayerWayland.hh b/wayland/cc/ILayerWayland.hh index d05ee848..68d2727f 100644 --- a/wayland/cc/ILayerWayland.hh +++ b/wayland/cc/ILayerWayland.hh @@ -7,5 +7,6 @@ namespace jwm { public: virtual void attachBuffer() = 0; virtual void swapBuffers() = 0; + virtual void detach() = 0; }; } diff --git a/wayland/cc/LayerGLWayland.cc b/wayland/cc/LayerGLWayland.cc index 474fa7a5..75562795 100644 --- a/wayland/cc/LayerGLWayland.cc +++ b/wayland/cc/LayerGLWayland.cc @@ -29,6 +29,11 @@ namespace jwm { void attach(WindowWayland* window) { fWindow = jwm::ref(window); fWindow->setLayer(this); + // HACK: reopen + bool visible = fWindow->_visible; + if (visible) { + fWindow->hide(); + } if (_display == nullptr) { _display = eglGetDisplay(window->_windowManager.display); @@ -66,6 +71,8 @@ namespace jwm { throw std::runtime_error("Couldn't make context"); } } + if (visible) + fWindow->show(); makeCurrentForced(); } @@ -92,13 +99,7 @@ namespace jwm { } void close() override { - eglMakeCurrent(_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); - if (_surface) { - eglDestroySurface(_display, _surface); - } - if (_eglWindow) { - wl_egl_window_destroy(_eglWindow); - } + detach(); eglDestroyContext(_display, _context); eglTerminate(_display); @@ -128,6 +129,17 @@ namespace jwm { makeCurrentForced(); } } + void detach() override { + eglMakeCurrent(_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + if (_surface) { + eglDestroySurface(_display, _surface); + } + _surface = nullptr; + if (_eglWindow) { + wl_egl_window_destroy(_eglWindow); + } + _eglWindow = nullptr; + } }; } // namespace jwm diff --git a/wayland/cc/LayerRasterWayland.cc b/wayland/cc/LayerRasterWayland.cc index 8457d266..a0c35f8a 100644 --- a/wayland/cc/LayerRasterWayland.cc +++ b/wayland/cc/LayerRasterWayland.cc @@ -4,59 +4,41 @@ #include "impl/Library.hh" #include "impl/RefCounted.hh" #include "WindowWayland.hh" -#include "ShmPool.hh" +#include "Buffer.hh" #include +#include namespace jwm { class LayerRaster: public RefCounted, public ILayerWayland { public: WindowWayland* fWindow; size_t _width = 0, _height = 0; - wl_buffer* _buffer = nullptr; - uint8_t* _imageData = nullptr; - ShmPool* _pool = nullptr; + std::vector _imageData; bool _attached = false; LayerRaster() = default; virtual ~LayerRaster() = default; void attach(WindowWayland* window) { + // HACK: close and reopen fWindow = jwm::ref(window); fWindow->setLayer(this); - // must have a default size for pointer initing : ) - resize(window->getWidth(), window->getHeight()); + if (fWindow->_visible) { + fWindow->hide(); + fWindow->show(); + } + } void resize(int width, int height) override { - wl_display* d = fWindow->_windowManager.display; + // god is dead _width = width; _height = height; - int stride = width * sizeof(uint32_t); - int bufSize = stride * height * 2; - // TODO: better pool impl - if (_pool) { - // TODO: don't mem leak : ) - // This memleaks - munmap causes skija to error out : / - _pool->close(); - } - _pool = new ShmPool(fWindow->_windowManager.shm, bufSize); - if (_buffer) { - wl_buffer_destroy(_buffer); - _imageData = nullptr; - } - // : ) - auto buf = _pool->createBuffer(0, width, height, stride, WL_SHM_FORMAT_XRGB8888); - - _buffer = buf.first; - _imageData = buf.second; - if (_attached) { - attachBuffer(); - } - // makeCurrentForced(); + _imageData = std::vector(_width * _height * sizeof(uint32_t)); } const void* getPixelsPtr() const { - return _imageData; + return _imageData.data(); } int getRowBytes() const { @@ -64,22 +46,33 @@ namespace jwm { return _width * sizeof(uint32_t); } + // only way to really be sane with raster + // it's SO slow that i'll prob + static wl_callback_listener _frameCallback; + + void swapNow() { + auto buf = Buffer::createShmBuffer(fWindow->_windowManager.shm, _width, _height, WL_SHM_FORMAT_XRGB8888); + void* daData = buf->getData(); + size_t size = buf->getSize(); + memcpy(daData, _imageData.data(), size); + wl_surface_attach(fWindow->_waylandWindow, buf->getBuffer(), 0, 0); + wl_surface_damage_buffer(fWindow->_waylandWindow, 0, 0, INT32_MAX, INT32_MAX); + wl_surface_set_buffer_scale(fWindow->_waylandWindow, fWindow->_scale); + wl_surface_commit(fWindow->_waylandWindow); + + } void swapBuffers() override { - if (fWindow->_waylandWindow) { - wl_surface_damage_buffer(fWindow->_waylandWindow, 0, 0, INT32_MAX, INT32_MAX); - wl_surface_commit(fWindow->_waylandWindow); + if (_attached && fWindow->_waylandWindow) { + // all impls that I've seen have to make a new buffer every frame. + // God awful. Never use raster if you value performance. + // wl_callback* cb = wl_surface_frame(fWindow->_waylandWindow); + // wl_callback_add_listener(cb, &_frameCallback, this); + swapNow(); } } void close() override { - if (_buffer) { - wl_buffer_destroy(_buffer); - _buffer = nullptr; - } - // ??? - if (_pool) { - _pool->close(); - } + detach(); jwm::unref(&fWindow); } @@ -91,22 +84,31 @@ namespace jwm { } void attachBuffer() override { - if (fWindow) { - if (fWindow->_waylandWindow) { - printf("gataching \n"); - wl_surface_attach(fWindow->_waylandWindow, _buffer, 0, 0); - wl_surface_damage_buffer(fWindow->_waylandWindow, 0, 0, INT32_MAX, INT32_MAX); - wl_surface_set_buffer_scale(fWindow->_waylandWindow, fWindow->_scale); - wl_surface_commit(fWindow->_waylandWindow); - _attached = true; - } + _attached = true; + swapBuffers(); + } + + void detach() override { + if (fWindow && fWindow->_waylandWindow) { + wl_surface_attach(fWindow->_waylandWindow, nullptr, 0, 0); + wl_surface_commit(fWindow->_waylandWindow); } + _attached = false; + } + void reconfigure() { + swapBuffers(); } }; } +using namespace jwm; +wl_callback_listener LayerRaster::_frameCallback = { + .done = [](void* data, wl_callback* cb, uint32_t cbData) { + auto self = reinterpret_cast(data); + self->swapNow(); - + } +}; extern "C" JNIEXPORT jlong JNICALL Java_io_github_humbleui_jwm_LayerRaster__1nMake (JNIEnv* env, jclass jclass) { jwm::LayerRaster* instance = new jwm::LayerRaster; @@ -122,7 +124,8 @@ extern "C" JNIEXPORT void JNICALL Java_io_github_humbleui_jwm_LayerRaster__1nAtt extern "C" JNIEXPORT void JNICALL Java_io_github_humbleui_jwm_LayerRaster__1nReconfigure (JNIEnv* env, jobject obj) { - + jwm::LayerRaster* instance = reinterpret_cast(jwm::classes::Native::fromJava(env, obj)); + instance->reconfigure(); } extern "C" JNIEXPORT void JNICALL Java_io_github_humbleui_jwm_LayerRaster__1nResize diff --git a/wayland/cc/ShmPool.cc b/wayland/cc/ShmPool.cc index 13bfe5ef..4366b45d 100644 --- a/wayland/cc/ShmPool.cc +++ b/wayland/cc/ShmPool.cc @@ -36,7 +36,7 @@ ShmPool::~ShmPool() { void ShmPool::close() { wl_shm_pool_destroy(_pool); ::close(_fd); - // munmap(_rawData, _size); + munmap(_rawData, _size); } void ShmPool::grow(size_t size) { diff --git a/wayland/cc/WindowManagerWayland.cc b/wayland/cc/WindowManagerWayland.cc index 60238201..e22c774e 100644 --- a/wayland/cc/WindowManagerWayland.cc +++ b/wayland/cc/WindowManagerWayland.cc @@ -22,6 +22,7 @@ #include #include #include +#include using namespace jwm; @@ -69,7 +70,8 @@ WindowManagerWayland::WindowManagerWayland(): // ???: Moving this after libdecor_new causes input to not work wl_seat_add_listener(seat, &_seatListener, this); - + dataDevice = wl_data_device_manager_get_data_device(deviceManager, seat); + wl_data_device_add_listener(dataDevice, &_deviceListener, this); decorCtx = libdecor_new(display, &_decorInterface); @@ -119,7 +121,6 @@ void WindowManagerWayland::runLoop() { } notifyFD = pipes[1]; fcntl(pipes[1], F_SETFL, O_NONBLOCK); // notifyLoop no blockie : ) - struct pollfd wayland_out = {.fd=wl_display_get_fd(display),.events=POLLOUT}; struct pollfd ps[] = { {.fd=libdecor_get_fd(decorCtx), .events=POLLIN}, {.fd=pipes[0], .events=POLLIN}, @@ -128,18 +129,19 @@ void WindowManagerWayland::runLoop() { while (_runLoop) { if (jwm::classes::Throwable::exceptionThrown(app.getJniEnv())) _runLoop = false; - _processCallbacks(); // block until event : ) if (poll(&ps[0], 2, -1) < 0) { printf("error with pipe\n"); break; } - if (ps[1].revents & POLLIN) { - while (read(pipes[0], buf, sizeof(buf)) == sizeof(buf)) { } - } if (ps[0].revents & POLLIN) { libdecor_dispatch(decorCtx, -1); } + _processCallbacks(); + if (ps[1].revents & POLLIN) { + while (read(pipes[0], buf, sizeof(buf)) == sizeof(buf)) { } + } + notifyBool.store(false); } @@ -177,10 +179,12 @@ void WindowManagerWayland::_processCallbacks() { for (auto p : copy) { if (p->isRedrawRequested()) { p->unsetRedrawRequest(); - if (p->_layer && p->_visible) { - p->_layer->makeCurrent(); + if (p->_visible && p->_configured) { + if (p->_layer) { + p->_layer->makeCurrent(); + } + p->dispatch(classes::EventFrame::kInstance); } - p->dispatch(classes::EventFrame::kInstance); } } } @@ -377,10 +381,12 @@ void WindowManagerWayland::keyboardKeymap(void* data, wl_keyboard* keyboard, uin void WindowManagerWayland::keyboardEnter(void* data, wl_keyboard* keyboard, uint32_t serial, wl_surface* surface, wl_array *keys) { auto self = reinterpret_cast(data); + self->keyboardSerial = serial; self->keyboardFocus = surface; } void WindowManagerWayland::keyboardLeave(void* data, wl_keyboard* keyboard, uint32_t serial, wl_surface* surface) { auto self = reinterpret_cast(data); + self->keyboardSerial = -1; self->keyboardFocus = nullptr; } @@ -479,68 +485,49 @@ void WindowManagerWayland::seatCapabilities(void* data, wl_seat* seat, uint32_t void WindowManagerWayland::seatName(void* data, wl_seat* seat, const char* name) { // who cares } -std::vector WindowManagerWayland::getClipboardFormats() { - /* - XConvertSelection(display, - _atoms.CLIPBOARD, - _atoms.TARGETS, - _atoms.JWM_CLIPBOARD, - nativeHandle, - CurrentTime); - - XEvent ev; +wl_data_offer_listener WindowManagerWayland::_offerListener = { + .offer = [](void* data, wl_data_offer* offer, const char* mimeType) { + auto self = reinterpret_cast(data); + self->_currentMimeTypes.push_back(std::string(mimeType)); + } +}; +wl_data_device_listener WindowManagerWayland::_deviceListener = { + .data_offer = [](void* data, wl_data_device* device, wl_data_offer* offer) { + auto self = reinterpret_cast(data); + self->_currentMimeTypes = {}; + wl_data_offer_add_listener(offer, &_offerListener, data); + }, + .selection = [](void* data, wl_data_device* device, wl_data_offer* offer) { + auto self = reinterpret_cast(data); + self->_myClipboardContents = {}; + if (offer == nullptr) { + return; + } + for (auto i : self->_currentMimeTypes) { + int fds[2]; + pipe(fds); + wl_data_offer_receive(offer, i.c_str(), fds[1]); + wl_display_flush(self->display); + close(fds[1]); + ByteBuf res; + + + while (true) { + char buf[1024]; + ssize_t n = read(fds[0], buf, sizeof(buf)); + if (n <= 0) + break; + res.insert(res.end(), buf, buf + n); - // fetch mime types - std::vector result; - - // using lambda here in order to break 2 loops - [&]{ - while (_runLoop) { - while (XPending(display)) { - XNextEvent(display, &ev); - if (ev.type == SelectionNotify) { - int format; - unsigned long count, lengthInBytes; - Atom type; - Atom* properties; - XGetWindowProperty(display, nativeHandle, _atoms.JWM_CLIPBOARD, 0, 1024 * sizeof(Atom), false, XA_ATOM, &type, &format, &count, &lengthInBytes, reinterpret_cast(&properties)); - - for (unsigned long i = 0; i < count; ++i) { - char* str = XGetAtomName(display, properties[i]); - if (str) { - std::string s = str; - // include only mime types - if (s.find('/') != std::string::npos) { - result.push_back(s); - } else if (s == "UTF8_STRING") { - // HACK: treat UTF8_STRING as text/plain under the hood - // avoid duplicates - std::string textPlain = "text/plain"; - if (std::find(result.begin(), result.end(), textPlain) != result.end()) { - result.push_back(textPlain); - } - } - XFree(str); - } - } - - XFree(properties); - return; - } else { - _processXEvent(ev); - } - } - _processCallbacks(); + self->_myClipboardContents[i] = res; + close(fds[0]); } - }(); - - // fetching data - - XDeleteProperty(display, nativeHandle, _atoms.JWM_CLIPBOARD); - */ - std::vector result; - return result; + wl_data_offer_destroy(offer); + } +}; +std::vector WindowManagerWayland::getClipboardFormats() { + return { _currentMimeTypes.begin(), _currentMimeTypes.end()}; } void WindowManagerWayland::mouseUpdate(WindowWayland* myWindow, uint32_t x, uint32_t y, uint32_t mask) { using namespace classes; @@ -567,7 +554,10 @@ void WindowManagerWayland::mouseUpdate(WindowWayland* myWindow, uint32_t x, uint myWindow->dispatch(foo); } jwm::ByteBuf WindowManagerWayland::getClipboardContents(const std::string& type) { - auto nativeHandle = _nativeWindowToMy.begin()->first; + auto it = _myClipboardContents.find(type); + if (it != _myClipboardContents.end()) { + return it->second; + } return {}; } @@ -590,7 +580,6 @@ void WindowManagerWayland::setClipboardContents(std::map&& assert(("create at least one window in order to use clipboard" && !_nativeWindowToMy.empty())); _myClipboardContents = c; // impl me : ) - auto window = _nativeWindowToMy.begin()->first; // XSetSelectionOwner(display, XA_PRIMARY, window, CurrentTime); // XSetSelectionOwner(display, _atoms.CLIPBOARD, window, CurrentTime); } @@ -607,6 +596,7 @@ void WindowManagerWayland::notifyLoop() { if (notifyFD==-1) return; // fast notifyBool path to not make system calls when not necessary if (!notifyBool.exchange(true)) { + printf(" : )\n"); char dummy[1] = {0}; int unused = write(notifyFD, dummy, 1); // this really shouldn't fail, but if it does, the pipe should either be full (good), or dead (bad, but not our business) } diff --git a/wayland/cc/WindowManagerWayland.hh b/wayland/cc/WindowManagerWayland.hh index c3baf58b..ad363b43 100644 --- a/wayland/cc/WindowManagerWayland.hh +++ b/wayland/cc/WindowManagerWayland.hh @@ -101,6 +101,10 @@ namespace jwm { static void seatCapabilities(void* data, wl_seat* seat, uint32_t capabilities); static void seatName(void* data, wl_seat* seat, const char* name); + static wl_data_source_listener _sourceListener; + static wl_data_offer_listener _offerListener; + static wl_data_device_listener _deviceListener; + ByteBuf getClipboardContents(const std::string& type); std::vector getClipboardFormats(); @@ -109,16 +113,18 @@ namespace jwm { wl_shm* shm = nullptr; wl_compositor* compositor = nullptr; wl_data_device_manager* deviceManager = nullptr; + // no multiseat? wl_seat* seat = nullptr; wl_pointer* pointer = nullptr; wl_keyboard* keyboard = nullptr; + wl_data_device* dataDevice = nullptr; + wl_data_source* currentSource = nullptr; + uint32_t keyboardSerial = -1; libdecor* decorCtx = nullptr; xkb_context* _xkbContext = nullptr; xkb_state* _xkbState = nullptr; std::list outputs; - // XVisualInfo* x11VisualInfo; - // XSetWindowAttributes x11SWA; bool _runLoop; int notifyFD = -1; std::atomic_bool notifyBool{false}; @@ -129,6 +135,7 @@ namespace jwm { std::map _nativeWindowToMy; std::map _myClipboardContents; + std::list _currentMimeTypes; wl_surface* cursorSurface; diff --git a/wayland/cc/WindowWayland.cc b/wayland/cc/WindowWayland.cc index 177d5934..083e0bc2 100644 --- a/wayland/cc/WindowWayland.cc +++ b/wayland/cc/WindowWayland.cc @@ -61,13 +61,20 @@ void WindowWayland::setTitlebarVisible(bool isVisible) { libdecor_frame_set_visibility(_frame, isVisible); } -// Closing is like... the exact same as hiding. WTH void WindowWayland::close() { if (_waylandWindow) { _windowManager.unregisterWindow(this); wl_surface_destroy(_waylandWindow); } _waylandWindow = nullptr; + hide(); + // TODO: more destruction! +} +void WindowWayland::hide() { + _visible = false; + if (_layer) { + _layer->detach(); + } if (_frame) { libdecor_frame_unref(_frame); } @@ -91,14 +98,12 @@ void WindowWayland::setFullScreen(bool isFullScreen) { } bool WindowWayland::isFullScreen() { - // impl me : ) - return false; + return _fullscreen; } void WindowWayland::setLayer(ILayerWayland* layer) { _layer = layer; - if (_visible) - _layer->attachBuffer(); + } void WindowWayland::getDecorations(int& left, int& top, int& right, int& bottom) { // impl me : ) @@ -116,8 +121,10 @@ void WindowWayland::getContentPosition(int& posX, int& posY) { } bool WindowWayland::resize(int width, int height) { + if (width < 0 || height < 0) + return false; // don't allow size to be set if currently tiled - if (!_floating && !_visible) + if (!_floating) return false; // Width and height are in absolute pixel units, and wayland will // complain if you try to set a width/height that isn't a multiple of scale. @@ -144,21 +151,22 @@ int WindowWayland::getTop() { } int WindowWayland::getWidth() { - return (_width <= 0 ? _floatingWidth : _width) * _scale; + return getUnscaledWidth() * _scale; +} +int WindowWayland::getUnscaledWidth() { + return _width <= 0 ? _floatingWidth : _width; } - int WindowWayland::getHeight() { - return (_height <= 0 ? _floatingHeight : _height) * _scale; + return getUnscaledHeight() * _scale; +} +int WindowWayland::getUnscaledHeight() { + return _height <= 0 ? _floatingHeight : _height; } float WindowWayland::getScale() { return _scale; } bool WindowWayland::init() { - return true; -} -void WindowWayland::show() -{ _waylandWindow = wl_compositor_create_surface(_windowManager.compositor); wl_surface_add_listener(_waylandWindow, &_surfaceListener, this); // unsure if listener data and user data are the same, so i do this for safety : ) @@ -166,14 +174,17 @@ void WindowWayland::show() wl_proxy_set_tag((wl_proxy*) _waylandWindow, &_windowTag); _windowManager.registerWindow(this); - _frame = libdecor_decorate(_windowManager.decorCtx, _waylandWindow, &_libdecorFrameInterface, this); _configured = false; + return true; +} +void WindowWayland::show() +{ + _frame = libdecor_decorate(_windowManager.decorCtx, _waylandWindow, &_libdecorFrameInterface, this); libdecor_frame_map(_frame); libdecor_dispatch(_windowManager.decorCtx, -1); - if (_width > 0 && _height > 0) - resize(_width * _scale, _height * _scale); setTitle(_title); setTitlebarVisible(_titlebarVisible); + _visible = true; } ScreenInfo WindowWayland::getScreen() { @@ -194,7 +205,7 @@ void WindowWayland::setVisible(bool isVisible) { if (_visible) { show(); } else { - close(); + hide(); } } } @@ -224,7 +235,7 @@ void jwm::WindowWayland::surfaceEnter(void* data, wl_surface* surface, wl_output self->_output = o; self->_scale = o->scale; wl_surface_set_buffer_scale(surface, o->scale); - self->_adaptSize(self->_width, self->_height); + self->_adaptSize(self->getUnscaledWidth(), self->getUnscaledHeight()); break; } } @@ -239,7 +250,7 @@ void jwm::WindowWayland::surfacePreferredBufferScale(void* data, wl_surface* sur // do I pinky promise here? // yes : ) wl_surface_set_buffer_scale(surface, factor); - self->_adaptSize(self->_width, self->_height); + self->_adaptSize(self->getUnscaledWidth(), self->getUnscaledHeight()); } void jwm::WindowWayland::surfacePreferredBufferTransform(void* data, wl_surface* surface, uint32_t transform) {} @@ -321,10 +332,10 @@ void jwm::WindowWayland::decorFrameConfigure(libdecor_frame* frame, libdecor_con // Throttle frame wl_callback_add_listener(callback, &_frameCallback, self); } - if (!self->_configured && self->_layer) { - self->_layer->attachBuffer(); + if (!self->_configured && self->_visible) { + if (self->_layer) + self->_layer->attachBuffer(); } - self->_configured = true; } void jwm::WindowWayland::decorFrameClose(libdecor_frame* frame, void* userData) { @@ -333,7 +344,7 @@ void jwm::WindowWayland::decorFrameClose(libdecor_frame* frame, void* userData) } void jwm::WindowWayland::decorFrameCommit(libdecor_frame* frame, void* userData) { WindowWayland* self = reinterpret_cast(userData); - self->requestRedraw(); + wl_surface_commit(self->_waylandWindow); } void jwm::WindowWayland::decorFrameDismissPopup(libdecor_frame* frame, const char* seatName, void* userData) {} void jwm::WindowWayland::_adaptSize(int newWidth, int newHeight) { @@ -342,7 +353,7 @@ void jwm::WindowWayland::_adaptSize(int newWidth, int newHeight) { _height = newHeight; int scaledWidth = _width * _scale; int scaledHeight = _height * _scale; - + printf("%i %i\n", scaledWidth, scaledHeight); jwm::JNILocal eventWindowResize( app.getJniEnv(), EventWindowResize::make( @@ -354,6 +365,9 @@ void jwm::WindowWayland::_adaptSize(int newWidth, int newHeight) { ) ); dispatch(eventWindowResize.get()); + // In Java Wayland doesn't actually cause a frame: + // however decorFrameCommit will cause a redraw anyway. + // Not doing it in wayland lets me not cause an exception on hide. } // JNI diff --git a/wayland/cc/WindowWayland.hh b/wayland/cc/WindowWayland.hh index fe119dda..cf72eacc 100644 --- a/wayland/cc/WindowWayland.hh +++ b/wayland/cc/WindowWayland.hh @@ -19,12 +19,15 @@ namespace jwm { void setVisible(bool isVisible); bool resize(int width, int height); void close(); + void hide(); bool init(); void show(); int getLeft(); int getTop(); int getWidth(); + int getUnscaledWidth(); int getHeight(); + int getUnscaledHeight(); float getScale(); void requestRedraw() { _isRedrawRequested = true; From f6fd997d2c49a2891785fc3ec1b554398fcbcfa1 Mon Sep 17 00:00:00 2001 From: BulbyVR <26726264+TheDrawingCoder-Gamer@users.noreply.github.com> Date: Sat, 9 Dec 2023 01:19:37 -0500 Subject: [PATCH 20/95] remove notify debug --- wayland/cc/WindowManagerWayland.cc | 1 - 1 file changed, 1 deletion(-) diff --git a/wayland/cc/WindowManagerWayland.cc b/wayland/cc/WindowManagerWayland.cc index e22c774e..f3ef2058 100644 --- a/wayland/cc/WindowManagerWayland.cc +++ b/wayland/cc/WindowManagerWayland.cc @@ -596,7 +596,6 @@ void WindowManagerWayland::notifyLoop() { if (notifyFD==-1) return; // fast notifyBool path to not make system calls when not necessary if (!notifyBool.exchange(true)) { - printf(" : )\n"); char dummy[1] = {0}; int unused = write(notifyFD, dummy, 1); // this really shouldn't fail, but if it does, the pipe should either be full (good), or dead (bad, but not our business) } From abe4eac0a8f3c953aebf9418ca9da28fa88f414b Mon Sep 17 00:00:00 2001 From: BulbyVR <26726264+TheDrawingCoder-Gamer@users.noreply.github.com> Date: Sat, 9 Dec 2023 13:46:35 -0500 Subject: [PATCH 21/95] More roundtrips : ) --- script/package.py | 3 ++- wayland/cc/LayerRasterWayland.cc | 7 ++++--- wayland/cc/WindowManagerWayland.cc | 2 +- wayland/cc/WindowWayland.cc | 10 ++++++---- 4 files changed, 13 insertions(+), 9 deletions(-) diff --git a/script/package.py b/script/package.py index 5bd1fa1f..adde3153 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/LayerRasterWayland.cc b/wayland/cc/LayerRasterWayland.cc index a0c35f8a..e740e8cd 100644 --- a/wayland/cc/LayerRasterWayland.cc +++ b/wayland/cc/LayerRasterWayland.cc @@ -65,9 +65,10 @@ namespace jwm { if (_attached && fWindow->_waylandWindow) { // all impls that I've seen have to make a new buffer every frame. // God awful. Never use raster if you value performance. - // wl_callback* cb = wl_surface_frame(fWindow->_waylandWindow); - // wl_callback_add_listener(cb, &_frameCallback, this); - swapNow(); + wl_callback* cb = wl_surface_frame(fWindow->_waylandWindow); + wl_callback_add_listener(cb, &_frameCallback, this); + wl_display_roundtrip(fWindow->_windowManager.display); + // swapNow(); } } diff --git a/wayland/cc/WindowManagerWayland.cc b/wayland/cc/WindowManagerWayland.cc index f3ef2058..193fadb0 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 083e0bc2..6838a0e0 100644 --- a/wayland/cc/WindowWayland.cc +++ b/wayland/cc/WindowWayland.cc @@ -14,10 +14,12 @@ 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 }; - + wl_output_listener WindowWayland::_outputListener = { .geometry = WindowWayland::outputGeometry, @@ -181,7 +183,7 @@ void WindowWayland::show() { _frame = libdecor_decorate(_windowManager.decorCtx, _waylandWindow, &_libdecorFrameInterface, this); libdecor_frame_map(_frame); - libdecor_dispatch(_windowManager.decorCtx, -1); + wl_display_roundtrip(_windowManager.display); setTitle(_title); setTitlebarVisible(_titlebarVisible); _visible = true; @@ -345,6 +347,7 @@ void jwm::WindowWayland::decorFrameClose(libdecor_frame* frame, void* userData) void jwm::WindowWayland::decorFrameCommit(libdecor_frame* frame, void* userData) { WindowWayland* self = reinterpret_cast(userData); wl_surface_commit(self->_waylandWindow); + wl_display_roundtrip(self->_windowManager.display); } void jwm::WindowWayland::decorFrameDismissPopup(libdecor_frame* frame, const char* seatName, void* userData) {} void jwm::WindowWayland::_adaptSize(int newWidth, int newHeight) { @@ -353,7 +356,6 @@ void jwm::WindowWayland::_adaptSize(int newWidth, int newHeight) { _height = newHeight; int scaledWidth = _width * _scale; int scaledHeight = _height * _scale; - printf("%i %i\n", scaledWidth, scaledHeight); jwm::JNILocal eventWindowResize( app.getJniEnv(), EventWindowResize::make( @@ -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); From 393e7088802cfd28783905a107b1ba37cfea1dbe Mon Sep 17 00:00:00 2001 From: BulbyVR <26726264+TheDrawingCoder-Gamer@users.noreply.github.com> Date: Sat, 9 Dec 2023 14:17:24 -0500 Subject: [PATCH 22/95] Don't commit if no window : ) --- wayland/cc/WindowWayland.cc | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/wayland/cc/WindowWayland.cc b/wayland/cc/WindowWayland.cc index 6838a0e0..603b8321 100644 --- a/wayland/cc/WindowWayland.cc +++ b/wayland/cc/WindowWayland.cc @@ -346,8 +346,10 @@ void jwm::WindowWayland::decorFrameClose(libdecor_frame* frame, void* userData) } void jwm::WindowWayland::decorFrameCommit(libdecor_frame* frame, void* userData) { WindowWayland* self = reinterpret_cast(userData); - wl_surface_commit(self->_waylandWindow); - wl_display_roundtrip(self->_windowManager.display); + if (self->_waylandWindow) { + wl_surface_commit(self->_waylandWindow); + wl_display_roundtrip(self->_windowManager.display); + } } void jwm::WindowWayland::decorFrameDismissPopup(libdecor_frame* frame, const char* seatName, void* userData) {} void jwm::WindowWayland::_adaptSize(int newWidth, int newHeight) { From 366363f587289e15187324b387a913d57da80508 Mon Sep 17 00:00:00 2001 From: BulbyVR <26726264+TheDrawingCoder-Gamer@users.noreply.github.com> Date: Mon, 11 Dec 2023 11:29:02 -0500 Subject: [PATCH 23/95] Make current before resize --- wayland/cc/LayerGLWayland.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/wayland/cc/LayerGLWayland.cc b/wayland/cc/LayerGLWayland.cc index 75562795..dc67240b 100644 --- a/wayland/cc/LayerGLWayland.cc +++ b/wayland/cc/LayerGLWayland.cc @@ -82,6 +82,8 @@ namespace jwm { } void resize(int width, int height) override { + // Make current to avoid artifacts in other windows + makeCurrentForced(); glClearStencil(0); glClearColor(0, 0, 0, 255); glStencilMask(0xffffffff); From 952c76c5087161ccb6ac8b9c74059dfeb8a5aaaa Mon Sep 17 00:00:00 2001 From: BulbyVR <26726264+TheDrawingCoder-Gamer@users.noreply.github.com> Date: Tue, 12 Dec 2023 10:38:15 -0500 Subject: [PATCH 24/95] Don't block on swap (fixes multiwindow render) --- wayland/cc/LayerGLWayland.cc | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/wayland/cc/LayerGLWayland.cc b/wayland/cc/LayerGLWayland.cc index dc67240b..91822996 100644 --- a/wayland/cc/LayerGLWayland.cc +++ b/wayland/cc/LayerGLWayland.cc @@ -70,10 +70,11 @@ namespace jwm { if ( _context == EGL_NO_CONTEXT ) { throw std::runtime_error("Couldn't make context"); } + // Don't block on swap + eglSwapInterval(_display, 0); } if (visible) fWindow->show(); - makeCurrentForced(); } @@ -97,7 +98,9 @@ namespace jwm { } void swapBuffers() override { + printf("before swap\n"); eglSwapBuffers(_display, _surface); + printf("after swap\n"); } void close() override { From ebafae5a9620c5112df9246cdf0513860cefd6e4 Mon Sep 17 00:00:00 2001 From: BulbyVR <26726264+TheDrawingCoder-Gamer@users.noreply.github.com> Date: Tue, 12 Dec 2023 14:40:53 -0500 Subject: [PATCH 25/95] Make multiwindow work --- script/build.py | 2 +- shared/cc/Window.cc | 1 + wayland/cc/LayerGLWayland.cc | 32 ++++++++++++++++++++---------- wayland/cc/WindowManagerWayland.hh | 4 +++- wayland/cc/WindowWayland.cc | 10 ++++++---- wayland/cc/WindowWayland.hh | 1 - 6 files changed, 32 insertions(+), 18 deletions(-) diff --git a/script/build.py b/script/build.py index 9954d826..589461fb 100755 --- a/script/build.py +++ b/script/build.py @@ -4,7 +4,7 @@ def build_native_system(system): os.chdir(common.basedir + "/" + system) subprocess.check_call(["cmake", - "-DCMAKE_BUILD_TYPE=Release", + "-DCMAKE_BUILD_TYPE=Debug", "-B", "build", "-G", "Ninja", "-DJWM_ARCH=" + build_utils.arch, diff --git a/shared/cc/Window.cc b/shared/cc/Window.cc index ce9c1f29..3567e77f 100644 --- a/shared/cc/Window.cc +++ b/shared/cc/Window.cc @@ -6,6 +6,7 @@ jwm::Window::~Window() { fEnv->DeleteGlobalRef(fWindow); + fWindow = nullptr; } void jwm::Window::dispatch(jobject event) { diff --git a/wayland/cc/LayerGLWayland.cc b/wayland/cc/LayerGLWayland.cc index 91822996..bb347e9b 100644 --- a/wayland/cc/LayerGLWayland.cc +++ b/wayland/cc/LayerGLWayland.cc @@ -22,11 +22,15 @@ namespace jwm { EGLDisplay _display = nullptr; EGLSurface _surface = nullptr; EGLConfig _config = nullptr; + bool _firstAttach = true; + bool _closed = false; LayerGL() = default; virtual ~LayerGL() = default; void attach(WindowWayland* window) { + // no idea why you would reopen it??? + _closed = false; fWindow = jwm::ref(window); fWindow->setLayer(this); // HACK: reopen @@ -34,12 +38,16 @@ namespace jwm { if (visible) { fWindow->hide(); } - if (_display == nullptr) { - _display = eglGetDisplay(window->_windowManager.display); + if (fWindow->_windowManager._eglDisplay == EGL_NO_DISPLAY) { + fWindow->_windowManager._eglDisplay = eglGetDisplay(window->_windowManager.display); - eglInitialize(_display, nullptr, nullptr); + eglInitialize(fWindow->_windowManager._eglDisplay, nullptr, nullptr); } + if (_firstAttach) { + _firstAttach = false; + } + _display = fWindow->_windowManager._eglDisplay; if ( eglBindAPI(EGL_OPENGL_API) == EGL_FALSE) { throw new std::runtime_error("Cannot bind EGL Api"); } @@ -71,6 +79,7 @@ namespace jwm { throw std::runtime_error("Couldn't make context"); } // Don't block on swap + // Blocking here will freeze the app on sway. eglSwapInterval(_display, 0); } if (visible) @@ -84,7 +93,7 @@ namespace jwm { void resize(int width, int height) override { // Make current to avoid artifacts in other windows - makeCurrentForced(); + makeCurrent(); glClearStencil(0); glClearColor(0, 0, 0, 255); glStencilMask(0xffffffff); @@ -98,17 +107,19 @@ namespace jwm { } void swapBuffers() override { - printf("before swap\n"); eglSwapBuffers(_display, _surface); - printf("after swap\n"); } void close() override { + if (_closed) + return; + _closed = true; detach(); eglDestroyContext(_display, _context); - eglTerminate(_display); - + + _firstAttach = true; jwm::unref(&fWindow); + fWindow = nullptr; } void makeCurrentForced() override { @@ -121,9 +132,6 @@ namespace jwm { void attachBuffer() override { if (fWindow && fWindow->_waylandWindow) { if (!_eglWindow) { - // _region = wl_compositor_create_region(fWindow->_windowManager.compositor); - // wl_region_add(_region, 0, 0, fWindow->getWidth(), fWindow->getHeight()); - // wl_surface_set_opaque_region(fWindow->_waylandWindow, _region); _eglWindow = wl_egl_window_create(fWindow->_waylandWindow, fWindow->getWidth(), fWindow->getHeight()); _surface = eglCreateWindowSurface(_display, _config, _eglWindow, nullptr); @@ -181,6 +189,8 @@ extern "C" JNIEXPORT void JNICALL Java_io_github_humbleui_jwm_LayerGL__1nResize extern "C" JNIEXPORT void JNICALL Java_io_github_humbleui_jwm_LayerGL__1nMakeCurrent (JNIEnv* env, jobject obj) { + jwm::LayerGL* instance = reinterpret_cast(jwm::classes::Native::fromJava(env, obj)); + instance->makeCurrent(); } extern "C" JNIEXPORT void JNICALL Java_io_github_humbleui_jwm_LayerGL__1nSwapBuffers diff --git a/wayland/cc/WindowManagerWayland.hh b/wayland/cc/WindowManagerWayland.hh index ad363b43..5dbcd7ab 100644 --- a/wayland/cc/WindowManagerWayland.hh +++ b/wayland/cc/WindowManagerWayland.hh @@ -1,6 +1,5 @@ #pragma once -#include #include #include #include @@ -17,6 +16,8 @@ #include "Output.hh" #include #include +#include +#include namespace jwm { class WindowWayland; @@ -123,6 +124,7 @@ namespace jwm { libdecor* decorCtx = nullptr; xkb_context* _xkbContext = nullptr; xkb_state* _xkbState = nullptr; + EGLDisplay _eglDisplay = EGL_NO_DISPLAY; std::list outputs; bool _runLoop; diff --git a/wayland/cc/WindowWayland.cc b/wayland/cc/WindowWayland.cc index 603b8321..a3ae16bf 100644 --- a/wayland/cc/WindowWayland.cc +++ b/wayland/cc/WindowWayland.cc @@ -47,7 +47,8 @@ WindowWayland::WindowWayland(JNIEnv* env, WindowManagerWayland& windowManager): } WindowWayland::~WindowWayland() { - close(); + // TODO: close gets called twice? + // close(); } @@ -64,12 +65,12 @@ void WindowWayland::setTitlebarVisible(bool isVisible) { } void WindowWayland::close() { + _windowManager.unregisterWindow(this); + hide(); if (_waylandWindow) { - _windowManager.unregisterWindow(this); wl_surface_destroy(_waylandWindow); } _waylandWindow = nullptr; - hide(); // TODO: more destruction! } void WindowWayland::hide() { @@ -264,7 +265,8 @@ void jwm::WindowWayland::outputDone(void* data, wl_output* output) {} void jwm::WindowWayland::outputScale(void* data, wl_output* output, int factor) { WindowWayland* self = reinterpret_cast(data); self->_scale = factor; - self->dispatch(classes::EventWindowScreenChange::kInstance); + if (self->_waylandWindow) + self->dispatch(classes::EventWindowScreenChange::kInstance); } void jwm::WindowWayland::outputName(void* data, wl_output* output, const char* name) {} void jwm::WindowWayland::outputDescription(void* data, wl_output* output, const char* desc) {} diff --git a/wayland/cc/WindowWayland.hh b/wayland/cc/WindowWayland.hh index cf72eacc..326838ee 100644 --- a/wayland/cc/WindowWayland.hh +++ b/wayland/cc/WindowWayland.hh @@ -52,7 +52,6 @@ namespace jwm { void setCursor(jwm::MouseCursor cursor); void setLayer(ILayerWayland* layer); - ScreenInfo getScreen(); static void surfaceEnter(void* data, wl_surface* surface, wl_output* output); From eb4527c549d772c087c3f5688c4a0cd9ad956d4c Mon Sep 17 00:00:00 2001 From: BulbyVR <26726264+TheDrawingCoder-Gamer@users.noreply.github.com> Date: Tue, 12 Dec 2023 14:41:24 -0500 Subject: [PATCH 26/95] forgot to revert to release build --- script/build.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/script/build.py b/script/build.py index 589461fb..9954d826 100755 --- a/script/build.py +++ b/script/build.py @@ -4,7 +4,7 @@ def build_native_system(system): os.chdir(common.basedir + "/" + system) subprocess.check_call(["cmake", - "-DCMAKE_BUILD_TYPE=Debug", + "-DCMAKE_BUILD_TYPE=Release", "-B", "build", "-G", "Ninja", "-DJWM_ARCH=" + build_utils.arch, From 7f6206a57032930ef0c9c053718bb361877a0f13 Mon Sep 17 00:00:00 2001 From: BulbyVR <26726264+TheDrawingCoder-Gamer@users.noreply.github.com> Date: Tue, 12 Dec 2023 14:52:35 -0500 Subject: [PATCH 27/95] Fully working rendering --- wayland/cc/LayerGLWayland.cc | 9 ++------- wayland/cc/LayerRasterWayland.cc | 20 +++++++++----------- 2 files changed, 11 insertions(+), 18 deletions(-) diff --git a/wayland/cc/LayerGLWayland.cc b/wayland/cc/LayerGLWayland.cc index bb347e9b..db458347 100644 --- a/wayland/cc/LayerGLWayland.cc +++ b/wayland/cc/LayerGLWayland.cc @@ -33,11 +33,6 @@ namespace jwm { _closed = false; fWindow = jwm::ref(window); fWindow->setLayer(this); - // HACK: reopen - bool visible = fWindow->_visible; - if (visible) { - fWindow->hide(); - } if (fWindow->_windowManager._eglDisplay == EGL_NO_DISPLAY) { fWindow->_windowManager._eglDisplay = eglGetDisplay(window->_windowManager.display); @@ -82,8 +77,8 @@ namespace jwm { // Blocking here will freeze the app on sway. eglSwapInterval(_display, 0); } - if (visible) - fWindow->show(); + if (fWindow->_configured) + attachBuffer(); makeCurrentForced(); } diff --git a/wayland/cc/LayerRasterWayland.cc b/wayland/cc/LayerRasterWayland.cc index e740e8cd..8ca5664b 100644 --- a/wayland/cc/LayerRasterWayland.cc +++ b/wayland/cc/LayerRasterWayland.cc @@ -20,14 +20,11 @@ namespace jwm { virtual ~LayerRaster() = default; void attach(WindowWayland* window) { - // HACK: close and reopen fWindow = jwm::ref(window); fWindow->setLayer(this); - if (fWindow->_visible) { - fWindow->hide(); - fWindow->show(); - } - + resize(window->getWidth(), window->getHeight()); + if (fWindow->_configured) + attachBuffer(); } void resize(int width, int height) override { @@ -65,16 +62,17 @@ namespace jwm { if (_attached && fWindow->_waylandWindow) { // all impls that I've seen have to make a new buffer every frame. // God awful. Never use raster if you value performance. - wl_callback* cb = wl_surface_frame(fWindow->_waylandWindow); - wl_callback_add_listener(cb, &_frameCallback, this); - wl_display_roundtrip(fWindow->_windowManager.display); - // swapNow(); + // wl_callback* cb = wl_surface_frame(fWindow->_waylandWindow); + // wl_callback_add_listener(cb, &_frameCallback, this); + // wl_display_roundtrip(fWindow->_windowManager.display); + swapNow(); } } void close() override { detach(); jwm::unref(&fWindow); + fWindow = nullptr; } void makeCurrentForced() override { @@ -90,7 +88,7 @@ namespace jwm { } void detach() override { - if (fWindow && fWindow->_waylandWindow) { + if (_attached && fWindow && fWindow->_waylandWindow) { wl_surface_attach(fWindow->_waylandWindow, nullptr, 0, 0); wl_surface_commit(fWindow->_waylandWindow); } From 78cd01f5d27b4d20c769794d29c2550289d5683c Mon Sep 17 00:00:00 2001 From: BulbyVR <26726264+TheDrawingCoder-Gamer@users.noreply.github.com> Date: Tue, 12 Dec 2023 17:03:11 -0500 Subject: [PATCH 28/95] Clean up, clipboard --- wayland/cc/ClipboardWayland.cc | 11 +++------- wayland/cc/WindowManagerWayland.cc | 34 +++++++++++++++++++++++++++--- wayland/cc/WindowWayland.cc | 7 ++++++ 3 files changed, 41 insertions(+), 11 deletions(-) diff --git a/wayland/cc/ClipboardWayland.cc b/wayland/cc/ClipboardWayland.cc index 0d02345a..92ca029f 100644 --- a/wayland/cc/ClipboardWayland.cc +++ b/wayland/cc/ClipboardWayland.cc @@ -14,7 +14,6 @@ namespace jwm { } jobjectArray getFormats(JNIEnv* env) const { - /* auto formats = jwm::app.getWindowManager().getClipboardFormats(); if (formats.empty()) { return nullptr; @@ -32,14 +31,10 @@ namespace jwm { for (jsize i = 0; i < static_cast(formatObjs.size()); ++i) { env->SetObjectArrayElement(jniFormats, i, formatObjs[i]); } - */ - // impl me : ) - jobjectArray formats = env->NewObjectArray(0, classes::ClipboardFormat::kCls, nullptr); - return formats; + return jniFormats; } jobject get(JNIEnv* env, jobjectArray formats) { - /* jsize formatsSize = env->GetArrayLength(formats); for (jsize i = 0; i < formatsSize; ++i) { jobject format = env->GetObjectArrayElement(formats, i); @@ -49,9 +44,11 @@ namespace jwm { ByteBuf contents; // HACK: prefer UTF8_STRING over text/plain and convert it to utf16 + /* if (formatId == "text/plain") { contents = app.getWindowManager().getClipboardContents("UTF8_STRING"); } + */ // TODO add another formats if (contents.empty()) { return nullptr; @@ -67,8 +64,6 @@ namespace jwm { return env->NewGlobalRef(entry.get()); } } - */ - // impl me : ) classes::Throwable::exceptionThrown(env); return nullptr; } diff --git a/wayland/cc/WindowManagerWayland.cc b/wayland/cc/WindowManagerWayland.cc index 193fadb0..1a9db08f 100644 --- a/wayland/cc/WindowManagerWayland.cc +++ b/wayland/cc/WindowManagerWayland.cc @@ -576,12 +576,40 @@ void WindowManagerWayland::terminate() { _runLoop = false; } +wl_data_source_listener WindowManagerWayland::_sourceListener = { + .send = [](void* data, wl_data_source* source, + const char* mimeType, int fd) { + auto self = reinterpret_cast(data); + auto it = self->_myClipboardContents.find(std::string(mimeType)); + if (it != self->_myClipboardContents.end()) { + write(fd, it->second.data(), it->second.size()); + } + close(fd); + }, + .cancelled = [](void* data, wl_data_source* source) { + auto self = reinterpret_cast(data); + wl_data_source_destroy(source); + } +}; void WindowManagerWayland::setClipboardContents(std::map&& c) { assert(("create at least one window in order to use clipboard" && !_nativeWindowToMy.empty())); _myClipboardContents = c; - // impl me : ) - // XSetSelectionOwner(display, XA_PRIMARY, window, CurrentTime); - // XSetSelectionOwner(display, _atoms.CLIPBOARD, window, CurrentTime); + + // god is dead if data device manager is null + + currentSource = wl_data_device_manager_create_data_source(deviceManager); + + wl_data_source_add_listener(currentSource, &_sourceListener, this); + + _currentMimeTypes = {}; + for (auto it : c) { + _currentMimeTypes.push_back(it.first.c_str()); + wl_data_source_offer(currentSource, it.first.c_str()); + } + + if (keyboardSerial >= 0) + wl_data_device_set_selection(dataDevice, currentSource, keyboardSerial); + } void WindowManagerWayland::enqueueTask(const std::function& task) { diff --git a/wayland/cc/WindowWayland.cc b/wayland/cc/WindowWayland.cc index a3ae16bf..f11877eb 100644 --- a/wayland/cc/WindowWayland.cc +++ b/wayland/cc/WindowWayland.cc @@ -133,11 +133,18 @@ bool WindowWayland::resize(int width, int height) { // complain if you try to set a width/height that isn't a multiple of scale. if ((width % _scale) != 0 || (height % _scale) != 0) return false; + + // TODO: adapting size while visible causes issues? + // I've noticed the frame not updating + if (_visible) + return false; _width = width / _scale; _height = height / _scale; + /* if (_visible) { _adaptSize(_width, _height); } + */ return true; } From 460744e7ac92015787e8ce0c6ca84853878c76d3 Mon Sep 17 00:00:00 2001 From: BulbyVR <26726264+TheDrawingCoder-Gamer@users.noreply.github.com> Date: Tue, 12 Dec 2023 19:16:55 -0500 Subject: [PATCH 29/95] Half-baked mouse cursors --- .../dashboard/java/PanelMouseCursors.java | 2 +- shared/cc/MouseCursor.hh | 2 +- wayland/cc/WindowManagerWayland.cc | 38 ++-------- wayland/cc/WindowManagerWayland.hh | 1 + wayland/cc/WindowWayland.cc | 69 +++++++++++++++---- wayland/cc/WindowWayland.hh | 3 + 6 files changed, 70 insertions(+), 45 deletions(-) diff --git a/examples/dashboard/java/PanelMouseCursors.java b/examples/dashboard/java/PanelMouseCursors.java index ee104e0a..fc32b105 100644 --- a/examples/dashboard/java/PanelMouseCursors.java +++ b/examples/dashboard/java/PanelMouseCursors.java @@ -56,7 +56,7 @@ public void accept(Event e) { keepCursor = false; } else if (!keepCursor) { if (window._lastCursor != MouseCursor.ARROW) - window.requestFrame(); + window.requestFrame(); window.setMouseCursor(MouseCursor.ARROW); } lastInside = inside; diff --git a/shared/cc/MouseCursor.hh b/shared/cc/MouseCursor.hh index f7d798ce..8a3f0ab0 100644 --- a/shared/cc/MouseCursor.hh +++ b/shared/cc/MouseCursor.hh @@ -51,4 +51,4 @@ namespace jwm { } } -} \ No newline at end of file +} diff --git a/wayland/cc/WindowManagerWayland.cc b/wayland/cc/WindowManagerWayland.cc index 1a9db08f..d5736d26 100644 --- a/wayland/cc/WindowManagerWayland.cc +++ b/wayland/cc/WindowManagerWayland.cc @@ -76,34 +76,7 @@ WindowManagerWayland::WindowManagerWayland(): wl_display_roundtrip(display); - { - wl_cursor_theme* cursor_theme = wl_cursor_theme_load(nullptr, 24, shm); - // TODO: what about if missing : ( - auto loadCursor = [&](const char* name) { - wl_cursor* cursor = wl_cursor_theme_get_cursor(cursor_theme, name); - wl_cursor_image* cursorImage = cursor->images[0]; - return cursorImage; - }; - - _cursors[static_cast(jwm::MouseCursor::ARROW )] = loadCursor("default"); - _cursors[static_cast(jwm::MouseCursor::CROSSHAIR )] = loadCursor("crosshair"); - _cursors[static_cast(jwm::MouseCursor::HELP )] = loadCursor("help"); - _cursors[static_cast(jwm::MouseCursor::POINTING_HAND )] = loadCursor("pointer"); - _cursors[static_cast(jwm::MouseCursor::IBEAM )] = loadCursor("text"); - _cursors[static_cast(jwm::MouseCursor::NOT_ALLOWED )] = loadCursor("not-allowed"); - _cursors[static_cast(jwm::MouseCursor::WAIT )] = loadCursor("watch"); - _cursors[static_cast(jwm::MouseCursor::RESIZE_NS )] = loadCursor("ns-resize"); - _cursors[static_cast(jwm::MouseCursor::RESIZE_WE )] = loadCursor("ew-resize"); - _cursors[static_cast(jwm::MouseCursor::RESIZE_NESW )] = loadCursor("nesw-resize"); - _cursors[static_cast(jwm::MouseCursor::RESIZE_NWSE )] = loadCursor("nwse-resize"); - - cursorSurface = wl_compositor_create_surface(compositor); - - wl_surface_attach(cursorSurface, - wl_cursor_image_get_buffer(_cursors[static_cast(jwm::MouseCursor::ARROW)]), 0, 0); - wl_surface_commit(cursorSurface); - } - + cursorSurface = wl_compositor_create_surface(compositor); } @@ -241,10 +214,12 @@ WindowWayland* WindowManagerWayland::getWindowForNative(wl_surface* surface) { void WindowManagerWayland::pointerHandleEnter(void* data, wl_pointer* pointer, uint32_t serial, wl_surface *surface, wl_fixed_t surface_x, wl_fixed_t surface_y) { WindowManagerWayland* self = (WindowManagerWayland*)data; - wl_cursor_image* image = self->_cursors[static_cast(jwm::MouseCursor::ARROW)]; - wl_pointer_set_cursor(pointer, serial, self->cursorSurface, image->hotspot_x, image->hotspot_y); - if (self->getWindowForNative(surface)) + self->mouseSerial = serial; + if (self->getWindowForNative(surface)) { + WindowWayland* window = self->getWindowForNative(surface); + window->setCursor(jwm::MouseCursor::ARROW); self->focusedSurface = surface; + } } void WindowManagerWayland::pointerHandleLeave(void* data, wl_pointer* pointer, uint32_t serial, wl_surface *surface) { @@ -252,6 +227,7 @@ void WindowManagerWayland::pointerHandleLeave(void* data, wl_pointer* pointer, u self->focusedSurface = nullptr; // ??? self->mouseMask = 0; + self->mouseSerial = -1; } void WindowManagerWayland::pointerHandleMotion(void* data, wl_pointer* pointer, uint32_t time, wl_fixed_t surface_x, wl_fixed_t surface_y) { diff --git a/wayland/cc/WindowManagerWayland.hh b/wayland/cc/WindowManagerWayland.hh index 5dbcd7ab..6913fd37 100644 --- a/wayland/cc/WindowManagerWayland.hh +++ b/wayland/cc/WindowManagerWayland.hh @@ -120,6 +120,7 @@ namespace jwm { wl_keyboard* keyboard = nullptr; wl_data_device* dataDevice = nullptr; wl_data_source* currentSource = nullptr; + uint32_t mouseSerial = -1; uint32_t keyboardSerial = -1; libdecor* decorCtx = nullptr; xkb_context* _xkbContext = nullptr; diff --git a/wayland/cc/WindowWayland.cc b/wayland/cc/WindowWayland.cc index f11877eb..75c0a63c 100644 --- a/wayland/cc/WindowWayland.cc +++ b/wayland/cc/WindowWayland.cc @@ -44,6 +44,7 @@ WindowWayland::WindowWayland(JNIEnv* env, WindowManagerWayland& windowManager): _title("") { + _makeCursors(); } WindowWayland::~WindowWayland() { @@ -148,6 +149,49 @@ bool WindowWayland::resize(int width, int height) { return true; } + +void WindowWayland::_makeCursors() { + + if (theme) + wl_cursor_theme_destroy(theme); + theme = wl_cursor_theme_load(nullptr, 24 * _scale, _windowManager.shm); + +} +// cursed +wl_cursor* WindowWayland::_getCursorFor(jwm::MouseCursor cursor) { + switch (cursor) { + case jwm::MouseCursor::ARROW: + // works + return wl_cursor_theme_get_cursor(theme, "default"); + case jwm::MouseCursor::CROSSHAIR: + // works + return wl_cursor_theme_get_cursor(theme, "crosshair"); + case jwm::MouseCursor::HELP: + // sometimes works? + return wl_cursor_theme_get_cursor(theme, "help"); + case jwm::MouseCursor::POINTING_HAND: + // SHOULD work + return wl_cursor_theme_get_cursor(theme, "pointer"); + case jwm::MouseCursor::IBEAM: + // doesn't work at all + return wl_cursor_theme_get_cursor(theme, "text"); + case jwm::MouseCursor::NOT_ALLOWED: + return wl_cursor_theme_get_cursor(theme, "not-allowed"); + case jwm::MouseCursor::WAIT: + return wl_cursor_theme_get_cursor(theme, "watch"); + case jwm::MouseCursor::WIN_UPARROW: + return wl_cursor_theme_get_cursor(theme, "up-arrow"); + case jwm::MouseCursor::RESIZE_NS: + return wl_cursor_theme_get_cursor(theme, "ns-resize"); + case jwm::MouseCursor::RESIZE_WE: + return wl_cursor_theme_get_cursor(theme, "ew-resize"); + case jwm::MouseCursor::RESIZE_NESW: + return wl_cursor_theme_get_cursor(theme, "nesw-resize"); + case jwm::MouseCursor::RESIZE_NWSE: + return wl_cursor_theme_get_cursor(theme, "nwse-resize"); + } + return nullptr; +} int WindowWayland::getLeft() { int x, y; getContentPosition(x, y); @@ -221,18 +265,17 @@ void WindowWayland::setVisible(bool isVisible) { } void jwm::WindowWayland::setCursor(jwm::MouseCursor cursor) { - if (auto wayCursor = _windowManager._cursors[static_cast(cursor)]) { - wl_surface_attach(_windowManager.cursorSurface, - wl_cursor_image_get_buffer(wayCursor), - 0, 0); - wl_surface_commit(_windowManager.cursorSurface); - // TODO: hotspots? - } else { - auto otherCursor = _windowManager._cursors[static_cast(jwm::MouseCursor::ARROW)]; - wl_surface_attach(_windowManager.cursorSurface, - wl_cursor_image_get_buffer(otherCursor), 0, 0); - wl_surface_commit(_windowManager.cursorSurface); - } + printf("%s\n", jwm::mouseCursorToStr(cursor)); + // ????? + // Doesn't work for higher numbers??? + auto wayCursor = _getCursorFor(cursor)->images[0]; + wl_surface_attach(_windowManager.cursorSurface, + wl_cursor_image_get_buffer(wayCursor), + 0, 0); + wl_surface_set_buffer_scale(_windowManager.cursorSurface, _scale); + wl_surface_commit(_windowManager.cursorSurface); + wl_pointer_set_cursor(_windowManager.pointer, _windowManager.mouseSerial, _windowManager.cursorSurface, + wayCursor->hotspot_x / _scale, wayCursor->hotspot_y / _scale); } // what do??? @@ -378,6 +421,8 @@ void jwm::WindowWayland::_adaptSize(int newWidth, int newHeight) { ) ); dispatch(eventWindowResize.get()); + + _makeCursors(); // In Java Wayland doesn't actually cause a frame: // however decorFrameCommit will cause a redraw anyway. // Not doing it in wayland lets me not cause an exception on hide. diff --git a/wayland/cc/WindowWayland.hh b/wayland/cc/WindowWayland.hh index 326838ee..6bdaaa75 100644 --- a/wayland/cc/WindowWayland.hh +++ b/wayland/cc/WindowWayland.hh @@ -52,6 +52,8 @@ namespace jwm { void setCursor(jwm::MouseCursor cursor); void setLayer(ILayerWayland* layer); + void _makeCursors(); + wl_cursor* _getCursorFor(jwm::MouseCursor cursor); ScreenInfo getScreen(); static void surfaceEnter(void* data, wl_surface* surface, wl_output* output); @@ -106,6 +108,7 @@ namespace jwm { wl_surface* _waylandWindow = nullptr; libdecor_frame* _frame = nullptr; Output* _output = nullptr; + wl_cursor_theme* theme = nullptr; static wl_surface_listener _surfaceListener; From cd600b01c0d3f630e80b89a5c5f5da8d450f84d3 Mon Sep 17 00:00:00 2001 From: BulbyVR <26726264+TheDrawingCoder-Gamer@users.noreply.github.com> Date: Tue, 12 Dec 2023 21:10:00 -0500 Subject: [PATCH 30/95] Squash merge upstream/main into wayland --- .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(-) diff --git a/.github/workflows/build-deploy.yml b/.github/workflows/build-deploy.yml index ce4f93f7..d3b256a8 100644 --- a/.github/workflows/build-deploy.yml +++ b/.github/workflows/build-deploy.yml @@ -86,6 +86,7 @@ jobs: name: Deploy to Maven Central run: | echo -n "$GPG_SIGNING_KEY" | base64 --decode | gpg --import + gpg --list-keys python3 script/release.py --only java --ref ${{ github.ref }} env: GPG_SIGNING_KEY: ${{ secrets.GPG_SIGNING_KEY }} diff --git a/.github/workflows/rebuild-docker-image.yaml b/.github/workflows/rebuild-docker-image.yaml index 50a5e969..58b337e8 100644 --- a/.github/workflows/rebuild-docker-image.yaml +++ b/.github/workflows/rebuild-docker-image.yaml @@ -13,7 +13,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Check out the repo - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Log in to Docker Hub uses: docker/login-action@v1 diff --git a/CHANGELOG.md b/CHANGELOG.md index 87b16706..0b81607b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,16 @@ +# 0.4.16 - Dec 1, 2023 + +Added: + +- macOS: Window::bringToFront, ::isFront (#269, thx @Quezion) +- macOS: LayerMetal::GetMetalLayer (#275, thx @smallshen) +- X11: add held modifier info on mouse events when window isn't focused (#274, thx @dzaima) + +Fixed: + +- X11: Use XQueryPointer mouse position instead of XI_Motion fields (#272, thx @dzaima) +- X11: fix getScreenCoordinates (#218, #276, thx @TheDrawingCoder-Gamer) + # 0.4.15 - Apr 3, 2023 Added: diff --git a/script/build_utils.py b/script/build_utils.py index 1067db9f..4521ec61 100644 --- a/script/build_utils.py +++ b/script/build_utils.py @@ -9,7 +9,8 @@ def get_arg(name): return vars(args).get(name.replace("-", "_")) execdir = os.getcwd() -arch = get_arg("arch") or {'AMD64': 'x64', 'x86_64': 'x64', 'arm64': 'arm64'}[platform.machine()] +native_arch = {'AMD64': 'x64', 'x86_64': 'x64', 'arm64': 'arm64', 'aarch64': 'arm64'}[platform.machine()] +arch = get_arg("arch") or native_arch system = get_arg("system") or {'Darwin': 'macos', 'Linux': 'linux', 'Windows': 'windows'}[platform.system()] classpath_separator = ';' if platform.system() == 'Windows' else ':' mvn = "mvn.cmd" if platform.system() == "Windows" else "mvn" diff --git a/shared/cc/impl/JNILocal.hh b/shared/cc/impl/JNILocal.hh index 96e9ce59..f9e4852b 100644 --- a/shared/cc/impl/JNILocal.hh +++ b/shared/cc/impl/JNILocal.hh @@ -24,4 +24,4 @@ namespace jwm { JNIEnv* fEnv; T fRef; }; -} \ No newline at end of file +} diff --git a/x11/cc/KeyX11.cc b/x11/cc/KeyX11.cc index 30045e30..0071718d 100644 --- a/x11/cc/KeyX11.cc +++ b/x11/cc/KeyX11.cc @@ -38,21 +38,23 @@ int jwm::KeyX11::getModifiersFromMask(int mask) { return m; } -jwm::Key jwm::KeyX11::fromNative(uint32_t v) { +jwm::Key jwm::KeyX11::fromNative(uint32_t v, jwm::KeyLocation& location, int& modifiers) { + location = jwm::KeyLocation::DEFAULT; + modifiers = 0; switch (v) { // Modifiers case XK_Caps_Lock: return Key::CAPS_LOCK; - case XK_Shift_R: + case XK_Shift_R: location = jwm::KeyLocation::RIGHT; // fallthrough case XK_Shift_L: return Key::SHIFT; - case XK_Control_R: + case XK_Control_R: location = jwm::KeyLocation::RIGHT; // fallthrough case XK_Control_L: return Key::CONTROL; - case XK_Alt_R: + case XK_Alt_R: location = jwm::KeyLocation::RIGHT; // fallthrough case XK_Alt_L: return Key::ALT; // Key::WIN_LOGO - case XK_Super_L: - case XK_Super_R: return Key::LINUX_SUPER; - case XK_Meta_L: - case XK_Meta_R: return Key::LINUX_META; + case XK_Super_R: location = jwm::KeyLocation::RIGHT; // fallthrough + case XK_Super_L: return Key::LINUX_SUPER; + case XK_Meta_R: location = jwm::KeyLocation::RIGHT; // fallthrough + case XK_Meta_L: return Key::LINUX_META; // Key::MAC_COMMAND // Key::MAC_OPTION // Key::MAC_FN @@ -119,23 +121,25 @@ jwm::Key jwm::KeyX11::fromNative(uint32_t v) { case XK_bracketleft: return Key::OPEN_BRACKET; case XK_backslash: return Key::BACK_SLASH; case XK_bracketright: return Key::CLOSE_BRACKET; - case XK_KP_0: return Key::DIGIT0; - case XK_KP_1: return Key::DIGIT1; - case XK_KP_2: return Key::DIGIT2; - case XK_KP_3: return Key::DIGIT3; - case XK_KP_4: return Key::DIGIT4; - case XK_KP_5: return Key::DIGIT5; - case XK_KP_6: return Key::DIGIT6; - case XK_KP_7: return Key::DIGIT7; - case XK_KP_8: return Key::DIGIT8; - case XK_KP_9: return Key::DIGIT9; + case XK_KP_0: case XK_KP_Insert: location = jwm::KeyLocation::KEYPAD; return Key::DIGIT0; + case XK_KP_1: case XK_KP_End: location = jwm::KeyLocation::KEYPAD; return Key::DIGIT1; + case XK_KP_2: case XK_KP_Down: location = jwm::KeyLocation::KEYPAD; return Key::DIGIT2; + case XK_KP_3: case XK_KP_Page_Down: location = jwm::KeyLocation::KEYPAD; return Key::DIGIT3; + case XK_KP_4: case XK_KP_Left: location = jwm::KeyLocation::KEYPAD; return Key::DIGIT4; + case XK_KP_5: case XK_KP_Begin: location = jwm::KeyLocation::KEYPAD; return Key::DIGIT5; + case XK_KP_6: case XK_KP_Right: location = jwm::KeyLocation::KEYPAD; return Key::DIGIT6; + case XK_KP_7: case XK_KP_Home: location = jwm::KeyLocation::KEYPAD; return Key::DIGIT7; + case XK_KP_8: case XK_KP_Up: location = jwm::KeyLocation::KEYPAD; return Key::DIGIT8; + case XK_KP_9: case XK_KP_Page_Up: location = jwm::KeyLocation::KEYPAD; return Key::DIGIT9; + case XK_KP_Add: location = jwm::KeyLocation::KEYPAD; return Key::ADD; + case XK_KP_Separator: location = jwm::KeyLocation::KEYPAD; return Key::SEPARATOR; + case XK_KP_Subtract: location = jwm::KeyLocation::KEYPAD; return Key::MINUS; + case XK_KP_Decimal: location = jwm::KeyLocation::KEYPAD; return Key::PERIOD; + case XK_KP_Divide: location = jwm::KeyLocation::KEYPAD; return Key::SLASH; + case XK_KP_Delete: location = jwm::KeyLocation::KEYPAD; return Key::DEL; + case XK_KP_Enter: location = jwm::KeyLocation::KEYPAD; return Key::ENTER; + case XK_KP_Multiply: location = jwm::KeyLocation::KEYPAD; return Key::MULTIPLY; case XK_multiply: return Key::MULTIPLY; - case XK_KP_Add: return Key::ADD; - case XK_KP_Separator: return Key::SEPARATOR; - case XK_KP_Subtract: return Key::MINUS; - case XK_KP_Decimal: return Key::PERIOD; - case XK_KP_Divide: return Key::SLASH; - case XK_KP_Delete: return Key::DEL; case XK_Delete: return Key::DEL; case XK_Num_Lock: return Key::NUM_LOCK; case XK_Scroll_Lock: return Key::SCROLL_LOCK; @@ -173,6 +177,53 @@ jwm::Key jwm::KeyX11::fromNative(uint32_t v) { // Key::VOLUME_UP // Key::VOLUME_DOWN // Key::MUTE + case XK_exclam: modifiers = (int)jwm::KeyModifier::SHIFT; return Key::DIGIT1; + case XK_quotedbl: modifiers = (int)jwm::KeyModifier::SHIFT; return Key::QUOTE; + case XK_numbersign: modifiers = (int)jwm::KeyModifier::SHIFT; return Key::DIGIT3; + case XK_dollar: modifiers = (int)jwm::KeyModifier::SHIFT; return Key::DIGIT4; + case XK_percent: modifiers = (int)jwm::KeyModifier::SHIFT; return Key::DIGIT5; + case XK_ampersand: modifiers = (int)jwm::KeyModifier::SHIFT; return Key::DIGIT7; + case XK_parenleft: modifiers = (int)jwm::KeyModifier::SHIFT; return Key::DIGIT9; + case XK_parenright: modifiers = (int)jwm::KeyModifier::SHIFT; return Key::DIGIT0; + case XK_asterisk: modifiers = (int)jwm::KeyModifier::SHIFT; return Key::DIGIT8; + case XK_plus: return Key::ADD; + case XK_colon: modifiers = (int)jwm::KeyModifier::SHIFT; return Key::SEMICOLON; + case XK_less: modifiers = (int)jwm::KeyModifier::SHIFT; return Key::COMMA; + case XK_greater: modifiers = (int)jwm::KeyModifier::SHIFT; return Key::PERIOD; + case XK_question: modifiers = (int)jwm::KeyModifier::SHIFT; return Key::SLASH; + case XK_at: modifiers = (int)jwm::KeyModifier::SHIFT; return Key::DIGIT2; + case XK_asciicircum: modifiers = (int)jwm::KeyModifier::SHIFT; return Key::DIGIT6; + case XK_underscore: modifiers = (int)jwm::KeyModifier::SHIFT; return Key::MINUS; + case XK_braceleft: modifiers = (int)jwm::KeyModifier::SHIFT; return Key::OPEN_BRACKET; + case XK_bar: modifiers = (int)jwm::KeyModifier::SHIFT; return Key::BACK_SLASH; + case XK_braceright: modifiers = (int)jwm::KeyModifier::SHIFT; return Key::CLOSE_BRACKET; + case XK_asciitilde: modifiers = (int)jwm::KeyModifier::SHIFT; return Key::BACK_QUOTE; + case XK_A: return Key::A; + case XK_B: return Key::B; + case XK_C: return Key::C; + case XK_D: return Key::D; + case XK_E: return Key::E; + case XK_F: return Key::F; + case XK_G: return Key::G; + case XK_H: return Key::H; + case XK_I: return Key::I; + case XK_J: return Key::J; + case XK_K: return Key::K; + case XK_L: return Key::L; + case XK_M: return Key::M; + case XK_N: return Key::N; + case XK_O: return Key::O; + case XK_P: return Key::P; + case XK_Q: return Key::Q; + case XK_R: return Key::R; + case XK_S: return Key::S; + case XK_T: return Key::T; + case XK_U: return Key::U; + case XK_V: return Key::V; + case XK_W: return Key::W; + case XK_X: return Key::X; + case XK_Y: return Key::Y; + case XK_Z: return Key::Z; default: return Key::UNDEFINED; } } diff --git a/x11/cc/KeyX11.hh b/x11/cc/KeyX11.hh index ce03b165..5f02ede2 100644 --- a/x11/cc/KeyX11.hh +++ b/x11/cc/KeyX11.hh @@ -2,10 +2,11 @@ #include #include "Key.hh" +#include "KeyLocation.hh" namespace jwm { namespace KeyX11 { - jwm::Key fromNative(uint32_t v); + jwm::Key fromNative(uint32_t v, jwm::KeyLocation& location, int& modifiers); bool getKeyState(jwm::Key key); void setKeyState(jwm::Key key, bool isDown); int getModifiers(); diff --git a/x11/cc/WindowManagerX11.cc b/x11/cc/WindowManagerX11.cc index 6d38f024..a1457961 100644 --- a/x11/cc/WindowManagerX11.cc +++ b/x11/cc/WindowManagerX11.cc @@ -588,13 +588,16 @@ void WindowManagerX11::_processXEvent(XEvent& ev) { KeySym s = XLookupKeysym(&ev.xkey, 0); if (s != NoSymbol) { - jwm::Key key = KeyX11::fromNative(s); + int modifiers; + jwm::KeyLocation location; + jwm::Key key = KeyX11::fromNative(s, location, modifiers); jwm::KeyX11::setKeyState(key, true); jwm::JNILocal eventKey(app.getJniEnv(), EventKey::make(app.getJniEnv(), key, true, - jwm::KeyX11::getModifiers())); + jwm::KeyX11::getModifiers() | modifiers, + location)); myWindow->dispatch(eventKey.get()); } @@ -630,13 +633,16 @@ void WindowManagerX11::_processXEvent(XEvent& ev) { case KeyRelease: { // keyboard up KeySym s = XLookupKeysym(&ev.xkey, 0); - jwm::Key key = KeyX11::fromNative(s); + int modifiers; + jwm::KeyLocation location; + jwm::Key key = KeyX11::fromNative(s, location, modifiers); jwm::KeyX11::setKeyState(key, false); jwm::JNILocal eventKey(app.getJniEnv(), EventKey::make(app.getJniEnv(), key, false, - jwm::KeyX11::getModifiers())); + jwm::KeyX11::getModifiers() | modifiers, + location)); myWindow->dispatch(eventKey.get()); break; } diff --git a/x11/cc/WindowX11.cc b/x11/cc/WindowX11.cc index 4c2605f2..177d8e0e 100644 --- a/x11/cc/WindowX11.cc +++ b/x11/cc/WindowX11.cc @@ -8,7 +8,6 @@ #include #include - using namespace jwm; @@ -307,9 +306,14 @@ void WindowX11::getDecorations(int& left, int& top, int& right, int& bottom) { void WindowX11::getContentPosition(int& posX, int& posY) { int x, y; ::Window child; + XWindowAttributes wa; + + XGetWindowAttributes(_windowManager.display, _x11Window, &wa); + + XTranslateCoordinates(_windowManager.display, _x11Window, - XRootWindow(_windowManager.display, 0), + wa.root, 0, 0, &x, &y, &child); @@ -434,7 +438,8 @@ const ScreenInfo& WindowX11::getScreen() { if (screen.bounds.isPointInside(centerX, centerY)) { return screen; } - } + } + return *jwm::app.getScreens().begin(); } From f03b9f124594e2b43f4ef1cac3110b144ed75b2f Mon Sep 17 00:00:00 2001 From: BulbyVR <26726264+TheDrawingCoder-Gamer@users.noreply.github.com> Date: Wed, 13 Dec 2023 22:42:32 -0500 Subject: [PATCH 31/95] Mouse works --- wayland/cc/WindowWayland.cc | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/wayland/cc/WindowWayland.cc b/wayland/cc/WindowWayland.cc index 75c0a63c..90111a15 100644 --- a/wayland/cc/WindowWayland.cc +++ b/wayland/cc/WindowWayland.cc @@ -149,13 +149,11 @@ bool WindowWayland::resize(int width, int height) { return true; } - void WindowWayland::_makeCursors() { if (theme) wl_cursor_theme_destroy(theme); theme = wl_cursor_theme_load(nullptr, 24 * _scale, _windowManager.shm); - } // cursed wl_cursor* WindowWayland::_getCursorFor(jwm::MouseCursor cursor) { @@ -265,17 +263,18 @@ void WindowWayland::setVisible(bool isVisible) { } void jwm::WindowWayland::setCursor(jwm::MouseCursor cursor) { - printf("%s\n", jwm::mouseCursorToStr(cursor)); // ????? // Doesn't work for higher numbers??? auto wayCursor = _getCursorFor(cursor)->images[0]; + auto buf = wl_cursor_image_get_buffer(wayCursor); wl_surface_attach(_windowManager.cursorSurface, wl_cursor_image_get_buffer(wayCursor), 0, 0); wl_surface_set_buffer_scale(_windowManager.cursorSurface, _scale); - wl_surface_commit(_windowManager.cursorSurface); + wl_surface_damage_buffer(_windowManager.cursorSurface, 0, 0, INT32_MAX, INT32_MAX); wl_pointer_set_cursor(_windowManager.pointer, _windowManager.mouseSerial, _windowManager.cursorSurface, wayCursor->hotspot_x / _scale, wayCursor->hotspot_y / _scale); + wl_surface_commit(_windowManager.cursorSurface); } // what do??? From 535f8a4a74bbdeefac5b04407fcdf42bd603fe3b Mon Sep 17 00:00:00 2001 From: BulbyVR <26726264+TheDrawingCoder-Gamer@users.noreply.github.com> Date: Wed, 13 Dec 2023 22:51:58 -0500 Subject: [PATCH 32/95] LESS THREAD SAFETY --- wayland/cc/AppWayland.cc | 10 ++-------- wayland/cc/AppWayland.hh | 2 +- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/wayland/cc/AppWayland.cc b/wayland/cc/AppWayland.cc index e32a2575..5bdde0a9 100644 --- a/wayland/cc/AppWayland.cc +++ b/wayland/cc/AppWayland.cc @@ -7,8 +7,7 @@ jwm::AppWayland jwm::app; void jwm::AppWayland::init(JNIEnv* jniEnv) { - jint rs = jniEnv->GetJavaVM(&_javaVM); - assert(rs == JNI_OK); + _jniEnv = jniEnv; } void jwm::AppWayland::start() { @@ -20,12 +19,7 @@ void jwm::AppWayland::terminate() { } JNIEnv* jwm::AppWayland::getJniEnv() { - JNIEnv* env; - // no-op on an already attached thread, so fast? - // makes it thread-safe (?) - jint rs = _javaVM->AttachCurrentThread((void**)&env, nullptr); - assert(rs == JNI_OK); - return env; + return _jniEnv; } // JNI diff --git a/wayland/cc/AppWayland.hh b/wayland/cc/AppWayland.hh index 2b5ba6f6..690a26f6 100644 --- a/wayland/cc/AppWayland.hh +++ b/wayland/cc/AppWayland.hh @@ -21,7 +21,7 @@ namespace jwm { JNIEnv* getJniEnv(); - JavaVM* _javaVM; + JNIEnv* _jniEnv; WindowManagerWayland wm; } app; } From 0909a7492fe7b37dc8bca1a8f4996dde05aa2bdf Mon Sep 17 00:00:00 2001 From: BulbyVR <26726264+TheDrawingCoder-Gamer@users.noreply.github.com> Date: Wed, 13 Dec 2023 23:16:09 -0500 Subject: [PATCH 33/95] do not load cursor on resize --- wayland/cc/WindowWayland.cc | 5 +++-- wayland/cc/WindowWayland.hh | 1 + 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/wayland/cc/WindowWayland.cc b/wayland/cc/WindowWayland.cc index 90111a15..d78e47ee 100644 --- a/wayland/cc/WindowWayland.cc +++ b/wayland/cc/WindowWayland.cc @@ -420,8 +420,9 @@ void jwm::WindowWayland::_adaptSize(int newWidth, int newHeight) { ) ); dispatch(eventWindowResize.get()); - - _makeCursors(); + if (_scale != _oldScale) + _makeCursors(); + _oldScale = _scale; // In Java Wayland doesn't actually cause a frame: // however decorFrameCommit will cause a redraw anyway. // Not doing it in wayland lets me not cause an exception on hide. diff --git a/wayland/cc/WindowWayland.hh b/wayland/cc/WindowWayland.hh index 6bdaaa75..b1b09384 100644 --- a/wayland/cc/WindowWayland.hh +++ b/wayland/cc/WindowWayland.hh @@ -84,6 +84,7 @@ namespace jwm { int _width = -1; int _newWidth = -1; int _scale = 1; + int _oldScale = 1; int _height = -1; int _newHeight = -1; int _WM_ADD = 1L; From 8349eb464a874d541698617910d3f299a076d215 Mon Sep 17 00:00:00 2001 From: BulbyVR <26726264+TheDrawingCoder-Gamer@users.noreply.github.com> Date: Thu, 14 Dec 2023 00:00:25 -0500 Subject: [PATCH 34/95] scrolling --- wayland/cc/WindowManagerWayland.cc | 51 ++++++++++++++++++++++++++++-- wayland/cc/WindowManagerWayland.hh | 3 ++ wayland/cc/WindowWayland.cc | 2 -- 3 files changed, 51 insertions(+), 5 deletions(-) diff --git a/wayland/cc/WindowManagerWayland.cc b/wayland/cc/WindowManagerWayland.cc index d5736d26..54e9b0cc 100644 --- a/wayland/cc/WindowManagerWayland.cc +++ b/wayland/cc/WindowManagerWayland.cc @@ -35,7 +35,38 @@ wl_pointer_listener WindowManagerWayland::_pointerListener = { .leave = WindowManagerWayland::pointerHandleLeave, .motion = WindowManagerWayland::pointerHandleMotion, .button = WindowManagerWayland::pointerHandleButton, - .axis = WindowManagerWayland::pointerHandleAxis + .axis = WindowManagerWayland::pointerHandleAxis, + .frame = [](void* data, wl_pointer* pointer) { + auto self = reinterpret_cast(data); + if (!self->focusedSurface) return; + if (self->_dX != 0.0f || self->_dY != 0.0f) { + auto env = app.getJniEnv(); + auto win = self->getWindowForNative(self->focusedSurface); + if (win) { + jwm::JNILocal eventAxis( + env, + jwm::classes::EventMouseScroll::make( + env, + self->_dX * win->_scale, + self->_dY * win->_scale, + 0.0f, + 0.0f, + 0.0f, + self->lastMousePosX, + self->lastMousePosY, + jwm::KeyWayland::getModifiers(self->_xkbState) + ) + ); + win->dispatch(eventAxis.get()); + + } + self->_dX = 0.0f; + self->_dY = 0.0f; + } + }, + .axis_source = [](void* data, wl_pointer* pointer, uint32_t source) {}, + .axis_stop = [](void* data, wl_pointer* pointer, uint32_t time, uint32_t axis) {}, + .axis_discrete = [](void* data, wl_pointer* pointer, uint32_t axis, int discrete) {} }; libdecor_interface WindowManagerWayland::_decorInterface = { @@ -178,7 +209,7 @@ void WindowManagerWayland::registryHandleGlobal(void* data, wl_registry *registr &wl_data_device_manager_interface, 1); } else if (strcmp(interface, wl_seat_interface.name) == 0) { self->seat = (wl_seat*)wl_registry_bind(registry, name, - &wl_seat_interface, 1); + &wl_seat_interface, 5); } else if (strcmp(interface, wl_output_interface.name) == 0) { wl_output* output = (wl_output*)wl_registry_bind(registry, name, &wl_output_interface, 2); @@ -321,7 +352,21 @@ void WindowManagerWayland::pointerHandleButton(void* data, wl_pointer* pointer, } } void WindowManagerWayland::pointerHandleAxis(void* data, wl_pointer* pointer, - uint32_t time, uint32_t axis, wl_fixed_t value) {} + uint32_t time, uint32_t axis, wl_fixed_t value) { + auto self = reinterpret_cast(data); + if (!self->focusedSurface) return; + + switch (axis) { + case WL_POINTER_AXIS_VERTICAL_SCROLL: + self->_dY += static_cast(wl_fixed_to_double(value)); + break; + case WL_POINTER_AXIS_HORIZONTAL_SCROLL: + self->_dX += static_cast(wl_fixed_to_double(value)); + break; + default: + break; + } +} void WindowManagerWayland::keyboardKeymap(void* data, wl_keyboard* keyboard, uint32_t format, int32_t fd, uint32_t size) { auto self = reinterpret_cast(data); diff --git a/wayland/cc/WindowManagerWayland.hh b/wayland/cc/WindowManagerWayland.hh index 6913fd37..8c5f73dd 100644 --- a/wayland/cc/WindowManagerWayland.hh +++ b/wayland/cc/WindowManagerWayland.hh @@ -133,6 +133,9 @@ namespace jwm { std::atomic_bool notifyBool{false}; int lastMousePosX = 0; int lastMousePosY = 0; + // god forgive me for using float + float _dX = 0.0f; + float _dY = 0.0f; int mouseMask = 0; void mouseUpdate(WindowWayland* myWindow, uint32_t x, uint32_t y, uint32_t mask); diff --git a/wayland/cc/WindowWayland.cc b/wayland/cc/WindowWayland.cc index d78e47ee..f4303a08 100644 --- a/wayland/cc/WindowWayland.cc +++ b/wayland/cc/WindowWayland.cc @@ -263,8 +263,6 @@ void WindowWayland::setVisible(bool isVisible) { } void jwm::WindowWayland::setCursor(jwm::MouseCursor cursor) { - // ????? - // Doesn't work for higher numbers??? auto wayCursor = _getCursorFor(cursor)->images[0]; auto buf = wl_cursor_image_get_buffer(wayCursor); wl_surface_attach(_windowManager.cursorSurface, From d9efd31c9d2c88ca0ef350dfcad0b78a28060343 Mon Sep 17 00:00:00 2001 From: BulbyVR <26726264+TheDrawingCoder-Gamer@users.noreply.github.com> Date: Fri, 15 Dec 2023 14:57:33 -0500 Subject: [PATCH 35/95] repeating (stalls) --- wayland/cc/Buffer.cc | 9 +- wayland/cc/Keyboard.cc | 166 +++++++++++++++ wayland/cc/Keyboard.hh | 49 +++++ wayland/cc/LayerRasterWayland.cc | 14 -- wayland/cc/WindowManagerWayland.cc | 315 +++++++++++------------------ wayland/cc/WindowManagerWayland.hh | 33 ++- wayland/cc/WindowWayland.cc | 10 +- 7 files changed, 359 insertions(+), 237 deletions(-) create mode 100644 wayland/cc/Keyboard.cc create mode 100644 wayland/cc/Keyboard.hh diff --git a/wayland/cc/Buffer.cc b/wayland/cc/Buffer.cc index 35644145..19ecf611 100644 --- a/wayland/cc/Buffer.cc +++ b/wayland/cc/Buffer.cc @@ -6,11 +6,12 @@ #include using namespace jwm; +static void bufRelease(void* data, wl_buffer* wlbuffer) { + auto buffer = reinterpret_cast(data); + delete buffer; +} wl_buffer_listener Buffer::_bufferListener = { - .release = [](void* data, wl_buffer* wlbuffer) { - auto buffer = reinterpret_cast(data); - delete buffer; - } + .release = bufRelease }; static void randname(char *buf) { diff --git a/wayland/cc/Keyboard.cc b/wayland/cc/Keyboard.cc new file mode 100644 index 00000000..a3a4a478 --- /dev/null +++ b/wayland/cc/Keyboard.cc @@ -0,0 +1,166 @@ +#include "Keyboard.hh" +#include "WindowManagerWayland.hh" +#include "WindowWayland.hh" +#include "KeyWayland.hh" +#include +#include +#include "StringUTF16.hh" +#include +#include +#include +#include +#include +#include "AppWayland.hh" +#include +using namespace jwm; + +// I've noticed that pointers to lambdas are null for some reason. +// No idea what's wrong. Going to cry myself to sleep tonight. +static void kbKeymap(void* data, wl_keyboard* kb, uint32_t format, int32_t fd, uint32_t size) { + auto self = reinterpret_cast(data); + + if (format != WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1) { + close(fd); + fprintf(stderr, "no xkb keymap\n"); + return; + } + + char* map_str = reinterpret_cast(mmap(nullptr, size, PROT_READ, MAP_PRIVATE, fd, 0)); + if (map_str == MAP_FAILED) { + close(fd); + fprintf(stderr, "keymap mmap failed: %s", strerror(errno)); + return; + } + + xkb_keymap* keymap = xkb_keymap_new_from_string( + self->_context, map_str, + XKB_KEYMAP_FORMAT_TEXT_V1, + XKB_KEYMAP_COMPILE_NO_FLAGS + ); + munmap(map_str, size); + close(fd); + + if (!keymap) { + return; + } + self->_state = xkb_state_new(keymap); + + xkb_keymap_unref(keymap); + +} +static void kbEnter(void* data, wl_keyboard* kb, uint32_t serial, wl_surface* surface, + wl_array *keys) { + auto self = reinterpret_cast(data); + auto win = self->_wm.getWindowForNative(surface); + if (!win) return; + self->_serial = serial; + self->_focus = win; + if (self->_state) { + // TODO: keys + } +} +static void kbLeave(void* data, wl_keyboard* kb, uint32_t serial, wl_surface* surface) { + auto self = reinterpret_cast(data); + self->_serial = -1; + self->_focus = nullptr; +} +static void kbKey(void* data, wl_keyboard* kb, uint32_t serial, uint32_t time, + uint32_t key, uint32_t state) { + auto self = reinterpret_cast(data); + if (!self->_state || !self->_focus) return; + + const xkb_keysym_t *syms; + + if (xkb_state_key_get_syms(self->_state, key + 8, &syms) != 1) + return; + jwm::Key jwmKey = KeyWayland::fromNative(syms[0]); + if (jwmKey != jwm::Key::UNDEFINED) { + jwm::KeyLocation location = jwm::KeyLocation::DEFAULT; + JNILocal keyEvent( + jwm::app.getJniEnv(), + classes::EventKey::make( + jwm::app.getJniEnv(), + jwmKey, + state == WL_KEYBOARD_KEY_STATE_PRESSED, + KeyWayland::getModifiers(self->_state), + location + ) + ); + self->_focus->dispatch(keyEvent.get()); + } + self->_repeatingText = false; + self->_repeating = false; + if (state != WL_KEYBOARD_KEY_STATE_PRESSED) { + self->_lastPress = std::chrono::time_point(); + self->_nextRepeat = std::chrono::time_point(); + return; + } + // ??? + self->_lastPress = std::chrono::steady_clock::now(); + self->_repeatKey = jwmKey; + if (self->_repeatRate > 0) { + self->_repeating = true; + self->_nextRepeat = self->_lastPress + std::chrono::milliseconds(self->_repeatDelay); + } + self->_wm.notifyLoop(); + char textBuffer[0x40]; + int count = xkb_state_key_get_utf8(self->_state, key + 8, textBuffer, sizeof(textBuffer)-1); + // ??? + if (count >= sizeof(textBuffer) - 1) { + return; + } + if (count > 0) { + // ignore sinful control symbols + if (textBuffer[0] != 127 && textBuffer[0] > 0x1f) { + JNIEnv* env = jwm::app.getJniEnv(); + + jwm::StringUTF16 converted = reinterpret_cast(textBuffer); + self->_repeatText = converted; + if (self->_repeatRate > 0) + self->_repeatingText = true; + jwm::JNILocal jtext = converted.toJString(env); + + jwm::JNILocal eventTextInput(env, classes::EventTextInput::make(env, jtext.get())); + + + self->_focus->dispatch(eventTextInput.get()); + } + } +} +void kbModifiers(void* data, wl_keyboard* kb, uint32_t serial, uint32_t mods_depressed, + uint32_t mods_latched, uint32_t mods_locked, uint32_t group) { + auto self = reinterpret_cast(data); + if (!self->_state) return; + xkb_state_update_mask(self->_state, + mods_depressed, mods_latched, mods_locked, + 0, 0, group); +} +void kbRepeatInfo(void* data, wl_keyboard* kb, int32_t rate, int32_t delay) { + auto self = reinterpret_cast(data); + self->_repeatRate = rate; + self->_repeatDelay = delay; +} + +wl_keyboard_listener Keyboard::_keyboardListener = { + .keymap = kbKeymap, + .enter = kbEnter, + .leave = kbLeave, + .key = kbKey, + .modifiers = kbModifiers, + .repeat_info = kbRepeatInfo +}; +Keyboard::Keyboard(wl_keyboard* kb, WindowManagerWayland* wm): + _keyboard(kb), + _wm(*wm) +{ + wl_keyboard_add_listener(kb, &_keyboardListener, this); + _context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); +} + +Keyboard::~Keyboard() +{ + if (_keyboard) + wl_keyboard_release(_keyboard); + if (_context) + xkb_context_unref(_context); +} diff --git a/wayland/cc/Keyboard.hh b/wayland/cc/Keyboard.hh new file mode 100644 index 00000000..e2ee2408 --- /dev/null +++ b/wayland/cc/Keyboard.hh @@ -0,0 +1,49 @@ +#pragma once + +#include +#include +#include +#include "KeyWayland.hh" +#include "StringUTF16.hh" + +namespace jwm { + class WindowManagerWayland; + class WindowWayland; + class Keyboard { + public: + Keyboard(wl_keyboard* kb, jwm::WindowManagerWayland* wm); + ~Keyboard(); + + wl_keyboard* _keyboard; + wl_keyboard* getKeyboard() const { + return _keyboard; + } + xkb_context* _context = nullptr; + xkb_state* _state = nullptr; + xkb_state* getState() const { + return _state; + } + jwm::WindowWayland* _focus = nullptr; + jwm::WindowWayland* getFocus() const { + return _focus; + } + uint32_t _serial = 0; + uint32_t getSerial() const { + return _serial; + } + std::chrono::time_point _lastPress; + std::chrono::time_point _nextRepeat; + int32_t _repeatRate = 100; + int32_t _repeatDelay = 300; + + jwm::StringUTF16 _repeatText; + jwm::Key _repeatKey = jwm::Key::UNDEFINED; + bool _repeating = false; + bool _repeatingText = false; + + jwm::WindowManagerWayland& _wm; + + static wl_keyboard_listener _keyboardListener; + + }; +} diff --git a/wayland/cc/LayerRasterWayland.cc b/wayland/cc/LayerRasterWayland.cc index 8ca5664b..9993b387 100644 --- a/wayland/cc/LayerRasterWayland.cc +++ b/wayland/cc/LayerRasterWayland.cc @@ -43,10 +43,6 @@ namespace jwm { return _width * sizeof(uint32_t); } - // only way to really be sane with raster - // it's SO slow that i'll prob - static wl_callback_listener _frameCallback; - void swapNow() { auto buf = Buffer::createShmBuffer(fWindow->_windowManager.shm, _width, _height, WL_SHM_FORMAT_XRGB8888); void* daData = buf->getData(); @@ -62,9 +58,6 @@ namespace jwm { if (_attached && fWindow->_waylandWindow) { // all impls that I've seen have to make a new buffer every frame. // God awful. Never use raster if you value performance. - // wl_callback* cb = wl_surface_frame(fWindow->_waylandWindow); - // wl_callback_add_listener(cb, &_frameCallback, this); - // wl_display_roundtrip(fWindow->_windowManager.display); swapNow(); } } @@ -101,13 +94,6 @@ namespace jwm { } using namespace jwm; -wl_callback_listener LayerRaster::_frameCallback = { - .done = [](void* data, wl_callback* cb, uint32_t cbData) { - auto self = reinterpret_cast(data); - self->swapNow(); - - } -}; extern "C" JNIEXPORT jlong JNICALL Java_io_github_humbleui_jwm_LayerRaster__1nMake (JNIEnv* env, jclass jclass) { jwm::LayerRaster* instance = new jwm::LayerRaster; diff --git a/wayland/cc/WindowManagerWayland.cc b/wayland/cc/WindowManagerWayland.cc index 54e9b0cc..efc0cb2b 100644 --- a/wayland/cc/WindowManagerWayland.cc +++ b/wayland/cc/WindowManagerWayland.cc @@ -23,6 +23,7 @@ #include #include #include +#include using namespace jwm; @@ -30,13 +31,7 @@ wl_registry_listener WindowManagerWayland::_registryListener = { .global = WindowManagerWayland::registryHandleGlobal, .global_remove = WindowManagerWayland::registryHandleGlobalRemove }; -wl_pointer_listener WindowManagerWayland::_pointerListener = { - .enter = WindowManagerWayland::pointerHandleEnter, - .leave = WindowManagerWayland::pointerHandleLeave, - .motion = WindowManagerWayland::pointerHandleMotion, - .button = WindowManagerWayland::pointerHandleButton, - .axis = WindowManagerWayland::pointerHandleAxis, - .frame = [](void* data, wl_pointer* pointer) { +static void pointerFrame(void* data, wl_pointer* pointer) { auto self = reinterpret_cast(data); if (!self->focusedSurface) return; if (self->_dX != 0.0f || self->_dY != 0.0f) { @@ -54,7 +49,7 @@ wl_pointer_listener WindowManagerWayland::_pointerListener = { 0.0f, self->lastMousePosX, self->lastMousePosY, - jwm::KeyWayland::getModifiers(self->_xkbState) + jwm::KeyWayland::getModifiers(self->getXkbState()) ) ); win->dispatch(eventAxis.get()); @@ -63,23 +58,26 @@ wl_pointer_listener WindowManagerWayland::_pointerListener = { self->_dX = 0.0f; self->_dY = 0.0f; } - }, - .axis_source = [](void* data, wl_pointer* pointer, uint32_t source) {}, - .axis_stop = [](void* data, wl_pointer* pointer, uint32_t time, uint32_t axis) {}, - .axis_discrete = [](void* data, wl_pointer* pointer, uint32_t axis, int discrete) {} +} +static void pointerAxisSource(void* data, wl_pointer* pointer, uint32_t source) {} +static void pointerAxisStop(void* data, wl_pointer* pointer, uint32_t time, uint32_t axis) {} +static void pointerAxisDiscrete(void* data, wl_pointer* pointer, uint32_t axis, int discrete) {} +// Lambdas turn into null pointers at runtime. God knows why. +wl_pointer_listener WindowManagerWayland::_pointerListener = { + .enter = WindowManagerWayland::pointerHandleEnter, + .leave = WindowManagerWayland::pointerHandleLeave, + .motion = WindowManagerWayland::pointerHandleMotion, + .button = WindowManagerWayland::pointerHandleButton, + .axis = WindowManagerWayland::pointerHandleAxis, + .frame = pointerFrame, + .axis_source = pointerAxisSource, + .axis_stop = pointerAxisStop, + .axis_discrete = pointerAxisDiscrete }; libdecor_interface WindowManagerWayland::_decorInterface = { .error = WindowManagerWayland::libdecorError }; -wl_keyboard_listener WindowManagerWayland::_keyboardListener = { - .keymap = WindowManagerWayland::keyboardKeymap, - .enter = WindowManagerWayland::keyboardEnter, - .leave = WindowManagerWayland::keyboardLeave, - .key = WindowManagerWayland::keyboardKey, - .modifiers = WindowManagerWayland::keyboardModifiers, - .repeat_info = WindowManagerWayland::keyboardRepeatInfo -}; wl_seat_listener WindowManagerWayland::_seatListener = { .capabilities = WindowManagerWayland::seatCapabilities, .name = WindowManagerWayland::seatName @@ -88,7 +86,6 @@ WindowManagerWayland::WindowManagerWayland(): display(wl_display_connect(nullptr)) { registry = wl_display_get_registry(display); wl_registry_add_listener(registry, &_registryListener, this); - wl_display_roundtrip(display); @@ -134,7 +131,21 @@ void WindowManagerWayland::runLoop() { if (jwm::classes::Throwable::exceptionThrown(app.getJniEnv())) _runLoop = false; // block until event : ) - if (poll(&ps[0], 2, -1) < 0) { + int timeout = -1; + if (_keyboard && _keyboard->_repeating) { + if (_keyboard->_repeatRate > 0) { + auto target = _keyboard->_nextRepeat; + auto now = std::chrono::steady_clock::now(); + if (now < target) + timeout = std::chrono::duration_cast(now - target).count(); + else { + auto ms = std::chrono::milliseconds(_keyboard->_repeatRate); + _keyboard->_nextRepeat += ms; + timeout = _keyboard->_repeatRate; + } + } + } + if (poll(&ps[0], 2, timeout) < 0) { printf("error with pipe\n"); break; } @@ -173,6 +184,31 @@ void WindowManagerWayland::_processCallbacks() { lock.lock(); } } + if (_keyboard && _keyboard->getFocus() && _keyboard->_repeating) { + auto now = std::chrono::steady_clock::now(); + if (now > _keyboard->_nextRepeat) { + auto focus = _keyboard->getFocus(); + auto env = jwm::app.getJniEnv(); + jwm::KeyLocation location = jwm::KeyLocation::DEFAULT; + JNILocal keyEvent( + env, + classes::EventKey::make( + env, + _keyboard->_repeatKey, + true, + KeyWayland::getModifiers(_keyboard->_state), + location + ) + ); + focus->dispatch(keyEvent.get()); + if (_keyboard->_repeatingText) { + jwm::JNILocal jtext = _keyboard->_repeatText.toJString(env); + jwm::JNILocal eventTextInput(env, classes::EventTextInput::make(env, jtext.get())); + + focus->dispatch(eventTextInput.get()); + } + } + } { // copy window list in case one closes any other, invalidating some iterator in _nativeWindowToMy std::vector copy; @@ -306,7 +342,7 @@ void WindowManagerWayland::pointerHandleButton(void* data, wl_pointer* pointer, false, self->lastMousePosX, self->lastMousePosY, - jwm::KeyWayland::getModifiers(self->_xkbState) + jwm::KeyWayland::getModifiers(self->getXkbState()) ) ); WindowWayland* window = self->getWindowForNative(self->focusedSurface); @@ -341,7 +377,7 @@ void WindowManagerWayland::pointerHandleButton(void* data, wl_pointer* pointer, true, self->lastMousePosX, self->lastMousePosY, - jwm::KeyWayland::getModifiers(self->_xkbState) + jwm::KeyWayland::getModifiers(self->getXkbState()) ) ); // me when this stuff is NULL : ( @@ -367,118 +403,6 @@ void WindowManagerWayland::pointerHandleAxis(void* data, wl_pointer* pointer, break; } } -void WindowManagerWayland::keyboardKeymap(void* data, wl_keyboard* keyboard, uint32_t format, - int32_t fd, uint32_t size) { - auto self = reinterpret_cast(data); - - if (format != WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1) { - close(fd); - fprintf(stderr, "no xkb keymap\n"); - return; - } - - char* map_str = reinterpret_cast(mmap(nullptr, size, PROT_READ, MAP_PRIVATE, fd, 0)); - if (map_str == MAP_FAILED) { - close(fd); - fprintf(stderr, "keymap mmap failed: %s", strerror(errno)); - return; - } - - xkb_keymap* keymap = xkb_keymap_new_from_string( - self->_xkbContext, map_str, - XKB_KEYMAP_FORMAT_TEXT_V1, - XKB_KEYMAP_COMPILE_NO_FLAGS - ); - munmap(map_str, size); - close(fd); - - if (!keymap) { - return; - } - self->_xkbState = xkb_state_new(keymap); - - xkb_keymap_unref(keymap); -} -void WindowManagerWayland::keyboardEnter(void* data, wl_keyboard* keyboard, uint32_t serial, wl_surface* surface, - wl_array *keys) { - auto self = reinterpret_cast(data); - self->keyboardSerial = serial; - self->keyboardFocus = surface; -} -void WindowManagerWayland::keyboardLeave(void* data, wl_keyboard* keyboard, uint32_t serial, wl_surface* surface) { - auto self = reinterpret_cast(data); - self->keyboardSerial = -1; - self->keyboardFocus = nullptr; -} - -void WindowManagerWayland::keyboardKey(void* data, wl_keyboard* keyboard, uint32_t serial, uint32_t time, - uint32_t key, uint32_t state) -{ - auto self = reinterpret_cast(data); - if (!self->_xkbState) return; - const xkb_keysym_t *syms; - - if (xkb_state_key_get_syms(self->_xkbState, key + 8, &syms) != 1) - return; - jwm::Key jwmKey = KeyWayland::fromNative(syms[0]); - // 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::DEFAULT; - JNILocal keyEvent( - app.getJniEnv(), - classes::EventKey::make( - app.getJniEnv(), - jwmKey, - state == WL_KEYBOARD_KEY_STATE_PRESSED, - KeyWayland::getModifiers(self->_xkbState), - location - ) - ); - auto window = self->getWindowForNative(self->keyboardFocus); - if (window) - window->dispatch(keyEvent.get()); - } - if (state != WL_KEYBOARD_KEY_STATE_PRESSED) return; - - char textBuffer[0x40]; - int count = xkb_state_key_get_utf8(self->_xkbState, key + 8, textBuffer, sizeof(textBuffer)-1); - // ??? - if (count >= sizeof(textBuffer) - 1) { - return; - } - if (count > 0) { - // ignore sinful control symbols - if (textBuffer[0] != 127 && textBuffer[0] > 0x1f) { - JNIEnv* env = app.getJniEnv(); - - jwm::StringUTF16 converted = reinterpret_cast(textBuffer); - jwm::JNILocal jtext = converted.toJString(env); - - jwm::JNILocal eventTextInput(env, classes::EventTextInput::make(env, jtext.get())); - - - auto window = self->getWindowForNative(self->keyboardFocus); - if (window) - window->dispatch(eventTextInput.get()); - } - } - -} -void WindowManagerWayland::keyboardModifiers(void* data, wl_keyboard* keyboard, - uint32_t serial, uint32_t mods_depressed, uint32_t mods_latched, uint32_t mods_locked, - uint32_t group) { - auto self = reinterpret_cast(data); - if (!self->_xkbState) return; - xkb_state_update_mask(self->_xkbState, - mods_depressed, mods_latched, mods_locked, - 0, 0, group); -} - -void WindowManagerWayland::keyboardRepeatInfo(void* data, wl_keyboard* keyboard, - int32_t rate, int32_t delay) { - // TODO: The client (for some godforsaken reason) is expected to handle repeating characters -} void WindowManagerWayland::seatCapabilities(void* data, wl_seat* seat, uint32_t capabilities) { auto self = reinterpret_cast(data); @@ -492,60 +416,60 @@ void WindowManagerWayland::seatCapabilities(void* data, wl_seat* seat, uint32_t } if ((capabilities & WL_SEAT_CAPABILITY_KEYBOARD) && - !self->keyboard) { - self->keyboard = wl_seat_get_keyboard(seat); - self->_xkbContext = xkb_context_new(XKB_CONTEXT_NO_FLAGS); - wl_keyboard_add_listener(self->keyboard, &_keyboardListener, self); + !self->_keyboard) { + self->_keyboard = new Keyboard(wl_seat_get_keyboard(seat), self); } else if (!(capabilities & WL_SEAT_CAPABILITY_KEYBOARD) && - self->keyboard) { - xkb_context_unref(self->_xkbContext); - wl_keyboard_release(self->keyboard); - self->keyboard = nullptr; + self->_keyboard) { + self->_keyboard = nullptr; } } void WindowManagerWayland::seatName(void* data, wl_seat* seat, const char* name) { // who cares } +static void offerOffer(void* data, wl_data_offer* offer, const char* mimeType) { + auto self = reinterpret_cast(data); + self->_currentMimeTypes.push_back(std::string(mimeType)); +} wl_data_offer_listener WindowManagerWayland::_offerListener = { - .offer = [](void* data, wl_data_offer* offer, const char* mimeType) { - auto self = reinterpret_cast(data); - self->_currentMimeTypes.push_back(std::string(mimeType)); - } + .offer = offerOffer }; -wl_data_device_listener WindowManagerWayland::_deviceListener = { - .data_offer = [](void* data, wl_data_device* device, wl_data_offer* offer) { - auto self = reinterpret_cast(data); - self->_currentMimeTypes = {}; - wl_data_offer_add_listener(offer, &_offerListener, data); - }, - .selection = [](void* data, wl_data_device* device, wl_data_offer* offer) { - auto self = reinterpret_cast(data); - self->_myClipboardContents = {}; - if (offer == nullptr) { - return; - } - for (auto i : self->_currentMimeTypes) { - int fds[2]; - pipe(fds); - wl_data_offer_receive(offer, i.c_str(), fds[1]); - wl_display_flush(self->display); - close(fds[1]); - ByteBuf res; - - - while (true) { - char buf[1024]; - ssize_t n = read(fds[0], buf, sizeof(buf)); - if (n <= 0) - break; - res.insert(res.end(), buf, buf + n); +static void deviceDataOffer(void* data, wl_data_device* device, wl_data_offer* offer) { + auto self = reinterpret_cast(data); + self->_currentMimeTypes = {}; + wl_data_offer_add_listener(offer, &WindowManagerWayland::_offerListener, data); +} +static void deviceSelection(void* data, wl_data_device* device, wl_data_offer* offer) { + auto self = reinterpret_cast(data); + self->_myClipboardContents = {}; + if (offer == nullptr) { + return; + } + for (auto i : self->_currentMimeTypes) { + int fds[2]; + pipe(fds); + wl_data_offer_receive(offer, i.c_str(), fds[1]); + wl_display_flush(self->display); + close(fds[1]); + ByteBuf res; + + + while (true) { + char buf[1024]; + ssize_t n = read(fds[0], buf, sizeof(buf)); + if (n <= 0) + break; + res.insert(res.end(), buf, buf + n); - } - self->_myClipboardContents[i] = res; - close(fds[0]); } - wl_data_offer_destroy(offer); + self->_myClipboardContents[i] = res; + close(fds[0]); } + wl_data_offer_destroy(offer); +} +wl_data_device_listener WindowManagerWayland::_deviceListener = { + .data_offer = deviceDataOffer, + .selection = deviceSelection + }; std::vector WindowManagerWayland::getClipboardFormats() { return { _currentMimeTypes.begin(), _currentMimeTypes.end()}; @@ -568,7 +492,7 @@ void WindowManagerWayland::mouseUpdate(WindowWayland* myWindow, uint32_t x, uint movementY, jwm::MouseButtonWayland::fromNativeMask(mask), // impl me! - jwm::KeyWayland::getModifiers(_xkbState) + jwm::KeyWayland::getModifiers(getXkbState()) ) ); auto foo = eventMove.get(); @@ -597,20 +521,21 @@ void WindowManagerWayland::terminate() { _runLoop = false; } -wl_data_source_listener WindowManagerWayland::_sourceListener = { - .send = [](void* data, wl_data_source* source, - const char* mimeType, int fd) { - auto self = reinterpret_cast(data); - auto it = self->_myClipboardContents.find(std::string(mimeType)); - if (it != self->_myClipboardContents.end()) { - write(fd, it->second.data(), it->second.size()); - } - close(fd); - }, - .cancelled = [](void* data, wl_data_source* source) { - auto self = reinterpret_cast(data); - wl_data_source_destroy(source); +static void dataSourceSend(void* data, wl_data_source* source, const char* mimeType, int fd) { + auto self = reinterpret_cast(data); + auto it = self->_myClipboardContents.find(std::string(mimeType)); + if (it != self->_myClipboardContents.end()) { + write(fd, it->second.data(), it->second.size()); } + close(fd); +} +static void dataSourceCancelled(void* data, wl_data_source* source) { + auto self = reinterpret_cast(data); + wl_data_source_destroy(source); +} +wl_data_source_listener WindowManagerWayland::_sourceListener = { + .send = dataSourceSend, + .cancelled = dataSourceCancelled }; void WindowManagerWayland::setClipboardContents(std::map&& c) { assert(("create at least one window in order to use clipboard" && !_nativeWindowToMy.empty())); @@ -628,8 +553,8 @@ void WindowManagerWayland::setClipboardContents(std::map&& wl_data_source_offer(currentSource, it.first.c_str()); } - if (keyboardSerial >= 0) - wl_data_device_set_selection(dataDevice, currentSource, keyboardSerial); + if (getKeyboardSerial() > 0) + wl_data_device_set_selection(dataDevice, currentSource, getKeyboardSerial()); } diff --git a/wayland/cc/WindowManagerWayland.hh b/wayland/cc/WindowManagerWayland.hh index 8c5f73dd..e6547f85 100644 --- a/wayland/cc/WindowManagerWayland.hh +++ b/wayland/cc/WindowManagerWayland.hh @@ -18,6 +18,7 @@ #include #include #include +#include "Keyboard.hh" namespace jwm { class WindowWayland; @@ -83,20 +84,6 @@ namespace jwm { static libdecor_interface _decorInterface; static void libdecorError(libdecor* context, enum libdecor_error error, const char* message); - static wl_keyboard_listener _keyboardListener; - static void keyboardKeymap(void* data, wl_keyboard* keyboard, - uint32_t format, int32_t fd, uint32_t size); - static void keyboardEnter(void* data, wl_keyboard* keyboard, - uint32_t serial, wl_surface* surface, wl_array* keys); - static void keyboardLeave(void* data, wl_keyboard* keyboard, uint32_t serial, - wl_surface* surface); - static void keyboardKey(void* data, wl_keyboard* keyboard, uint32_t serial, - uint32_t time, uint32_t key, uint32_t state); - static void keyboardModifiers(void* data, wl_keyboard* keyboard, uint32_t serial, - uint32_t modsDepressed, uint32_t modsLatched, uint32_t modLocked, - uint32_t group); - static void keyboardRepeatInfo(void* data, wl_keyboard* keyboard, int32_t rate, int32_t delay); - static wl_seat_listener _seatListener; static void seatCapabilities(void* data, wl_seat* seat, uint32_t capabilities); @@ -117,14 +104,21 @@ namespace jwm { // no multiseat? wl_seat* seat = nullptr; wl_pointer* pointer = nullptr; - wl_keyboard* keyboard = nullptr; + Keyboard* _keyboard = nullptr; wl_data_device* dataDevice = nullptr; wl_data_source* currentSource = nullptr; - uint32_t mouseSerial = -1; - uint32_t keyboardSerial = -1; + uint32_t mouseSerial = 0; + uint32_t getKeyboardSerial() const { + if (_keyboard) + return _keyboard->getSerial(); + return 0; + } + xkb_state* getXkbState() const { + if (_keyboard) + return _keyboard->getState(); + return nullptr; + } libdecor* decorCtx = nullptr; - xkb_context* _xkbContext = nullptr; - xkb_state* _xkbState = nullptr; EGLDisplay _eglDisplay = EGL_NO_DISPLAY; std::list outputs; @@ -146,7 +140,6 @@ namespace jwm { wl_surface* cursorSurface; wl_surface* focusedSurface = nullptr; - wl_surface* keyboardFocus = nullptr; // Is holding all cursors in memory a good idea? wl_cursor_image* _cursors[static_cast(jwm::MouseCursor::COUNT)]; diff --git a/wayland/cc/WindowWayland.cc b/wayland/cc/WindowWayland.cc index f4303a08..5e451a70 100644 --- a/wayland/cc/WindowWayland.cc +++ b/wayland/cc/WindowWayland.cc @@ -317,11 +317,13 @@ void jwm::WindowWayland::outputScale(void* data, wl_output* output, int factor) } void jwm::WindowWayland::outputName(void* data, wl_output* output, const char* name) {} void jwm::WindowWayland::outputDescription(void* data, wl_output* output, const char* desc) {} + +static void frameCallbackDone(void* data, wl_callback* cb, uint32_t cb_data) { + auto self = reinterpret_cast(data); + self->_adaptSize(self->_newWidth, self->_newHeight); +} wl_callback_listener jwm::WindowWayland::_frameCallback = { - .done = [](void* data, wl_callback* cb, uint32_t cb_data) { - auto self = reinterpret_cast(data); - self->_adaptSize(self->_newWidth, self->_newHeight); - } + .done = frameCallbackDone }; void jwm::WindowWayland::decorFrameConfigure(libdecor_frame* frame, libdecor_configuration* configuration, From 81077866806a200018174162d7e7c1dc78fbdf3a Mon Sep 17 00:00:00 2001 From: BulbyVR <26726264+TheDrawingCoder-Gamer@users.noreply.github.com> Date: Fri, 15 Dec 2023 15:56:16 -0500 Subject: [PATCH 36/95] lowercase keys --- wayland/cc/Keyboard.cc | 20 ++++++++++++-------- wayland/cc/Keyboard.hh | 1 + wayland/cc/WindowManagerWayland.cc | 12 ++++++++++++ 3 files changed, 25 insertions(+), 8 deletions(-) diff --git a/wayland/cc/Keyboard.cc b/wayland/cc/Keyboard.cc index a3a4a478..9b4fb405 100644 --- a/wayland/cc/Keyboard.cc +++ b/wayland/cc/Keyboard.cc @@ -45,8 +45,7 @@ static void kbKeymap(void* data, wl_keyboard* kb, uint32_t format, int32_t fd, u } self->_state = xkb_state_new(keymap); - xkb_keymap_unref(keymap); - + self->_keymap = keymap; } static void kbEnter(void* data, wl_keyboard* kb, uint32_t serial, wl_surface* surface, wl_array *keys) { @@ -70,10 +69,12 @@ static void kbKey(void* data, wl_keyboard* kb, uint32_t serial, uint32_t time, if (!self->_state || !self->_focus) return; const xkb_keysym_t *syms; - - if (xkb_state_key_get_syms(self->_state, key + 8, &syms) != 1) + + uint32_t keyCode = key + 8; + if (xkb_state_key_get_syms(self->_state, keyCode, &syms) != 1) return; - jwm::Key jwmKey = KeyWayland::fromNative(syms[0]); + auto lowerSym = xkb_keysym_to_lower(syms[0]); + jwm::Key jwmKey = KeyWayland::fromNative(lowerSym); if (jwmKey != jwm::Key::UNDEFINED) { jwm::KeyLocation location = jwm::KeyLocation::DEFAULT; JNILocal keyEvent( @@ -98,13 +99,14 @@ static void kbKey(void* data, wl_keyboard* kb, uint32_t serial, uint32_t time, // ??? self->_lastPress = std::chrono::steady_clock::now(); self->_repeatKey = jwmKey; - if (self->_repeatRate > 0) { + bool shouldRepeat = xkb_keymap_key_repeats(self->_keymap, keyCode) && (self->_repeatRate > 0); + if (shouldRepeat && (jwmKey != jwm::Key::UNDEFINED)) { self->_repeating = true; self->_nextRepeat = self->_lastPress + std::chrono::milliseconds(self->_repeatDelay); } self->_wm.notifyLoop(); char textBuffer[0x40]; - int count = xkb_state_key_get_utf8(self->_state, key + 8, textBuffer, sizeof(textBuffer)-1); + int count = xkb_state_key_get_utf8(self->_state, keyCode, textBuffer, sizeof(textBuffer)-1); // ??? if (count >= sizeof(textBuffer) - 1) { return; @@ -116,7 +118,7 @@ static void kbKey(void* data, wl_keyboard* kb, uint32_t serial, uint32_t time, jwm::StringUTF16 converted = reinterpret_cast(textBuffer); self->_repeatText = converted; - if (self->_repeatRate > 0) + if (shouldRepeat) self->_repeatingText = true; jwm::JNILocal jtext = converted.toJString(env); @@ -163,4 +165,6 @@ Keyboard::~Keyboard() wl_keyboard_release(_keyboard); if (_context) xkb_context_unref(_context); + if (_keymap) + xkb_keymap_unref(_keymap); } diff --git a/wayland/cc/Keyboard.hh b/wayland/cc/Keyboard.hh index e2ee2408..7b16522d 100644 --- a/wayland/cc/Keyboard.hh +++ b/wayland/cc/Keyboard.hh @@ -20,6 +20,7 @@ namespace jwm { } xkb_context* _context = nullptr; xkb_state* _state = nullptr; + xkb_keymap* _keymap = nullptr; xkb_state* getState() const { return _state; } diff --git a/wayland/cc/WindowManagerWayland.cc b/wayland/cc/WindowManagerWayland.cc index efc0cb2b..92e8be9d 100644 --- a/wayland/cc/WindowManagerWayland.cc +++ b/wayland/cc/WindowManagerWayland.cc @@ -190,6 +190,17 @@ void WindowManagerWayland::_processCallbacks() { auto focus = _keyboard->getFocus(); auto env = jwm::app.getJniEnv(); jwm::KeyLocation location = jwm::KeyLocation::DEFAULT; + JNILocal keyOffEvent( + env, + classes::EventKey::make( + env, + _keyboard->_repeatKey, + false, + KeyWayland::getModifiers(_keyboard->_state), + location + ) + ); + JNILocal keyEvent( env, classes::EventKey::make( @@ -200,6 +211,7 @@ void WindowManagerWayland::_processCallbacks() { location ) ); + focus->dispatch(keyOffEvent.get()); focus->dispatch(keyEvent.get()); if (_keyboard->_repeatingText) { jwm::JNILocal jtext = _keyboard->_repeatText.toJString(env); From fd42b2f66db8c6a43ce336f8422a03d553b6b2ee Mon Sep 17 00:00:00 2001 From: BulbyVR <26726264+TheDrawingCoder-Gamer@users.noreply.github.com> Date: Fri, 15 Dec 2023 16:21:23 -0500 Subject: [PATCH 37/95] Acceptable (lags) --- wayland/cc/WindowManagerWayland.cc | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/wayland/cc/WindowManagerWayland.cc b/wayland/cc/WindowManagerWayland.cc index 92e8be9d..145881db 100644 --- a/wayland/cc/WindowManagerWayland.cc +++ b/wayland/cc/WindowManagerWayland.cc @@ -134,14 +134,10 @@ void WindowManagerWayland::runLoop() { int timeout = -1; if (_keyboard && _keyboard->_repeating) { if (_keyboard->_repeatRate > 0) { - auto target = _keyboard->_nextRepeat; auto now = std::chrono::steady_clock::now(); - if (now < target) - timeout = std::chrono::duration_cast(now - target).count(); - else { - auto ms = std::chrono::milliseconds(_keyboard->_repeatRate); - _keyboard->_nextRepeat += ms; - timeout = _keyboard->_repeatRate; + auto target = _keyboard->_nextRepeat; + if (now < target) { + timeout = std::chrono::duration_cast(target - now).count(); } } } @@ -184,9 +180,10 @@ void WindowManagerWayland::_processCallbacks() { lock.lock(); } } - if (_keyboard && _keyboard->getFocus() && _keyboard->_repeating) { + if (_keyboard && _keyboard->_repeating && _keyboard->getFocus()) { auto now = std::chrono::steady_clock::now(); if (now > _keyboard->_nextRepeat) { + _keyboard->_nextRepeat = now + std::chrono::milliseconds(_keyboard->_repeatRate); auto focus = _keyboard->getFocus(); auto env = jwm::app.getJniEnv(); jwm::KeyLocation location = jwm::KeyLocation::DEFAULT; From 05cf38bcfe13cb1e269b8e69c145ddfb2963798f Mon Sep 17 00:00:00 2001 From: BulbyVR <26726264+TheDrawingCoder-Gamer@users.noreply.github.com> Date: Fri, 15 Dec 2023 17:23:43 -0500 Subject: [PATCH 38/95] compose --- wayland/cc/Keyboard.cc | 87 +++++++++++++++++++++++++++++++++--------- wayland/cc/Keyboard.hh | 4 ++ 2 files changed, 72 insertions(+), 19 deletions(-) diff --git a/wayland/cc/Keyboard.cc b/wayland/cc/Keyboard.cc index 9b4fb405..6fffc3e6 100644 --- a/wayland/cc/Keyboard.cc +++ b/wayland/cc/Keyboard.cc @@ -12,13 +12,15 @@ #include #include "AppWayland.hh" #include +#include using namespace jwm; // I've noticed that pointers to lambdas are null for some reason. // No idea what's wrong. Going to cry myself to sleep tonight. static void kbKeymap(void* data, wl_keyboard* kb, uint32_t format, int32_t fd, uint32_t size) { auto self = reinterpret_cast(data); - + + if (format != WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1) { close(fd); fprintf(stderr, "no xkb keymap\n"); @@ -46,6 +48,12 @@ static void kbKeymap(void* data, wl_keyboard* kb, uint32_t format, int32_t fd, u self->_state = xkb_state_new(keymap); self->_keymap = keymap; + + const char* locale = std::setlocale(LC_CTYPE, nullptr); + + self->_composeTable = xkb_compose_table_new_from_locale(self->_context, locale, XKB_COMPOSE_COMPILE_NO_FLAGS); + self->_composeState = xkb_compose_state_new(self->_composeTable, XKB_COMPOSE_STATE_NO_FLAGS); + } static void kbEnter(void* data, wl_keyboard* kb, uint32_t serial, wl_surface* surface, wl_array *keys) { @@ -71,29 +79,70 @@ static void kbKey(void* data, wl_keyboard* kb, uint32_t serial, uint32_t time, const xkb_keysym_t *syms; uint32_t keyCode = key + 8; - if (xkb_state_key_get_syms(self->_state, keyCode, &syms) != 1) + if (xkb_state_key_get_syms(self->_state, keyCode, &syms) != 1) { + xkb_compose_state_feed(self->_composeState, XKB_KEY_NoSymbol); return; - auto lowerSym = xkb_keysym_to_lower(syms[0]); - jwm::Key jwmKey = KeyWayland::fromNative(lowerSym); - if (jwmKey != jwm::Key::UNDEFINED) { - jwm::KeyLocation location = jwm::KeyLocation::DEFAULT; - JNILocal keyEvent( - jwm::app.getJniEnv(), - classes::EventKey::make( - jwm::app.getJniEnv(), - jwmKey, - state == WL_KEYBOARD_KEY_STATE_PRESSED, - KeyWayland::getModifiers(self->_state), - location - ) - ); - self->_focus->dispatch(keyEvent.get()); } + auto sym = syms[0]; + auto lowerSym = xkb_keysym_to_lower(sym); + if (state == WL_KEYBOARD_KEY_STATE_PRESSED) + xkb_compose_state_feed(self->_composeState, sym); + auto status = xkb_compose_state_get_status(self->_composeState); + bool composeRelated = status != XKB_COMPOSE_NOTHING; + jwm::Key jwmKey = KeyWayland::fromNative(lowerSym); self->_repeatingText = false; self->_repeating = false; + if (!composeRelated) { + if (jwmKey != jwm::Key::UNDEFINED) { + jwm::KeyLocation location = jwm::KeyLocation::DEFAULT; + JNILocal keyEvent( + jwm::app.getJniEnv(), + classes::EventKey::make( + jwm::app.getJniEnv(), + jwmKey, + state == WL_KEYBOARD_KEY_STATE_PRESSED, + KeyWayland::getModifiers(self->_state), + location + ) + ); + self->_focus->dispatch(keyEvent.get()); + } + } else { + int dacount; + switch (status) { + case XKB_COMPOSE_COMPOSING: + break; + case XKB_COMPOSE_COMPOSED: + // I am going to wager a guess that no one will ever have a compose key that binds to a + // key we actually parse. + // auto keysym = xkb_compose_state_get_one_sym(self->_composeState); + char textBuf[0x40]; + + dacount = xkb_compose_state_get_utf8(self->_composeState, textBuf, sizeof(textBuf) - 1); + + if (dacount > 0 && (dacount < sizeof(textBuf) - 1)) { + JNIEnv* env = jwm::app.getJniEnv(); + + jwm::StringUTF16 converted = reinterpret_cast(textBuf); + jwm::JNILocal jtext = converted.toJString(env); + + jwm::JNILocal eventTextInput(env, classes::EventTextInput::make(env, jtext.get())); + + + self->_focus->dispatch(eventTextInput.get()); + + } + + + xkb_compose_state_reset(self->_composeState); + break; + case XKB_COMPOSE_CANCELLED: + xkb_compose_state_reset(self->_composeState); + break; + } + return; + } if (state != WL_KEYBOARD_KEY_STATE_PRESSED) { - self->_lastPress = std::chrono::time_point(); - self->_nextRepeat = std::chrono::time_point(); return; } // ??? diff --git a/wayland/cc/Keyboard.hh b/wayland/cc/Keyboard.hh index 7b16522d..464259aa 100644 --- a/wayland/cc/Keyboard.hh +++ b/wayland/cc/Keyboard.hh @@ -2,6 +2,7 @@ #include #include +#include #include #include "KeyWayland.hh" #include "StringUTF16.hh" @@ -21,6 +22,9 @@ namespace jwm { xkb_context* _context = nullptr; xkb_state* _state = nullptr; xkb_keymap* _keymap = nullptr; + xkb_compose_table* _composeTable = nullptr; + xkb_compose_state* _composeState = nullptr; + xkb_state* getState() const { return _state; } From f6f8bd031fe7268a707476a21fff9c5360b76f3c Mon Sep 17 00:00:00 2001 From: BulbyVR <26726264+TheDrawingCoder-Gamer@users.noreply.github.com> Date: Fri, 15 Dec 2023 19:28:23 -0500 Subject: [PATCH 39/95] keycodes, scaling --- wayland/cc/KeyWayland.cc | 254 ++++++++++++++++++----------------- wayland/cc/Keyboard.cc | 36 +++-- wayland/cc/LayerGLWayland.cc | 5 + wayland/cc/WindowWayland.cc | 16 +-- 4 files changed, 156 insertions(+), 155 deletions(-) diff --git a/wayland/cc/KeyWayland.cc b/wayland/cc/KeyWayland.cc index 9c21f662..14d3b95f 100644 --- a/wayland/cc/KeyWayland.cc +++ b/wayland/cc/KeyWayland.cc @@ -2,6 +2,7 @@ #include "KeyModifier.hh" #include #include +#include int jwm::KeyWayland::getModifiers(xkb_state* state) { @@ -29,140 +30,141 @@ int jwm::KeyWayland::getModifiersFromMask(int mask) { }*/ jwm::Key jwm::KeyWayland::fromNative(uint32_t v) { - switch (v) { + switch (v - 8) { // Modifiers - case XKB_KEY_Caps_Lock: return Key::CAPS_LOCK; - case XKB_KEY_Shift_R: - case XKB_KEY_Shift_L: return Key::SHIFT; - case XKB_KEY_Control_R: - case XKB_KEY_Control_L: return Key::CONTROL; - case XKB_KEY_Alt_R: - case XKB_KEY_Alt_L: return Key::ALT; + case KEY_CAPSLOCK: return Key::CAPS_LOCK; + case KEY_RIGHTSHIFT: + case KEY_LEFTSHIFT: return Key::SHIFT; + case KEY_RIGHTCTRL: + case KEY_LEFTCTRL: return Key::CONTROL; + case KEY_RIGHTALT: + case KEY_LEFTALT: return Key::ALT; // Key::WIN_LOGO - case XKB_KEY_Super_L: - case XKB_KEY_Super_R: return Key::LINUX_SUPER; - case XKB_KEY_Meta_L: - case XKB_KEY_Meta_R: return Key::LINUX_META; + case KEY_LEFTMETA: + case KEY_RIGHTMETA: return Key::LINUX_SUPER; + // prefer super over meta + // KEY::LINUX_META // Key::MAC_COMMAND // Key::MAC_OPTION // Key::MAC_FN // Rest of the keys - case XKB_KEY_Return: return Key::ENTER; - case XKB_KEY_BackSpace: return Key::BACKSPACE; - case XKB_KEY_Tab: return Key::TAB; - case XKB_KEY_Cancel: return Key::CANCEL; - case XKB_KEY_Clear: return Key::CLEAR; - case XKB_KEY_Pause: return Key::PAUSE; - case XKB_KEY_Escape: return Key::ESCAPE; - case XKB_KEY_space: return Key::SPACE; - case XKB_KEY_Page_Up: return Key::PAGE_UP; - case XKB_KEY_Page_Down: return Key::PAGE_DOWN; - case XKB_KEY_End: return Key::END; - case XKB_KEY_Home: return Key::HOME; - case XKB_KEY_Left: return Key::LEFT; - case XKB_KEY_Up: return Key::UP; - case XKB_KEY_Right: return Key::RIGHT; - case XKB_KEY_Down: return Key::DOWN; - case XKB_KEY_comma: return Key::COMMA; - case XKB_KEY_minus: return Key::MINUS; - case XKB_KEY_period: return Key::PERIOD; - case XKB_KEY_slash: return Key::SLASH; - case XKB_KEY_0: return Key::DIGIT0; - case XKB_KEY_1: return Key::DIGIT1; - case XKB_KEY_2: return Key::DIGIT2; - case XKB_KEY_3: return Key::DIGIT3; - case XKB_KEY_4: return Key::DIGIT4; - case XKB_KEY_5: return Key::DIGIT5; - case XKB_KEY_6: return Key::DIGIT6; - case XKB_KEY_7: return Key::DIGIT7; - case XKB_KEY_8: return Key::DIGIT8; - case XKB_KEY_9: return Key::DIGIT9; - case XKB_KEY_semicolon: return Key::SEMICOLON; - case XKB_KEY_equal: return Key::EQUALS; - case XKB_KEY_a: return Key::A; - case XKB_KEY_b: return Key::B; - case XKB_KEY_c: return Key::C; - case XKB_KEY_d: return Key::D; - case XKB_KEY_e: return Key::E; - case XKB_KEY_f: return Key::F; - case XKB_KEY_g: return Key::G; - case XKB_KEY_h: return Key::H; - case XKB_KEY_i: return Key::I; - case XKB_KEY_j: return Key::J; - case XKB_KEY_k: return Key::K; - case XKB_KEY_l: return Key::L; - case XKB_KEY_m: return Key::M; - case XKB_KEY_n: return Key::N; - case XKB_KEY_o: return Key::O; - case XKB_KEY_p: return Key::P; - case XKB_KEY_q: return Key::Q; - case XKB_KEY_r: return Key::R; - case XKB_KEY_s: return Key::S; - case XKB_KEY_t: return Key::T; - case XKB_KEY_u: return Key::U; - case XKB_KEY_v: return Key::V; - case XKB_KEY_w: return Key::W; - case XKB_KEY_x: return Key::X; - case XKB_KEY_y: return Key::Y; - case XKB_KEY_z: return Key::Z; - case XKB_KEY_bracketleft: return Key::OPEN_BRACKET; - case XKB_KEY_backslash: return Key::BACK_SLASH; - case XKB_KEY_bracketright: return Key::CLOSE_BRACKET; - case XKB_KEY_KP_0: return Key::DIGIT0; - case XKB_KEY_KP_1: return Key::DIGIT1; - case XKB_KEY_KP_2: return Key::DIGIT2; - case XKB_KEY_KP_3: return Key::DIGIT3; - case XKB_KEY_KP_4: return Key::DIGIT4; - case XKB_KEY_KP_5: return Key::DIGIT5; - case XKB_KEY_KP_6: return Key::DIGIT6; - case XKB_KEY_KP_7: return Key::DIGIT7; - case XKB_KEY_KP_8: return Key::DIGIT8; - case XKB_KEY_KP_9: return Key::DIGIT9; - case XKB_KEY_multiply: return Key::MULTIPLY; - case XKB_KEY_KP_Add: return Key::ADD; - case XKB_KEY_KP_Separator: return Key::SEPARATOR; - case XKB_KEY_KP_Subtract: return Key::MINUS; - case XKB_KEY_KP_Decimal: return Key::PERIOD; - case XKB_KEY_KP_Divide: return Key::SLASH; - case XKB_KEY_KP_Delete: return Key::DEL; - case XKB_KEY_Delete: return Key::DEL; - case XKB_KEY_Num_Lock: return Key::NUM_LOCK; - case XKB_KEY_Scroll_Lock: return Key::SCROLL_LOCK; - case XKB_KEY_F1: return Key::F1; - case XKB_KEY_F2: return Key::F2; - case XKB_KEY_F3: return Key::F3; - case XKB_KEY_F4: return Key::F4; - case XKB_KEY_F5: return Key::F5; - case XKB_KEY_F6: return Key::F6; - case XKB_KEY_F7: return Key::F7; - case XKB_KEY_F8: return Key::F8; - case XKB_KEY_F9: return Key::F9; - case XKB_KEY_F10: return Key::F10; - case XKB_KEY_F11: return Key::F11; - case XKB_KEY_F12: return Key::F12; - case XKB_KEY_F13: return Key::F13; - case XKB_KEY_F14: return Key::F14; - case XKB_KEY_F15: return Key::F15; - case XKB_KEY_F16: return Key::F16; - case XKB_KEY_F17: return Key::F17; - case XKB_KEY_F18: return Key::F18; - case XKB_KEY_F19: return Key::F19; - case XKB_KEY_F20: return Key::F20; - case XKB_KEY_F21: return Key::F21; - case XKB_KEY_F22: return Key::F22; - case XKB_KEY_F23: return Key::F23; - case XKB_KEY_F24: return Key::F24; - case XKB_KEY_Print: return Key::PRINTSCREEN; - case XKB_KEY_Insert: return Key::INSERT; - case XKB_KEY_Help: return Key::HELP; - case XKB_KEY_grave: return Key::BACK_QUOTE; - case XKB_KEY_quoteright: return Key::QUOTE; - case XKB_KEY_Menu: return Key::MENU; + case KEY_ENTER: return Key::ENTER; + case KEY_BACKSPACE: return Key::BACKSPACE; + case KEY_TAB: return Key::TAB; + case KEY_CANCEL: return Key::CANCEL; + case KEY_CLEAR: return Key::CLEAR; + case KEY_PAUSE: return Key::PAUSE; + case KEY_ESC: return Key::ESCAPE; + case KEY_SPACE: return Key::SPACE; + case KEY_PAGEUP: return Key::PAGE_UP; + case KEY_PAGEDOWN: return Key::PAGE_DOWN; + case KEY_END: return Key::END; + case KEY_HOME: return Key::HOME; + case KEY_LEFT: return Key::LEFT; + case KEY_UP: return Key::UP; + case KEY_RIGHT: return Key::RIGHT; + case KEY_DOWN: return Key::DOWN; + case KEY_COMMA: return Key::COMMA; + case KEY_MINUS: return Key::MINUS; + case KEY_DOT: return Key::PERIOD; + case KEY_SLASH: return Key::SLASH; + case KEY_0: return Key::DIGIT0; + case KEY_1: return Key::DIGIT1; + case KEY_2: return Key::DIGIT2; + case KEY_3: return Key::DIGIT3; + case KEY_4: return Key::DIGIT4; + case KEY_5: return Key::DIGIT5; + case KEY_6: return Key::DIGIT6; + case KEY_7: return Key::DIGIT7; + case KEY_8: return Key::DIGIT8; + case KEY_9: return Key::DIGIT9; + case KEY_SEMICOLON: return Key::SEMICOLON; + case KEY_EQUAL: return Key::EQUALS; + case KEY_A: return Key::A; + case KEY_B: return Key::B; + case KEY_C: return Key::C; + case KEY_D: return Key::D; + case KEY_E: return Key::E; + case KEY_F: return Key::F; + case KEY_G: return Key::G; + case KEY_H: return Key::H; + case KEY_I: return Key::I; + case KEY_J: return Key::J; + case KEY_K: return Key::K; + case KEY_L: return Key::L; + case KEY_M: return Key::M; + case KEY_N: return Key::N; + case KEY_O: return Key::O; + case KEY_P: return Key::P; + case KEY_Q: return Key::Q; + case KEY_R: return Key::R; + case KEY_S: return Key::S; + case KEY_T: return Key::T; + case KEY_U: return Key::U; + case KEY_V: return Key::V; + case KEY_W: return Key::W; + case KEY_X: return Key::X; + case KEY_Y: return Key::Y; + case KEY_Z: return Key::Z; + case KEY_LEFTBRACE: return Key::OPEN_BRACKET; + case KEY_BACKSLASH: return Key::BACK_SLASH; + case KEY_RIGHTBRACE: return Key::CLOSE_BRACKET; + case KEY_KP0: return Key::DIGIT0; + case KEY_KP1: return Key::DIGIT1; + case KEY_KP2: return Key::DIGIT2; + case KEY_KP3: return Key::DIGIT3; + case KEY_KP4: return Key::DIGIT4; + case KEY_KP5: return Key::DIGIT5; + case KEY_KP6: return Key::DIGIT6; + case KEY_KP7: return Key::DIGIT7; + case KEY_KP8: return Key::DIGIT8; + case KEY_KP9: return Key::DIGIT9; + case KEY_KPASTERISK: return Key::MULTIPLY; + case KEY_KPPLUS: return Key::ADD; + case KEY_KPCOMMA: return Key::SEPARATOR; + case KEY_KPMINUS: return Key::MINUS; + case KEY_KPDOT: return Key::PERIOD; + case KEY_KPSLASH: return Key::SLASH; + // no kp delete? + // case KEY_: return Key::DEL; + case KEY_DELETE: return Key::DEL; + case KEY_NUMLOCK: return Key::NUM_LOCK; + case KEY_SCROLLLOCK: return Key::SCROLL_LOCK; + case KEY_F1: return Key::F1; + case KEY_F2: return Key::F2; + case KEY_F3: return Key::F3; + case KEY_F4: return Key::F4; + case KEY_F5: return Key::F5; + case KEY_F6: return Key::F6; + case KEY_F7: return Key::F7; + case KEY_F8: return Key::F8; + case KEY_F9: return Key::F9; + case KEY_F10: return Key::F10; + case KEY_F11: return Key::F11; + case KEY_F12: return Key::F12; + case KEY_F13: return Key::F13; + case KEY_F14: return Key::F14; + case KEY_F15: return Key::F15; + case KEY_F16: return Key::F16; + case KEY_F17: return Key::F17; + case KEY_F18: return Key::F18; + case KEY_F19: return Key::F19; + case KEY_F20: return Key::F20; + case KEY_F21: return Key::F21; + case KEY_F22: return Key::F22; + case KEY_F23: return Key::F23; + case KEY_F24: return Key::F24; + case KEY_PRINT: return Key::PRINTSCREEN; + case KEY_INSERT: return Key::INSERT; + case KEY_HELP: return Key::HELP; + case KEY_GRAVE: return Key::BACK_QUOTE; + case KEY_APOSTROPHE: return Key::QUOTE; + case KEY_MENU: return Key::MENU; // Key::KANA - // Key::VOLUME_UP - // Key::VOLUME_DOWN - // Key::MUTE + case KEY_VOLUMEUP: return Key::VOLUME_UP; + case KEY_VOLUMEDOWN: return Key::VOLUME_DOWN; + case KEY_MUTE: return Key::MUTE; default: return Key::UNDEFINED; } } diff --git a/wayland/cc/Keyboard.cc b/wayland/cc/Keyboard.cc index 6fffc3e6..5f7f83a3 100644 --- a/wayland/cc/Keyboard.cc +++ b/wayland/cc/Keyboard.cc @@ -77,37 +77,35 @@ static void kbKey(void* data, wl_keyboard* kb, uint32_t serial, uint32_t time, if (!self->_state || !self->_focus) return; const xkb_keysym_t *syms; - uint32_t keyCode = key + 8; if (xkb_state_key_get_syms(self->_state, keyCode, &syms) != 1) { xkb_compose_state_feed(self->_composeState, XKB_KEY_NoSymbol); return; } auto sym = syms[0]; - auto lowerSym = xkb_keysym_to_lower(sym); if (state == WL_KEYBOARD_KEY_STATE_PRESSED) xkb_compose_state_feed(self->_composeState, sym); auto status = xkb_compose_state_get_status(self->_composeState); bool composeRelated = status != XKB_COMPOSE_NOTHING; - jwm::Key jwmKey = KeyWayland::fromNative(lowerSym); + // use raw key code + jwm::Key jwmKey = KeyWayland::fromNative(keyCode); self->_repeatingText = false; self->_repeating = false; - if (!composeRelated) { - if (jwmKey != jwm::Key::UNDEFINED) { - jwm::KeyLocation location = jwm::KeyLocation::DEFAULT; - JNILocal keyEvent( - jwm::app.getJniEnv(), - classes::EventKey::make( - jwm::app.getJniEnv(), - jwmKey, - state == WL_KEYBOARD_KEY_STATE_PRESSED, - KeyWayland::getModifiers(self->_state), - location - ) - ); - self->_focus->dispatch(keyEvent.get()); - } - } else { + if (jwmKey != jwm::Key::UNDEFINED) { + jwm::KeyLocation location = jwm::KeyLocation::DEFAULT; + JNILocal keyEvent( + jwm::app.getJniEnv(), + classes::EventKey::make( + jwm::app.getJniEnv(), + jwmKey, + state == WL_KEYBOARD_KEY_STATE_PRESSED, + KeyWayland::getModifiers(self->_state), + location + ) + ); + self->_focus->dispatch(keyEvent.get()); + } + if (composeRelated) { int dacount; switch (status) { case XKB_COMPOSE_COMPOSING: diff --git a/wayland/cc/LayerGLWayland.cc b/wayland/cc/LayerGLWayland.cc index db458347..89328c21 100644 --- a/wayland/cc/LayerGLWayland.cc +++ b/wayland/cc/LayerGLWayland.cc @@ -99,6 +99,11 @@ namespace jwm { // God is dead if _eglWindow is null if (_eglWindow) wl_egl_window_resize(_eglWindow, width, height, 0, 0); + + if (fWindow->_scale != fWindow->_oldScale) { + swapBuffers(); + wl_surface_set_buffer_scale(fWindow->_waylandWindow, fWindow->_scale); + } } void swapBuffers() override { diff --git a/wayland/cc/WindowWayland.cc b/wayland/cc/WindowWayland.cc index 5e451a70..a067e383 100644 --- a/wayland/cc/WindowWayland.cc +++ b/wayland/cc/WindowWayland.cc @@ -284,7 +284,6 @@ void jwm::WindowWayland::surfaceEnter(void* data, wl_surface* surface, wl_output if (o->_output == output) { self->_output = o; self->_scale = o->scale; - wl_surface_set_buffer_scale(surface, o->scale); self->_adaptSize(self->getUnscaledWidth(), self->getUnscaledHeight()); break; } @@ -299,7 +298,6 @@ void jwm::WindowWayland::surfacePreferredBufferScale(void* data, wl_surface* sur self->_scale = factor; // do I pinky promise here? // yes : ) - wl_surface_set_buffer_scale(surface, factor); self->_adaptSize(self->getUnscaledWidth(), self->getUnscaledHeight()); } void jwm::WindowWayland::surfacePreferredBufferTransform(void* data, wl_surface* surface, uint32_t transform) {} @@ -378,12 +376,8 @@ void jwm::WindowWayland::decorFrameConfigure(libdecor_frame* frame, libdecor_con } - self->_newWidth = width; - self->_newHeight = height; + self->_adaptSize(width, height); // This flat out breaks window if it isn't throttled - wl_callback* callback = wl_surface_frame(self->_waylandWindow); - // Throttle frame - wl_callback_add_listener(callback, &_frameCallback, self); } if (!self->_configured && self->_visible) { if (self->_layer) @@ -420,12 +414,14 @@ void jwm::WindowWayland::_adaptSize(int newWidth, int newHeight) { ) ); dispatch(eventWindowResize.get()); - if (_scale != _oldScale) - _makeCursors(); - _oldScale = _scale; + if (_scale != _oldScale) { + _makeCursors(); + _windowManager._processCallbacks(); + } // In Java Wayland doesn't actually cause a frame: // however decorFrameCommit will cause a redraw anyway. // Not doing it in wayland lets me not cause an exception on hide. + _oldScale = _scale; } // JNI From 22fd80a8ffedda5bbf5fc5f05d28232888a4f2f7 Mon Sep 17 00:00:00 2001 From: BulbyVR <26726264+TheDrawingCoder-Gamer@users.noreply.github.com> Date: Sun, 17 Dec 2023 11:39:27 -0500 Subject: [PATCH 40/95] Bump Skija version --- script/common.py | 2 +- script/run.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/script/common.py b/script/common.py index 36a5f31a..5e8bd9fd 100644 --- a/script/common.py +++ b/script/common.py @@ -8,7 +8,7 @@ def deps_compile(): parser = argparse.ArgumentParser() parser.add_argument('--skija-dir', default=None) parser.add_argument('--skija-shared-jar', default=None) - parser.add_argument('--skija-version', default='0.116.1') + parser.add_argument('--skija-version', default='0.116.2') (args, _) = parser.parse_known_args() deps = [ diff --git a/script/run.py b/script/run.py index 4ba67860..64c201c2 100755 --- a/script/run.py +++ b/script/run.py @@ -5,7 +5,7 @@ def main(): parser = argparse.ArgumentParser() parser.add_argument('--example', default='dashboard') parser.add_argument('--jwm-version', default=None) - parser.add_argument('--skija-version', default='0.116.1') + parser.add_argument('--skija-version', default='0.116.2') parser.add_argument('--skija-dir', default=None) parser.add_argument('--skija-shared-jar', default=None) parser.add_argument('--skija-platform-jar', default=None) From 20a7a871bceed8d6333fc55090a82035ffb1fc78 Mon Sep 17 00:00:00 2001 From: BulbyVR <26726264+TheDrawingCoder-Gamer@users.noreply.github.com> Date: Sun, 17 Dec 2023 14:06:31 -0500 Subject: [PATCH 41/95] Repeat now not laggy (on GL) --- wayland/cc/Keyboard.cc | 58 +++++++++++++----- wayland/cc/Keyboard.hh | 6 ++ wayland/cc/WindowManagerWayland.cc | 94 +++++++++++++++++------------- wayland/cc/WindowManagerWayland.hh | 1 + 4 files changed, 104 insertions(+), 55 deletions(-) diff --git a/wayland/cc/Keyboard.cc b/wayland/cc/Keyboard.cc index 5f7f83a3..cf7708b2 100644 --- a/wayland/cc/Keyboard.cc +++ b/wayland/cc/Keyboard.cc @@ -13,6 +13,9 @@ #include "AppWayland.hh" #include #include +#include +#include + using namespace jwm; // I've noticed that pointers to lambdas are null for some reason. @@ -63,11 +66,24 @@ static void kbEnter(void* data, wl_keyboard* kb, uint32_t serial, wl_surface* su self->_serial = serial; self->_focus = win; if (self->_state) { - // TODO: keys + uint32_t* key; + // C++ jank + // Normal macro fails to compile bc `void*` can't implicitly convert into `uint32_t*` + for (key = (uint32_t*)keys->data; + (const char*) key < (const char*)keys->data + keys->size; + key++ + ) { + auto jwmKey = jwm::KeyWayland::fromNative(*key + 8); + self->submitKey(jwmKey, WL_KEYBOARD_KEY_STATE_PRESSED); + } } } static void kbLeave(void* data, wl_keyboard* kb, uint32_t serial, wl_surface* surface) { auto self = reinterpret_cast(data); + std::list liftedKeys(self->_depressedKeys); + for (auto key : liftedKeys) { + self->submitKey(key, WL_KEYBOARD_KEY_STATE_RELEASED); + } self->_serial = -1; self->_focus = nullptr; } @@ -91,20 +107,7 @@ static void kbKey(void* data, wl_keyboard* kb, uint32_t serial, uint32_t time, jwm::Key jwmKey = KeyWayland::fromNative(keyCode); self->_repeatingText = false; self->_repeating = false; - if (jwmKey != jwm::Key::UNDEFINED) { - jwm::KeyLocation location = jwm::KeyLocation::DEFAULT; - JNILocal keyEvent( - jwm::app.getJniEnv(), - classes::EventKey::make( - jwm::app.getJniEnv(), - jwmKey, - state == WL_KEYBOARD_KEY_STATE_PRESSED, - KeyWayland::getModifiers(self->_state), - location - ) - ); - self->_focus->dispatch(keyEvent.get()); - } + self->submitKey(jwmKey, state); if (composeRelated) { int dacount; switch (status) { @@ -215,3 +218,28 @@ Keyboard::~Keyboard() if (_keymap) xkb_keymap_unref(_keymap); } + +void Keyboard::submitKey(jwm::Key key, uint32_t state) { + if (key != jwm::Key::UNDEFINED) { + jwm::KeyLocation location = jwm::KeyLocation::DEFAULT; + JNILocal keyEvent( + jwm::app.getJniEnv(), + classes::EventKey::make( + jwm::app.getJniEnv(), + key, + state == WL_KEYBOARD_KEY_STATE_PRESSED, + KeyWayland::getModifiers(_state), + location + ) + ); + _focus->dispatch(keyEvent.get()); + if (state == WL_KEYBOARD_KEY_STATE_PRESSED) { + _depressedKeys.push_back(key); + } else { + auto it = std::find(_depressedKeys.begin(), _depressedKeys.end(), key); + if (it != _depressedKeys.end()) { + _depressedKeys.erase(it); + } + } + } +} diff --git a/wayland/cc/Keyboard.hh b/wayland/cc/Keyboard.hh index 464259aa..c4ad891b 100644 --- a/wayland/cc/Keyboard.hh +++ b/wayland/cc/Keyboard.hh @@ -6,6 +6,7 @@ #include #include "KeyWayland.hh" #include "StringUTF16.hh" +#include namespace jwm { class WindowManagerWayland; @@ -41,11 +42,16 @@ namespace jwm { int32_t _repeatRate = 100; int32_t _repeatDelay = 300; + void submitKey(jwm::Key key, uint32_t state); + + jwm::StringUTF16 _repeatText; jwm::Key _repeatKey = jwm::Key::UNDEFINED; bool _repeating = false; bool _repeatingText = false; + std::list _depressedKeys; + jwm::WindowManagerWayland& _wm; static wl_keyboard_listener _keyboardListener; diff --git a/wayland/cc/WindowManagerWayland.cc b/wayland/cc/WindowManagerWayland.cc index 145881db..cf2972b2 100644 --- a/wayland/cc/WindowManagerWayland.cc +++ b/wayland/cc/WindowManagerWayland.cc @@ -132,7 +132,7 @@ void WindowManagerWayland::runLoop() { _runLoop = false; // block until event : ) int timeout = -1; - if (_keyboard && _keyboard->_repeating) { + if (_keyboard && _keyboard->_repeating && _keyboard->getFocus()) { if (_keyboard->_repeatRate > 0) { auto now = std::chrono::steady_clock::now(); auto target = _keyboard->_nextRepeat; @@ -145,13 +145,20 @@ void WindowManagerWayland::runLoop() { printf("error with pipe\n"); break; } + if (ps[0].revents & POLLIN) { libdecor_dispatch(decorCtx, -1); } - _processCallbacks(); + if (ps[1].revents & POLLIN) { while (read(pipes[0], buf, sizeof(buf)) == sizeof(buf)) { } } + if (ps[0].revents & POLLIN || ps[1].revents & POLLIN) { + _processCallbacks(); + } else { + // don't test if we already calculated earlier + _processKeyboard(); + } notifyBool.store(false); } @@ -180,44 +187,6 @@ void WindowManagerWayland::_processCallbacks() { lock.lock(); } } - if (_keyboard && _keyboard->_repeating && _keyboard->getFocus()) { - auto now = std::chrono::steady_clock::now(); - if (now > _keyboard->_nextRepeat) { - _keyboard->_nextRepeat = now + std::chrono::milliseconds(_keyboard->_repeatRate); - auto focus = _keyboard->getFocus(); - auto env = jwm::app.getJniEnv(); - jwm::KeyLocation location = jwm::KeyLocation::DEFAULT; - JNILocal keyOffEvent( - env, - classes::EventKey::make( - env, - _keyboard->_repeatKey, - false, - KeyWayland::getModifiers(_keyboard->_state), - location - ) - ); - - JNILocal keyEvent( - env, - classes::EventKey::make( - env, - _keyboard->_repeatKey, - true, - KeyWayland::getModifiers(_keyboard->_state), - location - ) - ); - focus->dispatch(keyOffEvent.get()); - focus->dispatch(keyEvent.get()); - if (_keyboard->_repeatingText) { - jwm::JNILocal jtext = _keyboard->_repeatText.toJString(env); - jwm::JNILocal eventTextInput(env, classes::EventTextInput::make(env, jtext.get())); - - focus->dispatch(eventTextInput.get()); - } - } - } { // copy window list in case one closes any other, invalidating some iterator in _nativeWindowToMy std::vector copy; @@ -237,6 +206,51 @@ void WindowManagerWayland::_processCallbacks() { } } } + if (_keyboard && _keyboard->_repeating) { + auto now = std::chrono::steady_clock::now(); + if (now > _keyboard->_nextRepeat) + _processKeyboard(); + } + +} +void WindowManagerWayland::_processKeyboard() { + if (_keyboard && _keyboard->_repeating && _keyboard->getFocus()) { + auto now = std::chrono::steady_clock::now(); + _keyboard->_nextRepeat = now + std::chrono::milliseconds(_keyboard->_repeatRate); + auto focus = _keyboard->getFocus(); + auto env = jwm::app.getJniEnv(); + jwm::KeyLocation location = jwm::KeyLocation::DEFAULT; + JNILocal keyOffEvent( + env, + classes::EventKey::make( + env, + _keyboard->_repeatKey, + false, + KeyWayland::getModifiers(_keyboard->_state), + location + ) + ); + + JNILocal keyEvent( + env, + classes::EventKey::make( + env, + _keyboard->_repeatKey, + true, + KeyWayland::getModifiers(_keyboard->_state), + location + ) + ); + focus->dispatch(keyOffEvent.get()); + focus->dispatch(keyEvent.get()); + if (_keyboard->_repeatingText) { + jwm::JNILocal jtext = _keyboard->_repeatText.toJString(env); + jwm::JNILocal eventTextInput(env, classes::EventTextInput::make(env, jtext.get())); + + focus->dispatch(eventTextInput.get()); + } + } + } void WindowManagerWayland::registryHandleGlobal(void* data, wl_registry *registry, diff --git a/wayland/cc/WindowManagerWayland.hh b/wayland/cc/WindowManagerWayland.hh index e6547f85..7138f8c2 100644 --- a/wayland/cc/WindowManagerWayland.hh +++ b/wayland/cc/WindowManagerWayland.hh @@ -54,6 +54,7 @@ namespace jwm { } */ void _processCallbacks(); + void _processKeyboard(); void notifyLoop(); void enqueueTask(const std::function& task); From 5a5d611b27655cc6693e60b7517528e22e23a933 Mon Sep 17 00:00:00 2001 From: BulbyVR <26726264+TheDrawingCoder-Gamer@users.noreply.github.com> Date: Sun, 17 Dec 2023 14:49:08 -0500 Subject: [PATCH 42/95] nudge keyboard repeat --- wayland/cc/Keyboard.cc | 2 +- wayland/cc/WindowManagerWayland.cc | 10 +++------- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/wayland/cc/Keyboard.cc b/wayland/cc/Keyboard.cc index cf7708b2..b2dc1a74 100644 --- a/wayland/cc/Keyboard.cc +++ b/wayland/cc/Keyboard.cc @@ -153,8 +153,8 @@ static void kbKey(void* data, wl_keyboard* kb, uint32_t serial, uint32_t time, if (shouldRepeat && (jwmKey != jwm::Key::UNDEFINED)) { self->_repeating = true; self->_nextRepeat = self->_lastPress + std::chrono::milliseconds(self->_repeatDelay); + self->_wm.notifyLoop(); } - self->_wm.notifyLoop(); char textBuffer[0x40]; int count = xkb_state_key_get_utf8(self->_state, keyCode, textBuffer, sizeof(textBuffer)-1); // ??? diff --git a/wayland/cc/WindowManagerWayland.cc b/wayland/cc/WindowManagerWayland.cc index cf2972b2..fe8ccfff 100644 --- a/wayland/cc/WindowManagerWayland.cc +++ b/wayland/cc/WindowManagerWayland.cc @@ -138,6 +138,8 @@ void WindowManagerWayland::runLoop() { auto target = _keyboard->_nextRepeat; if (now < target) { timeout = std::chrono::duration_cast(target - now).count(); + } else { + _processKeyboard(); } } } @@ -206,11 +208,6 @@ void WindowManagerWayland::_processCallbacks() { } } } - if (_keyboard && _keyboard->_repeating) { - auto now = std::chrono::steady_clock::now(); - if (now > _keyboard->_nextRepeat) - _processKeyboard(); - } } void WindowManagerWayland::_processKeyboard() { @@ -305,8 +302,7 @@ void WindowManagerWayland::pointerHandleEnter(void* data, wl_pointer* pointer, u wl_surface *surface, wl_fixed_t surface_x, wl_fixed_t surface_y) { WindowManagerWayland* self = (WindowManagerWayland*)data; self->mouseSerial = serial; - if (self->getWindowForNative(surface)) { - WindowWayland* window = self->getWindowForNative(surface); + if (auto window = self->getWindowForNative(surface)) { window->setCursor(jwm::MouseCursor::ARROW); self->focusedSurface = surface; } From 9eabca1afcc4c2b779ff0bb50364e677db259a44 Mon Sep 17 00:00:00 2001 From: BulbyVR <26726264+TheDrawingCoder-Gamer@users.noreply.github.com> Date: Sun, 17 Dec 2023 15:35:17 -0500 Subject: [PATCH 43/95] don't commit on detach --- wayland/cc/LayerGLWayland.cc | 1 + wayland/cc/LayerRasterWayland.cc | 3 ++- wayland/cc/WindowWayland.cc | 1 - 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/wayland/cc/LayerGLWayland.cc b/wayland/cc/LayerGLWayland.cc index 89328c21..71a1bc57 100644 --- a/wayland/cc/LayerGLWayland.cc +++ b/wayland/cc/LayerGLWayland.cc @@ -33,6 +33,7 @@ namespace jwm { _closed = false; fWindow = jwm::ref(window); fWindow->setLayer(this); + // Force a reconfigure; needed to draw title bar correctly if (fWindow->_windowManager._eglDisplay == EGL_NO_DISPLAY) { fWindow->_windowManager._eglDisplay = eglGetDisplay(window->_windowManager.display); diff --git a/wayland/cc/LayerRasterWayland.cc b/wayland/cc/LayerRasterWayland.cc index 9993b387..cd2aea11 100644 --- a/wayland/cc/LayerRasterWayland.cc +++ b/wayland/cc/LayerRasterWayland.cc @@ -83,7 +83,8 @@ namespace jwm { void detach() override { if (_attached && fWindow && fWindow->_waylandWindow) { wl_surface_attach(fWindow->_waylandWindow, nullptr, 0, 0); - wl_surface_commit(fWindow->_waylandWindow); + // commit is not meant to be used in intermediate states + // wl_surface_commit(fWindow->_waylandWindow); } _attached = false; } diff --git a/wayland/cc/WindowWayland.cc b/wayland/cc/WindowWayland.cc index a067e383..acf80383 100644 --- a/wayland/cc/WindowWayland.cc +++ b/wayland/cc/WindowWayland.cc @@ -377,7 +377,6 @@ void jwm::WindowWayland::decorFrameConfigure(libdecor_frame* frame, libdecor_con self->_adaptSize(width, height); - // This flat out breaks window if it isn't throttled } if (!self->_configured && self->_visible) { if (self->_layer) From acc730d0a4a5426d2b4c8d162c2b77794b35a9c1 Mon Sep 17 00:00:00 2001 From: BulbyVR <26726264+TheDrawingCoder-Gamer@users.noreply.github.com> Date: Sun, 17 Dec 2023 17:19:58 -0500 Subject: [PATCH 44/95] fix clipboard? --- examples/dashboard/java/PanelTextInput.java | 2 +- shared/java/ClipboardEntry.java | 6 +-- wayland/cc/ClipboardWayland.cc | 6 +-- wayland/cc/WindowManagerWayland.cc | 50 +++++++++++---------- wayland/cc/WindowManagerWayland.hh | 1 + 5 files changed, 33 insertions(+), 32 deletions(-) diff --git a/examples/dashboard/java/PanelTextInput.java b/examples/dashboard/java/PanelTextInput.java index 9d55a522..04eff63b 100644 --- a/examples/dashboard/java/PanelTextInput.java +++ b/examples/dashboard/java/PanelTextInput.java @@ -232,4 +232,4 @@ public String getSubstring(int start, int end) { int end2 = Math.min(end, start2); return text.substring(start2, end2); } -} \ No newline at end of file +} diff --git a/shared/java/ClipboardEntry.java b/shared/java/ClipboardEntry.java index 6b1b0ddb..97b7f2ed 100644 --- a/shared/java/ClipboardEntry.java +++ b/shared/java/ClipboardEntry.java @@ -62,7 +62,7 @@ public static ClipboardEntry makeRTF(String text) { */ @NotNull @SneakyThrows public static ClipboardEntry makeString(ClipboardFormat format, String text) { - if (Platform.CURRENT == Platform.X11 || Platform.CURRENT == Platform.MACOS) { + if (Platform.CURRENT == Platform.X11 || Platform.CURRENT == Platform.MACOS || Platform.CURRENT == Platform.WAYLAND) { return make(format, text.getBytes("UTF-8")); } return make(format, text.getBytes("UTF-16LE")); @@ -76,9 +76,9 @@ public static ClipboardEntry makeString(ClipboardFormat format, String text) { */ @NotNull @SneakyThrows public String getString() { - if (Platform.CURRENT == Platform.X11 || Platform.CURRENT == Platform.MACOS) { + if (Platform.CURRENT == Platform.X11 || Platform.CURRENT == Platform.MACOS || Platform.CURRENT == Platform.WAYLAND) { return new String(_data, "UTF-8"); } return new String(_data, "UTF-16LE"); } -} \ No newline at end of file +} diff --git a/wayland/cc/ClipboardWayland.cc b/wayland/cc/ClipboardWayland.cc index 92ca029f..9b325eea 100644 --- a/wayland/cc/ClipboardWayland.cc +++ b/wayland/cc/ClipboardWayland.cc @@ -44,11 +44,11 @@ namespace jwm { ByteBuf contents; // HACK: prefer UTF8_STRING over text/plain and convert it to utf16 - /* if (formatId == "text/plain") { contents = app.getWindowManager().getClipboardContents("UTF8_STRING"); + } else { + contents = app.getWindowManager().getClipboardContents(formatId.toAscii()); } - */ // TODO add another formats if (contents.empty()) { return nullptr; @@ -70,7 +70,6 @@ namespace jwm { void set(JNIEnv* env, jobjectArray entries) { - /* jsize size = env->GetArrayLength(entries); std::map contents; for (jsize i = 0; i < size; ++i) { @@ -94,7 +93,6 @@ namespace jwm { } } jwm::app.getWindowManager().setClipboardContents(std::move(contents)); - */ // impl me : ) } }; } diff --git a/wayland/cc/WindowManagerWayland.cc b/wayland/cc/WindowManagerWayland.cc index fe8ccfff..ba3c9ac1 100644 --- a/wayland/cc/WindowManagerWayland.cc +++ b/wayland/cc/WindowManagerWayland.cc @@ -460,30 +460,8 @@ static void deviceDataOffer(void* data, wl_data_device* device, wl_data_offer* o static void deviceSelection(void* data, wl_data_device* device, wl_data_offer* offer) { auto self = reinterpret_cast(data); self->_myClipboardContents = {}; - if (offer == nullptr) { - return; - } - for (auto i : self->_currentMimeTypes) { - int fds[2]; - pipe(fds); - wl_data_offer_receive(offer, i.c_str(), fds[1]); - wl_display_flush(self->display); - close(fds[1]); - ByteBuf res; - - - while (true) { - char buf[1024]; - ssize_t n = read(fds[0], buf, sizeof(buf)); - if (n <= 0) - break; - res.insert(res.end(), buf, buf + n); - - } - self->_myClipboardContents[i] = res; - close(fds[0]); - } - wl_data_offer_destroy(offer); + // if null then w/e + self->currentOffer = offer; } wl_data_device_listener WindowManagerWayland::_deviceListener = { .data_offer = deviceDataOffer, @@ -521,6 +499,30 @@ jwm::ByteBuf WindowManagerWayland::getClipboardContents(const std::string& type) auto it = _myClipboardContents.find(type); if (it != _myClipboardContents.end()) { return it->second; + } else if (currentOffer) { + auto it2 = std::find(_currentMimeTypes.begin(), _currentMimeTypes.end(), type); + if (it2 != _currentMimeTypes.end()) { + auto mimeType = *it2; + // pull down offer + int fds[2]; + pipe(fds); + wl_data_offer_receive(currentOffer, mimeType.c_str(), fds[1]); + wl_display_flush(display); + close(fds[1]); + ByteBuf res; + + while (true) { + char buf[1024]; + ssize_t n = read(fds[0], buf, sizeof(buf)); + if (n <= 0) + break; + res.insert(res.end(), buf, buf + n); + } + // cache + _myClipboardContents[mimeType] = res; + close(fds[0]); + return res; + } } return {}; } diff --git a/wayland/cc/WindowManagerWayland.hh b/wayland/cc/WindowManagerWayland.hh index 7138f8c2..cfac150b 100644 --- a/wayland/cc/WindowManagerWayland.hh +++ b/wayland/cc/WindowManagerWayland.hh @@ -108,6 +108,7 @@ namespace jwm { Keyboard* _keyboard = nullptr; wl_data_device* dataDevice = nullptr; wl_data_source* currentSource = nullptr; + wl_data_offer* currentOffer = nullptr; uint32_t mouseSerial = 0; uint32_t getKeyboardSerial() const { if (_keyboard) From 7a6f95b248f89d8aae476f0627d7a622b0928377 Mon Sep 17 00:00:00 2001 From: BulbyVR <26726264+TheDrawingCoder-Gamer@users.noreply.github.com> Date: Sun, 17 Dec 2023 20:20:22 -0500 Subject: [PATCH 45/95] clipboard sauce --- wayland/cc/LayerGLWayland.cc | 2 +- wayland/cc/WindowManagerWayland.cc | 27 ++++++++++++++++----------- wayland/cc/WindowManagerWayland.hh | 1 + 3 files changed, 18 insertions(+), 12 deletions(-) diff --git a/wayland/cc/LayerGLWayland.cc b/wayland/cc/LayerGLWayland.cc index 71a1bc57..7e577e79 100644 --- a/wayland/cc/LayerGLWayland.cc +++ b/wayland/cc/LayerGLWayland.cc @@ -76,7 +76,6 @@ namespace jwm { } // Don't block on swap // Blocking here will freeze the app on sway. - eglSwapInterval(_display, 0); } if (fWindow->_configured) attachBuffer(); @@ -129,6 +128,7 @@ namespace jwm { _surface, _surface, _context); + eglSwapInterval(_display, 0); } void attachBuffer() override { if (fWindow && fWindow->_waylandWindow) { diff --git a/wayland/cc/WindowManagerWayland.cc b/wayland/cc/WindowManagerWayland.cc index ba3c9ac1..789eeca2 100644 --- a/wayland/cc/WindowManagerWayland.cc +++ b/wayland/cc/WindowManagerWayland.cc @@ -126,10 +126,12 @@ void WindowManagerWayland::runLoop() { {.fd=libdecor_get_fd(decorCtx), .events=POLLIN}, {.fd=pipes[0], .events=POLLIN}, }; + // who be out here running they loop while (_runLoop) { if (jwm::classes::Throwable::exceptionThrown(app.getJniEnv())) _runLoop = false; + // block until event : ) int timeout = -1; if (_keyboard && _keyboard->_repeating && _keyboard->getFocus()) { @@ -147,9 +149,11 @@ void WindowManagerWayland::runLoop() { printf("error with pipe\n"); break; } - if (ps[0].revents & POLLIN) { - libdecor_dispatch(decorCtx, -1); + if (libdecor_dispatch(decorCtx, -1) < 0) { + fprintf(stderr, "error with dispatch\n"); + break; + } } if (ps[1].revents & POLLIN) { @@ -180,14 +184,13 @@ void WindowManagerWayland::_processCallbacks() { { // process ui thread callbacks std::unique_lock lock(_taskQueueLock); - while (!_taskQueue.empty()) { auto callback = std::move(_taskQueue.front()); _taskQueue.pop(); lock.unlock(); callback(); lock.lock(); - } + } } { // copy window list in case one closes any other, invalidating some iterator in _nativeWindowToMy @@ -459,7 +462,6 @@ static void deviceDataOffer(void* data, wl_data_device* device, wl_data_offer* o } static void deviceSelection(void* data, wl_data_device* device, wl_data_offer* offer) { auto self = reinterpret_cast(data); - self->_myClipboardContents = {}; // if null then w/e self->currentOffer = offer; } @@ -544,8 +546,9 @@ void WindowManagerWayland::terminate() { static void dataSourceSend(void* data, wl_data_source* source, const char* mimeType, int fd) { auto self = reinterpret_cast(data); - auto it = self->_myClipboardContents.find(std::string(mimeType)); - if (it != self->_myClipboardContents.end()) { + auto it = self->_myClipboardSource.find(std::string(mimeType)); + + if (it != self->_myClipboardSource.end()) { write(fd, it->second.data(), it->second.size()); } close(fd); @@ -553,6 +556,8 @@ static void dataSourceSend(void* data, wl_data_source* source, const char* mimeT static void dataSourceCancelled(void* data, wl_data_source* source) { auto self = reinterpret_cast(data); wl_data_source_destroy(source); + self->currentSource = nullptr; + self->_myClipboardSource = {}; } wl_data_source_listener WindowManagerWayland::_sourceListener = { .send = dataSourceSend, @@ -560,10 +565,10 @@ wl_data_source_listener WindowManagerWayland::_sourceListener = { }; void WindowManagerWayland::setClipboardContents(std::map&& c) { assert(("create at least one window in order to use clipboard" && !_nativeWindowToMy.empty())); - _myClipboardContents = c; + _myClipboardSource = c; + + if (!deviceManager) return; - // god is dead if data device manager is null - currentSource = wl_data_device_manager_create_data_source(deviceManager); wl_data_source_add_listener(currentSource, &_sourceListener, this); @@ -573,7 +578,7 @@ void WindowManagerWayland::setClipboardContents(std::map&& _currentMimeTypes.push_back(it.first.c_str()); wl_data_source_offer(currentSource, it.first.c_str()); } - + if (getKeyboardSerial() > 0) wl_data_device_set_selection(dataDevice, currentSource, getKeyboardSerial()); diff --git a/wayland/cc/WindowManagerWayland.hh b/wayland/cc/WindowManagerWayland.hh index cfac150b..0f8e9e41 100644 --- a/wayland/cc/WindowManagerWayland.hh +++ b/wayland/cc/WindowManagerWayland.hh @@ -137,6 +137,7 @@ namespace jwm { std::map _nativeWindowToMy; std::map _myClipboardContents; + std::map _myClipboardSource; std::list _currentMimeTypes; From 291adf3c88e00e0434f2b8217c6a77c28c9d7a04 Mon Sep 17 00:00:00 2001 From: BulbyVR <26726264+TheDrawingCoder-Gamer@users.noreply.github.com> Date: Mon, 18 Dec 2023 11:26:11 -0500 Subject: [PATCH 46/95] Self clipboard pasting --- wayland/cc/ClipboardWayland.cc | 11 ++++------- wayland/cc/WindowManagerWayland.cc | 8 +++++++- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/wayland/cc/ClipboardWayland.cc b/wayland/cc/ClipboardWayland.cc index 9b325eea..25ff41a1 100644 --- a/wayland/cc/ClipboardWayland.cc +++ b/wayland/cc/ClipboardWayland.cc @@ -43,13 +43,10 @@ namespace jwm { ByteBuf contents; - // HACK: prefer UTF8_STRING over text/plain and convert it to utf16 - if (formatId == "text/plain") { - contents = app.getWindowManager().getClipboardContents("UTF8_STRING"); - } else { - contents = app.getWindowManager().getClipboardContents(formatId.toAscii()); - } - // TODO add another formats + // text will ALWAYS be utf8 if we are getting plain text. + // If you are outputting utf16 into something that other apps read from, + // you are going to hell. + contents = app.getWindowManager().getClipboardContents(formatId.toAscii()); if (contents.empty()) { return nullptr; } diff --git a/wayland/cc/WindowManagerWayland.cc b/wayland/cc/WindowManagerWayland.cc index 789eeca2..66c6f33e 100644 --- a/wayland/cc/WindowManagerWayland.cc +++ b/wayland/cc/WindowManagerWayland.cc @@ -501,6 +501,13 @@ jwm::ByteBuf WindowManagerWayland::getClipboardContents(const std::string& type) auto it = _myClipboardContents.find(type); if (it != _myClipboardContents.end()) { return it->second; + } else if (currentSource) { + // Self paste + auto it2 = _myClipboardSource.find(type); + if (it2 != _myClipboardSource.end()) { + _myClipboardContents[type] = it2->second; + return it2->second; + } } else if (currentOffer) { auto it2 = std::find(_currentMimeTypes.begin(), _currentMimeTypes.end(), type); if (it2 != _currentMimeTypes.end()) { @@ -547,7 +554,6 @@ void WindowManagerWayland::terminate() { static void dataSourceSend(void* data, wl_data_source* source, const char* mimeType, int fd) { auto self = reinterpret_cast(data); auto it = self->_myClipboardSource.find(std::string(mimeType)); - if (it != self->_myClipboardSource.end()) { write(fd, it->second.data(), it->second.size()); } From bc0bd2cc3163474ee10d13d6d82e7d010db62826 Mon Sep 17 00:00:00 2001 From: BulbyVR <26726264+TheDrawingCoder-Gamer@users.noreply.github.com> Date: Mon, 18 Dec 2023 11:34:47 -0500 Subject: [PATCH 47/95] change focus access to method --- wayland/cc/WindowManagerWayland.cc | 63 +++++++++++++----------------- wayland/cc/WindowManagerWayland.hh | 9 ++++- 2 files changed, 36 insertions(+), 36 deletions(-) diff --git a/wayland/cc/WindowManagerWayland.cc b/wayland/cc/WindowManagerWayland.cc index 66c6f33e..4922ff1f 100644 --- a/wayland/cc/WindowManagerWayland.cc +++ b/wayland/cc/WindowManagerWayland.cc @@ -33,28 +33,26 @@ wl_registry_listener WindowManagerWayland::_registryListener = { }; static void pointerFrame(void* data, wl_pointer* pointer) { auto self = reinterpret_cast(data); - if (!self->focusedSurface) return; + if (!self->getFocusedSurface()) return; if (self->_dX != 0.0f || self->_dY != 0.0f) { auto env = app.getJniEnv(); - auto win = self->getWindowForNative(self->focusedSurface); - if (win) { - jwm::JNILocal eventAxis( - env, - jwm::classes::EventMouseScroll::make( - env, - self->_dX * win->_scale, - self->_dY * win->_scale, - 0.0f, - 0.0f, - 0.0f, - self->lastMousePosX, - self->lastMousePosY, - jwm::KeyWayland::getModifiers(self->getXkbState()) - ) - ); - win->dispatch(eventAxis.get()); + auto win = self->getFocusedSurface(); + jwm::JNILocal eventAxis( + env, + jwm::classes::EventMouseScroll::make( + env, + self->_dX * win->_scale, + self->_dY * win->_scale, + 0.0f, + 0.0f, + 0.0f, + self->lastMousePosX, + self->lastMousePosY, + jwm::KeyWayland::getModifiers(self->getXkbState()) + ) + ); + win->dispatch(eventAxis.get()); - } self->_dX = 0.0f; self->_dY = 0.0f; } @@ -307,13 +305,13 @@ void WindowManagerWayland::pointerHandleEnter(void* data, wl_pointer* pointer, u self->mouseSerial = serial; if (auto window = self->getWindowForNative(surface)) { window->setCursor(jwm::MouseCursor::ARROW); - self->focusedSurface = surface; + self->setFocusedSurface(window); } } void WindowManagerWayland::pointerHandleLeave(void* data, wl_pointer* pointer, uint32_t serial, wl_surface *surface) { WindowManagerWayland* self = (WindowManagerWayland*)data; - self->focusedSurface = nullptr; + self->setFocusedSurface(nullptr); // ??? self->mouseMask = 0; self->mouseSerial = -1; @@ -321,8 +319,8 @@ void WindowManagerWayland::pointerHandleLeave(void* data, wl_pointer* pointer, u void WindowManagerWayland::pointerHandleMotion(void* data, wl_pointer* pointer, uint32_t time, wl_fixed_t surface_x, wl_fixed_t surface_y) { WindowManagerWayland* self = (WindowManagerWayland*)data; - if (self->focusedSurface) { - ::WindowWayland* window = self->getWindowForNative(self->focusedSurface); + if (self->getFocusedSurface()) { + ::WindowWayland* window = self->getFocusedSurface(); // God is dead if window is null if (window) self->mouseUpdate(window, @@ -335,7 +333,8 @@ void WindowManagerWayland::pointerHandleButton(void* data, wl_pointer* pointer, uint32_t serial, uint32_t time, uint32_t button, uint32_t state) { using namespace classes; WindowManagerWayland* self = (WindowManagerWayland*)data; - if (!self->focusedSurface) return; + if (!self->getFocusedSurface()) return; + auto window = self->getFocusedSurface(); if (state == 0) { // release switch (button) { @@ -355,7 +354,7 @@ void WindowManagerWayland::pointerHandleButton(void* data, wl_pointer* pointer, break; } - if (MouseButtonWayland::isButton(button) && self->focusedSurface) { + if (MouseButtonWayland::isButton(button)) { jwm::JNILocal eventButton( app.getJniEnv(), EventMouseButton::make( @@ -367,9 +366,7 @@ void WindowManagerWayland::pointerHandleButton(void* data, wl_pointer* pointer, jwm::KeyWayland::getModifiers(self->getXkbState()) ) ); - WindowWayland* window = self->getWindowForNative(self->focusedSurface); - if (window) - window->dispatch(eventButton.get()); + window->dispatch(eventButton.get()); } } else { // down @@ -390,7 +387,7 @@ void WindowManagerWayland::pointerHandleButton(void* data, wl_pointer* pointer, break; } - if (MouseButtonWayland::isButton(button) && self->focusedSurface) { + if (MouseButtonWayland::isButton(button)) { jwm::JNILocal eventButton( app.getJniEnv(), EventMouseButton::make( @@ -402,18 +399,14 @@ void WindowManagerWayland::pointerHandleButton(void* data, wl_pointer* pointer, jwm::KeyWayland::getModifiers(self->getXkbState()) ) ); - // me when this stuff is NULL : ( - WindowWayland* window = self->getWindowForNative(self->focusedSurface); - if (window) - window->dispatch(eventButton.get()); + window->dispatch(eventButton.get()); } } } void WindowManagerWayland::pointerHandleAxis(void* data, wl_pointer* pointer, uint32_t time, uint32_t axis, wl_fixed_t value) { auto self = reinterpret_cast(data); - if (!self->focusedSurface) return; - + if (!self->getFocusedSurface()) return; switch (axis) { case WL_POINTER_AXIS_VERTICAL_SCROLL: self->_dY += static_cast(wl_fixed_to_double(value)); diff --git a/wayland/cc/WindowManagerWayland.hh b/wayland/cc/WindowManagerWayland.hh index 0f8e9e41..a594ddd2 100644 --- a/wayland/cc/WindowManagerWayland.hh +++ b/wayland/cc/WindowManagerWayland.hh @@ -124,6 +124,7 @@ namespace jwm { EGLDisplay _eglDisplay = EGL_NO_DISPLAY; std::list outputs; + wl_cursor* currentCursor = nullptr; bool _runLoop; int notifyFD = -1; std::atomic_bool notifyBool{false}; @@ -142,7 +143,13 @@ namespace jwm { wl_surface* cursorSurface; - wl_surface* focusedSurface = nullptr; + WindowWayland* _focusedSurface = nullptr; + WindowWayland* getFocusedSurface() const { + return _focusedSurface; + } + void setFocusedSurface(WindowWayland* surface) { + _focusedSurface = surface; + } // Is holding all cursors in memory a good idea? wl_cursor_image* _cursors[static_cast(jwm::MouseCursor::COUNT)]; From 78ab64faf1a9bd8b58ff4d3052fe5c4dd09f24db Mon Sep 17 00:00:00 2001 From: BulbyVR <26726264+TheDrawingCoder-Gamer@users.noreply.github.com> Date: Mon, 18 Dec 2023 12:58:03 -0500 Subject: [PATCH 48/95] yank out pointer into class --- wayland/cc/Pointer.cc | 226 +++++++++++++++++++++++++++++ wayland/cc/Pointer.hh | 49 +++++++ wayland/cc/WindowManagerWayland.cc | 197 +------------------------ wayland/cc/WindowManagerWayland.hh | 50 +++---- wayland/cc/WindowWayland.cc | 13 +- 5 files changed, 304 insertions(+), 231 deletions(-) create mode 100644 wayland/cc/Pointer.cc create mode 100644 wayland/cc/Pointer.hh diff --git a/wayland/cc/Pointer.cc b/wayland/cc/Pointer.cc new file mode 100644 index 00000000..06c242c3 --- /dev/null +++ b/wayland/cc/Pointer.cc @@ -0,0 +1,226 @@ +#include "Pointer.hh" +#include "WindowManagerWayland.hh" +#include "WindowWayland.hh" +#include "MouseButtonWayland.hh" +#include "AppWayland.hh" +#include "KeyWayland.hh" +#include +#include +using namespace jwm; + +static void pointerEnter(void* data, wl_pointer* pointer, uint32_t serial, + wl_surface* surface, wl_fixed_t surface_x, wl_fixed_t surface_y + ) +{ + Pointer* self = reinterpret_cast(data); + self->_serial = serial; + if (auto window = self->_wm.getWindowForNative(surface)) { + window->setCursor(jwm::MouseCursor::ARROW); + self->_focusedSurface = window; + } +} + +static void pointerLeave(void* data, wl_pointer* pointer, uint32_t serial, + wl_surface* surface) +{ + auto self = reinterpret_cast(data); + self->_focusedSurface = nullptr; + + self->_mouseMask = 0; + self->_serial = 0; +} +static void pointerMotion(void* data, wl_pointer* pointer, uint32_t time, + wl_fixed_t surface_x, wl_fixed_t surface_y) +{ + auto self = reinterpret_cast(data); + self->mouseUpdateUnscaled(wl_fixed_to_int(surface_x), wl_fixed_to_int(surface_y), self->_mouseMask); +} + +static void pointerButton(void* data, wl_pointer* pointer, uint32_t serial, + uint32_t time, uint32_t button, uint32_t state) +{ + using namespace classes; + auto self = reinterpret_cast(data); + auto window = self->_focusedSurface; + if (!window) return; + if (state == 0) { + // release + switch (button) { + // primary + case BTN_LEFT: + self->_mouseMask &= ~0x100; + break; + // secondary + case BTN_RIGHT: + self->_mouseMask &= ~0x400; + break; + // middle + case BTN_MIDDLE: + self->_mouseMask &= ~0x200; + break; + default: + break; + } + + if (MouseButtonWayland::isButton(button)) { + jwm::JNILocal eventButton( + app.getJniEnv(), + jwm::classes::EventMouseButton::make( + app.getJniEnv(), + MouseButtonWayland::fromNative(button), + false, + self->_lastMouseX, + self->_lastMouseY, + jwm::KeyWayland::getModifiers(self->_wm.getXkbState()) + ) + ); + window->dispatch(eventButton.get()); + } + } else { + // down + switch (button) { + // primary + case BTN_LEFT: + self->_mouseMask |= 0x100; + break; + // secondary + case BTN_RIGHT: + self->_mouseMask |= 0x400; + break; + // middle + case BTN_MIDDLE: + self->_mouseMask |= 0x200; + break; + default: + break; + } + + if (MouseButtonWayland::isButton(button)) { + jwm::JNILocal eventButton( + app.getJniEnv(), + jwm::classes::EventMouseButton::make( + app.getJniEnv(), + MouseButtonWayland::fromNative(button), + true, + self->_lastMouseX, + self->_lastMouseY, + jwm::KeyWayland::getModifiers(self->_wm.getXkbState()) + ) + ); + window->dispatch(eventButton.get()); + } + } +} + +static void pointerAxis(void* data, wl_pointer* pointer, uint32_t time, + uint32_t axis, wl_fixed_t value) +{ + auto self = reinterpret_cast(data); + if (!self->_focusedSurface) return; + float fvalue = static_cast(wl_fixed_to_double(value)); + switch (axis) { + case WL_POINTER_AXIS_VERTICAL_SCROLL: + self->_dY += fvalue; + break; + case WL_POINTER_AXIS_HORIZONTAL_SCROLL: + self->_dX += fvalue; + break; + default: + break; + } +} + +static void pointerFrame(void* data, wl_pointer* pointer) +{ + auto self = reinterpret_cast(data); + auto win = self->_focusedSurface; + if (!win) return; + if (self->_dX != 0.0f || self->_dY != 0.0f) { + auto env = app.getJniEnv(); + + jwm::JNILocal eventAxis( + env, + jwm::classes::EventMouseScroll::make( + env, + self->_dX * win->_scale, + self->_dY * win->_scale, + 0.0f, + 0.0f, + 0.0f, + self->_lastMouseX, + self->_lastMouseY, + jwm::KeyWayland::getModifiers(self->_wm.getXkbState()) + ) + ); + win->dispatch(eventAxis.get()); + + self->_dX = 0.0f; + self->_dY = 0.0f; + } +} + +static void pointerAxisSource(void* data, wl_pointer* pointer, uint32_t source) {} +static void pointerAxisStop(void* data, wl_pointer* pointer, uint32_t time, uint32_t axis) {} +static void pointerAxisDiscrete(void* data, wl_pointer* pointer, uint32_t axis, int discrete) {} + +wl_pointer_listener Pointer::_pointerListener = { + .enter = pointerEnter, + .leave = pointerLeave, + .motion = pointerMotion, + .button = pointerButton, + .axis = pointerAxis, + .frame = pointerFrame, + .axis_source = pointerAxisSource, + .axis_stop = pointerAxisStop, + .axis_discrete = pointerAxisDiscrete +}; + +Pointer::Pointer(wl_pointer* pointer, WindowManagerWayland* wm): + _pointer(pointer), + _wm(*wm) +{ + _surface = wl_compositor_create_surface(_wm.compositor); + wl_pointer_add_listener(pointer, &_pointerListener, this); +} + +Pointer::~Pointer() +{ + if (_pointer) + wl_pointer_release(_pointer); +} + +void Pointer::mouseUpdate(uint32_t x, uint32_t y, uint32_t mask) { + auto window = _focusedSurface; + if (!window) + return; + if (_lastMouseX == x && _lastMouseY == y) + return; + _lastMouseX = x; + _lastMouseY = y; + int movementX = 0, movementY = 0; + + jwm::JNILocal eventMove( + app.getJniEnv(), + jwm::classes::EventMouseMove::make(app.getJniEnv(), + x, + y, + movementX, + movementY, + jwm::MouseButtonWayland::fromNativeMask(mask), + // impl me! + jwm::KeyWayland::getModifiers(_wm.getXkbState()) + ) + ); + window->dispatch(eventMove.get()); +} + +void Pointer::mouseUpdateUnscaled(uint32_t x, uint32_t y, uint32_t mask) { + auto window = _focusedSurface; + if (!window) return; + mouseUpdate(x * window->_scale, y * window->_scale, mask); +} + +void Pointer::updateHotspot(int x, int y) { + if (!_focusedSurface) return; + wl_pointer_set_cursor(_pointer, _serial, _surface, x / _focusedSurface->_scale, y / _focusedSurface->_scale); +} diff --git a/wayland/cc/Pointer.hh b/wayland/cc/Pointer.hh new file mode 100644 index 00000000..8fb979eb --- /dev/null +++ b/wayland/cc/Pointer.hh @@ -0,0 +1,49 @@ +#pragma once + +#include + +namespace jwm { + class WindowManagerWayland; + class WindowWayland; + class Pointer { + public: + Pointer(wl_pointer* pointer, jwm::WindowManagerWayland* wm); + ~Pointer(); + + wl_pointer* _pointer; + wl_pointer* getPointer() const { + return _pointer; + } + + uint32_t _serial = 0; + uint32_t getSerial() { + return _serial; + } + + wl_surface* _surface; + wl_surface* getSurface() const { + return _surface; + } + + WindowWayland* _focusedSurface; + WindowWayland* getFocusedSurface() const { + return _focusedSurface; + } + + uint32_t _lastMouseX; + uint32_t _lastMouseY; + float _dX = 0.0; + float _dY = 0.0; + + int _mouseMask = 0; + jwm::WindowManagerWayland& _wm; + + static wl_pointer_listener _pointerListener; + + void mouseUpdate(uint32_t x, uint32_t y, uint32_t mask); + void mouseUpdateUnscaled(uint32_t x, uint32_t y, uint32_t mask); + + void updateHotspot(int x, int y); + + }; +} diff --git a/wayland/cc/WindowManagerWayland.cc b/wayland/cc/WindowManagerWayland.cc index 4922ff1f..e8d3481e 100644 --- a/wayland/cc/WindowManagerWayland.cc +++ b/wayland/cc/WindowManagerWayland.cc @@ -31,48 +31,6 @@ wl_registry_listener WindowManagerWayland::_registryListener = { .global = WindowManagerWayland::registryHandleGlobal, .global_remove = WindowManagerWayland::registryHandleGlobalRemove }; -static void pointerFrame(void* data, wl_pointer* pointer) { - auto self = reinterpret_cast(data); - if (!self->getFocusedSurface()) return; - if (self->_dX != 0.0f || self->_dY != 0.0f) { - auto env = app.getJniEnv(); - auto win = self->getFocusedSurface(); - jwm::JNILocal eventAxis( - env, - jwm::classes::EventMouseScroll::make( - env, - self->_dX * win->_scale, - self->_dY * win->_scale, - 0.0f, - 0.0f, - 0.0f, - self->lastMousePosX, - self->lastMousePosY, - jwm::KeyWayland::getModifiers(self->getXkbState()) - ) - ); - win->dispatch(eventAxis.get()); - - self->_dX = 0.0f; - self->_dY = 0.0f; - } -} -static void pointerAxisSource(void* data, wl_pointer* pointer, uint32_t source) {} -static void pointerAxisStop(void* data, wl_pointer* pointer, uint32_t time, uint32_t axis) {} -static void pointerAxisDiscrete(void* data, wl_pointer* pointer, uint32_t axis, int discrete) {} -// Lambdas turn into null pointers at runtime. God knows why. -wl_pointer_listener WindowManagerWayland::_pointerListener = { - .enter = WindowManagerWayland::pointerHandleEnter, - .leave = WindowManagerWayland::pointerHandleLeave, - .motion = WindowManagerWayland::pointerHandleMotion, - .button = WindowManagerWayland::pointerHandleButton, - .axis = WindowManagerWayland::pointerHandleAxis, - .frame = pointerFrame, - .axis_source = pointerAxisSource, - .axis_stop = pointerAxisStop, - .axis_discrete = pointerAxisDiscrete -}; - libdecor_interface WindowManagerWayland::_decorInterface = { .error = WindowManagerWayland::libdecorError }; @@ -102,7 +60,6 @@ WindowManagerWayland::WindowManagerWayland(): wl_display_roundtrip(display); - cursorSurface = wl_compositor_create_surface(compositor); } @@ -299,135 +256,13 @@ WindowWayland* WindowManagerWayland::getWindowForNative(wl_surface* surface) { return myWindow; */ } -void WindowManagerWayland::pointerHandleEnter(void* data, wl_pointer* pointer, uint32_t serial, - wl_surface *surface, wl_fixed_t surface_x, wl_fixed_t surface_y) { - WindowManagerWayland* self = (WindowManagerWayland*)data; - self->mouseSerial = serial; - if (auto window = self->getWindowForNative(surface)) { - window->setCursor(jwm::MouseCursor::ARROW); - self->setFocusedSurface(window); - } -} -void WindowManagerWayland::pointerHandleLeave(void* data, wl_pointer* pointer, uint32_t serial, - wl_surface *surface) { - WindowManagerWayland* self = (WindowManagerWayland*)data; - self->setFocusedSurface(nullptr); - // ??? - self->mouseMask = 0; - self->mouseSerial = -1; -} -void WindowManagerWayland::pointerHandleMotion(void* data, wl_pointer* pointer, - uint32_t time, wl_fixed_t surface_x, wl_fixed_t surface_y) { - WindowManagerWayland* self = (WindowManagerWayland*)data; - if (self->getFocusedSurface()) { - ::WindowWayland* window = self->getFocusedSurface(); - // God is dead if window is null - if (window) - self->mouseUpdate(window, - wl_fixed_to_int(surface_x) * window->_scale, - wl_fixed_to_int(surface_y) * window->_scale, self->mouseMask); - } - -} -void WindowManagerWayland::pointerHandleButton(void* data, wl_pointer* pointer, - uint32_t serial, uint32_t time, uint32_t button, uint32_t state) { - using namespace classes; - WindowManagerWayland* self = (WindowManagerWayland*)data; - if (!self->getFocusedSurface()) return; - auto window = self->getFocusedSurface(); - if (state == 0) { - // release - switch (button) { - // primary - case BTN_LEFT: - self->mouseMask &= ~0x100; - break; - // secondary - case BTN_RIGHT: - self->mouseMask &= ~0x400; - break; - // middle - case BTN_MIDDLE: - self->mouseMask &= ~0x200; - break; - default: - break; - } - - if (MouseButtonWayland::isButton(button)) { - jwm::JNILocal eventButton( - app.getJniEnv(), - EventMouseButton::make( - app.getJniEnv(), - MouseButtonWayland::fromNative(button), - false, - self->lastMousePosX, - self->lastMousePosY, - jwm::KeyWayland::getModifiers(self->getXkbState()) - ) - ); - window->dispatch(eventButton.get()); - } - } else { - // down - switch (button) { - // primary - case BTN_LEFT: - self->mouseMask |= 0x100; - break; - // secondary - case BTN_RIGHT: - self->mouseMask |= 0x400; - break; - // middle - case BTN_MIDDLE: - self->mouseMask |= 0x200; - break; - default: - break; - } - - if (MouseButtonWayland::isButton(button)) { - jwm::JNILocal eventButton( - app.getJniEnv(), - EventMouseButton::make( - app.getJniEnv(), - MouseButtonWayland::fromNative(button), - true, - self->lastMousePosX, - self->lastMousePosY, - jwm::KeyWayland::getModifiers(self->getXkbState()) - ) - ); - window->dispatch(eventButton.get()); - } - } -} -void WindowManagerWayland::pointerHandleAxis(void* data, wl_pointer* pointer, - uint32_t time, uint32_t axis, wl_fixed_t value) { - auto self = reinterpret_cast(data); - if (!self->getFocusedSurface()) return; - switch (axis) { - case WL_POINTER_AXIS_VERTICAL_SCROLL: - self->_dY += static_cast(wl_fixed_to_double(value)); - break; - case WL_POINTER_AXIS_HORIZONTAL_SCROLL: - self->_dX += static_cast(wl_fixed_to_double(value)); - break; - default: - break; - } -} - void WindowManagerWayland::seatCapabilities(void* data, wl_seat* seat, uint32_t capabilities) { auto self = reinterpret_cast(data); if ((capabilities & WL_SEAT_CAPABILITY_POINTER) && - !self->pointer) { - self->pointer = wl_seat_get_pointer(seat); - wl_pointer_add_listener(self->pointer, &_pointerListener, self); - } else if (!(capabilities & WL_SEAT_CAPABILITY_POINTER) && self->pointer) { - wl_pointer_release(self->pointer); - self->pointer = nullptr; + !self->_pointer) { + self->_pointer = new Pointer(wl_seat_get_pointer(seat), self); + } else if (!(capabilities & WL_SEAT_CAPABILITY_POINTER) && self->_pointer) { + self->_pointer = nullptr; } if ((capabilities & WL_SEAT_CAPABILITY_KEYBOARD) && @@ -466,30 +301,6 @@ wl_data_device_listener WindowManagerWayland::_deviceListener = { std::vector WindowManagerWayland::getClipboardFormats() { return { _currentMimeTypes.begin(), _currentMimeTypes.end()}; } -void WindowManagerWayland::mouseUpdate(WindowWayland* myWindow, uint32_t x, uint32_t y, uint32_t mask) { - using namespace classes; - if (!myWindow) - return; - // impl me : ) - if (lastMousePosX == x && lastMousePosY == y) return; - lastMousePosX = x; - lastMousePosY = y; - int movementX = 0, movementY = 0; - jwm::JNILocal eventMove( - app.getJniEnv(), - EventMouseMove::make(app.getJniEnv(), - x, - y, - movementX, - movementY, - jwm::MouseButtonWayland::fromNativeMask(mask), - // impl me! - jwm::KeyWayland::getModifiers(getXkbState()) - ) - ); - auto foo = eventMove.get(); - myWindow->dispatch(foo); -} jwm::ByteBuf WindowManagerWayland::getClipboardContents(const std::string& type) { auto it = _myClipboardContents.find(type); if (it != _myClipboardContents.end()) { diff --git a/wayland/cc/WindowManagerWayland.hh b/wayland/cc/WindowManagerWayland.hh index a594ddd2..173b9bfc 100644 --- a/wayland/cc/WindowManagerWayland.hh +++ b/wayland/cc/WindowManagerWayland.hh @@ -19,6 +19,7 @@ #include #include #include "Keyboard.hh" +#include "Pointer.hh" namespace jwm { class WindowWayland; @@ -67,21 +68,6 @@ namespace jwm { static void registryHandleGlobalRemove(void* data, wl_registry *registry, uint32_t name); - static wl_pointer_listener _pointerListener; - - static void pointerHandleEnter(void* data, wl_pointer *pointer, - uint32_t serial, wl_surface *surface, wl_fixed_t surface_x, wl_fixed_t surface_y); - static void pointerHandleLeave(void* data, wl_pointer *pointer, - uint32_t serial, wl_surface* surface); - static void pointerHandleMotion(void* data, wl_pointer *pointer, - uint32_t time, wl_fixed_t surface_x, wl_fixed_t surface_y); - static void pointerHandleButton(void* data, wl_pointer *pointer, - uint32_t serial, uint32_t time, uint32_t button, - uint32_t state); - static void pointerHandleAxis(void* data, wl_pointer *pointer, - uint32_t time, uint32_t axis, wl_fixed_t value); - - static libdecor_interface _decorInterface; static void libdecorError(libdecor* context, enum libdecor_error error, const char* message); @@ -104,12 +90,19 @@ namespace jwm { wl_data_device_manager* deviceManager = nullptr; // no multiseat? wl_seat* seat = nullptr; - wl_pointer* pointer = nullptr; + Pointer* _pointer = nullptr; + Pointer* getPointer() const { + return _pointer; + } Keyboard* _keyboard = nullptr; wl_data_device* dataDevice = nullptr; wl_data_source* currentSource = nullptr; wl_data_offer* currentOffer = nullptr; - uint32_t mouseSerial = 0; + uint32_t getMouseSerial() const { + if (_pointer) + return _pointer->getSerial(); + return 0; + } uint32_t getKeyboardSerial() const { if (_keyboard) return _keyboard->getSerial(); @@ -124,31 +117,24 @@ namespace jwm { EGLDisplay _eglDisplay = EGL_NO_DISPLAY; std::list outputs; - wl_cursor* currentCursor = nullptr; bool _runLoop; int notifyFD = -1; std::atomic_bool notifyBool{false}; - int lastMousePosX = 0; - int lastMousePosY = 0; - // god forgive me for using float - float _dX = 0.0f; - float _dY = 0.0f; - int mouseMask = 0; - void mouseUpdate(WindowWayland* myWindow, uint32_t x, uint32_t y, uint32_t mask); std::map _nativeWindowToMy; std::map _myClipboardContents; std::map _myClipboardSource; std::list _currentMimeTypes; - - wl_surface* cursorSurface; - WindowWayland* _focusedSurface = nullptr; - WindowWayland* getFocusedSurface() const { - return _focusedSurface; + wl_surface* getCursorSurface() const { + if (_pointer) + return _pointer->getSurface(); + return nullptr; } - void setFocusedSurface(WindowWayland* surface) { - _focusedSurface = surface; + WindowWayland* getFocusedSurface() const { + if (_pointer) + return _pointer->getFocusedSurface(); + return nullptr; } // Is holding all cursors in memory a good idea? wl_cursor_image* _cursors[static_cast(jwm::MouseCursor::COUNT)]; diff --git a/wayland/cc/WindowWayland.cc b/wayland/cc/WindowWayland.cc index acf80383..a5c1b7f0 100644 --- a/wayland/cc/WindowWayland.cc +++ b/wayland/cc/WindowWayland.cc @@ -263,16 +263,17 @@ void WindowWayland::setVisible(bool isVisible) { } void jwm::WindowWayland::setCursor(jwm::MouseCursor cursor) { + if (!_windowManager.getPointer()) return; auto wayCursor = _getCursorFor(cursor)->images[0]; auto buf = wl_cursor_image_get_buffer(wayCursor); - wl_surface_attach(_windowManager.cursorSurface, + auto cursorSurface = _windowManager.getCursorSurface(); + wl_surface_attach(cursorSurface, wl_cursor_image_get_buffer(wayCursor), 0, 0); - wl_surface_set_buffer_scale(_windowManager.cursorSurface, _scale); - wl_surface_damage_buffer(_windowManager.cursorSurface, 0, 0, INT32_MAX, INT32_MAX); - wl_pointer_set_cursor(_windowManager.pointer, _windowManager.mouseSerial, _windowManager.cursorSurface, - wayCursor->hotspot_x / _scale, wayCursor->hotspot_y / _scale); - wl_surface_commit(_windowManager.cursorSurface); + wl_surface_set_buffer_scale(cursorSurface, _scale); + wl_surface_damage_buffer(cursorSurface, 0, 0, INT32_MAX, INT32_MAX); + _windowManager.getPointer()->updateHotspot(wayCursor->hotspot_x, wayCursor->hotspot_y); + wl_surface_commit(cursorSurface); } // what do??? From adc9d353c4c69da8ca7b60f98841f155db657416 Mon Sep 17 00:00:00 2001 From: BulbyVR <26726264+TheDrawingCoder-Gamer@users.noreply.github.com> Date: Mon, 18 Dec 2023 22:37:26 -0500 Subject: [PATCH 49/95] check on deref --- wayland/cc/Keyboard.cc | 10 ++++++++-- wayland/cc/Keyboard.hh | 1 - wayland/cc/LayerGLWayland.cc | 4 +++- wayland/cc/Pointer.cc | 4 +++- 4 files changed, 14 insertions(+), 5 deletions(-) diff --git a/wayland/cc/Keyboard.cc b/wayland/cc/Keyboard.cc index b2dc1a74..4a90c94d 100644 --- a/wayland/cc/Keyboard.cc +++ b/wayland/cc/Keyboard.cc @@ -64,7 +64,7 @@ static void kbEnter(void* data, wl_keyboard* kb, uint32_t serial, wl_surface* su auto win = self->_wm.getWindowForNative(surface); if (!win) return; self->_serial = serial; - self->_focus = win; + self->_focus = jwm::ref(win); if (self->_state) { uint32_t* key; // C++ jank @@ -84,7 +84,11 @@ static void kbLeave(void* data, wl_keyboard* kb, uint32_t serial, wl_surface* su for (auto key : liftedKeys) { self->submitKey(key, WL_KEYBOARD_KEY_STATE_RELEASED); } - self->_serial = -1; + self->_repeating = false; + self->_repeatingText = false; + self->_serial = 0; + if (self->_focus) + jwm::unref(&self->_focus); self->_focus = nullptr; } static void kbKey(void* data, wl_keyboard* kb, uint32_t serial, uint32_t time, @@ -233,6 +237,7 @@ void Keyboard::submitKey(jwm::Key key, uint32_t state) { ) ); _focus->dispatch(keyEvent.get()); + if (state == WL_KEYBOARD_KEY_STATE_PRESSED) { _depressedKeys.push_back(key); } else { @@ -243,3 +248,4 @@ void Keyboard::submitKey(jwm::Key key, uint32_t state) { } } } + diff --git a/wayland/cc/Keyboard.hh b/wayland/cc/Keyboard.hh index c4ad891b..2793d66a 100644 --- a/wayland/cc/Keyboard.hh +++ b/wayland/cc/Keyboard.hh @@ -44,7 +44,6 @@ namespace jwm { void submitKey(jwm::Key key, uint32_t state); - jwm::StringUTF16 _repeatText; jwm::Key _repeatKey = jwm::Key::UNDEFINED; bool _repeating = false; diff --git a/wayland/cc/LayerGLWayland.cc b/wayland/cc/LayerGLWayland.cc index 7e577e79..3ad71741 100644 --- a/wayland/cc/LayerGLWayland.cc +++ b/wayland/cc/LayerGLWayland.cc @@ -95,7 +95,7 @@ namespace jwm { glClear(GL_STENCIL_BUFFER_BIT | GL_COLOR_BUFFER_BIT); // ??? - glViewport(0, 0, width, height); + // glViewport(0, 0, width, height); // God is dead if _eglWindow is null if (_eglWindow) wl_egl_window_resize(_eglWindow, width, height, 0, 0); @@ -107,6 +107,8 @@ namespace jwm { } void swapBuffers() override { + makeCurrent(); + eglSwapBuffers(_display, _surface); } diff --git a/wayland/cc/Pointer.cc b/wayland/cc/Pointer.cc index 06c242c3..5ed89dfb 100644 --- a/wayland/cc/Pointer.cc +++ b/wayland/cc/Pointer.cc @@ -16,7 +16,7 @@ static void pointerEnter(void* data, wl_pointer* pointer, uint32_t serial, self->_serial = serial; if (auto window = self->_wm.getWindowForNative(surface)) { window->setCursor(jwm::MouseCursor::ARROW); - self->_focusedSurface = window; + self->_focusedSurface = jwm::ref(window); } } @@ -24,6 +24,8 @@ static void pointerLeave(void* data, wl_pointer* pointer, uint32_t serial, wl_surface* surface) { auto self = reinterpret_cast(data); + if (self->_focusedSurface) + jwm::unref(&self->_focusedSurface); self->_focusedSurface = nullptr; self->_mouseMask = 0; From 59c30826584b0bcd992b7fac148a3f0bafb54ad3 Mon Sep 17 00:00:00 2001 From: BulbyVR <26726264+TheDrawingCoder-Gamer@users.noreply.github.com> Date: Mon, 18 Dec 2023 23:07:46 -0500 Subject: [PATCH 50/95] no copy/move stuff in things w/ destructors --- wayland/cc/Buffer.hh | 6 ++ wayland/cc/Keyboard.cc | 1 - wayland/cc/Keyboard.hh | 7 ++- wayland/cc/Pointer.cc | 1 - wayland/cc/Pointer.hh | 5 ++ wayland/cc/ShmPool.cc | 97 ------------------------------ wayland/cc/ShmPool.hh | 26 -------- wayland/cc/WindowManagerWayland.cc | 3 +- 8 files changed, 19 insertions(+), 127 deletions(-) delete mode 100644 wayland/cc/ShmPool.cc delete mode 100644 wayland/cc/ShmPool.hh diff --git a/wayland/cc/Buffer.hh b/wayland/cc/Buffer.hh index d6bfe08c..a536ba07 100644 --- a/wayland/cc/Buffer.hh +++ b/wayland/cc/Buffer.hh @@ -29,5 +29,11 @@ namespace jwm { static wl_buffer_listener _bufferListener; static Buffer* createShmBuffer(wl_shm* shm, int width, int height, uint32_t format); + private: + Buffer(const Buffer&) = delete; + Buffer(Buffer&&) = delete; + Buffer& operator=(const Buffer&) = delete; + Buffer& operator=(Buffer&&) = delete; + }; } diff --git a/wayland/cc/Keyboard.cc b/wayland/cc/Keyboard.cc index 4a90c94d..57bf50e6 100644 --- a/wayland/cc/Keyboard.cc +++ b/wayland/cc/Keyboard.cc @@ -89,7 +89,6 @@ static void kbLeave(void* data, wl_keyboard* kb, uint32_t serial, wl_surface* su self->_serial = 0; if (self->_focus) jwm::unref(&self->_focus); - self->_focus = nullptr; } static void kbKey(void* data, wl_keyboard* kb, uint32_t serial, uint32_t time, uint32_t key, uint32_t state) { diff --git a/wayland/cc/Keyboard.hh b/wayland/cc/Keyboard.hh index 2793d66a..04baf904 100644 --- a/wayland/cc/Keyboard.hh +++ b/wayland/cc/Keyboard.hh @@ -54,6 +54,11 @@ namespace jwm { jwm::WindowManagerWayland& _wm; static wl_keyboard_listener _keyboardListener; - + private: + // no copy or move + Keyboard(const Keyboard&) = delete; + Keyboard(Keyboard&&) = delete; + Keyboard& operator=(const Keyboard&) = delete; + Keyboard& operator=(Keyboard&&) = delete; }; } diff --git a/wayland/cc/Pointer.cc b/wayland/cc/Pointer.cc index 5ed89dfb..3674d230 100644 --- a/wayland/cc/Pointer.cc +++ b/wayland/cc/Pointer.cc @@ -26,7 +26,6 @@ static void pointerLeave(void* data, wl_pointer* pointer, uint32_t serial, auto self = reinterpret_cast(data); if (self->_focusedSurface) jwm::unref(&self->_focusedSurface); - self->_focusedSurface = nullptr; self->_mouseMask = 0; self->_serial = 0; diff --git a/wayland/cc/Pointer.hh b/wayland/cc/Pointer.hh index 8fb979eb..4aca0068 100644 --- a/wayland/cc/Pointer.hh +++ b/wayland/cc/Pointer.hh @@ -45,5 +45,10 @@ namespace jwm { void updateHotspot(int x, int y); + private: + Pointer(const Pointer& other) = delete; + Pointer(Pointer&&) = delete; + Pointer& operator=(const Pointer& other) = delete; + Pointer& operator=(Pointer&&) = delete; }; } diff --git a/wayland/cc/ShmPool.cc b/wayland/cc/ShmPool.cc deleted file mode 100644 index 4366b45d..00000000 --- a/wayland/cc/ShmPool.cc +++ /dev/null @@ -1,97 +0,0 @@ -#include "ShmPool.hh" -#include -#include -#include -#include -#include -#include - -using namespace jwm; - -static void randname(char *buf) -{ - struct timespec ts; - clock_gettime(CLOCK_REALTIME, &ts); - long r = ts.tv_nsec; - for (int i = 0; i < 6; ++i) { - buf[i] = 'A'+(r&15)+(r&16)*2; - r >>= 5; - } -} -ShmPool::ShmPool(wl_shm* shm, size_t size): - _size(size) { - _fd = _allocateShmFile(size); - if (_fd < 0) { - // why : ( - throw std::system_error(EIO, std::generic_category(), "Couldn't allocate buffer"); - } - _rawData = (uint8_t*)mmap(nullptr, size, - PROT_READ | PROT_WRITE, MAP_SHARED, _fd, 0); - _pool = wl_shm_create_pool(shm, _fd, size); - - } -ShmPool::~ShmPool() { - close(); -} -void ShmPool::close() { - wl_shm_pool_destroy(_pool); - ::close(_fd); - munmap(_rawData, _size); -} - -void ShmPool::grow(size_t size) { - if (size <= _size) - return; - int ret; - do { - ret = ftruncate(_fd, size); - } while (ret < 0 && errno == EINTR); - if (ret < 0) { - // AAHHHHH! - throw std::system_error(EIO, std::generic_category(), "Couldn't grow buffer"); - } - uint8_t* newData = (uint8_t*)mmap(nullptr, size, - PROT_READ | PROT_WRITE, MAP_SHARED, _fd, 0); - // do I need to memcpy??? lets say no :troll: - // TODO: error checking :troll: - munmap(_rawData, _size); - _rawData = newData; - _size = size; - wl_shm_pool_resize(_pool, size); -} - -int ShmPool::_createShmFile() { - int retries = 100; - do { - char name[] = "/wl_shm-XXXXXX"; - randname(name + sizeof(name) - 7); - --retries; - int fd = shm_open(name, O_RDWR | O_CREAT | O_EXCL, 0600); - if (fd >= 0) { - shm_unlink(name); - return fd; - } - } while (retries > 0 && errno == EEXIST); - return -1; -} - -int ShmPool::_allocateShmFile(size_t size) { - int fd = _createShmFile(); - if (fd < 0) - return -1; - int ret; - do { - ret = ftruncate(fd, size); - } while (ret < 0 && errno == EINTR); - if (ret < 0) { - ::close(fd); - return -1; - } - return fd; -} - -std::pair ShmPool::createBuffer(int offset, int width, int height, int stride, uint32_t format) { - wl_buffer* buffer = wl_shm_pool_create_buffer(_pool, offset, width, height, stride, format); - uint8_t* data = &_rawData[offset]; - return std::pair(buffer, data); -} diff --git a/wayland/cc/ShmPool.hh b/wayland/cc/ShmPool.hh deleted file mode 100644 index 402660b6..00000000 --- a/wayland/cc/ShmPool.hh +++ /dev/null @@ -1,26 +0,0 @@ -#pragma once - -#include -#include - -namespace jwm { - class ShmPool { - public: - ShmPool(wl_shm* shm, size_t size); - ~ShmPool(); - - size_t _size; - int _fd; - wl_shm_pool* _pool; - uint8_t* _rawData; - - // grows current file to at least this size - void grow(size_t size); - - std::pair createBuffer(int offset, int width, int height, int stride, uint32_t format); - - int _createShmFile(); - int _allocateShmFile(size_t size); - void close(); - }; -} diff --git a/wayland/cc/WindowManagerWayland.cc b/wayland/cc/WindowManagerWayland.cc index e8d3481e..e2673e7a 100644 --- a/wayland/cc/WindowManagerWayland.cc +++ b/wayland/cc/WindowManagerWayland.cc @@ -107,7 +107,8 @@ void WindowManagerWayland::runLoop() { if (ps[0].revents & POLLIN) { if (libdecor_dispatch(decorCtx, -1) < 0) { fprintf(stderr, "error with dispatch\n"); - break; + // ??? + // break; } } From aff279051634a01c67c81bb791658eb929fb9c44 Mon Sep 17 00:00:00 2001 From: TheDrawingCoding-Gamer Date: Tue, 19 Dec 2023 13:59:34 -0500 Subject: [PATCH 51/95] clarify what detaching does --- wayland/cc/ILayerWayland.hh | 2 +- wayland/cc/LayerGLWayland.cc | 4 ++-- wayland/cc/LayerRasterWayland.cc | 4 ++-- wayland/cc/WindowWayland.cc | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/wayland/cc/ILayerWayland.hh b/wayland/cc/ILayerWayland.hh index 68d2727f..2e10e226 100644 --- a/wayland/cc/ILayerWayland.hh +++ b/wayland/cc/ILayerWayland.hh @@ -7,6 +7,6 @@ namespace jwm { public: virtual void attachBuffer() = 0; virtual void swapBuffers() = 0; - virtual void detach() = 0; + virtual void detachBuffer() = 0; }; } diff --git a/wayland/cc/LayerGLWayland.cc b/wayland/cc/LayerGLWayland.cc index 3ad71741..e04da171 100644 --- a/wayland/cc/LayerGLWayland.cc +++ b/wayland/cc/LayerGLWayland.cc @@ -116,7 +116,7 @@ namespace jwm { if (_closed) return; _closed = true; - detach(); + detachBuffer(); eglDestroyContext(_display, _context); _firstAttach = true; @@ -145,7 +145,7 @@ namespace jwm { makeCurrentForced(); } } - void detach() override { + void detachBuffer() override { eglMakeCurrent(_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); if (_surface) { eglDestroySurface(_display, _surface); diff --git a/wayland/cc/LayerRasterWayland.cc b/wayland/cc/LayerRasterWayland.cc index cd2aea11..b4bd047e 100644 --- a/wayland/cc/LayerRasterWayland.cc +++ b/wayland/cc/LayerRasterWayland.cc @@ -63,7 +63,7 @@ namespace jwm { } void close() override { - detach(); + detachBuffer(); jwm::unref(&fWindow); fWindow = nullptr; } @@ -80,7 +80,7 @@ namespace jwm { swapBuffers(); } - void detach() override { + void detachBuffer() override { if (_attached && fWindow && fWindow->_waylandWindow) { wl_surface_attach(fWindow->_waylandWindow, nullptr, 0, 0); // commit is not meant to be used in intermediate states diff --git a/wayland/cc/WindowWayland.cc b/wayland/cc/WindowWayland.cc index a5c1b7f0..6f1c05d3 100644 --- a/wayland/cc/WindowWayland.cc +++ b/wayland/cc/WindowWayland.cc @@ -77,7 +77,7 @@ void WindowWayland::close() { void WindowWayland::hide() { _visible = false; if (_layer) { - _layer->detach(); + _layer->detachBuffer(); } if (_frame) { libdecor_frame_unref(_frame); From 9748b978f7c82a4f7acbe63edb9971e9652d516b Mon Sep 17 00:00:00 2001 From: TheDrawingCoding-Gamer Date: Tue, 19 Dec 2023 21:56:47 -0500 Subject: [PATCH 52/95] various fixes, nvidia worksish --- wayland/cc/LayerGLWayland.cc | 40 ++++++---------- wayland/cc/MouseButtonWayland.cc | 16 ++++--- wayland/cc/Output.cc | 7 ++- wayland/cc/Output.hh | 7 +++ wayland/cc/Pointer.cc | 4 +- wayland/cc/WindowManagerWayland.cc | 73 ++++++++++++++++-------------- wayland/cc/WindowWayland.cc | 34 +++----------- wayland/cc/WindowWayland.hh | 11 ----- 8 files changed, 82 insertions(+), 110 deletions(-) diff --git a/wayland/cc/LayerGLWayland.cc b/wayland/cc/LayerGLWayland.cc index e04da171..665976ea 100644 --- a/wayland/cc/LayerGLWayland.cc +++ b/wayland/cc/LayerGLWayland.cc @@ -22,15 +22,11 @@ namespace jwm { EGLDisplay _display = nullptr; EGLSurface _surface = nullptr; EGLConfig _config = nullptr; - bool _firstAttach = true; - bool _closed = false; LayerGL() = default; virtual ~LayerGL() = default; void attach(WindowWayland* window) { - // no idea why you would reopen it??? - _closed = false; fWindow = jwm::ref(window); fWindow->setLayer(this); // Force a reconfigure; needed to draw title bar correctly @@ -40,9 +36,6 @@ namespace jwm { eglInitialize(fWindow->_windowManager._eglDisplay, nullptr, nullptr); } - if (_firstAttach) { - _firstAttach = false; - } _display = fWindow->_windowManager._eglDisplay; if ( eglBindAPI(EGL_OPENGL_API) == EGL_FALSE) { throw new std::runtime_error("Cannot bind EGL Api"); @@ -74,8 +67,6 @@ namespace jwm { if ( _context == EGL_NO_CONTEXT ) { throw std::runtime_error("Couldn't make context"); } - // Don't block on swap - // Blocking here will freeze the app on sway. } if (fWindow->_configured) attachBuffer(); @@ -95,42 +86,37 @@ namespace jwm { glClear(GL_STENCIL_BUFFER_BIT | GL_COLOR_BUFFER_BIT); // ??? - // glViewport(0, 0, width, height); + glViewport(0, 0, width, height); // God is dead if _eglWindow is null if (_eglWindow) wl_egl_window_resize(_eglWindow, width, height, 0, 0); - if (fWindow->_scale != fWindow->_oldScale) { - swapBuffers(); - wl_surface_set_buffer_scale(fWindow->_waylandWindow, fWindow->_scale); - } + wl_surface_set_buffer_scale(fWindow->_waylandWindow, fWindow->_scale); } void swapBuffers() override { makeCurrent(); - - eglSwapBuffers(_display, _surface); + if (_surface) + eglSwapBuffers(_display, _surface); } void close() override { - if (_closed) - return; - _closed = true; detachBuffer(); eglDestroyContext(_display, _context); - _firstAttach = true; - jwm::unref(&fWindow); - fWindow = nullptr; + if (fWindow) + jwm::unref(&fWindow); } void makeCurrentForced() override { ILayer::makeCurrentForced(); - eglMakeCurrent(_display, - _surface, - _surface, - _context); - eglSwapInterval(_display, 0); + if (_surface) { + eglMakeCurrent(_display, + _surface, + _surface, + _context); + eglSwapInterval(_display, 0); + } } void attachBuffer() override { if (fWindow && fWindow->_waylandWindow) { diff --git a/wayland/cc/MouseButtonWayland.cc b/wayland/cc/MouseButtonWayland.cc index d2fcd79d..b81f1f37 100644 --- a/wayland/cc/MouseButtonWayland.cc +++ b/wayland/cc/MouseButtonWayland.cc @@ -1,13 +1,17 @@ #include "MouseButtonWayland.hh" - +#include jwm::MouseButton jwm::MouseButtonWayland::fromNative(uint32_t v) { switch (v) { - case 0x110: return jwm::MouseButton::PRIMARY; - case 0x112: return jwm::MouseButton::MIDDLE; - case 0x111: return jwm::MouseButton::SECONDARY; - case 0x116: return jwm::MouseButton::BACK; - case 0x115: return jwm::MouseButton::FORWARD; + case BTN_LEFT: return jwm::MouseButton::PRIMARY; + case BTN_MIDDLE: return jwm::MouseButton::MIDDLE; + case BTN_RIGHT: return jwm::MouseButton::SECONDARY; + // TODO: is this mapping consistent? + // I've gotten this from observing my mouse + case BTN_SIDE: + case BTN_BACK: return jwm::MouseButton::BACK; + case BTN_EXTRA: + case BTN_FORWARD: return jwm::MouseButton::FORWARD; } return jwm::MouseButton::PRIMARY; } diff --git a/wayland/cc/Output.cc b/wayland/cc/Output.cc index 91590ce5..753e36de 100644 --- a/wayland/cc/Output.cc +++ b/wayland/cc/Output.cc @@ -16,12 +16,17 @@ Output::Output(wl_output* output, uint32_t name): { wl_output_add_listener(output, &_outputListener, this); } +Output::~Output() +{ + if (_output) + wl_output_release(_output); +} ScreenInfo Output::getScreenInfo() const { return { .id = _name, .bounds = jwm::IRect::makeXYWH(0, 0, width, height), - .isPrimary = false, + .isPrimary = primary, .scale = scale }; } diff --git a/wayland/cc/Output.hh b/wayland/cc/Output.hh index cf8c9381..8af4c421 100644 --- a/wayland/cc/Output.hh +++ b/wayland/cc/Output.hh @@ -8,9 +8,11 @@ namespace jwm { class Output { public: Output(wl_output* output, uint32_t name); + ~Output(); wl_output* _output; uint32_t _name; + bool primary = false; int scale = 1; int width = 0; int height = 0; @@ -25,5 +27,10 @@ namespace jwm { static void outputScale(void* data, wl_output* output, int factor); static void outputName(void* data, wl_output* output, const char* name); static void outputDescription(void* data, wl_output* output, const char* desc); + private: + Output(const Output&) = delete; + Output(Output&&) = delete; + Output& operator=(const Output&) = delete; + Output& operator=(Output&&) = delete; }; } diff --git a/wayland/cc/Pointer.cc b/wayland/cc/Pointer.cc index 3674d230..f3871d94 100644 --- a/wayland/cc/Pointer.cc +++ b/wayland/cc/Pointer.cc @@ -12,11 +12,11 @@ static void pointerEnter(void* data, wl_pointer* pointer, uint32_t serial, wl_surface* surface, wl_fixed_t surface_x, wl_fixed_t surface_y ) { - Pointer* self = reinterpret_cast(data); + auto self = reinterpret_cast(data); self->_serial = serial; if (auto window = self->_wm.getWindowForNative(surface)) { - window->setCursor(jwm::MouseCursor::ARROW); self->_focusedSurface = jwm::ref(window); + window->setCursor(jwm::MouseCursor::ARROW); } } diff --git a/wayland/cc/WindowManagerWayland.cc b/wayland/cc/WindowManagerWayland.cc index e2673e7a..89de285d 100644 --- a/wayland/cc/WindowManagerWayland.cc +++ b/wayland/cc/WindowManagerWayland.cc @@ -170,41 +170,41 @@ void WindowManagerWayland::_processCallbacks() { } void WindowManagerWayland::_processKeyboard() { - if (_keyboard && _keyboard->_repeating && _keyboard->getFocus()) { - auto now = std::chrono::steady_clock::now(); - _keyboard->_nextRepeat = now + std::chrono::milliseconds(_keyboard->_repeatRate); - auto focus = _keyboard->getFocus(); - auto env = jwm::app.getJniEnv(); - jwm::KeyLocation location = jwm::KeyLocation::DEFAULT; - JNILocal keyOffEvent( - env, - classes::EventKey::make( - env, - _keyboard->_repeatKey, - false, - KeyWayland::getModifiers(_keyboard->_state), - location - ) - ); - - JNILocal keyEvent( - env, - classes::EventKey::make( - env, - _keyboard->_repeatKey, - true, - KeyWayland::getModifiers(_keyboard->_state), - location - ) - ); - focus->dispatch(keyOffEvent.get()); - focus->dispatch(keyEvent.get()); - if (_keyboard->_repeatingText) { - jwm::JNILocal jtext = _keyboard->_repeatText.toJString(env); - jwm::JNILocal eventTextInput(env, classes::EventTextInput::make(env, jtext.get())); - - focus->dispatch(eventTextInput.get()); - } + if (!_keyboard || !_keyboard->_repeating) return; + auto focus = _keyboard->getFocus(); + if (!focus) return; + auto now = std::chrono::steady_clock::now(); + _keyboard->_nextRepeat = now + std::chrono::milliseconds(_keyboard->_repeatRate); + auto env = jwm::app.getJniEnv(); + jwm::KeyLocation location = jwm::KeyLocation::DEFAULT; + JNILocal keyOffEvent( + env, + classes::EventKey::make( + env, + _keyboard->_repeatKey, + false, + KeyWayland::getModifiers(_keyboard->_state), + location + ) + ); + + JNILocal keyEvent( + env, + classes::EventKey::make( + env, + _keyboard->_repeatKey, + true, + KeyWayland::getModifiers(_keyboard->_state), + location + ) + ); + focus->dispatch(keyOffEvent.get()); + focus->dispatch(keyEvent.get()); + if (_keyboard->_repeatingText) { + jwm::JNILocal jtext = _keyboard->_repeatText.toJString(env); + jwm::JNILocal eventTextInput(env, classes::EventTextInput::make(env, jtext.get())); + + focus->dispatch(eventTextInput.get()); } } @@ -229,6 +229,9 @@ void WindowManagerWayland::registryHandleGlobal(void* data, wl_registry *registr wl_output* output = (wl_output*)wl_registry_bind(registry, name, &wl_output_interface, 2); Output* good = new Output(output, name); + if (self->outputs.empty()) + // ??? this is a race condition (probably) but i have to do this to prevent crashes + good->primary = true; self->outputs.push_back(good); } } diff --git a/wayland/cc/WindowWayland.cc b/wayland/cc/WindowWayland.cc index 6f1c05d3..ef3defae 100644 --- a/wayland/cc/WindowWayland.cc +++ b/wayland/cc/WindowWayland.cc @@ -20,15 +20,6 @@ wl_surface_listener WindowWayland::_surfaceListener = { #endif }; - -wl_output_listener WindowWayland::_outputListener = { - .geometry = WindowWayland::outputGeometry, - .mode = WindowWayland::outputMode, - .done = WindowWayland::outputDone, - .scale = WindowWayland::outputScale, - .name = WindowWayland::outputName, - .description = WindowWayland::outputDescription -}; libdecor_frame_interface WindowWayland::_libdecorFrameInterface = { .configure = WindowWayland::decorFrameConfigure, .close = WindowWayland::decorFrameClose, @@ -303,20 +294,6 @@ void jwm::WindowWayland::surfacePreferredBufferScale(void* data, wl_surface* sur } void jwm::WindowWayland::surfacePreferredBufferTransform(void* data, wl_surface* surface, uint32_t transform) {} -void jwm::WindowWayland::outputGeometry(void* data, wl_output* output, int x, int y, int pWidth, int pHeight, - int subpixel, const char* make, const char* model, int transform) {} -void jwm::WindowWayland::outputMode(void* data, wl_output* output, uint32_t flags, int width, int height, - int refresh) {} -void jwm::WindowWayland::outputDone(void* data, wl_output* output) {} -void jwm::WindowWayland::outputScale(void* data, wl_output* output, int factor) { - WindowWayland* self = reinterpret_cast(data); - self->_scale = factor; - if (self->_waylandWindow) - self->dispatch(classes::EventWindowScreenChange::kInstance); -} -void jwm::WindowWayland::outputName(void* data, wl_output* output, const char* name) {} -void jwm::WindowWayland::outputDescription(void* data, wl_output* output, const char* desc) {} - static void frameCallbackDone(void* data, wl_callback* cb, uint32_t cb_data) { auto self = reinterpret_cast(data); self->_adaptSize(self->_newWidth, self->_newHeight); @@ -368,6 +345,11 @@ void jwm::WindowWayland::decorFrameConfigure(libdecor_frame* frame, libdecor_con self->_fullscreen = fullscreen; self->_floating = libdecor_frame_is_floating(frame); } + // before width + if (!self->_configured) { + if (self->_layer) + self->_layer->attachBuffer(); + } if (self->_width != width || self->_height != height) { if (libdecor_frame_is_floating(frame)) { if (width > 0) @@ -379,10 +361,6 @@ void jwm::WindowWayland::decorFrameConfigure(libdecor_frame* frame, libdecor_con self->_adaptSize(width, height); } - if (!self->_configured && self->_visible) { - if (self->_layer) - self->_layer->attachBuffer(); - } self->_configured = true; } void jwm::WindowWayland::decorFrameClose(libdecor_frame* frame, void* userData) { @@ -393,12 +371,12 @@ void jwm::WindowWayland::decorFrameCommit(libdecor_frame* frame, void* userData) WindowWayland* self = reinterpret_cast(userData); if (self->_waylandWindow) { wl_surface_commit(self->_waylandWindow); - wl_display_roundtrip(self->_windowManager.display); } } void jwm::WindowWayland::decorFrameDismissPopup(libdecor_frame* frame, const char* seatName, void* userData) {} void jwm::WindowWayland::_adaptSize(int newWidth, int newHeight) { using namespace classes; + if (newWidth == _width && newHeight == _height && _scale == _oldScale) return; _width = newWidth; _height = newHeight; int scaledWidth = _width * _scale; diff --git a/wayland/cc/WindowWayland.hh b/wayland/cc/WindowWayland.hh index b1b09384..2923077e 100644 --- a/wayland/cc/WindowWayland.hh +++ b/wayland/cc/WindowWayland.hh @@ -61,16 +61,6 @@ namespace jwm { static void surfacePreferredBufferScale(void* data, wl_surface* surface, int factor); static void surfacePreferredBufferTransform(void* data, wl_surface* surface, uint32_t transform); - static void outputGeometry(void* data, wl_output* output, int x, int y, int physWidth, int physHeight, - int subpixelOrient, const char* make, const char* model, int transform); - static void outputMode(void* data, wl_output* output, uint32_t flags, int width, int height, int refresh); - static void outputDone(void* data, wl_output* output); - // YEAH THAT'S WHAT I'VE BEEN WAITING FOR - static void outputScale(void* data, wl_output* output, int factor); - static void outputName(void* data, wl_output* output, const char* name); - static void outputDescription(void* data, wl_output* output, const char* desc); - - static void decorFrameConfigure(libdecor_frame* frame, libdecor_configuration* config, void* userData); static void decorFrameClose(libdecor_frame* frame, void* userData); static void decorFrameCommit(libdecor_frame* frame, void* userData); @@ -113,7 +103,6 @@ namespace jwm { static wl_surface_listener _surfaceListener; - static wl_output_listener _outputListener; static libdecor_frame_interface _libdecorFrameInterface; static wl_callback_listener _frameCallback; From 1d7fb1af4fed7bbd0f40b23569a7b1d86bd59a9c Mon Sep 17 00:00:00 2001 From: TheDrawingCoding-Gamer Date: Tue, 19 Dec 2023 22:12:52 -0500 Subject: [PATCH 53/95] change how cursors are loaded --- wayland/cc/Pointer.cc | 44 ++++++++++++++++++++++++++++++++++ wayland/cc/Pointer.hh | 9 +++++++ wayland/cc/WindowWayland.cc | 48 ++++--------------------------------- wayland/cc/WindowWayland.hh | 1 - 4 files changed, 58 insertions(+), 44 deletions(-) diff --git a/wayland/cc/Pointer.cc b/wayland/cc/Pointer.cc index f3871d94..df1ef231 100644 --- a/wayland/cc/Pointer.cc +++ b/wayland/cc/Pointer.cc @@ -225,3 +225,47 @@ void Pointer::updateHotspot(int x, int y) { if (!_focusedSurface) return; wl_pointer_set_cursor(_pointer, _serial, _surface, x / _focusedSurface->_scale, y / _focusedSurface->_scale); } + +wl_cursor_theme* Pointer::_makeCursors(int scale) { + auto theme = wl_cursor_theme_load(nullptr, 24 * scale, _wm.shm); + _cursorThemes[scale] = theme; + return theme; +} + +wl_cursor_theme* Pointer::getThemeFor(int scale) { + auto it = _cursorThemes.find(scale); + if (it != _cursorThemes.end()) + return it->second; + return _makeCursors(scale); +} +wl_cursor* Pointer::getCursorFor(int scale, jwm::MouseCursor cursor) { + auto theme = getThemeFor(scale); + + switch (cursor) { + case jwm::MouseCursor::ARROW: + return wl_cursor_theme_get_cursor(theme, "default"); + case jwm::MouseCursor::CROSSHAIR: + return wl_cursor_theme_get_cursor(theme, "crosshair"); + case jwm::MouseCursor::HELP: + return wl_cursor_theme_get_cursor(theme, "help"); + case jwm::MouseCursor::POINTING_HAND: + return wl_cursor_theme_get_cursor(theme, "pointer"); + case jwm::MouseCursor::IBEAM: + return wl_cursor_theme_get_cursor(theme, "text"); + case jwm::MouseCursor::NOT_ALLOWED: + return wl_cursor_theme_get_cursor(theme, "not-allowed"); + case jwm::MouseCursor::WAIT: + return wl_cursor_theme_get_cursor(theme, "watch"); + case jwm::MouseCursor::WIN_UPARROW: + return wl_cursor_theme_get_cursor(theme, "up-arrow"); + case jwm::MouseCursor::RESIZE_NS: + return wl_cursor_theme_get_cursor(theme, "ns-resize"); + case jwm::MouseCursor::RESIZE_WE: + return wl_cursor_theme_get_cursor(theme, "ew-resize"); + case jwm::MouseCursor::RESIZE_NESW: + return wl_cursor_theme_get_cursor(theme, "nesw-resize"); + case jwm::MouseCursor::RESIZE_NWSE: + return wl_cursor_theme_get_cursor(theme, "nwse-resize"); + } + return nullptr; +} diff --git a/wayland/cc/Pointer.hh b/wayland/cc/Pointer.hh index 4aca0068..ab5ea0ec 100644 --- a/wayland/cc/Pointer.hh +++ b/wayland/cc/Pointer.hh @@ -1,6 +1,9 @@ #pragma once #include +#include +#include +#include "MouseCursor.hh" namespace jwm { class WindowManagerWayland; @@ -45,6 +48,12 @@ namespace jwm { void updateHotspot(int x, int y); + std::map _cursorThemes; + + wl_cursor_theme* _makeCursors(int scale); + wl_cursor_theme* getThemeFor(int scale); + wl_cursor* getCursorFor(int scale, jwm::MouseCursor cursor); + private: Pointer(const Pointer& other) = delete; Pointer(Pointer&&) = delete; diff --git a/wayland/cc/WindowWayland.cc b/wayland/cc/WindowWayland.cc index ef3defae..5345b50a 100644 --- a/wayland/cc/WindowWayland.cc +++ b/wayland/cc/WindowWayland.cc @@ -35,7 +35,6 @@ WindowWayland::WindowWayland(JNIEnv* env, WindowManagerWayland& windowManager): _title("") { - _makeCursors(); } WindowWayland::~WindowWayland() { @@ -140,47 +139,6 @@ bool WindowWayland::resize(int width, int height) { return true; } -void WindowWayland::_makeCursors() { - - if (theme) - wl_cursor_theme_destroy(theme); - theme = wl_cursor_theme_load(nullptr, 24 * _scale, _windowManager.shm); -} -// cursed -wl_cursor* WindowWayland::_getCursorFor(jwm::MouseCursor cursor) { - switch (cursor) { - case jwm::MouseCursor::ARROW: - // works - return wl_cursor_theme_get_cursor(theme, "default"); - case jwm::MouseCursor::CROSSHAIR: - // works - return wl_cursor_theme_get_cursor(theme, "crosshair"); - case jwm::MouseCursor::HELP: - // sometimes works? - return wl_cursor_theme_get_cursor(theme, "help"); - case jwm::MouseCursor::POINTING_HAND: - // SHOULD work - return wl_cursor_theme_get_cursor(theme, "pointer"); - case jwm::MouseCursor::IBEAM: - // doesn't work at all - return wl_cursor_theme_get_cursor(theme, "text"); - case jwm::MouseCursor::NOT_ALLOWED: - return wl_cursor_theme_get_cursor(theme, "not-allowed"); - case jwm::MouseCursor::WAIT: - return wl_cursor_theme_get_cursor(theme, "watch"); - case jwm::MouseCursor::WIN_UPARROW: - return wl_cursor_theme_get_cursor(theme, "up-arrow"); - case jwm::MouseCursor::RESIZE_NS: - return wl_cursor_theme_get_cursor(theme, "ns-resize"); - case jwm::MouseCursor::RESIZE_WE: - return wl_cursor_theme_get_cursor(theme, "ew-resize"); - case jwm::MouseCursor::RESIZE_NESW: - return wl_cursor_theme_get_cursor(theme, "nesw-resize"); - case jwm::MouseCursor::RESIZE_NWSE: - return wl_cursor_theme_get_cursor(theme, "nwse-resize"); - } - return nullptr; -} int WindowWayland::getLeft() { int x, y; getContentPosition(x, y); @@ -209,6 +167,11 @@ int WindowWayland::getUnscaledHeight() { float WindowWayland::getScale() { return _scale; } +wl_cursor* WindowWayland::_getCursorFor(jwm::MouseCursor cursor) { + if (auto ptr = _windowManager.getPointer()) + return ptr->getCursorFor(_scale, cursor); + return nullptr; +} bool WindowWayland::init() { _waylandWindow = wl_compositor_create_surface(_windowManager.compositor); wl_surface_add_listener(_waylandWindow, &_surfaceListener, this); @@ -393,7 +356,6 @@ void jwm::WindowWayland::_adaptSize(int newWidth, int newHeight) { ); dispatch(eventWindowResize.get()); if (_scale != _oldScale) { - _makeCursors(); _windowManager._processCallbacks(); } // In Java Wayland doesn't actually cause a frame: diff --git a/wayland/cc/WindowWayland.hh b/wayland/cc/WindowWayland.hh index 2923077e..97a7012e 100644 --- a/wayland/cc/WindowWayland.hh +++ b/wayland/cc/WindowWayland.hh @@ -52,7 +52,6 @@ namespace jwm { void setCursor(jwm::MouseCursor cursor); void setLayer(ILayerWayland* layer); - void _makeCursors(); wl_cursor* _getCursorFor(jwm::MouseCursor cursor); ScreenInfo getScreen(); From 1b040917951e3ca4619bab6356e26aa7a25d8729 Mon Sep 17 00:00:00 2001 From: TheDrawingCoding-Gamer Date: Wed, 20 Dec 2023 18:25:55 -0500 Subject: [PATCH 54/95] More safety? --- wayland/cc/AppWayland.cc | 2 ++ wayland/cc/AppWayland.hh | 2 ++ wayland/cc/LayerGLWayland.cc | 6 ++++- wayland/cc/Output.cc | 12 +++++++++ wayland/cc/Output.hh | 2 ++ wayland/cc/Pointer.cc | 21 +++++++++------ wayland/cc/WindowManagerWayland.cc | 3 ++- wayland/cc/WindowWayland.cc | 43 +++++++++++++++++++++--------- wayland/cc/WindowWayland.hh | 2 +- 9 files changed, 69 insertions(+), 24 deletions(-) diff --git a/wayland/cc/AppWayland.cc b/wayland/cc/AppWayland.cc index 5bdde0a9..598e74d1 100644 --- a/wayland/cc/AppWayland.cc +++ b/wayland/cc/AppWayland.cc @@ -6,6 +6,8 @@ jwm::AppWayland jwm::app; +const char* jwm::AppWayland::proxyTag = "JWM"; + void jwm::AppWayland::init(JNIEnv* jniEnv) { _jniEnv = jniEnv; } diff --git a/wayland/cc/AppWayland.hh b/wayland/cc/AppWayland.hh index 690a26f6..4013b841 100644 --- a/wayland/cc/AppWayland.hh +++ b/wayland/cc/AppWayland.hh @@ -23,5 +23,7 @@ namespace jwm { JNIEnv* _jniEnv; WindowManagerWayland wm; + + static const char* proxyTag; } app; } diff --git a/wayland/cc/LayerGLWayland.cc b/wayland/cc/LayerGLWayland.cc index 665976ea..e6716a66 100644 --- a/wayland/cc/LayerGLWayland.cc +++ b/wayland/cc/LayerGLWayland.cc @@ -70,7 +70,6 @@ namespace jwm { } if (fWindow->_configured) attachBuffer(); - makeCurrentForced(); } void setVsyncMode(VSync v) override { @@ -116,6 +115,11 @@ namespace jwm { _surface, _context); eglSwapInterval(_display, 0); + } else { + eglMakeCurrent(_display, + EGL_NO_SURFACE, + EGL_NO_SURFACE, + EGL_NO_CONTEXT); } } void attachBuffer() override { diff --git a/wayland/cc/Output.cc b/wayland/cc/Output.cc index 753e36de..58cc2d90 100644 --- a/wayland/cc/Output.cc +++ b/wayland/cc/Output.cc @@ -1,4 +1,6 @@ #include "Output.hh" +#include "AppWayland.hh" + using namespace jwm; @@ -15,6 +17,7 @@ Output::Output(wl_output* output, uint32_t name): _name(name) { wl_output_add_listener(output, &_outputListener, this); + wl_proxy_set_tag((wl_proxy*) output, &AppWayland::proxyTag); } Output::~Output() { @@ -47,3 +50,12 @@ void Output::outputName(void* data, wl_output* output, const char* name) { } void Output::outputDescription(void* data, wl_output* output, const char* desc) {} +Output* Output::getForNative(wl_output* output) { + if (!output) return nullptr; + + if (wl_proxy_get_tag((wl_proxy*) output) == &AppWayland::proxyTag) { + return reinterpret_cast(wl_output_get_user_data(output)); + } + return nullptr; +} + diff --git a/wayland/cc/Output.hh b/wayland/cc/Output.hh index 8af4c421..1c68d4f2 100644 --- a/wayland/cc/Output.hh +++ b/wayland/cc/Output.hh @@ -27,6 +27,8 @@ namespace jwm { static void outputScale(void* data, wl_output* output, int factor); static void outputName(void* data, wl_output* output, const char* name); static void outputDescription(void* data, wl_output* output, const char* desc); + + static Output* getForNative(wl_output* output); private: Output(const Output&) = delete; Output(Output&&) = delete; diff --git a/wayland/cc/Pointer.cc b/wayland/cc/Pointer.cc index df1ef231..1a8bae33 100644 --- a/wayland/cc/Pointer.cc +++ b/wayland/cc/Pointer.cc @@ -24,11 +24,13 @@ static void pointerLeave(void* data, wl_pointer* pointer, uint32_t serial, wl_surface* surface) { auto self = reinterpret_cast(data); - if (self->_focusedSurface) - jwm::unref(&self->_focusedSurface); - - self->_mouseMask = 0; - self->_serial = 0; + if (auto window = self->_wm.getWindowForNative(surface)) { + if (window == self->_focusedSurface) { + jwm::unref(&self->_focusedSurface); + self->_mouseMask = 0; + self->_serial = 0; + } + } } static void pointerMotion(void* data, wl_pointer* pointer, uint32_t time, wl_fixed_t surface_x, wl_fixed_t surface_y) @@ -188,10 +190,13 @@ Pointer::~Pointer() { if (_pointer) wl_pointer_release(_pointer); + if (_surface) + wl_surface_destroy(_surface); } void Pointer::mouseUpdate(uint32_t x, uint32_t y, uint32_t mask) { auto window = _focusedSurface; + // printf("???\n"); if (!window) return; if (_lastMouseX == x && _lastMouseY == y) @@ -216,9 +221,9 @@ void Pointer::mouseUpdate(uint32_t x, uint32_t y, uint32_t mask) { } void Pointer::mouseUpdateUnscaled(uint32_t x, uint32_t y, uint32_t mask) { - auto window = _focusedSurface; - if (!window) return; - mouseUpdate(x * window->_scale, y * window->_scale, mask); + if (!_focusedSurface) return; + // printf("%i %i\n", x, y); + mouseUpdate(x * _focusedSurface->_scale, y * _focusedSurface->_scale, mask); } void Pointer::updateHotspot(int x, int y) { diff --git a/wayland/cc/WindowManagerWayland.cc b/wayland/cc/WindowManagerWayland.cc index 89de285d..64c5e893 100644 --- a/wayland/cc/WindowManagerWayland.cc +++ b/wayland/cc/WindowManagerWayland.cc @@ -108,7 +108,7 @@ void WindowManagerWayland::runLoop() { if (libdecor_dispatch(decorCtx, -1) < 0) { fprintf(stderr, "error with dispatch\n"); // ??? - // break; + break; } } @@ -246,6 +246,7 @@ void WindowManagerWayland::registryHandleGlobalRemove(void* data, wl_registry *r } } WindowWayland* WindowManagerWayland::getWindowForNative(wl_surface* surface) { + if (!surface) return nullptr; // the tag makes it safe. Should:TM: be faster than searching a list every time const char* const* tag = wl_proxy_get_tag((wl_proxy*) surface); if (tag != &WindowWayland::_windowTag) { diff --git a/wayland/cc/WindowWayland.cc b/wayland/cc/WindowWayland.cc index 5345b50a..569c6a66 100644 --- a/wayland/cc/WindowWayland.cc +++ b/wayland/cc/WindowWayland.cc @@ -7,6 +7,7 @@ #include "impl/JNILocal.hh" #include #include +#include using namespace jwm; @@ -136,6 +137,7 @@ bool WindowWayland::resize(int width, int height) { _adaptSize(_width, _height); } */ + _oldScale = _scale; return true; } @@ -194,8 +196,8 @@ void WindowWayland::show() } ScreenInfo WindowWayland::getScreen() { - if (_output) { - return _output->getScreenInfo(); + if (!_outputs.empty()) { + return _outputs.front()->getScreenInfo(); } else { return { .id = -1, @@ -235,16 +237,35 @@ void jwm::WindowWayland::surfaceEnter(void* data, wl_surface* surface, wl_output // doesn't crash : ) WindowWayland* self = reinterpret_cast(data); - for (auto o : self->_windowManager.outputs) { - if (o->_output == output) { - self->_output = o; - self->_scale = o->scale; - self->_adaptSize(self->getUnscaledWidth(), self->getUnscaledHeight()); - break; + if (auto out = Output::getForNative(output)) { + self->_outputs.push_back(out); + int scale = 1; + for (auto i : self->_outputs) { + if (i->scale > scale) + scale = i->scale; + } + self->_scale = scale; + self->_adaptSize(self->getUnscaledWidth(), self->getUnscaledHeight()); + } +} +void jwm::WindowWayland::surfaceLeave(void* data, wl_surface* surface, wl_output* output) { + auto self = reinterpret_cast(data); + + if (auto out = Output::getForNative(output)) { + auto it = std::find(self->_outputs.begin(), self->_outputs.end(), out); + + if (it != self->_outputs.end()) + self->_outputs.erase(it); + + int scale = 1; + for (auto i : self->_outputs) { + if (i->scale > scale) + scale = i->scale; } + self->_scale = scale; + self->_adaptSize(self->getUnscaledWidth(), self->getUnscaledHeight()); } } -void jwm::WindowWayland::surfaceLeave(void* data, wl_surface* surface, wl_output* output) {} void jwm::WindowWayland::surfacePreferredBufferScale(void* data, wl_surface* surface, int factor) { WindowWayland* self = (WindowWayland*) data; if (factor < 1) { @@ -355,13 +376,9 @@ void jwm::WindowWayland::_adaptSize(int newWidth, int newHeight) { ) ); dispatch(eventWindowResize.get()); - if (_scale != _oldScale) { - _windowManager._processCallbacks(); - } // In Java Wayland doesn't actually cause a frame: // however decorFrameCommit will cause a redraw anyway. // Not doing it in wayland lets me not cause an exception on hide. - _oldScale = _scale; } // JNI diff --git a/wayland/cc/WindowWayland.hh b/wayland/cc/WindowWayland.hh index 97a7012e..264aaec4 100644 --- a/wayland/cc/WindowWayland.hh +++ b/wayland/cc/WindowWayland.hh @@ -97,7 +97,7 @@ namespace jwm { ILayerWayland* _layer = nullptr; wl_surface* _waylandWindow = nullptr; libdecor_frame* _frame = nullptr; - Output* _output = nullptr; + std::list _outputs; wl_cursor_theme* theme = nullptr; static wl_surface_listener _surfaceListener; From 6e7fb7d3c7fbb126195d6da0774577258e2cb1cd Mon Sep 17 00:00:00 2001 From: TheDrawingCoding-Gamer Date: Wed, 20 Dec 2023 22:47:00 -0500 Subject: [PATCH 55/95] rework proxy tags, more checks --- wayland/cc/AppWayland.cc | 6 ++++++ wayland/cc/AppWayland.hh | 3 +++ wayland/cc/Buffer.hh | 2 +- wayland/cc/LayerGLWayland.cc | 6 +++--- wayland/cc/Output.cc | 5 ++++- wayland/cc/Output.hh | 3 ++- wayland/cc/Pointer.cc | 33 +++++++++++++++++++++--------- wayland/cc/Pointer.hh | 9 +++++--- wayland/cc/WindowManagerWayland.cc | 17 +++++++-------- wayland/cc/WindowWayland.cc | 16 +++++++++++---- wayland/cc/WindowWayland.hh | 12 +++++++++-- 11 files changed, 79 insertions(+), 33 deletions(-) diff --git a/wayland/cc/AppWayland.cc b/wayland/cc/AppWayland.cc index 598e74d1..e426fcf7 100644 --- a/wayland/cc/AppWayland.cc +++ b/wayland/cc/AppWayland.cc @@ -23,6 +23,12 @@ void jwm::AppWayland::terminate() { JNIEnv* jwm::AppWayland::getJniEnv() { return _jniEnv; } + +bool jwm::AppWayland::ownProxy(wl_proxy* proxy) { + if (!proxy) + return false; + return wl_proxy_get_tag(proxy) == &proxyTag; +} // JNI extern "C" JNIEXPORT void JNICALL Java_io_github_humbleui_jwm_App__1nStart(JNIEnv* env, jclass jclass, jobject launcher) { diff --git a/wayland/cc/AppWayland.hh b/wayland/cc/AppWayland.hh index 4013b841..defcc38c 100644 --- a/wayland/cc/AppWayland.hh +++ b/wayland/cc/AppWayland.hh @@ -6,6 +6,7 @@ #include #include "impl/Library.hh" #include "ScreenInfo.hh" +#include namespace jwm { extern class AppWayland { @@ -25,5 +26,7 @@ namespace jwm { WindowManagerWayland wm; static const char* proxyTag; + + static bool ownProxy(wl_proxy* proxy); } app; } diff --git a/wayland/cc/Buffer.hh b/wayland/cc/Buffer.hh index a536ba07..2fd0510e 100644 --- a/wayland/cc/Buffer.hh +++ b/wayland/cc/Buffer.hh @@ -11,7 +11,7 @@ namespace jwm { void *data, size_t dataSize); ~Buffer(); - wl_buffer* _buffer; + wl_buffer* _buffer = nullptr; wl_buffer* getBuffer() const { return _buffer; } diff --git a/wayland/cc/LayerGLWayland.cc b/wayland/cc/LayerGLWayland.cc index e6716a66..9b4c7ada 100644 --- a/wayland/cc/LayerGLWayland.cc +++ b/wayland/cc/LayerGLWayland.cc @@ -29,7 +29,6 @@ namespace jwm { void attach(WindowWayland* window) { fWindow = jwm::ref(window); fWindow->setLayer(this); - // Force a reconfigure; needed to draw title bar correctly if (fWindow->_windowManager._eglDisplay == EGL_NO_DISPLAY) { fWindow->_windowManager._eglDisplay = eglGetDisplay(window->_windowManager.display); @@ -128,7 +127,7 @@ namespace jwm { _eglWindow = wl_egl_window_create(fWindow->_waylandWindow, fWindow->getWidth(), fWindow->getHeight()); _surface = eglCreateWindowSurface(_display, _config, _eglWindow, nullptr); - if ( _eglWindow == EGL_NO_SURFACE ) { + if ( _surface == EGL_NO_SURFACE ) { throw std::runtime_error("couldn't get surface"); } } @@ -136,11 +135,12 @@ namespace jwm { } } void detachBuffer() override { - eglMakeCurrent(_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); if (_surface) { eglDestroySurface(_display, _surface); } _surface = nullptr; + // force the current layer to update + makeCurrentForced(); if (_eglWindow) { wl_egl_window_destroy(_eglWindow); } diff --git a/wayland/cc/Output.cc b/wayland/cc/Output.cc index 58cc2d90..4298d459 100644 --- a/wayland/cc/Output.cc +++ b/wayland/cc/Output.cc @@ -53,9 +53,12 @@ void Output::outputDescription(void* data, wl_output* output, const char* desc) Output* Output::getForNative(wl_output* output) { if (!output) return nullptr; - if (wl_proxy_get_tag((wl_proxy*) output) == &AppWayland::proxyTag) { + if (ownOutput(output)) { return reinterpret_cast(wl_output_get_user_data(output)); } return nullptr; } +bool Output::ownOutput(wl_output* output) { + return AppWayland::ownProxy((wl_proxy*) output); +} diff --git a/wayland/cc/Output.hh b/wayland/cc/Output.hh index 1c68d4f2..8ca461b9 100644 --- a/wayland/cc/Output.hh +++ b/wayland/cc/Output.hh @@ -10,7 +10,7 @@ namespace jwm { Output(wl_output* output, uint32_t name); ~Output(); - wl_output* _output; + wl_output* _output = nullptr; uint32_t _name; bool primary = false; int scale = 1; @@ -29,6 +29,7 @@ namespace jwm { static void outputDescription(void* data, wl_output* output, const char* desc); static Output* getForNative(wl_output* output); + static bool ownOutput(wl_output* output); private: Output(const Output&) = delete; Output(Output&&) = delete; diff --git a/wayland/cc/Pointer.cc b/wayland/cc/Pointer.cc index 1a8bae33..75564572 100644 --- a/wayland/cc/Pointer.cc +++ b/wayland/cc/Pointer.cc @@ -6,15 +6,19 @@ #include "KeyWayland.hh" #include #include + using namespace jwm; static void pointerEnter(void* data, wl_pointer* pointer, uint32_t serial, wl_surface* surface, wl_fixed_t surface_x, wl_fixed_t surface_y ) { + if (!Pointer::ownPointer(pointer)) { + return; + } auto self = reinterpret_cast(data); - self->_serial = serial; if (auto window = self->_wm.getWindowForNative(surface)) { + self->_serial = serial; self->_focusedSurface = jwm::ref(window); window->setCursor(jwm::MouseCursor::ARROW); } @@ -23,18 +27,22 @@ static void pointerEnter(void* data, wl_pointer* pointer, uint32_t serial, static void pointerLeave(void* data, wl_pointer* pointer, uint32_t serial, wl_surface* surface) { + if (!Pointer::ownPointer(pointer)) { + return; + } auto self = reinterpret_cast(data); - if (auto window = self->_wm.getWindowForNative(surface)) { - if (window == self->_focusedSurface) { - jwm::unref(&self->_focusedSurface); - self->_mouseMask = 0; - self->_serial = 0; - } + if (self->_focusedSurface && self->_focusedSurface->isNativeSelf(surface)) { + jwm::unref(&self->_focusedSurface); + self->_mouseMask = 0; + self->_serial = 0; } } static void pointerMotion(void* data, wl_pointer* pointer, uint32_t time, wl_fixed_t surface_x, wl_fixed_t surface_y) { + if (!Pointer::ownPointer(pointer)) { + return; + } auto self = reinterpret_cast(data); self->mouseUpdateUnscaled(wl_fixed_to_int(surface_x), wl_fixed_to_int(surface_y), self->_mouseMask); } @@ -184,6 +192,7 @@ Pointer::Pointer(wl_pointer* pointer, WindowManagerWayland* wm): { _surface = wl_compositor_create_surface(_wm.compositor); wl_pointer_add_listener(pointer, &_pointerListener, this); + wl_proxy_set_tag((wl_proxy*)pointer, &AppWayland::proxyTag); } Pointer::~Pointer() @@ -196,7 +205,6 @@ Pointer::~Pointer() void Pointer::mouseUpdate(uint32_t x, uint32_t y, uint32_t mask) { auto window = _focusedSurface; - // printf("???\n"); if (!window) return; if (_lastMouseX == x && _lastMouseY == y) @@ -222,8 +230,9 @@ void Pointer::mouseUpdate(uint32_t x, uint32_t y, uint32_t mask) { void Pointer::mouseUpdateUnscaled(uint32_t x, uint32_t y, uint32_t mask) { if (!_focusedSurface) return; - // printf("%i %i\n", x, y); - mouseUpdate(x * _focusedSurface->_scale, y * _focusedSurface->_scale, mask); + auto newX = x * _focusedSurface->_scale; + auto newY = y * _focusedSurface->_scale; + mouseUpdate(newX, newY, mask); } void Pointer::updateHotspot(int x, int y) { @@ -274,3 +283,7 @@ wl_cursor* Pointer::getCursorFor(int scale, jwm::MouseCursor cursor) { } return nullptr; } + +bool Pointer::ownPointer(wl_pointer* pointer) { + return AppWayland::ownProxy((wl_proxy*) pointer); +} diff --git a/wayland/cc/Pointer.hh b/wayland/cc/Pointer.hh index ab5ea0ec..41ee7c96 100644 --- a/wayland/cc/Pointer.hh +++ b/wayland/cc/Pointer.hh @@ -13,7 +13,7 @@ namespace jwm { Pointer(wl_pointer* pointer, jwm::WindowManagerWayland* wm); ~Pointer(); - wl_pointer* _pointer; + wl_pointer* _pointer = nullptr; wl_pointer* getPointer() const { return _pointer; } @@ -23,12 +23,12 @@ namespace jwm { return _serial; } - wl_surface* _surface; + wl_surface* _surface = nullptr; wl_surface* getSurface() const { return _surface; } - WindowWayland* _focusedSurface; + WindowWayland* _focusedSurface = nullptr; WindowWayland* getFocusedSurface() const { return _focusedSurface; } @@ -54,6 +54,9 @@ namespace jwm { wl_cursor_theme* getThemeFor(int scale); wl_cursor* getCursorFor(int scale, jwm::MouseCursor cursor); + static bool ownPointer(wl_pointer* pointer); + + private: Pointer(const Pointer& other) = delete; Pointer(Pointer&&) = delete; diff --git a/wayland/cc/WindowManagerWayland.cc b/wayland/cc/WindowManagerWayland.cc index 64c5e893..fe2749b2 100644 --- a/wayland/cc/WindowManagerWayland.cc +++ b/wayland/cc/WindowManagerWayland.cc @@ -86,7 +86,11 @@ void WindowManagerWayland::runLoop() { while (_runLoop) { if (jwm::classes::Throwable::exceptionThrown(app.getJniEnv())) _runLoop = false; + while (wl_display_prepare_read(display) != 0) { + wl_display_dispatch_pending(display); + } + wl_display_flush(display); // block until event : ) int timeout = -1; if (_keyboard && _keyboard->_repeating && _keyboard->getFocus()) { @@ -102,14 +106,13 @@ void WindowManagerWayland::runLoop() { } if (poll(&ps[0], 2, timeout) < 0) { printf("error with pipe\n"); + wl_display_cancel_read(display); break; } if (ps[0].revents & POLLIN) { - if (libdecor_dispatch(decorCtx, -1) < 0) { - fprintf(stderr, "error with dispatch\n"); - // ??? - break; - } + wl_display_read_events(display); + } else { + wl_display_cancel_read(display); } if (ps[1].revents & POLLIN) { @@ -248,10 +251,8 @@ void WindowManagerWayland::registryHandleGlobalRemove(void* data, wl_registry *r WindowWayland* WindowManagerWayland::getWindowForNative(wl_surface* surface) { if (!surface) return nullptr; // the tag makes it safe. Should:TM: be faster than searching a list every time - const char* const* tag = wl_proxy_get_tag((wl_proxy*) surface); - if (tag != &WindowWayland::_windowTag) { + if (!WindowWayland::ownSurface(surface)) return nullptr; - } return reinterpret_cast(wl_surface_get_user_data(surface)); /* WindowWayland* myWindow = nullptr; diff --git a/wayland/cc/WindowWayland.cc b/wayland/cc/WindowWayland.cc index 569c6a66..152c27bf 100644 --- a/wayland/cc/WindowWayland.cc +++ b/wayland/cc/WindowWayland.cc @@ -28,8 +28,6 @@ libdecor_frame_interface WindowWayland::_libdecorFrameInterface = { .dismiss_popup = WindowWayland::decorFrameDismissPopup }; -const char* WindowWayland::_windowTag = "WindowWayland"; - WindowWayland::WindowWayland(JNIEnv* env, WindowManagerWayland& windowManager): jwm::Window(env), _windowManager(windowManager), @@ -57,12 +55,13 @@ void WindowWayland::setTitlebarVisible(bool isVisible) { } void WindowWayland::close() { - _windowManager.unregisterWindow(this); + _closed = true; hide(); if (_waylandWindow) { wl_surface_destroy(_waylandWindow); } _waylandWindow = nullptr; + _windowManager.unregisterWindow(this); // TODO: more destruction! } void WindowWayland::hide() { @@ -179,7 +178,7 @@ bool WindowWayland::init() { wl_surface_add_listener(_waylandWindow, &_surfaceListener, this); // unsure if listener data and user data are the same, so i do this for safety : ) wl_surface_set_user_data(_waylandWindow, this); - wl_proxy_set_tag((wl_proxy*) _waylandWindow, &_windowTag); + wl_proxy_set_tag((wl_proxy*) _waylandWindow, &AppWayland::proxyTag); _windowManager.registerWindow(this); _configured = false; @@ -190,6 +189,8 @@ void WindowWayland::show() _frame = libdecor_decorate(_windowManager.decorCtx, _waylandWindow, &_libdecorFrameInterface, this); libdecor_frame_map(_frame); wl_display_roundtrip(_windowManager.display); + + setTitle(_title); setTitlebarVisible(_titlebarVisible); _visible = true; @@ -248,6 +249,13 @@ void jwm::WindowWayland::surfaceEnter(void* data, wl_surface* surface, wl_output self->_adaptSize(self->getUnscaledWidth(), self->getUnscaledHeight()); } } +bool jwm::WindowWayland::isNativeSelf(wl_surface* surface) { + if (!_waylandWindow) return false; + return surface == _waylandWindow; +} +bool jwm::WindowWayland::ownSurface(wl_surface* surface) { + return AppWayland::ownProxy((wl_proxy*) surface); +} void jwm::WindowWayland::surfaceLeave(void* data, wl_surface* surface, wl_output* output) { auto self = reinterpret_cast(data); diff --git a/wayland/cc/WindowWayland.hh b/wayland/cc/WindowWayland.hh index 264aaec4..f27edc5f 100644 --- a/wayland/cc/WindowWayland.hh +++ b/wayland/cc/WindowWayland.hh @@ -12,6 +12,7 @@ namespace jwm { class WindowWayland: public jwm::Window { public: WindowWayland(JNIEnv* env, WindowManagerWayland& windowManager); + WindowWayland() = delete; ~WindowWayland() override; void getDecorations(int& left, int& top, int& right, int& bottom); @@ -67,6 +68,9 @@ namespace jwm { void _adaptSize(int newWidth, int newHeight); + bool isNativeSelf(wl_surface* surface); + static bool ownSurface(wl_surface* surface); + int _posX = -1; int _posY = -1; @@ -93,6 +97,12 @@ namespace jwm { std::string _title; bool _titlebarVisible = true; + bool _closed = false; + + bool isClosed() const { + return _closed; + } + WindowManagerWayland& _windowManager; ILayerWayland* _layer = nullptr; wl_surface* _waylandWindow = nullptr; @@ -106,7 +116,5 @@ namespace jwm { static wl_callback_listener _frameCallback; - static const char* _windowTag; - }; } From 32a841d6b5223db252c92c00b3896edccde1d995 Mon Sep 17 00:00:00 2001 From: BulbyVR <26726264+TheDrawingCoder-Gamer@users.noreply.github.com> Date: Fri, 22 Dec 2023 21:09:43 -0500 Subject: [PATCH 56/95] Dont trust EGL size --- wayland/cc/ILayerWayland.cc | 9 +++++ wayland/cc/ILayerWayland.hh | 5 ++- wayland/cc/LayerGLWayland.cc | 48 +++++++++++++++++++------ wayland/cc/LayerRasterWayland.cc | 1 + wayland/cc/WindowManagerWayland.cc | 57 ++++++++++++++++++------------ wayland/cc/WindowManagerWayland.hh | 2 +- wayland/cc/WindowWayland.cc | 47 ++++++++++++------------ wayland/cc/WindowWayland.hh | 1 + 8 files changed, 110 insertions(+), 60 deletions(-) create mode 100644 wayland/cc/ILayerWayland.cc diff --git a/wayland/cc/ILayerWayland.cc b/wayland/cc/ILayerWayland.cc new file mode 100644 index 00000000..d892979c --- /dev/null +++ b/wayland/cc/ILayerWayland.cc @@ -0,0 +1,9 @@ +#include "ILayerWayland.hh" +#include +#include "WindowWayland.hh" + +void jwm::ILayerWayland::detachBuffer() { + if (fWindow && fWindow->_waylandWindow) { + wl_surface_set_buffer_scale(fWindow->_waylandWindow, 1); + } +} diff --git a/wayland/cc/ILayerWayland.hh b/wayland/cc/ILayerWayland.hh index 2e10e226..054abeb5 100644 --- a/wayland/cc/ILayerWayland.hh +++ b/wayland/cc/ILayerWayland.hh @@ -3,10 +3,13 @@ #include namespace jwm { + class WindowWayland; class ILayerWayland: public ILayer { public: + WindowWayland* fWindow = nullptr; + virtual void attachBuffer() = 0; virtual void swapBuffers() = 0; - virtual void detachBuffer() = 0; + virtual void detachBuffer(); }; } diff --git a/wayland/cc/LayerGLWayland.cc b/wayland/cc/LayerGLWayland.cc index 9b4c7ada..d31acc13 100644 --- a/wayland/cc/LayerGLWayland.cc +++ b/wayland/cc/LayerGLWayland.cc @@ -22,11 +22,16 @@ namespace jwm { EGLDisplay _display = nullptr; EGLSurface _surface = nullptr; EGLConfig _config = nullptr; + bool _closed = false; LayerGL() = default; virtual ~LayerGL() = default; void attach(WindowWayland* window) { + if (_closed) { + fprintf(stderr, "already closed\n"); + throw std::runtime_error("Already closed"); + } fWindow = jwm::ref(window); fWindow->setLayer(this); if (fWindow->_windowManager._eglDisplay == EGL_NO_DISPLAY) { @@ -67,8 +72,11 @@ namespace jwm { throw std::runtime_error("Couldn't make context"); } } + if (fWindow->_waylandWindow) + wl_surface_set_buffer_scale(fWindow->_waylandWindow, 1); if (fWindow->_configured) attachBuffer(); + makeCurrentForced(); } void setVsyncMode(VSync v) override { @@ -76,29 +84,42 @@ namespace jwm { } void resize(int width, int height) override { + if (!_surface || !_eglWindow) return; // Make current to avoid artifacts in other windows - makeCurrent(); + makeCurrentForced(); glClearStencil(0); glClearColor(0, 0, 0, 255); glStencilMask(0xffffffff); glClear(GL_STENCIL_BUFFER_BIT | GL_COLOR_BUFFER_BIT); - - // ??? + glViewport(0, 0, width, height); // God is dead if _eglWindow is null - if (_eglWindow) - wl_egl_window_resize(_eglWindow, width, height, 0, 0); - - wl_surface_set_buffer_scale(fWindow->_waylandWindow, fWindow->_scale); + if (_eglWindow && fWindow && fWindow->_waylandWindow) { + // HACK: make new window with new scale + // https://gitlab.freedesktop.org/mesa/mesa/-/issues/7217 + if (fWindow->_scale != fWindow->_oldScale) { + fprintf(stderr, "HACK: remaking egl window\n"); + detachBuffer(); + attachBuffer(); + } else wl_egl_window_resize(_eglWindow, width, height, 0, 0); + wl_surface_set_buffer_scale(fWindow->_waylandWindow, fWindow->getIntScale()); + fWindow->_oldScale = fWindow->_scale; + } } void swapBuffers() override { - makeCurrent(); - if (_surface) + if (_surface) { + makeCurrent(); eglSwapBuffers(_display, _surface); + } } void close() override { + if (_closed) { + fprintf(stderr, "already closed\n"); + return; + } + _closed = true; detachBuffer(); eglDestroyContext(_display, _context); @@ -125,16 +146,21 @@ namespace jwm { if (fWindow && fWindow->_waylandWindow) { if (!_eglWindow) { _eglWindow = wl_egl_window_create(fWindow->_waylandWindow, fWindow->getWidth(), fWindow->getHeight()); + + if (_eglWindow == nullptr) { + fprintf(stderr, "failed to get window\n"); + } _surface = eglCreateWindowSurface(_display, _config, _eglWindow, nullptr); if ( _surface == EGL_NO_SURFACE ) { - throw std::runtime_error("couldn't get surface"); + fprintf(stderr, "failed to get surface\n"); } + makeCurrentForced(); } - makeCurrentForced(); } } void detachBuffer() override { + ILayerWayland::detachBuffer(); if (_surface) { eglDestroySurface(_display, _surface); } diff --git a/wayland/cc/LayerRasterWayland.cc b/wayland/cc/LayerRasterWayland.cc index b4bd047e..5502b544 100644 --- a/wayland/cc/LayerRasterWayland.cc +++ b/wayland/cc/LayerRasterWayland.cc @@ -81,6 +81,7 @@ namespace jwm { } void detachBuffer() override { + ILayerWayland::detachBuffer(); if (_attached && fWindow && fWindow->_waylandWindow) { wl_surface_attach(fWindow->_waylandWindow, nullptr, 0, 0); // commit is not meant to be used in intermediate states diff --git a/wayland/cc/WindowManagerWayland.cc b/wayland/cc/WindowManagerWayland.cc index fe2749b2..1d174fce 100644 --- a/wayland/cc/WindowManagerWayland.cc +++ b/wayland/cc/WindowManagerWayland.cc @@ -1,5 +1,6 @@ #include "WindowManagerWayland.hh" #include "WindowWayland.hh" +#include #include #include #include @@ -15,7 +16,6 @@ #include #include "Log.hh" #include -#include #include "Output.hh" #include #include @@ -77,8 +77,9 @@ void WindowManagerWayland::runLoop() { } notifyFD = pipes[1]; fcntl(pipes[1], F_SETFL, O_NONBLOCK); // notifyLoop no blockie : ) + struct pollfd wayland_out = {.fd=wl_display_get_fd(display),.events=POLLOUT}; struct pollfd ps[] = { - {.fd=libdecor_get_fd(decorCtx), .events=POLLIN}, + {.fd=wl_display_get_fd(display), .events=POLLIN}, {.fd=pipes[0], .events=POLLIN}, }; @@ -90,7 +91,27 @@ void WindowManagerWayland::runLoop() { wl_display_dispatch_pending(display); } - wl_display_flush(display); + while (true) { + int res = wl_display_flush(display); + if (res >= 0) + break; + + switch (errno) { + case EPIPE: + wl_display_read_events(display); + throw std::system_error(errno, std::generic_category(), "connection to wayland server unexpectedly terminated"); + break; + case EAGAIN: + if (poll(&wayland_out, 1, -1) < 0) { + throw std::system_error(EPIPE, std::generic_category(), "poll failed"); + } + break; + default: + throw std::system_error(errno, std::generic_category(), "failed to flush requests"); + break; + } + + } // block until event : ) int timeout = -1; if (_keyboard && _keyboard->_repeating && _keyboard->getFocus()) { @@ -110,7 +131,10 @@ void WindowManagerWayland::runLoop() { break; } if (ps[0].revents & POLLIN) { - wl_display_read_events(display); + if (wl_display_read_events(display) < 0) { + std::perror("events failed"); + break; + } } else { wl_display_cancel_read(display); } @@ -124,7 +148,8 @@ void WindowManagerWayland::runLoop() { // don't test if we already calculated earlier _processKeyboard(); } - + + wl_display_dispatch_pending(display); notifyBool.store(false); } @@ -152,11 +177,7 @@ void WindowManagerWayland::_processCallbacks() { } } { - // copy window list in case one closes any other, invalidating some iterator in _nativeWindowToMy - std::vector copy; - for (auto& p : _nativeWindowToMy) { - copy.push_back(p.second); - } + std::list copy(_windows); // process redraw requests for (auto p : copy) { if (p->isRedrawRequested()) { @@ -254,13 +275,6 @@ WindowWayland* WindowManagerWayland::getWindowForNative(wl_surface* surface) { if (!WindowWayland::ownSurface(surface)) return nullptr; return reinterpret_cast(wl_surface_get_user_data(surface)); - /* - WindowWayland* myWindow = nullptr; - auto it = _nativeWindowToMy.find(surface); - if (it != _nativeWindowToMy.end()) - myWindow = it->second; - return myWindow; - */ } void WindowManagerWayland::seatCapabilities(void* data, wl_seat* seat, uint32_t capabilities) { auto self = reinterpret_cast(data); @@ -347,13 +361,13 @@ jwm::ByteBuf WindowManagerWayland::getClipboardContents(const std::string& type) } void WindowManagerWayland::registerWindow(WindowWayland* window) { - _nativeWindowToMy[window->_waylandWindow] = window; + _windows.push_back(window); } void WindowManagerWayland::unregisterWindow(WindowWayland* window) { - auto it = _nativeWindowToMy.find(window->_waylandWindow); - if (it != _nativeWindowToMy.end()) { - _nativeWindowToMy.erase(it); + auto it = std::find(_windows.begin(), _windows.end(), window); + if (it != _windows.end()) { + _windows.erase(it); } } @@ -380,7 +394,6 @@ wl_data_source_listener WindowManagerWayland::_sourceListener = { .cancelled = dataSourceCancelled }; void WindowManagerWayland::setClipboardContents(std::map&& c) { - assert(("create at least one window in order to use clipboard" && !_nativeWindowToMy.empty())); _myClipboardSource = c; if (!deviceManager) return; diff --git a/wayland/cc/WindowManagerWayland.hh b/wayland/cc/WindowManagerWayland.hh index 173b9bfc..b2f395ed 100644 --- a/wayland/cc/WindowManagerWayland.hh +++ b/wayland/cc/WindowManagerWayland.hh @@ -121,7 +121,7 @@ namespace jwm { int notifyFD = -1; std::atomic_bool notifyBool{false}; - std::map _nativeWindowToMy; + std::list _windows; std::map _myClipboardContents; std::map _myClipboardSource; std::list _currentMimeTypes; diff --git a/wayland/cc/WindowWayland.cc b/wayland/cc/WindowWayland.cc index 152c27bf..a2a81ceb 100644 --- a/wayland/cc/WindowWayland.cc +++ b/wayland/cc/WindowWayland.cc @@ -57,11 +57,6 @@ void WindowWayland::setTitlebarVisible(bool isVisible) { void WindowWayland::close() { _closed = true; hide(); - if (_waylandWindow) { - wl_surface_destroy(_waylandWindow); - } - _waylandWindow = nullptr; - _windowManager.unregisterWindow(this); // TODO: more destruction! } void WindowWayland::hide() { @@ -69,10 +64,15 @@ void WindowWayland::hide() { if (_layer) { _layer->detachBuffer(); } + if (_waylandWindow) { + wl_surface_destroy(_waylandWindow); + } + _waylandWindow = nullptr; if (_frame) { libdecor_frame_unref(_frame); } _frame = nullptr; + _windowManager.unregisterWindow(this); _configured = false; } void WindowWayland::maximize() { @@ -131,12 +131,6 @@ bool WindowWayland::resize(int width, int height) { return false; _width = width / _scale; _height = height / _scale; - /* - if (_visible) { - _adaptSize(_width, _height); - } - */ - _oldScale = _scale; return true; } @@ -166,6 +160,9 @@ int WindowWayland::getUnscaledHeight() { } float WindowWayland::getScale() { + return getIntScale(); +} +int WindowWayland::getIntScale() { return _scale; } wl_cursor* WindowWayland::_getCursorFor(jwm::MouseCursor cursor) { @@ -174,25 +171,21 @@ wl_cursor* WindowWayland::_getCursorFor(jwm::MouseCursor cursor) { return nullptr; } bool WindowWayland::init() { - _waylandWindow = wl_compositor_create_surface(_windowManager.compositor); - wl_surface_add_listener(_waylandWindow, &_surfaceListener, this); - // unsure if listener data and user data are the same, so i do this for safety : ) - wl_surface_set_user_data(_waylandWindow, this); - wl_proxy_set_tag((wl_proxy*) _waylandWindow, &AppWayland::proxyTag); - - _windowManager.registerWindow(this); - _configured = false; return true; } void WindowWayland::show() { - _frame = libdecor_decorate(_windowManager.decorCtx, _waylandWindow, &_libdecorFrameInterface, this); - libdecor_frame_map(_frame); + _waylandWindow = wl_compositor_create_surface(_windowManager.compositor); + wl_surface_add_listener(_waylandWindow, &_surfaceListener, this); + wl_proxy_set_tag((wl_proxy*) _waylandWindow, &AppWayland::proxyTag); + _windowManager.registerWindow(this); wl_display_roundtrip(_windowManager.display); - - + _frame = libdecor_decorate(_windowManager.decorCtx, _waylandWindow, &_libdecorFrameInterface, this); setTitle(_title); setTitlebarVisible(_titlebarVisible); + libdecor_frame_map(_frame); + + _configured = false; _visible = true; } @@ -202,7 +195,7 @@ ScreenInfo WindowWayland::getScreen() { } else { return { .id = -1, - .bounds = jwm::IRect::makeXYWH(0, 0, _width, _height), + .bounds = jwm::IRect::makeXYWH(0, 0, getWidth(), getHeight()), .isPrimary = false, .scale = _scale }; @@ -342,6 +335,7 @@ void jwm::WindowWayland::decorFrameConfigure(libdecor_frame* frame, libdecor_con if (self->_layer) self->_layer->attachBuffer(); } + self->_configured = true; if (self->_width != width || self->_height != height) { if (libdecor_frame_is_floating(frame)) { if (width > 0) @@ -353,7 +347,6 @@ void jwm::WindowWayland::decorFrameConfigure(libdecor_frame* frame, libdecor_con self->_adaptSize(width, height); } - self->_configured = true; } void jwm::WindowWayland::decorFrameClose(libdecor_frame* frame, void* userData) { WindowWayland* self = reinterpret_cast(userData); @@ -368,6 +361,10 @@ void jwm::WindowWayland::decorFrameCommit(libdecor_frame* frame, void* userData) void jwm::WindowWayland::decorFrameDismissPopup(libdecor_frame* frame, const char* seatName, void* userData) {} void jwm::WindowWayland::_adaptSize(int newWidth, int newHeight) { using namespace classes; + if (!_configured) { + printf("???\n"); + return; + } if (newWidth == _width && newHeight == _height && _scale == _oldScale) return; _width = newWidth; _height = newHeight; diff --git a/wayland/cc/WindowWayland.hh b/wayland/cc/WindowWayland.hh index f27edc5f..a881c5c9 100644 --- a/wayland/cc/WindowWayland.hh +++ b/wayland/cc/WindowWayland.hh @@ -30,6 +30,7 @@ namespace jwm { int getHeight(); int getUnscaledHeight(); float getScale(); + int getIntScale(); void requestRedraw() { _isRedrawRequested = true; _windowManager.notifyLoop(); From 7c95878d9c15f8a8c0c3259536da9696b33e111b Mon Sep 17 00:00:00 2001 From: TheDrawingCoding-Gamer Date: Fri, 22 Dec 2023 21:47:37 -0500 Subject: [PATCH 57/95] do memleak frame --- wayland/cc/WindowWayland.cc | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/wayland/cc/WindowWayland.cc b/wayland/cc/WindowWayland.cc index a2a81ceb..a697738f 100644 --- a/wayland/cc/WindowWayland.cc +++ b/wayland/cc/WindowWayland.cc @@ -68,9 +68,14 @@ void WindowWayland::hide() { wl_surface_destroy(_waylandWindow); } _waylandWindow = nullptr; + // HACK: memory corruption issue in libdecor: + // https://gitlab.freedesktop.org/libdecor/libdecor/-/issues/59 + // Also causes protocol error that doesn't really make sense + /* if (_frame) { libdecor_frame_unref(_frame); } + */ _frame = nullptr; _windowManager.unregisterWindow(this); _configured = false; From 980ad0df8effbd1e794d1edad6d426db25eec96c Mon Sep 17 00:00:00 2001 From: TheDrawingCoding-Gamer Date: Sat, 23 Dec 2023 13:27:14 -0500 Subject: [PATCH 58/95] grab pointer and keyboard as unique_ptr --- wayland/cc/WindowManagerWayland.cc | 8 ++++---- wayland/cc/WindowManagerWayland.hh | 28 ++++------------------------ 2 files changed, 8 insertions(+), 28 deletions(-) diff --git a/wayland/cc/WindowManagerWayland.cc b/wayland/cc/WindowManagerWayland.cc index 1d174fce..6af927df 100644 --- a/wayland/cc/WindowManagerWayland.cc +++ b/wayland/cc/WindowManagerWayland.cc @@ -280,17 +280,17 @@ void WindowManagerWayland::seatCapabilities(void* data, wl_seat* seat, uint32_t auto self = reinterpret_cast(data); if ((capabilities & WL_SEAT_CAPABILITY_POINTER) && !self->_pointer) { - self->_pointer = new Pointer(wl_seat_get_pointer(seat), self); + self->_pointer.reset(new Pointer(wl_seat_get_pointer(seat), self)); } else if (!(capabilities & WL_SEAT_CAPABILITY_POINTER) && self->_pointer) { - self->_pointer = nullptr; + self->_pointer.reset(); } if ((capabilities & WL_SEAT_CAPABILITY_KEYBOARD) && !self->_keyboard) { - self->_keyboard = new Keyboard(wl_seat_get_keyboard(seat), self); + self->_keyboard.reset(new Keyboard(wl_seat_get_keyboard(seat), self)); } else if (!(capabilities & WL_SEAT_CAPABILITY_KEYBOARD) && self->_keyboard) { - self->_keyboard = nullptr; + self->_keyboard.reset(); } } void WindowManagerWayland::seatName(void* data, wl_seat* seat, const char* name) { diff --git a/wayland/cc/WindowManagerWayland.hh b/wayland/cc/WindowManagerWayland.hh index b2f395ed..32c1030c 100644 --- a/wayland/cc/WindowManagerWayland.hh +++ b/wayland/cc/WindowManagerWayland.hh @@ -20,6 +20,7 @@ #include #include "Keyboard.hh" #include "Pointer.hh" +#include namespace jwm { class WindowWayland; @@ -33,27 +34,6 @@ namespace jwm { void registerWindow(WindowWayland* window); void unregisterWindow(WindowWayland* window); - // XVisualInfo* pickVisual(); - // static int _xerrorhandler(Display* dsp, XErrorEvent* error); - // void _xi2IterateDevices(); - - // XVisualInfo* getVisualInfo() const { return x11VisualInfo; } - // XSetWindowAttributes& getSWA() { return x11SWA; } - // XIM getIM() const { return _im; } - /* - int getX11VisualDepth() const { - if (x11VisualInfo) { - return x11VisualInfo->depth; - } - return DefaultDepth(display, 0); - } - Visual* getX11Visual() const { - if (x11VisualInfo) { - return x11VisualInfo->visual; - } - return DefaultVisual(display, 0); - } - */ void _processCallbacks(); void _processKeyboard(); void notifyLoop(); @@ -90,11 +70,11 @@ namespace jwm { wl_data_device_manager* deviceManager = nullptr; // no multiseat? wl_seat* seat = nullptr; - Pointer* _pointer = nullptr; + std::unique_ptr _pointer = nullptr; Pointer* getPointer() const { - return _pointer; + return _pointer.get(); } - Keyboard* _keyboard = nullptr; + std::unique_ptr _keyboard = nullptr; wl_data_device* dataDevice = nullptr; wl_data_source* currentSource = nullptr; wl_data_offer* currentOffer = nullptr; From ba5192e28d1d9b6c85fb57689fa52ac74153edfb Mon Sep 17 00:00:00 2001 From: TheDrawingCoding-Gamer Date: Sat, 23 Dec 2023 14:09:49 -0500 Subject: [PATCH 59/95] scroll fix --- wayland/cc/Pointer.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wayland/cc/Pointer.cc b/wayland/cc/Pointer.cc index 75564572..cfabdd0a 100644 --- a/wayland/cc/Pointer.cc +++ b/wayland/cc/Pointer.cc @@ -131,7 +131,7 @@ static void pointerAxis(void* data, wl_pointer* pointer, uint32_t time, float fvalue = static_cast(wl_fixed_to_double(value)); switch (axis) { case WL_POINTER_AXIS_VERTICAL_SCROLL: - self->_dY += fvalue; + self->_dY += -fvalue; break; case WL_POINTER_AXIS_HORIZONTAL_SCROLL: self->_dX += fvalue; From aae53240c113b41bf1385c821d68e79e10a4d277 Mon Sep 17 00:00:00 2001 From: TheDrawingCoding-Gamer Date: Sat, 23 Dec 2023 21:00:59 -0500 Subject: [PATCH 60/95] lock cursor --- wayland/CMakeLists.txt | 31 ++++++++++++++++++++----- wayland/cc/Pointer.cc | 36 ++++++++++++++++++++++++++++++ wayland/cc/Pointer.hh | 9 ++++++++ wayland/cc/WindowManagerWayland.cc | 3 +++ wayland/cc/WindowManagerWayland.hh | 4 ++++ wayland/cc/WindowWayland.cc | 18 +++++++++++++++ wayland/cc/WindowWayland.hh | 2 ++ wayland/java/WindowWayland.java | 4 +++- 8 files changed, 101 insertions(+), 6 deletions(-) diff --git a/wayland/CMakeLists.txt b/wayland/CMakeLists.txt index 47a98501..8c19fef3 100644 --- a/wayland/CMakeLists.txt +++ b/wayland/CMakeLists.txt @@ -1,9 +1,24 @@ -cmake_minimum_required(VERSION 3.9) - +cmake_minimum_required(VERSION 3.16) # prefer the newer GL library (GLVND) cmake_policy(SET CMP0072 NEW) +find_package(ECM REQUIRED NO_MODULE) +set(CMAKE_MODULE_PATH ${ECM_MODULE_PATH}) + +include(FindWaylandProtocols) +include(FindWaylandScanner) +find_package(WaylandProtocols 1.32) +set_package_properties(WaylandProtocols PROPERTIES + TYPE REQUIRED +) +if (NOT WaylandProtocols_FOUND) + message(FATAL_ERROR "No protocols installed") +endif() +if (NOT WaylandScanner_FOUND) + message(FATAL_ERROR "No wayland-scanner") +endif() project(jwm LANGUAGES CXX) +project(cursorlock LANGUAGES C) set(CMAKE_CXX_STANDARD 14) set(CMAKE_CXX_STANDARD_REQUIRED ON) @@ -20,13 +35,18 @@ file(GLOB SOURCES_CXX ${CMAKE_CURRENT_LIST_DIR}/../shared/cc/*.cc ${CMAKE_CURRENT_LIST_DIR}/cc/*.cc ) file(GLOB SOURCES_CXX_IMPL ${CMAKE_CURRENT_LIST_DIR}/../shared/cc/impl/*.cc) add_library(jwm SHARED ${SOURCES_OBJC} ${SOURCES_CXX} ${SOURCES_CXX_IMPL}) +add_library(cursorlock STATIC) +# set_property(TARGET cursorlock PROPERTY POSITION_INDEPENDENT_CODE ON) +ecm_add_wayland_client_protocol(cursorlock + PROTOCOL "${WaylandProtocols_DATADIR}/unstable/pointer-constraints/pointer-constraints-unstable-v1.xml" + BASENAME pointer-constraints-unstable-v1 +) find_library(WAYLAND_CLIENT_LIB wayland-client) find_library(DECOR_LIB decor-0) find_library(WAYLAND_CURSOR wayland-cursor) find_library(XKBCOMMON xkbcommon) find_library(EGL EGL) find_library(WAYLAND_EGL wayland-egl) -# find_library(OPENGL_ES2 GLESv2) find_package(OpenGL REQUIRED) set(JAVA_HOME $ENV{JAVA_HOME}) if (NOT JAVA_HOME) @@ -39,11 +59,12 @@ if (NOT JAVA_HOME) endif() endif() -target_include_directories(jwm PRIVATE ${CMAKE_CURRENT_LIST_DIR}/../shared/cc ${CMAKE_CURRENT_LIST_DIR}/../linux/cc ${JAVA_HOME}/include ${JAVA_HOME}/include/linux) +target_include_directories(jwm PRIVATE ${CMAKE_CURRENT_LIST_DIR}/../shared/cc ${CMAKE_CURRENT_LIST_DIR}/../linux/cc ${JAVA_HOME}/include ${JAVA_HOME}/include/linux + ${CMAKE_CURRENT_LIST_DIR}/build) set_target_properties(jwm PROPERTIES OUTPUT_NAME "jwm_${JWM_ARCH}_wayland") target_link_libraries(jwm PRIVATE ${WAYLAND_CLIENT_LIB} ${DECOR_LIB} ${WAYLAND_CURSOR} ${XKBCOMMON}) target_link_libraries(jwm PRIVATE ${EGL} ${WAYLAND_EGL}) target_link_libraries(jwm PRIVATE OpenGL::GL) - +target_link_libraries(jwm PRIVATE cursorlock) diff --git a/wayland/cc/Pointer.cc b/wayland/cc/Pointer.cc index cfabdd0a..e774b821 100644 --- a/wayland/cc/Pointer.cc +++ b/wayland/cc/Pointer.cc @@ -287,3 +287,39 @@ wl_cursor* Pointer::getCursorFor(int scale, jwm::MouseCursor cursor) { bool Pointer::ownPointer(wl_pointer* pointer) { return AppWayland::ownProxy((wl_proxy*) pointer); } + +static void lockLocked(void* data, zwp_locked_pointer_v1* pointer) { + auto self = reinterpret_cast(data); + + self->_locked = true; +} +static void lockUnlocked(void* data, zwp_locked_pointer_v1* pointer) { + zwp_locked_pointer_v1_destroy(pointer); + + auto self = reinterpret_cast(data); + + self->_locked = false; + self->_lock = nullptr; +} + +static zwp_locked_pointer_v1_listener lockListener = { + .locked = lockLocked, + .unlocked = lockUnlocked +}; +void Pointer::lock() { + if (_wm.pointerConstraints && _focusedSurface && _focusedSurface->_waylandWindow) { + if (_lock) { + zwp_locked_pointer_v1_destroy(_lock); + } + _lock = zwp_pointer_constraints_v1_lock_pointer(_wm.pointerConstraints, _focusedSurface->_waylandWindow, + _pointer, nullptr, ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_ONESHOT); + zwp_locked_pointer_v1_add_listener(_lock, &lockListener, this); + } +} +void Pointer::unlock() { + if (_lock) { + zwp_locked_pointer_v1_destroy(_lock); + } + _lock = nullptr; + _locked = false; +} diff --git a/wayland/cc/Pointer.hh b/wayland/cc/Pointer.hh index 41ee7c96..fbaef4f4 100644 --- a/wayland/cc/Pointer.hh +++ b/wayland/cc/Pointer.hh @@ -4,6 +4,7 @@ #include #include #include "MouseCursor.hh" +#include namespace jwm { class WindowManagerWayland; @@ -33,6 +34,14 @@ namespace jwm { return _focusedSurface; } + zwp_locked_pointer_v1* _lock = nullptr; + bool _locked = false; + void lock(); + void unlock(); + bool isLocked() { + return _locked; + } + uint32_t _lastMouseX; uint32_t _lastMouseY; float _dX = 0.0; diff --git a/wayland/cc/WindowManagerWayland.cc b/wayland/cc/WindowManagerWayland.cc index 6af927df..5d6136c7 100644 --- a/wayland/cc/WindowManagerWayland.cc +++ b/wayland/cc/WindowManagerWayland.cc @@ -257,6 +257,9 @@ void WindowManagerWayland::registryHandleGlobal(void* data, wl_registry *registr // ??? this is a race condition (probably) but i have to do this to prevent crashes good->primary = true; self->outputs.push_back(good); + } else if (strcmp(interface, zwp_pointer_constraints_v1_interface.name) == 0) { + self->pointerConstraints = (zwp_pointer_constraints_v1*)wl_registry_bind(registry, name, + &zwp_pointer_constraints_v1_interface, 1); } } void WindowManagerWayland::registryHandleGlobalRemove(void* data, wl_registry *registry, uint32_t name) { diff --git a/wayland/cc/WindowManagerWayland.hh b/wayland/cc/WindowManagerWayland.hh index 32c1030c..235ae912 100644 --- a/wayland/cc/WindowManagerWayland.hh +++ b/wayland/cc/WindowManagerWayland.hh @@ -21,6 +21,7 @@ #include "Keyboard.hh" #include "Pointer.hh" #include +#include namespace jwm { class WindowWayland; @@ -70,6 +71,7 @@ namespace jwm { wl_data_device_manager* deviceManager = nullptr; // no multiseat? wl_seat* seat = nullptr; + zwp_pointer_constraints_v1* pointerConstraints = nullptr; std::unique_ptr _pointer = nullptr; Pointer* getPointer() const { return _pointer.get(); @@ -93,6 +95,8 @@ namespace jwm { return _keyboard->getState(); return nullptr; } + + libdecor* decorCtx = nullptr; EGLDisplay _eglDisplay = EGL_NO_DISPLAY; std::list outputs; diff --git a/wayland/cc/WindowWayland.cc b/wayland/cc/WindowWayland.cc index a697738f..0537694b 100644 --- a/wayland/cc/WindowWayland.cc +++ b/wayland/cc/WindowWayland.cc @@ -390,6 +390,17 @@ void jwm::WindowWayland::_adaptSize(int newWidth, int newHeight) { // however decorFrameCommit will cause a redraw anyway. // Not doing it in wayland lets me not cause an exception on hide. } + +void jwm::WindowWayland::lockCursor(bool locked) { + auto pointer = _windowManager.getPointer(); + if (!pointer) return; + if (pointer->getFocusedSurface() == this) { + if (locked) + pointer->lock(); + else + pointer->unlock(); + } +} // JNI extern "C" JNIEXPORT jlong JNICALL Java_io_github_humbleui_jwm_WindowWayland__1nMake @@ -521,3 +532,10 @@ extern "C" JNIEXPORT jfloat JNICALL Java_io_github_humbleui_jwm_WindowWayland__1 jwm::WindowWayland* instance = reinterpret_cast(jwm::classes::Native::fromJava(env, obj)); return instance->getScale(); } + +extern "C" JNIEXPORT void JNICALL Java_io_github_humbleui_jwm_WindowWayland__1nLockMouseCursor + (JNIEnv* env, jobject obj, jboolean locked) +{ + jwm::WindowWayland* instance = reinterpret_cast(jwm::classes::Native::fromJava(env, obj)); + instance->lockCursor(locked); +} diff --git a/wayland/cc/WindowWayland.hh b/wayland/cc/WindowWayland.hh index a881c5c9..78b02ddf 100644 --- a/wayland/cc/WindowWayland.hh +++ b/wayland/cc/WindowWayland.hh @@ -72,6 +72,8 @@ namespace jwm { bool isNativeSelf(wl_surface* surface); static bool ownSurface(wl_surface* surface); + void lockCursor(bool locked); + int _posX = -1; int _posY = -1; diff --git a/wayland/java/WindowWayland.java b/wayland/java/WindowWayland.java index 74441875..4ac14ac7 100644 --- a/wayland/java/WindowWayland.java +++ b/wayland/java/WindowWayland.java @@ -96,7 +96,8 @@ public Window hideMouseCursorUntilMoved(boolean value) { @Override public Window lockMouseCursor(boolean value) { - // TODO impl me! + assert _onUIThread() : "Should be run on UI thread"; + _nLockMouseCursor(value); return this; } @@ -235,4 +236,5 @@ public float getScale() { @ApiStatus.Internal public native void _nSetFullScreen(boolean isFullScreen); @ApiStatus.Internal public native boolean _nIsFullScreen(); @ApiStatus.Internal public native float _nGetScale(); + @ApiStatus.Internal public native void _nLockMouseCursor(boolean locked); } From b2af1e5a124a77aa950d8b3bfecadac004507e42 Mon Sep 17 00:00:00 2001 From: TheDrawingCoding-Gamer Date: Sun, 24 Dec 2023 13:11:27 -0500 Subject: [PATCH 61/95] Movement, request new lock --- wayland/cc/Pointer.cc | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/wayland/cc/Pointer.cc b/wayland/cc/Pointer.cc index e774b821..bef1404a 100644 --- a/wayland/cc/Pointer.cc +++ b/wayland/cc/Pointer.cc @@ -21,6 +21,7 @@ static void pointerEnter(void* data, wl_pointer* pointer, uint32_t serial, self->_serial = serial; self->_focusedSurface = jwm::ref(window); window->setCursor(jwm::MouseCursor::ARROW); + self->mouseUpdateUnscaled(wl_fixed_to_int(surface_x), wl_fixed_to_int(surface_y), self->_mouseMask); } } @@ -209,9 +210,9 @@ void Pointer::mouseUpdate(uint32_t x, uint32_t y, uint32_t mask) { return; if (_lastMouseX == x && _lastMouseY == y) return; + int movementX = x - _lastMouseX, movementY = y - _lastMouseY; _lastMouseX = x; _lastMouseY = y; - int movementX = 0, movementY = 0; jwm::JNILocal eventMove( app.getJniEnv(), @@ -298,8 +299,11 @@ static void lockUnlocked(void* data, zwp_locked_pointer_v1* pointer) { auto self = reinterpret_cast(data); - self->_locked = false; self->_lock = nullptr; + if (self->_locked) { + // Request a new lock on surface reenter + self->lock(); + } } static zwp_locked_pointer_v1_listener lockListener = { From f6202e66ea1781401f2b87f985e3e99dae3cb2b9 Mon Sep 17 00:00:00 2001 From: TheDrawingCoding-Gamer Date: Sun, 24 Dec 2023 13:44:56 -0500 Subject: [PATCH 62/95] hide cursor, no primary screen --- shared/java/App.java | 5 ++++- wayland/cc/Output.cc | 2 +- wayland/cc/Output.hh | 1 - wayland/cc/Pointer.cc | 32 ++++++++++++++++++++++++++++++ wayland/cc/Pointer.hh | 12 +++++++++++ wayland/cc/WindowManagerWayland.cc | 5 +---- wayland/cc/WindowWayland.cc | 28 ++++++++++++++++---------- wayland/cc/WindowWayland.hh | 2 ++ wayland/java/WindowWayland.java | 4 +++- 9 files changed, 73 insertions(+), 18 deletions(-) diff --git a/shared/java/App.java b/shared/java/App.java index 7a1c5a76..3275eafd 100644 --- a/shared/java/App.java +++ b/shared/java/App.java @@ -100,12 +100,15 @@ public static Screen[] getScreens() { * * @return primary desktop screen */ + @Nullable public static Screen getPrimaryScreen() { assert _onUIThread() : "Should be run on UI thread"; + if (Platform.CURRENT == Platform.WAYLAND) + return null; for (Screen s: getScreens()) if (s.isPrimary()) return s; - throw new IllegalStateException("Can't find primary screen"); + return null; } public static void openSymbolsPalette() { diff --git a/wayland/cc/Output.cc b/wayland/cc/Output.cc index 4298d459..1cb9d0a2 100644 --- a/wayland/cc/Output.cc +++ b/wayland/cc/Output.cc @@ -29,7 +29,7 @@ ScreenInfo Output::getScreenInfo() const { return { .id = _name, .bounds = jwm::IRect::makeXYWH(0, 0, width, height), - .isPrimary = primary, + .isPrimary = false, .scale = scale }; } diff --git a/wayland/cc/Output.hh b/wayland/cc/Output.hh index 8ca461b9..b137d5a3 100644 --- a/wayland/cc/Output.hh +++ b/wayland/cc/Output.hh @@ -12,7 +12,6 @@ namespace jwm { wl_output* _output = nullptr; uint32_t _name; - bool primary = false; int scale = 1; int width = 0; int height = 0; diff --git a/wayland/cc/Pointer.cc b/wayland/cc/Pointer.cc index bef1404a..5b3122c0 100644 --- a/wayland/cc/Pointer.cc +++ b/wayland/cc/Pointer.cc @@ -45,6 +45,7 @@ static void pointerMotion(void* data, wl_pointer* pointer, uint32_t time, return; } auto self = reinterpret_cast(data); + self->unhide(); self->mouseUpdateUnscaled(wl_fixed_to_int(surface_x), wl_fixed_to_int(surface_y), self->_mouseMask); } @@ -147,6 +148,8 @@ static void pointerFrame(void* data, wl_pointer* pointer) auto self = reinterpret_cast(data); auto win = self->_focusedSurface; if (!win) return; + // this is always sent so I can safely issue an unhide here + self->unhide(); if (self->_dX != 0.0f || self->_dY != 0.0f) { auto env = app.getJniEnv(); @@ -318,12 +321,41 @@ void Pointer::lock() { _lock = zwp_pointer_constraints_v1_lock_pointer(_wm.pointerConstraints, _focusedSurface->_waylandWindow, _pointer, nullptr, ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_ONESHOT); zwp_locked_pointer_v1_add_listener(_lock, &lockListener, this); + hide(); } } void Pointer::unlock() { if (_lock) { zwp_locked_pointer_v1_destroy(_lock); } + unhide(); _lock = nullptr; _locked = false; } + +void Pointer::hide() { + if (_hidden) return; + _hidden = true; + if (_surface) { + wl_surface_attach(_surface, nullptr, 0, 0); + wl_surface_commit(_surface); + } +} + +void Pointer::unhide() { + if (!_hidden) return; + _hidden = false; + setCursor(_scale, _cursor); +} + +void Pointer::setCursor(int scale, jwm::MouseCursor cursor) { + _cursor = cursor; + _scale = scale; + auto wayCursor = getCursorFor(scale, cursor)->images[0]; + auto buf = wl_cursor_image_get_buffer(wayCursor); + wl_surface_attach(_surface, buf, 0, 0); + wl_surface_set_buffer_scale(_surface, scale); + wl_surface_damage_buffer(_surface, 0, 0, INT32_MAX, INT32_MAX); + updateHotspot(wayCursor->hotspot_x, wayCursor->hotspot_y); + wl_surface_commit(_surface); +} diff --git a/wayland/cc/Pointer.hh b/wayland/cc/Pointer.hh index fbaef4f4..35fb2f59 100644 --- a/wayland/cc/Pointer.hh +++ b/wayland/cc/Pointer.hh @@ -42,6 +42,13 @@ namespace jwm { return _locked; } + bool _hidden = false; + void hide(); + void unhide(); + bool isHidden() { + return _hidden; + } + uint32_t _lastMouseX; uint32_t _lastMouseY; float _dX = 0.0; @@ -63,6 +70,11 @@ namespace jwm { wl_cursor_theme* getThemeFor(int scale); wl_cursor* getCursorFor(int scale, jwm::MouseCursor cursor); + jwm::MouseCursor _cursor = jwm::MouseCursor::ARROW; + int _scale = 1; + + void setCursor(int scale, jwm::MouseCursor cursor); + static bool ownPointer(wl_pointer* pointer); diff --git a/wayland/cc/WindowManagerWayland.cc b/wayland/cc/WindowManagerWayland.cc index 5d6136c7..748eb4f5 100644 --- a/wayland/cc/WindowManagerWayland.cc +++ b/wayland/cc/WindowManagerWayland.cc @@ -253,10 +253,7 @@ void WindowManagerWayland::registryHandleGlobal(void* data, wl_registry *registr wl_output* output = (wl_output*)wl_registry_bind(registry, name, &wl_output_interface, 2); Output* good = new Output(output, name); - if (self->outputs.empty()) - // ??? this is a race condition (probably) but i have to do this to prevent crashes - good->primary = true; - self->outputs.push_back(good); + self->outputs.push_back(std::move(good)); } else if (strcmp(interface, zwp_pointer_constraints_v1_interface.name) == 0) { self->pointerConstraints = (zwp_pointer_constraints_v1*)wl_registry_bind(registry, name, &zwp_pointer_constraints_v1_interface, 1); diff --git a/wayland/cc/WindowWayland.cc b/wayland/cc/WindowWayland.cc index 0537694b..6dbe29da 100644 --- a/wayland/cc/WindowWayland.cc +++ b/wayland/cc/WindowWayland.cc @@ -219,16 +219,7 @@ void WindowWayland::setVisible(bool isVisible) { void jwm::WindowWayland::setCursor(jwm::MouseCursor cursor) { if (!_windowManager.getPointer()) return; - auto wayCursor = _getCursorFor(cursor)->images[0]; - auto buf = wl_cursor_image_get_buffer(wayCursor); - auto cursorSurface = _windowManager.getCursorSurface(); - wl_surface_attach(cursorSurface, - wl_cursor_image_get_buffer(wayCursor), - 0, 0); - wl_surface_set_buffer_scale(cursorSurface, _scale); - wl_surface_damage_buffer(cursorSurface, 0, 0, INT32_MAX, INT32_MAX); - _windowManager.getPointer()->updateHotspot(wayCursor->hotspot_x, wayCursor->hotspot_y); - wl_surface_commit(cursorSurface); + _windowManager.getPointer()->setCursor(_scale, cursor); } // what do??? @@ -401,6 +392,16 @@ void jwm::WindowWayland::lockCursor(bool locked) { pointer->unlock(); } } +void jwm::WindowWayland::hideCursor(bool hidden) { + auto pointer = _windowManager.getPointer(); + if (!pointer) return; + if (pointer->getFocusedSurface() == this) { + if (hidden) + pointer->hide(); + else + pointer->unhide(); + } +} // JNI extern "C" JNIEXPORT jlong JNICALL Java_io_github_humbleui_jwm_WindowWayland__1nMake @@ -539,3 +540,10 @@ extern "C" JNIEXPORT void JNICALL Java_io_github_humbleui_jwm_WindowWayland__1nL jwm::WindowWayland* instance = reinterpret_cast(jwm::classes::Native::fromJava(env, obj)); instance->lockCursor(locked); } + +extern "C" JNIEXPORT void JNICALL Java_io_github_humbleui_jwm_WindowWayland__1nHideMouseCursor + (JNIEnv* env, jobject obj, jboolean hidden) +{ + jwm::WindowWayland* instance = reinterpret_cast(jwm::classes::Native::fromJava(env, obj)); + instance->hideCursor(hidden); +} diff --git a/wayland/cc/WindowWayland.hh b/wayland/cc/WindowWayland.hh index 78b02ddf..ee25b658 100644 --- a/wayland/cc/WindowWayland.hh +++ b/wayland/cc/WindowWayland.hh @@ -74,6 +74,8 @@ namespace jwm { void lockCursor(bool locked); + void hideCursor(bool hidden); + int _posX = -1; int _posY = -1; diff --git a/wayland/java/WindowWayland.java b/wayland/java/WindowWayland.java index 4ac14ac7..1ebae6ac 100644 --- a/wayland/java/WindowWayland.java +++ b/wayland/java/WindowWayland.java @@ -90,7 +90,8 @@ public Window setVisible(boolean isVisible) { @Override public Window hideMouseCursorUntilMoved(boolean value) { - // TODO impl me! + assert _onUIThread() : "Should be run on UI thread"; + _nHideMouseCursor(value); return this; } @@ -237,4 +238,5 @@ public float getScale() { @ApiStatus.Internal public native boolean _nIsFullScreen(); @ApiStatus.Internal public native float _nGetScale(); @ApiStatus.Internal public native void _nLockMouseCursor(boolean locked); + @ApiStatus.Internal public native void _nHideMouseCursor(boolean hidden); } From f09192ddd520c8698ed7416ac8a60efc8f12f2be Mon Sep 17 00:00:00 2001 From: TheDrawingCoding-Gamer Date: Sun, 24 Dec 2023 14:38:23 -0500 Subject: [PATCH 63/95] Relative mouse movement --- wayland/CMakeLists.txt | 7 +++- wayland/cc/Pointer.cc | 63 ++++++++++++++++++++---------- wayland/cc/Pointer.hh | 13 ++++-- wayland/cc/WindowManagerWayland.cc | 3 ++ wayland/cc/WindowManagerWayland.hh | 2 + 5 files changed, 63 insertions(+), 25 deletions(-) diff --git a/wayland/CMakeLists.txt b/wayland/CMakeLists.txt index 8c19fef3..ae10dd47 100644 --- a/wayland/CMakeLists.txt +++ b/wayland/CMakeLists.txt @@ -36,11 +36,16 @@ file(GLOB SOURCES_CXX ${CMAKE_CURRENT_LIST_DIR}/../shared/cc/*.cc file(GLOB SOURCES_CXX_IMPL ${CMAKE_CURRENT_LIST_DIR}/../shared/cc/impl/*.cc) add_library(jwm SHARED ${SOURCES_OBJC} ${SOURCES_CXX} ${SOURCES_CXX_IMPL}) add_library(cursorlock STATIC) +add_library(relativepointer STATIC) # set_property(TARGET cursorlock PROPERTY POSITION_INDEPENDENT_CODE ON) ecm_add_wayland_client_protocol(cursorlock PROTOCOL "${WaylandProtocols_DATADIR}/unstable/pointer-constraints/pointer-constraints-unstable-v1.xml" BASENAME pointer-constraints-unstable-v1 ) +ecm_add_wayland_client_protocol(relativepointer + PROTOCOL "${WaylandProtocols_DATADIR}/unstable/relative-pointer/relative-pointer-unstable-v1.xml" + BASENAME relative-pointer-unstable-v1 +) find_library(WAYLAND_CLIENT_LIB wayland-client) find_library(DECOR_LIB decor-0) find_library(WAYLAND_CURSOR wayland-cursor) @@ -67,4 +72,4 @@ target_link_libraries(jwm PRIVATE ${WAYLAND_CLIENT_LIB} ${DECOR_LIB} ${WAYLAND_CURSOR} ${XKBCOMMON}) target_link_libraries(jwm PRIVATE ${EGL} ${WAYLAND_EGL}) target_link_libraries(jwm PRIVATE OpenGL::GL) -target_link_libraries(jwm PRIVATE cursorlock) +target_link_libraries(jwm PRIVATE cursorlock relativepointer) diff --git a/wayland/cc/Pointer.cc b/wayland/cc/Pointer.cc index 5b3122c0..8ca30911 100644 --- a/wayland/cc/Pointer.cc +++ b/wayland/cc/Pointer.cc @@ -21,7 +21,8 @@ static void pointerEnter(void* data, wl_pointer* pointer, uint32_t serial, self->_serial = serial; self->_focusedSurface = jwm::ref(window); window->setCursor(jwm::MouseCursor::ARROW); - self->mouseUpdateUnscaled(wl_fixed_to_int(surface_x), wl_fixed_to_int(surface_y), self->_mouseMask); + // frame probably isn't called so I immediately call + self->mouseUpdateUnscaled(wl_fixed_to_int(surface_x), wl_fixed_to_int(surface_y), 0, 0, self->_mouseMask); } } @@ -46,7 +47,12 @@ static void pointerMotion(void* data, wl_pointer* pointer, uint32_t time, } auto self = reinterpret_cast(data); self->unhide(); - self->mouseUpdateUnscaled(wl_fixed_to_int(surface_x), wl_fixed_to_int(surface_y), self->_mouseMask); + self->_absX = wl_fixed_to_int(surface_x); + self->_absY = wl_fixed_to_int(surface_y); + self->_movement = true; + // I only unhide here, bc i'm unsure of what exactly is wanted with mouse lock. + // This event isn't sent on mouse lock, only relative events are sent. + self->unhide(); } static void pointerButton(void* data, wl_pointer* pointer, uint32_t serial, @@ -82,8 +88,8 @@ static void pointerButton(void* data, wl_pointer* pointer, uint32_t serial, app.getJniEnv(), MouseButtonWayland::fromNative(button), false, - self->_lastMouseX, - self->_lastMouseY, + self->_absX, + self->_absY, jwm::KeyWayland::getModifiers(self->_wm.getXkbState()) ) ); @@ -115,8 +121,8 @@ static void pointerButton(void* data, wl_pointer* pointer, uint32_t serial, app.getJniEnv(), MouseButtonWayland::fromNative(button), true, - self->_lastMouseX, - self->_lastMouseY, + self->_absX, + self->_absY, jwm::KeyWayland::getModifiers(self->_wm.getXkbState()) ) ); @@ -148,8 +154,6 @@ static void pointerFrame(void* data, wl_pointer* pointer) auto self = reinterpret_cast(data); auto win = self->_focusedSurface; if (!win) return; - // this is always sent so I can safely issue an unhide here - self->unhide(); if (self->_dX != 0.0f || self->_dY != 0.0f) { auto env = app.getJniEnv(); @@ -162,8 +166,8 @@ static void pointerFrame(void* data, wl_pointer* pointer) 0.0f, 0.0f, 0.0f, - self->_lastMouseX, - self->_lastMouseY, + self->_absX * win->_scale, + self->_absY * win->_scale, jwm::KeyWayland::getModifiers(self->_wm.getXkbState()) ) ); @@ -172,6 +176,13 @@ static void pointerFrame(void* data, wl_pointer* pointer) self->_dX = 0.0f; self->_dY = 0.0f; } + if (self->_movement || self->_dXPos != 0.0 || self->_dYPos != 0.0) { + auto scale = win->_scale; + self->mouseUpdateUnscaled(self->_absX * scale, self->_absY * scale, static_cast(self->_dXPos * scale), static_cast(self->_dYPos * scale), self->_mouseMask); + self->_movement = false; + self->_dXPos = 0.0; + self->_dYPos = 0.0; + } } static void pointerAxisSource(void* data, wl_pointer* pointer, uint32_t source) {} @@ -190,6 +201,16 @@ wl_pointer_listener Pointer::_pointerListener = { .axis_discrete = pointerAxisDiscrete }; +static void relativePointerRelativeMotion(void* data, zwp_relative_pointer_v1* relative, uint32_t utime_hi, uint32_t utime_lo, + wl_fixed_t dx, wl_fixed_t dy, wl_fixed_t dx_unaccel, wl_fixed_t dy_unaccel) { + auto self = reinterpret_cast(data); + + self->_dXPos += static_cast(wl_fixed_to_double(dx)); + self->_dYPos += static_cast(wl_fixed_to_double(dy)); +} +static zwp_relative_pointer_v1_listener relativePointerListener = { + .relative_motion = relativePointerRelativeMotion +}; Pointer::Pointer(wl_pointer* pointer, WindowManagerWayland* wm): _pointer(pointer), _wm(*wm) @@ -197,6 +218,10 @@ Pointer::Pointer(wl_pointer* pointer, WindowManagerWayland* wm): _surface = wl_compositor_create_surface(_wm.compositor); wl_pointer_add_listener(pointer, &_pointerListener, this); wl_proxy_set_tag((wl_proxy*)pointer, &AppWayland::proxyTag); + if (_wm.relativePointerManager) { + _relative = zwp_relative_pointer_manager_v1_get_relative_pointer(_wm.relativePointerManager, pointer); + zwp_relative_pointer_v1_add_listener(_relative, &relativePointerListener, this); + } } Pointer::~Pointer() @@ -207,23 +232,18 @@ Pointer::~Pointer() wl_surface_destroy(_surface); } -void Pointer::mouseUpdate(uint32_t x, uint32_t y, uint32_t mask) { +void Pointer::mouseUpdate(uint32_t x, uint32_t y, int32_t relX, int32_t relY, uint32_t mask) { auto window = _focusedSurface; if (!window) return; - if (_lastMouseX == x && _lastMouseY == y) - return; - int movementX = x - _lastMouseX, movementY = y - _lastMouseY; - _lastMouseX = x; - _lastMouseY = y; jwm::JNILocal eventMove( app.getJniEnv(), jwm::classes::EventMouseMove::make(app.getJniEnv(), x, y, - movementX, - movementY, + relX, + relY, jwm::MouseButtonWayland::fromNativeMask(mask), // impl me! jwm::KeyWayland::getModifiers(_wm.getXkbState()) @@ -232,11 +252,14 @@ void Pointer::mouseUpdate(uint32_t x, uint32_t y, uint32_t mask) { window->dispatch(eventMove.get()); } -void Pointer::mouseUpdateUnscaled(uint32_t x, uint32_t y, uint32_t mask) { +void Pointer::mouseUpdateUnscaled(uint32_t x, uint32_t y, int32_t relX, int32_t relY, uint32_t mask) { if (!_focusedSurface) return; auto newX = x * _focusedSurface->_scale; auto newY = y * _focusedSurface->_scale; - mouseUpdate(newX, newY, mask); + auto newDX = relX * _focusedSurface->_scale; + auto newDY = relY * _focusedSurface->_scale; + + mouseUpdate(newX, newY, newDX, newDY, mask); } void Pointer::updateHotspot(int x, int y) { diff --git a/wayland/cc/Pointer.hh b/wayland/cc/Pointer.hh index 35fb2f59..0f3b1f26 100644 --- a/wayland/cc/Pointer.hh +++ b/wayland/cc/Pointer.hh @@ -5,6 +5,7 @@ #include #include "MouseCursor.hh" #include +#include namespace jwm { class WindowManagerWayland; @@ -49,8 +50,12 @@ namespace jwm { return _hidden; } - uint32_t _lastMouseX; - uint32_t _lastMouseY; + zwp_relative_pointer_v1* _relative = nullptr; + bool _movement = false; + int _absX = 0; + int _absY = 0; + float _dXPos = 0.0; + float _dYPos = 0.0; float _dX = 0.0; float _dY = 0.0; @@ -59,8 +64,8 @@ namespace jwm { static wl_pointer_listener _pointerListener; - void mouseUpdate(uint32_t x, uint32_t y, uint32_t mask); - void mouseUpdateUnscaled(uint32_t x, uint32_t y, uint32_t mask); + void mouseUpdate(uint32_t x, uint32_t y, int32_t relX, int32_t relY, uint32_t mask); + void mouseUpdateUnscaled(uint32_t x, uint32_t y, int32_t relX, int32_t relY, uint32_t mask); void updateHotspot(int x, int y); diff --git a/wayland/cc/WindowManagerWayland.cc b/wayland/cc/WindowManagerWayland.cc index 748eb4f5..287a7130 100644 --- a/wayland/cc/WindowManagerWayland.cc +++ b/wayland/cc/WindowManagerWayland.cc @@ -257,6 +257,9 @@ void WindowManagerWayland::registryHandleGlobal(void* data, wl_registry *registr } else if (strcmp(interface, zwp_pointer_constraints_v1_interface.name) == 0) { self->pointerConstraints = (zwp_pointer_constraints_v1*)wl_registry_bind(registry, name, &zwp_pointer_constraints_v1_interface, 1); + } else if (strcmp(interface, zwp_relative_pointer_manager_v1_interface.name) == 0) { + self->relativePointerManager = (zwp_relative_pointer_manager_v1*)wl_registry_bind(registry, name, + &zwp_relative_pointer_manager_v1_interface, 1); } } void WindowManagerWayland::registryHandleGlobalRemove(void* data, wl_registry *registry, uint32_t name) { diff --git a/wayland/cc/WindowManagerWayland.hh b/wayland/cc/WindowManagerWayland.hh index 235ae912..61d396c6 100644 --- a/wayland/cc/WindowManagerWayland.hh +++ b/wayland/cc/WindowManagerWayland.hh @@ -22,6 +22,7 @@ #include "Pointer.hh" #include #include +#include namespace jwm { class WindowWayland; @@ -72,6 +73,7 @@ namespace jwm { // no multiseat? wl_seat* seat = nullptr; zwp_pointer_constraints_v1* pointerConstraints = nullptr; + zwp_relative_pointer_manager_v1* relativePointerManager = nullptr; std::unique_ptr _pointer = nullptr; Pointer* getPointer() const { return _pointer.get(); From 79170545061a933861199d82d458ca3423edb282 Mon Sep 17 00:00:00 2001 From: BulbyVR <26726264+TheDrawingCoder-Gamer@users.noreply.github.com> Date: Mon, 25 Dec 2023 16:37:43 -0500 Subject: [PATCH 64/95] fix mouse scaling --- wayland/cc/LayerRasterWayland.cc | 4 ++-- wayland/cc/Pointer.cc | 4 ++-- wayland/cc/WindowWayland.cc | 3 ++- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/wayland/cc/LayerRasterWayland.cc b/wayland/cc/LayerRasterWayland.cc index 5502b544..a8302eca 100644 --- a/wayland/cc/LayerRasterWayland.cc +++ b/wayland/cc/LayerRasterWayland.cc @@ -64,8 +64,8 @@ namespace jwm { void close() override { detachBuffer(); - jwm::unref(&fWindow); - fWindow = nullptr; + if (fWindow) + jwm::unref(&fWindow); } void makeCurrentForced() override { diff --git a/wayland/cc/Pointer.cc b/wayland/cc/Pointer.cc index 8ca30911..a96fc4c0 100644 --- a/wayland/cc/Pointer.cc +++ b/wayland/cc/Pointer.cc @@ -46,7 +46,6 @@ static void pointerMotion(void* data, wl_pointer* pointer, uint32_t time, return; } auto self = reinterpret_cast(data); - self->unhide(); self->_absX = wl_fixed_to_int(surface_x); self->_absY = wl_fixed_to_int(surface_y); self->_movement = true; @@ -178,7 +177,8 @@ static void pointerFrame(void* data, wl_pointer* pointer) } if (self->_movement || self->_dXPos != 0.0 || self->_dYPos != 0.0) { auto scale = win->_scale; - self->mouseUpdateUnscaled(self->_absX * scale, self->_absY * scale, static_cast(self->_dXPos * scale), static_cast(self->_dYPos * scale), self->_mouseMask); + // rounding inaccuracy? + self->mouseUpdateUnscaled(self->_absX, self->_absY, static_cast(self->_dXPos), static_cast(self->_dYPos), self->_mouseMask); self->_movement = false; self->_dXPos = 0.0; self->_dYPos = 0.0; diff --git a/wayland/cc/WindowWayland.cc b/wayland/cc/WindowWayland.cc index 6dbe29da..3e303d10 100644 --- a/wayland/cc/WindowWayland.cc +++ b/wayland/cc/WindowWayland.cc @@ -38,7 +38,7 @@ WindowWayland::WindowWayland(JNIEnv* env, WindowManagerWayland& windowManager): WindowWayland::~WindowWayland() { // TODO: close gets called twice? - // close(); + close(); } @@ -56,6 +56,7 @@ void WindowWayland::setTitlebarVisible(bool isVisible) { void WindowWayland::close() { _closed = true; + if (_closed) return; hide(); // TODO: more destruction! } From 26182c772464cd0caaa66d4a93c08addcc202ed8 Mon Sep 17 00:00:00 2001 From: BulbyVR <26726264+TheDrawingCoder-Gamer@users.noreply.github.com> Date: Mon, 25 Dec 2023 16:41:33 -0500 Subject: [PATCH 65/95] Really fix mouse --- wayland/cc/Pointer.cc | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/wayland/cc/Pointer.cc b/wayland/cc/Pointer.cc index a96fc4c0..8c8af5bb 100644 --- a/wayland/cc/Pointer.cc +++ b/wayland/cc/Pointer.cc @@ -61,6 +61,7 @@ static void pointerButton(void* data, wl_pointer* pointer, uint32_t serial, auto self = reinterpret_cast(data); auto window = self->_focusedSurface; if (!window) return; + int scale = window->getIntScale(); if (state == 0) { // release switch (button) { @@ -87,8 +88,8 @@ static void pointerButton(void* data, wl_pointer* pointer, uint32_t serial, app.getJniEnv(), MouseButtonWayland::fromNative(button), false, - self->_absX, - self->_absY, + self->_absX * scale, + self->_absY * scale, jwm::KeyWayland::getModifiers(self->_wm.getXkbState()) ) ); @@ -120,8 +121,8 @@ static void pointerButton(void* data, wl_pointer* pointer, uint32_t serial, app.getJniEnv(), MouseButtonWayland::fromNative(button), true, - self->_absX, - self->_absY, + self->_absX * scale, + self->_absY * scale, jwm::KeyWayland::getModifiers(self->_wm.getXkbState()) ) ); From c041ebd53bd177dfeef1bdc3dd9eb28c606473db Mon Sep 17 00:00:00 2001 From: BulbyVR <26726264+TheDrawingCoder-Gamer@users.noreply.github.com> Date: Tue, 26 Dec 2023 00:32:08 -0500 Subject: [PATCH 66/95] Fix jank related to resizing --- shared/java/Window.java | 6 ++++-- wayland/cc/WindowWayland.cc | 1 - wayland/java/WindowWayland.java | 4 +++- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/shared/java/Window.java b/shared/java/Window.java index 7b5ce0a4..172486ce 100644 --- a/shared/java/Window.java +++ b/shared/java/Window.java @@ -63,7 +63,9 @@ public Window setLayer(@Nullable Layer layer) { if (layer != null) { layer.attach(this); _layer = layer; - accept(EventWindowScreenChange.INSTANCE); + // accepting this immediately causes crashes on wayland + if (Platform.CURRENT != Platform.WAYLAND) + accept(EventWindowScreenChange.INSTANCE); } return this; } @@ -423,7 +425,7 @@ public void accept(Event e) { if (e instanceof EventWindowScreenChange) { accept(new EventWindowResize(this)); - } else if (e instanceof EventWindowResize && Platform.CURRENT != Platform.X11 && Platform.CURRENT != Platform.WAYLAND) { + } else if (e instanceof EventWindowResize && Platform.CURRENT != Platform.X11) { accept(EventFrame.INSTANCE); } } diff --git a/wayland/cc/WindowWayland.cc b/wayland/cc/WindowWayland.cc index 3e303d10..f466edd0 100644 --- a/wayland/cc/WindowWayland.cc +++ b/wayland/cc/WindowWayland.cc @@ -359,7 +359,6 @@ void jwm::WindowWayland::decorFrameDismissPopup(libdecor_frame* frame, const cha void jwm::WindowWayland::_adaptSize(int newWidth, int newHeight) { using namespace classes; if (!_configured) { - printf("???\n"); return; } if (newWidth == _width && newHeight == _height && _scale == _oldScale) return; diff --git a/wayland/java/WindowWayland.java b/wayland/java/WindowWayland.java index 1ebae6ac..fd99375c 100644 --- a/wayland/java/WindowWayland.java +++ b/wayland/java/WindowWayland.java @@ -85,7 +85,9 @@ public Window setTitlebarVisible(boolean value) { public Window setVisible(boolean isVisible) { assert _onUIThread() : "Should be run on UI thread"; _nSetVisible(isVisible); - return super.setVisible(true); + // this calls a screen change, which will cause a crash because GL context isn't ready yet + // return super.setVisible(true); + return this; } @Override From 6435c2fb33ac44344a729f70dd9e0c946e770be0 Mon Sep 17 00:00:00 2001 From: BulbyVR <26726264+TheDrawingCoder-Gamer@users.noreply.github.com> Date: Tue, 26 Dec 2023 00:58:40 -0500 Subject: [PATCH 67/95] Fix closing --- wayland/cc/WindowWayland.cc | 2 +- wayland/java/WindowWayland.java | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/wayland/cc/WindowWayland.cc b/wayland/cc/WindowWayland.cc index f466edd0..a6b6d9bc 100644 --- a/wayland/cc/WindowWayland.cc +++ b/wayland/cc/WindowWayland.cc @@ -55,8 +55,8 @@ void WindowWayland::setTitlebarVisible(bool isVisible) { } void WindowWayland::close() { - _closed = true; if (_closed) return; + _closed = true; hide(); // TODO: more destruction! } diff --git a/wayland/java/WindowWayland.java b/wayland/java/WindowWayland.java index fd99375c..a8601472 100644 --- a/wayland/java/WindowWayland.java +++ b/wayland/java/WindowWayland.java @@ -77,6 +77,7 @@ public Window setIcon(File icon) { @Override public Window setTitlebarVisible(boolean value) { + assert _onUIThread() : "Should be run on UI thread"; _nSetTitlebarVisible(value); return this; } From ddff041c1fe1352ff2904bbda9416ea63b5781a0 Mon Sep 17 00:00:00 2001 From: TheDrawingCoding-Gamer Date: Sat, 30 Dec 2023 18:59:52 -0500 Subject: [PATCH 68/95] don't constantly set buffer size --- wayland/cc/LayerGLWayland.cc | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/wayland/cc/LayerGLWayland.cc b/wayland/cc/LayerGLWayland.cc index d31acc13..8eb1a52c 100644 --- a/wayland/cc/LayerGLWayland.cc +++ b/wayland/cc/LayerGLWayland.cc @@ -101,9 +101,10 @@ namespace jwm { fprintf(stderr, "HACK: remaking egl window\n"); detachBuffer(); attachBuffer(); - } else wl_egl_window_resize(_eglWindow, width, height, 0, 0); - wl_surface_set_buffer_scale(fWindow->_waylandWindow, fWindow->getIntScale()); - fWindow->_oldScale = fWindow->_scale; + wl_surface_set_buffer_scale(fWindow->_waylandWindow, fWindow->getIntScale()); + fWindow->_oldScale = fWindow->_scale; + } else + wl_egl_window_resize(_eglWindow, width, height, 0, 0); } } From 96ed37d6873865b36f98f352079872febaec57c1 Mon Sep 17 00:00:00 2001 From: TheDrawingCoding-Gamer Date: Sat, 30 Dec 2023 20:15:42 -0500 Subject: [PATCH 69/95] fix layer swapping --- shared/java/LayerRaster.java | 4 ++-- shared/java/Window.java | 2 +- wayland/cc/LayerGLWayland.cc | 10 +++++++--- wayland/cc/LayerRasterWayland.cc | 16 +++++++--------- 4 files changed, 17 insertions(+), 15 deletions(-) diff --git a/shared/java/LayerRaster.java b/shared/java/LayerRaster.java index e097bfe0..4f5bd379 100644 --- a/shared/java/LayerRaster.java +++ b/shared/java/LayerRaster.java @@ -15,8 +15,8 @@ public LayerRaster() { @Override public void attach(Window window) { assert _onUIThread() : "Should be run on UI thread"; - _nAttach(window); _window = window; + _nAttach(window); } @Override @@ -81,4 +81,4 @@ public int getRowBytes() { @ApiStatus.Internal public native long _nGetPixelsPtr(); @ApiStatus.Internal public native int _nGetRowBytes(); @ApiStatus.Internal public native void _nClose(); -} \ No newline at end of file +} diff --git a/shared/java/Window.java b/shared/java/Window.java index 172486ce..a77f5060 100644 --- a/shared/java/Window.java +++ b/shared/java/Window.java @@ -61,8 +61,8 @@ public Window setLayer(@Nullable Layer layer) { _layer = null; } if (layer != null) { - layer.attach(this); _layer = layer; + layer.attach(this); // accepting this immediately causes crashes on wayland if (Platform.CURRENT != Platform.WAYLAND) accept(EventWindowScreenChange.INSTANCE); diff --git a/wayland/cc/LayerGLWayland.cc b/wayland/cc/LayerGLWayland.cc index 8eb1a52c..eaea177a 100644 --- a/wayland/cc/LayerGLWayland.cc +++ b/wayland/cc/LayerGLWayland.cc @@ -74,8 +74,10 @@ namespace jwm { } if (fWindow->_waylandWindow) wl_surface_set_buffer_scale(fWindow->_waylandWindow, 1); - if (fWindow->_configured) + if (fWindow->_configured) { attachBuffer(); + fWindow->dispatch(jwm::classes::EventWindowScreenChange::kInstance); + } makeCurrentForced(); } @@ -124,8 +126,10 @@ namespace jwm { detachBuffer(); eglDestroyContext(_display, _context); - if (fWindow) + if (fWindow) { + fWindow->setLayer(nullptr); jwm::unref(&fWindow); + } } void makeCurrentForced() override { @@ -198,7 +202,7 @@ extern "C" JNIEXPORT void JNICALL Java_io_github_humbleui_jwm_LayerGL__1nAttach } extern "C" JNIEXPORT void JNICALL Java_io_github_humbleui_jwm_LayerGL__1nReconfigure - (JNIEnv* env, jobject obj, jint width, jint height) { + (JNIEnv* env, jobject obj) { } extern "C" JNIEXPORT void JNICALL Java_io_github_humbleui_jwm_LayerGL__1nResize diff --git a/wayland/cc/LayerRasterWayland.cc b/wayland/cc/LayerRasterWayland.cc index a8302eca..f74e77f2 100644 --- a/wayland/cc/LayerRasterWayland.cc +++ b/wayland/cc/LayerRasterWayland.cc @@ -22,9 +22,11 @@ namespace jwm { void attach(WindowWayland* window) { fWindow = jwm::ref(window); fWindow->setLayer(this); - resize(window->getWidth(), window->getHeight()); - if (fWindow->_configured) + if (fWindow->_configured) { attachBuffer(); + // delay this as much as possible + fWindow->dispatch(jwm::classes::EventWindowScreenChange::kInstance); + } } void resize(int width, int height) override { @@ -64,8 +66,10 @@ namespace jwm { void close() override { detachBuffer(); - if (fWindow) + if (fWindow) { + fWindow->setLayer(nullptr); jwm::unref(&fWindow); + } } void makeCurrentForced() override { @@ -77,7 +81,6 @@ namespace jwm { void attachBuffer() override { _attached = true; - swapBuffers(); } void detachBuffer() override { @@ -89,9 +92,6 @@ namespace jwm { } _attached = false; } - void reconfigure() { - swapBuffers(); - } }; } @@ -111,8 +111,6 @@ extern "C" JNIEXPORT void JNICALL Java_io_github_humbleui_jwm_LayerRaster__1nAtt extern "C" JNIEXPORT void JNICALL Java_io_github_humbleui_jwm_LayerRaster__1nReconfigure (JNIEnv* env, jobject obj) { - jwm::LayerRaster* instance = reinterpret_cast(jwm::classes::Native::fromJava(env, obj)); - instance->reconfigure(); } extern "C" JNIEXPORT void JNICALL Java_io_github_humbleui_jwm_LayerRaster__1nResize From 38280f60ff85c8b45acdf693316e0da096c25158 Mon Sep 17 00:00:00 2001 From: TheDrawingCoding-Gamer Date: Sat, 30 Dec 2023 20:16:31 -0500 Subject: [PATCH 70/95] don't forget GL too --- shared/java/LayerGL.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shared/java/LayerGL.java b/shared/java/LayerGL.java index 5162c70a..b31be871 100644 --- a/shared/java/LayerGL.java +++ b/shared/java/LayerGL.java @@ -15,8 +15,8 @@ public LayerGL() { @Override public void attach(Window window) { assert _onUIThread() : "Should be run on UI thread"; - _nAttach(window); _window = window; + _nAttach(window); } @Override From 353d420799432fcf2673d1ed6944ae5e3ec7731b Mon Sep 17 00:00:00 2001 From: BulbyVR <26726264+TheDrawingCoder-Gamer@users.noreply.github.com> Date: Wed, 3 Jan 2024 16:14:58 -0500 Subject: [PATCH 71/95] NO MORE LIBDECOR --- wayland/CMakeLists.txt | 26 +- wayland/cc/Buffer.hh | 2 + wayland/cc/Decoration.cc | 368 +++++++++++++++++++++++++++++ wayland/cc/Decoration.hh | 143 +++++++++++ wayland/cc/LayerGLWayland.cc | 2 +- wayland/cc/LayerRasterWayland.cc | 2 +- wayland/cc/Pointer.cc | 124 +++++++++- wayland/cc/Pointer.hh | 8 +- wayland/cc/WindowManagerWayland.cc | 38 +-- wayland/cc/WindowManagerWayland.hh | 13 +- wayland/cc/WindowWayland.cc | 140 +++-------- wayland/cc/WindowWayland.hh | 19 +- 12 files changed, 722 insertions(+), 163 deletions(-) create mode 100644 wayland/cc/Decoration.cc create mode 100644 wayland/cc/Decoration.hh diff --git a/wayland/CMakeLists.txt b/wayland/CMakeLists.txt index ae10dd47..0b18c608 100644 --- a/wayland/CMakeLists.txt +++ b/wayland/CMakeLists.txt @@ -18,7 +18,7 @@ if (NOT WaylandScanner_FOUND) message(FATAL_ERROR "No wayland-scanner") endif() project(jwm LANGUAGES CXX) -project(cursorlock LANGUAGES C) +project(protocols LANGUAGES C) set(CMAKE_CXX_STANDARD 14) set(CMAKE_CXX_STANDARD_REQUIRED ON) @@ -35,19 +35,29 @@ file(GLOB SOURCES_CXX ${CMAKE_CURRENT_LIST_DIR}/../shared/cc/*.cc ${CMAKE_CURRENT_LIST_DIR}/cc/*.cc ) file(GLOB SOURCES_CXX_IMPL ${CMAKE_CURRENT_LIST_DIR}/../shared/cc/impl/*.cc) add_library(jwm SHARED ${SOURCES_OBJC} ${SOURCES_CXX} ${SOURCES_CXX_IMPL}) -add_library(cursorlock STATIC) -add_library(relativepointer STATIC) +add_library(protocols STATIC) # set_property(TARGET cursorlock PROPERTY POSITION_INDEPENDENT_CODE ON) -ecm_add_wayland_client_protocol(cursorlock +ecm_add_wayland_client_protocol(protocols PROTOCOL "${WaylandProtocols_DATADIR}/unstable/pointer-constraints/pointer-constraints-unstable-v1.xml" BASENAME pointer-constraints-unstable-v1 ) -ecm_add_wayland_client_protocol(relativepointer +ecm_add_wayland_client_protocol(protocols PROTOCOL "${WaylandProtocols_DATADIR}/unstable/relative-pointer/relative-pointer-unstable-v1.xml" BASENAME relative-pointer-unstable-v1 ) +ecm_add_wayland_client_protocol(protocols + PROTOCOL "${WaylandProtocols_DATADIR}/stable/xdg-shell/xdg-shell.xml" + BASENAME xdg-shell +) +ecm_add_wayland_client_protocol(protocols + PROTOCOL "${WaylandProtocols_DATADIR}/unstable/xdg-decoration/xdg-decoration-unstable-v1.xml" + BASENAME xdg-decoration-unstable-v1 +) +ecm_add_wayland_client_protocol(protocols + PROTOCOL "${WaylandProtocols_DATADIR}/stable/viewporter/viewporter.xml" + BASENAME viewporter +) find_library(WAYLAND_CLIENT_LIB wayland-client) -find_library(DECOR_LIB decor-0) find_library(WAYLAND_CURSOR wayland-cursor) find_library(XKBCOMMON xkbcommon) find_library(EGL EGL) @@ -68,8 +78,8 @@ target_include_directories(jwm PRIVATE ${CMAKE_CURRENT_LIST_DIR}/../shared/cc ${ ${CMAKE_CURRENT_LIST_DIR}/build) set_target_properties(jwm PROPERTIES OUTPUT_NAME "jwm_${JWM_ARCH}_wayland") -target_link_libraries(jwm PRIVATE ${WAYLAND_CLIENT_LIB} ${DECOR_LIB} +target_link_libraries(jwm PRIVATE ${WAYLAND_CLIENT_LIB} ${WAYLAND_CURSOR} ${XKBCOMMON}) target_link_libraries(jwm PRIVATE ${EGL} ${WAYLAND_EGL}) target_link_libraries(jwm PRIVATE OpenGL::GL) -target_link_libraries(jwm PRIVATE cursorlock relativepointer) +target_link_libraries(jwm PRIVATE protocols) diff --git a/wayland/cc/Buffer.hh b/wayland/cc/Buffer.hh index 2fd0510e..2cdfc90c 100644 --- a/wayland/cc/Buffer.hh +++ b/wayland/cc/Buffer.hh @@ -1,3 +1,5 @@ +#pragma once + #include #include #include diff --git a/wayland/cc/Decoration.cc b/wayland/cc/Decoration.cc new file mode 100644 index 00000000..77a01766 --- /dev/null +++ b/wayland/cc/Decoration.cc @@ -0,0 +1,368 @@ +#include "Decoration.hh" +#include "WindowWayland.hh" +#include "WindowManagerWayland.hh" +#include + +using namespace jwm; + +static unsigned int grey_data[] = {0xFF333333}; +// no image editor +// pure unadulterated programming +static unsigned int close_data[] = { + 0xFFFFFFFF, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFFFFFFFF, + 0x00000000, 0xFFFFFFFF, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFFFFFFFF, 0x00000000, + 0x00000000, 0x00000000, 0xFFFFFFFF, 0x00000000, 0x00000000, 0x00000000, 0xFFFFFFFF, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFFFFFFFF, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0xFFFFFFFF, 0x00000000, 0x00000000, 0x00000000, 0xFFFFFFFF, 0x00000000, 0x00000000, + 0x00000000, 0xFFFFFFFF, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFFFFFFFF, 0x00000000, + 0xFFFFFFFF, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFFFFFFFF, +}; +static unsigned int min_data[] = { + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, +}; +static unsigned int max_data[] = { + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFFFFFFFF, + 0xFFFFFFFF, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFFFFFFFF, + 0xFFFFFFFF, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFFFFFFFF, + 0xFFFFFFFF, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFFFFFFFF, + 0xFFFFFFFF, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFFFFFFFF, + 0xFFFFFFFF, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFFFFFFFF, + 0xFFFFFFFF, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, +}; + +static void _decorationConfigure(void* data, zxdg_toplevel_decoration_v1* decoration, uint32_t mode) { + auto self = reinterpret_cast(data); + if (mode == ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE) { + self->_serverSide = true; + } else { + self->_serverSide = false; + } +} +static zxdg_toplevel_decoration_v1_listener _decorationListener = { + .configure = _decorationConfigure +}; + +static void _xdgSurfaceConfigure(void* data, xdg_surface* surface, uint32_t serial) { + auto self = reinterpret_cast(data); + auto& window = self->_window; + int width = 0, height = 0; + + if (self->_pendingWidth > 0) + width = self->_pendingWidth; + else + width = window._floatingWidth; + + if (self->_pendingHeight > 0) + height = self->_pendingHeight; + else + height = window._floatingHeight; + + self->_pendingWidth = 0; + self->_pendingHeight = 0; + if (self->_oldActive != self->_active) { + if (self->_active) + window.dispatch(classes::EventWindowFocusIn::kInstance); + else + window.dispatch(classes::EventWindowFocusOut::kInstance); + } + self->_oldActive = self->_active; + if (self->_oldMaximized != self->_maximized) { + if (self->_maximized) + window.dispatch(classes::EventWindowMaximize::kInstance); + } + self->_oldMaximized = self->_maximized; + if (self->_oldFullscreen != self->_fullscreen) { + if (self->_fullscreen) + window.dispatch(classes::EventWindowFullScreenEnter::kInstance); + else + window.dispatch(classes::EventWindowFullScreenExit::kInstance); + } + self->_oldFullscreen = self->_fullscreen; + if (!self->_configured) { + if (window._layer) + window._layer->attachBuffer(); + } + if (window.getUnscaledWidth() != width || window.getUnscaledHeight() != height) { + if (self->_floating) { + if (width > 0) { + window._floatingWidth = width; + } + if (height > 0) { + window._floatingHeight = height; + } + } + window._adaptSize(width, height); + self->_adaptSize(); + } + if (self->_serverSide) { + if (self->_top.surface) + self->_destroyDecorations(); + } else if (self->_isVisible) { + if (!self->_top.surface) + self->_showDecorations(); + } + wl_surface_commit(window._waylandWindow); + xdg_surface_ack_configure(self->_xdgSurface, serial); + self->_configured = true; +} + +static xdg_surface_listener _xdgSurfaceListener = { + .configure = _xdgSurfaceConfigure +}; + +static void _xdgToplevelConfigure(void* data, xdg_toplevel* toplevel, int width, int height, wl_array* states) { + auto self = reinterpret_cast(data); + + self->_pendingWidth = width; + self->_pendingHeight = height; + if (!self->_serverSide) { + self->_pendingWidth -= DECORATION_LEFT_WIDTH + DECORATION_RIGHT_WIDTH; + self->_pendingHeight -= DECORATION_TOP_HEIGHT + DECORATION_BOTTOM_HEIGHT; + } + + bool active = false; + bool maximized = false; + bool fullscreen = false; + bool floating = true; + for (uint32_t* pos = (uint32_t*)states->data; + (const char*)pos < ((const char*) states->data + states->size); + pos++) { + switch (*pos) { + case XDG_TOPLEVEL_STATE_MAXIMIZED: + maximized = true; + floating = false; + break; + case XDG_TOPLEVEL_STATE_ACTIVATED: + active = true; + break; + case XDG_TOPLEVEL_STATE_FULLSCREEN: + fullscreen = true; + floating = false; + break; + case XDG_TOPLEVEL_STATE_TILED_LEFT: + case XDG_TOPLEVEL_STATE_TILED_RIGHT: + case XDG_TOPLEVEL_STATE_TILED_TOP: + case XDG_TOPLEVEL_STATE_TILED_BOTTOM: + floating = false; + break; + } + } + self->_active = active; + self->_maximized = maximized; + self->_fullscreen = fullscreen; + self->_floating = floating; +} +static void _xdgToplevelClose(void* data, xdg_toplevel* toplevel) { + auto self = reinterpret_cast(data); + auto& window = self->_window; + + window.dispatch(classes::EventWindowCloseRequest::kInstance); +} + +static void _xdgToplevelConfigureBounds(void* data, xdg_toplevel* toplevel, int width, int height) { + // above version +} +static void _xdgToplevelWmCapabilities(void* data, xdg_toplevel* toplevel, wl_array* capabilities) { + // above version +} +static xdg_toplevel_listener _xdgToplevelListener = { + .configure = _xdgToplevelConfigure, + .close = _xdgToplevelClose, + .configure_bounds = _xdgToplevelConfigureBounds, + .wm_capabilities = _xdgToplevelWmCapabilities +}; + +const char* Decoration::proxyTag = "DecorationJWM"; +Decoration::Decoration(WindowWayland& window): + _window(window), + _wm(window._windowManager) +{ + _xdgSurface = xdg_wm_base_get_xdg_surface(_wm.xdgWm, window._waylandWindow); + xdg_surface_add_listener(_xdgSurface, &_xdgSurfaceListener, this); + _xdgToplevel = xdg_surface_get_toplevel(_xdgSurface); + xdg_toplevel_add_listener(_xdgToplevel, &_xdgToplevelListener, this); + if (_wm.decorationManager) { + _decoration = zxdg_decoration_manager_v1_get_toplevel_decoration(_wm.decorationManager, _xdgToplevel); + zxdg_toplevel_decoration_v1_add_listener(_decoration, &_decorationListener, this); + // for the love of GOD do it for me + zxdg_toplevel_decoration_v1_set_mode(_decoration, ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE); + + } + + _decBuffer = Buffer::createShmBuffer(_wm.shm, 1, 1, WL_SHM_FORMAT_ARGB8888); + memcpy(_decBuffer->getData(), grey_data, 1 * sizeof(uint32_t)); + _closeBuffer = Buffer::createShmBuffer(_wm.shm, 9, 9, WL_SHM_FORMAT_ARGB8888); + memcpy(_closeBuffer->getData(), close_data, 9 * 9 * sizeof(uint32_t)); + _maxBuffer = Buffer::createShmBuffer(_wm.shm, 9, 9, WL_SHM_FORMAT_ARGB8888); + memcpy(_maxBuffer->getData(), max_data, 9 * 9 * sizeof(uint32_t)); + _minBuffer = Buffer::createShmBuffer(_wm.shm, 9, 9, WL_SHM_FORMAT_ARGB8888); + memcpy(_minBuffer->getData(), min_data, 9 * 9 * sizeof(uint32_t)); + + + // delay making parts until configure : ) + +} + +void Decoration::close() { + if (_top.surface) + _destroyDecorations(); + if (_decoration) + zxdg_toplevel_decoration_v1_destroy(_decoration); + xdg_toplevel_destroy(_xdgToplevel); + xdg_surface_destroy(_xdgSurface); +} + +void Decoration::_makePart(DecorationPart* decoration, Buffer* buf, bool opaque, int x, int y, int width, int height) { + decoration->surface = wl_compositor_create_surface(_wm.compositor); + wl_proxy_set_tag((wl_proxy*)decoration->surface, &proxyTag); + wl_proxy_set_user_data((wl_proxy*)decoration->surface, this); + decoration->subsurface = wl_subcompositor_get_subsurface(_wm.subcompositor, decoration->surface, _window._waylandWindow); + wl_subsurface_set_position(decoration->subsurface, x, y); + decoration->viewport = wp_viewporter_get_viewport(_wm.viewporter, decoration->surface); + wp_viewport_set_destination(decoration->viewport, width, height); + if (buf) + wl_surface_attach(decoration->surface, buf->getBuffer(), 0, 0); + + if (opaque) { + wl_region* region = wl_compositor_create_region(_wm.compositor); + wl_region_add(region, 0, 0, width, height); + wl_surface_set_opaque_region(decoration->surface, region); + wl_surface_commit(decoration->surface); + wl_region_destroy(region); + } else + wl_surface_commit(decoration->surface); +} + +void Decoration::_resizeDecoration(DecorationPart* decoration, int x, int y, int width, int height) { + if (decoration->surface) { + wl_subsurface_set_position(decoration->subsurface, x, y); + wp_viewport_set_destination(decoration->viewport, width, height); + wl_surface_commit(decoration->surface); + } +} +void Decoration::_destroyDecoration(DecorationPart* decoration) { + if (decoration->subsurface) { + wl_subsurface_destroy(decoration->subsurface); + } + if (decoration->surface) + wl_surface_destroy(decoration->surface); + if (decoration->viewport) + wp_viewport_destroy(decoration->viewport); + decoration->subsurface = nullptr; + decoration->surface = nullptr; + decoration->viewport = nullptr; +} + +void Decoration::_destroyDecorations() { + _destroyDecoration(&_top); + _destroyDecoration(&_left); + _destroyDecoration(&_right); + _destroyDecoration(&_bottom); + _destroyDecoration(&_close); + _destroyDecoration(&_min); + _destroyDecoration(&_max); +} + +void Decoration::_showDecorations() { + _makePart(&_top, _decBuffer, true, DECORATION_TOP_X, DECORATION_TOP_Y, DECORATION_TOP_WIDTH(_window), DECORATION_TOP_HEIGHT); + _makePart(&_left, _decBuffer, true, DECORATION_LEFT_X, DECORATION_LEFT_Y, DECORATION_LEFT_WIDTH, DECORATION_LEFT_HEIGHT(_window)); + _makePart(&_right, _decBuffer, true, DECORATION_RIGHT_X(_window), DECORATION_RIGHT_Y, DECORATION_RIGHT_WIDTH, DECORATION_RIGHT_HEIGHT(_window)); + _makePart(&_bottom, _decBuffer, true, DECORATION_BOTTOM_X, DECORATION_BOTTOM_Y(_window), DECORATION_BOTTOM_WIDTH(_window), DECORATION_BOTTOM_HEIGHT); + + _makePart(&_close, _closeBuffer, false, DECORATION_CLOSE_X(_window), DECORATION_CLOSE_Y, DECORATION_CLOSE_WIDTH, DECORATION_CLOSE_HEIGHT); + _makePart(&_max, _maxBuffer, false, DECORATION_MAX_X(_window), DECORATION_MAX_Y, DECORATION_MAX_WIDTH, DECORATION_MAX_HEIGHT); + _makePart(&_min, _minBuffer, false, DECORATION_MIN_X(_window), DECORATION_MIN_Y, DECORATION_MIN_WIDTH, DECORATION_MIN_HEIGHT); + +} + +void Decoration::_adaptSize() { + _resizeDecoration(&_top, DECORATION_TOP_X, DECORATION_TOP_Y, DECORATION_TOP_WIDTH(_window), DECORATION_TOP_HEIGHT); + _resizeDecoration(&_left, DECORATION_LEFT_X, DECORATION_LEFT_Y, DECORATION_LEFT_WIDTH, DECORATION_LEFT_HEIGHT(_window)); + _resizeDecoration(&_right, DECORATION_RIGHT_X(_window), DECORATION_RIGHT_Y, DECORATION_RIGHT_WIDTH, DECORATION_RIGHT_HEIGHT(_window)); + _resizeDecoration(&_bottom, DECORATION_BOTTOM_X, DECORATION_BOTTOM_Y(_window), DECORATION_BOTTOM_WIDTH(_window), DECORATION_BOTTOM_HEIGHT); + + + _resizeDecoration(&_close, DECORATION_CLOSE_X(_window), DECORATION_CLOSE_Y, DECORATION_CLOSE_WIDTH, DECORATION_CLOSE_HEIGHT); + _resizeDecoration(&_min, DECORATION_MIN_X(_window), DECORATION_MIN_Y, DECORATION_MIN_WIDTH, DECORATION_MIN_HEIGHT); + _resizeDecoration(&_max, DECORATION_MAX_X(_window), DECORATION_MAX_Y, DECORATION_MAX_WIDTH, DECORATION_MAX_HEIGHT); +} + +bool Decoration::ownDecorationSurface(wl_surface* surface) { + return wl_proxy_get_tag((wl_proxy*)surface) == &proxyTag; +} + +Decoration* Decoration::getDecorationForSurface(wl_surface* surface, DecorationFocus* focus) { + if (ownDecorationSurface(surface)) { + Decoration* decoration = (Decoration*)wl_proxy_get_user_data((wl_proxy*) surface); + if (surface == decoration->_top.surface) { + *focus = DECORATION_FOCUS_TOP; + } else if (surface == decoration->_left.surface) { + *focus = DECORATION_FOCUS_LEFT; + } else if (surface == decoration->_right.surface) { + *focus = DECORATION_FOCUS_RIGHT; + } else if (surface == decoration->_bottom.surface) { + *focus = DECORATION_FOCUS_BOTTOM; + } else if (surface == decoration->_close.surface) { + *focus = DECORATION_FOCUS_CLOSE_BUTTON; + } else if (surface == decoration->_min.surface) { + *focus = DECORATION_FOCUS_MIN_BUTTON; + } else if (surface == decoration->_max.surface) { + *focus = DECORATION_FOCUS_MAX_BUTTON; + } + + return decoration; + } else { + *focus = DECORATION_FOCUS_MAIN; + return nullptr; + } +} + +void Decoration::setVisible(bool isVisible) { + if (isVisible != _isVisible) { + _isVisible = isVisible; + if (isVisible) { + if (_decoration) { + zxdg_toplevel_decoration_v1_set_mode(_decoration, ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE); + } + } else { + if (_decoration) { + zxdg_toplevel_decoration_v1_set_mode(_decoration, ZXDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE); + } + _destroyDecorations(); + } + + + } +} + +void Decoration::setTitle(const std::string& title) { + xdg_toplevel_set_title(_xdgToplevel, title.c_str()); + // grab a copy - unused for now but maybe a text renderer will eventually be pulled in + _title = title; +} + +void Decoration::getBorders(int& left, int& top, int& right, int& bottom) { + if (_serverSide) { + left = 0; + top = 0; + right = 0; + bottom = 0; + } else { + left = DECORATION_LEFT_WIDTH; + top = DECORATION_TOP_HEIGHT; + right = DECORATION_RIGHT_WIDTH; + bottom = DECORATION_BOTTOM_HEIGHT; + } +} diff --git a/wayland/cc/Decoration.hh b/wayland/cc/Decoration.hh new file mode 100644 index 00000000..082911be --- /dev/null +++ b/wayland/cc/Decoration.hh @@ -0,0 +1,143 @@ +#pragma once + +#include +#include +#include +#include +#include +#include "Buffer.hh" +#include + +#define DECORATION_WIDTH 10 + +#define DECORATION_TOP_X 0 +#define DECORATION_TOP_Y -(DECORATION_TOP_HEIGHT) +#define DECORATION_TOP_WIDTH(window) window.getUnscaledWidth() +#define DECORATION_TOP_HEIGHT DECORATION_WIDTH * 3 + +#define DECORATION_LEFT_X -(DECORATION_WIDTH) +#define DECORATION_LEFT_Y -(DECORATION_TOP_HEIGHT) +#define DECORATION_LEFT_WIDTH DECORATION_WIDTH +#define DECORATION_LEFT_HEIGHT(window) window.getUnscaledHeight() + DECORATION_TOP_HEIGHT + DECORATION_WIDTH + +#define DECORATION_RIGHT_X(window) window.getUnscaledWidth() +#define DECORATION_RIGHT_Y -(DECORATION_TOP_HEIGHT) +#define DECORATION_RIGHT_WIDTH DECORATION_WIDTH +#define DECORATION_RIGHT_HEIGHT(window) window.getUnscaledHeight() + DECORATION_TOP_HEIGHT + DECORATION_WIDTH + +#define DECORATION_BOTTOM_X 0 +#define DECORATION_BOTTOM_Y(window) window.getUnscaledHeight() +#define DECORATION_BOTTOM_WIDTH(window) window.getUnscaledWidth() +#define DECORATION_BOTTOM_HEIGHT DECORATION_WIDTH + +#define DECORATION_CLOSE_X(window) window.getUnscaledWidth() - 10 +#define DECORATION_CLOSE_Y -20 +#define DECORATION_CLOSE_WIDTH 9 +#define DECORATION_CLOSE_HEIGHT 9 + +#define DECORATION_MAX_X(window) (DECORATION_CLOSE_X(window)) - 10 +#define DECORATION_MAX_Y -20 +#define DECORATION_MAX_WIDTH 9 +#define DECORATION_MAX_HEIGHT 9 + +#define DECORATION_MIN_X(window) (DECORATION_MAX_X(window)) - 10 +#define DECORATION_MIN_Y -20 +#define DECORATION_MIN_WIDTH 9 +#define DECORATION_MIN_HEIGHT 9 + +namespace jwm { + class WindowManagerWayland; + class WindowWayland; + struct DecorationPart { + wl_surface* surface = nullptr; + wl_subsurface* subsurface = nullptr; + wp_viewport* viewport = nullptr; + }; + enum DecorationFocus { + DECORATION_FOCUS_MAIN, + DECORATION_FOCUS_TOP, + DECORATION_FOCUS_LEFT, + DECORATION_FOCUS_RIGHT, + DECORATION_FOCUS_BOTTOM, + DECORATION_FOCUS_CLOSE_BUTTON, + DECORATION_FOCUS_MAX_BUTTON, + DECORATION_FOCUS_MIN_BUTTON + }; + // Creation is mapping + // Closing is unmapping + class Decoration { + public: + Decoration() = delete; + Decoration(WindowWayland& window); + ~Decoration(); + + WindowManagerWayland& _wm; + WindowWayland& _window; + + Buffer* _decBuffer; + Buffer* _closeBuffer; + Buffer* _maxBuffer; + Buffer* _minBuffer; + + DecorationPart _top; + DecorationPart _left; + DecorationPart _right; + DecorationPart _bottom; + + DecorationPart _close; + DecorationPart _max; + DecorationPart _min; + // May be null at runtime + zxdg_toplevel_decoration_v1* _decoration = nullptr; + + xdg_surface* _xdgSurface = nullptr; + xdg_toplevel* _xdgToplevel = nullptr; + + bool _serverSide = false; + int _pendingWidth = 0; + int _pendingHeight = 0; + bool _oldActive = false; + // : ) + bool _active = true; + bool _oldMaximized = false; + bool _maximized = false; + bool _oldFullscreen = false; + bool _fullscreen = false; + bool _floating = true; + bool _configured = false; + // unmap and dispose + void close(); + + void _makePart(DecorationPart* part, Buffer* buf, bool opaque, int x, int y, int width, int height); + void _resizeDecoration(DecorationPart* part, int x, int y, int width, int height); + void _destroyDecoration(DecorationPart* part); + + void _adaptSize(); + void _destroyDecorations(); + void _showDecorations(); + + static const char* proxyTag; + static bool ownDecorationSurface(wl_surface* surface); + static Decoration* getDecorationForSurface(wl_surface* surface, DecorationFocus* focus); + + std::string _title; + + void setTitle(const std::string& title); + + bool _isVisible = true; + void setVisible(bool isVisible); + + void getBorders(int& left, int& top, int& right, int& bottom); + + + private: + Decoration(Decoration&& other) = delete; + Decoration& operator=(Decoration&& other) = delete; + + Decoration(Decoration& other) = delete; + Decoration& operator=(Decoration& other) = delete; + + + + }; +} diff --git a/wayland/cc/LayerGLWayland.cc b/wayland/cc/LayerGLWayland.cc index eaea177a..ecdba2f3 100644 --- a/wayland/cc/LayerGLWayland.cc +++ b/wayland/cc/LayerGLWayland.cc @@ -74,7 +74,7 @@ namespace jwm { } if (fWindow->_waylandWindow) wl_surface_set_buffer_scale(fWindow->_waylandWindow, 1); - if (fWindow->_configured) { + if (fWindow->isConfigured()) { attachBuffer(); fWindow->dispatch(jwm::classes::EventWindowScreenChange::kInstance); } diff --git a/wayland/cc/LayerRasterWayland.cc b/wayland/cc/LayerRasterWayland.cc index f74e77f2..156d3b91 100644 --- a/wayland/cc/LayerRasterWayland.cc +++ b/wayland/cc/LayerRasterWayland.cc @@ -22,7 +22,7 @@ namespace jwm { void attach(WindowWayland* window) { fWindow = jwm::ref(window); fWindow->setLayer(this); - if (fWindow->_configured) { + if (fWindow->isConfigured()) { attachBuffer(); // delay this as much as possible fWindow->dispatch(jwm::classes::EventWindowScreenChange::kInstance); diff --git a/wayland/cc/Pointer.cc b/wayland/cc/Pointer.cc index 8c8af5bb..36def276 100644 --- a/wayland/cc/Pointer.cc +++ b/wayland/cc/Pointer.cc @@ -6,6 +6,7 @@ #include "KeyWayland.hh" #include #include +#include using namespace jwm; @@ -17,13 +18,20 @@ static void pointerEnter(void* data, wl_pointer* pointer, uint32_t serial, return; } auto self = reinterpret_cast(data); + DecorationFocus focus = DECORATION_FOCUS_MAIN; if (auto window = self->_wm.getWindowForNative(surface)) { self->_serial = serial; self->_focusedSurface = jwm::ref(window); - window->setCursor(jwm::MouseCursor::ARROW); + window->setCursorMaybe(jwm::MouseCursor::ARROW, true); // frame probably isn't called so I immediately call self->mouseUpdateUnscaled(wl_fixed_to_int(surface_x), wl_fixed_to_int(surface_y), 0, 0, self->_mouseMask); + } else if (auto decoration = Decoration::getDecorationForSurface(surface, &focus)) { + self->_serial = serial; + auto window = jwm::ref(&decoration->_window); + self->_focusedSurface = window; + window->setCursorMaybe(jwm::MouseCursor::ARROW, true); } + self->_decorationFocus = focus; } static void pointerLeave(void* data, wl_pointer* pointer, uint32_t serial, @@ -33,10 +41,43 @@ static void pointerLeave(void* data, wl_pointer* pointer, uint32_t serial, return; } auto self = reinterpret_cast(data); - if (self->_focusedSurface && self->_focusedSurface->isNativeSelf(surface)) { + DecorationFocus focus = DECORATION_FOCUS_MAIN; + Decoration* decoration = Decoration::getDecorationForSurface(surface, &focus); + if (self->_focusedSurface && self->_focusedSurface->isNativeSelf(surface) && (!decoration || !decoration->_window.isNativeSelf(surface))) { jwm::unref(&self->_focusedSurface); self->_mouseMask = 0; self->_serial = 0; + self->_decorationFocus = DECORATION_FOCUS_MAIN; + } +} +static xdg_toplevel_resize_edge resizeEdge(DecorationFocus focus, WindowWayland& window, int x, int y) { + switch (focus) { + case DECORATION_FOCUS_MAIN: + // ??? + return XDG_TOPLEVEL_RESIZE_EDGE_NONE; + case DECORATION_FOCUS_TOP: + if (y < DECORATION_TOP_HEIGHT / 2) + return XDG_TOPLEVEL_RESIZE_EDGE_TOP; + else + return XDG_TOPLEVEL_RESIZE_EDGE_NONE; + case DECORATION_FOCUS_LEFT: + if (y < DECORATION_TOP_HEIGHT / 2) + return XDG_TOPLEVEL_RESIZE_EDGE_TOP_LEFT; + else if (y > DECORATION_BOTTOM_Y(window)) + return XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_LEFT; + else + return XDG_TOPLEVEL_RESIZE_EDGE_LEFT; + case DECORATION_FOCUS_RIGHT: + if (y < DECORATION_TOP_HEIGHT / 2) + return XDG_TOPLEVEL_RESIZE_EDGE_TOP_RIGHT; + else if (y > DECORATION_BOTTOM_Y(window)) + return XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_RIGHT; + else + return XDG_TOPLEVEL_RESIZE_EDGE_RIGHT; + case DECORATION_FOCUS_BOTTOM: + return XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM; + default: + return XDG_TOPLEVEL_RESIZE_EDGE_NONE; } } static void pointerMotion(void* data, wl_pointer* pointer, uint32_t time, @@ -46,12 +87,46 @@ static void pointerMotion(void* data, wl_pointer* pointer, uint32_t time, return; } auto self = reinterpret_cast(data); - self->_absX = wl_fixed_to_int(surface_x); - self->_absY = wl_fixed_to_int(surface_y); - self->_movement = true; + self->unhide(); + if (self->_decorationFocus != DECORATION_FOCUS_MAIN && !self->_focusedSurface) + return; + int x = wl_fixed_to_int(surface_x); + int y = wl_fixed_to_int(surface_y); + // ??? + self->_absX = x; + self->_absY = y; + switch (self->_decorationFocus) { + case DECORATION_FOCUS_MAIN: + self->_movement = true; + break; + default: + auto edge = resizeEdge(self->_decorationFocus, *self->_focusedSurface, x, y); + switch (edge) { + case XDG_TOPLEVEL_RESIZE_EDGE_TOP: + case XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM: + self->_focusedSurface->setCursor(jwm::MouseCursor::RESIZE_NS); + break; + case XDG_TOPLEVEL_RESIZE_EDGE_LEFT: + case XDG_TOPLEVEL_RESIZE_EDGE_RIGHT: + self->_focusedSurface->setCursor(jwm::MouseCursor::RESIZE_WE); + break; + case XDG_TOPLEVEL_RESIZE_EDGE_TOP_LEFT: + case XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_RIGHT: + self->_focusedSurface->setCursor(jwm::MouseCursor::RESIZE_NWSE); + break; + case XDG_TOPLEVEL_RESIZE_EDGE_TOP_RIGHT: + case XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_LEFT: + self->_focusedSurface->setCursor(jwm::MouseCursor::RESIZE_NESW); + break; + default: + self->_focusedSurface->setCursor(jwm::MouseCursor::POINTING_HAND); + break; + } + break; + + } // I only unhide here, bc i'm unsure of what exactly is wanted with mouse lock. // This event isn't sent on mouse lock, only relative events are sent. - self->unhide(); } static void pointerButton(void* data, wl_pointer* pointer, uint32_t serial, @@ -62,6 +137,32 @@ static void pointerButton(void* data, wl_pointer* pointer, uint32_t serial, auto window = self->_focusedSurface; if (!window) return; int scale = window->getIntScale(); + if (self->_decorationFocus != DECORATION_FOCUS_MAIN) { + if (button != BTN_LEFT || state == 0) + return; + switch (self->_decorationFocus) { + case DECORATION_FOCUS_CLOSE_BUTTON: + window->dispatch(EventWindowCloseRequest::kInstance); + break; + case DECORATION_FOCUS_MIN_BUTTON: + xdg_toplevel_set_minimized(window->_decoration->_xdgToplevel); + break; + case DECORATION_FOCUS_MAX_BUTTON: + if (window->_decoration->_maximized) + xdg_toplevel_unset_maximized(window->_decoration->_xdgToplevel); + else + xdg_toplevel_set_maximized(window->_decoration->_xdgToplevel); + break; + default: + xdg_toplevel_resize_edge edge = resizeEdge(self->_decorationFocus, *window, self->_absX, self->_absY); + if (edge == XDG_TOPLEVEL_RESIZE_EDGE_NONE) + xdg_toplevel_move(window->_decoration->_xdgToplevel, self->_seat, serial); + else + xdg_toplevel_resize(window->_decoration->_xdgToplevel, self->_seat, serial, static_cast(edge)); + break; + } + return; + } if (state == 0) { // release switch (button) { @@ -212,9 +313,10 @@ static void relativePointerRelativeMotion(void* data, zwp_relative_pointer_v1* r static zwp_relative_pointer_v1_listener relativePointerListener = { .relative_motion = relativePointerRelativeMotion }; -Pointer::Pointer(wl_pointer* pointer, WindowManagerWayland* wm): +Pointer::Pointer(wl_seat* seat, wl_pointer* pointer, WindowManagerWayland* wm): _pointer(pointer), - _wm(*wm) + _wm(*wm), + _seat(seat) { _surface = wl_compositor_create_surface(_wm.compositor); wl_pointer_add_listener(pointer, &_pointerListener, this); @@ -369,10 +471,12 @@ void Pointer::hide() { void Pointer::unhide() { if (!_hidden) return; _hidden = false; - setCursor(_scale, _cursor); + setCursor(_scale, _cursor, true); } -void Pointer::setCursor(int scale, jwm::MouseCursor cursor) { +void Pointer::setCursor(int scale, jwm::MouseCursor cursor, bool force) { + if (!force && _cursor == cursor && _scale == scale) + return; _cursor = cursor; _scale = scale; auto wayCursor = getCursorFor(scale, cursor)->images[0]; diff --git a/wayland/cc/Pointer.hh b/wayland/cc/Pointer.hh index 0f3b1f26..739a3399 100644 --- a/wayland/cc/Pointer.hh +++ b/wayland/cc/Pointer.hh @@ -6,15 +6,18 @@ #include "MouseCursor.hh" #include #include +#include "Decoration.hh" namespace jwm { class WindowManagerWayland; class WindowWayland; class Pointer { public: - Pointer(wl_pointer* pointer, jwm::WindowManagerWayland* wm); + Pointer(wl_seat* seat, wl_pointer* pointer, jwm::WindowManagerWayland* wm); ~Pointer(); + wl_seat* _seat = nullptr; + wl_pointer* _pointer = nullptr; wl_pointer* getPointer() const { return _pointer; @@ -31,6 +34,7 @@ namespace jwm { } WindowWayland* _focusedSurface = nullptr; + DecorationFocus _decorationFocus = DECORATION_FOCUS_MAIN; WindowWayland* getFocusedSurface() const { return _focusedSurface; } @@ -78,7 +82,7 @@ namespace jwm { jwm::MouseCursor _cursor = jwm::MouseCursor::ARROW; int _scale = 1; - void setCursor(int scale, jwm::MouseCursor cursor); + void setCursor(int scale, jwm::MouseCursor cursor, bool force); static bool ownPointer(wl_pointer* pointer); diff --git a/wayland/cc/WindowManagerWayland.cc b/wayland/cc/WindowManagerWayland.cc index 287a7130..4bbd59d9 100644 --- a/wayland/cc/WindowManagerWayland.cc +++ b/wayland/cc/WindowManagerWayland.cc @@ -17,7 +17,6 @@ #include "Log.hh" #include #include "Output.hh" -#include #include #include #include @@ -31,13 +30,17 @@ wl_registry_listener WindowManagerWayland::_registryListener = { .global = WindowManagerWayland::registryHandleGlobal, .global_remove = WindowManagerWayland::registryHandleGlobalRemove }; -libdecor_interface WindowManagerWayland::_decorInterface = { - .error = WindowManagerWayland::libdecorError -}; wl_seat_listener WindowManagerWayland::_seatListener = { .capabilities = WindowManagerWayland::seatCapabilities, .name = WindowManagerWayland::seatName }; + +static void xdgWmBasePing(void* data, xdg_wm_base* wm, uint32_t serial) { + xdg_wm_base_pong(wm, serial); +} +static xdg_wm_base_listener _wmListener = { + .ping = xdgWmBasePing +}; WindowManagerWayland::WindowManagerWayland(): display(wl_display_connect(nullptr)) { registry = wl_display_get_registry(display); @@ -46,17 +49,17 @@ WindowManagerWayland::WindowManagerWayland(): - if (!(shm && compositor && deviceManager && seat)) { + if (!(shm && compositor && deviceManager && seat && xdgWm && subcompositor && viewporter)) { // ??? // Bad. Means our compositor no supportie : ( throw std::system_error(ENOTSUP, std::generic_category(), "Unsupported compositor"); } - + + xdg_wm_base_add_listener(xdgWm, &_wmListener, nullptr); // ???: Moving this after libdecor_new causes input to not work wl_seat_add_listener(seat, &_seatListener, this); dataDevice = wl_data_device_manager_get_data_device(deviceManager, seat); wl_data_device_add_listener(dataDevice, &_deviceListener, this); - decorCtx = libdecor_new(display, &_decorInterface); wl_display_roundtrip(display); @@ -159,11 +162,6 @@ void WindowManagerWayland::runLoop() { } -void WindowManagerWayland::libdecorError(libdecor* context, enum libdecor_error error, const char* message) { - // ??? - fprintf(stderr, "Caught error (%d): %s\n", error, message); - throw std::runtime_error("lib decor error > : ("); -} void WindowManagerWayland::_processCallbacks() { { // process ui thread callbacks @@ -182,7 +180,7 @@ void WindowManagerWayland::_processCallbacks() { for (auto p : copy) { if (p->isRedrawRequested()) { p->unsetRedrawRequest(); - if (p->_visible && p->_configured) { + if (p->_visible && p->isConfigured()) { if (p->_layer) { p->_layer->makeCurrent(); } @@ -260,6 +258,18 @@ void WindowManagerWayland::registryHandleGlobal(void* data, wl_registry *registr } else if (strcmp(interface, zwp_relative_pointer_manager_v1_interface.name) == 0) { self->relativePointerManager = (zwp_relative_pointer_manager_v1*)wl_registry_bind(registry, name, &zwp_relative_pointer_manager_v1_interface, 1); + } else if (strcmp(interface, wp_viewporter_interface.name) == 0) { + self->viewporter = (wp_viewporter*)wl_registry_bind(registry, name, + &wp_viewporter_interface, 1); + } else if (strcmp(interface, wl_subcompositor_interface.name) == 0) { + self->subcompositor = (wl_subcompositor*)wl_registry_bind(registry, name, + &wl_subcompositor_interface, 1); + } else if (strcmp(interface, xdg_wm_base_interface.name) == 0) { + self->xdgWm = (xdg_wm_base*)wl_registry_bind(registry, name, + &xdg_wm_base_interface, 1); + } else if (strcmp(interface, zxdg_decoration_manager_v1_interface.name) == 0) { + self->decorationManager = (zxdg_decoration_manager_v1*)wl_registry_bind(registry, name, + &zxdg_decoration_manager_v1_interface, 1); } } void WindowManagerWayland::registryHandleGlobalRemove(void* data, wl_registry *registry, uint32_t name) { @@ -283,7 +293,7 @@ void WindowManagerWayland::seatCapabilities(void* data, wl_seat* seat, uint32_t auto self = reinterpret_cast(data); if ((capabilities & WL_SEAT_CAPABILITY_POINTER) && !self->_pointer) { - self->_pointer.reset(new Pointer(wl_seat_get_pointer(seat), self)); + self->_pointer.reset(new Pointer(seat, wl_seat_get_pointer(seat), self)); } else if (!(capabilities & WL_SEAT_CAPABILITY_POINTER) && self->_pointer) { self->_pointer.reset(); } diff --git a/wayland/cc/WindowManagerWayland.hh b/wayland/cc/WindowManagerWayland.hh index 61d396c6..e0c63917 100644 --- a/wayland/cc/WindowManagerWayland.hh +++ b/wayland/cc/WindowManagerWayland.hh @@ -14,7 +14,6 @@ #include #include #include "Output.hh" -#include #include #include #include @@ -23,6 +22,9 @@ #include #include #include +#include +#include +#include namespace jwm { class WindowWayland; @@ -50,9 +52,6 @@ namespace jwm { static void registryHandleGlobalRemove(void* data, wl_registry *registry, uint32_t name); - static libdecor_interface _decorInterface; - static void libdecorError(libdecor* context, enum libdecor_error error, const char* message); - static wl_seat_listener _seatListener; static void seatCapabilities(void* data, wl_seat* seat, uint32_t capabilities); @@ -74,6 +73,11 @@ namespace jwm { wl_seat* seat = nullptr; zwp_pointer_constraints_v1* pointerConstraints = nullptr; zwp_relative_pointer_manager_v1* relativePointerManager = nullptr; + xdg_wm_base* xdgWm = nullptr; + wp_viewporter* viewporter = nullptr; + zxdg_decoration_manager_v1* decorationManager = nullptr; + wl_subcompositor* subcompositor = nullptr; + std::unique_ptr _pointer = nullptr; Pointer* getPointer() const { return _pointer.get(); @@ -99,7 +103,6 @@ namespace jwm { } - libdecor* decorCtx = nullptr; EGLDisplay _eglDisplay = EGL_NO_DISPLAY; std::list outputs; diff --git a/wayland/cc/WindowWayland.cc b/wayland/cc/WindowWayland.cc index a6b6d9bc..cc063139 100644 --- a/wayland/cc/WindowWayland.cc +++ b/wayland/cc/WindowWayland.cc @@ -21,13 +21,6 @@ wl_surface_listener WindowWayland::_surfaceListener = { #endif }; -libdecor_frame_interface WindowWayland::_libdecorFrameInterface = { - .configure = WindowWayland::decorFrameConfigure, - .close = WindowWayland::decorFrameClose, - .commit = WindowWayland::decorFrameCommit, - .dismiss_popup = WindowWayland::decorFrameDismissPopup -}; - WindowWayland::WindowWayland(JNIEnv* env, WindowManagerWayland& windowManager): jwm::Window(env), _windowManager(windowManager), @@ -44,14 +37,14 @@ WindowWayland::~WindowWayland() { void WindowWayland::setTitle(const std::string& title) { _title = title; - if (_frame) - libdecor_frame_set_title(_frame, _title.c_str()); + if (_decoration) + _decoration->setTitle(title); } void WindowWayland::setTitlebarVisible(bool isVisible) { _titlebarVisible = isVisible; - if (_frame) - libdecor_frame_set_visibility(_frame, isVisible); + if (_decoration) + _decoration->setVisible(isVisible); } void WindowWayland::close() { @@ -69,17 +62,11 @@ void WindowWayland::hide() { wl_surface_destroy(_waylandWindow); } _waylandWindow = nullptr; - // HACK: memory corruption issue in libdecor: - // https://gitlab.freedesktop.org/libdecor/libdecor/-/issues/59 - // Also causes protocol error that doesn't really make sense - /* - if (_frame) { - libdecor_frame_unref(_frame); + if (_decoration) { + _decoration->close(); } - */ - _frame = nullptr; + _decoration = nullptr; _windowManager.unregisterWindow(this); - _configured = false; } void WindowWayland::maximize() { // impl me :) @@ -98,7 +85,9 @@ void WindowWayland::setFullScreen(bool isFullScreen) { } bool WindowWayland::isFullScreen() { - return _fullscreen; + if (_decoration) + return _decoration->_fullscreen; + return false; } void WindowWayland::setLayer(ILayerWayland* layer) { @@ -107,10 +96,14 @@ void WindowWayland::setLayer(ILayerWayland* layer) { } void WindowWayland::getDecorations(int& left, int& top, int& right, int& bottom) { // impl me : ) - left = 0; - right = 0; - top = 0; - bottom = 0; + if (_decoration) { + _decoration->getBorders(left, top, right, bottom); + } else { + left = 0; + right = 0; + top = 0; + bottom = 0; + } } @@ -123,9 +116,6 @@ void WindowWayland::getContentPosition(int& posX, int& posY) { bool WindowWayland::resize(int width, int height) { if (width < 0 || height < 0) return false; - // don't allow size to be set if currently tiled - if (!_floating) - return false; // Width and height are in absolute pixel units, and wayland will // complain if you try to set a width/height that isn't a multiple of scale. if ((width % _scale) != 0 || (height % _scale) != 0) @@ -179,6 +169,11 @@ wl_cursor* WindowWayland::_getCursorFor(jwm::MouseCursor cursor) { bool WindowWayland::init() { return true; } +bool WindowWayland::isConfigured() { + if (_decoration) + return _decoration->_configured; + return false; +} void WindowWayland::show() { _waylandWindow = wl_compositor_create_surface(_windowManager.compositor); @@ -186,12 +181,11 @@ void WindowWayland::show() wl_proxy_set_tag((wl_proxy*) _waylandWindow, &AppWayland::proxyTag); _windowManager.registerWindow(this); wl_display_roundtrip(_windowManager.display); - _frame = libdecor_decorate(_windowManager.decorCtx, _waylandWindow, &_libdecorFrameInterface, this); + _decoration = new Decoration(*this); setTitle(_title); setTitlebarVisible(_titlebarVisible); - libdecor_frame_map(_frame); - - _configured = false; + // map + wl_surface_commit(_waylandWindow); _visible = true; } @@ -218,9 +212,12 @@ void WindowWayland::setVisible(bool isVisible) { } } -void jwm::WindowWayland::setCursor(jwm::MouseCursor cursor) { +void jwm::WindowWayland::setCursorMaybe(jwm::MouseCursor cursor, bool force) { if (!_windowManager.getPointer()) return; - _windowManager.getPointer()->setCursor(_scale, cursor); + _windowManager.getPointer()->setCursor(_scale, cursor, force); +} +void jwm::WindowWayland::setCursor(jwm::MouseCursor cursor) { + setCursorMaybe(cursor, false); } // what do??? @@ -283,82 +280,9 @@ static void frameCallbackDone(void* data, wl_callback* cb, uint32_t cb_data) { wl_callback_listener jwm::WindowWayland::_frameCallback = { .done = frameCallbackDone }; - -void jwm::WindowWayland::decorFrameConfigure(libdecor_frame* frame, libdecor_configuration* configuration, - void *userData) { - auto self = reinterpret_cast(userData); - int width = 0, height = 0; - libdecor_window_state winState; - - libdecor_configuration_get_content_size(configuration, frame, &width, &height); - - - width = (width <= 0) ? self->_floatingWidth : width; - height = (height <= 0) ? self->_floatingHeight : height; - - libdecor_state* state = libdecor_state_new(width, height); - libdecor_frame_commit(frame, state, configuration); - libdecor_state_free(state); - - if (libdecor_configuration_get_window_state(configuration, &winState)) { - bool active = (winState & LIBDECOR_WINDOW_STATE_ACTIVE) != 0; - bool maximized = (winState & LIBDECOR_WINDOW_STATE_MAXIMIZED) != 0; - bool fullscreen = (winState & LIBDECOR_WINDOW_STATE_FULLSCREEN) != 0; - // Some compositors (like weston) don't actually tell me on focus in and focus out. - // Libdecor simply sends an active at the beginning and keeps chugging. - if (active != self->_active) - if (active) - self->dispatch(classes::EventWindowFocusIn::kInstance); - else - self->dispatch(classes::EventWindowFocusOut::kInstance); - self->_active = active; - if (maximized != self->_maximized) - if (maximized) - self->dispatch(classes::EventWindowMaximize::kInstance); - self->_maximized = maximized; - // ??? - /* - if (fullscreen != self->_fullscreen) - if (fullscreen) - self->dispatch(classes::EventWindowFullScreenEnter::kInstance); - else - self->dispatch(classes::EventWindowFullScreenLeave::kInstance); - */ - self->_fullscreen = fullscreen; - self->_floating = libdecor_frame_is_floating(frame); - } - // before width - if (!self->_configured) { - if (self->_layer) - self->_layer->attachBuffer(); - } - self->_configured = true; - if (self->_width != width || self->_height != height) { - if (libdecor_frame_is_floating(frame)) { - if (width > 0) - self->_floatingWidth = width; - if (height > 0) - self->_floatingHeight = height; - } - - - self->_adaptSize(width, height); - } -} -void jwm::WindowWayland::decorFrameClose(libdecor_frame* frame, void* userData) { - WindowWayland* self = reinterpret_cast(userData); - self->dispatch(classes::EventWindowCloseRequest::kInstance); -} -void jwm::WindowWayland::decorFrameCommit(libdecor_frame* frame, void* userData) { - WindowWayland* self = reinterpret_cast(userData); - if (self->_waylandWindow) { - wl_surface_commit(self->_waylandWindow); - } -} -void jwm::WindowWayland::decorFrameDismissPopup(libdecor_frame* frame, const char* seatName, void* userData) {} void jwm::WindowWayland::_adaptSize(int newWidth, int newHeight) { using namespace classes; - if (!_configured) { + if (!isConfigured()) { return; } if (newWidth == _width && newHeight == _height && _scale == _oldScale) return; diff --git a/wayland/cc/WindowWayland.hh b/wayland/cc/WindowWayland.hh index ee25b658..e853acc2 100644 --- a/wayland/cc/WindowWayland.hh +++ b/wayland/cc/WindowWayland.hh @@ -6,8 +6,9 @@ #include "WindowManagerWayland.hh" #include "ILayerWayland.hh" #include "ScreenInfo.hh" -#include #include +#include "Decoration.hh" + namespace jwm { class WindowWayland: public jwm::Window { public: @@ -51,6 +52,7 @@ namespace jwm { void setFullScreen(bool isFullScreen); bool isFullScreen(); + void setCursorMaybe(jwm::MouseCursor cursor, bool force); void setCursor(jwm::MouseCursor cursor); void setLayer(ILayerWayland* layer); @@ -62,11 +64,6 @@ namespace jwm { static void surfacePreferredBufferScale(void* data, wl_surface* surface, int factor); static void surfacePreferredBufferTransform(void* data, wl_surface* surface, uint32_t transform); - static void decorFrameConfigure(libdecor_frame* frame, libdecor_configuration* config, void* userData); - static void decorFrameClose(libdecor_frame* frame, void* userData); - static void decorFrameCommit(libdecor_frame* frame, void* userData); - static void decorFrameDismissPopup(libdecor_frame* frame, const char* seatName, void* userData); - void _adaptSize(int newWidth, int newHeight); bool isNativeSelf(wl_surface* surface); @@ -76,6 +73,7 @@ namespace jwm { void hideCursor(bool hidden); + bool isConfigured(); int _posX = -1; int _posY = -1; @@ -93,11 +91,6 @@ namespace jwm { bool _canMaximize = false; bool _canFullscreen = false; bool _visible = false; - bool _configured = false; - bool _active = false; - bool _maximized = false; - bool _fullscreen = false; - bool _floating = false; bool _isRedrawRequested = false; std::string _title; bool _titlebarVisible = true; @@ -111,14 +104,12 @@ namespace jwm { WindowManagerWayland& _windowManager; ILayerWayland* _layer = nullptr; wl_surface* _waylandWindow = nullptr; - libdecor_frame* _frame = nullptr; + Decoration* _decoration = nullptr; std::list _outputs; wl_cursor_theme* theme = nullptr; static wl_surface_listener _surfaceListener; - static libdecor_frame_interface _libdecorFrameInterface; - static wl_callback_listener _frameCallback; }; From b61b050dda81a9051fc4739768de6b95389a6d1c Mon Sep 17 00:00:00 2001 From: BulbyVR <26726264+TheDrawingCoder-Gamer@users.noreply.github.com> Date: Wed, 3 Jan 2024 18:06:59 -0500 Subject: [PATCH 72/95] Don't crash on unmaximize --- proto,log | 63 ++++++++++++++++++++++++++++++++++++++++ wayland/cc/Decoration.cc | 33 ++++++++++++++++----- wayland/cc/Pointer.cc | 3 +- 3 files changed, 91 insertions(+), 8 deletions(-) create mode 100644 proto,log diff --git a/proto,log b/proto,log new file mode 100644 index 00000000..ec2f45dc --- /dev/null +++ b/proto,log @@ -0,0 +1,63 @@ +Date: 2024-01-03 EST +[17:20:09.252] weston 13.0.0 + https://wayland.freedesktop.org + Bug reports to: https://gitlab.freedesktop.org/wayland/weston/issues/ + Build: 13.0.0 +[17:20:09.252] Command line: weston --logging-scopes proto,log +[17:20:09.252] OS: Linux, 5.19.8-arch1-1-surface, #1 SMP PREEMPT_DYNAMIC Wed, 14 Sep 2022 22:06:46 +0000, x86_64 +[17:20:09.252] Flight recorder: enabled +[17:20:09.252] Using config file '/home/bulby/.config/weston.ini' +[17:20:09.252] Output repaint window is 7 ms maximum. +[17:20:09.252] Loading module '/usr/lib/libweston-13/wayland-backend.so' +[17:20:09.289] Loading module '/usr/lib/libweston-13/gl-renderer.so' +[17:20:09.335] Using rendering device: /dev/dri/renderD128 +[17:20:09.335] EGL version: 1.5 +[17:20:09.335] EGL vendor: Mesa Project +[17:20:09.335] EGL client APIs: OpenGL OpenGL_ES +[17:20:09.335] EGL features: + EGL Wayland extension: yes + context priority: yes + buffer age: yes + partial update: no + swap buffers with damage: yes + configless context: yes + surfaceless context: yes + dmabuf support: modifiers +[17:20:09.337] GL version: OpenGL ES 3.2 Mesa 23.3.1-arch1.1 +[17:20:09.337] GLSL version: OpenGL ES GLSL ES 3.20 +[17:20:09.337] GL vendor: Intel +[17:20:09.337] GL renderer: Mesa Intel(R) UHD Graphics 620 (KBL GT2) +[17:20:09.343] GL ES 3.2 - renderer features: + read-back format: ARGB8888 + glReadPixels supports y-flip: yes + wl_shm 10 bpc formats: yes + wl_shm 16 bpc formats: yes + wl_shm half-float formats: yes + internal R and RG formats: yes + OES_EGL_image_external: yes +[17:20:09.343] Using GL renderer +[17:20:09.343] Registered plugin API 'weston_windowed_output_api_v2' of size 16 +[17:20:09.343] Color manager: no-op +[17:20:09.343] Output 'wayland0' attempts EOTF mode: SDR +[17:20:09.343] Output 'wayland0' using color profile: stock sRGB color profile +[17:20:09.343] Creating 1024x640 wayland output at (0, 0) +[17:20:09.346] wayland-backend: Using xdg_wm_base +[17:20:09.347] Chosen EGL config details: id: 41 rgba: 8 8 8 8 buf: 32 dep: 0 stcl: 0 int: 0-1 type: win vis_id: 0 +[17:20:09.355] Output 'wayland0' enabled with head(s) wayland0 +[17:20:09.355] Compositor capabilities: + arbitrary surface rotation: yes + screen capture uses y-flip: yes + cursor planes: no + arbitrary resolutions: no + view mask clipping: yes + explicit sync: yes + color operations: yes + presentation clock: CLOCK_MONOTONIC_RAW, id 4 + presentation clock resolution: 0.000000001 s +[17:20:09.355] libwayland: unable to lock lockfile /run/user/1000/wayland-1.lock, maybe another compositor is running +[17:20:09.355] Loading module '/usr/lib/weston/desktop-shell.so' +[17:20:09.358] launching '/usr/lib/weston/weston-keyboard' +[17:20:09.360] launching '/usr/lib/weston/weston-desktop-shell' +[17:20:09.365] Chosen EGL config details: id: 41 rgba: 8 8 8 8 buf: 32 dep: 0 stcl: 0 int: 0-1 type: win vis_id: 0 +[17:20:20.076] caught signal 12 +[17:20:20.077] BUG: finalizing a layer with views still on it. diff --git a/wayland/cc/Decoration.cc b/wayland/cc/Decoration.cc index 77a01766..f8d8925c 100644 --- a/wayland/cc/Decoration.cc +++ b/wayland/cc/Decoration.cc @@ -94,6 +94,10 @@ static void _xdgSurfaceConfigure(void* data, xdg_surface* surface, uint32_t seri if (window._layer) window._layer->attachBuffer(); } + // ask to configure _before_ commit. jank. + if (self->_floating) { + xdg_surface_ack_configure(self->_xdgSurface, serial); + } if (window.getUnscaledWidth() != width || window.getUnscaledHeight() != height) { if (self->_floating) { if (width > 0) { @@ -113,9 +117,13 @@ static void _xdgSurfaceConfigure(void* data, xdg_surface* surface, uint32_t seri if (!self->_top.surface) self->_showDecorations(); } - wl_surface_commit(window._waylandWindow); - xdg_surface_ack_configure(self->_xdgSurface, serial); + // at the end so that the size isn't adapted on first render self->_configured = true; + wl_surface_commit(window._waylandWindow); + if (!self->_floating) { + + xdg_surface_ack_configure(self->_xdgSurface, serial); + } } static xdg_surface_listener _xdgSurfaceListener = { @@ -127,7 +135,7 @@ static void _xdgToplevelConfigure(void* data, xdg_toplevel* toplevel, int width, self->_pendingWidth = width; self->_pendingHeight = height; - if (!self->_serverSide) { + if (!self->_serverSide && self->_isVisible) { self->_pendingWidth -= DECORATION_LEFT_WIDTH + DECORATION_RIGHT_WIDTH; self->_pendingHeight -= DECORATION_TOP_HEIGHT + DECORATION_BOTTOM_HEIGHT; } @@ -297,9 +305,17 @@ void Decoration::_adaptSize() { _resizeDecoration(&_close, DECORATION_CLOSE_X(_window), DECORATION_CLOSE_Y, DECORATION_CLOSE_WIDTH, DECORATION_CLOSE_HEIGHT); _resizeDecoration(&_min, DECORATION_MIN_X(_window), DECORATION_MIN_Y, DECORATION_MIN_WIDTH, DECORATION_MIN_HEIGHT); _resizeDecoration(&_max, DECORATION_MAX_X(_window), DECORATION_MAX_Y, DECORATION_MAX_WIDTH, DECORATION_MAX_HEIGHT); + + if (!_serverSide && _isVisible) + xdg_surface_set_window_geometry(_xdgSurface, -DECORATION_LEFT_WIDTH, -DECORATION_TOP_HEIGHT, + _window.getUnscaledWidth() + DECORATION_RIGHT_WIDTH + DECORATION_LEFT_WIDTH, + _window.getUnscaledHeight() + DECORATION_BOTTOM_HEIGHT + DECORATION_TOP_HEIGHT); + else + xdg_surface_set_window_geometry(_xdgSurface, 0, 0, _window.getUnscaledWidth(), _window.getUnscaledHeight()); } bool Decoration::ownDecorationSurface(wl_surface* surface) { + if (!surface) return false; return wl_proxy_get_tag((wl_proxy*)surface) == &proxyTag; } @@ -335,15 +351,18 @@ void Decoration::setVisible(bool isVisible) { if (isVisible) { if (_decoration) { zxdg_toplevel_decoration_v1_set_mode(_decoration, ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE); + } else { + _showDecorations(); } } else { + _destroyDecorations(); if (_decoration) { zxdg_toplevel_decoration_v1_set_mode(_decoration, ZXDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE); + } else { + // TODO: request reconfigure + wl_surface_commit(_window._waylandWindow); } - _destroyDecorations(); } - - } } @@ -354,7 +373,7 @@ void Decoration::setTitle(const std::string& title) { } void Decoration::getBorders(int& left, int& top, int& right, int& bottom) { - if (_serverSide) { + if (_serverSide || !_isVisible) { left = 0; top = 0; right = 0; diff --git a/wayland/cc/Pointer.cc b/wayland/cc/Pointer.cc index 36def276..6fee9743 100644 --- a/wayland/cc/Pointer.cc +++ b/wayland/cc/Pointer.cc @@ -255,6 +255,7 @@ static void pointerFrame(void* data, wl_pointer* pointer) auto self = reinterpret_cast(data); auto win = self->_focusedSurface; if (!win) return; + if (self->_decorationFocus != DECORATION_FOCUS_MAIN) return; if (self->_dX != 0.0f || self->_dY != 0.0f) { auto env = app.getJniEnv(); @@ -306,7 +307,7 @@ wl_pointer_listener Pointer::_pointerListener = { static void relativePointerRelativeMotion(void* data, zwp_relative_pointer_v1* relative, uint32_t utime_hi, uint32_t utime_lo, wl_fixed_t dx, wl_fixed_t dy, wl_fixed_t dx_unaccel, wl_fixed_t dy_unaccel) { auto self = reinterpret_cast(data); - + if (self->_decorationFocus != DECORATION_FOCUS_MAIN) return; self->_dXPos += static_cast(wl_fixed_to_double(dx)); self->_dYPos += static_cast(wl_fixed_to_double(dy)); } From 26f1bc79074aabf48a6d474fbe14d9ac11f55809 Mon Sep 17 00:00:00 2001 From: BulbyVR <26726264+TheDrawingCoder-Gamer@users.noreply.github.com> Date: Wed, 3 Jan 2024 18:31:57 -0500 Subject: [PATCH 73/95] Implement screen-state related items --- wayland/cc/WindowWayland.cc | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/wayland/cc/WindowWayland.cc b/wayland/cc/WindowWayland.cc index cc063139..0aa918ba 100644 --- a/wayland/cc/WindowWayland.cc +++ b/wayland/cc/WindowWayland.cc @@ -69,19 +69,28 @@ void WindowWayland::hide() { _windowManager.unregisterWindow(this); } void WindowWayland::maximize() { - // impl me :) + if (!_visible || !_decoration) return; + xdg_toplevel_set_maximized(_decoration->_xdgToplevel); } void WindowWayland::minimize() { - // impl me : ) + if (!_visible || !_decoration) return; + xdg_toplevel_set_minimized(_decoration->_xdgToplevel); } void WindowWayland::restore() { - // impl me + // Not possible for minimize + if (!_visible || !_decoration) return; + xdg_toplevel_unset_maximized(_decoration->_xdgToplevel); } void WindowWayland::setFullScreen(bool isFullScreen) { - // impl me : ) + if (!_visible || !_decoration) return; + if (_decoration->_fullscreen == isFullScreen) return; + if (isFullScreen) + xdg_toplevel_set_fullscreen(_decoration->_xdgToplevel, nullptr); + else + xdg_toplevel_unset_fullscreen(_decoration->_xdgToplevel); } bool WindowWayland::isFullScreen() { From a4af84aba5d9ad3cbfb4b6d7afb3c4674e6342c9 Mon Sep 17 00:00:00 2001 From: BulbyVR <26726264+TheDrawingCoder-Gamer@users.noreply.github.com> Date: Wed, 3 Jan 2024 18:53:50 -0500 Subject: [PATCH 74/95] try fix build --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 6776c8ac..4646e375 100644 --- a/Dockerfile +++ b/Dockerfile @@ -6,7 +6,7 @@ RUN apt-get update -y && apt-get install -y software-properties-common RUN add-apt-repository -y ppa:ubuntu-toolchain-r/test RUN add-apt-repository -y ppa:openjdk-r/ppa RUN add-apt-repository -y ppa:git-core/ppa -RUN apt-get update -y && apt-get install -y wget zip python git build-essential g++-9 cmake ninja-build libxcomposite-dev libxrandr-dev libgl1-mesa-dev libxi-dev libxcursor-dev openjdk-11-jdk-headless +RUN apt-get update -y && apt-get install -y wget zip python git build-essential g++-9 cmake ninja-build libxcomposite-dev libxrandr-dev libgl1-mesa-dev libxi-dev libxcursor-dev openjdk-11-jdk-headless libegl1-mesa libegl1-mesa-dev extra-cmake-modules wayland-protocols wayland-utils libwayland-dev RUN wget --no-verbose https://downloads.apache.org/maven/maven-3/3.6.3/binaries/apache-maven-3.6.3-bin.tar.gz --output-document - | tar -xz RUN echo 'export JAVA_HOME=/usr/lib/jvm/java-11-openjdk-amd64' > /etc/profile.d/02-jdk.sh RUN echo 'export PATH=$JAVA_HOME/bin:/root/apache-maven-3.6.3/bin:$PATH' >> /etc/profile.d/02-jdk.sh From 25972d86b940f411dce1b1c2005afb9894b52f5b Mon Sep 17 00:00:00 2001 From: BulbyVR <26726264+TheDrawingCoder-Gamer@users.noreply.github.com> Date: Wed, 3 Jan 2024 19:48:00 -0500 Subject: [PATCH 75/95] Hidden actually means just no title bar --- examples/dashboard/java/PanelScreens.java | 12 ++- wayland/cc/Decoration.cc | 100 ++++++++++++++-------- wayland/cc/Decoration.hh | 9 +- wayland/cc/WindowWayland.cc | 2 +- 4 files changed, 81 insertions(+), 42 deletions(-) diff --git a/examples/dashboard/java/PanelScreens.java b/examples/dashboard/java/PanelScreens.java index 378f8ef3..dd287ab7 100644 --- a/examples/dashboard/java/PanelScreens.java +++ b/examples/dashboard/java/PanelScreens.java @@ -18,7 +18,7 @@ public PanelScreens(Window window) { super(window); if (Platform.MACOS == Platform.CURRENT) { titleStyles = new Options("Default", "Hidden", "Transparent", "Unified", "Unified Compact", "Unified Transparent", "Unified Compact Transparent"); - } else if (Platform.X11 == Platform.CURRENT) { + } else if (Platform.X11 == Platform.CURRENT || Platform.WAYLAND == Platform.CURRENT) { titleStyles = new Options("Default", "Hidden"); } } @@ -66,6 +66,14 @@ public void setTitleStyle(String style) { case "Hidden" -> w.setTitlebarVisible(false); } + } else if (Platform.WAYLAND == Platform.CURRENT) { + WindowWayland w = (WindowWayland) window; + switch (style) { + case "Default" -> + w.setTitlebarVisible(true); + case "Hidden" -> + w.setTitlebarVisible(false); + } } } @@ -171,4 +179,4 @@ public void paintImpl(Canvas canvas, int width, int height, float scale) { canvas.translate(0, lineHeight); canvas.restore(); } -} \ No newline at end of file +} diff --git a/wayland/cc/Decoration.cc b/wayland/cc/Decoration.cc index f8d8925c..b0cd4947 100644 --- a/wayland/cc/Decoration.cc +++ b/wayland/cc/Decoration.cc @@ -113,9 +113,9 @@ static void _xdgSurfaceConfigure(void* data, xdg_surface* surface, uint32_t seri if (self->_serverSide) { if (self->_top.surface) self->_destroyDecorations(); - } else if (self->_isVisible) { + } else { if (!self->_top.surface) - self->_showDecorations(); + self->_showDecorations(!self->_isVisible); } // at the end so that the size isn't adapted on first render self->_configured = true; @@ -135,9 +135,13 @@ static void _xdgToplevelConfigure(void* data, xdg_toplevel* toplevel, int width, self->_pendingWidth = width; self->_pendingHeight = height; - if (!self->_serverSide && self->_isVisible) { + if (!self->_serverSide) { self->_pendingWidth -= DECORATION_LEFT_WIDTH + DECORATION_RIGHT_WIDTH; - self->_pendingHeight -= DECORATION_TOP_HEIGHT + DECORATION_BOTTOM_HEIGHT; + self->_pendingHeight -= DECORATION_BOTTOM_HEIGHT; + if (self->_isVisible) + self->_pendingHeight -= DECORATION_TOP_HEIGHT; + else + self->_pendingHeight -= DECORATION_BOTTOM_HEIGHT; } bool active = false; @@ -208,19 +212,7 @@ Decoration::Decoration(WindowWayland& window): zxdg_toplevel_decoration_v1_set_mode(_decoration, ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE); } - - _decBuffer = Buffer::createShmBuffer(_wm.shm, 1, 1, WL_SHM_FORMAT_ARGB8888); - memcpy(_decBuffer->getData(), grey_data, 1 * sizeof(uint32_t)); - _closeBuffer = Buffer::createShmBuffer(_wm.shm, 9, 9, WL_SHM_FORMAT_ARGB8888); - memcpy(_closeBuffer->getData(), close_data, 9 * 9 * sizeof(uint32_t)); - _maxBuffer = Buffer::createShmBuffer(_wm.shm, 9, 9, WL_SHM_FORMAT_ARGB8888); - memcpy(_maxBuffer->getData(), max_data, 9 * 9 * sizeof(uint32_t)); - _minBuffer = Buffer::createShmBuffer(_wm.shm, 9, 9, WL_SHM_FORMAT_ARGB8888); - memcpy(_minBuffer->getData(), min_data, 9 * 9 * sizeof(uint32_t)); - - // delay making parts until configure : ) - } void Decoration::close() { @@ -283,31 +275,60 @@ void Decoration::_destroyDecorations() { _destroyDecoration(&_max); } -void Decoration::_showDecorations() { - _makePart(&_top, _decBuffer, true, DECORATION_TOP_X, DECORATION_TOP_Y, DECORATION_TOP_WIDTH(_window), DECORATION_TOP_HEIGHT); - _makePart(&_left, _decBuffer, true, DECORATION_LEFT_X, DECORATION_LEFT_Y, DECORATION_LEFT_WIDTH, DECORATION_LEFT_HEIGHT(_window)); - _makePart(&_right, _decBuffer, true, DECORATION_RIGHT_X(_window), DECORATION_RIGHT_Y, DECORATION_RIGHT_WIDTH, DECORATION_RIGHT_HEIGHT(_window)); +void Decoration::_showDecorations(bool hidden) { + // ??? + // When destroyed these get released + _decBuffer = Buffer::createShmBuffer(_wm.shm, 1, 1, WL_SHM_FORMAT_ARGB8888); + memcpy(_decBuffer->getData(), grey_data, 1 * sizeof(uint32_t)); + _closeBuffer = Buffer::createShmBuffer(_wm.shm, 9, 9, WL_SHM_FORMAT_ARGB8888); + memcpy(_closeBuffer->getData(), close_data, 9 * 9 * sizeof(uint32_t)); + _maxBuffer = Buffer::createShmBuffer(_wm.shm, 9, 9, WL_SHM_FORMAT_ARGB8888); + memcpy(_maxBuffer->getData(), max_data, 9 * 9 * sizeof(uint32_t)); + _minBuffer = Buffer::createShmBuffer(_wm.shm, 9, 9, WL_SHM_FORMAT_ARGB8888); + memcpy(_minBuffer->getData(), min_data, 9 * 9 * sizeof(uint32_t)); + if (hidden) { + _makePart(&_top, _decBuffer, true, DECORATION_TOP_X, DECORATION_HIDDEN_TOP_Y, DECORATION_BOTTOM_WIDTH(_window), DECORATION_BOTTOM_HEIGHT); + _makePart(&_left, _decBuffer, true, DECORATION_LEFT_X, DECORATION_HIDDEN_LEFT_Y, DECORATION_LEFT_WIDTH, + DECORATION_HIDDEN_LEFT_HEIGHT(_window)); + _makePart(&_right, _decBuffer, true, DECORATION_RIGHT_X(_window), DECORATION_HIDDEN_RIGHT_Y, + DECORATION_RIGHT_WIDTH, DECORATION_HIDDEN_RIGHT_HEIGHT(_window)); + } else { + _makePart(&_top, _decBuffer, true, DECORATION_TOP_X, DECORATION_TOP_Y, DECORATION_TOP_WIDTH(_window), DECORATION_TOP_HEIGHT); + _makePart(&_left, _decBuffer, true, DECORATION_LEFT_X, DECORATION_LEFT_Y, DECORATION_LEFT_WIDTH, DECORATION_LEFT_HEIGHT(_window)); + _makePart(&_right, _decBuffer, true, DECORATION_RIGHT_X(_window), DECORATION_RIGHT_Y, DECORATION_RIGHT_WIDTH, DECORATION_RIGHT_HEIGHT(_window)); + } _makePart(&_bottom, _decBuffer, true, DECORATION_BOTTOM_X, DECORATION_BOTTOM_Y(_window), DECORATION_BOTTOM_WIDTH(_window), DECORATION_BOTTOM_HEIGHT); - - _makePart(&_close, _closeBuffer, false, DECORATION_CLOSE_X(_window), DECORATION_CLOSE_Y, DECORATION_CLOSE_WIDTH, DECORATION_CLOSE_HEIGHT); - _makePart(&_max, _maxBuffer, false, DECORATION_MAX_X(_window), DECORATION_MAX_Y, DECORATION_MAX_WIDTH, DECORATION_MAX_HEIGHT); - _makePart(&_min, _minBuffer, false, DECORATION_MIN_X(_window), DECORATION_MIN_Y, DECORATION_MIN_WIDTH, DECORATION_MIN_HEIGHT); + + if (!hidden) { + _makePart(&_close, _closeBuffer, false, DECORATION_CLOSE_X(_window), DECORATION_CLOSE_Y, DECORATION_CLOSE_WIDTH, DECORATION_CLOSE_HEIGHT); + _makePart(&_max, _maxBuffer, false, DECORATION_MAX_X(_window), DECORATION_MAX_Y, DECORATION_MAX_WIDTH, DECORATION_MAX_HEIGHT); + _makePart(&_min, _minBuffer, false, DECORATION_MIN_X(_window), DECORATION_MIN_Y, DECORATION_MIN_WIDTH, DECORATION_MIN_HEIGHT); + } } void Decoration::_adaptSize() { - _resizeDecoration(&_top, DECORATION_TOP_X, DECORATION_TOP_Y, DECORATION_TOP_WIDTH(_window), DECORATION_TOP_HEIGHT); - _resizeDecoration(&_left, DECORATION_LEFT_X, DECORATION_LEFT_Y, DECORATION_LEFT_WIDTH, DECORATION_LEFT_HEIGHT(_window)); - _resizeDecoration(&_right, DECORATION_RIGHT_X(_window), DECORATION_RIGHT_Y, DECORATION_RIGHT_WIDTH, DECORATION_RIGHT_HEIGHT(_window)); + if (!_isVisible) { + _resizeDecoration(&_top, DECORATION_TOP_X, DECORATION_HIDDEN_TOP_Y, DECORATION_BOTTOM_WIDTH(_window), DECORATION_BOTTOM_HEIGHT); + _resizeDecoration(&_left, DECORATION_LEFT_X, DECORATION_HIDDEN_LEFT_Y, DECORATION_LEFT_WIDTH, + DECORATION_HIDDEN_LEFT_HEIGHT(_window)); + _resizeDecoration(&_right, DECORATION_RIGHT_X(_window), DECORATION_HIDDEN_RIGHT_Y, DECORATION_RIGHT_WIDTH, + DECORATION_HIDDEN_RIGHT_HEIGHT(_window)); + } else { + _resizeDecoration(&_top, DECORATION_TOP_X, DECORATION_TOP_Y, DECORATION_TOP_WIDTH(_window), DECORATION_TOP_HEIGHT); + _resizeDecoration(&_left, DECORATION_LEFT_X, DECORATION_LEFT_Y, DECORATION_LEFT_WIDTH, DECORATION_LEFT_HEIGHT(_window)); + _resizeDecoration(&_right, DECORATION_RIGHT_X(_window), DECORATION_RIGHT_Y, DECORATION_RIGHT_WIDTH, DECORATION_RIGHT_HEIGHT(_window)); + } _resizeDecoration(&_bottom, DECORATION_BOTTOM_X, DECORATION_BOTTOM_Y(_window), DECORATION_BOTTOM_WIDTH(_window), DECORATION_BOTTOM_HEIGHT); + if (_isVisible) { + _resizeDecoration(&_close, DECORATION_CLOSE_X(_window), DECORATION_CLOSE_Y, DECORATION_CLOSE_WIDTH, DECORATION_CLOSE_HEIGHT); + _resizeDecoration(&_min, DECORATION_MIN_X(_window), DECORATION_MIN_Y, DECORATION_MIN_WIDTH, DECORATION_MIN_HEIGHT); + _resizeDecoration(&_max, DECORATION_MAX_X(_window), DECORATION_MAX_Y, DECORATION_MAX_WIDTH, DECORATION_MAX_HEIGHT); + } - _resizeDecoration(&_close, DECORATION_CLOSE_X(_window), DECORATION_CLOSE_Y, DECORATION_CLOSE_WIDTH, DECORATION_CLOSE_HEIGHT); - _resizeDecoration(&_min, DECORATION_MIN_X(_window), DECORATION_MIN_Y, DECORATION_MIN_WIDTH, DECORATION_MIN_HEIGHT); - _resizeDecoration(&_max, DECORATION_MAX_X(_window), DECORATION_MAX_Y, DECORATION_MAX_WIDTH, DECORATION_MAX_HEIGHT); - - if (!_serverSide && _isVisible) - xdg_surface_set_window_geometry(_xdgSurface, -DECORATION_LEFT_WIDTH, -DECORATION_TOP_HEIGHT, + if (!_serverSide) + xdg_surface_set_window_geometry(_xdgSurface, -DECORATION_LEFT_WIDTH, _isVisible ? -DECORATION_TOP_HEIGHT : -DECORATION_BOTTOM_HEIGHT, _window.getUnscaledWidth() + DECORATION_RIGHT_WIDTH + DECORATION_LEFT_WIDTH, _window.getUnscaledHeight() + DECORATION_BOTTOM_HEIGHT + DECORATION_TOP_HEIGHT); else @@ -345,17 +366,19 @@ Decoration* Decoration::getDecorationForSurface(wl_surface* surface, DecorationF } } -void Decoration::setVisible(bool isVisible) { +void Decoration::setTitlebarVisible(bool isVisible) { if (isVisible != _isVisible) { _isVisible = isVisible; if (isVisible) { + _destroyDecorations(); if (_decoration) { zxdg_toplevel_decoration_v1_set_mode(_decoration, ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE); } else { - _showDecorations(); + _showDecorations(false); } } else { _destroyDecorations(); + _showDecorations(true); if (_decoration) { zxdg_toplevel_decoration_v1_set_mode(_decoration, ZXDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE); } else { @@ -373,14 +396,17 @@ void Decoration::setTitle(const std::string& title) { } void Decoration::getBorders(int& left, int& top, int& right, int& bottom) { - if (_serverSide || !_isVisible) { + if (_serverSide) { left = 0; top = 0; right = 0; bottom = 0; } else { left = DECORATION_LEFT_WIDTH; - top = DECORATION_TOP_HEIGHT; + if (_isVisible) + top = DECORATION_TOP_HEIGHT; + else + top = DECORATION_BOTTOM_HEIGHT; right = DECORATION_RIGHT_WIDTH; bottom = DECORATION_BOTTOM_HEIGHT; } diff --git a/wayland/cc/Decoration.hh b/wayland/cc/Decoration.hh index 082911be..a834fe99 100644 --- a/wayland/cc/Decoration.hh +++ b/wayland/cc/Decoration.hh @@ -10,16 +10,21 @@ #define DECORATION_WIDTH 10 +#define DECORATION_HIDDEN_TOP_Y -(DECORATION_BOTTOM_HEIGHT) #define DECORATION_TOP_X 0 #define DECORATION_TOP_Y -(DECORATION_TOP_HEIGHT) #define DECORATION_TOP_WIDTH(window) window.getUnscaledWidth() #define DECORATION_TOP_HEIGHT DECORATION_WIDTH * 3 +#define DECORATION_HIDDEN_LEFT_Y DECORATION_HIDDEN_TOP_Y +#define DECORATION_HIDDEN_LEFT_HEIGHT(window) window.getUnscaledHeight() + DECORATION_WIDTH + DECORATION_WIDTH #define DECORATION_LEFT_X -(DECORATION_WIDTH) #define DECORATION_LEFT_Y -(DECORATION_TOP_HEIGHT) #define DECORATION_LEFT_WIDTH DECORATION_WIDTH #define DECORATION_LEFT_HEIGHT(window) window.getUnscaledHeight() + DECORATION_TOP_HEIGHT + DECORATION_WIDTH +#define DECORATION_HIDDEN_RIGHT_Y DECORATION_HIDDEN_LEFT_Y +#define DECORATION_HIDDEN_RIGHT_HEIGHT(window) DECORATION_HIDDEN_LEFT_HEIGHT(window) #define DECORATION_RIGHT_X(window) window.getUnscaledWidth() #define DECORATION_RIGHT_Y -(DECORATION_TOP_HEIGHT) #define DECORATION_RIGHT_WIDTH DECORATION_WIDTH @@ -114,7 +119,7 @@ namespace jwm { void _adaptSize(); void _destroyDecorations(); - void _showDecorations(); + void _showDecorations(bool hidden); static const char* proxyTag; static bool ownDecorationSurface(wl_surface* surface); @@ -125,7 +130,7 @@ namespace jwm { void setTitle(const std::string& title); bool _isVisible = true; - void setVisible(bool isVisible); + void setTitlebarVisible(bool isVisible); void getBorders(int& left, int& top, int& right, int& bottom); diff --git a/wayland/cc/WindowWayland.cc b/wayland/cc/WindowWayland.cc index 0aa918ba..954d69c6 100644 --- a/wayland/cc/WindowWayland.cc +++ b/wayland/cc/WindowWayland.cc @@ -44,7 +44,7 @@ void WindowWayland::setTitle(const std::string& title) { void WindowWayland::setTitlebarVisible(bool isVisible) { _titlebarVisible = isVisible; if (_decoration) - _decoration->setVisible(isVisible); + _decoration->setTitlebarVisible(isVisible); } void WindowWayland::close() { From 94d4539407b982940a37a0d4203ed7f9e5784116 Mon Sep 17 00:00:00 2001 From: BulbyVR <26726264+TheDrawingCoder-Gamer@users.noreply.github.com> Date: Wed, 3 Jan 2024 19:50:06 -0500 Subject: [PATCH 76/95] Thinner = better --- wayland/cc/Decoration.hh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/wayland/cc/Decoration.hh b/wayland/cc/Decoration.hh index a834fe99..afa9a4bf 100644 --- a/wayland/cc/Decoration.hh +++ b/wayland/cc/Decoration.hh @@ -8,13 +8,13 @@ #include "Buffer.hh" #include -#define DECORATION_WIDTH 10 +#define DECORATION_WIDTH 4 #define DECORATION_HIDDEN_TOP_Y -(DECORATION_BOTTOM_HEIGHT) #define DECORATION_TOP_X 0 #define DECORATION_TOP_Y -(DECORATION_TOP_HEIGHT) #define DECORATION_TOP_WIDTH(window) window.getUnscaledWidth() -#define DECORATION_TOP_HEIGHT DECORATION_WIDTH * 3 +#define DECORATION_TOP_HEIGHT 30 #define DECORATION_HIDDEN_LEFT_Y DECORATION_HIDDEN_TOP_Y #define DECORATION_HIDDEN_LEFT_HEIGHT(window) window.getUnscaledHeight() + DECORATION_WIDTH + DECORATION_WIDTH From ebf0c4c6131044c19268eb6791100bae21070a7e Mon Sep 17 00:00:00 2001 From: BulbyVR <26726264+TheDrawingCoder-Gamer@users.noreply.github.com> Date: Thu, 4 Jan 2024 15:43:00 -0500 Subject: [PATCH 77/95] Fix movement when titlebar is hidden --- wayland/cc/Decoration.cc | 14 ++++++++++---- wayland/cc/Decoration.hh | 2 ++ wayland/cc/Pointer.cc | 20 ++++++++++++++++---- 3 files changed, 28 insertions(+), 8 deletions(-) diff --git a/wayland/cc/Decoration.cc b/wayland/cc/Decoration.cc index b0cd4947..8b8c3712 100644 --- a/wayland/cc/Decoration.cc +++ b/wayland/cc/Decoration.cc @@ -403,11 +403,17 @@ void Decoration::getBorders(int& left, int& top, int& right, int& bottom) { bottom = 0; } else { left = DECORATION_LEFT_WIDTH; - if (_isVisible) - top = DECORATION_TOP_HEIGHT; - else - top = DECORATION_BOTTOM_HEIGHT; + top = getTopSize(); right = DECORATION_RIGHT_WIDTH; bottom = DECORATION_BOTTOM_HEIGHT; } } + +int Decoration::getTopSize() { + if (_serverSide) + return 0; + if (_isVisible) + return DECORATION_TOP_HEIGHT; + else + return DECORATION_BOTTOM_HEIGHT; +} diff --git a/wayland/cc/Decoration.hh b/wayland/cc/Decoration.hh index afa9a4bf..2c36223f 100644 --- a/wayland/cc/Decoration.hh +++ b/wayland/cc/Decoration.hh @@ -134,6 +134,8 @@ namespace jwm { void getBorders(int& left, int& top, int& right, int& bottom); + int getTopSize(); + private: Decoration(Decoration&& other) = delete; diff --git a/wayland/cc/Pointer.cc b/wayland/cc/Pointer.cc index 6fee9743..84246051 100644 --- a/wayland/cc/Pointer.cc +++ b/wayland/cc/Pointer.cc @@ -56,19 +56,19 @@ static xdg_toplevel_resize_edge resizeEdge(DecorationFocus focus, WindowWayland& // ??? return XDG_TOPLEVEL_RESIZE_EDGE_NONE; case DECORATION_FOCUS_TOP: - if (y < DECORATION_TOP_HEIGHT / 2) + if (y < window._decoration->getTopSize() / 2) return XDG_TOPLEVEL_RESIZE_EDGE_TOP; else return XDG_TOPLEVEL_RESIZE_EDGE_NONE; case DECORATION_FOCUS_LEFT: - if (y < DECORATION_TOP_HEIGHT / 2) + if (y < window._decoration->getTopSize() / 2) return XDG_TOPLEVEL_RESIZE_EDGE_TOP_LEFT; else if (y > DECORATION_BOTTOM_Y(window)) return XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_LEFT; else return XDG_TOPLEVEL_RESIZE_EDGE_LEFT; case DECORATION_FOCUS_RIGHT: - if (y < DECORATION_TOP_HEIGHT / 2) + if (y < window._decoration->getTopSize() / 2) return XDG_TOPLEVEL_RESIZE_EDGE_TOP_RIGHT; else if (y > DECORATION_BOTTOM_Y(window)) return XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_RIGHT; @@ -138,7 +138,19 @@ static void pointerButton(void* data, wl_pointer* pointer, uint32_t serial, if (!window) return; int scale = window->getIntScale(); if (self->_decorationFocus != DECORATION_FOCUS_MAIN) { - if (button != BTN_LEFT || state == 0) + if (state == 0) + return; + if (button == BTN_RIGHT) { + switch (self->_decorationFocus) { + case DECORATION_FOCUS_TOP: + // not showing but this code is being reached + xdg_toplevel_show_window_menu(window->_decoration->_xdgToplevel, self->_seat, serial, self->_absX, self->_absY); + return; + default: + return; + } + } + if (button != BTN_LEFT) return; switch (self->_decorationFocus) { case DECORATION_FOCUS_CLOSE_BUTTON: From 0a6f4ff6acdf55d922d40f6cf44a497fc654c7ad Mon Sep 17 00:00:00 2001 From: BulbyVR <26726264+TheDrawingCoder-Gamer@users.noreply.github.com> Date: Thu, 4 Jan 2024 16:22:09 -0500 Subject: [PATCH 78/95] remove log --- proto,log | 63 ------------------------------------------------------- 1 file changed, 63 deletions(-) delete mode 100644 proto,log diff --git a/proto,log b/proto,log deleted file mode 100644 index ec2f45dc..00000000 --- a/proto,log +++ /dev/null @@ -1,63 +0,0 @@ -Date: 2024-01-03 EST -[17:20:09.252] weston 13.0.0 - https://wayland.freedesktop.org - Bug reports to: https://gitlab.freedesktop.org/wayland/weston/issues/ - Build: 13.0.0 -[17:20:09.252] Command line: weston --logging-scopes proto,log -[17:20:09.252] OS: Linux, 5.19.8-arch1-1-surface, #1 SMP PREEMPT_DYNAMIC Wed, 14 Sep 2022 22:06:46 +0000, x86_64 -[17:20:09.252] Flight recorder: enabled -[17:20:09.252] Using config file '/home/bulby/.config/weston.ini' -[17:20:09.252] Output repaint window is 7 ms maximum. -[17:20:09.252] Loading module '/usr/lib/libweston-13/wayland-backend.so' -[17:20:09.289] Loading module '/usr/lib/libweston-13/gl-renderer.so' -[17:20:09.335] Using rendering device: /dev/dri/renderD128 -[17:20:09.335] EGL version: 1.5 -[17:20:09.335] EGL vendor: Mesa Project -[17:20:09.335] EGL client APIs: OpenGL OpenGL_ES -[17:20:09.335] EGL features: - EGL Wayland extension: yes - context priority: yes - buffer age: yes - partial update: no - swap buffers with damage: yes - configless context: yes - surfaceless context: yes - dmabuf support: modifiers -[17:20:09.337] GL version: OpenGL ES 3.2 Mesa 23.3.1-arch1.1 -[17:20:09.337] GLSL version: OpenGL ES GLSL ES 3.20 -[17:20:09.337] GL vendor: Intel -[17:20:09.337] GL renderer: Mesa Intel(R) UHD Graphics 620 (KBL GT2) -[17:20:09.343] GL ES 3.2 - renderer features: - read-back format: ARGB8888 - glReadPixels supports y-flip: yes - wl_shm 10 bpc formats: yes - wl_shm 16 bpc formats: yes - wl_shm half-float formats: yes - internal R and RG formats: yes - OES_EGL_image_external: yes -[17:20:09.343] Using GL renderer -[17:20:09.343] Registered plugin API 'weston_windowed_output_api_v2' of size 16 -[17:20:09.343] Color manager: no-op -[17:20:09.343] Output 'wayland0' attempts EOTF mode: SDR -[17:20:09.343] Output 'wayland0' using color profile: stock sRGB color profile -[17:20:09.343] Creating 1024x640 wayland output at (0, 0) -[17:20:09.346] wayland-backend: Using xdg_wm_base -[17:20:09.347] Chosen EGL config details: id: 41 rgba: 8 8 8 8 buf: 32 dep: 0 stcl: 0 int: 0-1 type: win vis_id: 0 -[17:20:09.355] Output 'wayland0' enabled with head(s) wayland0 -[17:20:09.355] Compositor capabilities: - arbitrary surface rotation: yes - screen capture uses y-flip: yes - cursor planes: no - arbitrary resolutions: no - view mask clipping: yes - explicit sync: yes - color operations: yes - presentation clock: CLOCK_MONOTONIC_RAW, id 4 - presentation clock resolution: 0.000000001 s -[17:20:09.355] libwayland: unable to lock lockfile /run/user/1000/wayland-1.lock, maybe another compositor is running -[17:20:09.355] Loading module '/usr/lib/weston/desktop-shell.so' -[17:20:09.358] launching '/usr/lib/weston/weston-keyboard' -[17:20:09.360] launching '/usr/lib/weston/weston-desktop-shell' -[17:20:09.365] Chosen EGL config details: id: 41 rgba: 8 8 8 8 buf: 32 dep: 0 stcl: 0 int: 0-1 type: win vis_id: 0 -[17:20:20.076] caught signal 12 -[17:20:20.077] BUG: finalizing a layer with views still on it. From 67ce6f9de3b664b12a03bedad166a0d86fe6d6c5 Mon Sep 17 00:00:00 2001 From: BulbyVR <26726264+TheDrawingCoder-Gamer@users.noreply.github.com> Date: Thu, 4 Jan 2024 16:37:06 -0500 Subject: [PATCH 79/95] Undo some unneeded changes --- linux/cc/ILayer.hh | 1 - wayland/cc/LayerGLWayland.cc | 3 +-- wayland/cc/LayerRasterWayland.cc | 2 +- x11/cc/LayerGLX11.cc | 2 +- x11/cc/LayerRasterX11.cc | 2 +- x11/cc/ScreenInfo.cc | 1 + 6 files changed, 5 insertions(+), 6 deletions(-) diff --git a/linux/cc/ILayer.hh b/linux/cc/ILayer.hh index 5b8ee030..81a32c2b 100644 --- a/linux/cc/ILayer.hh +++ b/linux/cc/ILayer.hh @@ -15,7 +15,6 @@ public: virtual void makeCurrentForced(); virtual void setVsyncMode(VSync v) = 0; virtual void close() = 0; - virtual void resize(int width, int height) = 0; static ILayer* _ourCurrentLayer; diff --git a/wayland/cc/LayerGLWayland.cc b/wayland/cc/LayerGLWayland.cc index ecdba2f3..ebe5a658 100644 --- a/wayland/cc/LayerGLWayland.cc +++ b/wayland/cc/LayerGLWayland.cc @@ -85,7 +85,7 @@ namespace jwm { // vsync? what vsync? } - void resize(int width, int height) override { + void resize(int width, int height) { if (!_surface || !_eglWindow) return; // Make current to avoid artifacts in other windows makeCurrentForced(); @@ -100,7 +100,6 @@ namespace jwm { // HACK: make new window with new scale // https://gitlab.freedesktop.org/mesa/mesa/-/issues/7217 if (fWindow->_scale != fWindow->_oldScale) { - fprintf(stderr, "HACK: remaking egl window\n"); detachBuffer(); attachBuffer(); wl_surface_set_buffer_scale(fWindow->_waylandWindow, fWindow->getIntScale()); diff --git a/wayland/cc/LayerRasterWayland.cc b/wayland/cc/LayerRasterWayland.cc index 156d3b91..d64ef789 100644 --- a/wayland/cc/LayerRasterWayland.cc +++ b/wayland/cc/LayerRasterWayland.cc @@ -29,7 +29,7 @@ namespace jwm { } } - void resize(int width, int height) override { + void resize(int width, int height) { // god is dead _width = width; _height = height; diff --git a/x11/cc/LayerGLX11.cc b/x11/cc/LayerGLX11.cc index 1b4b8e02..aa78e4ee 100644 --- a/x11/cc/LayerGLX11.cc +++ b/x11/cc/LayerGLX11.cc @@ -49,7 +49,7 @@ namespace jwm { } } - void resize(int width, int height) override { + void resize(int width, int height) { glClearStencil(0); glClearColor(0, 0, 0, 255); glStencilMask(0xffffffff); diff --git a/x11/cc/LayerRasterX11.cc b/x11/cc/LayerRasterX11.cc index cc22ea88..4928aee6 100644 --- a/x11/cc/LayerRasterX11.cc +++ b/x11/cc/LayerRasterX11.cc @@ -30,7 +30,7 @@ namespace jwm { _graphicsContext = DefaultGC(d, DefaultScreen(d)); } - void resize(int width, int height) override { + void resize(int width, int height) { Display* d = fWindow->_windowManager.getDisplay(); _width = width; _height = height; diff --git a/x11/cc/ScreenInfo.cc b/x11/cc/ScreenInfo.cc index f1e56dc2..f3aa1dee 100644 --- a/x11/cc/ScreenInfo.cc +++ b/x11/cc/ScreenInfo.cc @@ -1,6 +1,7 @@ #include "ScreenInfo.hh" #include "AppX11.hh" + jobject jwm::ScreenInfo::asJavaObject(JNIEnv* env) const { return jwm::classes::Screen::make(env, id, isPrimary, bounds, bounds, jwm::app.getScale()); } From ff8827b90556e475ff3acf19986e9adcc1e5991e Mon Sep 17 00:00:00 2001 From: BulbyVR <26726264+TheDrawingCoder-Gamer@users.noreply.github.com> Date: Fri, 5 Jan 2024 14:15:07 -0500 Subject: [PATCH 80/95] Combine border into one part Done to make any future work pulling in drawing libraries easier. --- wayland/cc/Decoration.cc | 71 ++++++++++++++-------------------------- wayland/cc/Decoration.hh | 40 +++++----------------- wayland/cc/Pointer.cc | 70 +++++++++++++++++++++------------------ 3 files changed, 71 insertions(+), 110 deletions(-) diff --git a/wayland/cc/Decoration.cc b/wayland/cc/Decoration.cc index 8b8c3712..58b8efe4 100644 --- a/wayland/cc/Decoration.cc +++ b/wayland/cc/Decoration.cc @@ -111,10 +111,10 @@ static void _xdgSurfaceConfigure(void* data, xdg_surface* surface, uint32_t seri self->_adaptSize(); } if (self->_serverSide) { - if (self->_top.surface) + if (self->_border.surface) self->_destroyDecorations(); } else { - if (!self->_top.surface) + if (!self->_border.surface) self->_showDecorations(!self->_isVisible); } // at the end so that the size isn't adapted on first render @@ -136,12 +136,8 @@ static void _xdgToplevelConfigure(void* data, xdg_toplevel* toplevel, int width, self->_pendingWidth = width; self->_pendingHeight = height; if (!self->_serverSide) { - self->_pendingWidth -= DECORATION_LEFT_WIDTH + DECORATION_RIGHT_WIDTH; - self->_pendingHeight -= DECORATION_BOTTOM_HEIGHT; - if (self->_isVisible) - self->_pendingHeight -= DECORATION_TOP_HEIGHT; - else - self->_pendingHeight -= DECORATION_BOTTOM_HEIGHT; + self->_pendingWidth -= DECORATION_WIDTH + DECORATION_WIDTH; + self->_pendingHeight -= DECORATION_WIDTH + self->getTopSize(); } bool active = false; @@ -216,7 +212,7 @@ Decoration::Decoration(WindowWayland& window): } void Decoration::close() { - if (_top.surface) + if (_border.surface) _destroyDecorations(); if (_decoration) zxdg_toplevel_decoration_v1_destroy(_decoration); @@ -266,10 +262,7 @@ void Decoration::_destroyDecoration(DecorationPart* decoration) { } void Decoration::_destroyDecorations() { - _destroyDecoration(&_top); - _destroyDecoration(&_left); - _destroyDecoration(&_right); - _destroyDecoration(&_bottom); + _destroyDecoration(&_border); _destroyDecoration(&_close); _destroyDecoration(&_min); _destroyDecoration(&_max); @@ -287,18 +280,13 @@ void Decoration::_showDecorations(bool hidden) { _minBuffer = Buffer::createShmBuffer(_wm.shm, 9, 9, WL_SHM_FORMAT_ARGB8888); memcpy(_minBuffer->getData(), min_data, 9 * 9 * sizeof(uint32_t)); if (hidden) { - _makePart(&_top, _decBuffer, true, DECORATION_TOP_X, DECORATION_HIDDEN_TOP_Y, DECORATION_BOTTOM_WIDTH(_window), DECORATION_BOTTOM_HEIGHT); - _makePart(&_left, _decBuffer, true, DECORATION_LEFT_X, DECORATION_HIDDEN_LEFT_Y, DECORATION_LEFT_WIDTH, - DECORATION_HIDDEN_LEFT_HEIGHT(_window)); - _makePart(&_right, _decBuffer, true, DECORATION_RIGHT_X(_window), DECORATION_HIDDEN_RIGHT_Y, - DECORATION_RIGHT_WIDTH, DECORATION_HIDDEN_RIGHT_HEIGHT(_window)); + _makePart(&_border, _decBuffer, true, DECORATION_BORDER_X, DECORATION_HIDDEN_BORDER_Y, + DECORATION_BORDER_WIDTH(_window), DECORATION_HIDDEN_BORDER_HEIGHT(_window)); } else { - _makePart(&_top, _decBuffer, true, DECORATION_TOP_X, DECORATION_TOP_Y, DECORATION_TOP_WIDTH(_window), DECORATION_TOP_HEIGHT); - _makePart(&_left, _decBuffer, true, DECORATION_LEFT_X, DECORATION_LEFT_Y, DECORATION_LEFT_WIDTH, DECORATION_LEFT_HEIGHT(_window)); - _makePart(&_right, _decBuffer, true, DECORATION_RIGHT_X(_window), DECORATION_RIGHT_Y, DECORATION_RIGHT_WIDTH, DECORATION_RIGHT_HEIGHT(_window)); + _makePart(&_border, _decBuffer, true, DECORATION_BORDER_X, DECORATION_BORDER_Y, + DECORATION_BORDER_WIDTH(_window), DECORATION_BORDER_HEIGHT(_window)); } - _makePart(&_bottom, _decBuffer, true, DECORATION_BOTTOM_X, DECORATION_BOTTOM_Y(_window), DECORATION_BOTTOM_WIDTH(_window), DECORATION_BOTTOM_HEIGHT); - + wl_subsurface_place_below(_border.subsurface, _window._waylandWindow); if (!hidden) { _makePart(&_close, _closeBuffer, false, DECORATION_CLOSE_X(_window), DECORATION_CLOSE_Y, DECORATION_CLOSE_WIDTH, DECORATION_CLOSE_HEIGHT); _makePart(&_max, _maxBuffer, false, DECORATION_MAX_X(_window), DECORATION_MAX_Y, DECORATION_MAX_WIDTH, DECORATION_MAX_HEIGHT); @@ -309,17 +297,12 @@ void Decoration::_showDecorations(bool hidden) { void Decoration::_adaptSize() { if (!_isVisible) { - _resizeDecoration(&_top, DECORATION_TOP_X, DECORATION_HIDDEN_TOP_Y, DECORATION_BOTTOM_WIDTH(_window), DECORATION_BOTTOM_HEIGHT); - _resizeDecoration(&_left, DECORATION_LEFT_X, DECORATION_HIDDEN_LEFT_Y, DECORATION_LEFT_WIDTH, - DECORATION_HIDDEN_LEFT_HEIGHT(_window)); - _resizeDecoration(&_right, DECORATION_RIGHT_X(_window), DECORATION_HIDDEN_RIGHT_Y, DECORATION_RIGHT_WIDTH, - DECORATION_HIDDEN_RIGHT_HEIGHT(_window)); + _resizeDecoration(&_border, DECORATION_BORDER_X, DECORATION_HIDDEN_BORDER_Y, DECORATION_BORDER_WIDTH(_window), + DECORATION_HIDDEN_BORDER_HEIGHT(_window)); } else { - _resizeDecoration(&_top, DECORATION_TOP_X, DECORATION_TOP_Y, DECORATION_TOP_WIDTH(_window), DECORATION_TOP_HEIGHT); - _resizeDecoration(&_left, DECORATION_LEFT_X, DECORATION_LEFT_Y, DECORATION_LEFT_WIDTH, DECORATION_LEFT_HEIGHT(_window)); - _resizeDecoration(&_right, DECORATION_RIGHT_X(_window), DECORATION_RIGHT_Y, DECORATION_RIGHT_WIDTH, DECORATION_RIGHT_HEIGHT(_window)); + _resizeDecoration(&_border, DECORATION_BORDER_X, DECORATION_BORDER_Y, + DECORATION_BORDER_WIDTH(_window), DECORATION_BORDER_HEIGHT(_window)); } - _resizeDecoration(&_bottom, DECORATION_BOTTOM_X, DECORATION_BOTTOM_Y(_window), DECORATION_BOTTOM_WIDTH(_window), DECORATION_BOTTOM_HEIGHT); if (_isVisible) { _resizeDecoration(&_close, DECORATION_CLOSE_X(_window), DECORATION_CLOSE_Y, DECORATION_CLOSE_WIDTH, DECORATION_CLOSE_HEIGHT); @@ -328,9 +311,9 @@ void Decoration::_adaptSize() { } if (!_serverSide) - xdg_surface_set_window_geometry(_xdgSurface, -DECORATION_LEFT_WIDTH, _isVisible ? -DECORATION_TOP_HEIGHT : -DECORATION_BOTTOM_HEIGHT, - _window.getUnscaledWidth() + DECORATION_RIGHT_WIDTH + DECORATION_LEFT_WIDTH, - _window.getUnscaledHeight() + DECORATION_BOTTOM_HEIGHT + DECORATION_TOP_HEIGHT); + xdg_surface_set_window_geometry(_xdgSurface, DECORATION_BORDER_X, _isVisible ? DECORATION_BORDER_Y : DECORATION_HIDDEN_BORDER_Y, + DECORATION_BORDER_WIDTH(_window), + _isVisible ? DECORATION_BORDER_HEIGHT(_window) : DECORATION_HIDDEN_BORDER_HEIGHT(_window)); else xdg_surface_set_window_geometry(_xdgSurface, 0, 0, _window.getUnscaledWidth(), _window.getUnscaledHeight()); } @@ -343,14 +326,8 @@ bool Decoration::ownDecorationSurface(wl_surface* surface) { Decoration* Decoration::getDecorationForSurface(wl_surface* surface, DecorationFocus* focus) { if (ownDecorationSurface(surface)) { Decoration* decoration = (Decoration*)wl_proxy_get_user_data((wl_proxy*) surface); - if (surface == decoration->_top.surface) { - *focus = DECORATION_FOCUS_TOP; - } else if (surface == decoration->_left.surface) { - *focus = DECORATION_FOCUS_LEFT; - } else if (surface == decoration->_right.surface) { - *focus = DECORATION_FOCUS_RIGHT; - } else if (surface == decoration->_bottom.surface) { - *focus = DECORATION_FOCUS_BOTTOM; + if (surface == decoration->_border.surface) { + *focus = DECORATION_FOCUS_BORDER; } else if (surface == decoration->_close.surface) { *focus = DECORATION_FOCUS_CLOSE_BUTTON; } else if (surface == decoration->_min.surface) { @@ -402,10 +379,10 @@ void Decoration::getBorders(int& left, int& top, int& right, int& bottom) { right = 0; bottom = 0; } else { - left = DECORATION_LEFT_WIDTH; + left = DECORATION_WIDTH; top = getTopSize(); - right = DECORATION_RIGHT_WIDTH; - bottom = DECORATION_BOTTOM_HEIGHT; + right = DECORATION_WIDTH; + bottom = DECORATION_WIDTH; } } @@ -415,5 +392,5 @@ int Decoration::getTopSize() { if (_isVisible) return DECORATION_TOP_HEIGHT; else - return DECORATION_BOTTOM_HEIGHT; + return DECORATION_WIDTH; } diff --git a/wayland/cc/Decoration.hh b/wayland/cc/Decoration.hh index 2c36223f..1382f7c6 100644 --- a/wayland/cc/Decoration.hh +++ b/wayland/cc/Decoration.hh @@ -9,31 +9,15 @@ #include #define DECORATION_WIDTH 4 - -#define DECORATION_HIDDEN_TOP_Y -(DECORATION_BOTTOM_HEIGHT) -#define DECORATION_TOP_X 0 -#define DECORATION_TOP_Y -(DECORATION_TOP_HEIGHT) -#define DECORATION_TOP_WIDTH(window) window.getUnscaledWidth() #define DECORATION_TOP_HEIGHT 30 -#define DECORATION_HIDDEN_LEFT_Y DECORATION_HIDDEN_TOP_Y -#define DECORATION_HIDDEN_LEFT_HEIGHT(window) window.getUnscaledHeight() + DECORATION_WIDTH + DECORATION_WIDTH -#define DECORATION_LEFT_X -(DECORATION_WIDTH) -#define DECORATION_LEFT_Y -(DECORATION_TOP_HEIGHT) -#define DECORATION_LEFT_WIDTH DECORATION_WIDTH -#define DECORATION_LEFT_HEIGHT(window) window.getUnscaledHeight() + DECORATION_TOP_HEIGHT + DECORATION_WIDTH - -#define DECORATION_HIDDEN_RIGHT_Y DECORATION_HIDDEN_LEFT_Y -#define DECORATION_HIDDEN_RIGHT_HEIGHT(window) DECORATION_HIDDEN_LEFT_HEIGHT(window) -#define DECORATION_RIGHT_X(window) window.getUnscaledWidth() -#define DECORATION_RIGHT_Y -(DECORATION_TOP_HEIGHT) -#define DECORATION_RIGHT_WIDTH DECORATION_WIDTH -#define DECORATION_RIGHT_HEIGHT(window) window.getUnscaledHeight() + DECORATION_TOP_HEIGHT + DECORATION_WIDTH - -#define DECORATION_BOTTOM_X 0 -#define DECORATION_BOTTOM_Y(window) window.getUnscaledHeight() -#define DECORATION_BOTTOM_WIDTH(window) window.getUnscaledWidth() -#define DECORATION_BOTTOM_HEIGHT DECORATION_WIDTH +#define DECORATION_BORDER_X -(DECORATION_WIDTH) +#define DECORATION_BORDER_Y -(DECORATION_TOP_HEIGHT) +#define DECORATION_BORDER_WIDTH(window) window.getUnscaledWidth() + DECORATION_WIDTH + DECORATION_WIDTH +#define DECORATION_BORDER_HEIGHT(window) window.getUnscaledHeight() + DECORATION_TOP_HEIGHT + DECORATION_WIDTH + +#define DECORATION_HIDDEN_BORDER_Y -(DECORATION_WIDTH) +#define DECORATION_HIDDEN_BORDER_HEIGHT(window) window.getUnscaledHeight() + DECORATION_WIDTH + DECORATION_WIDTH #define DECORATION_CLOSE_X(window) window.getUnscaledWidth() - 10 #define DECORATION_CLOSE_Y -20 @@ -60,10 +44,7 @@ namespace jwm { }; enum DecorationFocus { DECORATION_FOCUS_MAIN, - DECORATION_FOCUS_TOP, - DECORATION_FOCUS_LEFT, - DECORATION_FOCUS_RIGHT, - DECORATION_FOCUS_BOTTOM, + DECORATION_FOCUS_BORDER, DECORATION_FOCUS_CLOSE_BUTTON, DECORATION_FOCUS_MAX_BUTTON, DECORATION_FOCUS_MIN_BUTTON @@ -84,10 +65,7 @@ namespace jwm { Buffer* _maxBuffer; Buffer* _minBuffer; - DecorationPart _top; - DecorationPart _left; - DecorationPart _right; - DecorationPart _bottom; + DecorationPart _border; DecorationPart _close; DecorationPart _max; diff --git a/wayland/cc/Pointer.cc b/wayland/cc/Pointer.cc index 84246051..44e70f06 100644 --- a/wayland/cc/Pointer.cc +++ b/wayland/cc/Pointer.cc @@ -51,31 +51,37 @@ static void pointerLeave(void* data, wl_pointer* pointer, uint32_t serial, } } static xdg_toplevel_resize_edge resizeEdge(DecorationFocus focus, WindowWayland& window, int x, int y) { + int rightEdge = window.getUnscaledWidth(); + int bottomEdge = window.getUnscaledHeight(); switch (focus) { case DECORATION_FOCUS_MAIN: // ??? return XDG_TOPLEVEL_RESIZE_EDGE_NONE; - case DECORATION_FOCUS_TOP: - if (y < window._decoration->getTopSize() / 2) - return XDG_TOPLEVEL_RESIZE_EDGE_TOP; - else - return XDG_TOPLEVEL_RESIZE_EDGE_NONE; - case DECORATION_FOCUS_LEFT: - if (y < window._decoration->getTopSize() / 2) - return XDG_TOPLEVEL_RESIZE_EDGE_TOP_LEFT; - else if (y > DECORATION_BOTTOM_Y(window)) - return XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_LEFT; - else - return XDG_TOPLEVEL_RESIZE_EDGE_LEFT; - case DECORATION_FOCUS_RIGHT: - if (y < window._decoration->getTopSize() / 2) - return XDG_TOPLEVEL_RESIZE_EDGE_TOP_RIGHT; - else if (y > DECORATION_BOTTOM_Y(window)) - return XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_RIGHT; - else - return XDG_TOPLEVEL_RESIZE_EDGE_RIGHT; - case DECORATION_FOCUS_BOTTOM: - return XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM; + case DECORATION_FOCUS_BORDER: + if (y < -window._decoration->getTopSize() / 2) { + if (x < 0) + return XDG_TOPLEVEL_RESIZE_EDGE_TOP_LEFT; + else if (x > rightEdge) + return XDG_TOPLEVEL_RESIZE_EDGE_TOP_RIGHT; + else + return XDG_TOPLEVEL_RESIZE_EDGE_TOP; + } + else if (y > bottomEdge) { + if (x < 0) + return XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_LEFT; + else if (x > rightEdge) + return XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_RIGHT; + else + return XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM; + } else { + if (x < 0) + return XDG_TOPLEVEL_RESIZE_EDGE_LEFT; + else if (x > rightEdge) + return XDG_TOPLEVEL_RESIZE_EDGE_RIGHT; + else + return XDG_TOPLEVEL_RESIZE_EDGE_NONE; + } + return XDG_TOPLEVEL_RESIZE_EDGE_NONE; default: return XDG_TOPLEVEL_RESIZE_EDGE_NONE; } @@ -93,14 +99,18 @@ static void pointerMotion(void* data, wl_pointer* pointer, uint32_t time, int x = wl_fixed_to_int(surface_x); int y = wl_fixed_to_int(surface_y); // ??? - self->_absX = x; - self->_absY = y; switch (self->_decorationFocus) { case DECORATION_FOCUS_MAIN: + self->_absX = x; + self->_absY = y; self->_movement = true; break; default: - auto edge = resizeEdge(self->_decorationFocus, *self->_focusedSurface, x, y); + if (self->_decorationFocus == DECORATION_FOCUS_BORDER) { + self->_absX = x - DECORATION_WIDTH; + self->_absY = y - self->_focusedSurface->_decoration->getTopSize(); + } + auto edge = resizeEdge(self->_decorationFocus, *self->_focusedSurface, self->_absX, self->_absY); switch (edge) { case XDG_TOPLEVEL_RESIZE_EDGE_TOP: case XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM: @@ -141,14 +151,10 @@ static void pointerButton(void* data, wl_pointer* pointer, uint32_t serial, if (state == 0) return; if (button == BTN_RIGHT) { - switch (self->_decorationFocus) { - case DECORATION_FOCUS_TOP: - // not showing but this code is being reached - xdg_toplevel_show_window_menu(window->_decoration->_xdgToplevel, self->_seat, serial, self->_absX, self->_absY); - return; - default: - return; - } + if (self->_absY < 0) + xdg_toplevel_show_window_menu(window->_decoration->_xdgToplevel, self->_seat, serial, + self->_absX + DECORATION_WIDTH, self->_absY + window->_decoration->getTopSize()); + return; } if (button != BTN_LEFT) return; From 7c921ca23c820de76afcb56af0ea5dce6ab28a60 Mon Sep 17 00:00:00 2001 From: BulbyVR <26726264+TheDrawingCoder-Gamer@users.noreply.github.com> Date: Sat, 6 Jan 2024 13:37:25 -0500 Subject: [PATCH 81/95] make title great again --- wayland/cc/Decoration.cc | 35 +++++++++++++++-------------------- wayland/cc/Decoration.hh | 13 +++++++++---- wayland/cc/Pointer.cc | 33 ++++++++++++++++++++++++++------- 3 files changed, 50 insertions(+), 31 deletions(-) diff --git a/wayland/cc/Decoration.cc b/wayland/cc/Decoration.cc index 58b8efe4..fba2bdec 100644 --- a/wayland/cc/Decoration.cc +++ b/wayland/cc/Decoration.cc @@ -206,7 +206,6 @@ Decoration::Decoration(WindowWayland& window): zxdg_toplevel_decoration_v1_add_listener(_decoration, &_decorationListener, this); // for the love of GOD do it for me zxdg_toplevel_decoration_v1_set_mode(_decoration, ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE); - } // delay making parts until configure : ) } @@ -263,6 +262,7 @@ void Decoration::_destroyDecoration(DecorationPart* decoration) { void Decoration::_destroyDecorations() { _destroyDecoration(&_border); + _destroyDecoration(&_titleComp); _destroyDecoration(&_close); _destroyDecoration(&_min); _destroyDecoration(&_max); @@ -279,15 +279,13 @@ void Decoration::_showDecorations(bool hidden) { memcpy(_maxBuffer->getData(), max_data, 9 * 9 * sizeof(uint32_t)); _minBuffer = Buffer::createShmBuffer(_wm.shm, 9, 9, WL_SHM_FORMAT_ARGB8888); memcpy(_minBuffer->getData(), min_data, 9 * 9 * sizeof(uint32_t)); - if (hidden) { - _makePart(&_border, _decBuffer, true, DECORATION_BORDER_X, DECORATION_HIDDEN_BORDER_Y, - DECORATION_BORDER_WIDTH(_window), DECORATION_HIDDEN_BORDER_HEIGHT(_window)); - } else { - _makePart(&_border, _decBuffer, true, DECORATION_BORDER_X, DECORATION_BORDER_Y, - DECORATION_BORDER_WIDTH(_window), DECORATION_BORDER_HEIGHT(_window)); - } + _makePart(&_border, _decBuffer, true, DECORATION_BORDER_X, DECORATION_BORDER_Y, + DECORATION_BORDER_WIDTH(_window), DECORATION_BORDER_HEIGHT(_window)); wl_subsurface_place_below(_border.subsurface, _window._waylandWindow); if (!hidden) { + _makePart(&_titleComp, _decBuffer, true, DECORATION_TITLE_X, DECORATION_TITLE_Y, + DECORATION_TITLE_WIDTH(_window), DECORATION_TITLE_HEIGHT); + wl_subsurface_place_above(_titleComp.subsurface, _border.surface); _makePart(&_close, _closeBuffer, false, DECORATION_CLOSE_X(_window), DECORATION_CLOSE_Y, DECORATION_CLOSE_WIDTH, DECORATION_CLOSE_HEIGHT); _makePart(&_max, _maxBuffer, false, DECORATION_MAX_X(_window), DECORATION_MAX_Y, DECORATION_MAX_WIDTH, DECORATION_MAX_HEIGHT); _makePart(&_min, _minBuffer, false, DECORATION_MIN_X(_window), DECORATION_MIN_Y, DECORATION_MIN_WIDTH, DECORATION_MIN_HEIGHT); @@ -296,24 +294,20 @@ void Decoration::_showDecorations(bool hidden) { } void Decoration::_adaptSize() { - if (!_isVisible) { - _resizeDecoration(&_border, DECORATION_BORDER_X, DECORATION_HIDDEN_BORDER_Y, DECORATION_BORDER_WIDTH(_window), - DECORATION_HIDDEN_BORDER_HEIGHT(_window)); - } else { - _resizeDecoration(&_border, DECORATION_BORDER_X, DECORATION_BORDER_Y, - DECORATION_BORDER_WIDTH(_window), DECORATION_BORDER_HEIGHT(_window)); - } - + _resizeDecoration(&_border, DECORATION_BORDER_X, DECORATION_BORDER_Y, + DECORATION_BORDER_WIDTH(_window), DECORATION_BORDER_HEIGHT(_window)); if (_isVisible) { + _resizeDecoration(&_titleComp, DECORATION_TITLE_X, DECORATION_TITLE_Y, + DECORATION_TITLE_WIDTH(_window), DECORATION_TITLE_HEIGHT); _resizeDecoration(&_close, DECORATION_CLOSE_X(_window), DECORATION_CLOSE_Y, DECORATION_CLOSE_WIDTH, DECORATION_CLOSE_HEIGHT); _resizeDecoration(&_min, DECORATION_MIN_X(_window), DECORATION_MIN_Y, DECORATION_MIN_WIDTH, DECORATION_MIN_HEIGHT); _resizeDecoration(&_max, DECORATION_MAX_X(_window), DECORATION_MAX_Y, DECORATION_MAX_WIDTH, DECORATION_MAX_HEIGHT); } if (!_serverSide) - xdg_surface_set_window_geometry(_xdgSurface, DECORATION_BORDER_X, _isVisible ? DECORATION_BORDER_Y : DECORATION_HIDDEN_BORDER_Y, + xdg_surface_set_window_geometry(_xdgSurface, DECORATION_BORDER_X, _isVisible ? DECORATION_TITLE_Y : DECORATION_BORDER_Y, DECORATION_BORDER_WIDTH(_window), - _isVisible ? DECORATION_BORDER_HEIGHT(_window) : DECORATION_HIDDEN_BORDER_HEIGHT(_window)); + _isVisible ? DECORATION_BORDER_FULL_HEIGHT(_window) : DECORATION_BORDER_HEIGHT(_window)); else xdg_surface_set_window_geometry(_xdgSurface, 0, 0, _window.getUnscaledWidth(), _window.getUnscaledHeight()); } @@ -328,6 +322,8 @@ Decoration* Decoration::getDecorationForSurface(wl_surface* surface, DecorationF Decoration* decoration = (Decoration*)wl_proxy_get_user_data((wl_proxy*) surface); if (surface == decoration->_border.surface) { *focus = DECORATION_FOCUS_BORDER; + } else if (surface == decoration->_titleComp.surface) { + *focus = DECORATION_FOCUS_TITLE; } else if (surface == decoration->_close.surface) { *focus = DECORATION_FOCUS_CLOSE_BUTTON; } else if (surface == decoration->_min.surface) { @@ -355,11 +351,10 @@ void Decoration::setTitlebarVisible(bool isVisible) { } } else { _destroyDecorations(); - _showDecorations(true); if (_decoration) { zxdg_toplevel_decoration_v1_set_mode(_decoration, ZXDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE); } else { - // TODO: request reconfigure + _showDecorations(true); wl_surface_commit(_window._waylandWindow); } } diff --git a/wayland/cc/Decoration.hh b/wayland/cc/Decoration.hh index 1382f7c6..7c18708b 100644 --- a/wayland/cc/Decoration.hh +++ b/wayland/cc/Decoration.hh @@ -12,12 +12,15 @@ #define DECORATION_TOP_HEIGHT 30 #define DECORATION_BORDER_X -(DECORATION_WIDTH) -#define DECORATION_BORDER_Y -(DECORATION_TOP_HEIGHT) +#define DECORATION_BORDER_Y -(DECORATION_WIDTH) #define DECORATION_BORDER_WIDTH(window) window.getUnscaledWidth() + DECORATION_WIDTH + DECORATION_WIDTH -#define DECORATION_BORDER_HEIGHT(window) window.getUnscaledHeight() + DECORATION_TOP_HEIGHT + DECORATION_WIDTH +#define DECORATION_BORDER_HEIGHT(window) window.getUnscaledHeight() + DECORATION_WIDTH + DECORATION_WIDTH +#define DECORATION_BORDER_FULL_HEIGHT(window) window.getUnscaledHeight() + DECORATION_TOP_HEIGHT + DECORATION_WIDTH -#define DECORATION_HIDDEN_BORDER_Y -(DECORATION_WIDTH) -#define DECORATION_HIDDEN_BORDER_HEIGHT(window) window.getUnscaledHeight() + DECORATION_WIDTH + DECORATION_WIDTH +#define DECORATION_TITLE_X -(DECORATION_WIDTH) +#define DECORATION_TITLE_Y -(DECORATION_TOP_HEIGHT) +#define DECORATION_TITLE_WIDTH(window) window.getUnscaledWidth() + DECORATION_WIDTH + DECORATION_WIDTH +#define DECORATION_TITLE_HEIGHT DECORATION_TOP_HEIGHT #define DECORATION_CLOSE_X(window) window.getUnscaledWidth() - 10 #define DECORATION_CLOSE_Y -20 @@ -45,6 +48,7 @@ namespace jwm { enum DecorationFocus { DECORATION_FOCUS_MAIN, DECORATION_FOCUS_BORDER, + DECORATION_FOCUS_TITLE, DECORATION_FOCUS_CLOSE_BUTTON, DECORATION_FOCUS_MAX_BUTTON, DECORATION_FOCUS_MIN_BUTTON @@ -66,6 +70,7 @@ namespace jwm { Buffer* _minBuffer; DecorationPart _border; + DecorationPart _titleComp; DecorationPart _close; DecorationPart _max; diff --git a/wayland/cc/Pointer.cc b/wayland/cc/Pointer.cc index 44e70f06..a861ff70 100644 --- a/wayland/cc/Pointer.cc +++ b/wayland/cc/Pointer.cc @@ -7,6 +7,8 @@ #include #include #include +#include +#include using namespace jwm; @@ -50,21 +52,33 @@ static void pointerLeave(void* data, wl_pointer* pointer, uint32_t serial, self->_decorationFocus = DECORATION_FOCUS_MAIN; } } -static xdg_toplevel_resize_edge resizeEdge(DecorationFocus focus, WindowWayland& window, int x, int y) { +static xdg_toplevel_resize_edge resizeEdge(DecorationFocus focus, WindowWayland& window, int x, int y, xkb_state* state) { int rightEdge = window.getUnscaledWidth(); int bottomEdge = window.getUnscaledHeight(); switch (focus) { case DECORATION_FOCUS_MAIN: // ??? return XDG_TOPLEVEL_RESIZE_EDGE_NONE; + case DECORATION_FOCUS_TITLE: + if (y < -DECORATION_TOP_HEIGHT / 2) + return XDG_TOPLEVEL_RESIZE_EDGE_TOP; + else + return XDG_TOPLEVEL_RESIZE_EDGE_NONE; case DECORATION_FOCUS_BORDER: - if (y < -window._decoration->getTopSize() / 2) { + + if (y < 0) { if (x < 0) return XDG_TOPLEVEL_RESIZE_EDGE_TOP_LEFT; else if (x > rightEdge) return XDG_TOPLEVEL_RESIZE_EDGE_TOP_RIGHT; - else + else { + if (state) { + if (xkb_state_mod_name_is_active(state, XKB_MOD_NAME_CTRL, XKB_STATE_MODS_EFFECTIVE)) + // grab + return XDG_TOPLEVEL_RESIZE_EDGE_NONE; + } return XDG_TOPLEVEL_RESIZE_EDGE_TOP; + } } else if (y > bottomEdge) { if (x < 0) @@ -108,9 +122,13 @@ static void pointerMotion(void* data, wl_pointer* pointer, uint32_t time, default: if (self->_decorationFocus == DECORATION_FOCUS_BORDER) { self->_absX = x - DECORATION_WIDTH; - self->_absY = y - self->_focusedSurface->_decoration->getTopSize(); + self->_absY = y - DECORATION_WIDTH; + } else if (self->_decorationFocus == DECORATION_FOCUS_TITLE) { + self->_absX = x - DECORATION_WIDTH; + self->_absY = y - DECORATION_TOP_HEIGHT; } - auto edge = resizeEdge(self->_decorationFocus, *self->_focusedSurface, self->_absX, self->_absY); + auto edge = resizeEdge(self->_decorationFocus, *self->_focusedSurface, self->_absX, self->_absY, + self->_wm.getXkbState()); switch (edge) { case XDG_TOPLEVEL_RESIZE_EDGE_TOP: case XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM: @@ -171,8 +189,9 @@ static void pointerButton(void* data, wl_pointer* pointer, uint32_t serial, else xdg_toplevel_set_maximized(window->_decoration->_xdgToplevel); break; - default: - xdg_toplevel_resize_edge edge = resizeEdge(self->_decorationFocus, *window, self->_absX, self->_absY); + case DECORATION_FOCUS_BORDER: + case DECORATION_FOCUS_TITLE: + xdg_toplevel_resize_edge edge = resizeEdge(self->_decorationFocus, *window, self->_absX, self->_absY, self->_wm.getXkbState()); if (edge == XDG_TOPLEVEL_RESIZE_EDGE_NONE) xdg_toplevel_move(window->_decoration->_xdgToplevel, self->_seat, serial); else From a2c8a479a699525c48cfb612bfc57d9b55cd8856 Mon Sep 17 00:00:00 2001 From: BulbyVR <26726264+TheDrawingCoder-Gamer@users.noreply.github.com> Date: Sun, 7 Jan 2024 13:43:49 -0500 Subject: [PATCH 82/95] edit title bar grabbing --- wayland/cc/Pointer.cc | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/wayland/cc/Pointer.cc b/wayland/cc/Pointer.cc index a861ff70..a095d168 100644 --- a/wayland/cc/Pointer.cc +++ b/wayland/cc/Pointer.cc @@ -60,10 +60,16 @@ static xdg_toplevel_resize_edge resizeEdge(DecorationFocus focus, WindowWayland& // ??? return XDG_TOPLEVEL_RESIZE_EDGE_NONE; case DECORATION_FOCUS_TITLE: - if (y < -DECORATION_TOP_HEIGHT / 2) - return XDG_TOPLEVEL_RESIZE_EDGE_TOP; - else - return XDG_TOPLEVEL_RESIZE_EDGE_NONE; + if (x < 0) { + return XDG_TOPLEVEL_RESIZE_EDGE_TOP_LEFT; + } else if (x > rightEdge) { + return XDG_TOPLEVEL_RESIZE_EDGE_TOP_RIGHT; + } else { + if (y < -DECORATION_TOP_HEIGHT / 2) + return XDG_TOPLEVEL_RESIZE_EDGE_TOP; + else + return XDG_TOPLEVEL_RESIZE_EDGE_NONE; + } case DECORATION_FOCUS_BORDER: if (y < 0) { From adef9aef4ef5700bc22fda50ab9b37d5fe2ff488 Mon Sep 17 00:00:00 2001 From: TheDrawingCoding-Gamer Date: Sun, 7 Jan 2024 15:37:49 -0500 Subject: [PATCH 83/95] configure on first resize --- wayland/cc/Decoration.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/wayland/cc/Decoration.cc b/wayland/cc/Decoration.cc index fba2bdec..86232824 100644 --- a/wayland/cc/Decoration.cc +++ b/wayland/cc/Decoration.cc @@ -94,6 +94,8 @@ static void _xdgSurfaceConfigure(void* data, xdg_surface* surface, uint32_t seri if (window._layer) window._layer->attachBuffer(); } + // resize on first configure + self->_configured = true; // ask to configure _before_ commit. jank. if (self->_floating) { xdg_surface_ack_configure(self->_xdgSurface, serial); @@ -117,8 +119,6 @@ static void _xdgSurfaceConfigure(void* data, xdg_surface* surface, uint32_t seri if (!self->_border.surface) self->_showDecorations(!self->_isVisible); } - // at the end so that the size isn't adapted on first render - self->_configured = true; wl_surface_commit(window._waylandWindow); if (!self->_floating) { From d34bd628bd991d4638b51e4ce0e334bf5dcb22bb Mon Sep 17 00:00:00 2001 From: BulbyVR <26726264+TheDrawingCoder-Gamer@users.noreply.github.com> Date: Mon, 8 Jan 2024 18:21:40 -0500 Subject: [PATCH 84/95] always allow movement on border with ctrl --- wayland/cc/Pointer.cc | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/wayland/cc/Pointer.cc b/wayland/cc/Pointer.cc index a095d168..c428e33f 100644 --- a/wayland/cc/Pointer.cc +++ b/wayland/cc/Pointer.cc @@ -71,18 +71,17 @@ static xdg_toplevel_resize_edge resizeEdge(DecorationFocus focus, WindowWayland& return XDG_TOPLEVEL_RESIZE_EDGE_NONE; } case DECORATION_FOCUS_BORDER: - + if (state) { + if (xkb_state_mod_name_is_active(state, XKB_MOD_NAME_CTRL, XKB_STATE_MODS_EFFECTIVE)) { + return XDG_TOPLEVEL_RESIZE_EDGE_NONE; + } + } if (y < 0) { if (x < 0) return XDG_TOPLEVEL_RESIZE_EDGE_TOP_LEFT; else if (x > rightEdge) return XDG_TOPLEVEL_RESIZE_EDGE_TOP_RIGHT; else { - if (state) { - if (xkb_state_mod_name_is_active(state, XKB_MOD_NAME_CTRL, XKB_STATE_MODS_EFFECTIVE)) - // grab - return XDG_TOPLEVEL_RESIZE_EDGE_NONE; - } return XDG_TOPLEVEL_RESIZE_EDGE_TOP; } } From 737857e93e934dd1c2ec6556854c11b783fa2e1b Mon Sep 17 00:00:00 2001 From: BulbyVR <26726264+TheDrawingCoder-Gamer@users.noreply.github.com> Date: Mon, 8 Jan 2024 21:42:25 -0500 Subject: [PATCH 85/95] urgency/focus stealing only tested on sway; weston is known to not support activation, but mutter and kwin do. need to test there. On sway this is seen as a red flash in the swaybar. --- wayland/CMakeLists.txt | 4 ++++ wayland/cc/Decoration.cc | 20 ++++++++++--------- wayland/cc/Token.cc | 31 ++++++++++++++++++++++++++++++ wayland/cc/Token.hh | 14 ++++++++++++++ wayland/cc/WindowManagerWayland.cc | 3 +++ wayland/cc/WindowManagerWayland.hh | 3 ++- wayland/cc/WindowWayland.cc | 14 ++++++++++++++ wayland/cc/WindowWayland.hh | 1 + wayland/java/WindowWayland.java | 3 ++- 9 files changed, 82 insertions(+), 11 deletions(-) create mode 100644 wayland/cc/Token.cc create mode 100644 wayland/cc/Token.hh diff --git a/wayland/CMakeLists.txt b/wayland/CMakeLists.txt index 0b18c608..c3685b6f 100644 --- a/wayland/CMakeLists.txt +++ b/wayland/CMakeLists.txt @@ -57,6 +57,10 @@ ecm_add_wayland_client_protocol(protocols PROTOCOL "${WaylandProtocols_DATADIR}/stable/viewporter/viewporter.xml" BASENAME viewporter ) +ecm_add_wayland_client_protocol(protocols + PROTOCOL "${WaylandProtocols_DATADIR}/staging/xdg-activation/xdg-activation-v1.xml" + BASENAME xdg-activation-v1 + ) find_library(WAYLAND_CLIENT_LIB wayland-client) find_library(WAYLAND_CURSOR wayland-cursor) find_library(XKBCOMMON xkbcommon) diff --git a/wayland/cc/Decoration.cc b/wayland/cc/Decoration.cc index 86232824..ebd7bcca 100644 --- a/wayland/cc/Decoration.cc +++ b/wayland/cc/Decoration.cc @@ -58,14 +58,20 @@ static void _xdgSurfaceConfigure(void* data, xdg_surface* surface, uint32_t seri auto self = reinterpret_cast(data); auto& window = self->_window; int width = 0, height = 0; - - if (self->_pendingWidth > 0) - width = self->_pendingWidth; + int pendingWidth = self->_pendingWidth; + int pendingHeight = self->_pendingHeight; + // do it here bc we don't know configure order + if (!self->_serverSide) { + pendingWidth -= DECORATION_WIDTH + DECORATION_WIDTH; + pendingHeight -= DECORATION_WIDTH + self->getTopSize(); + } + if (pendingWidth > 0) + width = pendingWidth; else width = window._floatingWidth; - if (self->_pendingHeight > 0) - height = self->_pendingHeight; + if (pendingHeight > 0) + height = pendingHeight; else height = window._floatingHeight; @@ -135,10 +141,6 @@ static void _xdgToplevelConfigure(void* data, xdg_toplevel* toplevel, int width, self->_pendingWidth = width; self->_pendingHeight = height; - if (!self->_serverSide) { - self->_pendingWidth -= DECORATION_WIDTH + DECORATION_WIDTH; - self->_pendingHeight -= DECORATION_WIDTH + self->getTopSize(); - } bool active = false; bool maximized = false; diff --git a/wayland/cc/Token.cc b/wayland/cc/Token.cc new file mode 100644 index 00000000..d95034b2 --- /dev/null +++ b/wayland/cc/Token.cc @@ -0,0 +1,31 @@ +#include "Token.hh" +#include "WindowManagerWayland.hh" + + +static void _xdgTokenDone(void* data, xdg_activation_token_v1* token, const char* tokenStr) { + xdg_activation_token_v1_destroy(token); + std::string* str = reinterpret_cast(data); + *str = std::string(tokenStr); +} +static xdg_activation_token_v1_listener _tokenListener = { + .done = _xdgTokenDone +}; +jwm::Token jwm::Token::make(jwm::WindowManagerWayland& wm, wl_surface* surface) { + if (!wm.xdgActivation) + return {}; + auto token = xdg_activation_v1_get_activation_token(wm.xdgActivation); + if (surface) + xdg_activation_token_v1_set_surface(token, surface); + std::string str; + xdg_activation_token_v1_add_listener(token, &_tokenListener, &str); + xdg_activation_token_v1_commit(token); + wl_display_roundtrip(wm.display); + + return {str}; +} + +void jwm::Token::grab(jwm::WindowManagerWayland& wm, wl_surface* surface) { + if (!wm.xdgActivation) + return; + xdg_activation_v1_activate(wm.xdgActivation, token.c_str(), surface); +} diff --git a/wayland/cc/Token.hh b/wayland/cc/Token.hh new file mode 100644 index 00000000..23f781cf --- /dev/null +++ b/wayland/cc/Token.hh @@ -0,0 +1,14 @@ +#include +#include +#include + +namespace jwm { + class WindowManagerWayland; + struct Token { + std::string token; + // blocks + static Token make(WindowManagerWayland& wm, wl_surface* surface); + + void grab(WindowManagerWayland& wm, wl_surface* surface); + }; +} diff --git a/wayland/cc/WindowManagerWayland.cc b/wayland/cc/WindowManagerWayland.cc index 4bbd59d9..14bc4f5a 100644 --- a/wayland/cc/WindowManagerWayland.cc +++ b/wayland/cc/WindowManagerWayland.cc @@ -270,6 +270,9 @@ void WindowManagerWayland::registryHandleGlobal(void* data, wl_registry *registr } else if (strcmp(interface, zxdg_decoration_manager_v1_interface.name) == 0) { self->decorationManager = (zxdg_decoration_manager_v1*)wl_registry_bind(registry, name, &zxdg_decoration_manager_v1_interface, 1); + } else if (strcmp(interface, xdg_activation_v1_interface.name) == 0) { + self->xdgActivation = (xdg_activation_v1*)wl_registry_bind(registry, name, + &xdg_activation_v1_interface, 1); } } void WindowManagerWayland::registryHandleGlobalRemove(void* data, wl_registry *registry, uint32_t name) { diff --git a/wayland/cc/WindowManagerWayland.hh b/wayland/cc/WindowManagerWayland.hh index e0c63917..f0c536a7 100644 --- a/wayland/cc/WindowManagerWayland.hh +++ b/wayland/cc/WindowManagerWayland.hh @@ -25,6 +25,7 @@ #include #include #include +#include namespace jwm { class WindowWayland; @@ -77,7 +78,7 @@ namespace jwm { wp_viewporter* viewporter = nullptr; zxdg_decoration_manager_v1* decorationManager = nullptr; wl_subcompositor* subcompositor = nullptr; - + xdg_activation_v1* xdgActivation = nullptr; std::unique_ptr _pointer = nullptr; Pointer* getPointer() const { return _pointer.get(); diff --git a/wayland/cc/WindowWayland.cc b/wayland/cc/WindowWayland.cc index 954d69c6..a65eddbe 100644 --- a/wayland/cc/WindowWayland.cc +++ b/wayland/cc/WindowWayland.cc @@ -8,6 +8,7 @@ #include #include #include +#include "Token.hh" using namespace jwm; @@ -335,6 +336,12 @@ void jwm::WindowWayland::hideCursor(bool hidden) { pointer->unhide(); } } + +void jwm::WindowWayland::focus() { + if (!_waylandWindow) return; + auto token = Token::make(_windowManager, _waylandWindow); + token.grab(_windowManager, _waylandWindow); +} // JNI extern "C" JNIEXPORT jlong JNICALL Java_io_github_humbleui_jwm_WindowWayland__1nMake @@ -480,3 +487,10 @@ extern "C" JNIEXPORT void JNICALL Java_io_github_humbleui_jwm_WindowWayland__1nH jwm::WindowWayland* instance = reinterpret_cast(jwm::classes::Native::fromJava(env, obj)); instance->hideCursor(hidden); } + +extern "C" JNIEXPORT void JNICALL Java_io_github_humbleui_jwm_WindowWayland__1nFocus + (JNIEnv* env, jobject obj) +{ + jwm::WindowWayland* instance = reinterpret_cast(jwm::classes::Native::fromJava(env, obj)); + instance->focus(); +} diff --git a/wayland/cc/WindowWayland.hh b/wayland/cc/WindowWayland.hh index e853acc2..753f6d64 100644 --- a/wayland/cc/WindowWayland.hh +++ b/wayland/cc/WindowWayland.hh @@ -55,6 +55,7 @@ namespace jwm { void setCursorMaybe(jwm::MouseCursor cursor, bool force); void setCursor(jwm::MouseCursor cursor); void setLayer(ILayerWayland* layer); + void focus(); wl_cursor* _getCursorFor(jwm::MouseCursor cursor); ScreenInfo getScreen(); diff --git a/wayland/java/WindowWayland.java b/wayland/java/WindowWayland.java index a8601472..59d94310 100644 --- a/wayland/java/WindowWayland.java +++ b/wayland/java/WindowWayland.java @@ -155,7 +155,7 @@ public Window minimize() { @Override public Window focus() { assert _onUIThread() : "Should be run on UI thread"; - // TODO implement + _nFocus(); return this; } @@ -242,4 +242,5 @@ public float getScale() { @ApiStatus.Internal public native float _nGetScale(); @ApiStatus.Internal public native void _nLockMouseCursor(boolean locked); @ApiStatus.Internal public native void _nHideMouseCursor(boolean hidden); + @ApiStatus.Internal public native void _nFocus(); } From 8f8842b605a44b829024a4461c487ecce4804570 Mon Sep 17 00:00:00 2001 From: BulbyVR <26726264+TheDrawingCoder-Gamer@users.noreply.github.com> Date: Mon, 8 Jan 2024 21:56:39 -0500 Subject: [PATCH 86/95] Add example code for focus Focus stealing isn't possible in most compositors, and sway uses focus stealing attempts as a way to mark urgency. Focus will simply activate itself (stealing focus) and notify on sway. --- examples/dashboard/java/Example.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/examples/dashboard/java/Example.java b/examples/dashboard/java/Example.java index 28150556..27613297 100644 --- a/examples/dashboard/java/Example.java +++ b/examples/dashboard/java/Example.java @@ -200,6 +200,18 @@ public void accept(Event e) { window.minimize(); case B -> setProgressBar(progressBars.next()); + case S -> { + var timer = new Timer(); + // delay to allow workspace/focus switching + timer.schedule(new TimerTask() { + public void run() { + App.runOnUIThread(() -> { + window.focus(); + timer.cancel(); + }); + } + }, 2000); + } } } } else if (e instanceof EventFrame) { From 27ed3079f24a7407ec24694af958a68f13b75413 Mon Sep 17 00:00:00 2001 From: BulbyVR <26726264+TheDrawingCoder-Gamer@users.noreply.github.com> Date: Tue, 9 Jan 2024 13:09:46 -0500 Subject: [PATCH 87/95] slim decorations --- wayland/cc/Decoration.cc | 32 +++++++++++++++++++------------- wayland/cc/Decoration.hh | 16 +++++++++------- wayland/cc/Pointer.cc | 16 +++++----------- 3 files changed, 33 insertions(+), 31 deletions(-) diff --git a/wayland/cc/Decoration.cc b/wayland/cc/Decoration.cc index ebd7bcca..b9ae45a2 100644 --- a/wayland/cc/Decoration.cc +++ b/wayland/cc/Decoration.cc @@ -6,6 +6,7 @@ using namespace jwm; static unsigned int grey_data[] = {0xFF333333}; +static unsigned int zero_data[] = {0x00000000}; // no image editor // pure unadulterated programming static unsigned int close_data[] = { @@ -62,8 +63,7 @@ static void _xdgSurfaceConfigure(void* data, xdg_surface* surface, uint32_t seri int pendingHeight = self->_pendingHeight; // do it here bc we don't know configure order if (!self->_serverSide) { - pendingWidth -= DECORATION_WIDTH + DECORATION_WIDTH; - pendingHeight -= DECORATION_WIDTH + self->getTopSize(); + pendingHeight -= self->getTopSize(); } if (pendingWidth > 0) width = pendingWidth; @@ -275,14 +275,18 @@ void Decoration::_showDecorations(bool hidden) { // When destroyed these get released _decBuffer = Buffer::createShmBuffer(_wm.shm, 1, 1, WL_SHM_FORMAT_ARGB8888); memcpy(_decBuffer->getData(), grey_data, 1 * sizeof(uint32_t)); + _zeroBuffer = Buffer::createShmBuffer(_wm.shm, 1, 1, WL_SHM_FORMAT_ARGB8888); + memcpy(_zeroBuffer->getData(), zero_data, 1 * sizeof(uint32_t)); _closeBuffer = Buffer::createShmBuffer(_wm.shm, 9, 9, WL_SHM_FORMAT_ARGB8888); memcpy(_closeBuffer->getData(), close_data, 9 * 9 * sizeof(uint32_t)); _maxBuffer = Buffer::createShmBuffer(_wm.shm, 9, 9, WL_SHM_FORMAT_ARGB8888); memcpy(_maxBuffer->getData(), max_data, 9 * 9 * sizeof(uint32_t)); _minBuffer = Buffer::createShmBuffer(_wm.shm, 9, 9, WL_SHM_FORMAT_ARGB8888); memcpy(_minBuffer->getData(), min_data, 9 * 9 * sizeof(uint32_t)); - _makePart(&_border, _decBuffer, true, DECORATION_BORDER_X, DECORATION_BORDER_Y, - DECORATION_BORDER_WIDTH(_window), DECORATION_BORDER_HEIGHT(_window)); + int borderY = hidden ? DECORATION_BORDER_HIDDEN_Y : DECORATION_BORDER_Y; + int borderHeight = hidden ? DECORATION_BORDER_HEIGHT(_window) : DECORATION_BORDER_FULL_HEIGHT(_window); + _makePart(&_border, _zeroBuffer, false, DECORATION_BORDER_X, borderY, + DECORATION_BORDER_WIDTH(_window), borderHeight); wl_subsurface_place_below(_border.subsurface, _window._waylandWindow); if (!hidden) { _makePart(&_titleComp, _decBuffer, true, DECORATION_TITLE_X, DECORATION_TITLE_Y, @@ -296,8 +300,10 @@ void Decoration::_showDecorations(bool hidden) { } void Decoration::_adaptSize() { - _resizeDecoration(&_border, DECORATION_BORDER_X, DECORATION_BORDER_Y, - DECORATION_BORDER_WIDTH(_window), DECORATION_BORDER_HEIGHT(_window)); + int borderY = _isVisible ? DECORATION_BORDER_Y : DECORATION_BORDER_HIDDEN_Y; + int borderHeight = _isVisible ? DECORATION_BORDER_FULL_HEIGHT(_window) : DECORATION_BORDER_HEIGHT(_window); + _resizeDecoration(&_border, DECORATION_BORDER_X, borderY, + DECORATION_BORDER_WIDTH(_window), borderHeight); if (_isVisible) { _resizeDecoration(&_titleComp, DECORATION_TITLE_X, DECORATION_TITLE_Y, DECORATION_TITLE_WIDTH(_window), DECORATION_TITLE_HEIGHT); @@ -307,9 +313,9 @@ void Decoration::_adaptSize() { } if (!_serverSide) - xdg_surface_set_window_geometry(_xdgSurface, DECORATION_BORDER_X, _isVisible ? DECORATION_TITLE_Y : DECORATION_BORDER_Y, - DECORATION_BORDER_WIDTH(_window), - _isVisible ? DECORATION_BORDER_FULL_HEIGHT(_window) : DECORATION_BORDER_HEIGHT(_window)); + xdg_surface_set_window_geometry(_xdgSurface, 0, _isVisible ? DECORATION_TITLE_Y : 0, + _window.getUnscaledWidth(), + (_isVisible ? DECORATION_TITLE_HEIGHT : 0) + _window.getUnscaledHeight() ); else xdg_surface_set_window_geometry(_xdgSurface, 0, 0, _window.getUnscaledWidth(), _window.getUnscaledHeight()); } @@ -376,10 +382,10 @@ void Decoration::getBorders(int& left, int& top, int& right, int& bottom) { right = 0; bottom = 0; } else { - left = DECORATION_WIDTH; + left = 0; top = getTopSize(); - right = DECORATION_WIDTH; - bottom = DECORATION_WIDTH; + right = 0; + bottom = 0; } } @@ -389,5 +395,5 @@ int Decoration::getTopSize() { if (_isVisible) return DECORATION_TOP_HEIGHT; else - return DECORATION_WIDTH; + return 0; } diff --git a/wayland/cc/Decoration.hh b/wayland/cc/Decoration.hh index 7c18708b..ceec149d 100644 --- a/wayland/cc/Decoration.hh +++ b/wayland/cc/Decoration.hh @@ -9,17 +9,18 @@ #include #define DECORATION_WIDTH 4 -#define DECORATION_TOP_HEIGHT 30 +#define DECORATION_TOP_HEIGHT 25 #define DECORATION_BORDER_X -(DECORATION_WIDTH) -#define DECORATION_BORDER_Y -(DECORATION_WIDTH) +#define DECORATION_BORDER_HIDDEN_Y -(DECORATION_WIDTH) +#define DECORATION_BORDER_Y -(DECORATION_WIDTH + DECORATION_TOP_HEIGHT) #define DECORATION_BORDER_WIDTH(window) window.getUnscaledWidth() + DECORATION_WIDTH + DECORATION_WIDTH #define DECORATION_BORDER_HEIGHT(window) window.getUnscaledHeight() + DECORATION_WIDTH + DECORATION_WIDTH -#define DECORATION_BORDER_FULL_HEIGHT(window) window.getUnscaledHeight() + DECORATION_TOP_HEIGHT + DECORATION_WIDTH +#define DECORATION_BORDER_FULL_HEIGHT(window) window.getUnscaledHeight() + DECORATION_TOP_HEIGHT + DECORATION_WIDTH + DECORATION_WIDTH -#define DECORATION_TITLE_X -(DECORATION_WIDTH) +#define DECORATION_TITLE_X 0 #define DECORATION_TITLE_Y -(DECORATION_TOP_HEIGHT) -#define DECORATION_TITLE_WIDTH(window) window.getUnscaledWidth() + DECORATION_WIDTH + DECORATION_WIDTH +#define DECORATION_TITLE_WIDTH(window) window.getUnscaledWidth() #define DECORATION_TITLE_HEIGHT DECORATION_TOP_HEIGHT #define DECORATION_CLOSE_X(window) window.getUnscaledWidth() - 10 @@ -28,12 +29,12 @@ #define DECORATION_CLOSE_HEIGHT 9 #define DECORATION_MAX_X(window) (DECORATION_CLOSE_X(window)) - 10 -#define DECORATION_MAX_Y -20 +#define DECORATION_MAX_Y DECORATION_CLOSE_Y #define DECORATION_MAX_WIDTH 9 #define DECORATION_MAX_HEIGHT 9 #define DECORATION_MIN_X(window) (DECORATION_MAX_X(window)) - 10 -#define DECORATION_MIN_Y -20 +#define DECORATION_MIN_Y DECORATION_CLOSE_Y #define DECORATION_MIN_WIDTH 9 #define DECORATION_MIN_HEIGHT 9 @@ -65,6 +66,7 @@ namespace jwm { WindowWayland& _window; Buffer* _decBuffer; + Buffer* _zeroBuffer; Buffer* _closeBuffer; Buffer* _maxBuffer; Buffer* _minBuffer; diff --git a/wayland/cc/Pointer.cc b/wayland/cc/Pointer.cc index c428e33f..b131ef4d 100644 --- a/wayland/cc/Pointer.cc +++ b/wayland/cc/Pointer.cc @@ -53,6 +53,7 @@ static void pointerLeave(void* data, wl_pointer* pointer, uint32_t serial, } } static xdg_toplevel_resize_edge resizeEdge(DecorationFocus focus, WindowWayland& window, int x, int y, xkb_state* state) { + int topEdge = window._decoration->_isVisible ? DECORATION_TITLE_Y : 0; int rightEdge = window.getUnscaledWidth(); int bottomEdge = window.getUnscaledHeight(); switch (focus) { @@ -60,23 +61,14 @@ static xdg_toplevel_resize_edge resizeEdge(DecorationFocus focus, WindowWayland& // ??? return XDG_TOPLEVEL_RESIZE_EDGE_NONE; case DECORATION_FOCUS_TITLE: - if (x < 0) { - return XDG_TOPLEVEL_RESIZE_EDGE_TOP_LEFT; - } else if (x > rightEdge) { - return XDG_TOPLEVEL_RESIZE_EDGE_TOP_RIGHT; - } else { - if (y < -DECORATION_TOP_HEIGHT / 2) - return XDG_TOPLEVEL_RESIZE_EDGE_TOP; - else - return XDG_TOPLEVEL_RESIZE_EDGE_NONE; - } + return XDG_TOPLEVEL_RESIZE_EDGE_NONE; case DECORATION_FOCUS_BORDER: if (state) { if (xkb_state_mod_name_is_active(state, XKB_MOD_NAME_CTRL, XKB_STATE_MODS_EFFECTIVE)) { return XDG_TOPLEVEL_RESIZE_EDGE_NONE; } } - if (y < 0) { + if (y < topEdge) { if (x < 0) return XDG_TOPLEVEL_RESIZE_EDGE_TOP_LEFT; else if (x > rightEdge) @@ -128,6 +120,8 @@ static void pointerMotion(void* data, wl_pointer* pointer, uint32_t time, if (self->_decorationFocus == DECORATION_FOCUS_BORDER) { self->_absX = x - DECORATION_WIDTH; self->_absY = y - DECORATION_WIDTH; + if (self->_focusedSurface->_decoration->_isVisible) + self->_absY -= DECORATION_TOP_HEIGHT; } else if (self->_decorationFocus == DECORATION_FOCUS_TITLE) { self->_absX = x - DECORATION_WIDTH; self->_absY = y - DECORATION_TOP_HEIGHT; From ccd584fa76b966a96202c2366f3603c58afcbf01 Mon Sep 17 00:00:00 2001 From: BulbyVR <26726264+TheDrawingCoder-Gamer@users.noreply.github.com> Date: Tue, 9 Jan 2024 14:09:01 -0500 Subject: [PATCH 88/95] constrain size of window --- wayland/cc/Decoration.cc | 35 ++++++++++++++++++++++++++--------- wayland/cc/Decoration.hh | 5 +++++ 2 files changed, 31 insertions(+), 9 deletions(-) diff --git a/wayland/cc/Decoration.cc b/wayland/cc/Decoration.cc index b9ae45a2..328656e9 100644 --- a/wayland/cc/Decoration.cc +++ b/wayland/cc/Decoration.cc @@ -74,7 +74,8 @@ static void _xdgSurfaceConfigure(void* data, xdg_surface* surface, uint32_t seri height = pendingHeight; else height = window._floatingHeight; - + if (self->_floating) + self->constrainSize(width, height); self->_pendingWidth = 0; self->_pendingHeight = 0; if (self->_oldActive != self->_active) { @@ -100,6 +101,13 @@ static void _xdgSurfaceConfigure(void* data, xdg_surface* surface, uint32_t seri if (window._layer) window._layer->attachBuffer(); } + if (self->_serverSide) { + if (self->_border.surface) + self->_destroyDecorations(); + } else { + if (!self->_border.surface) + self->_showDecorations(!self->_isVisible); + } // resize on first configure self->_configured = true; // ask to configure _before_ commit. jank. @@ -118,13 +126,7 @@ static void _xdgSurfaceConfigure(void* data, xdg_surface* surface, uint32_t seri window._adaptSize(width, height); self->_adaptSize(); } - if (self->_serverSide) { - if (self->_border.surface) - self->_destroyDecorations(); - } else { - if (!self->_border.surface) - self->_showDecorations(!self->_isVisible); - } + wl_surface_commit(window._waylandWindow); if (!self->_floating) { @@ -287,7 +289,7 @@ void Decoration::_showDecorations(bool hidden) { int borderHeight = hidden ? DECORATION_BORDER_HEIGHT(_window) : DECORATION_BORDER_FULL_HEIGHT(_window); _makePart(&_border, _zeroBuffer, false, DECORATION_BORDER_X, borderY, DECORATION_BORDER_WIDTH(_window), borderHeight); - wl_subsurface_place_below(_border.subsurface, _window._waylandWindow); + wl_subsurface_place_below(_border.subsurface, _window._waylandWindow); if (!hidden) { _makePart(&_titleComp, _decBuffer, true, DECORATION_TITLE_X, DECORATION_TITLE_Y, DECORATION_TITLE_WIDTH(_window), DECORATION_TITLE_HEIGHT); @@ -397,3 +399,18 @@ int Decoration::getTopSize() { else return 0; } + +void Decoration::setMinSize(int width, int height) { + _minWidth = width; + _minHeight = height; +} + +void Decoration::constrainSize(int& width, int& height) { + if (width < _minWidth) + width = _minWidth; + // hard coded for buttons + if (width < 40) + width = 40; + if (height < _minHeight) + height = _minHeight; +} diff --git a/wayland/cc/Decoration.hh b/wayland/cc/Decoration.hh index ceec149d..f1d7729e 100644 --- a/wayland/cc/Decoration.hh +++ b/wayland/cc/Decoration.hh @@ -121,6 +121,11 @@ namespace jwm { int getTopSize(); + int _minWidth = 10; + int _minHeight = 10; + void setMinSize(int width, int height); + void constrainSize(int& width, int& height); + private: Decoration(Decoration&& other) = delete; From 1c2f4be5ff97b4d21691b9d95babe39dc6427be4 Mon Sep 17 00:00:00 2001 From: BulbyVR <26726264+TheDrawingCoder-Gamer@users.noreply.github.com> Date: Sun, 14 Jan 2024 16:52:27 -0500 Subject: [PATCH 89/95] Use var for protocols --- wayland/CMakeLists.txt | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/wayland/CMakeLists.txt b/wayland/CMakeLists.txt index c3685b6f..eef3c646 100644 --- a/wayland/CMakeLists.txt +++ b/wayland/CMakeLists.txt @@ -7,7 +7,7 @@ set(CMAKE_MODULE_PATH ${ECM_MODULE_PATH}) include(FindWaylandProtocols) include(FindWaylandScanner) -find_package(WaylandProtocols 1.32) +find_package(WaylandProtocols 1.25) set_package_properties(WaylandProtocols PROPERTIES TYPE REQUIRED ) @@ -35,32 +35,31 @@ file(GLOB SOURCES_CXX ${CMAKE_CURRENT_LIST_DIR}/../shared/cc/*.cc ${CMAKE_CURRENT_LIST_DIR}/cc/*.cc ) file(GLOB SOURCES_CXX_IMPL ${CMAKE_CURRENT_LIST_DIR}/../shared/cc/impl/*.cc) add_library(jwm SHARED ${SOURCES_OBJC} ${SOURCES_CXX} ${SOURCES_CXX_IMPL}) -add_library(protocols STATIC) -# set_property(TARGET cursorlock PROPERTY POSITION_INDEPENDENT_CODE ON) -ecm_add_wayland_client_protocol(protocols +ecm_add_wayland_client_protocol(PROTOCOLS_SOURCE PROTOCOL "${WaylandProtocols_DATADIR}/unstable/pointer-constraints/pointer-constraints-unstable-v1.xml" BASENAME pointer-constraints-unstable-v1 ) -ecm_add_wayland_client_protocol(protocols +ecm_add_wayland_client_protocol(PROTOCOLS_SOURCE PROTOCOL "${WaylandProtocols_DATADIR}/unstable/relative-pointer/relative-pointer-unstable-v1.xml" BASENAME relative-pointer-unstable-v1 ) -ecm_add_wayland_client_protocol(protocols +ecm_add_wayland_client_protocol(PROTOCOLS_SOURCE PROTOCOL "${WaylandProtocols_DATADIR}/stable/xdg-shell/xdg-shell.xml" BASENAME xdg-shell ) -ecm_add_wayland_client_protocol(protocols +ecm_add_wayland_client_protocol(PROTOCOLS_SOURCE PROTOCOL "${WaylandProtocols_DATADIR}/unstable/xdg-decoration/xdg-decoration-unstable-v1.xml" BASENAME xdg-decoration-unstable-v1 ) -ecm_add_wayland_client_protocol(protocols +ecm_add_wayland_client_protocol(PROTOCOLS_SOURCE PROTOCOL "${WaylandProtocols_DATADIR}/stable/viewporter/viewporter.xml" BASENAME viewporter ) -ecm_add_wayland_client_protocol(protocols +ecm_add_wayland_client_protocol(PROTOCOLS_SOURCE PROTOCOL "${WaylandProtocols_DATADIR}/staging/xdg-activation/xdg-activation-v1.xml" BASENAME xdg-activation-v1 ) +add_library(protocols STATIC ${PROTOCOLS_SOURCE}) find_library(WAYLAND_CLIENT_LIB wayland-client) find_library(WAYLAND_CURSOR wayland-cursor) find_library(XKBCOMMON xkbcommon) From a7f078f5e372281f8c1d00a9b55d4b7f643cc565 Mon Sep 17 00:00:00 2001 From: BulbyVR <26726264+TheDrawingCoder-Gamer@users.noreply.github.com> Date: Sun, 14 Jan 2024 17:13:00 -0500 Subject: [PATCH 90/95] Omit functions that are high version --- wayland/cc/Decoration.cc | 8 -------- 1 file changed, 8 deletions(-) diff --git a/wayland/cc/Decoration.cc b/wayland/cc/Decoration.cc index 328656e9..856690ea 100644 --- a/wayland/cc/Decoration.cc +++ b/wayland/cc/Decoration.cc @@ -183,17 +183,9 @@ static void _xdgToplevelClose(void* data, xdg_toplevel* toplevel) { window.dispatch(classes::EventWindowCloseRequest::kInstance); } -static void _xdgToplevelConfigureBounds(void* data, xdg_toplevel* toplevel, int width, int height) { - // above version -} -static void _xdgToplevelWmCapabilities(void* data, xdg_toplevel* toplevel, wl_array* capabilities) { - // above version -} static xdg_toplevel_listener _xdgToplevelListener = { .configure = _xdgToplevelConfigure, .close = _xdgToplevelClose, - .configure_bounds = _xdgToplevelConfigureBounds, - .wm_capabilities = _xdgToplevelWmCapabilities }; const char* Decoration::proxyTag = "DecorationJWM"; From d407cbaf45e5284433f44582dc281dfc206f6570 Mon Sep 17 00:00:00 2001 From: BulbyVR <26726264+TheDrawingCoder-Gamer@users.noreply.github.com> Date: Tue, 16 Jan 2024 14:13:29 -0500 Subject: [PATCH 91/95] remove atoms --- wayland/cc/WindowWayland.hh | 2 -- 1 file changed, 2 deletions(-) diff --git a/wayland/cc/WindowWayland.hh b/wayland/cc/WindowWayland.hh index 753f6d64..6a5e0a5e 100644 --- a/wayland/cc/WindowWayland.hh +++ b/wayland/cc/WindowWayland.hh @@ -84,8 +84,6 @@ namespace jwm { int _oldScale = 1; int _height = -1; int _newHeight = -1; - int _WM_ADD = 1L; - int _WM_REMOVE = 0L; int _floatingWidth = 400; int _floatingHeight = 400; bool _canMinimize = false; From 030cd7d75e332df4526165765872bd7050b509a7 Mon Sep 17 00:00:00 2001 From: BulbyVR <26726264+TheDrawingCoder-Gamer@users.noreply.github.com> Date: Fri, 19 Jan 2024 12:46:33 -0500 Subject: [PATCH 92/95] fix crash on close under sway --- wayland/cc/Decoration.cc | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/wayland/cc/Decoration.cc b/wayland/cc/Decoration.cc index 856690ea..9dd3affa 100644 --- a/wayland/cc/Decoration.cc +++ b/wayland/cc/Decoration.cc @@ -209,10 +209,16 @@ Decoration::Decoration(WindowWayland& window): void Decoration::close() { if (_border.surface) _destroyDecorations(); - if (_decoration) - zxdg_toplevel_decoration_v1_destroy(_decoration); + if (_decoration) { + // ??? + // zxdg_toplevel_decoration_v1_destroy(_decoration); + _decoration = nullptr; + } xdg_toplevel_destroy(_xdgToplevel); + _xdgToplevel = nullptr; xdg_surface_destroy(_xdgSurface); + _xdgSurface = nullptr; + } void Decoration::_makePart(DecorationPart* decoration, Buffer* buf, bool opaque, int x, int y, int width, int height) { From 9baac616a8ce512dbaed21da9424c60697858a5d Mon Sep 17 00:00:00 2001 From: TheDrawingCoding-Gamer Date: Fri, 19 Jan 2024 13:10:15 -0500 Subject: [PATCH 93/95] NVIDIA JUST WORKS:tm: Taken from Sodium's nvidia workaround. Disables optimizations for threading on Nvidia, thus preventing some weird crashes. --- wayland/cc/LayerGLWayland.cc | 12 ++++++++++++ wayland/cc/WindowManagerWayland.hh | 4 +++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/wayland/cc/LayerGLWayland.cc b/wayland/cc/LayerGLWayland.cc index ebe5a658..815fa6fd 100644 --- a/wayland/cc/LayerGLWayland.cc +++ b/wayland/cc/LayerGLWayland.cc @@ -10,6 +10,9 @@ #include #include #include "ILayerWayland.hh" +#include +#include +#include namespace jwm { @@ -32,6 +35,7 @@ namespace jwm { fprintf(stderr, "already closed\n"); throw std::runtime_error("Already closed"); } + fWindow = jwm::ref(window); fWindow->setLayer(this); if (fWindow->_windowManager._eglDisplay == EGL_NO_DISPLAY) { @@ -39,11 +43,19 @@ namespace jwm { eglInitialize(fWindow->_windowManager._eglDisplay, nullptr, nullptr); + fWindow->_windowManager.vendor = eglQueryString(_display, EGL_VENDOR); + + if (fWindow->_windowManager.vendor != nullptr && (strcmp(fWindow->_windowManager.vendor, "NVIDIA") == 0)) { + // Thankfully observed from minecraft's sodium + // https://github.com/CaffeineMC/sodium-fabric/blob/dev/src/main/java/me/jellysquid/mods/sodium/client/compatibility/workarounds/nvidia/NvidiaWorkarounds.java#L29 + setenv("__GL_THREADED_OPTIMIZATIONS", "0", true); + } } _display = fWindow->_windowManager._eglDisplay; if ( eglBindAPI(EGL_OPENGL_API) == EGL_FALSE) { throw new std::runtime_error("Cannot bind EGL Api"); } + if (_context == nullptr) { EGLint attrList[] = { EGL_SURFACE_TYPE, EGL_WINDOW_BIT, diff --git a/wayland/cc/WindowManagerWayland.hh b/wayland/cc/WindowManagerWayland.hh index f0c536a7..d9c2bb08 100644 --- a/wayland/cc/WindowManagerWayland.hh +++ b/wayland/cc/WindowManagerWayland.hh @@ -16,7 +16,8 @@ #include "Output.hh" #include #include -#include +#include +#include #include "Keyboard.hh" #include "Pointer.hh" #include @@ -115,6 +116,7 @@ namespace jwm { std::map _myClipboardContents; std::map _myClipboardSource; std::list _currentMimeTypes; + const char* vendor = nullptr; wl_surface* getCursorSurface() const { if (_pointer) From eab4687e9eb852d3759205c8040ef7d1644ad00b Mon Sep 17 00:00:00 2001 From: TheDrawingCoding-Gamer Date: Thu, 22 Jan 2026 10:09:37 -0500 Subject: [PATCH 94/95] remove metals --- .gitignore | 5 +- .metals/metals.lock.db | 6 --- .metals/metals.log | 101 ----------------------------------------- .metals/metals.mv.db | Bin 36864 -> 0 bytes .vscode/settings.json | 3 +- 5 files changed, 6 insertions(+), 109 deletions(-) delete mode 100644 .metals/metals.lock.db delete mode 100644 .metals/metals.log delete mode 100644 .metals/metals.mv.db diff --git a/.gitignore b/.gitignore index 2283ec96..834bac3d 100644 --- a/.gitignore +++ b/.gitignore @@ -13,4 +13,7 @@ docs/apidocs /macos/cmake-build-debug # jvm crash logs -hs_err_*.log \ No newline at end of file +hs_err_*.log + +# metals +.metals \ No newline at end of file diff --git a/.metals/metals.lock.db b/.metals/metals.lock.db deleted file mode 100644 index 8b7e1ce8..00000000 --- a/.metals/metals.lock.db +++ /dev/null @@ -1,6 +0,0 @@ -#FileLock -#Thu Jan 22 09:55:44 EST 2026 -hostName=localhost -id=19be6344720a31039ee91c2d45d1c13e1c6005deee8 -method=file -server=localhost\:40481 diff --git a/.metals/metals.log b/.metals/metals.log deleted file mode 100644 index 674857b9..00000000 --- a/.metals/metals.log +++ /dev/null @@ -1,101 +0,0 @@ -2026.01.22 09:55:43 INFO Started: Metals version 1.6.5 in folders '/home/bulby/Documents/github/JWM' for client Code - OSS 1.106.0. -2026.01.22 09:55:43 INFO Memory monitor started -2026.01.22 09:55:44 WARN Using H2 2.4.240 which is newer than the version Flyway has been verified with. The latest verified version of H2 is 2.3.232. -2026.01.22 09:55:44 WARN no build tool detected in workspace '/home/bulby/Documents/github/JWM'. The most common cause for this problem is that the editor was opened in the wrong working directory, for example if you use sbt then the workspace directory should contain build.sbt. -2026.01.22 09:55:44 WARN Build server is not auto-connectable. -Jan 22, 2026 9:55:44 AM org.eclipse.lsp4j.jsonrpc.RemoteEndpoint handleNotification -WARNING: Notification threw an exception: { - "jsonrpc": "2.0", - "method": "metals/didFocusTextDocument", - "params": [ - "git:/home/bulby/Documents/github/JWM/examples/dashboard/java/Example.java?%7B%22path%22%3A%22%2Fhome%2Fbulby%2FDocuments%2Fgithub%2FJWM%2Fexamples%2Fdashboard%2Fjava%2FExample.java%22%2C%22ref%22%3A%22%22%7D" - ] -} -java.nio.file.FileSystemNotFoundException: Provider "git" not installed - at java.base/java.nio.file.Path.of(Path.java:213) - at java.base/java.nio.file.Paths.get(Paths.java:98) - at scala.meta.internal.mtags.MtagsEnrichments$XtensionURIMtags.toAbsolutePath(MtagsEnrichments.scala:149) - at scala.meta.internal.mtags.MtagsEnrichments$XtensionStringMtags.toAbsolutePath(MtagsEnrichments.scala:209) - at scala.meta.internal.metals.MetalsEnrichments$XtensionString.toAbsolutePath(MetalsEnrichments.scala:778) - at scala.meta.internal.metals.MetalsEnrichments$XtensionString.toAbsolutePath(MetalsEnrichments.scala:775) - at scala.meta.internal.metals.WorkspaceLspService.didFocus(WorkspaceLspService.scala:808) - at scala.meta.metals.lsp.DelegatingScalaService.didFocus(DelegatingScalaService.scala:42) - at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) - at java.base/java.lang.reflect.Method.invoke(Method.java:580) - at org.eclipse.lsp4j.jsonrpc.services.GenericEndpoint.lambda$recursiveFindRpcMethods$0(GenericEndpoint.java:65) - at org.eclipse.lsp4j.jsonrpc.services.GenericEndpoint.notify(GenericEndpoint.java:160) - at org.eclipse.lsp4j.jsonrpc.RemoteEndpoint.handleNotification(RemoteEndpoint.java:231) - at org.eclipse.lsp4j.jsonrpc.RemoteEndpoint.consume(RemoteEndpoint.java:198) - at org.eclipse.lsp4j.jsonrpc.json.StreamMessageProducer.handleMessage(StreamMessageProducer.java:185) - at org.eclipse.lsp4j.jsonrpc.json.StreamMessageProducer.listen(StreamMessageProducer.java:97) - at org.eclipse.lsp4j.jsonrpc.json.ConcurrentMessageProcessor.run(ConcurrentMessageProcessor.java:114) - at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:572) - at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:317) - at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1144) - at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:642) - at java.base/java.lang.Thread.run(Thread.java:1583) - -Jan 22, 2026 9:55:56 AM org.eclipse.lsp4j.jsonrpc.RemoteEndpoint handleNotification -WARNING: Notification threw an exception: { - "jsonrpc": "2.0", - "method": "metals/didFocusTextDocument", - "params": [ - "git:/home/bulby/Documents/github/JWM/examples/dashboard/java/Example.java?%7B%22path%22%3A%22%2Fhome%2Fbulby%2FDocuments%2Fgithub%2FJWM%2Fexamples%2Fdashboard%2Fjava%2FExample.java%22%2C%22ref%22%3A%22%22%7D" - ] -} -java.nio.file.FileSystemNotFoundException: Provider "git" not installed - at java.base/java.nio.file.Path.of(Path.java:213) - at java.base/java.nio.file.Paths.get(Paths.java:98) - at scala.meta.internal.mtags.MtagsEnrichments$XtensionURIMtags.toAbsolutePath(MtagsEnrichments.scala:149) - at scala.meta.internal.mtags.MtagsEnrichments$XtensionStringMtags.toAbsolutePath(MtagsEnrichments.scala:209) - at scala.meta.internal.metals.MetalsEnrichments$XtensionString.toAbsolutePath(MetalsEnrichments.scala:778) - at scala.meta.internal.metals.MetalsEnrichments$XtensionString.toAbsolutePath(MetalsEnrichments.scala:775) - at scala.meta.internal.metals.WorkspaceLspService.didFocus(WorkspaceLspService.scala:808) - at scala.meta.metals.lsp.DelegatingScalaService.didFocus(DelegatingScalaService.scala:42) - at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) - at java.base/java.lang.reflect.Method.invoke(Method.java:580) - at org.eclipse.lsp4j.jsonrpc.services.GenericEndpoint.lambda$recursiveFindRpcMethods$0(GenericEndpoint.java:65) - at org.eclipse.lsp4j.jsonrpc.services.GenericEndpoint.notify(GenericEndpoint.java:160) - at org.eclipse.lsp4j.jsonrpc.RemoteEndpoint.handleNotification(RemoteEndpoint.java:231) - at org.eclipse.lsp4j.jsonrpc.RemoteEndpoint.consume(RemoteEndpoint.java:198) - at org.eclipse.lsp4j.jsonrpc.json.StreamMessageProducer.handleMessage(StreamMessageProducer.java:185) - at org.eclipse.lsp4j.jsonrpc.json.StreamMessageProducer.listen(StreamMessageProducer.java:97) - at org.eclipse.lsp4j.jsonrpc.json.ConcurrentMessageProcessor.run(ConcurrentMessageProcessor.java:114) - at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:572) - at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:317) - at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1144) - at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:642) - at java.base/java.lang.Thread.run(Thread.java:1583) - -Jan 22, 2026 9:55:57 AM org.eclipse.lsp4j.jsonrpc.RemoteEndpoint handleNotification -WARNING: Notification threw an exception: { - "jsonrpc": "2.0", - "method": "metals/didFocusTextDocument", - "params": [ - "git:/home/bulby/Documents/github/JWM/examples/dashboard/java/PanelTheme.java?%7B%22path%22%3A%22%2Fhome%2Fbulby%2FDocuments%2Fgithub%2FJWM%2Fexamples%2Fdashboard%2Fjava%2FPanelTheme.java%22%2C%22ref%22%3A%22%22%7D" - ] -} -java.nio.file.FileSystemNotFoundException: Provider "git" not installed - at java.base/java.nio.file.Path.of(Path.java:213) - at java.base/java.nio.file.Paths.get(Paths.java:98) - at scala.meta.internal.mtags.MtagsEnrichments$XtensionURIMtags.toAbsolutePath(MtagsEnrichments.scala:149) - at scala.meta.internal.mtags.MtagsEnrichments$XtensionStringMtags.toAbsolutePath(MtagsEnrichments.scala:209) - at scala.meta.internal.metals.MetalsEnrichments$XtensionString.toAbsolutePath(MetalsEnrichments.scala:778) - at scala.meta.internal.metals.MetalsEnrichments$XtensionString.toAbsolutePath(MetalsEnrichments.scala:775) - at scala.meta.internal.metals.WorkspaceLspService.didFocus(WorkspaceLspService.scala:808) - at scala.meta.metals.lsp.DelegatingScalaService.didFocus(DelegatingScalaService.scala:42) - at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) - at java.base/java.lang.reflect.Method.invoke(Method.java:580) - at org.eclipse.lsp4j.jsonrpc.services.GenericEndpoint.lambda$recursiveFindRpcMethods$0(GenericEndpoint.java:65) - at org.eclipse.lsp4j.jsonrpc.services.GenericEndpoint.notify(GenericEndpoint.java:160) - at org.eclipse.lsp4j.jsonrpc.RemoteEndpoint.handleNotification(RemoteEndpoint.java:231) - at org.eclipse.lsp4j.jsonrpc.RemoteEndpoint.consume(RemoteEndpoint.java:198) - at org.eclipse.lsp4j.jsonrpc.json.StreamMessageProducer.handleMessage(StreamMessageProducer.java:185) - at org.eclipse.lsp4j.jsonrpc.json.StreamMessageProducer.listen(StreamMessageProducer.java:97) - at org.eclipse.lsp4j.jsonrpc.json.ConcurrentMessageProcessor.run(ConcurrentMessageProcessor.java:114) - at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:572) - at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:317) - at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1144) - at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:642) - at java.base/java.lang.Thread.run(Thread.java:1583) - diff --git a/.metals/metals.mv.db b/.metals/metals.mv.db deleted file mode 100644 index 7754a04d9fa0e79fe14275ba72201b5e2969b47c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 36864 zcmeHQYi#7!b>>i`)vR`9d+pe1{0euZ-PN??DS62mk^>_(HAAh&mPYanC9l%dEQiC{ zRd=LOHPU8Pv_R^_HJS!Z;r?h+r$x|L3N(e00x1f#K!Vl@QuL31q-cYn51?s^{s>T@ z0g`{|;k}n5l0!2QUp>2QUp>2QUp>2QUp>2QUp>2QUp>2-V+EcrTYJSf)6PXDFP`1DFP`1 zDFP`1DFP`1DFP`1DFP`1DFSDS05S3l#lEvs6^gsoO=k$U{B7%QRT6jzcdKe~Z!p-e zO5hT}^F7I~6!*8b9iQo6DDF6SU0;Cxfeop;+&%pRFM>=j^EHM&$K=WiFLuP9kSnWJ z#o}clr}QjTM!Ym-OL9*{YR<7b4v%Ch*OMffmsF)V*t=P}g-Y9ZhWmp(r?h{!v_X6p zobHCzKX8o0U1x65vf=IFl~Ckvr_>sJv~KNN{#e;b!jW+7eaNY_aJcVidwbTQmx>GJ ziDNAfc5ZqRW2Hcaia?4$ia?4$ia?4$ia?4$iaA*8*)%C11@wP}^?$(*=>H1V|I3nO6a8Nqx&9-f|4Uf^N1{B+ z{GMF6y9nN79%Lq2Cd0<6rg<(u^N9>mSh@AK-r^dITyxdn^c#(~(dP1PEzh;JWrMqB zsSc9MeAnXDP6hOxYuu97UgECxY#H%FuVag%=#*u(%v(xNbrew&rCvo46i41^}Xc3KY{*y$?jpbDTch{Oy{Xb!^mqtqwND)X8ND)X8ND)X8ND)X8`2UGO zQ2+MyGUU_$Eztj^GuHn_tp7{Y{@=m+zbL6hy%YuF0bsi;0G)29%aauVL9s@Pq)65P z6ubr?3g})g0>BEu)U#7>Fzl029sp&>vMoWia%HCjnyJ!rFGNc%E4hl*RVr%N&M6&5 zQ58jUFI3)kL~_v;$c4bGW&A`DBt$P{dLi{lDo973^vYduJAiz8Y8fZDU=^U}KC$Go z?6?;l2aZZ`FYK=1s5zxuQ7oKK*jd4@SYpMN%ek&9+p;6cKKBJwwk)|Lzjyo?&cKmk zB8waS8RX1PxH+Fe#;^u;CizaD!%#3x*r_Di1qH}}^{`}d*;B=Z*r&)hlQ8bYDQK8r zlhfelo16y7I3c~+Q_zq-1r2V#6A5XUJVo3b#GOi*?=-|cpD-?Np;;EZg6@eM(-}0# z++Owg78npRuV>ztu2EbCl;ZGB<6 zQOlR|z5d}xtwVDN#}jQ^=B+K)6YwxUKkXVQ@>@H@eXHMhx+b`r-Oh83rlDU41A=Qk zXs$0W7fCcaHOq6gC9S2^42ZFzwKf~g>+@y4KqGaXp}n`Y>w2t3MMOMb2%o$i9D)s=P)L-vAmAbxHU5xD|*||R@S(>zNoE(Ri(Dx zYUxeGJbDv?&$;W^2QDtA>peOmDU8POz_y*?FwZTlt}g3ZbM$_?Fvney^s$*fHQ}jQ$#bhs-@6zQ2y?%1g}XBIxeOSJfWOJa2+TfpuD0528!ZiF=cF>t zU63&!{qtiafCSd{8@_yk^|}f;>`)xSF(Yef4$ZCZ-Gq3I7~qP2n_FzO+D73Tw6V{6 z*0q>6R@c{bpu1VyG~27|t(xw&FGIg!nDy1#`id(c>#atF%#GMgMHIW7nugZ8uAi7> zx-buj{Cr}`P(w&W5S|~WxljPX_;ie5jAs!q6i1R`0wn=Kh$!_~Sy1yo35@yt35=KNe3Xu$)sK2c){bnT$n{i*`C1C>B3I|e|{k`e-=ahIVvcE{wPirfm5H2;nZ=N zCE^)S_pW%hn12R%_G>5bti1r-YFvj-)@wy8^>ReJq0I&QVq?AS_RCEekYLyf4qY!T z=}ohx*H&9~z3%J1rwcIbFXX;5F?o@B;K7G3J^aY~9)0ZnA9$Q80g11jKw`boUTL)3 zdfkN58jFpZW;9lt9=Q!DhYqBaMoh#gk&g=hd;P{*qow1T&=F?3@Y6tyNu?*I@2M@V zwxNo&*MO13Bg6OP7>S-PWFG<%{cvItttcTGjUXWJE4z@r1Ty*SF)}%xIFTg62p5n< ze|g7t8$f;bVF2|HPT+8WE^w*X&|4m^(b5=}ySBcp!$hfW(!}E|@bf-WK(mhkW7ZQh zMiq}^j8})eOMW5yKH$l#NqFLsA3M88Rmur$NEiNwPIj!yWW*qdjErWZhVJvY1x1cYtHm|o$V-+SVpmSv3590a} zT6%bL=93cqYHW@=&fq+*oPR2o{Q#8ka!d)~3{0FnFb@t#$G6%XulK&Yb>5Sf?jy)$(OT}k1$U>`P9=N+ z=x0Zz(1uLWOx&y!HX?sJ88v%oFi(O&{cMatjfrfye;uPeLYkg}0z8Ffz1e_XV7!6km#nshG5)2K=n)|)(nG_@~3U#EJf+Py2Bng_SsdC*^QN5k+Rdj$mc+b8wKfpNhb*iO+E<5jSxwgOYE(e%e#C>#j{9bOW)1b@d( zQ#RV5&$U;VH(Y&=E|r0lxl2Qy7K5wp>+i-80evz!GYC~ZqRC%7hN=DzIT23*p?AvC zqd_6-uKmPSIW)*eOomRbG7tH$BpyhlS_o7>D)UtD5CFwriC$s!M^6SKpcoh~PF6R7 z#w-F7z8=#$L!$#se&g0ReIf-T=;CTiZ(MJ>YqDh2O3*4HSxaBkq0!WIaMB+w*!yvy zEgWRE4*H~_b2Y79)9NsZScS#@+6!3`D)Y6t$^&gF0MXr?Qz4y?arW&sd#BaG);JB(q}mFTYvWZ z|B#`H{|6F({G|6L=KE|}X`IRqzXNqR=DrE6nKX{VUuIaL240ij@ z9jDK2JKG&NYJq`qI|R1xZ-7N}n(IvsUgi^nsD55e;MbW%Lkxbjrj4KB7klt^60%R@1A0{>j! zx1v6p2;I!b3u zbMn*a9qto-P*8yS_{QS;gGNw6QI6#fb{`F-yAMtx3JyABk`jD9)-e5JQjE0tx!0o} zvM|Z~^HTqO<|;Xrs*5|1k~!m!IVIZH34yyx;7;}5Au(`Q4%{h$yFfaT_rDWS;4YAh z7|2Bmq>~hK%oU-!;=iG9-u>BGgfh}d1X?=1qjjfuQXnR+1ARy9K<*Glm)=oy>7DF9 zxGPYA7`zKalutO}D|p}+3f_wTSxQVdKz*|V4KRaPL1G1k6)YCnEb6jCj}>_q!N0&F zkws+|RajBTz#HG0TdW|mLYWmRtRS<3%AyX7daU4NAP>YM$Wnk~xvWfw72G4#{;KrN zX(o5HJDltiN5}3GCr5VKBdP9g>Gz(4=y z@t#MV_gO)*wZJ=C5Vp6EHnbDs++zkJiId2#gYk0*2t_9lhrH7W{KSc{vmNhZP!a6w zCzm`@1%+G+Js8l*rJ~AZJa4cY2S41yT<(<> zFKFWmo4~~2>#zw-ybo-E{xRuvR=nSvz68f}q*lBnU%(02=I#?Ko_|{MUNPITB4R}m z=9)iu{+B11^W#=KV!abn>)qXIz_fT4*1O-Oc2BWL&dMfQ%h9Q2N2W#j=may*wus~k z)o1AU%9Qw1&B8WMmEHc}0MY+F{o4~W=}7;#9MJ!jGt>Y75C3vUuK)l5 diff --git a/.vscode/settings.json b/.vscode/settings.json index 32cfc61d..1c1b44db 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,5 +1,6 @@ { "files.watcherExclude": { "**/target": true - } + }, + "editor.formatOnSave": false } \ No newline at end of file From c0217c2a23ca74dd0ac38f2f02207fa0d535505d Mon Sep 17 00:00:00 2001 From: TheDrawingCoding-Gamer Date: Thu, 22 Jan 2026 10:12:01 -0500 Subject: [PATCH 95/95] Fix destruction order --- wayland/cc/Decoration.cc | 2 +- wayland/cc/WindowWayland.cc | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/wayland/cc/Decoration.cc b/wayland/cc/Decoration.cc index 9dd3affa..76e549dd 100644 --- a/wayland/cc/Decoration.cc +++ b/wayland/cc/Decoration.cc @@ -211,7 +211,7 @@ void Decoration::close() { _destroyDecorations(); if (_decoration) { // ??? - // zxdg_toplevel_decoration_v1_destroy(_decoration); + zxdg_toplevel_decoration_v1_destroy(_decoration); _decoration = nullptr; } xdg_toplevel_destroy(_xdgToplevel); diff --git a/wayland/cc/WindowWayland.cc b/wayland/cc/WindowWayland.cc index a65eddbe..ad4a2bd3 100644 --- a/wayland/cc/WindowWayland.cc +++ b/wayland/cc/WindowWayland.cc @@ -59,14 +59,14 @@ void WindowWayland::hide() { if (_layer) { _layer->detachBuffer(); } - if (_waylandWindow) { - wl_surface_destroy(_waylandWindow); - } - _waylandWindow = nullptr; if (_decoration) { _decoration->close(); } _decoration = nullptr; + if (_waylandWindow) { + wl_surface_destroy(_waylandWindow); + } + _waylandWindow = nullptr; _windowManager.unregisterWindow(this); } void WindowWayland::maximize() {