Squashed 'libraries/ZWidget/' changes from 924b8e0e78..b5ae8c8ab0 (#3357)
b5ae8c8ab0 Fix sdl2 initial paint a06bc7f8b4 Wayland Backend: Use unaccelerated dx and dy for OnMouseMoveRawEvent() f1789718d1 Wayland Backend: Implement clipboard support 11f3fe0e21 Fix a typo 2f4e3603e0 Add timers bcecfa9ee2 Fix filling with wrong window background sometimes 6681d2c2d8 Fix timer linked list 4564f7ea88 OnXInputEvent calls callbacks, which potentially could create/destroy windows 92f99a1abf Improve timer stability 4ccb1a03f8 Fix Cursor Shape protocol related crashes - If the protocol is available, avoid handling buffers directly fdefb2945d Add timers for X11 backend 25a2a2c846 WaylandDisplayBackend: New protocols and improvements - Add xdg_toplevel_icon and cursor_shape protocols support - Implement WaylandDisplayWindow::SetWindowIcon() based on the xdg_toplevel_icon protocol. d817b79b98 Fix compile error af5a87dba5 Add missing include statement 43996b7864 setIcon for x11 49ae157f31 Implement setIcon for Windows Fix spaces that should be tabs in the wayland backend a564526bb7 WaylandDisplayBackend: Combine LShift and RShift into Shift 46031df314 CMakeLists: Remove the unnecessary additional SDL2 check in the example section 5363f5354e Implement open folder dialog Improve support for paths with spaces in them bec2844e11 Fix the CI label for macOS e2c6af9e5d Seems CI was set to use a macOS version from 1865 991ca3377a Enable macOS in CI 1f5c95edef include SDL2 directory to fix mac compilation git-subtree-dir: libraries/ZWidget git-subtree-split: b5ae8c8ab038789298a8df1384322f0e69e57e5c
44
libraries/ZWidget/.github/workflows/build.yaml
vendored
|
|
@ -64,30 +64,30 @@ jobs:
|
|||
shell: bash
|
||||
run: cmake --build . --config $BUILD_TYPE
|
||||
|
||||
# macos-build:
|
||||
# runs-on: macos-10.15
|
||||
# name: 🍎 macOS 10.15
|
||||
# steps:
|
||||
macos-build:
|
||||
runs-on: macos-latest
|
||||
name: 🍎 macOS arm64
|
||||
steps:
|
||||
|
||||
# - name: 🧰 Checkout
|
||||
# uses: actions/checkout@v2
|
||||
# with:
|
||||
# fetch-depth: 0
|
||||
# submodules: true
|
||||
- name: 🧰 Checkout
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
submodules: true
|
||||
|
||||
# - name: ⬇️ Install dependencies
|
||||
# run: brew install sdl2 sdl2_mixer
|
||||
- name: ⬇️ Install dependencies
|
||||
run: brew install sdl2 sdl2_mixer
|
||||
|
||||
# - name: Create Build Environment
|
||||
# run: cmake -E make_directory ${{runner.workspace}}/build
|
||||
- name: Create Build Environment
|
||||
run: cmake -E make_directory ${{runner.workspace}}/build
|
||||
|
||||
# - name: Configure CMake
|
||||
# shell: bash
|
||||
# working-directory: ${{runner.workspace}}/build
|
||||
# run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE
|
||||
- name: Configure CMake
|
||||
shell: bash
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE
|
||||
|
||||
# - name: Build
|
||||
# working-directory: ${{runner.workspace}}/build
|
||||
# shell: bash
|
||||
# run: |
|
||||
# cmake --build . --config $BUILD_TYPE
|
||||
- name: Build
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
shell: bash
|
||||
run: |
|
||||
cmake --build . --config $BUILD_TYPE
|
||||
|
|
|
|||
|
|
@ -146,6 +146,8 @@ set(ZWIDGET_X11_SOURCES
|
|||
src/window/x11/x11_display_backend.h
|
||||
src/window/x11/x11_display_window.cpp
|
||||
src/window/x11/x11_display_window.h
|
||||
src/window/x11/x11_connection.cpp
|
||||
src/window/x11/x11_connection.h
|
||||
)
|
||||
|
||||
set(ZWIDGET_WAYLAND_SOURCES
|
||||
|
|
@ -155,6 +157,10 @@ set(ZWIDGET_WAYLAND_SOURCES
|
|||
src/window/wayland/wayland_display_window.h
|
||||
src/window/wayland/wl_fractional_scaling_protocol.cpp
|
||||
src/window/wayland/wl_fractional_scaling_protocol.hpp
|
||||
src/window/wayland/wl_cursor_shape.cpp
|
||||
src/window/wayland/wl_cursor_shape.hpp
|
||||
src/window/wayland/wl_xdg_toplevel_icon.cpp
|
||||
src/window/wayland/wl_xdg_toplevel_icon.hpp
|
||||
)
|
||||
|
||||
source_group("src" REGULAR_EXPRESSION "${CMAKE_CURRENT_SOURCE_DIR}/src/.+")
|
||||
|
|
@ -273,7 +279,9 @@ target_link_options(zwidget PRIVATE ${ZWIDGET_LINK_OPTIONS})
|
|||
target_link_libraries(zwidget ${ZWIDGET_LIBS})
|
||||
set_target_properties(zwidget PROPERTIES CXX_STANDARD 20)
|
||||
target_compile_options(zwidget PRIVATE ${CXX_WARNING_FLAGS})
|
||||
target_include_directories(zwidget PRIVATE ${SDL2_INCLUDE_DIRS})
|
||||
if(SDL2_FOUND)
|
||||
target_include_directories(zwidget PRIVATE ${SDL2_INCLUDE_DIRS})
|
||||
endif()
|
||||
|
||||
if(MSVC)
|
||||
set_property(TARGET zwidget PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
|
||||
|
|
@ -282,10 +290,6 @@ endif()
|
|||
option(ZWIDGET_BUILD_EXAMPLE "Build the zwidget example application" ON)
|
||||
|
||||
if(ZWIDGET_BUILD_EXAMPLE)
|
||||
if (UNIX AND NOT APPLE)
|
||||
find_package(SDL2 REQUIRED)
|
||||
endif()
|
||||
|
||||
add_executable(zwidget_example WIN32
|
||||
example/example.cpp
|
||||
example/picopng.cpp
|
||||
|
|
|
|||
|
|
@ -86,6 +86,22 @@ LauncherWindow::LauncherWindow(): Widget(nullptr, WidgetType::Window)
|
|||
{
|
||||
SetWindowTitle("ZWidget Demo");
|
||||
|
||||
try
|
||||
{
|
||||
SetWindowIcon({
|
||||
Image::LoadResource("surreal-engine-icon-16.png"),
|
||||
Image::LoadResource("surreal-engine-icon-24.png"),
|
||||
Image::LoadResource("surreal-engine-icon-32.png"),
|
||||
Image::LoadResource("surreal-engine-icon-48.png"),
|
||||
Image::LoadResource("surreal-engine-icon-64.png"),
|
||||
Image::LoadResource("surreal-engine-icon-128.png"),
|
||||
Image::LoadResource("surreal-engine-icon-256.png")
|
||||
});
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
}
|
||||
|
||||
Logo = new ImageBox(this);
|
||||
ExitButton = new PushButton(this);
|
||||
Pages = new TabWidget(this);
|
||||
|
|
@ -298,18 +314,17 @@ std::vector<uint8_t> LoadWidgetData(const std::string& name)
|
|||
return ReadAllBytes(name);
|
||||
}
|
||||
|
||||
enum class Backend {
|
||||
enum class Backend
|
||||
{
|
||||
Default, Win32, SDL2, X11, Wayland
|
||||
};
|
||||
|
||||
enum class Theme {
|
||||
enum class Theme
|
||||
{
|
||||
Default, Light, Dark
|
||||
};
|
||||
|
||||
int example(
|
||||
Backend backend = Backend::Default,
|
||||
Theme theme = Theme::Default
|
||||
)
|
||||
int example(Backend backend = Backend::Default, Theme theme = Theme::Default)
|
||||
{
|
||||
// just for testing themes
|
||||
switch (theme)
|
||||
|
|
@ -344,6 +359,8 @@ int example(
|
|||
|
||||
#ifdef WIN32
|
||||
|
||||
#define WIN32_MEAN_AND_LEAN
|
||||
#define NOMINMAX
|
||||
#include <Windows.h>
|
||||
#include <stdexcept>
|
||||
|
||||
|
|
@ -395,6 +412,7 @@ static std::vector<uint8_t> ReadAllBytes(const std::string& filename)
|
|||
|
||||
int APIENTRY WinMain(HINSTANCE hInst, HINSTANCE hInstPrev, PSTR cmdline, int cmdshow)
|
||||
{
|
||||
SetProcessDPIAware();
|
||||
example();
|
||||
}
|
||||
|
||||
|
|
|
|||
BIN
libraries/ZWidget/example/surreal-engine-icon-128.png
Normal file
|
After Width: | Height: | Size: 26 KiB |
BIN
libraries/ZWidget/example/surreal-engine-icon-16.png
Normal file
|
After Width: | Height: | Size: 896 B |
BIN
libraries/ZWidget/example/surreal-engine-icon-24.png
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
BIN
libraries/ZWidget/example/surreal-engine-icon-256.png
Normal file
|
After Width: | Height: | Size: 91 KiB |
BIN
libraries/ZWidget/example/surreal-engine-icon-32.png
Normal file
|
After Width: | Height: | Size: 2.5 KiB |
BIN
libraries/ZWidget/example/surreal-engine-icon-48.png
Normal file
|
After Width: | Height: | Size: 4.8 KiB |
BIN
libraries/ZWidget/example/surreal-engine-icon-64.png
Normal file
|
After Width: | Height: | Size: 7.7 KiB |
|
|
@ -40,8 +40,8 @@ public:
|
|||
std::string GetWindowTitle() const;
|
||||
void SetWindowTitle(const std::string& text);
|
||||
|
||||
// Icon GetWindowIcon() const;
|
||||
// void SetWindowIcon(const Icon& icon);
|
||||
std::vector<std::shared_ptr<Image>> GetWindowIcon() const;
|
||||
void SetWindowIcon(const std::vector<std::shared_ptr<Image>>& images);
|
||||
|
||||
// Widget content box
|
||||
Size GetSize() const;
|
||||
|
|
@ -233,6 +233,7 @@ private:
|
|||
Colorf WindowBackground = Colorf::fromRgba8(240, 240, 240);
|
||||
|
||||
std::string WindowTitle;
|
||||
std::vector<std::shared_ptr<Image>> WindowIcon;
|
||||
std::unique_ptr<DisplayWindow> DispWindow;
|
||||
std::unique_ptr<Canvas> DispCanvas;
|
||||
Widget* FocusWidget = nullptr;
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ class Widget;
|
|||
class OpenFileDialog;
|
||||
class SaveFileDialog;
|
||||
class OpenFolderDialog;
|
||||
class Image;
|
||||
|
||||
enum class StandardCursor
|
||||
{
|
||||
|
|
@ -163,6 +164,7 @@ public:
|
|||
virtual ~DisplayWindow() = default;
|
||||
|
||||
virtual void SetWindowTitle(const std::string& text) = 0;
|
||||
virtual void SetWindowIcon(const std::vector<std::shared_ptr<Image>>& images) = 0;
|
||||
virtual void SetWindowFrame(const Rect& box) = 0;
|
||||
virtual void SetClientFrame(const Rect& box) = 0;
|
||||
virtual void Show() = 0;
|
||||
|
|
|
|||
|
|
@ -5,9 +5,9 @@
|
|||
|
||||
Timer::Timer(Widget* owner) : OwnerObj(owner)
|
||||
{
|
||||
PrevTimerObj = owner->FirstTimerObj;
|
||||
if (PrevTimerObj)
|
||||
PrevTimerObj->PrevTimerObj = this;
|
||||
NextTimerObj = owner->FirstTimerObj;
|
||||
if (NextTimerObj)
|
||||
NextTimerObj->PrevTimerObj = this;
|
||||
owner->FirstTimerObj = this;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -173,6 +173,18 @@ void Widget::SetWindowTitle(const std::string& text)
|
|||
}
|
||||
}
|
||||
|
||||
std::vector<std::shared_ptr<Image>> Widget::GetWindowIcon() const
|
||||
{
|
||||
return WindowIcon;
|
||||
}
|
||||
|
||||
void Widget::SetWindowIcon(const std::vector<std::shared_ptr<Image>>& images)
|
||||
{
|
||||
WindowIcon = images;
|
||||
if (DispWindow)
|
||||
DispWindow->SetWindowIcon(WindowIcon);
|
||||
}
|
||||
|
||||
Size Widget::GetSize() const
|
||||
{
|
||||
return ContentGeometry.size();
|
||||
|
|
@ -365,7 +377,7 @@ void Widget::Repaint()
|
|||
Widget* w = Window();
|
||||
if (w->DispCanvas)
|
||||
{
|
||||
w->DispCanvas->begin(WindowBackground);
|
||||
w->DispCanvas->begin(w->WindowBackground);
|
||||
w->Paint(w->DispCanvas.get());
|
||||
w->DispCanvas->end();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -281,8 +281,30 @@ bool DBusOpenFileDialog::Show()
|
|||
for (const std::string& uri : uris)
|
||||
{
|
||||
if (uri.size() > 7 && uri.substr(0, 7) == "file://")
|
||||
outputFilenames.push_back(uri.substr(7));
|
||||
outputFilenames.push_back(unescapeUri(uri.substr(7)));
|
||||
}
|
||||
|
||||
return !uris.empty();
|
||||
}
|
||||
|
||||
std::string DBusOpenFileDialog::unescapeUri(const std::string& s)
|
||||
{
|
||||
// This needs to be much more advanced to support any kind of uri. Why did they have to pass it as an uri!? idiots.
|
||||
std::string result;
|
||||
result.reserve(s.size());
|
||||
size_t i = 0;
|
||||
while (i < s.length())
|
||||
{
|
||||
if (s[i] == '%' && i + 2 < s.length() && s[i + 1] == '2' && s[i + 2] == '0')
|
||||
{
|
||||
result += ' ';
|
||||
i += 3;
|
||||
}
|
||||
else
|
||||
{
|
||||
result += s[i];
|
||||
i++;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,6 +20,8 @@ public:
|
|||
bool Show() override;
|
||||
|
||||
private:
|
||||
static std::string unescapeUri(const std::string& s);
|
||||
|
||||
std::string ownerHandle;
|
||||
std::string title;
|
||||
std::string initialDirectory;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,8 @@
|
|||
|
||||
#include "dbus_open_folder_dialog.h"
|
||||
#include <dbus/dbus.h>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
|
||||
DBusOpenFolderDialog::DBusOpenFolderDialog(std::string ownerHandle) : ownerHandle(ownerHandle)
|
||||
{
|
||||
|
|
@ -7,20 +10,271 @@ DBusOpenFolderDialog::DBusOpenFolderDialog(std::string ownerHandle) : ownerHandl
|
|||
|
||||
bool DBusOpenFolderDialog::Show()
|
||||
{
|
||||
return false;
|
||||
// https://flatpak.github.io/xdg-desktop-portal/docs/doc-org.freedesktop.portal.FileChooser.html
|
||||
|
||||
dbus_bool_t bresult = {};
|
||||
DBusError error = {};
|
||||
dbus_error_init(&error);
|
||||
|
||||
DBusConnection* connection = dbus_bus_get(DBUS_BUS_SESSION, &error);
|
||||
if (!connection)
|
||||
{
|
||||
dbus_error_free(&error);
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string busname = dbus_bus_get_unique_name(connection);
|
||||
|
||||
DBusMessage* request = dbus_message_new_method_call("org.freedesktop.portal.Desktop", "/org/freedesktop/portal/desktop", "org.freedesktop.portal.FileChooser", "OpenFile");
|
||||
if (!request)
|
||||
{
|
||||
dbus_connection_unref(connection);
|
||||
dbus_error_free(&error);
|
||||
return false;
|
||||
}
|
||||
|
||||
const char* parentWindow = ownerHandle.c_str();
|
||||
const char* title = this->title.c_str();
|
||||
|
||||
DBusMessageIter requestArgs = {};
|
||||
dbus_message_iter_init_append(request, &requestArgs);
|
||||
|
||||
bresult = dbus_message_iter_append_basic(&requestArgs, DBUS_TYPE_STRING, &parentWindow);
|
||||
bresult = dbus_message_iter_append_basic(&requestArgs, DBUS_TYPE_STRING, &title);
|
||||
|
||||
DBusMessageIter requestOptions = {};
|
||||
bresult = dbus_message_iter_open_container(&requestArgs, DBUS_TYPE_ARRAY, "{sv}", &requestOptions);
|
||||
|
||||
// handle_token - s race condition prevention for signal (/org/freedesktop/portal/desktop/request/SENDER/TOKEN)
|
||||
// accept_label - s text label for the OK button
|
||||
// modal - b makes dialog modal. Defaults to true. Not really sure what it means since nothing stayed modal on my computer
|
||||
// directory - b open folder mode, added in version 3
|
||||
// filters - a(sa(us)) [('Images', [(0, '*.ico'), (1, 'image/png')]), ('Text', [(0, '*.txt')])]
|
||||
// current_filter - sa(us) filter from filters that should be current filter
|
||||
// choices - a(ssa(ss)s) list of serialized combo boxes to add to the file chooser
|
||||
// current_name - s suggested name
|
||||
|
||||
// current_folder - ay
|
||||
if (!initialDirectory.empty())
|
||||
{
|
||||
// Note: the docs unfortunately says "The portal implementation is free to ignore this option"
|
||||
|
||||
const char* key = "current_folder";
|
||||
const char* value = initialDirectory.c_str();
|
||||
int valueCount = (int)initialDirectory.size() + 1;
|
||||
|
||||
DBusMessageIter entry = {};
|
||||
bresult = dbus_message_iter_open_container(&requestOptions, DBUS_TYPE_DICT_ENTRY, nullptr, &entry);
|
||||
bresult = dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key);
|
||||
|
||||
DBusMessageIter variant = {};
|
||||
DBusMessageIter array = {};
|
||||
bresult = dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT, "ay", &variant);
|
||||
bresult = dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE_AS_STRING, &array);
|
||||
bresult = dbus_message_iter_append_fixed_array(&array, DBUS_TYPE_BYTE, &value, valueCount);
|
||||
bresult = dbus_message_iter_close_container(&variant, &array);
|
||||
bresult = dbus_message_iter_close_container(&entry, &variant);
|
||||
|
||||
bresult = dbus_message_iter_close_container(&requestOptions, &entry);
|
||||
}
|
||||
|
||||
// directory - b
|
||||
{
|
||||
const char* key = "directory";
|
||||
dbus_bool_t value = true;
|
||||
|
||||
DBusMessageIter entry = {};
|
||||
bresult = dbus_message_iter_open_container(&requestOptions, DBUS_TYPE_DICT_ENTRY, nullptr, &entry);
|
||||
bresult = dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key);
|
||||
|
||||
DBusMessageIter variant = {};
|
||||
bresult = dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT, DBUS_TYPE_BOOLEAN_AS_STRING, &variant);
|
||||
bresult = dbus_message_iter_append_basic(&variant, DBUS_TYPE_BOOLEAN, &value);
|
||||
bresult = dbus_message_iter_close_container(&entry, &variant);
|
||||
|
||||
bresult = dbus_message_iter_close_container(&requestOptions, &entry);
|
||||
}
|
||||
|
||||
bresult = dbus_message_iter_close_container(&requestArgs, &requestOptions);
|
||||
|
||||
DBusMessage* response = dbus_connection_send_with_reply_and_block(connection, request, DBUS_TIMEOUT_USE_DEFAULT, &error);
|
||||
if (!response)
|
||||
{
|
||||
dbus_message_unref(request);
|
||||
dbus_connection_unref(connection);
|
||||
dbus_error_free(&error);
|
||||
return false;
|
||||
}
|
||||
|
||||
const char* handle = nullptr;
|
||||
bresult = dbus_message_get_args(response, &error, DBUS_TYPE_OBJECT_PATH, &handle, DBUS_TYPE_INVALID);
|
||||
if (!bresult)
|
||||
{
|
||||
dbus_message_unref(response);
|
||||
dbus_message_unref(request);
|
||||
dbus_connection_unref(connection);
|
||||
dbus_error_free(&error);
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string signalObjectPath = handle;
|
||||
|
||||
dbus_message_unref(response);
|
||||
dbus_message_unref(request);
|
||||
|
||||
std::string rule = "type='signal',interface='org.freedesktop.portal.Request',member='Response',path='" + signalObjectPath + "'";
|
||||
dbus_bus_add_match(connection, rule.c_str(), &error);
|
||||
|
||||
// Wait for the response signal
|
||||
//
|
||||
// To do: process the run loop while we wait
|
||||
//
|
||||
DBusMessage* signalmsg = nullptr;
|
||||
while (!signalmsg && dbus_connection_read_write(connection, -1))
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
DBusMessage* message = dbus_connection_pop_message(connection);
|
||||
if (!message)
|
||||
break;
|
||||
|
||||
if (dbus_message_is_signal(message, "org.freedesktop.portal.Request", "Response") && dbus_message_get_path(message) == signalObjectPath)
|
||||
{
|
||||
signalmsg = message;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
dbus_message_unref(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
dbus_bus_remove_match(connection, rule.c_str(), &error);
|
||||
|
||||
// Read the response
|
||||
|
||||
dbus_uint32_t responseCode = 0;
|
||||
std::vector<std::string> uris;
|
||||
|
||||
DBusMessageIter signalArgs;
|
||||
bresult = dbus_message_iter_init(signalmsg, &signalArgs);
|
||||
|
||||
// response code - u
|
||||
if (dbus_message_iter_get_arg_type(&signalArgs) == DBUS_TYPE_UINT32)
|
||||
{
|
||||
dbus_message_iter_get_basic(&signalArgs, &responseCode);
|
||||
}
|
||||
dbus_message_iter_next(&signalArgs);
|
||||
|
||||
// results - a{sv}
|
||||
if (dbus_message_iter_get_arg_type(&signalArgs) == DBUS_TYPE_ARRAY)
|
||||
{
|
||||
DBusMessageIter resultsArray = {};
|
||||
dbus_message_iter_recurse(&signalArgs, &resultsArray);
|
||||
while (true)
|
||||
{
|
||||
int type = dbus_message_iter_get_arg_type(&resultsArray);
|
||||
if (type != DBUS_TYPE_DICT_ENTRY)
|
||||
break;
|
||||
|
||||
DBusMessageIter entry = {};
|
||||
dbus_message_iter_recurse(&resultsArray, &entry);
|
||||
|
||||
const char* key = nullptr;
|
||||
dbus_message_iter_get_basic(&entry, &key);
|
||||
dbus_message_iter_next(&entry);
|
||||
|
||||
DBusMessageIter value = {};
|
||||
dbus_message_iter_recurse(&entry, &value);
|
||||
|
||||
std::string k = key;
|
||||
if (k == "uris" && dbus_message_iter_get_arg_type(&value) == DBUS_TYPE_ARRAY) // as
|
||||
{
|
||||
DBusMessageIter uriArray = {};
|
||||
dbus_message_iter_recurse(&value, &uriArray);
|
||||
while (true)
|
||||
{
|
||||
int type = dbus_message_iter_get_arg_type(&uriArray);
|
||||
if (type != DBUS_TYPE_STRING)
|
||||
break;
|
||||
|
||||
const char* uri = nullptr;
|
||||
dbus_message_iter_get_basic(&uriArray, &uri);
|
||||
uris.push_back(uri);
|
||||
|
||||
dbus_message_iter_next(&uriArray);
|
||||
}
|
||||
}
|
||||
else if (k == "choices" && dbus_message_iter_get_arg_type(&value) == DBUS_TYPE_ARRAY) // a(ss)
|
||||
{
|
||||
// An array of pairs of strings,
|
||||
// the first string being the ID of a combobox that was passed into this call,
|
||||
// the second string being the selected option.
|
||||
}
|
||||
else if (k == "current_filter" && dbus_message_iter_get_arg_type(&value) == DBUS_TYPE_STRUCT) // sa(us)
|
||||
{
|
||||
// The filter that was selected.
|
||||
// This may match a filter in the filter list or another filter that was applied unconditionally.
|
||||
}
|
||||
|
||||
dbus_message_iter_next(&entry);
|
||||
dbus_message_iter_next(&resultsArray);
|
||||
}
|
||||
}
|
||||
dbus_message_iter_next(&signalArgs);
|
||||
|
||||
dbus_message_unref(signalmsg);
|
||||
dbus_connection_unref(connection);
|
||||
dbus_error_free(&error);
|
||||
|
||||
if (responseCode != 0) // User cancelled
|
||||
return false;
|
||||
|
||||
for (const std::string& uri : uris)
|
||||
{
|
||||
if (uri.size() > 7 && uri.substr(0, 7) == "file://")
|
||||
{
|
||||
selectedPath = unescapeUri(uri.substr(7));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return !selectedPath.empty();
|
||||
}
|
||||
|
||||
std::string DBusOpenFolderDialog::SelectedPath() const
|
||||
{
|
||||
return selected_path;
|
||||
return selectedPath;
|
||||
}
|
||||
|
||||
void DBusOpenFolderDialog::SetInitialDirectory(const std::string& path)
|
||||
{
|
||||
initial_directory = path;
|
||||
initialDirectory = path;
|
||||
}
|
||||
|
||||
void DBusOpenFolderDialog::SetTitle(const std::string& newtitle)
|
||||
{
|
||||
title = newtitle;
|
||||
}
|
||||
|
||||
std::string DBusOpenFolderDialog::unescapeUri(const std::string& s)
|
||||
{
|
||||
// This needs to be much more advanced to support any kind of uri. Why did they have to pass it as an uri!? idiots.
|
||||
std::string result;
|
||||
result.reserve(s.size());
|
||||
size_t i = 0;
|
||||
while (i < s.length())
|
||||
{
|
||||
if (s[i] == '%' && i + 2 < s.length() && s[i + 1] == '2' && s[i + 2] == '0')
|
||||
{
|
||||
result += ' ';
|
||||
i += 3;
|
||||
}
|
||||
else
|
||||
{
|
||||
result += s[i];
|
||||
i++;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,9 +13,10 @@ public:
|
|||
void SetTitle(const std::string& newtitle) override;
|
||||
|
||||
private:
|
||||
std::string ownerHandle;
|
||||
static std::string unescapeUri(const std::string& s);
|
||||
|
||||
std::string selected_path;
|
||||
std::string initial_directory;
|
||||
std::string ownerHandle;
|
||||
std::string title;
|
||||
std::string initialDirectory;
|
||||
std::string selectedPath;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
#include "sdl2_display_window.h"
|
||||
#include <stdexcept>
|
||||
#include <SDL2/SDL_vulkan.h>
|
||||
|
|
@ -33,7 +32,7 @@ SDL2DisplayWindow::SDL2DisplayWindow(DisplayWindowHost* windowHost, bool popupWi
|
|||
if (result != 0)
|
||||
throw std::runtime_error(std::string("Unable to create SDL window:") + SDL_GetError());
|
||||
}
|
||||
|
||||
|
||||
WindowList[SDL_GetWindowID(Handle.window)] = this;
|
||||
}
|
||||
|
||||
|
|
@ -83,6 +82,12 @@ void SDL2DisplayWindow::SetWindowTitle(const std::string& text)
|
|||
SDL_SetWindowTitle(Handle.window, text.c_str());
|
||||
}
|
||||
|
||||
void SDL2DisplayWindow::SetWindowIcon(const std::vector<std::shared_ptr<Image>>& images)
|
||||
{
|
||||
// To do: call SDL_SetWindowIcon for the highest resolution image (or maybe the first 32x32 or higher? not sure what is best here)
|
||||
// To do: when this is upgraded to SDL3, call SDL_AddSurfaceAlternateImage for all the images
|
||||
}
|
||||
|
||||
void SDL2DisplayWindow::SetWindowFrame(const Rect& box)
|
||||
{
|
||||
// SDL2 doesn't really seem to have an API for this.
|
||||
|
|
@ -107,14 +112,12 @@ void SDL2DisplayWindow::SetClientFrame(const Rect& box)
|
|||
void SDL2DisplayWindow::Show()
|
||||
{
|
||||
SDL_ShowWindow(Handle.window);
|
||||
WindowHost->OnWindowPaint();
|
||||
}
|
||||
|
||||
void SDL2DisplayWindow::ShowFullscreen()
|
||||
{
|
||||
SDL_ShowWindow(Handle.window);
|
||||
SDL_SetWindowFullscreen(Handle.window, SDL_WINDOW_FULLSCREEN_DESKTOP);
|
||||
WindowHost->OnWindowPaint();
|
||||
isFullscreen = true;
|
||||
}
|
||||
|
||||
|
|
@ -122,14 +125,12 @@ void SDL2DisplayWindow::ShowMaximized()
|
|||
{
|
||||
SDL_ShowWindow(Handle.window);
|
||||
SDL_MaximizeWindow(Handle.window);
|
||||
WindowHost->OnWindowPaint();
|
||||
}
|
||||
|
||||
void SDL2DisplayWindow::ShowMinimized()
|
||||
{
|
||||
SDL_ShowWindow(Handle.window);
|
||||
SDL_MinimizeWindow(Handle.window);
|
||||
WindowHost->OnWindowPaint();
|
||||
}
|
||||
|
||||
void SDL2DisplayWindow::ShowNormal()
|
||||
|
|
@ -137,7 +138,6 @@ void SDL2DisplayWindow::ShowNormal()
|
|||
SDL_ShowWindow(Handle.window);
|
||||
SDL_SetWindowFullscreen(Handle.window, 0);
|
||||
isFullscreen = false;
|
||||
WindowHost->OnWindowPaint();
|
||||
}
|
||||
|
||||
bool SDL2DisplayWindow::IsWindowFullscreen()
|
||||
|
|
@ -427,14 +427,14 @@ SDL2DisplayWindow* SDL2DisplayWindow::FindEventWindow(const SDL_Event& event)
|
|||
int windowID;
|
||||
switch (event.type)
|
||||
{
|
||||
case SDL_WINDOWEVENT: windowID = event.window.windowID; break;
|
||||
case SDL_TEXTINPUT: windowID = event.text.windowID; break;
|
||||
case SDL_KEYUP: windowID = event.key.windowID; break;
|
||||
case SDL_KEYDOWN: windowID = event.key.windowID; break;
|
||||
case SDL_MOUSEBUTTONUP: windowID = event.button.windowID; break;
|
||||
case SDL_WINDOWEVENT: windowID = event.window.windowID; break;
|
||||
case SDL_TEXTINPUT: windowID = event.text.windowID; break;
|
||||
case SDL_KEYUP: windowID = event.key.windowID; break;
|
||||
case SDL_KEYDOWN: windowID = event.key.windowID; break;
|
||||
case SDL_MOUSEBUTTONUP: windowID = event.button.windowID; break;
|
||||
case SDL_MOUSEBUTTONDOWN: windowID = event.button.windowID; break;
|
||||
case SDL_MOUSEWHEEL: windowID = event.wheel.windowID; break;
|
||||
case SDL_MOUSEMOTION: windowID = event.motion.windowID; break;
|
||||
case SDL_MOUSEWHEEL: windowID = event.wheel.windowID; break;
|
||||
case SDL_MOUSEMOTION: windowID = event.motion.windowID; break;
|
||||
default:
|
||||
if (event.type == PaintEventNumber) windowID = event.user.windowID;
|
||||
else return nullptr;
|
||||
|
|
@ -455,14 +455,14 @@ void SDL2DisplayWindow::DispatchEvent(const SDL_Event& event)
|
|||
|
||||
switch (event.type)
|
||||
{
|
||||
case SDL_WINDOWEVENT: window->OnWindowEvent(event.window); break;
|
||||
case SDL_TEXTINPUT: window->OnTextInput(event.text); break;
|
||||
case SDL_KEYUP: window->OnKeyUp(event.key); break;
|
||||
case SDL_KEYDOWN: window->OnKeyDown(event.key); break;
|
||||
case SDL_MOUSEBUTTONUP: window->OnMouseButtonUp(event.button); break;
|
||||
case SDL_WINDOWEVENT: window->OnWindowEvent (event.window); break;
|
||||
case SDL_TEXTINPUT: window->OnTextInput (event.text); break;
|
||||
case SDL_KEYUP: window->OnKeyUp (event.key); break;
|
||||
case SDL_KEYDOWN: window->OnKeyDown (event.key); break;
|
||||
case SDL_MOUSEBUTTONUP: window->OnMouseButtonUp (event.button); break;
|
||||
case SDL_MOUSEBUTTONDOWN: window->OnMouseButtonDown(event.button); break;
|
||||
case SDL_MOUSEWHEEL: window->OnMouseWheel(event.wheel); break;
|
||||
case SDL_MOUSEMOTION: window->OnMouseMotion(event.motion); break;
|
||||
case SDL_MOUSEWHEEL: window->OnMouseWheel (event.wheel); break;
|
||||
case SDL_MOUSEMOTION: window->OnMouseMotion (event.motion); break;
|
||||
default:
|
||||
if (event.type == PaintEventNumber) window->OnPaintEvent();
|
||||
}
|
||||
|
|
@ -472,13 +472,39 @@ void SDL2DisplayWindow::OnWindowEvent(const SDL_WindowEvent& event)
|
|||
{
|
||||
switch (event.event)
|
||||
{
|
||||
case SDL_WINDOWEVENT_CLOSE: WindowHost->OnWindowClose(); break;
|
||||
case SDL_WINDOWEVENT_MOVED: WindowHost->OnWindowGeometryChanged(); break;
|
||||
case SDL_WINDOWEVENT_RESIZED: WindowHost->OnWindowGeometryChanged(); WindowHost->OnWindowPaint(); break;
|
||||
case SDL_WINDOWEVENT_SHOWN: WindowHost->OnWindowPaint(); break;
|
||||
case SDL_WINDOWEVENT_EXPOSED: WindowHost->OnWindowPaint(); break;
|
||||
case SDL_WINDOWEVENT_FOCUS_GAINED: WindowHost->OnWindowActivated(); break;
|
||||
case SDL_WINDOWEVENT_FOCUS_LOST: WindowHost->OnWindowDeactivated(); break;
|
||||
case SDL_WINDOWEVENT_CLOSE:
|
||||
WindowHost->OnWindowClose();
|
||||
break;
|
||||
|
||||
case SDL_WINDOWEVENT_MOVED:
|
||||
case SDL_WINDOWEVENT_SIZE_CHANGED:
|
||||
case SDL_WINDOWEVENT_RESIZED:
|
||||
WindowHost->OnWindowGeometryChanged();
|
||||
break;
|
||||
|
||||
case SDL_WINDOWEVENT_SHOWN:
|
||||
case SDL_WINDOWEVENT_EXPOSED:
|
||||
WindowHost->OnWindowPaint();
|
||||
break;
|
||||
|
||||
case SDL_WINDOWEVENT_FOCUS_GAINED:
|
||||
case SDL_WINDOWEVENT_FOCUS_LOST:
|
||||
WindowHost->OnWindowDeactivated();
|
||||
break;
|
||||
|
||||
case SDL_WINDOWEVENT_NONE:
|
||||
case SDL_WINDOWEVENT_HIDDEN:
|
||||
case SDL_WINDOWEVENT_MINIMIZED:
|
||||
case SDL_WINDOWEVENT_MAXIMIZED:
|
||||
case SDL_WINDOWEVENT_RESTORED:
|
||||
case SDL_WINDOWEVENT_ENTER:
|
||||
case SDL_WINDOWEVENT_LEAVE:
|
||||
case SDL_WINDOWEVENT_TAKE_FOCUS:
|
||||
case SDL_WINDOWEVENT_HIT_TEST:
|
||||
case SDL_WINDOWEVENT_ICCPROF_CHANGED:
|
||||
case SDL_WINDOWEVENT_DISPLAY_CHANGED:
|
||||
// nope
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -792,5 +818,5 @@ SDL_Scancode SDL2DisplayWindow::InputKeyToScancode(InputKey inputkey)
|
|||
case InputKey::RControl: return SDL_SCANCODE_RCTRL;
|
||||
case InputKey::Tilde: return SDL_SCANCODE_GRAVE;
|
||||
default: return (SDL_Scancode)0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ public:
|
|||
~SDL2DisplayWindow();
|
||||
|
||||
void SetWindowTitle(const std::string& text) override;
|
||||
void SetWindowIcon(const std::vector<std::shared_ptr<Image>>& images) override;
|
||||
void SetWindowFrame(const Rect& box) override;
|
||||
void SetClientFrame(const Rect& box) override;
|
||||
void Show() override;
|
||||
|
|
|
|||
|
|
@ -9,49 +9,69 @@
|
|||
#include <wayland-client-protocol-unstable.hpp>
|
||||
#include <wayland-cursor.hpp>
|
||||
#include "wl_fractional_scaling_protocol.hpp"
|
||||
#include "wl_xdg_toplevel_icon.hpp"
|
||||
#include <linux/input.h>
|
||||
#include <poll.h>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <xkbcommon/xkbcommon.h>
|
||||
|
||||
static short poll_single(int fd, short events, int timeout) {
|
||||
pollfd pfd { .fd = fd, .events = events, .revents = 0 };
|
||||
if (0 > poll(&pfd, 1, timeout)) {
|
||||
throw std::runtime_error("poll() failed");
|
||||
}
|
||||
#include "wl_cursor_shape.hpp"
|
||||
|
||||
return pfd.revents;
|
||||
inline auto hash_proxy = [](wayland::proxy_t const& proxy) { return reinterpret_cast<size_t>(proxy.c_ptr()); };
|
||||
using hash_proxy_t = decltype(hash_proxy);
|
||||
|
||||
static short poll_single(int fd, short events, int timeout)
|
||||
{
|
||||
pollfd pfd { .fd = fd, .events = events, .revents = 0 };
|
||||
if (0 > poll(&pfd, 1, timeout))
|
||||
{
|
||||
throw std::runtime_error("poll() failed");
|
||||
}
|
||||
|
||||
return pfd.revents;
|
||||
}
|
||||
|
||||
enum pointer_event_mask {
|
||||
POINTER_EVENT_ENTER = 1 << 0,
|
||||
POINTER_EVENT_LEAVE = 1 << 1,
|
||||
POINTER_EVENT_MOTION = 1 << 2,
|
||||
POINTER_EVENT_BUTTON = 1 << 3,
|
||||
POINTER_EVENT_AXIS = 1 << 4,
|
||||
POINTER_EVENT_AXIS_SOURCE = 1 << 5,
|
||||
POINTER_EVENT_AXIS_STOP = 1 << 6,
|
||||
POINTER_EVENT_AXIS_DISCRETE = 1 << 7,
|
||||
POINTER_EVENT_AXIS_120 = 1 << 8,
|
||||
POINTER_EVENT_RELATIVE_MOTION = 1 << 9,
|
||||
enum pointer_event_mask
|
||||
{
|
||||
POINTER_EVENT_ENTER = 1 << 0,
|
||||
POINTER_EVENT_LEAVE = 1 << 1,
|
||||
POINTER_EVENT_MOTION = 1 << 2,
|
||||
POINTER_EVENT_BUTTON = 1 << 3,
|
||||
POINTER_EVENT_AXIS = 1 << 4,
|
||||
POINTER_EVENT_AXIS_SOURCE = 1 << 5,
|
||||
POINTER_EVENT_AXIS_STOP = 1 << 6,
|
||||
POINTER_EVENT_AXIS_DISCRETE = 1 << 7,
|
||||
POINTER_EVENT_AXIS_120 = 1 << 8,
|
||||
POINTER_EVENT_RELATIVE_MOTION = 1 << 9,
|
||||
};
|
||||
|
||||
struct WaylandPointerEvent
|
||||
{
|
||||
uint32_t event_mask;
|
||||
double surfaceX, surfaceY;
|
||||
double dx, dy;
|
||||
uint32_t button;
|
||||
wayland::pointer_button_state state;
|
||||
uint32_t time;
|
||||
uint32_t serial;
|
||||
struct {
|
||||
bool valid;
|
||||
double value;
|
||||
int32_t discrete;
|
||||
int32_t value120;
|
||||
} axes[2];
|
||||
wayland::pointer_axis_source axis_source;
|
||||
uint32_t event_mask;
|
||||
double surfaceX, surfaceY;
|
||||
double dx, dy, dx_unaccel, dy_unaccel;
|
||||
uint32_t button;
|
||||
wayland::pointer_button_state state;
|
||||
uint32_t time;
|
||||
uint32_t serial;
|
||||
struct {
|
||||
bool valid;
|
||||
double value;
|
||||
int32_t discrete;
|
||||
int32_t value120;
|
||||
} axes[2];
|
||||
wayland::pointer_axis_source axis_source;
|
||||
};
|
||||
|
||||
class WaylandTimer
|
||||
{
|
||||
public:
|
||||
WaylandTimer(int timeoutMilliseconds, std::function<void()> onTimer, int64_t nextTime) : timeoutMilliseconds(timeoutMilliseconds), onTimer(onTimer), nextTime(nextTime) {}
|
||||
|
||||
int timeoutMilliseconds = 0;
|
||||
std::function<void()> onTimer;
|
||||
int64_t nextTime = 0;
|
||||
};
|
||||
|
||||
class WaylandDisplayWindow;
|
||||
|
|
@ -60,7 +80,7 @@ class WaylandDisplayBackend : public DisplayBackend
|
|||
{
|
||||
public:
|
||||
WaylandDisplayBackend();
|
||||
~WaylandDisplayBackend();
|
||||
~WaylandDisplayBackend();
|
||||
|
||||
std::unique_ptr<DisplayWindow> Create(DisplayWindowHost* windowHost, bool popupWindow, DisplayWindow* owner, RenderAPI renderAPI) override;
|
||||
void ProcessEvents() override;
|
||||
|
|
@ -78,9 +98,13 @@ public:
|
|||
void OnWindowDestroyed(WaylandDisplayWindow* window);
|
||||
|
||||
void SetCursor(StandardCursor cursor);
|
||||
void ShowCursor(bool enable);
|
||||
void ShowCursor(bool enable);
|
||||
bool GetKeyState(InputKey key);
|
||||
|
||||
std::string GetClipboardText() { return m_ClipboardContents; }
|
||||
wayland::data_device_t& GetDataDevice() { return m_DataDevice; }
|
||||
uint32_t GetKeyboardSerial() const { return m_KeyboardSerial; }
|
||||
|
||||
#ifdef USE_DBUS
|
||||
std::unique_ptr<OpenFileDialog> CreateOpenFileDialog(DisplayWindow* owner) override;
|
||||
std::unique_ptr<SaveFileDialog> CreateSaveFileDialog(DisplayWindow* owner) override;
|
||||
|
|
@ -93,76 +117,92 @@ public:
|
|||
wayland::registry_t s_waylandRegistry;
|
||||
std::vector<WaylandDisplayWindow*> s_Windows;
|
||||
WaylandDisplayWindow* m_FocusWindow = nullptr;
|
||||
WaylandDisplayWindow* m_MouseFocusWindow = nullptr; // Mouse focus should be tracked separately.
|
||||
WaylandDisplayWindow* m_MouseFocusWindow = nullptr; // Mouse focus should be tracked separately.
|
||||
|
||||
wayland::compositor_t m_waylandCompositor;
|
||||
wayland::shm_t m_waylandSHM;
|
||||
wayland::seat_t m_waylandSeat;
|
||||
wayland::output_t m_waylandOutput;
|
||||
wayland::data_device_manager_t m_DataDeviceManager;
|
||||
wayland::xdg_wm_base_t m_XDGWMBase;
|
||||
wayland::zwp_pointer_constraints_v1_t m_PointerConstraints;
|
||||
wayland::xdg_activation_v1_t m_XDGActivation;
|
||||
wayland::zxdg_decoration_manager_v1_t m_XDGDecorationManager;
|
||||
wayland::fractional_scale_manager_v1_t m_FractionalScaleManager;
|
||||
wayland::zxdg_output_manager_v1_t m_XDGOutputManager;
|
||||
wayland::zxdg_output_v1_t m_XDGOutput;
|
||||
wayland::zxdg_exporter_v2_t m_XDGExporter;
|
||||
wayland::compositor_t m_waylandCompositor;
|
||||
wayland::shm_t m_waylandSHM;
|
||||
wayland::seat_t m_waylandSeat;
|
||||
wayland::output_t m_waylandOutput;
|
||||
wayland::data_device_manager_t m_DataDeviceManager;
|
||||
wayland::xdg_wm_base_t m_XDGWMBase;
|
||||
wayland::zwp_pointer_constraints_v1_t m_PointerConstraints;
|
||||
wayland::xdg_activation_v1_t m_XDGActivation;
|
||||
wayland::zxdg_decoration_manager_v1_t m_XDGDecorationManager;
|
||||
wayland::fractional_scale_manager_v1_t m_FractionalScaleManager;
|
||||
wayland::zxdg_output_manager_v1_t m_XDGOutputManager;
|
||||
wayland::zxdg_output_v1_t m_XDGOutput;
|
||||
wayland::zxdg_exporter_v2_t m_XDGExporter;
|
||||
|
||||
wayland::keyboard_t m_waylandKeyboard;
|
||||
wayland::pointer_t m_waylandPointer;
|
||||
wayland::keyboard_t m_waylandKeyboard;
|
||||
wayland::pointer_t m_waylandPointer;
|
||||
|
||||
wayland::zwp_relative_pointer_manager_v1_t m_RelativePointerManager;
|
||||
wayland::zwp_relative_pointer_v1_t m_RelativePointer;
|
||||
wayland::zwp_relative_pointer_manager_v1_t m_RelativePointerManager;
|
||||
wayland::zwp_relative_pointer_v1_t m_RelativePointer;
|
||||
|
||||
wayland::cursor_image_t m_cursorImage;
|
||||
wayland::surface_t m_cursorSurface;
|
||||
wayland::buffer_t m_cursorBuffer;
|
||||
wayland::xdg_toplevel_icon_manager_v1_t m_XDGToplevelIconManager;
|
||||
|
||||
std::map<InputKey, bool> inputKeyStates; // True when the key is pressed, false when isn't
|
||||
wayland::cursor_shape_manager_v1_t m_CursorShapeManager;
|
||||
wayland::cursor_shape_device_v1_t m_CursorShapeDevice;
|
||||
|
||||
bool IsMouseLocked() { return hasMouseLock; }
|
||||
void SetMouseLocked(bool val) { hasMouseLock = val; }
|
||||
wayland::cursor_image_t m_cursorImage;
|
||||
wayland::surface_t m_cursorSurface;
|
||||
wayland::buffer_t m_cursorBuffer;
|
||||
|
||||
std::map<InputKey, bool> inputKeyStates; // True when the key is pressed, false when isn't
|
||||
|
||||
bool IsMouseLocked() { return hasMouseLock; }
|
||||
void SetMouseLocked(bool val) { hasMouseLock = val; }
|
||||
|
||||
private:
|
||||
void CheckNeedsUpdate();
|
||||
void CheckNeedsUpdate();
|
||||
void UpdateTimers();
|
||||
void ConnectDeviceEvents();
|
||||
void OnKeyboardKeyEvent(xkb_keysym_t xkbKeySym, wayland::keyboard_key_state state);
|
||||
void OnKeyboardCharEvent(const char* ch, wayland::keyboard_key_state state);
|
||||
void OnKeyboardDelayEnd();
|
||||
void OnKeyboardRepeat();
|
||||
void OnMouseEnterEvent(uint32_t serial);
|
||||
void OnMouseLeaveEvent();
|
||||
void OnMousePressEvent(InputKey button);
|
||||
void OnMouseReleaseEvent(InputKey button);
|
||||
void OnMouseMoveEvent(Point surfacePos);
|
||||
void OnMouseMoveRawEvent(int surfaceX, int surfaceY);
|
||||
void OnMouseWheelEvent(InputKey button);
|
||||
void WaitForEvents(int timeout);
|
||||
int GetTimerTimeout();
|
||||
void ConnectDeviceEvents();
|
||||
void OnKeyboardKeyEvent(xkb_keysym_t xkbKeySym, wayland::keyboard_key_state state);
|
||||
void OnKeyboardCharEvent(const char* ch, wayland::keyboard_key_state state);
|
||||
void OnKeyboardDelayEnd();
|
||||
void OnKeyboardRepeat();
|
||||
void OnMouseEnterEvent(uint32_t serial);
|
||||
void OnMouseLeaveEvent();
|
||||
void OnMousePressEvent(InputKey button);
|
||||
void OnMouseReleaseEvent(InputKey button);
|
||||
void OnMouseMoveEvent(Point surfacePos);
|
||||
void OnMouseMoveRawEvent(int surfaceX, int surfaceY);
|
||||
void OnMouseWheelEvent(InputKey button);
|
||||
|
||||
InputKey XKBKeySymToInputKey(xkb_keysym_t keySym);
|
||||
InputKey LinuxInputEventCodeToInputKey(uint32_t inputCode);
|
||||
InputKey XKBKeySymToInputKey(xkb_keysym_t keySym);
|
||||
InputKey LinuxInputEventCodeToInputKey(uint32_t inputCode);
|
||||
|
||||
std::string GetWaylandCursorName(StandardCursor cursor);
|
||||
wayland::cursor_shape_device_v1_shape GetWaylandCursorShape(StandardCursor cursor);
|
||||
std::string GetWaylandCursorName(StandardCursor cursor);
|
||||
|
||||
bool hasKeyboard = false;
|
||||
bool hasPointer = false;
|
||||
bool hasMouseLock = false;
|
||||
bool hasKeyboard = false;
|
||||
bool hasPointer = false;
|
||||
bool hasMouseLock = false;
|
||||
|
||||
ZTimer::TimePoint m_previousTime;
|
||||
ZTimer::TimePoint m_currentTime;
|
||||
ZTimer::TimePoint m_previousTime;
|
||||
ZTimer::TimePoint m_currentTime;
|
||||
|
||||
ZTimer m_keyboardDelayTimer;
|
||||
ZTimer m_keyboardRepeatTimer;
|
||||
ZTimer m_keyboardDelayTimer;
|
||||
ZTimer m_keyboardRepeatTimer;
|
||||
|
||||
InputKey previousKey = {};
|
||||
std::string previousChars;
|
||||
InputKey previousKey = {};
|
||||
std::string previousChars;
|
||||
|
||||
uint32_t m_KeyboardSerial = 0;
|
||||
uint32_t m_KeyboardSerial = 0;
|
||||
uint32_t m_MouseSerial = 0;
|
||||
|
||||
xkb_context* m_KeymapContext = nullptr;
|
||||
xkb_keymap* m_Keymap = nullptr;
|
||||
xkb_state* m_KeyboardState = nullptr;
|
||||
xkb_context* m_KeymapContext = nullptr;
|
||||
xkb_keymap* m_Keymap = nullptr;
|
||||
xkb_state* m_KeyboardState = nullptr;
|
||||
|
||||
WaylandPointerEvent currentPointerEvent = {0};
|
||||
WaylandPointerEvent currentPointerEvent = {0};
|
||||
|
||||
wayland::data_device_t m_DataDevice;
|
||||
std::unordered_map<wayland::data_offer_t, std::set<std::string>, hash_proxy_t> m_DataOfferMimeTypes;
|
||||
|
||||
std::string m_ClipboardContents;
|
||||
|
||||
std::vector<std::shared_ptr<WaylandTimer>> m_timers;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -3,288 +3,313 @@
|
|||
#include <cstring>
|
||||
#include <dlfcn.h>
|
||||
|
||||
#include "core/image.h"
|
||||
|
||||
WaylandDisplayWindow::WaylandDisplayWindow(WaylandDisplayBackend* backend, DisplayWindowHost* windowHost, bool popupWindow, WaylandDisplayWindow* owner, RenderAPI renderAPI)
|
||||
: backend(backend), m_owner(owner), windowHost(windowHost), m_PopupWindow(popupWindow), m_renderAPI(renderAPI)
|
||||
: backend(backend), m_owner(owner), windowHost(windowHost), m_PopupWindow(popupWindow), m_renderAPI(renderAPI)
|
||||
{
|
||||
m_AppSurface = backend->m_waylandCompositor.create_surface();
|
||||
m_AppSurface = backend->m_waylandCompositor.create_surface();
|
||||
|
||||
m_NativeHandle.display = backend->s_waylandDisplay;
|
||||
m_NativeHandle.surface = m_AppSurface;
|
||||
m_NativeHandle.display = backend->s_waylandDisplay;
|
||||
m_NativeHandle.surface = m_AppSurface;
|
||||
|
||||
if (backend->m_FractionalScaleManager)
|
||||
{
|
||||
m_FractionalScale = backend->m_FractionalScaleManager.get_fractional_scale(m_AppSurface);
|
||||
if (backend->m_FractionalScaleManager)
|
||||
{
|
||||
m_FractionalScale = backend->m_FractionalScaleManager.get_fractional_scale(m_AppSurface);
|
||||
|
||||
m_FractionalScale.on_preferred_scale() = [&](uint32_t scale_numerator) {
|
||||
// parameter is the numerator of a fraction with the denominator of 120
|
||||
m_ScaleFactor = scale_numerator / 120.0;
|
||||
m_FractionalScale.on_preferred_scale() = [&](uint32_t scale_numerator) {
|
||||
// parameter is the numerator of a fraction with the denominator of 120
|
||||
m_ScaleFactor = scale_numerator / 120.0;
|
||||
|
||||
m_NeedsUpdate = true;
|
||||
windowHost->OnWindowDpiScaleChanged();
|
||||
};
|
||||
}
|
||||
m_NeedsUpdate = true;
|
||||
windowHost->OnWindowDpiScaleChanged();
|
||||
};
|
||||
}
|
||||
|
||||
m_XDGSurface = backend->m_XDGWMBase.get_xdg_surface(m_AppSurface);
|
||||
m_XDGSurface.on_configure() = [&] (uint32_t serial) {
|
||||
m_XDGSurface.ack_configure(serial);
|
||||
};
|
||||
m_XDGSurface = backend->m_XDGWMBase.get_xdg_surface(m_AppSurface);
|
||||
m_XDGSurface.on_configure() = [&] (uint32_t serial) {
|
||||
m_XDGSurface.ack_configure(serial);
|
||||
};
|
||||
|
||||
if (popupWindow)
|
||||
{
|
||||
InitializePopup();
|
||||
}
|
||||
else
|
||||
{
|
||||
InitializeToplevel();
|
||||
}
|
||||
if (popupWindow)
|
||||
{
|
||||
InitializePopup();
|
||||
}
|
||||
else
|
||||
{
|
||||
InitializeToplevel();
|
||||
}
|
||||
|
||||
backend->OnWindowCreated(this);
|
||||
backend->OnWindowCreated(this);
|
||||
|
||||
this->DrawSurface();
|
||||
this->DrawSurface();
|
||||
}
|
||||
|
||||
WaylandDisplayWindow::~WaylandDisplayWindow()
|
||||
{
|
||||
backend->OnWindowDestroyed(this);
|
||||
backend->OnWindowDestroyed(this);
|
||||
}
|
||||
|
||||
void WaylandDisplayWindow::InitializeToplevel()
|
||||
{
|
||||
m_XDGToplevel = m_XDGSurface.get_toplevel();
|
||||
m_XDGToplevel.set_title("ZWidget Window");
|
||||
m_XDGToplevel = m_XDGSurface.get_toplevel();
|
||||
m_XDGToplevel.set_title("ZWidget Window");
|
||||
|
||||
if (m_owner)
|
||||
m_XDGToplevel.set_parent(m_owner->m_XDGToplevel);
|
||||
if (m_owner)
|
||||
m_XDGToplevel.set_parent(m_owner->m_XDGToplevel);
|
||||
|
||||
if (backend->m_XDGDecorationManager)
|
||||
{
|
||||
// Force server side decorations if possible
|
||||
m_XDGToplevelDecoration = backend->m_XDGDecorationManager.get_toplevel_decoration(m_XDGToplevel);
|
||||
m_XDGToplevelDecoration.set_mode(wayland::zxdg_toplevel_decoration_v1_mode::server_side);
|
||||
}
|
||||
if (backend->m_XDGDecorationManager)
|
||||
{
|
||||
// Force server side decorations if possible
|
||||
m_XDGToplevelDecoration = backend->m_XDGDecorationManager.get_toplevel_decoration(m_XDGToplevel);
|
||||
m_XDGToplevelDecoration.set_mode(wayland::zxdg_toplevel_decoration_v1_mode::server_side);
|
||||
}
|
||||
|
||||
m_AppSurface.commit();
|
||||
m_AppSurface.commit();
|
||||
|
||||
backend->s_waylandDisplay.roundtrip();
|
||||
backend->s_waylandDisplay.roundtrip();
|
||||
|
||||
// These have to be added after the roundtrip
|
||||
m_XDGToplevel.on_configure() = [&] (int32_t width, int32_t height, wayland::array_t states) {
|
||||
OnXDGToplevelConfigureEvent(width, height);
|
||||
};
|
||||
// These have to be added after the roundtrip
|
||||
m_XDGToplevel.on_configure() = [&] (int32_t width, int32_t height, wayland::array_t states) {
|
||||
OnXDGToplevelConfigureEvent(width, height);
|
||||
};
|
||||
|
||||
m_XDGToplevel.on_close() = [&] () {
|
||||
OnExitEvent();
|
||||
};
|
||||
m_XDGToplevel.on_close() = [&] () {
|
||||
OnExitEvent();
|
||||
};
|
||||
|
||||
m_XDGToplevel.on_configure_bounds() = [this] (int32_t width, int32_t height)
|
||||
{
|
||||
m_XDGToplevel.on_configure_bounds() = [this] (int32_t width, int32_t height)
|
||||
{
|
||||
|
||||
};
|
||||
};
|
||||
|
||||
m_XDGExported = backend->m_XDGExporter.export_toplevel(m_AppSurface);
|
||||
m_XDGExported = backend->m_XDGExporter.export_toplevel(m_AppSurface);
|
||||
|
||||
m_XDGExported.on_handle() = [&] (std::string handleStr) {
|
||||
OnExportHandleEvent(handleStr);
|
||||
};
|
||||
m_XDGExported.on_handle() = [&] (std::string handleStr) {
|
||||
OnExportHandleEvent(handleStr);
|
||||
};
|
||||
}
|
||||
|
||||
void WaylandDisplayWindow::InitializePopup()
|
||||
{
|
||||
if (!m_owner)
|
||||
throw std::runtime_error("Popup window must have an owner!");
|
||||
if (!m_owner)
|
||||
throw std::runtime_error("Popup window must have an owner!");
|
||||
|
||||
wayland::xdg_positioner_t popupPositioner = backend->m_XDGWMBase.create_positioner();
|
||||
wayland::xdg_positioner_t popupPositioner = backend->m_XDGWMBase.create_positioner();
|
||||
|
||||
popupPositioner.set_anchor(wayland::xdg_positioner_anchor::bottom);
|
||||
popupPositioner.set_anchor_rect(0, 0, 1, 30);
|
||||
popupPositioner.set_size(1, 1);
|
||||
popupPositioner.set_anchor(wayland::xdg_positioner_anchor::bottom);
|
||||
popupPositioner.set_anchor_rect(0, 0, 1, 30);
|
||||
popupPositioner.set_size(1, 1);
|
||||
|
||||
m_XDGPopup = m_XDGSurface.get_popup(m_owner->m_XDGSurface, popupPositioner);
|
||||
m_XDGPopup = m_XDGSurface.get_popup(m_owner->m_XDGSurface, popupPositioner);
|
||||
|
||||
m_XDGPopup.on_configure() = [&] (int32_t x, int32_t y, int32_t width, int32_t height) {
|
||||
SetClientFrame(Rect::xywh(x, y, width, height));
|
||||
};
|
||||
m_XDGPopup.on_configure() = [&] (int32_t x, int32_t y, int32_t width, int32_t height) {
|
||||
SetClientFrame(Rect::xywh(x, y, width, height));
|
||||
};
|
||||
|
||||
//m_XDGPopup.on_repositioned()
|
||||
//m_XDGPopup.on_repositioned()
|
||||
|
||||
m_XDGPopup.on_popup_done() = [&] () {
|
||||
OnExitEvent();
|
||||
};
|
||||
m_XDGPopup.on_popup_done() = [&] () {
|
||||
OnExitEvent();
|
||||
};
|
||||
|
||||
m_AppSurface.commit();
|
||||
m_AppSurface.commit();
|
||||
|
||||
backend->s_waylandDisplay.roundtrip();
|
||||
backend->s_waylandDisplay.roundtrip();
|
||||
}
|
||||
|
||||
|
||||
void WaylandDisplayWindow::SetWindowTitle(const std::string& text)
|
||||
{
|
||||
if (m_XDGToplevel)
|
||||
m_XDGToplevel.set_title(text);
|
||||
if (m_XDGToplevel)
|
||||
m_XDGToplevel.set_title(text);
|
||||
}
|
||||
|
||||
void WaylandDisplayWindow::SetWindowIcon(const std::vector<std::shared_ptr<Image>>& images)
|
||||
{
|
||||
if (backend->m_XDGToplevelIconManager)
|
||||
{
|
||||
if (images.empty())
|
||||
{
|
||||
backend->m_XDGToplevelIconManager.set_icon(m_XDGToplevel, nullptr);
|
||||
return;
|
||||
}
|
||||
|
||||
m_XDGToplevelIcon = backend->m_XDGToplevelIconManager.create_icon();
|
||||
|
||||
CreateAppIconBuffers(images);
|
||||
|
||||
for (auto& icon_buffer : appIconBuffers)
|
||||
{
|
||||
m_XDGToplevelIcon.add_buffer(icon_buffer, 1);
|
||||
}
|
||||
|
||||
backend->m_XDGToplevelIconManager.set_icon(m_XDGToplevel, m_XDGToplevelIcon);
|
||||
}
|
||||
}
|
||||
|
||||
void WaylandDisplayWindow::SetWindowFrame(const Rect& box)
|
||||
{
|
||||
// Resizing will be shown on the next commit
|
||||
CreateBuffers(box.width, box.height);
|
||||
windowHost->OnWindowGeometryChanged();
|
||||
m_NeedsUpdate = true;
|
||||
m_AppSurface.commit();
|
||||
// Resizing will be shown on the next commit
|
||||
CreateBuffers(box.width, box.height);
|
||||
windowHost->OnWindowGeometryChanged();
|
||||
m_NeedsUpdate = true;
|
||||
m_AppSurface.commit();
|
||||
}
|
||||
|
||||
void WaylandDisplayWindow::SetClientFrame(const Rect& box)
|
||||
{
|
||||
SetWindowFrame(box);
|
||||
SetWindowFrame(box);
|
||||
}
|
||||
|
||||
void WaylandDisplayWindow::Show()
|
||||
{
|
||||
m_AppSurface.attach(m_AppSurfaceBuffer, 0, 0);
|
||||
m_AppSurface.damage(0, 0, m_WindowSize.width, m_WindowSize.height);
|
||||
m_AppSurface.commit();
|
||||
m_AppSurface.attach(m_AppSurfaceBuffer, 0, 0);
|
||||
m_AppSurface.damage(0, 0, m_WindowSize.width, m_WindowSize.height);
|
||||
m_AppSurface.commit();
|
||||
}
|
||||
|
||||
void WaylandDisplayWindow::ShowFullscreen()
|
||||
{
|
||||
if (m_XDGToplevel)
|
||||
{
|
||||
m_XDGToplevel.set_fullscreen(backend->m_waylandOutput);
|
||||
isFullscreen = true;
|
||||
}
|
||||
if (m_XDGToplevel)
|
||||
{
|
||||
m_XDGToplevel.set_fullscreen(backend->m_waylandOutput);
|
||||
isFullscreen = true;
|
||||
}
|
||||
}
|
||||
|
||||
void WaylandDisplayWindow::ShowMaximized()
|
||||
{
|
||||
if (m_XDGToplevel)
|
||||
m_XDGToplevel.set_maximized();
|
||||
if (m_XDGToplevel)
|
||||
m_XDGToplevel.set_maximized();
|
||||
}
|
||||
|
||||
void WaylandDisplayWindow::ShowMinimized()
|
||||
{
|
||||
if (m_XDGToplevel)
|
||||
m_XDGToplevel.set_minimized();
|
||||
if (m_XDGToplevel)
|
||||
m_XDGToplevel.set_minimized();
|
||||
}
|
||||
|
||||
void WaylandDisplayWindow::ShowNormal()
|
||||
{
|
||||
if (m_XDGToplevel)
|
||||
m_XDGToplevel.unset_fullscreen();
|
||||
if (m_XDGToplevel)
|
||||
m_XDGToplevel.unset_fullscreen();
|
||||
}
|
||||
|
||||
bool WaylandDisplayWindow::IsWindowFullscreen()
|
||||
{
|
||||
return isFullscreen;
|
||||
return isFullscreen;
|
||||
}
|
||||
|
||||
void WaylandDisplayWindow::Hide()
|
||||
{
|
||||
// Apparently this is how hiding a window works
|
||||
// By attaching a null buffer to the surface
|
||||
// See: https://lists.freedesktop.org/archives/wayland-devel/2017-November/035963.html
|
||||
m_AppSurface.attach(nullptr, 0, 0);
|
||||
m_AppSurface.commit();
|
||||
// Apparently this is how hiding a window works
|
||||
// By attaching a null buffer to the surface
|
||||
// See: https://lists.freedesktop.org/archives/wayland-devel/2017-November/035963.html
|
||||
m_AppSurface.attach(nullptr, 0, 0);
|
||||
m_AppSurface.commit();
|
||||
}
|
||||
|
||||
void WaylandDisplayWindow::Activate()
|
||||
{
|
||||
// To do: this needs to be in the backend instance if all windows share the activation token
|
||||
wayland::xdg_activation_token_v1_t xdgActivationToken = backend->m_XDGActivation.get_activation_token();
|
||||
// To do: this needs to be in the backend instance if all windows share the activation token
|
||||
wayland::xdg_activation_token_v1_t xdgActivationToken = backend->m_XDGActivation.get_activation_token();
|
||||
|
||||
std::string tokenString;
|
||||
std::string tokenString;
|
||||
|
||||
xdgActivationToken.on_done() = [&tokenString] (std::string obtainedString) {
|
||||
tokenString = obtainedString;
|
||||
};
|
||||
xdgActivationToken.on_done() = [&tokenString] (std::string obtainedString) {
|
||||
tokenString = obtainedString;
|
||||
};
|
||||
|
||||
xdgActivationToken.set_surface(m_AppSurface);
|
||||
xdgActivationToken.commit(); // This will set our token string
|
||||
xdgActivationToken.set_surface(m_AppSurface);
|
||||
xdgActivationToken.commit(); // This will set our token string
|
||||
|
||||
backend->m_XDGActivation.activate(tokenString, m_AppSurface);
|
||||
backend->m_FocusWindow = this;
|
||||
backend->m_MouseFocusWindow = this;
|
||||
windowHost->OnWindowActivated();
|
||||
backend->m_XDGActivation.activate(tokenString, m_AppSurface);
|
||||
backend->m_FocusWindow = this;
|
||||
backend->m_MouseFocusWindow = this;
|
||||
windowHost->OnWindowActivated();
|
||||
}
|
||||
|
||||
void WaylandDisplayWindow::ShowCursor(bool enable)
|
||||
{
|
||||
backend->ShowCursor(enable);
|
||||
backend->ShowCursor(enable);
|
||||
}
|
||||
|
||||
void WaylandDisplayWindow::LockCursor()
|
||||
{
|
||||
m_LockedPointer = backend->m_PointerConstraints.lock_pointer(m_AppSurface, backend->m_waylandPointer, nullptr, wayland::zwp_pointer_constraints_v1_lifetime::persistent);
|
||||
backend->SetMouseLocked(true);
|
||||
ShowCursor(false);
|
||||
m_LockedPointer = backend->m_PointerConstraints.lock_pointer(m_AppSurface, backend->m_waylandPointer, nullptr, wayland::zwp_pointer_constraints_v1_lifetime::persistent);
|
||||
backend->SetMouseLocked(true);
|
||||
ShowCursor(false);
|
||||
}
|
||||
|
||||
void WaylandDisplayWindow::UnlockCursor()
|
||||
{
|
||||
if (m_LockedPointer)
|
||||
m_LockedPointer.proxy_release();
|
||||
backend->SetMouseLocked(false);
|
||||
ShowCursor(true);
|
||||
if (m_LockedPointer)
|
||||
m_LockedPointer.proxy_release();
|
||||
backend->SetMouseLocked(false);
|
||||
ShowCursor(true);
|
||||
}
|
||||
|
||||
void WaylandDisplayWindow::CaptureMouse()
|
||||
{
|
||||
m_ConfinedPointer = backend->m_PointerConstraints.confine_pointer(GetWindowSurface(), backend->m_waylandPointer, nullptr, wayland::zwp_pointer_constraints_v1_lifetime::persistent);
|
||||
ShowCursor(false);
|
||||
m_ConfinedPointer = backend->m_PointerConstraints.confine_pointer(GetWindowSurface(), backend->m_waylandPointer, nullptr, wayland::zwp_pointer_constraints_v1_lifetime::persistent);
|
||||
ShowCursor(false);
|
||||
}
|
||||
|
||||
void WaylandDisplayWindow::ReleaseMouseCapture()
|
||||
{
|
||||
if (m_ConfinedPointer)
|
||||
m_ConfinedPointer.proxy_release();
|
||||
ShowCursor(true);
|
||||
if (m_ConfinedPointer)
|
||||
m_ConfinedPointer.proxy_release();
|
||||
ShowCursor(true);
|
||||
}
|
||||
|
||||
void WaylandDisplayWindow::Update()
|
||||
{
|
||||
m_NeedsUpdate = true;
|
||||
m_NeedsUpdate = true;
|
||||
}
|
||||
|
||||
bool WaylandDisplayWindow::GetKeyState(InputKey key)
|
||||
{
|
||||
return backend->GetKeyState(key);
|
||||
return backend->GetKeyState(key);
|
||||
}
|
||||
|
||||
void WaylandDisplayWindow::SetCursor(StandardCursor cursor)
|
||||
{
|
||||
backend->SetCursor(cursor);
|
||||
backend->SetCursor(cursor);
|
||||
}
|
||||
|
||||
Rect WaylandDisplayWindow::GetWindowFrame() const
|
||||
{
|
||||
return Rect(m_WindowGlobalPos.x, m_WindowGlobalPos.y, m_WindowSize.width, m_WindowSize.height);
|
||||
return Rect(m_WindowGlobalPos.x, m_WindowGlobalPos.y, m_WindowSize.width, m_WindowSize.height);
|
||||
}
|
||||
|
||||
Size WaylandDisplayWindow::GetClientSize() const
|
||||
{
|
||||
return m_WindowSize / m_ScaleFactor;
|
||||
return m_WindowSize / m_ScaleFactor;
|
||||
}
|
||||
|
||||
int WaylandDisplayWindow::GetPixelWidth() const
|
||||
{
|
||||
return m_WindowSize.width;
|
||||
return m_WindowSize.width;
|
||||
}
|
||||
|
||||
int WaylandDisplayWindow::GetPixelHeight() const
|
||||
{
|
||||
return m_WindowSize.height;
|
||||
return m_WindowSize.height;
|
||||
}
|
||||
|
||||
double WaylandDisplayWindow::GetDpiScale() const
|
||||
{
|
||||
return m_ScaleFactor;
|
||||
return m_ScaleFactor;
|
||||
}
|
||||
|
||||
void WaylandDisplayWindow::PresentBitmap(int width, int height, const uint32_t* pixels)
|
||||
{
|
||||
// Make new buffers if the sizes don't match
|
||||
if (width != m_WindowSize.width || height != m_WindowSize.height)
|
||||
CreateBuffers(width, height);
|
||||
// Make new buffers if the sizes don't match
|
||||
if (width != m_WindowSize.width || height != m_WindowSize.height)
|
||||
CreateBuffers(width, height);
|
||||
|
||||
std::memcpy(shared_mem->get_mem(), (void*)pixels, width * height * 4);
|
||||
std::memcpy(shared_mem->get_mem(), (void*)pixels, width * height * 4);
|
||||
}
|
||||
|
||||
void WaylandDisplayWindow::SetBorderColor(uint32_t bgra8)
|
||||
|
|
@ -304,79 +329,115 @@ void WaylandDisplayWindow::SetCaptionTextColor(uint32_t bgra8)
|
|||
|
||||
std::string WaylandDisplayWindow::GetClipboardText()
|
||||
{
|
||||
return m_ClipboardContents;
|
||||
return backend->GetClipboardText();
|
||||
}
|
||||
|
||||
void WaylandDisplayWindow::SetClipboardText(const std::string& text)
|
||||
{
|
||||
m_ClipboardContents = text;
|
||||
m_DataSource = backend->m_DataDeviceManager.create_data_source();
|
||||
|
||||
m_DataSource.on_send() = [text] (const std::string& mime_type, const int fd)
|
||||
{
|
||||
write(fd, text.c_str(), text.size());
|
||||
close(fd);
|
||||
};
|
||||
|
||||
m_DataSource.offer("text/plain");
|
||||
backend->GetDataDevice().set_selection(m_DataSource, backend->GetKeyboardSerial());
|
||||
}
|
||||
|
||||
Point WaylandDisplayWindow::MapFromGlobal(const Point& pos) const
|
||||
{
|
||||
return (pos - m_WindowGlobalPos) / m_ScaleFactor;
|
||||
return (pos - m_WindowGlobalPos) / m_ScaleFactor;
|
||||
}
|
||||
|
||||
Point WaylandDisplayWindow::MapToGlobal(const Point& pos) const
|
||||
{
|
||||
return (m_WindowGlobalPos + pos) / m_ScaleFactor;
|
||||
return (m_WindowGlobalPos + pos) / m_ScaleFactor;
|
||||
}
|
||||
|
||||
void WaylandDisplayWindow::OnXDGToplevelConfigureEvent(int32_t width, int32_t height)
|
||||
{
|
||||
Rect rect = GetWindowFrame();
|
||||
rect.width = width / m_ScaleFactor;
|
||||
rect.height = height / m_ScaleFactor;
|
||||
SetWindowFrame(rect);
|
||||
windowHost->OnWindowGeometryChanged();
|
||||
Rect rect = GetWindowFrame();
|
||||
rect.width = width / m_ScaleFactor;
|
||||
rect.height = height / m_ScaleFactor;
|
||||
SetWindowFrame(rect);
|
||||
windowHost->OnWindowGeometryChanged();
|
||||
}
|
||||
|
||||
void WaylandDisplayWindow::OnExportHandleEvent(std::string exportedHandle)
|
||||
{
|
||||
m_windowID = exportedHandle;
|
||||
m_windowID = exportedHandle;
|
||||
}
|
||||
|
||||
void WaylandDisplayWindow::OnExitEvent()
|
||||
{
|
||||
windowHost->OnWindowClose();
|
||||
windowHost->OnWindowClose();
|
||||
}
|
||||
|
||||
void WaylandDisplayWindow::DrawSurface(uint32_t serial)
|
||||
{
|
||||
m_AppSurface.attach(m_AppSurfaceBuffer, 0, 0);
|
||||
m_AppSurface.damage(0, 0, m_WindowSize.width, m_WindowSize.height);
|
||||
m_AppSurface.attach(m_AppSurfaceBuffer, 0, 0);
|
||||
m_AppSurface.damage(0, 0, m_WindowSize.width, m_WindowSize.height);
|
||||
|
||||
if (m_renderAPI == RenderAPI::Unspecified || m_renderAPI == RenderAPI::Bitmap)
|
||||
{
|
||||
m_FrameCallback = m_AppSurface.frame();
|
||||
if (m_renderAPI == RenderAPI::Unspecified || m_renderAPI == RenderAPI::Bitmap)
|
||||
{
|
||||
m_FrameCallback = m_AppSurface.frame();
|
||||
|
||||
m_FrameCallback.on_done() = bind_mem_fn(&WaylandDisplayWindow::DrawSurface, this);
|
||||
}
|
||||
m_AppSurface.commit();
|
||||
m_FrameCallback.on_done() = bind_mem_fn(&WaylandDisplayWindow::DrawSurface, this);
|
||||
}
|
||||
m_AppSurface.commit();
|
||||
}
|
||||
|
||||
void WaylandDisplayWindow::CreateBuffers(int32_t width, int32_t height)
|
||||
{
|
||||
if (width == 0 || height == 0)
|
||||
return;
|
||||
if (width == 0 || height == 0)
|
||||
return;
|
||||
|
||||
if (shared_mem)
|
||||
shared_mem.reset();
|
||||
if (shared_mem)
|
||||
shared_mem.reset();
|
||||
|
||||
int scaled_width = width * m_ScaleFactor;
|
||||
int scaled_height = height * m_ScaleFactor;
|
||||
int scaled_width = width * m_ScaleFactor;
|
||||
int scaled_height = height * m_ScaleFactor;
|
||||
|
||||
shared_mem = std::make_shared<SharedMemHelper>(scaled_width * scaled_height * 4);
|
||||
auto pool = backend->m_waylandSHM.create_pool(shared_mem->get_fd(), scaled_width * scaled_height * 4);
|
||||
shared_mem = std::make_shared<SharedMemHelper>(scaled_width * scaled_height * 4);
|
||||
auto pool = backend->m_waylandSHM.create_pool(shared_mem->get_fd(), scaled_width * scaled_height * 4);
|
||||
|
||||
m_AppSurfaceBuffer = pool.create_buffer(0, scaled_width, scaled_height, scaled_width * 4, wayland::shm_format::xrgb8888);
|
||||
m_AppSurfaceBuffer = pool.create_buffer(0, scaled_width, scaled_height, scaled_width * 4, wayland::shm_format::xrgb8888);
|
||||
|
||||
m_WindowSize = Size(scaled_width, scaled_height);
|
||||
m_WindowSize = Size(scaled_width, scaled_height);
|
||||
}
|
||||
|
||||
void WaylandDisplayWindow::CreateAppIconBuffers(const std::vector<std::shared_ptr<Image>>& images)
|
||||
{
|
||||
if (images.empty())
|
||||
return;
|
||||
|
||||
appIconBuffers.clear();
|
||||
appIconSharedMems.clear();
|
||||
|
||||
for (auto& image: images)
|
||||
{
|
||||
const int image_size = image->GetWidth() * image->GetHeight() * 4;
|
||||
const int stride = image->GetWidth() * 4;
|
||||
|
||||
auto new_shared_mem = std::make_shared<SharedMemHelper>(image_size);
|
||||
|
||||
auto pool = backend->m_waylandSHM.create_pool(new_shared_mem->get_fd(), image_size);
|
||||
|
||||
auto new_buffer = pool.create_buffer(0, image->GetWidth(), image->GetHeight(), stride, wayland::shm_format::argb8888);
|
||||
|
||||
// Fill the buffer with the icon data
|
||||
std::memcpy(new_shared_mem->get_mem(), image->GetData(), image_size);
|
||||
|
||||
appIconSharedMems.push_back(new_shared_mem);
|
||||
appIconBuffers.push_back(new_buffer);
|
||||
}
|
||||
}
|
||||
|
||||
std::string WaylandDisplayWindow::GetWaylandWindowID()
|
||||
{
|
||||
return m_windowID;
|
||||
return m_windowID;
|
||||
}
|
||||
|
||||
// This is to avoid needing all the Vulkan headers and the volk binding library just for this:
|
||||
|
|
@ -398,11 +459,11 @@ typedef PFN_vkVoidFunction(VKAPI_PTR* PFN_vkGetInstanceProcAddr)(VkInstance inst
|
|||
typedef VkFlags VkWaylandSurfaceCreateFlagsKHR;
|
||||
typedef struct VkWaylandSurfaceCreateInfoKHR
|
||||
{
|
||||
VkStructureType sType;
|
||||
const void* pNext;
|
||||
VkWaylandSurfaceCreateFlagsKHR flags;
|
||||
struct wl_display* display;
|
||||
struct wl_surface* surface;
|
||||
VkStructureType sType;
|
||||
const void* pNext;
|
||||
VkWaylandSurfaceCreateFlagsKHR flags;
|
||||
struct wl_display* display;
|
||||
struct wl_surface* surface;
|
||||
} VkWaylandSurfaceCreateInfoKHR;
|
||||
|
||||
typedef VkResult(VKAPI_PTR* PFN_vkCreateWaylandSurfaceKHR)(VkInstance instance, const VkWaylandSurfaceCreateInfoKHR* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSurfaceKHR* pSurface);
|
||||
|
|
|
|||
|
|
@ -25,70 +25,71 @@ template <typename R, typename T, typename... Args>
|
|||
std::function<R(Args...)> bind_mem_fn(R(T::* func)(Args...), T *t)
|
||||
{
|
||||
return [func, t] (Args... args)
|
||||
{
|
||||
return (t->*func)(args...);
|
||||
};
|
||||
{
|
||||
return (t->*func)(args...);
|
||||
};
|
||||
}
|
||||
|
||||
class SharedMemHelper
|
||||
{
|
||||
public:
|
||||
SharedMemHelper(size_t size)
|
||||
: len(size)
|
||||
{
|
||||
std::stringstream ss;
|
||||
std::random_device device;
|
||||
std::default_random_engine engine(device());
|
||||
std::uniform_int_distribution<unsigned int> distribution(0, std::numeric_limits<unsigned int>::max());
|
||||
ss << distribution(engine);
|
||||
name = ss.str();
|
||||
SharedMemHelper(size_t size)
|
||||
: len(size)
|
||||
{
|
||||
std::stringstream ss;
|
||||
std::random_device device;
|
||||
std::default_random_engine engine(device());
|
||||
std::uniform_int_distribution<unsigned int> distribution(0, std::numeric_limits<unsigned int>::max());
|
||||
ss << distribution(engine);
|
||||
name = ss.str();
|
||||
|
||||
fd = memfd_create(name.c_str(), 0);
|
||||
if(fd < 0)
|
||||
throw std::runtime_error("shm_open failed.");
|
||||
fd = memfd_create(name.c_str(), 0);
|
||||
if(fd < 0)
|
||||
throw std::runtime_error("shm_open failed.");
|
||||
|
||||
if(ftruncate(fd, size) < 0)
|
||||
throw std::runtime_error("ftruncate failed.");
|
||||
if(ftruncate(fd, size) < 0)
|
||||
throw std::runtime_error("ftruncate failed.");
|
||||
|
||||
mem = mmap(nullptr, len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
|
||||
if(mem == MAP_FAILED) // NOLINT
|
||||
throw std::runtime_error("mmap failed with len " + std::to_string(len) + ".");
|
||||
}
|
||||
mem = mmap(nullptr, len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
|
||||
if(mem == MAP_FAILED) // NOLINT
|
||||
throw std::runtime_error("mmap failed with len " + std::to_string(len) + ".");
|
||||
}
|
||||
|
||||
~SharedMemHelper() noexcept
|
||||
{
|
||||
if(fd)
|
||||
{
|
||||
munmap(mem, len);
|
||||
close(fd);
|
||||
shm_unlink(name.c_str());
|
||||
}
|
||||
}
|
||||
~SharedMemHelper() noexcept
|
||||
{
|
||||
if(fd)
|
||||
{
|
||||
munmap(mem, len);
|
||||
close(fd);
|
||||
shm_unlink(name.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
int get_fd() const
|
||||
{
|
||||
return fd;
|
||||
}
|
||||
int get_fd() const
|
||||
{
|
||||
return fd;
|
||||
}
|
||||
|
||||
void *get_mem()
|
||||
{
|
||||
return mem;
|
||||
}
|
||||
void *get_mem()
|
||||
{
|
||||
return mem;
|
||||
}
|
||||
|
||||
private:
|
||||
std::string name;
|
||||
int fd = 0;
|
||||
size_t len = 0;
|
||||
void *mem = nullptr;
|
||||
std::string name;
|
||||
int fd = 0;
|
||||
size_t len = 0;
|
||||
void *mem = nullptr;
|
||||
};
|
||||
|
||||
class WaylandDisplayWindow : public DisplayWindow
|
||||
{
|
||||
public:
|
||||
WaylandDisplayWindow(WaylandDisplayBackend* backend, DisplayWindowHost* windowHost, bool popupWindow, WaylandDisplayWindow* owner, RenderAPI renderAPI);
|
||||
~WaylandDisplayWindow();
|
||||
WaylandDisplayWindow(WaylandDisplayBackend* backend, DisplayWindowHost* windowHost, bool popupWindow, WaylandDisplayWindow* owner, RenderAPI renderAPI);
|
||||
~WaylandDisplayWindow();
|
||||
|
||||
void SetWindowTitle(const std::string& text) override;
|
||||
void SetWindowTitle(const std::string& text) override;
|
||||
void SetWindowIcon(const std::vector<std::shared_ptr<Image>>& images) override;
|
||||
void SetWindowFrame(const Rect& box) override;
|
||||
void SetClientFrame(const Rect& box) override;
|
||||
void Show() override;
|
||||
|
|
@ -135,64 +136,69 @@ public:
|
|||
bool IsWindowFullscreen() override;
|
||||
|
||||
private:
|
||||
// Event handlers as otherwise linking DisplayWindowHost On...() functions with Wayland events directly crashes the app
|
||||
// Alternatively to avoid crashes one can capture by value ([=]) instead of reference ([&])
|
||||
void OnXDGToplevelConfigureEvent(int32_t width, int32_t height);
|
||||
void OnExportHandleEvent(std::string exportedHandle);
|
||||
void OnExitEvent();
|
||||
// Event handlers as otherwise linking DisplayWindowHost On...() functions with Wayland events directly crashes the app
|
||||
// Alternatively to avoid crashes one can capture by value ([=]) instead of reference ([&])
|
||||
void OnXDGToplevelConfigureEvent(int32_t width, int32_t height);
|
||||
void OnExportHandleEvent(std::string exportedHandle);
|
||||
void OnExitEvent();
|
||||
|
||||
void DrawSurface(uint32_t serial = 0);
|
||||
void DrawSurface(uint32_t serial = 0);
|
||||
|
||||
void InitializeToplevel();
|
||||
void InitializePopup();
|
||||
void InitializeToplevel();
|
||||
void InitializePopup();
|
||||
|
||||
WaylandDisplayBackend* backend = nullptr;
|
||||
WaylandDisplayWindow* m_owner = nullptr;
|
||||
DisplayWindowHost* windowHost = nullptr;
|
||||
bool m_PopupWindow = false;
|
||||
WaylandDisplayBackend* backend = nullptr;
|
||||
WaylandDisplayWindow* m_owner = nullptr;
|
||||
DisplayWindowHost* windowHost = nullptr;
|
||||
bool m_PopupWindow = false;
|
||||
|
||||
bool m_NeedsUpdate = true;
|
||||
bool m_NeedsUpdate = true;
|
||||
|
||||
Point m_WindowGlobalPos = Point(0, 0);
|
||||
Size m_WindowSize = Size(0, 0);
|
||||
double m_ScaleFactor = 1.0;
|
||||
Point m_WindowGlobalPos = Point(0, 0);
|
||||
Size m_WindowSize = Size(0, 0);
|
||||
double m_ScaleFactor = 1.0;
|
||||
|
||||
Point m_SurfaceMousePos = Point(0, 0);
|
||||
Point m_SurfaceMousePos = Point(0, 0);
|
||||
|
||||
WaylandNativeHandle m_NativeHandle;
|
||||
RenderAPI m_renderAPI;
|
||||
WaylandNativeHandle m_NativeHandle;
|
||||
RenderAPI m_renderAPI;
|
||||
|
||||
wayland::data_device_t m_DataDevice;
|
||||
wayland::data_source_t m_DataSource;
|
||||
wayland::data_source_t m_DataSource;
|
||||
|
||||
wayland::zxdg_toplevel_decoration_v1_t m_XDGToplevelDecoration;
|
||||
wayland::zxdg_toplevel_decoration_v1_t m_XDGToplevelDecoration;
|
||||
|
||||
wayland::fractional_scale_v1_t m_FractionalScale;
|
||||
wayland::fractional_scale_v1_t m_FractionalScale;
|
||||
|
||||
wayland::surface_t m_AppSurface;
|
||||
wayland::buffer_t m_AppSurfaceBuffer;
|
||||
wayland::surface_t m_AppSurface;
|
||||
wayland::buffer_t m_AppSurfaceBuffer;
|
||||
|
||||
wayland::xdg_surface_t m_XDGSurface;
|
||||
wayland::xdg_toplevel_t m_XDGToplevel;
|
||||
wayland::xdg_popup_t m_XDGPopup;
|
||||
wayland::xdg_surface_t m_XDGSurface;
|
||||
wayland::xdg_toplevel_t m_XDGToplevel;
|
||||
wayland::xdg_popup_t m_XDGPopup;
|
||||
|
||||
wayland::zxdg_exported_v2_t m_XDGExported;
|
||||
wayland::zxdg_exported_v2_t m_XDGExported;
|
||||
|
||||
wayland::zwp_locked_pointer_v1_t m_LockedPointer;
|
||||
wayland::zwp_confined_pointer_v1_t m_ConfinedPointer;
|
||||
wayland::zwp_locked_pointer_v1_t m_LockedPointer;
|
||||
wayland::zwp_confined_pointer_v1_t m_ConfinedPointer;
|
||||
|
||||
wayland::callback_t m_FrameCallback;
|
||||
wayland::xdg_toplevel_icon_v1_t m_XDGToplevelIcon;
|
||||
|
||||
std::string m_windowID;
|
||||
std::string m_ClipboardContents;
|
||||
wayland::callback_t m_FrameCallback;
|
||||
|
||||
std::shared_ptr<SharedMemHelper> shared_mem;
|
||||
std::string m_windowID;
|
||||
|
||||
bool isFullscreen = false;
|
||||
std::shared_ptr<SharedMemHelper> shared_mem;
|
||||
|
||||
// Helper functions
|
||||
void CreateBuffers(int32_t width, int32_t height);
|
||||
std::string GetWaylandWindowID();
|
||||
std::vector<std::shared_ptr<SharedMemHelper>> appIconSharedMems;
|
||||
std::vector<wayland::buffer_t> appIconBuffers;
|
||||
|
||||
friend WaylandDisplayBackend;
|
||||
bool isFullscreen = false;
|
||||
|
||||
// Helper functions
|
||||
void CreateBuffers(int32_t width, int32_t height);
|
||||
|
||||
void CreateAppIconBuffers(const std::vector<std::shared_ptr<Image>>& images);
|
||||
std::string GetWaylandWindowID();
|
||||
|
||||
friend WaylandDisplayBackend;
|
||||
};
|
||||
|
|
|
|||
218
libraries/ZWidget/src/window/wayland/wl_cursor_shape.cpp
Normal file
|
|
@ -0,0 +1,218 @@
|
|||
#include "wl_cursor_shape.hpp"
|
||||
|
||||
using namespace wayland;
|
||||
using namespace wayland::detail;
|
||||
|
||||
const wl_interface* cursor_shape_manager_v1_interface_destroy_request[0] = {
|
||||
};
|
||||
|
||||
const wl_interface* cursor_shape_manager_v1_interface_get_pointer_request[2] = {
|
||||
&cursor_shape_device_v1_interface,
|
||||
&pointer_interface,
|
||||
};
|
||||
|
||||
const wl_interface* cursor_shape_manager_v1_interface_get_tablet_tool_v2_request[2] = {
|
||||
&cursor_shape_device_v1_interface,
|
||||
&zwp_tablet_tool_v2_interface,
|
||||
};
|
||||
|
||||
const wl_message cursor_shape_manager_v1_interface_requests[3] = {
|
||||
{
|
||||
"destroy",
|
||||
"",
|
||||
cursor_shape_manager_v1_interface_destroy_request,
|
||||
},
|
||||
{
|
||||
"get_pointer",
|
||||
"no",
|
||||
cursor_shape_manager_v1_interface_get_pointer_request,
|
||||
},
|
||||
{
|
||||
"get_tablet_tool_v2",
|
||||
"no",
|
||||
cursor_shape_manager_v1_interface_get_tablet_tool_v2_request,
|
||||
},
|
||||
};
|
||||
|
||||
const wl_message cursor_shape_manager_v1_interface_events[0] = {
|
||||
};
|
||||
|
||||
const wl_interface wayland::detail::cursor_shape_manager_v1_interface =
|
||||
{
|
||||
"wp_cursor_shape_manager_v1",
|
||||
2,
|
||||
3,
|
||||
cursor_shape_manager_v1_interface_requests,
|
||||
0,
|
||||
cursor_shape_manager_v1_interface_events,
|
||||
};
|
||||
|
||||
const wl_interface* cursor_shape_device_v1_interface_destroy_request[0] = {
|
||||
};
|
||||
|
||||
const wl_interface* cursor_shape_device_v1_interface_set_shape_request[2] = {
|
||||
nullptr,
|
||||
nullptr,
|
||||
};
|
||||
|
||||
const wl_message cursor_shape_device_v1_interface_requests[2] = {
|
||||
{
|
||||
"destroy",
|
||||
"",
|
||||
cursor_shape_device_v1_interface_destroy_request,
|
||||
},
|
||||
{
|
||||
"set_shape",
|
||||
"uu",
|
||||
cursor_shape_device_v1_interface_set_shape_request,
|
||||
},
|
||||
};
|
||||
|
||||
const wl_message cursor_shape_device_v1_interface_events[0] = {
|
||||
};
|
||||
|
||||
const wl_interface wayland::detail::cursor_shape_device_v1_interface =
|
||||
{
|
||||
"wp_cursor_shape_device_v1",
|
||||
2,
|
||||
2,
|
||||
cursor_shape_device_v1_interface_requests,
|
||||
0,
|
||||
cursor_shape_device_v1_interface_events,
|
||||
};
|
||||
|
||||
cursor_shape_manager_v1_t::cursor_shape_manager_v1_t(const proxy_t &p)
|
||||
: proxy_t(p)
|
||||
{
|
||||
if(proxy_has_object() && get_wrapper_type() == wrapper_type::standard)
|
||||
{
|
||||
set_events(std::shared_ptr<detail::events_base_t>(new events_t), dispatcher);
|
||||
set_destroy_opcode(0U);
|
||||
}
|
||||
set_interface(&cursor_shape_manager_v1_interface);
|
||||
set_copy_constructor([] (const proxy_t &p) -> proxy_t
|
||||
{ return cursor_shape_manager_v1_t(p); });
|
||||
}
|
||||
|
||||
cursor_shape_manager_v1_t::cursor_shape_manager_v1_t()
|
||||
{
|
||||
set_interface(&cursor_shape_manager_v1_interface);
|
||||
set_copy_constructor([] (const proxy_t &p) -> proxy_t
|
||||
{ return cursor_shape_manager_v1_t(p); });
|
||||
}
|
||||
|
||||
cursor_shape_manager_v1_t::cursor_shape_manager_v1_t(wp_cursor_shape_manager_v1 *p, wrapper_type t)
|
||||
: proxy_t(reinterpret_cast<wl_proxy*> (p), t){
|
||||
if(proxy_has_object() && get_wrapper_type() == wrapper_type::standard)
|
||||
{
|
||||
set_events(std::shared_ptr<detail::events_base_t>(new events_t), dispatcher);
|
||||
set_destroy_opcode(0U);
|
||||
}
|
||||
set_interface(&cursor_shape_manager_v1_interface);
|
||||
set_copy_constructor([] (const proxy_t &p) -> proxy_t
|
||||
{ return cursor_shape_manager_v1_t(p); });
|
||||
}
|
||||
|
||||
cursor_shape_manager_v1_t::cursor_shape_manager_v1_t(proxy_t const &wrapped_proxy, construct_proxy_wrapper_tag /*unused*/)
|
||||
: proxy_t(wrapped_proxy, construct_proxy_wrapper_tag()){
|
||||
set_interface(&cursor_shape_manager_v1_interface);
|
||||
set_copy_constructor([] (const proxy_t &p) -> proxy_t
|
||||
{ return cursor_shape_manager_v1_t(p); });
|
||||
}
|
||||
|
||||
cursor_shape_manager_v1_t cursor_shape_manager_v1_t::proxy_create_wrapper()
|
||||
{
|
||||
return {*this, construct_proxy_wrapper_tag()};
|
||||
}
|
||||
|
||||
const std::string cursor_shape_manager_v1_t::interface_name = "wp_cursor_shape_manager_v1";
|
||||
|
||||
cursor_shape_manager_v1_t::operator wp_cursor_shape_manager_v1*() const
|
||||
{
|
||||
return reinterpret_cast<wp_cursor_shape_manager_v1*> (c_ptr());
|
||||
}
|
||||
|
||||
cursor_shape_device_v1_t cursor_shape_manager_v1_t::get_pointer(pointer_t const& pointer)
|
||||
{
|
||||
proxy_t p = marshal_constructor(1U, &cursor_shape_device_v1_interface, nullptr, pointer.proxy_has_object() ? reinterpret_cast<wl_object*>(pointer.c_ptr()) : nullptr);
|
||||
return cursor_shape_device_v1_t(p);
|
||||
}
|
||||
|
||||
|
||||
cursor_shape_device_v1_t cursor_shape_manager_v1_t::get_tablet_tool_v2(zwp_tablet_tool_v2_t const& tablet_tool)
|
||||
{
|
||||
proxy_t p = marshal_constructor(2U, &cursor_shape_device_v1_interface, nullptr, tablet_tool.proxy_has_object() ? reinterpret_cast<wl_object*>(tablet_tool.c_ptr()) : nullptr);
|
||||
return cursor_shape_device_v1_t(p);
|
||||
}
|
||||
|
||||
|
||||
int cursor_shape_manager_v1_t::dispatcher(uint32_t opcode, const std::vector<any>& args, const std::shared_ptr<detail::events_base_t>& e)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
cursor_shape_device_v1_t::cursor_shape_device_v1_t(const proxy_t &p)
|
||||
: proxy_t(p)
|
||||
{
|
||||
if(proxy_has_object() && get_wrapper_type() == wrapper_type::standard)
|
||||
{
|
||||
set_events(std::shared_ptr<detail::events_base_t>(new events_t), dispatcher);
|
||||
set_destroy_opcode(0U);
|
||||
}
|
||||
set_interface(&cursor_shape_device_v1_interface);
|
||||
set_copy_constructor([] (const proxy_t &p) -> proxy_t
|
||||
{ return cursor_shape_device_v1_t(p); });
|
||||
}
|
||||
|
||||
cursor_shape_device_v1_t::cursor_shape_device_v1_t()
|
||||
{
|
||||
set_interface(&cursor_shape_device_v1_interface);
|
||||
set_copy_constructor([] (const proxy_t &p) -> proxy_t
|
||||
{ return cursor_shape_device_v1_t(p); });
|
||||
}
|
||||
|
||||
cursor_shape_device_v1_t::cursor_shape_device_v1_t(wp_cursor_shape_device_v1 *p, wrapper_type t)
|
||||
: proxy_t(reinterpret_cast<wl_proxy*> (p), t){
|
||||
if(proxy_has_object() && get_wrapper_type() == wrapper_type::standard)
|
||||
{
|
||||
set_events(std::shared_ptr<detail::events_base_t>(new events_t), dispatcher);
|
||||
set_destroy_opcode(0U);
|
||||
}
|
||||
set_interface(&cursor_shape_device_v1_interface);
|
||||
set_copy_constructor([] (const proxy_t &p) -> proxy_t
|
||||
{ return cursor_shape_device_v1_t(p); });
|
||||
}
|
||||
|
||||
cursor_shape_device_v1_t::cursor_shape_device_v1_t(proxy_t const &wrapped_proxy, construct_proxy_wrapper_tag /*unused*/)
|
||||
: proxy_t(wrapped_proxy, construct_proxy_wrapper_tag()){
|
||||
set_interface(&cursor_shape_device_v1_interface);
|
||||
set_copy_constructor([] (const proxy_t &p) -> proxy_t
|
||||
{ return cursor_shape_device_v1_t(p); });
|
||||
}
|
||||
|
||||
cursor_shape_device_v1_t cursor_shape_device_v1_t::proxy_create_wrapper()
|
||||
{
|
||||
return {*this, construct_proxy_wrapper_tag()};
|
||||
}
|
||||
|
||||
const std::string cursor_shape_device_v1_t::interface_name = "wp_cursor_shape_device_v1";
|
||||
|
||||
cursor_shape_device_v1_t::operator wp_cursor_shape_device_v1*() const
|
||||
{
|
||||
return reinterpret_cast<wp_cursor_shape_device_v1*> (c_ptr());
|
||||
}
|
||||
|
||||
void cursor_shape_device_v1_t::set_shape(uint32_t serial, cursor_shape_device_v1_shape const& shape)
|
||||
{
|
||||
marshal(1U, serial, static_cast<uint32_t>(shape));
|
||||
}
|
||||
|
||||
|
||||
int cursor_shape_device_v1_t::dispatcher(uint32_t opcode, const std::vector<any>& args, const std::shared_ptr<detail::events_base_t>& e)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
258
libraries/ZWidget/src/window/wayland/wl_cursor_shape.hpp
Normal file
|
|
@ -0,0 +1,258 @@
|
|||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <wayland-client.hpp>
|
||||
#include <wayland-client-protocol-unstable.hpp>
|
||||
|
||||
struct wp_cursor_shape_manager_v1;
|
||||
struct wp_cursor_shape_device_v1;
|
||||
|
||||
namespace wayland
|
||||
{
|
||||
class cursor_shape_manager_v1_t;
|
||||
class cursor_shape_device_v1_t;
|
||||
enum class cursor_shape_device_v1_shape : uint32_t;
|
||||
enum class cursor_shape_device_v1_error : uint32_t;
|
||||
|
||||
namespace detail
|
||||
{
|
||||
extern const wl_interface cursor_shape_manager_v1_interface;
|
||||
extern const wl_interface cursor_shape_device_v1_interface;
|
||||
}
|
||||
|
||||
/** \brief cursor shape manager
|
||||
|
||||
This global offers an alternative, optional way to set cursor images. This
|
||||
new way uses enumerated cursors instead of a wl_surface like
|
||||
wl_pointer.set_cursor does.
|
||||
|
||||
Warning! The protocol described in this file is currently in the testing
|
||||
phase. Backward compatible changes may be added together with the
|
||||
corresponding interface version bump. Backward incompatible changes can
|
||||
only be done by creating a new major version of the extension.
|
||||
|
||||
*/
|
||||
class cursor_shape_manager_v1_t : public proxy_t
|
||||
{
|
||||
private:
|
||||
struct events_t : public detail::events_base_t
|
||||
{
|
||||
};
|
||||
|
||||
static int dispatcher(uint32_t opcode, const std::vector<detail::any>& args, const std::shared_ptr<detail::events_base_t>& e);
|
||||
|
||||
cursor_shape_manager_v1_t(proxy_t const &wrapped_proxy, construct_proxy_wrapper_tag /*unused*/);
|
||||
|
||||
public:
|
||||
cursor_shape_manager_v1_t();
|
||||
explicit cursor_shape_manager_v1_t(const proxy_t &proxy);
|
||||
cursor_shape_manager_v1_t(wp_cursor_shape_manager_v1 *p, wrapper_type t = wrapper_type::standard);
|
||||
|
||||
cursor_shape_manager_v1_t proxy_create_wrapper();
|
||||
|
||||
static const std::string interface_name;
|
||||
|
||||
operator wp_cursor_shape_manager_v1*() const;
|
||||
|
||||
/** \brief manage the cursor shape of a pointer device
|
||||
\param pointer
|
||||
|
||||
Obtain a wp_cursor_shape_device_v1 for a wl_pointer object.
|
||||
|
||||
When the pointer capability is removed from the wl_seat, the
|
||||
wp_cursor_shape_device_v1 object becomes inert.
|
||||
|
||||
*/
|
||||
cursor_shape_device_v1_t get_pointer(pointer_t const& pointer);
|
||||
|
||||
/** \brief Minimum protocol version required for the \ref get_pointer function
|
||||
*/
|
||||
static constexpr std::uint32_t get_pointer_since_version = 1;
|
||||
|
||||
/** \brief manage the cursor shape of a tablet tool device
|
||||
\param tablet_tool
|
||||
|
||||
Obtain a wp_cursor_shape_device_v1 for a zwp_tablet_tool_v2 object.
|
||||
|
||||
When the zwp_tablet_tool_v2 is removed, the wp_cursor_shape_device_v1
|
||||
object becomes inert.
|
||||
|
||||
*/
|
||||
cursor_shape_device_v1_t get_tablet_tool_v2(zwp_tablet_tool_v2_t const& tablet_tool);
|
||||
|
||||
/** \brief Minimum protocol version required for the \ref get_tablet_tool_v2 function
|
||||
*/
|
||||
static constexpr std::uint32_t get_tablet_tool_v2_since_version = 1;
|
||||
|
||||
};
|
||||
|
||||
|
||||
/** \brief cursor shape for a device
|
||||
|
||||
This interface allows clients to set the cursor shape.
|
||||
|
||||
*/
|
||||
class cursor_shape_device_v1_t : public proxy_t
|
||||
{
|
||||
private:
|
||||
struct events_t : public detail::events_base_t
|
||||
{
|
||||
};
|
||||
|
||||
static int dispatcher(uint32_t opcode, const std::vector<detail::any>& args, const std::shared_ptr<detail::events_base_t>& e);
|
||||
|
||||
cursor_shape_device_v1_t(proxy_t const &wrapped_proxy, construct_proxy_wrapper_tag /*unused*/);
|
||||
|
||||
public:
|
||||
cursor_shape_device_v1_t();
|
||||
explicit cursor_shape_device_v1_t(const proxy_t &proxy);
|
||||
cursor_shape_device_v1_t(wp_cursor_shape_device_v1 *p, wrapper_type t = wrapper_type::standard);
|
||||
|
||||
cursor_shape_device_v1_t proxy_create_wrapper();
|
||||
|
||||
static const std::string interface_name;
|
||||
|
||||
operator wp_cursor_shape_device_v1*() const;
|
||||
|
||||
/** \brief set device cursor to the shape
|
||||
\param serial serial number of the enter event
|
||||
\param shape
|
||||
|
||||
Sets the device cursor to the specified shape. The compositor will
|
||||
change the cursor image based on the specified shape.
|
||||
|
||||
The cursor actually changes only if the input device focus is one of
|
||||
the requesting client's surfaces. If any, the previous cursor image
|
||||
(surface or shape) is replaced.
|
||||
|
||||
The "shape" argument must be a valid enum entry, otherwise the
|
||||
invalid_shape protocol error is raised.
|
||||
|
||||
This is similar to the wl_pointer.set_cursor and
|
||||
zwp_tablet_tool_v2.set_cursor requests, but this request accepts a
|
||||
shape instead of contents in the form of a surface. Clients can mix
|
||||
set_cursor and set_shape requests.
|
||||
|
||||
The serial parameter must match the latest wl_pointer.enter or
|
||||
zwp_tablet_tool_v2.proximity_in serial number sent to the client.
|
||||
Otherwise the request will be ignored.
|
||||
|
||||
*/
|
||||
void set_shape(uint32_t serial, cursor_shape_device_v1_shape const& shape);
|
||||
|
||||
/** \brief Minimum protocol version required for the \ref set_shape function
|
||||
*/
|
||||
static constexpr std::uint32_t set_shape_since_version = 1;
|
||||
|
||||
};
|
||||
|
||||
/** \brief cursor shapes
|
||||
|
||||
This enum describes cursor shapes.
|
||||
|
||||
The names are taken from the CSS W3C specification:
|
||||
https://w3c.github.io/csswg-drafts/css-ui/#cursor
|
||||
with a few additions.
|
||||
|
||||
Note that there are some groups of cursor shapes that are related:
|
||||
The first group is drag-and-drop cursors which are used to indicate
|
||||
the selected action during dnd operations. The second group is resize
|
||||
cursors which are used to indicate resizing and moving possibilities
|
||||
on window borders. It is recommended that the shapes in these groups
|
||||
should use visually compatible images and metaphors.
|
||||
|
||||
*/
|
||||
enum class cursor_shape_device_v1_shape : uint32_t
|
||||
{
|
||||
/** \brief default cursor */
|
||||
_default = 1,
|
||||
/** \brief a context menu is available for the object under the cursor */
|
||||
context_menu = 2,
|
||||
/** \brief help is available for the object under the cursor */
|
||||
help = 3,
|
||||
/** \brief pointer that indicates a link or another interactive element */
|
||||
pointer = 4,
|
||||
/** \brief progress indicator */
|
||||
progress = 5,
|
||||
/** \brief program is busy, user should wait */
|
||||
wait = 6,
|
||||
/** \brief a cell or set of cells may be selected */
|
||||
cell = 7,
|
||||
/** \brief simple crosshair */
|
||||
crosshair = 8,
|
||||
/** \brief text may be selected */
|
||||
text = 9,
|
||||
/** \brief vertical text may be selected */
|
||||
vertical_text = 10,
|
||||
/** \brief drag-and-drop: alias of/shortcut to something is to be created */
|
||||
alias = 11,
|
||||
/** \brief drag-and-drop: something is to be copied */
|
||||
copy = 12,
|
||||
/** \brief drag-and-drop: something is to be moved */
|
||||
move = 13,
|
||||
/** \brief drag-and-drop: the dragged item cannot be dropped at the current cursor location */
|
||||
no_drop = 14,
|
||||
/** \brief drag-and-drop: the requested action will not be carried out */
|
||||
not_allowed = 15,
|
||||
/** \brief drag-and-drop: something can be grabbed */
|
||||
grab = 16,
|
||||
/** \brief drag-and-drop: something is being grabbed */
|
||||
grabbing = 17,
|
||||
/** \brief resizing: the east border is to be moved */
|
||||
e_resize = 18,
|
||||
/** \brief resizing: the north border is to be moved */
|
||||
n_resize = 19,
|
||||
/** \brief resizing: the north-east corner is to be moved */
|
||||
ne_resize = 20,
|
||||
/** \brief resizing: the north-west corner is to be moved */
|
||||
nw_resize = 21,
|
||||
/** \brief resizing: the south border is to be moved */
|
||||
s_resize = 22,
|
||||
/** \brief resizing: the south-east corner is to be moved */
|
||||
se_resize = 23,
|
||||
/** \brief resizing: the south-west corner is to be moved */
|
||||
sw_resize = 24,
|
||||
/** \brief resizing: the west border is to be moved */
|
||||
w_resize = 25,
|
||||
/** \brief resizing: the east and west borders are to be moved */
|
||||
ew_resize = 26,
|
||||
/** \brief resizing: the north and south borders are to be moved */
|
||||
ns_resize = 27,
|
||||
/** \brief resizing: the north-east and south-west corners are to be moved */
|
||||
nesw_resize = 28,
|
||||
/** \brief resizing: the north-west and south-east corners are to be moved */
|
||||
nwse_resize = 29,
|
||||
/** \brief resizing: that the item/column can be resized horizontally */
|
||||
col_resize = 30,
|
||||
/** \brief resizing: that the item/row can be resized vertically */
|
||||
row_resize = 31,
|
||||
/** \brief something can be scrolled in any direction */
|
||||
all_scroll = 32,
|
||||
/** \brief something can be zoomed in */
|
||||
zoom_in = 33,
|
||||
/** \brief something can be zoomed out */
|
||||
zoom_out = 34,
|
||||
/** \brief drag-and-drop: the user will select which action will be carried out (non-css value) */
|
||||
dnd_ask = 35,
|
||||
/** \brief resizing: something can be moved or resized in any direction (non-css value) */
|
||||
all_resize = 36
|
||||
};
|
||||
|
||||
/** \brief
|
||||
|
||||
*/
|
||||
enum class cursor_shape_device_v1_error : uint32_t
|
||||
{
|
||||
/** \brief the specified shape value is invalid */
|
||||
invalid_shape = 1
|
||||
};
|
||||
|
||||
|
||||
|
||||
}
|
||||
267
libraries/ZWidget/src/window/wayland/wl_xdg_toplevel_icon.cpp
Normal file
|
|
@ -0,0 +1,267 @@
|
|||
#include "wl_xdg_toplevel_icon.hpp"
|
||||
|
||||
using namespace wayland;
|
||||
using namespace wayland::detail;
|
||||
|
||||
const wl_interface* xdg_toplevel_icon_manager_v1_interface_destroy_request[0] = {
|
||||
};
|
||||
|
||||
const wl_interface* xdg_toplevel_icon_manager_v1_interface_create_icon_request[1] = {
|
||||
&xdg_toplevel_icon_v1_interface,
|
||||
};
|
||||
|
||||
const wl_interface* xdg_toplevel_icon_manager_v1_interface_set_icon_request[2] = {
|
||||
&xdg_toplevel_interface,
|
||||
&xdg_toplevel_icon_v1_interface,
|
||||
};
|
||||
|
||||
const wl_interface* xdg_toplevel_icon_manager_v1_interface_icon_size_event[1] = {
|
||||
nullptr,
|
||||
};
|
||||
|
||||
const wl_interface* xdg_toplevel_icon_manager_v1_interface_done_event[0] = {
|
||||
};
|
||||
|
||||
const wl_message xdg_toplevel_icon_manager_v1_interface_requests[3] = {
|
||||
{
|
||||
"destroy",
|
||||
"",
|
||||
xdg_toplevel_icon_manager_v1_interface_destroy_request,
|
||||
},
|
||||
{
|
||||
"create_icon",
|
||||
"n",
|
||||
xdg_toplevel_icon_manager_v1_interface_create_icon_request,
|
||||
},
|
||||
{
|
||||
"set_icon",
|
||||
"o?o",
|
||||
xdg_toplevel_icon_manager_v1_interface_set_icon_request,
|
||||
},
|
||||
};
|
||||
|
||||
const wl_message xdg_toplevel_icon_manager_v1_interface_events[2] = {
|
||||
{
|
||||
"icon_size",
|
||||
"i",
|
||||
xdg_toplevel_icon_manager_v1_interface_icon_size_event,
|
||||
},
|
||||
{
|
||||
"done",
|
||||
"",
|
||||
xdg_toplevel_icon_manager_v1_interface_done_event,
|
||||
},
|
||||
};
|
||||
|
||||
const wl_interface wayland::detail::xdg_toplevel_icon_manager_v1_interface =
|
||||
{
|
||||
"xdg_toplevel_icon_manager_v1",
|
||||
1,
|
||||
3,
|
||||
xdg_toplevel_icon_manager_v1_interface_requests,
|
||||
2,
|
||||
xdg_toplevel_icon_manager_v1_interface_events,
|
||||
};
|
||||
|
||||
const wl_interface* xdg_toplevel_icon_v1_interface_destroy_request[0] = {
|
||||
};
|
||||
|
||||
const wl_interface* xdg_toplevel_icon_v1_interface_set_name_request[1] = {
|
||||
nullptr,
|
||||
};
|
||||
|
||||
const wl_interface* xdg_toplevel_icon_v1_interface_add_buffer_request[2] = {
|
||||
&buffer_interface,
|
||||
nullptr,
|
||||
};
|
||||
|
||||
const wl_message xdg_toplevel_icon_v1_interface_requests[3] = {
|
||||
{
|
||||
"destroy",
|
||||
"",
|
||||
xdg_toplevel_icon_v1_interface_destroy_request,
|
||||
},
|
||||
{
|
||||
"set_name",
|
||||
"s",
|
||||
xdg_toplevel_icon_v1_interface_set_name_request,
|
||||
},
|
||||
{
|
||||
"add_buffer",
|
||||
"oi",
|
||||
xdg_toplevel_icon_v1_interface_add_buffer_request,
|
||||
},
|
||||
};
|
||||
|
||||
const wl_message xdg_toplevel_icon_v1_interface_events[0] = {
|
||||
};
|
||||
|
||||
const wl_interface wayland::detail::xdg_toplevel_icon_v1_interface =
|
||||
{
|
||||
"xdg_toplevel_icon_v1",
|
||||
1,
|
||||
3,
|
||||
xdg_toplevel_icon_v1_interface_requests,
|
||||
0,
|
||||
xdg_toplevel_icon_v1_interface_events,
|
||||
};
|
||||
|
||||
xdg_toplevel_icon_manager_v1_t::xdg_toplevel_icon_manager_v1_t(const proxy_t &p)
|
||||
: proxy_t(p)
|
||||
{
|
||||
if(proxy_has_object() && get_wrapper_type() == wrapper_type::standard)
|
||||
{
|
||||
set_events(std::shared_ptr<detail::events_base_t>(new events_t), dispatcher);
|
||||
set_destroy_opcode(0U);
|
||||
}
|
||||
set_interface(&xdg_toplevel_icon_manager_v1_interface);
|
||||
set_copy_constructor([] (const proxy_t &p) -> proxy_t
|
||||
{ return xdg_toplevel_icon_manager_v1_t(p); });
|
||||
}
|
||||
|
||||
xdg_toplevel_icon_manager_v1_t::xdg_toplevel_icon_manager_v1_t()
|
||||
{
|
||||
set_interface(&xdg_toplevel_icon_manager_v1_interface);
|
||||
set_copy_constructor([] (const proxy_t &p) -> proxy_t
|
||||
{ return xdg_toplevel_icon_manager_v1_t(p); });
|
||||
}
|
||||
|
||||
xdg_toplevel_icon_manager_v1_t::xdg_toplevel_icon_manager_v1_t(xdg_toplevel_icon_manager_v1 *p, wrapper_type t)
|
||||
: proxy_t(reinterpret_cast<wl_proxy*> (p), t){
|
||||
if(proxy_has_object() && get_wrapper_type() == wrapper_type::standard)
|
||||
{
|
||||
set_events(std::shared_ptr<detail::events_base_t>(new events_t), dispatcher);
|
||||
set_destroy_opcode(0U);
|
||||
}
|
||||
set_interface(&xdg_toplevel_icon_manager_v1_interface);
|
||||
set_copy_constructor([] (const proxy_t &p) -> proxy_t
|
||||
{ return xdg_toplevel_icon_manager_v1_t(p); });
|
||||
}
|
||||
|
||||
xdg_toplevel_icon_manager_v1_t::xdg_toplevel_icon_manager_v1_t(proxy_t const &wrapped_proxy, construct_proxy_wrapper_tag /*unused*/)
|
||||
: proxy_t(wrapped_proxy, construct_proxy_wrapper_tag()){
|
||||
set_interface(&xdg_toplevel_icon_manager_v1_interface);
|
||||
set_copy_constructor([] (const proxy_t &p) -> proxy_t
|
||||
{ return xdg_toplevel_icon_manager_v1_t(p); });
|
||||
}
|
||||
|
||||
xdg_toplevel_icon_manager_v1_t xdg_toplevel_icon_manager_v1_t::proxy_create_wrapper()
|
||||
{
|
||||
return {*this, construct_proxy_wrapper_tag()};
|
||||
}
|
||||
|
||||
const std::string xdg_toplevel_icon_manager_v1_t::interface_name = "xdg_toplevel_icon_manager_v1";
|
||||
|
||||
xdg_toplevel_icon_manager_v1_t::operator xdg_toplevel_icon_manager_v1*() const
|
||||
{
|
||||
return reinterpret_cast<xdg_toplevel_icon_manager_v1*> (c_ptr());
|
||||
}
|
||||
|
||||
xdg_toplevel_icon_v1_t xdg_toplevel_icon_manager_v1_t::create_icon()
|
||||
{
|
||||
proxy_t p = marshal_constructor(1U, &xdg_toplevel_icon_v1_interface, nullptr);
|
||||
return xdg_toplevel_icon_v1_t(p);
|
||||
}
|
||||
|
||||
|
||||
void xdg_toplevel_icon_manager_v1_t::set_icon(xdg_toplevel_t const& toplevel, xdg_toplevel_icon_v1_t const& icon)
|
||||
{
|
||||
marshal(2U, toplevel.proxy_has_object() ? reinterpret_cast<wl_object*>(toplevel.c_ptr()) : nullptr, icon.proxy_has_object() ? reinterpret_cast<wl_object*>(icon.c_ptr()) : nullptr);
|
||||
}
|
||||
|
||||
|
||||
std::function<void(int32_t)> &xdg_toplevel_icon_manager_v1_t::on_icon_size()
|
||||
{
|
||||
return std::static_pointer_cast<events_t>(get_events())->icon_size;
|
||||
}
|
||||
|
||||
std::function<void()> &xdg_toplevel_icon_manager_v1_t::on_done()
|
||||
{
|
||||
return std::static_pointer_cast<events_t>(get_events())->done;
|
||||
}
|
||||
|
||||
int xdg_toplevel_icon_manager_v1_t::dispatcher(uint32_t opcode, const std::vector<any>& args, const std::shared_ptr<detail::events_base_t>& e)
|
||||
{
|
||||
std::shared_ptr<events_t> events = std::static_pointer_cast<events_t>(e);
|
||||
switch(opcode)
|
||||
{
|
||||
case 0:
|
||||
if(events->icon_size) events->icon_size(args[0].get<int32_t>());
|
||||
break;
|
||||
case 1:
|
||||
if(events->done) events->done();
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
xdg_toplevel_icon_v1_t::xdg_toplevel_icon_v1_t(const proxy_t &p)
|
||||
: proxy_t(p)
|
||||
{
|
||||
if(proxy_has_object() && get_wrapper_type() == wrapper_type::standard)
|
||||
{
|
||||
set_events(std::shared_ptr<detail::events_base_t>(new events_t), dispatcher);
|
||||
set_destroy_opcode(0U);
|
||||
}
|
||||
set_interface(&xdg_toplevel_icon_v1_interface);
|
||||
set_copy_constructor([] (const proxy_t &p) -> proxy_t
|
||||
{ return xdg_toplevel_icon_v1_t(p); });
|
||||
}
|
||||
|
||||
xdg_toplevel_icon_v1_t::xdg_toplevel_icon_v1_t()
|
||||
{
|
||||
set_interface(&xdg_toplevel_icon_v1_interface);
|
||||
set_copy_constructor([] (const proxy_t &p) -> proxy_t
|
||||
{ return xdg_toplevel_icon_v1_t(p); });
|
||||
}
|
||||
|
||||
xdg_toplevel_icon_v1_t::xdg_toplevel_icon_v1_t(xdg_toplevel_icon_v1 *p, wrapper_type t)
|
||||
: proxy_t(reinterpret_cast<wl_proxy*> (p), t){
|
||||
if(proxy_has_object() && get_wrapper_type() == wrapper_type::standard)
|
||||
{
|
||||
set_events(std::shared_ptr<detail::events_base_t>(new events_t), dispatcher);
|
||||
set_destroy_opcode(0U);
|
||||
}
|
||||
set_interface(&xdg_toplevel_icon_v1_interface);
|
||||
set_copy_constructor([] (const proxy_t &p) -> proxy_t
|
||||
{ return xdg_toplevel_icon_v1_t(p); });
|
||||
}
|
||||
|
||||
xdg_toplevel_icon_v1_t::xdg_toplevel_icon_v1_t(proxy_t const &wrapped_proxy, construct_proxy_wrapper_tag /*unused*/)
|
||||
: proxy_t(wrapped_proxy, construct_proxy_wrapper_tag()){
|
||||
set_interface(&xdg_toplevel_icon_v1_interface);
|
||||
set_copy_constructor([] (const proxy_t &p) -> proxy_t
|
||||
{ return xdg_toplevel_icon_v1_t(p); });
|
||||
}
|
||||
|
||||
xdg_toplevel_icon_v1_t xdg_toplevel_icon_v1_t::proxy_create_wrapper()
|
||||
{
|
||||
return {*this, construct_proxy_wrapper_tag()};
|
||||
}
|
||||
|
||||
const std::string xdg_toplevel_icon_v1_t::interface_name = "xdg_toplevel_icon_v1";
|
||||
|
||||
xdg_toplevel_icon_v1_t::operator xdg_toplevel_icon_v1*() const
|
||||
{
|
||||
return reinterpret_cast<xdg_toplevel_icon_v1*> (c_ptr());
|
||||
}
|
||||
|
||||
void xdg_toplevel_icon_v1_t::set_name(std::string const& icon_name)
|
||||
{
|
||||
marshal(1U, icon_name);
|
||||
}
|
||||
|
||||
|
||||
void xdg_toplevel_icon_v1_t::add_buffer(buffer_t const& buffer, int32_t scale)
|
||||
{
|
||||
marshal(2U, buffer.proxy_has_object() ? reinterpret_cast<wl_object*>(buffer.c_ptr()) : nullptr, scale);
|
||||
}
|
||||
|
||||
|
||||
int xdg_toplevel_icon_v1_t::dispatcher(uint32_t opcode, const std::vector<any>& args, const std::shared_ptr<detail::events_base_t>& e)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
244
libraries/ZWidget/src/window/wayland/wl_xdg_toplevel_icon.hpp
Normal file
|
|
@ -0,0 +1,244 @@
|
|||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <wayland-client.hpp>
|
||||
#include <wayland-client-protocol-extra.hpp>
|
||||
|
||||
struct xdg_toplevel_icon_manager_v1;
|
||||
struct xdg_toplevel_icon_v1;
|
||||
|
||||
namespace wayland
|
||||
{
|
||||
class xdg_toplevel_icon_manager_v1_t;
|
||||
class xdg_toplevel_icon_v1_t;
|
||||
enum class xdg_toplevel_icon_v1_error : uint32_t;
|
||||
|
||||
namespace detail
|
||||
{
|
||||
extern const wl_interface xdg_toplevel_icon_manager_v1_interface;
|
||||
extern const wl_interface xdg_toplevel_icon_v1_interface;
|
||||
}
|
||||
|
||||
/** \brief interface to manage toplevel icons
|
||||
|
||||
This interface allows clients to create toplevel window icons and set
|
||||
them on toplevel windows to be displayed to the user.
|
||||
|
||||
*/
|
||||
class xdg_toplevel_icon_manager_v1_t : public proxy_t
|
||||
{
|
||||
private:
|
||||
struct events_t : public detail::events_base_t
|
||||
{
|
||||
std::function<void(int32_t)> icon_size;
|
||||
std::function<void()> done;
|
||||
};
|
||||
|
||||
static int dispatcher(uint32_t opcode, const std::vector<detail::any>& args, const std::shared_ptr<detail::events_base_t>& e);
|
||||
|
||||
xdg_toplevel_icon_manager_v1_t(proxy_t const &wrapped_proxy, construct_proxy_wrapper_tag /*unused*/);
|
||||
|
||||
public:
|
||||
xdg_toplevel_icon_manager_v1_t();
|
||||
explicit xdg_toplevel_icon_manager_v1_t(const proxy_t &proxy);
|
||||
xdg_toplevel_icon_manager_v1_t(xdg_toplevel_icon_manager_v1 *p, wrapper_type t = wrapper_type::standard);
|
||||
|
||||
xdg_toplevel_icon_manager_v1_t proxy_create_wrapper();
|
||||
|
||||
static const std::string interface_name;
|
||||
|
||||
operator xdg_toplevel_icon_manager_v1*() const;
|
||||
|
||||
/** \brief create a new icon instance
|
||||
|
||||
Creates a new icon object. This icon can then be attached to a
|
||||
xdg_toplevel via the 'set_icon' request.
|
||||
|
||||
*/
|
||||
xdg_toplevel_icon_v1_t create_icon();
|
||||
|
||||
/** \brief Minimum protocol version required for the \ref create_icon function
|
||||
*/
|
||||
static constexpr std::uint32_t create_icon_since_version = 1;
|
||||
|
||||
/** \brief set an icon on a toplevel window
|
||||
\param toplevel the toplevel to act on
|
||||
\param icon
|
||||
|
||||
This request assigns the icon 'icon' to 'toplevel', or clears the
|
||||
toplevel icon if 'icon' was null.
|
||||
This state is double-buffered and is applied on the next
|
||||
wl_surface.commit of the toplevel.
|
||||
|
||||
After making this call, the xdg_toplevel_icon_v1 provided as 'icon'
|
||||
can be destroyed by the client without 'toplevel' losing its icon.
|
||||
The xdg_toplevel_icon_v1 is immutable from this point, and any
|
||||
future attempts to change it must raise the
|
||||
'xdg_toplevel_icon_v1.immutable' protocol error.
|
||||
|
||||
The compositor must set the toplevel icon from either the pixel data
|
||||
the icon provides, or by loading a stock icon using the icon name.
|
||||
See the description of 'xdg_toplevel_icon_v1' for details.
|
||||
|
||||
If 'icon' is set to null, the icon of the respective toplevel is reset
|
||||
to its default icon (usually the icon of the application, derived from
|
||||
its desktop-entry file, or a placeholder icon).
|
||||
If this request is passed an icon with no pixel buffers or icon name
|
||||
assigned, the icon must be reset just like if 'icon' was null.
|
||||
|
||||
*/
|
||||
void set_icon(xdg_toplevel_t const& toplevel, xdg_toplevel_icon_v1_t const& icon);
|
||||
|
||||
/** \brief Minimum protocol version required for the \ref set_icon function
|
||||
*/
|
||||
static constexpr std::uint32_t set_icon_since_version = 1;
|
||||
|
||||
/** \brief describes a supported & preferred icon size
|
||||
\param size the edge size of the square icon in surface-local coordinates, e.g. 64
|
||||
|
||||
This event indicates an icon size the compositor prefers to be
|
||||
available if the client has scalable icons and can render to any size.
|
||||
|
||||
When the 'xdg_toplevel_icon_manager_v1' object is created, the
|
||||
compositor may send one or more 'icon_size' events to describe the list
|
||||
of preferred icon sizes. If the compositor has no size preference, it
|
||||
may not send any 'icon_size' event, and it is up to the client to
|
||||
decide a suitable icon size.
|
||||
|
||||
A sequence of 'icon_size' events must be finished with a 'done' event.
|
||||
If the compositor has no size preferences, it must still send the
|
||||
'done' event, without any preceding 'icon_size' events.
|
||||
|
||||
*/
|
||||
std::function<void(int32_t)> &on_icon_size();
|
||||
|
||||
/** \brief all information has been sent
|
||||
|
||||
This event is sent after all 'icon_size' events have been sent.
|
||||
|
||||
*/
|
||||
std::function<void()> &on_done();
|
||||
|
||||
};
|
||||
|
||||
|
||||
/** \brief a toplevel window icon
|
||||
|
||||
This interface defines a toplevel icon.
|
||||
An icon can have a name, and multiple buffers.
|
||||
In order to be applied, the icon must have either a name, or at least
|
||||
one buffer assigned. Applying an empty icon (with no buffer or name) to
|
||||
a toplevel should reset its icon to the default icon.
|
||||
|
||||
It is up to compositor policy whether to prefer using a buffer or loading
|
||||
an icon via its name. See 'set_name' and 'add_buffer' for details.
|
||||
|
||||
*/
|
||||
class xdg_toplevel_icon_v1_t : public proxy_t
|
||||
{
|
||||
private:
|
||||
struct events_t : public detail::events_base_t
|
||||
{
|
||||
};
|
||||
|
||||
static int dispatcher(uint32_t opcode, const std::vector<detail::any>& args, const std::shared_ptr<detail::events_base_t>& e);
|
||||
|
||||
xdg_toplevel_icon_v1_t(proxy_t const &wrapped_proxy, construct_proxy_wrapper_tag /*unused*/);
|
||||
|
||||
public:
|
||||
xdg_toplevel_icon_v1_t();
|
||||
explicit xdg_toplevel_icon_v1_t(const proxy_t &proxy);
|
||||
xdg_toplevel_icon_v1_t(xdg_toplevel_icon_v1 *p, wrapper_type t = wrapper_type::standard);
|
||||
|
||||
xdg_toplevel_icon_v1_t proxy_create_wrapper();
|
||||
|
||||
static const std::string interface_name;
|
||||
|
||||
operator xdg_toplevel_icon_v1*() const;
|
||||
|
||||
/** \brief set an icon name
|
||||
\param icon_name
|
||||
|
||||
This request assigns an icon name to this icon.
|
||||
Any previously set name is overridden.
|
||||
|
||||
The compositor must resolve 'icon_name' according to the lookup rules
|
||||
described in the XDG icon theme specification[1] using the
|
||||
environment's current icon theme.
|
||||
|
||||
If the compositor does not support icon names or cannot resolve
|
||||
'icon_name' according to the XDG icon theme specification it must
|
||||
fall back to using pixel buffer data instead.
|
||||
|
||||
If this request is made after the icon has been assigned to a toplevel
|
||||
via 'set_icon', a 'immutable' error must be raised.
|
||||
|
||||
[1]: https://specifications.freedesktop.org/icon-theme-spec/icon-theme-spec-latest.html
|
||||
|
||||
*/
|
||||
void set_name(std::string const& icon_name);
|
||||
|
||||
/** \brief Minimum protocol version required for the \ref set_name function
|
||||
*/
|
||||
static constexpr std::uint32_t set_name_since_version = 1;
|
||||
|
||||
/** \brief add icon data from a pixel buffer
|
||||
\param buffer
|
||||
\param scale the scaling factor of the icon, e.g. 1
|
||||
|
||||
This request adds pixel data supplied as wl_buffer to the icon.
|
||||
|
||||
The client should add pixel data for all icon sizes and scales that
|
||||
it can provide, or which are explicitly requested by the compositor
|
||||
via 'icon_size' events on xdg_toplevel_icon_manager_v1.
|
||||
|
||||
The wl_buffer supplying pixel data as 'buffer' must be backed by wl_shm
|
||||
and must be a square (width and height being equal).
|
||||
If any of these buffer requirements are not fulfilled, a 'invalid_buffer'
|
||||
error must be raised.
|
||||
|
||||
If this icon instance already has a buffer of the same size and scale
|
||||
from a previous 'add_buffer' request, data from the last request
|
||||
overrides the preexisting pixel data.
|
||||
|
||||
The wl_buffer must be kept alive for as long as the xdg_toplevel_icon
|
||||
it is associated with is not destroyed, otherwise a 'no_buffer' error
|
||||
is raised. The buffer contents must not be modified after it was
|
||||
assigned to the icon. As a result, the region of the wl_shm_pool's
|
||||
backing storage used for the wl_buffer must not be modified after this
|
||||
request is sent. The wl_buffer.release event is unused.
|
||||
|
||||
If this request is made after the icon has been assigned to a toplevel
|
||||
via 'set_icon', a 'immutable' error must be raised.
|
||||
|
||||
*/
|
||||
void add_buffer(buffer_t const& buffer, int32_t scale);
|
||||
|
||||
/** \brief Minimum protocol version required for the \ref add_buffer function
|
||||
*/
|
||||
static constexpr std::uint32_t add_buffer_since_version = 1;
|
||||
|
||||
};
|
||||
|
||||
/** \brief
|
||||
|
||||
*/
|
||||
enum class xdg_toplevel_icon_v1_error : uint32_t
|
||||
{
|
||||
/** \brief the provided buffer does not satisfy requirements */
|
||||
invalid_buffer = 1,
|
||||
/** \brief the icon has already been assigned to a toplevel and must not be changed */
|
||||
immutable = 2,
|
||||
/** \brief the provided buffer has been destroyed before the toplevel icon */
|
||||
no_buffer = 3
|
||||
};
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
|
||||
#include "win32_display_window.h"
|
||||
#include <zwidget/core/image.h>
|
||||
#include <windowsx.h>
|
||||
#include <stdexcept>
|
||||
#include <cmath>
|
||||
|
|
@ -30,11 +31,11 @@
|
|||
|
||||
// Code for delay loading DPI related functions, needed for continued Windows 7 compatibility.
|
||||
typedef BOOL (WINAPI *PFN_AdjustWindowRectExForDpi)(
|
||||
LPRECT lpRect,
|
||||
DWORD dwStyle,
|
||||
BOOL bMenu,
|
||||
DWORD dwExStyle,
|
||||
UINT dpi
|
||||
LPRECT lpRect,
|
||||
DWORD dwStyle,
|
||||
BOOL bMenu,
|
||||
DWORD dwExStyle,
|
||||
UINT dpi
|
||||
);
|
||||
|
||||
typedef UINT (WINAPI *PFN_GetDpiForWindow)(HWND hwnd);
|
||||
|
|
@ -44,39 +45,36 @@ static PFN_GetDpiForWindow pGetDpiForWindow = nullptr;
|
|||
|
||||
static void DPIDelayLoad()
|
||||
{
|
||||
HMODULE hUser32 = GetModuleHandleW(L"user32.dll");
|
||||
if (hUser32) {
|
||||
pAdjustWindowRectExForDpi = (PFN_AdjustWindowRectExForDpi)
|
||||
GetProcAddress(hUser32, "AdjustWindowRectExForDpi");
|
||||
pGetDpiForWindow = (PFN_GetDpiForWindow)
|
||||
GetProcAddress(hUser32, "GetDpiForWindow");
|
||||
}
|
||||
HMODULE hUser32 = GetModuleHandleW(L"user32.dll");
|
||||
if (hUser32)
|
||||
{
|
||||
pAdjustWindowRectExForDpi = (PFN_AdjustWindowRectExForDpi)GetProcAddress(hUser32, "AdjustWindowRectExForDpi");
|
||||
pGetDpiForWindow = (PFN_GetDpiForWindow)GetProcAddress(hUser32, "GetDpiForWindow");
|
||||
}
|
||||
}
|
||||
|
||||
static BOOL DelayLoadAdjustWindowRectExForDpi(
|
||||
LPRECT lpRect,
|
||||
DWORD dwStyle,
|
||||
BOOL bMenu,
|
||||
DWORD dwExStyle,
|
||||
HWND hwnd
|
||||
) {
|
||||
static BOOL DelayLoadAdjustWindowRectExForDpi(LPRECT lpRect, DWORD dwStyle, BOOL bMenu, DWORD dwExStyle, HWND hwnd)
|
||||
{
|
||||
DPIDelayLoad();
|
||||
if (pAdjustWindowRectExForDpi) {
|
||||
return pAdjustWindowRectExForDpi(lpRect, dwStyle, bMenu, dwExStyle,
|
||||
pGetDpiForWindow(hwnd));
|
||||
} else {
|
||||
return AdjustWindowRectEx(lpRect, dwStyle, bMenu, dwExStyle);
|
||||
}
|
||||
if (pAdjustWindowRectExForDpi)
|
||||
{
|
||||
return pAdjustWindowRectExForDpi(lpRect, dwStyle, bMenu, dwExStyle,
|
||||
pGetDpiForWindow(hwnd));
|
||||
}
|
||||
else
|
||||
{
|
||||
return AdjustWindowRectEx(lpRect, dwStyle, bMenu, dwExStyle);
|
||||
}
|
||||
}
|
||||
|
||||
static double DelayLoadGetDpiScale(HWND hwnd)
|
||||
{
|
||||
DPIDelayLoad();
|
||||
if (pGetDpiForWindow) {
|
||||
return pGetDpiForWindow(hwnd) / 96.0;
|
||||
} else {
|
||||
return 1.0;
|
||||
}
|
||||
if (pGetDpiForWindow) {
|
||||
return pGetDpiForWindow(hwnd) / 96.0;
|
||||
} else {
|
||||
return 1.0;
|
||||
}
|
||||
}
|
||||
|
||||
Win32DisplayWindow::Win32DisplayWindow(DisplayWindowHost* windowHost, bool popupWindow, Win32DisplayWindow* owner, RenderAPI renderAPI) : WindowHost(windowHost), PopupWindow(popupWindow)
|
||||
|
|
@ -120,6 +118,11 @@ Win32DisplayWindow::~Win32DisplayWindow()
|
|||
WindowHandle.hwnd = 0;
|
||||
}
|
||||
|
||||
if (SmallIcon)
|
||||
DestroyCursor(SmallIcon);
|
||||
if (LargeIcon)
|
||||
DestroyCursor(LargeIcon);
|
||||
|
||||
Windows.erase(WindowsIterator);
|
||||
}
|
||||
|
||||
|
|
@ -128,6 +131,95 @@ void Win32DisplayWindow::SetWindowTitle(const std::string& text)
|
|||
SetWindowText(WindowHandle.hwnd, to_utf16(text).c_str());
|
||||
}
|
||||
|
||||
static HICON CreateIconFromImageList(HDC hdc, const std::vector<std::shared_ptr<Image>>& images, int desiredSize)
|
||||
{
|
||||
if (images.empty())
|
||||
return 0;
|
||||
|
||||
Image* image = images.front().get();
|
||||
for (const auto& i : images)
|
||||
{
|
||||
int curdist = std::abs(image->GetWidth() - desiredSize);
|
||||
int dist = std::abs(i->GetWidth() - desiredSize);
|
||||
if (dist < curdist)
|
||||
image = i.get();
|
||||
}
|
||||
|
||||
int width = image->GetWidth();
|
||||
int height = image->GetHeight();
|
||||
std::vector<uint32_t> pixels(width * height * 4);
|
||||
|
||||
if (image->GetFormat() == ImageFormat::R8G8B8A8)
|
||||
{
|
||||
int count = width * height;
|
||||
const uint32_t* src = (const uint32_t*)image->GetData();
|
||||
uint32_t* dest = pixels.data();
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
uint32_t r = src[i] & 0xff;
|
||||
uint32_t g = (src[i] >> 8) & 0xff;
|
||||
uint32_t b = (src[i] >> 16) & 0xff;
|
||||
uint32_t a = (src[i] >> 24) & 0xff;
|
||||
dest[i] = (a << 24) | (r << 16) | (g << 8) | b;
|
||||
}
|
||||
}
|
||||
else if (image->GetFormat() == ImageFormat::B8G8R8A8)
|
||||
{
|
||||
memcpy(pixels.data(), image->GetData(), width * height * 4);
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
BITMAPV5HEADER bmp_header = {};
|
||||
bmp_header.bV5Size = sizeof(BITMAPV5HEADER);
|
||||
bmp_header.bV5Width = width;
|
||||
bmp_header.bV5Height = height;
|
||||
bmp_header.bV5Planes = 1;
|
||||
bmp_header.bV5BitCount = 32;
|
||||
bmp_header.bV5Compression = BI_RGB;
|
||||
|
||||
HBITMAP bitmap = CreateDIBitmap(hdc, (BITMAPINFOHEADER*)&bmp_header, CBM_INIT, pixels.data(), (BITMAPINFO*)&bmp_header, DIB_RGB_COLORS);
|
||||
if (!bitmap)
|
||||
return 0;
|
||||
|
||||
ICONINFO iconinfo = {};
|
||||
iconinfo.fIcon = TRUE;
|
||||
iconinfo.hbmColor = bitmap;
|
||||
iconinfo.hbmMask = bitmap;
|
||||
HICON icon = CreateIconIndirect(&iconinfo);
|
||||
DeleteObject(bitmap);
|
||||
return icon;
|
||||
}
|
||||
|
||||
void Win32DisplayWindow::SetWindowIcon(const std::vector<std::shared_ptr<Image>>& images)
|
||||
{
|
||||
double dpiScale = GetDpiScale();
|
||||
|
||||
if (SmallIcon)
|
||||
{
|
||||
DestroyCursor(SmallIcon);
|
||||
SmallIcon = {};
|
||||
}
|
||||
if (LargeIcon)
|
||||
{
|
||||
DestroyCursor(LargeIcon);
|
||||
LargeIcon = {};
|
||||
}
|
||||
|
||||
HDC hdc = GetDC(WindowHandle.hwnd);
|
||||
if (hdc)
|
||||
{
|
||||
SmallIcon = CreateIconFromImageList(hdc, images, (int)std::round(16 * dpiScale));
|
||||
LargeIcon = CreateIconFromImageList(hdc, images, (int)std::round(32 * dpiScale));
|
||||
ReleaseDC(WindowHandle.hwnd, hdc);
|
||||
}
|
||||
|
||||
SendMessage(WindowHandle.hwnd, WM_SETICON, ICON_SMALL, (LPARAM)SmallIcon);
|
||||
SendMessage(WindowHandle.hwnd, WM_SETICON, ICON_BIG, (LPARAM)LargeIcon);
|
||||
}
|
||||
|
||||
void Win32DisplayWindow::SetBorderColor(uint32_t bgra8)
|
||||
{
|
||||
bgra8 = bgra8 & 0x00ffffff;
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ public:
|
|||
~Win32DisplayWindow();
|
||||
|
||||
void SetWindowTitle(const std::string& text) override;
|
||||
void SetWindowIcon(const std::vector<std::shared_ptr<Image>>& images) override;
|
||||
void SetWindowFrame(const Rect& box) override;
|
||||
void SetClientFrame(const Rect& box) override;
|
||||
void Show() override;
|
||||
|
|
@ -90,5 +91,8 @@ public:
|
|||
|
||||
HDC PaintDC = 0;
|
||||
|
||||
HICON SmallIcon = {};
|
||||
HICON LargeIcon = {};
|
||||
|
||||
StandardCursor CurrentCursor = StandardCursor::arrow;
|
||||
};
|
||||
|
|
|
|||
261
libraries/ZWidget/src/window/x11/x11_connection.cpp
Normal file
|
|
@ -0,0 +1,261 @@
|
|||
|
||||
#include "x11_connection.h"
|
||||
#include "x11_display_window.h"
|
||||
#include <stdexcept>
|
||||
#include <chrono>
|
||||
|
||||
X11Connection* GetX11Connection()
|
||||
{
|
||||
static X11Connection connection;
|
||||
return &connection;
|
||||
}
|
||||
|
||||
X11Connection::X11Connection()
|
||||
{
|
||||
// This is required by vulkan
|
||||
XInitThreads();
|
||||
|
||||
display = XOpenDisplay(nullptr);
|
||||
if (!display)
|
||||
throw std::runtime_error("Could not open X11 display");
|
||||
|
||||
// Make auto-repeat keys detectable
|
||||
Bool supports_detectable_autorepeat = {};
|
||||
XkbSetDetectableAutoRepeat(display, True, &supports_detectable_autorepeat);
|
||||
|
||||
// Loads the XMODIFIERS environment variable to see what IME to use
|
||||
XSetLocaleModifiers("");
|
||||
xim = XOpenIM(display, 0, 0, 0);
|
||||
if (!xim)
|
||||
{
|
||||
// fallback to internal input method
|
||||
XSetLocaleModifiers("@im=none");
|
||||
xim = XOpenIM(display, 0, 0, 0);
|
||||
}
|
||||
|
||||
// Look for XInput support
|
||||
int event = 0, error = 0;
|
||||
if (XQueryExtension(display, "XInputExtension", &XInputOpcode, &event, &error))
|
||||
{
|
||||
// We need XInput 2.0 support
|
||||
int major = 2, minor = 0;
|
||||
if (XIQueryVersion(display, &major, &minor) != BadRequest)
|
||||
{
|
||||
// And we need a master pointer ID
|
||||
int ndevices = 0;
|
||||
XIDeviceInfo *devices = XIQueryDevice(display, XIAllDevices, &ndevices);
|
||||
if (devices)
|
||||
{
|
||||
for (int i = 0; i < ndevices; i++)
|
||||
{
|
||||
if (devices[i].use == XIMasterPointer)
|
||||
{
|
||||
MasterPointerID = devices[i].deviceid;
|
||||
XInput2Supported = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
XIFreeDeviceInfo(devices);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This XInput API is the gift that just keeps on giving. Instead of routing events to the
|
||||
// focus window it sends it to the root window. Thanks!
|
||||
if (XInput2Supported)
|
||||
{
|
||||
unsigned char mask[3] = { 0 };
|
||||
XISetMask(mask, XI_RawMotion);
|
||||
XIEventMask eventmask;
|
||||
eventmask.deviceid = MasterPointerID;
|
||||
eventmask.mask_len = sizeof(mask);
|
||||
eventmask.mask = mask;
|
||||
Window root = XRootWindow(display, XDefaultScreen(display));
|
||||
XISelectEvents(display, root, &eventmask, 1);
|
||||
}
|
||||
}
|
||||
|
||||
X11Connection::~X11Connection()
|
||||
{
|
||||
for (auto& it : standardCursors)
|
||||
XFreeCursor(display, it.second);
|
||||
if (xim)
|
||||
XCloseIM(xim);
|
||||
XCloseDisplay(display);
|
||||
}
|
||||
|
||||
Atom X11Connection::GetAtom(const std::string& name)
|
||||
{
|
||||
auto it = atoms.find(name);
|
||||
if (it != atoms.end())
|
||||
return it->second;
|
||||
|
||||
Atom atom = XInternAtom(display, name.c_str(), True);
|
||||
atoms[name] = atom;
|
||||
return atom;
|
||||
}
|
||||
|
||||
void X11Connection::ProcessEvents()
|
||||
{
|
||||
CheckNeedsUpdate();
|
||||
while (XPending(display) > 0)
|
||||
{
|
||||
XEvent event = {};
|
||||
XNextEvent(display, &event);
|
||||
DispatchEvent(&event);
|
||||
}
|
||||
CheckTimers();
|
||||
}
|
||||
|
||||
void X11Connection::RunLoop()
|
||||
{
|
||||
X11Connection* connection = GetX11Connection();
|
||||
connection->ExitRunLoop = false;
|
||||
while (!connection->ExitRunLoop && !connection->windows.empty())
|
||||
{
|
||||
ProcessEvents();
|
||||
WaitForEvents(GetTimerTimeout());
|
||||
}
|
||||
}
|
||||
|
||||
void X11Connection::ExitLoop()
|
||||
{
|
||||
X11Connection* connection = GetX11Connection();
|
||||
connection->ExitRunLoop = true;
|
||||
}
|
||||
|
||||
bool X11Connection::WaitForEvents(int timeout)
|
||||
{
|
||||
int fd = XConnectionNumber(display);
|
||||
|
||||
struct timeval tv;
|
||||
if (timeout > 0)
|
||||
{
|
||||
tv.tv_sec = timeout / 1000;
|
||||
tv.tv_usec = (timeout % 1000) / 1000;
|
||||
}
|
||||
|
||||
fd_set rfds;
|
||||
FD_ZERO(&rfds);
|
||||
FD_SET(fd, &rfds);
|
||||
int result = select(fd + 1, &rfds, nullptr, nullptr, timeout >= 0 ? &tv : nullptr);
|
||||
return result > 0 && FD_ISSET(fd, &rfds);
|
||||
}
|
||||
|
||||
void X11Connection::CheckNeedsUpdate()
|
||||
{
|
||||
for (auto& it : windows)
|
||||
{
|
||||
if (it.second->needsUpdate)
|
||||
{
|
||||
it.second->needsUpdate = false;
|
||||
it.second->windowHost->OnWindowPaint();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void X11Connection::DispatchEvent(XEvent* event)
|
||||
{
|
||||
if (XInput2Supported)
|
||||
{
|
||||
// XInput sends all raw input to the root window. We want it routed to the focused window
|
||||
if (event->xcookie.type == GenericEvent &&
|
||||
event->xcookie.extension == XInputOpcode &&
|
||||
XGetEventData(display, &event->xcookie))
|
||||
{
|
||||
for (auto& it : windows)
|
||||
{
|
||||
X11DisplayWindow* window = it.second;
|
||||
if (window->OnXInputEvent(event))
|
||||
break;
|
||||
}
|
||||
XFreeEventData(display, &event->xcookie);
|
||||
}
|
||||
}
|
||||
|
||||
auto it = windows.find(event->xany.window);
|
||||
if (it != windows.end())
|
||||
{
|
||||
X11DisplayWindow* window = it->second;
|
||||
window->OnEvent(event);
|
||||
}
|
||||
}
|
||||
|
||||
Size X11Connection::GetScreenSize()
|
||||
{
|
||||
int screen = XDefaultScreen(display);
|
||||
|
||||
int disp_width_px = XDisplayWidth(display, screen);
|
||||
int disp_height_px = XDisplayHeight(display, screen);
|
||||
int disp_width_mm = XDisplayWidthMM(display, screen);
|
||||
double ppi = (disp_width_mm < 24) ? 96.0 : (25.4 * static_cast<double>(disp_width_px) / static_cast<double>(disp_width_mm));
|
||||
double dpiScale = ppi / 96.0;
|
||||
|
||||
return Size(disp_width_px / dpiScale, disp_height_px / dpiScale);
|
||||
}
|
||||
|
||||
static int64_t GetTimePoint()
|
||||
{
|
||||
using namespace std::chrono;
|
||||
return (int64_t)(duration_cast<milliseconds>(steady_clock::now().time_since_epoch()).count());
|
||||
}
|
||||
|
||||
void* X11Connection::StartTimer(int timeoutMilliseconds, std::function<void()> onTimer)
|
||||
{
|
||||
timeoutMilliseconds = std::max(timeoutMilliseconds, 1);
|
||||
timers.push_back(std::make_shared<X11Timer>(timeoutMilliseconds, onTimer, GetTimePoint() + timeoutMilliseconds));
|
||||
return timers.back().get();
|
||||
}
|
||||
|
||||
void X11Connection::StopTimer(void* timerID)
|
||||
{
|
||||
for (auto it = timers.begin(); it != timers.end(); ++it)
|
||||
{
|
||||
if (it->get() == timerID)
|
||||
{
|
||||
timers.erase(it);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void X11Connection::CheckTimers()
|
||||
{
|
||||
int64_t now = GetTimePoint();
|
||||
|
||||
// The callback may stop timers. Iterators might invalidate.
|
||||
while (true)
|
||||
{
|
||||
std::shared_ptr<X11Timer> foundTimer;
|
||||
for (auto& timer : timers)
|
||||
{
|
||||
if (timer->nextTime < now)
|
||||
{
|
||||
foundTimer = timer;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!foundTimer)
|
||||
break;
|
||||
|
||||
// Not very precise, but these aren't high precision timers
|
||||
foundTimer->nextTime = now + foundTimer->timeoutMilliseconds;
|
||||
foundTimer->onTimer();
|
||||
}
|
||||
}
|
||||
|
||||
int X11Connection::GetTimerTimeout()
|
||||
{
|
||||
if (timers.empty())
|
||||
return 0;
|
||||
|
||||
int64_t nextTime = timers.front()->nextTime;
|
||||
for (auto& timer : timers)
|
||||
{
|
||||
nextTime = std::min(nextTime, timer->nextTime);
|
||||
}
|
||||
|
||||
int64_t now = GetTimePoint();
|
||||
return (int)std::max(nextTime - now, (int64_t)1);
|
||||
}
|
||||
65
libraries/ZWidget/src/window/x11/x11_connection.h
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
#pragma once
|
||||
|
||||
#include "window/window.h"
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <X11/Xlib.h>
|
||||
#include <X11/Xutil.h>
|
||||
#include <X11/Xatom.h>
|
||||
#include <X11/cursorfont.h>
|
||||
#include <X11/keysymdef.h>
|
||||
#include <X11/XKBlib.h>
|
||||
#include <X11/extensions/XInput2.h>
|
||||
|
||||
class X11DisplayWindow;
|
||||
|
||||
class X11Timer
|
||||
{
|
||||
public:
|
||||
X11Timer(int timeoutMilliseconds, std::function<void()> onTimer, int64_t nextTime) : timeoutMilliseconds(timeoutMilliseconds), onTimer(onTimer), nextTime(nextTime) {}
|
||||
|
||||
int timeoutMilliseconds = 0;
|
||||
std::function<void()> onTimer;
|
||||
int64_t nextTime = 0;
|
||||
};
|
||||
|
||||
class X11Connection
|
||||
{
|
||||
public:
|
||||
X11Connection();
|
||||
~X11Connection();
|
||||
|
||||
void ProcessEvents();
|
||||
void RunLoop();
|
||||
void ExitLoop();
|
||||
bool WaitForEvents(int timeout);
|
||||
|
||||
void* StartTimer(int timeoutMilliseconds, std::function<void()> onTimer);
|
||||
void StopTimer(void* timerID);
|
||||
|
||||
Size GetScreenSize();
|
||||
|
||||
Atom GetAtom(const std::string& name);
|
||||
|
||||
Display* display = nullptr;
|
||||
std::map<std::string, Atom> atoms;
|
||||
std::map<Window, X11DisplayWindow*> windows;
|
||||
std::map<StandardCursor, Cursor> standardCursors;
|
||||
bool ExitRunLoop = false;
|
||||
|
||||
XIM xim = nullptr;
|
||||
bool XInput2Supported = false;
|
||||
int XInputOpcode = 0;
|
||||
int MasterPointerID = 0;
|
||||
|
||||
private:
|
||||
void DispatchEvent(XEvent* event);
|
||||
void CheckNeedsUpdate();
|
||||
void CheckTimers();
|
||||
int GetTimerTimeout();
|
||||
|
||||
std::vector<std::shared_ptr<X11Timer>> timers;
|
||||
};
|
||||
|
||||
X11Connection* GetX11Connection();
|
||||
|
|
@ -1,6 +1,8 @@
|
|||
|
||||
#include "x11_display_backend.h"
|
||||
#include "x11_display_window.h"
|
||||
#include "x11_connection.h"
|
||||
#include <stdexcept>
|
||||
|
||||
#ifdef USE_DBUS
|
||||
#include "window/dbus/dbus_open_file_dialog.h"
|
||||
|
|
@ -15,32 +17,32 @@ std::unique_ptr<DisplayWindow> X11DisplayBackend::Create(DisplayWindowHost* wind
|
|||
|
||||
void X11DisplayBackend::ProcessEvents()
|
||||
{
|
||||
X11DisplayWindow::ProcessEvents();
|
||||
GetX11Connection()->ProcessEvents();
|
||||
}
|
||||
|
||||
void X11DisplayBackend::RunLoop()
|
||||
{
|
||||
X11DisplayWindow::RunLoop();
|
||||
GetX11Connection()->RunLoop();
|
||||
}
|
||||
|
||||
void X11DisplayBackend::ExitLoop()
|
||||
{
|
||||
X11DisplayWindow::ExitLoop();
|
||||
GetX11Connection()->ExitLoop();
|
||||
}
|
||||
|
||||
Size X11DisplayBackend::GetScreenSize()
|
||||
{
|
||||
return X11DisplayWindow::GetScreenSize();
|
||||
return GetX11Connection()->GetScreenSize();
|
||||
}
|
||||
|
||||
void* X11DisplayBackend::StartTimer(int timeoutMilliseconds, std::function<void()> onTimer)
|
||||
{
|
||||
return X11DisplayWindow::StartTimer(timeoutMilliseconds, std::move(onTimer));
|
||||
return GetX11Connection()->StartTimer(timeoutMilliseconds, onTimer);
|
||||
}
|
||||
|
||||
void X11DisplayBackend::StopTimer(void* timerID)
|
||||
{
|
||||
X11DisplayWindow::StopTimer(timerID);
|
||||
GetX11Connection()->StopTimer(timerID);
|
||||
}
|
||||
|
||||
#ifdef USE_DBUS
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
|
||||
#include "x11_display_window.h"
|
||||
#include "x11_connection.h"
|
||||
#include <zwidget/core/image.h>
|
||||
#include <stdexcept>
|
||||
#include <vector>
|
||||
#include <cmath>
|
||||
|
|
@ -10,113 +12,6 @@
|
|||
#include <unistd.h>
|
||||
#include <iostream>
|
||||
|
||||
class X11Connection
|
||||
{
|
||||
public:
|
||||
X11Connection()
|
||||
{
|
||||
// This is required by vulkan
|
||||
XInitThreads();
|
||||
|
||||
display = XOpenDisplay(nullptr);
|
||||
if (!display)
|
||||
throw std::runtime_error("Could not open X11 display");
|
||||
|
||||
// Make auto-repeat keys detectable
|
||||
Bool supports_detectable_autorepeat = {};
|
||||
XkbSetDetectableAutoRepeat(display, True, &supports_detectable_autorepeat);
|
||||
|
||||
// Loads the XMODIFIERS environment variable to see what IME to use
|
||||
XSetLocaleModifiers("");
|
||||
xim = XOpenIM(display, 0, 0, 0);
|
||||
if (!xim)
|
||||
{
|
||||
// fallback to internal input method
|
||||
XSetLocaleModifiers("@im=none");
|
||||
xim = XOpenIM(display, 0, 0, 0);
|
||||
}
|
||||
|
||||
// Look for XInput support
|
||||
int event = 0, error = 0;
|
||||
if (XQueryExtension(display, "XInputExtension", &XInputOpcode, &event, &error))
|
||||
{
|
||||
// We need XInput 2.0 support
|
||||
int major = 2, minor = 0;
|
||||
if (XIQueryVersion(display, &major, &minor) != BadRequest)
|
||||
{
|
||||
// And we need a master pointer ID
|
||||
int ndevices = 0;
|
||||
XIDeviceInfo *devices = XIQueryDevice(display, XIAllDevices, &ndevices);
|
||||
if (devices)
|
||||
{
|
||||
for (int i = 0; i < ndevices; i++)
|
||||
{
|
||||
if (devices[i].use == XIMasterPointer)
|
||||
{
|
||||
MasterPointerID = devices[i].deviceid;
|
||||
XInput2Supported = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
XIFreeDeviceInfo(devices);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This XInput API is the gift that just keeps on giving. Instead of routing events to the
|
||||
// focus window it sends it to the root window. Thanks!
|
||||
if (XInput2Supported)
|
||||
{
|
||||
unsigned char mask[3] = { 0 };
|
||||
XISetMask(mask, XI_RawMotion);
|
||||
XIEventMask eventmask;
|
||||
eventmask.deviceid = MasterPointerID;
|
||||
eventmask.mask_len = sizeof(mask);
|
||||
eventmask.mask = mask;
|
||||
Window root = XRootWindow(display, XDefaultScreen(display));
|
||||
XISelectEvents(display, root, &eventmask, 1);
|
||||
}
|
||||
}
|
||||
|
||||
~X11Connection()
|
||||
{
|
||||
for (auto& it : standardCursors)
|
||||
XFreeCursor(display, it.second);
|
||||
if (xim)
|
||||
XCloseIM(xim);
|
||||
XCloseDisplay(display);
|
||||
}
|
||||
|
||||
Display* display = nullptr;
|
||||
std::map<std::string, Atom> atoms;
|
||||
std::map<Window, X11DisplayWindow*> windows;
|
||||
std::map<StandardCursor, Cursor> standardCursors;
|
||||
bool ExitRunLoop = false;
|
||||
|
||||
XIM xim = nullptr;
|
||||
bool XInput2Supported = false;
|
||||
int XInputOpcode = 0;
|
||||
int MasterPointerID = 0;
|
||||
};
|
||||
|
||||
static X11Connection* GetX11Connection()
|
||||
{
|
||||
static X11Connection connection;
|
||||
return &connection;
|
||||
}
|
||||
|
||||
static Atom GetAtom(const std::string& name)
|
||||
{
|
||||
auto connection = GetX11Connection();
|
||||
auto it = connection->atoms.find(name);
|
||||
if (it != connection->atoms.end())
|
||||
return it->second;
|
||||
|
||||
Atom atom = XInternAtom(connection->display, name.c_str(), True);
|
||||
connection->atoms[name] = atom;
|
||||
return atom;
|
||||
}
|
||||
|
||||
X11DisplayWindow::X11DisplayWindow(DisplayWindowHost* windowHost, bool popupWindow, X11DisplayWindow* owner, RenderAPI renderAPI) : windowHost(windowHost), owner(owner)
|
||||
{
|
||||
auto connection = GetX11Connection();
|
||||
|
|
@ -175,51 +70,51 @@ X11DisplayWindow::X11DisplayWindow(DisplayWindowHost* windowHost, bool popupWind
|
|||
}
|
||||
|
||||
// Tell window manager which process this window came from
|
||||
if (GetAtom("_NET_WM_PID") != None)
|
||||
if (connection->GetAtom("_NET_WM_PID") != None)
|
||||
{
|
||||
int32_t pid = getpid();
|
||||
if (pid != 0)
|
||||
{
|
||||
XChangeProperty(display, window, GetAtom("_NET_WM_PID"), XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&pid, 1);
|
||||
XChangeProperty(display, window, connection->GetAtom("_NET_WM_PID"), XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&pid, 1);
|
||||
}
|
||||
}
|
||||
|
||||
// Tell window manager which machine this window came from
|
||||
if (GetAtom("WM_CLIENT_MACHINE") != None)
|
||||
if (connection->GetAtom("WM_CLIENT_MACHINE") != None)
|
||||
{
|
||||
std::vector<char> hostname(256);
|
||||
if (gethostname(hostname.data(), hostname.size()) >= 0)
|
||||
{
|
||||
hostname.push_back(0);
|
||||
XChangeProperty(display, window, GetAtom("WM_CLIENT_MACHINE"), XA_STRING, 8, PropModeReplace, (unsigned char *)hostname.data(), strlen(hostname.data()));
|
||||
XChangeProperty(display, window, connection->GetAtom("WM_CLIENT_MACHINE"), XA_STRING, 8, PropModeReplace, (unsigned char *)hostname.data(), strlen(hostname.data()));
|
||||
}
|
||||
}
|
||||
|
||||
// Tell window manager we want to listen to close events
|
||||
if (GetAtom("WM_DELETE_WINDOW") != None)
|
||||
if (connection->GetAtom("WM_DELETE_WINDOW") != None)
|
||||
{
|
||||
Atom protocol = GetAtom("WM_DELETE_WINDOW");
|
||||
Atom protocol = connection->GetAtom("WM_DELETE_WINDOW");
|
||||
XSetWMProtocols(display, window, &protocol, 1);
|
||||
}
|
||||
|
||||
// Tell window manager what type of window we are
|
||||
if (GetAtom("_NET_WM_WINDOW_TYPE") != None)
|
||||
if (connection->GetAtom("_NET_WM_WINDOW_TYPE") != None)
|
||||
{
|
||||
Atom type = None;
|
||||
if (popupWindow)
|
||||
{
|
||||
type = GetAtom("_NET_WM_WINDOW_TYPE_DROPDOWN_MENU");
|
||||
type = connection->GetAtom("_NET_WM_WINDOW_TYPE_DROPDOWN_MENU");
|
||||
if (type == None)
|
||||
type = GetAtom("_NET_WM_WINDOW_TYPE_POPUP_MENU");
|
||||
type = connection->GetAtom("_NET_WM_WINDOW_TYPE_POPUP_MENU");
|
||||
if (type == None)
|
||||
type = GetAtom("_NET_WM_WINDOW_TYPE_COMBO");
|
||||
type = connection->GetAtom("_NET_WM_WINDOW_TYPE_COMBO");
|
||||
}
|
||||
if (type == None)
|
||||
type = GetAtom("_NET_WM_WINDOW_TYPE_NORMAL");
|
||||
type = connection->GetAtom("_NET_WM_WINDOW_TYPE_NORMAL");
|
||||
|
||||
if (type != None)
|
||||
{
|
||||
XChangeProperty(display, window, GetAtom("_NET_WM_WINDOW_TYPE"), XA_ATOM, 32, PropModeReplace, (unsigned char *)&type, 1);
|
||||
XChangeProperty(display, window, connection->GetAtom("_NET_WM_WINDOW_TYPE"), XA_ATOM, 32, PropModeReplace, (unsigned char *)&type, 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -254,6 +149,87 @@ void X11DisplayWindow::SetWindowTitle(const std::string& text)
|
|||
XSetStandardProperties(display, window, text.c_str(), text.c_str(), None, nullptr, 0, nullptr);
|
||||
}
|
||||
|
||||
void X11DisplayWindow::SetWindowIcon(const std::vector<std::shared_ptr<Image>>& images)
|
||||
{
|
||||
if (images.empty())
|
||||
return;
|
||||
|
||||
double dpiscale = GetDpiScale();
|
||||
int desiredSize = (int)std::round(32 * dpiscale);
|
||||
|
||||
Image* image = images.front().get();
|
||||
for (const auto& i : images)
|
||||
{
|
||||
int curdist = std::abs(image->GetWidth() - desiredSize);
|
||||
int dist = std::abs(i->GetWidth() - desiredSize);
|
||||
if (dist < curdist)
|
||||
image = i.get();
|
||||
}
|
||||
|
||||
int width = image->GetWidth();
|
||||
int height = image->GetHeight();
|
||||
const uint32_t* s = (const uint32_t*)image->GetData();
|
||||
|
||||
unsigned int size = (width * height) + 2; // header is 2 ints
|
||||
unsigned long* data = (unsigned long*)malloc(size * sizeof(unsigned long));
|
||||
|
||||
// set header
|
||||
data[0] = width;
|
||||
data[1] = height;
|
||||
|
||||
// on 64bit systems, the destination buffer is 64 bit per pixel
|
||||
// thus, we have to copy each pixel individually (no memcpy)
|
||||
|
||||
// icon data is expected as ARGB
|
||||
|
||||
if (image->GetFormat() == ImageFormat::R8G8B8A8)
|
||||
{
|
||||
for (int y = 0; y < height; ++y)
|
||||
{
|
||||
const uint32_t* src = s + y * width;
|
||||
unsigned long* dst = &data[2 + (y * width)];
|
||||
for (int x = 0; x < width; ++x)
|
||||
{
|
||||
uint32_t r = src[x] & 0xff;
|
||||
uint32_t g = (src[x] >> 8) & 0xff;
|
||||
uint32_t b = (src[x] >> 16) & 0xff;
|
||||
uint32_t a = (src[x] >> 24) & 0xff;
|
||||
dst[x] = (a << 24) | (r << 16) | (g << 8) | b;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (image->GetFormat() == ImageFormat::B8G8R8A8)
|
||||
{
|
||||
for (int y = 0; y < height; ++y)
|
||||
{
|
||||
const uint32_t* src = s + y * width;
|
||||
unsigned long* dst = &data[2 + (y * width)];
|
||||
for (int x = 0; x < width; ++x)
|
||||
{
|
||||
dst[x] = src[x];
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
free(data);
|
||||
return;
|
||||
}
|
||||
|
||||
// set icon geometry
|
||||
unsigned long* geom = (unsigned long*)malloc(4 * sizeof(unsigned long));
|
||||
geom[0] = geom[1] = 0; // x, y
|
||||
geom[2] = width;
|
||||
geom[3] = height;
|
||||
|
||||
Atom propertyGeom = XInternAtom(display, "_NET_WM_ICON_GEOMETRY", 0);
|
||||
XChangeProperty(display, window, propertyGeom, XA_CARDINAL, 32, PropModeReplace, (unsigned char*)geom, 4);
|
||||
|
||||
// set icon data
|
||||
Atom property = XInternAtom(display, "_NET_WM_ICON", 0);
|
||||
XChangeProperty(display, window, property, XA_CARDINAL, 32, PropModeReplace, (unsigned char*)data, size);
|
||||
}
|
||||
|
||||
void X11DisplayWindow::SetWindowFrame(const Rect& box)
|
||||
{
|
||||
// To do: this requires cooperation with the window manager
|
||||
|
|
@ -292,10 +268,11 @@ void X11DisplayWindow::ShowFullscreen()
|
|||
{
|
||||
Show();
|
||||
|
||||
if (GetAtom("_NET_WM_STATE") != None && GetAtom("_NET_WM_STATE_FULLSCREEN") != None)
|
||||
auto connection = GetX11Connection();
|
||||
if (connection->GetAtom("_NET_WM_STATE") != None && connection->GetAtom("_NET_WM_STATE_FULLSCREEN") != None)
|
||||
{
|
||||
Atom state = GetAtom("_NET_WM_STATE_FULLSCREEN");
|
||||
XChangeProperty(display, window, GetAtom("_NET_WM_STATE"), XA_ATOM, 32, PropModeReplace, (unsigned char *)&state, 1);
|
||||
Atom state = connection->GetAtom("_NET_WM_STATE_FULLSCREEN");
|
||||
XChangeProperty(display, window, connection->GetAtom("_NET_WM_STATE"), XA_ATOM, 32, PropModeReplace, (unsigned char *)&state, 1);
|
||||
isFullscreen = true;
|
||||
}
|
||||
}
|
||||
|
|
@ -582,7 +559,8 @@ std::vector<uint8_t> X11DisplayWindow::GetWindowProperty(Atom property, Atom &ac
|
|||
|
||||
std::string X11DisplayWindow::GetClipboardText()
|
||||
{
|
||||
Atom clipboard = GetAtom("CLIPBOARD");
|
||||
auto connection = GetX11Connection();
|
||||
Atom clipboard = connection->GetAtom("CLIPBOARD");
|
||||
if (clipboard == None)
|
||||
return {};
|
||||
|
||||
|
|
@ -595,7 +573,7 @@ std::string X11DisplayWindow::GetClipboardText()
|
|||
{
|
||||
if (XCheckTypedWindowEvent(display, window, SelectionNotify, &event))
|
||||
break;
|
||||
if (!WaitForEvents(500))
|
||||
if (!connection->WaitForEvents(500))
|
||||
return {};
|
||||
}
|
||||
|
||||
|
|
@ -614,7 +592,8 @@ void X11DisplayWindow::SetClipboardText(const std::string& text)
|
|||
{
|
||||
clipboardText = text;
|
||||
|
||||
Atom clipboard = GetAtom("CLIPBOARD");
|
||||
auto connection = GetX11Connection();
|
||||
Atom clipboard = connection->GetAtom("CLIPBOARD");
|
||||
if (clipboard == None)
|
||||
return;
|
||||
|
||||
|
|
@ -653,96 +632,6 @@ void* X11DisplayWindow::GetNativeHandle()
|
|||
return reinterpret_cast<void*>(window);
|
||||
}
|
||||
|
||||
bool X11DisplayWindow::WaitForEvents(int timeout)
|
||||
{
|
||||
Display* display = GetX11Connection()->display;
|
||||
int fd = XConnectionNumber(display);
|
||||
|
||||
struct timeval tv;
|
||||
if (timeout > 0)
|
||||
{
|
||||
tv.tv_sec = timeout / 1000;
|
||||
tv.tv_usec = (timeout % 1000) / 1000;
|
||||
}
|
||||
|
||||
fd_set rfds;
|
||||
FD_ZERO(&rfds);
|
||||
FD_SET(fd, &rfds);
|
||||
int result = select(fd + 1, &rfds, nullptr, nullptr, timeout >= 0 ? &tv : nullptr);
|
||||
return result > 0 && FD_ISSET(fd, &rfds);
|
||||
}
|
||||
|
||||
void X11DisplayWindow::CheckNeedsUpdate()
|
||||
{
|
||||
for (auto& it : GetX11Connection()->windows)
|
||||
{
|
||||
if (it.second->needsUpdate)
|
||||
{
|
||||
it.second->needsUpdate = false;
|
||||
it.second->windowHost->OnWindowPaint();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void X11DisplayWindow::ProcessEvents()
|
||||
{
|
||||
CheckNeedsUpdate();
|
||||
Display* display = GetX11Connection()->display;
|
||||
while (XPending(display) > 0)
|
||||
{
|
||||
XEvent event = {};
|
||||
XNextEvent(display, &event);
|
||||
DispatchEvent(&event);
|
||||
}
|
||||
}
|
||||
|
||||
void X11DisplayWindow::RunLoop()
|
||||
{
|
||||
X11Connection* connection = GetX11Connection();
|
||||
connection->ExitRunLoop = false;
|
||||
while (!connection->ExitRunLoop && !connection->windows.empty())
|
||||
{
|
||||
CheckNeedsUpdate();
|
||||
XEvent event = {};
|
||||
XNextEvent(connection->display, &event);
|
||||
DispatchEvent(&event);
|
||||
}
|
||||
}
|
||||
|
||||
void X11DisplayWindow::ExitLoop()
|
||||
{
|
||||
X11Connection* connection = GetX11Connection();
|
||||
connection->ExitRunLoop = true;
|
||||
}
|
||||
|
||||
void X11DisplayWindow::DispatchEvent(XEvent* event)
|
||||
{
|
||||
X11Connection* connection = GetX11Connection();
|
||||
|
||||
if (connection->XInput2Supported)
|
||||
{
|
||||
// XInput sends all raw input to the root window. We want it routed to the focused window
|
||||
if (event->xcookie.type == GenericEvent &&
|
||||
event->xcookie.extension == connection->XInputOpcode &&
|
||||
XGetEventData(connection->display, &event->xcookie))
|
||||
{
|
||||
for (auto& it : connection->windows)
|
||||
{
|
||||
X11DisplayWindow* window = it.second;
|
||||
window->OnXInputEvent(event);
|
||||
}
|
||||
XFreeEventData(connection->display, &event->xcookie);
|
||||
}
|
||||
}
|
||||
|
||||
auto it = connection->windows.find(event->xany.window);
|
||||
if (it != connection->windows.end())
|
||||
{
|
||||
X11DisplayWindow* window = it->second;
|
||||
window->OnEvent(event);
|
||||
}
|
||||
}
|
||||
|
||||
void X11DisplayWindow::OnEvent(XEvent* event)
|
||||
{
|
||||
if (event->type == ConfigureNotify)
|
||||
|
|
@ -786,11 +675,12 @@ void X11DisplayWindow::OnConfigureNotify(XEvent* event)
|
|||
|
||||
void X11DisplayWindow::OnClientMessage(XEvent* event)
|
||||
{
|
||||
Atom protocolsAtom = GetAtom("WM_PROTOCOLS");
|
||||
auto connection = GetX11Connection();
|
||||
Atom protocolsAtom = connection->GetAtom("WM_PROTOCOLS");
|
||||
if (protocolsAtom != None && event->xclient.message_type == protocolsAtom)
|
||||
{
|
||||
Atom deleteAtom = GetAtom("WM_DELETE_WINDOW");
|
||||
Atom pingAtom = GetAtom("_NET_WM_PING");
|
||||
Atom deleteAtom = connection->GetAtom("WM_DELETE_WINDOW");
|
||||
Atom pingAtom = connection->GetAtom("_NET_WM_PING");
|
||||
|
||||
Atom protocol = event->xclient.data.l[0];
|
||||
if (deleteAtom != None && protocol == deleteAtom)
|
||||
|
|
@ -1087,14 +977,14 @@ void X11DisplayWindow::OnMotionNotify(XEvent* event)
|
|||
}
|
||||
}
|
||||
|
||||
void X11DisplayWindow::OnXInputEvent(XEvent* event)
|
||||
bool X11DisplayWindow::OnXInputEvent(XEvent* event)
|
||||
{
|
||||
// This API is so horrible it makes Win32 look attractive!
|
||||
if (event->xcookie.evtype == XI_ButtonPress || event->xcookie.evtype == XI_ButtonRelease)
|
||||
{
|
||||
auto deviceEvent = (XIDeviceEvent*)event->xcookie.data;
|
||||
if (deviceEvent->event != window)
|
||||
return;
|
||||
return false;
|
||||
|
||||
InputKey key = {};
|
||||
switch (deviceEvent->detail)
|
||||
|
|
@ -1106,7 +996,7 @@ void X11DisplayWindow::OnXInputEvent(XEvent* event)
|
|||
case 5: key = InputKey::MouseWheelDown; break;
|
||||
// case 6: key = InputKey::XButton1; break;
|
||||
// case 7: key = InputKey::XButton2; break;
|
||||
default: return;
|
||||
default: return false;
|
||||
}
|
||||
|
||||
double dpiScale = GetDpiScale();
|
||||
|
|
@ -1132,12 +1022,14 @@ void X11DisplayWindow::OnXInputEvent(XEvent* event)
|
|||
keyState[key] = false;
|
||||
windowHost->OnWindowMouseUp(mousePos, key);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
else if (event->xcookie.evtype == XI_Motion)
|
||||
{
|
||||
auto deviceEvent = (XIDeviceEvent*)event->xcookie.data;
|
||||
if (deviceEvent->event != window)
|
||||
return;
|
||||
return false;
|
||||
|
||||
if (isCursorEnabled)
|
||||
{
|
||||
|
|
@ -1160,6 +1052,8 @@ void X11DisplayWindow::OnXInputEvent(XEvent* event)
|
|||
XWarpPointer(display, window, window, 0, 0, ClientSizeX, ClientSizeY, centerX, centerY);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
else if (!isCursorEnabled && RawInput.Focused && event->xcookie.evtype == XI_RawMotion)
|
||||
{
|
||||
|
|
@ -1185,7 +1079,10 @@ void X11DisplayWindow::OnXInputEvent(XEvent* event)
|
|||
if (dx != 0 || dy != 0)
|
||||
windowHost->OnWindowRawMouseMove(dx, dy);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void X11DisplayWindow::OnLeaveNotify(XEvent* event)
|
||||
|
|
@ -1209,8 +1106,9 @@ void X11DisplayWindow::OnSelectionRequest(XEvent* event)
|
|||
if (requestor == window)
|
||||
return;
|
||||
|
||||
Atom targetsAtom = GetAtom("TARGETS");
|
||||
Atom multipleAtom = GetAtom("MULTIPLE");
|
||||
X11Connection* connection = GetX11Connection();
|
||||
Atom targetsAtom = connection->GetAtom("TARGETS");
|
||||
Atom multipleAtom = connection->GetAtom("MULTIPLE");
|
||||
|
||||
struct Request { Window target; Atom property; };
|
||||
std::vector<Request> requests;
|
||||
|
|
@ -1267,31 +1165,6 @@ void X11DisplayWindow::OnSelectionRequest(XEvent* event)
|
|||
}
|
||||
}
|
||||
|
||||
Size X11DisplayWindow::GetScreenSize()
|
||||
{
|
||||
X11Connection* connection = GetX11Connection();
|
||||
Display* display = connection->display;
|
||||
int screen = XDefaultScreen(display);
|
||||
|
||||
int disp_width_px = XDisplayWidth(display, screen);
|
||||
int disp_height_px = XDisplayHeight(display, screen);
|
||||
int disp_width_mm = XDisplayWidthMM(display, screen);
|
||||
double ppi = (disp_width_mm < 24) ? 96.0 : (25.4 * static_cast<double>(disp_width_px) / static_cast<double>(disp_width_mm));
|
||||
double dpiScale = ppi / 96.0;
|
||||
|
||||
return Size(disp_width_px / dpiScale, disp_height_px / dpiScale);
|
||||
}
|
||||
|
||||
void* X11DisplayWindow::StartTimer(int timeoutMilliseconds, std::function<void()> onTimer)
|
||||
{
|
||||
throw std::logic_error("unimplemented: X11DisplayWindow::StartTimer");
|
||||
}
|
||||
|
||||
void X11DisplayWindow::StopTimer(void* timerID)
|
||||
{
|
||||
throw std::logic_error("unimplemented: X11DisplayWindow::StopTimer");
|
||||
}
|
||||
|
||||
// This is to avoid needing all the Vulkan headers and the volk binding library just for this:
|
||||
#ifndef VK_VERSION_1_0
|
||||
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ public:
|
|||
~X11DisplayWindow();
|
||||
|
||||
void SetWindowTitle(const std::string& text) override;
|
||||
void SetWindowIcon(const std::vector<std::shared_ptr<Image>>& images) override;
|
||||
void SetWindowFrame(const Rect& box) override;
|
||||
void SetClientFrame(const Rect& box) override;
|
||||
void Show() override;
|
||||
|
|
@ -60,13 +61,6 @@ public:
|
|||
std::vector<std::string> GetVulkanInstanceExtensions() override;
|
||||
VkSurfaceKHR CreateVulkanSurface(VkInstance instance) override;
|
||||
|
||||
static void ProcessEvents();
|
||||
static void RunLoop();
|
||||
static void ExitLoop();
|
||||
static Size GetScreenSize();
|
||||
static void* StartTimer(int timeoutMilliseconds, std::function<void()> onTimer);
|
||||
static void StopTimer(void* timerID);
|
||||
|
||||
private:
|
||||
void UpdateCursor();
|
||||
|
||||
|
|
@ -86,7 +80,7 @@ private:
|
|||
void OnSelectionClear(XEvent* event);
|
||||
void OnSelectionNotify(XEvent* event);
|
||||
void OnSelectionRequest(XEvent* event);
|
||||
void OnXInputEvent(XEvent* event);
|
||||
bool OnXInputEvent(XEvent* event);
|
||||
|
||||
void CreateBackbuffer(int width, int height);
|
||||
void DestroyBackbuffer();
|
||||
|
|
@ -94,11 +88,6 @@ private:
|
|||
InputKey GetInputKey(XEvent* event);
|
||||
Point GetMousePos(XEvent* event);
|
||||
|
||||
static bool WaitForEvents(int timeout);
|
||||
static void DispatchEvent(XEvent* event);
|
||||
|
||||
static void CheckNeedsUpdate();
|
||||
|
||||
std::vector<uint8_t> GetWindowProperty(Atom property, Atom &actual_type, int &actual_format, unsigned long &item_count);
|
||||
|
||||
DisplayWindowHost* windowHost = nullptr;
|
||||
|
|
@ -145,5 +134,6 @@ private:
|
|||
|
||||
bool needsUpdate = false;
|
||||
|
||||
friend class X11Connection;
|
||||
friend class X11DisplayBackend;
|
||||
};
|
||||
|
|
|
|||