Merge remote-tracking branch 'gzdoom/master' into gzdoom_merge
|
|
@ -1,4 +1,5 @@
|
|||
cmake_minimum_required(VERSION 3.15)
|
||||
cmake_minimum_required(VERSION 3.24)
|
||||
|
||||
project(zvulkan)
|
||||
|
||||
option( VULKAN_USE_XLIB "Use Vulkan xlib (X11) WSI integration" ON )
|
||||
|
|
@ -198,15 +199,19 @@ source_group("include\\volk" REGULAR_EXPRESSION "${CMAKE_CURRENT_SOURCE_DIR}/inc
|
|||
include_directories(include include/zvulkan src)
|
||||
|
||||
if(WIN32)
|
||||
set(ZVULKAN_SOURCES ${ZVULKAN_SOURCES} ${ZVULKAN_WIN32_SOURCES})
|
||||
list(APPEND ZVULKAN_SOURCES ${ZVULKAN_WIN32_SOURCES})
|
||||
add_definitions(-DUNICODE -D_UNICODE)
|
||||
else()
|
||||
set(ZVULKAN_SOURCES ${ZVULKAN_SOURCES} ${ZVULKAN_UNIX_SOURCES})
|
||||
if(NOT HAIKU)
|
||||
set(ZVULKAN_LIBS ${CMAKE_DL_LIBS} -ldl)
|
||||
if(APPLE)
|
||||
find_package(Vulkan REQUIRED COMPONENTS MoltenVK)
|
||||
list(APPEND ZVULKAN_LIBS Vulkan::Vulkan)
|
||||
elseif(NOT HAIKU AND NOT CMAKE_SYSTEM_NAME MATCHES "FreeBSD|NetBSD|OpenBSD|DragonFly")
|
||||
list(APPEND ZVULKAN_LIBS ${CMAKE_DL_LIBS} -ldl)
|
||||
else()
|
||||
set(ZVULKAN_LIBS ${CMAKE_DL_LIBS})
|
||||
list(APPEND ZVULKAN_LIBS ${CMAKE_DL_LIBS})
|
||||
endif()
|
||||
|
||||
list(APPEND ZVULKAN_SOURCES ${ZVULKAN_UNIX_SOURCES})
|
||||
add_definitions(-DUNIX -D_UNIX)
|
||||
add_link_options(-pthread)
|
||||
endif()
|
||||
|
|
@ -220,7 +225,7 @@ if(MSVC)
|
|||
endif()
|
||||
|
||||
add_library(zvulkan STATIC ${ZVULKAN_SOURCES} ${ZVULKAN_INCLUDES} ${VULKAN_INCLUDES})
|
||||
target_link_libraries(zvulkan ${ZVULKAN_LIBS})
|
||||
target_link_libraries(zvulkan PUBLIC ${ZVULKAN_LIBS})
|
||||
set_target_properties(zvulkan PROPERTIES CXX_STANDARD 17)
|
||||
|
||||
if(MSVC)
|
||||
|
|
|
|||
|
|
@ -569,7 +569,7 @@ inline RenderPassBegin& RenderPassBegin::Framebuffer(VulkanFramebuffer* framebuf
|
|||
inline RenderPassBegin& RenderPassBegin::AddClearColor(float r, float g, float b, float a)
|
||||
{
|
||||
VkClearValue clearValue = { };
|
||||
clearValue.color = { r, g, b, a };
|
||||
clearValue.color = {{ r, g, b, a }};
|
||||
clearValues.push_back(clearValue);
|
||||
|
||||
renderPassInfo.clearValueCount = (uint32_t)clearValues.size();
|
||||
|
|
|
|||
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
|
||||
|
|
|
|||
|
|
@ -17,6 +17,14 @@ if (UNIX AND NOT APPLE)
|
|||
)
|
||||
endif()
|
||||
|
||||
# SDL2 finding stuff
|
||||
# Optional on all platforms
|
||||
find_package(SDL2 QUIET)
|
||||
if(NOT ${SDL2_FOUND})
|
||||
include(FindPkgConfig)
|
||||
pkg_search_module(SDL2 sdl2)
|
||||
endif()
|
||||
|
||||
set(ZWIDGET_SOURCES
|
||||
src/core/canvas.cpp
|
||||
src/core/font.cpp
|
||||
|
|
@ -138,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
|
||||
|
|
@ -147,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/.+")
|
||||
|
|
@ -226,11 +240,12 @@ elseif(APPLE)
|
|||
set(ZWIDGET_DEFINES -DUNIX -D_UNIX)
|
||||
set(ZWIDGET_LINK_OPTIONS -pthread)
|
||||
else()
|
||||
set(ZWIDGET_SOURCES ${ZWIDGET_SOURCES} ${ZWIDGET_SDL2_SOURCES} ${ZWIDGET_X11_SOURCES})
|
||||
set(ZWIDGET_LIBS ${CMAKE_DL_LIBS} -ldl -lX11)
|
||||
set(ZWIDGET_DEFINES -DUNIX -D_UNIX -DUSE_SDL2 -DUSE_X11)
|
||||
set(ZWIDGET_SOURCES ${ZWIDGET_SOURCES} ${ZWIDGET_X11_SOURCES})
|
||||
set(ZWIDGET_LIBS ${CMAKE_DL_LIBS} -lX11 -lXi)
|
||||
set(ZWIDGET_DEFINES -DUNIX -D_UNIX -DUSE_X11)
|
||||
set(ZWIDGET_LINK_OPTIONS -pthread)
|
||||
if (DBUS_FOUND)
|
||||
include_directories("/usr/lib64/dbus-1.0/include") # Bazzite (and probably Fedora 42) keeps the platform-specific dbus headers in this folder
|
||||
set(ZWIDGET_SOURCES ${ZWIDGET_SOURCES} ${ZWIDGET_DBUS_SOURCES})
|
||||
set(ZWIDGET_INCLUDE_DIRS ${ZWIDGET_INCLUDE_DIRS} ${DBUS_INCLUDE_DIRS})
|
||||
set(ZWIDGET_LIBS ${ZWIDGET_LIBS} ${DBUS_LDFLAGS})
|
||||
|
|
@ -244,14 +259,77 @@ else()
|
|||
endif()
|
||||
endif()
|
||||
|
||||
if(SDL2_FOUND AND NOT WIN32)
|
||||
set(ZWIDGET_SOURCES ${ZWIDGET_SOURCES} ${ZWIDGET_SDL2_SOURCES})
|
||||
set(ZWIDGET_DEFINES ${ZWIDGET_DEFINES} -DUSE_SDL2)
|
||||
endif()
|
||||
|
||||
if(MSVC)
|
||||
set(CXX_WARNING_FLAGS /W3)
|
||||
else()
|
||||
set(CXX_WARNING_FLAGS -Wall -Wpedantic)
|
||||
endif()
|
||||
|
||||
add_library(zwidget STATIC ${ZWIDGET_SOURCES} ${ZWIDGET_INCLUDES})
|
||||
target_compile_options(zwidget PRIVATE ${ZWIDGET_COMPILE_OPTIONS})
|
||||
target_compile_definitions(zwidget PRIVATE ${ZWIDGET_DEFINES})
|
||||
target_include_directories(zwidget PRIVATE ${ZWIDGET_INCLUDE_DIRS})
|
||||
target_link_options(zwidget PRIVATE ${ZWIDGET_LINK_OPTIONS})
|
||||
target_link_libraries(zwidget ${ZWIDGET_LIBS})
|
||||
set_target_properties(zwidget PROPERTIES CXX_STANDARD 17)
|
||||
target_link_libraries(zwidget PRIVATE ${ZWIDGET_LIBS})
|
||||
if(SDL2_FOUND)
|
||||
if(TARGET SDL2::SDL2)
|
||||
target_link_libraries(zwidget PRIVATE SDL2::SDL2)
|
||||
else() # needed for gzdoom compat for now
|
||||
target_include_directories(zwidget PRIVATE ${SDL2_INCLUDE_DIR} ${SDL2_INCLUDE_DIRS})
|
||||
target_link_libraries(zwidget PRIVATE ${SDL2_LIBRARY})
|
||||
endif()
|
||||
endif()
|
||||
set_target_properties(zwidget PROPERTIES CXX_STANDARD 20)
|
||||
target_compile_options(zwidget PRIVATE ${CXX_WARNING_FLAGS})
|
||||
|
||||
if(MSVC)
|
||||
set_property(TARGET zwidget PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
|
||||
endif()
|
||||
|
||||
option(ZWIDGET_BUILD_EXAMPLE "Build the zwidget example application" ON)
|
||||
|
||||
if(ZWIDGET_BUILD_EXAMPLE)
|
||||
add_executable(zwidget_example WIN32
|
||||
example/example.cpp
|
||||
example/picopng.cpp
|
||||
example/picopng.h
|
||||
)
|
||||
target_compile_options(zwidget_example PRIVATE ${CXX_WARNING_FLAGS})
|
||||
|
||||
add_custom_command(
|
||||
TARGET zwidget_example POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/example/banner.png"
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/banner.png"
|
||||
COMMAND ${CMAKE_COMMAND} -E copy
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/example/OpenSans.ttf"
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/OpenSans.ttf"
|
||||
)
|
||||
|
||||
target_include_directories(zwidget_example PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/example)
|
||||
target_link_libraries(zwidget_example PRIVATE zwidget)
|
||||
|
||||
if(WIN32)
|
||||
target_compile_definitions(zwidget_example PRIVATE UNICODE _UNICODE)
|
||||
target_link_libraries(zwidget_example PRIVATE gdi32 user32 shell32 comdlg32)
|
||||
elseif(APPLE)
|
||||
target_link_libraries(zwidget_example PRIVATE "-framework Cocoa -framework OpenGL")
|
||||
else()
|
||||
target_link_libraries(zwidget_example PRIVATE ${ZWIDGET_LIBS})
|
||||
endif()
|
||||
|
||||
if(SDL2_FOUND)
|
||||
target_link_libraries(zwidget_example PRIVATE SDL2::SDL2)
|
||||
endif()
|
||||
|
||||
set_target_properties(zwidget_example PROPERTIES CXX_STANDARD 20)
|
||||
|
||||
if(MSVC)
|
||||
set_property(TARGET zwidget_example PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
|
||||
endif()
|
||||
endif()
|
||||
|
|
|
|||
|
|
@ -13,136 +13,29 @@
|
|||
#include <zwidget/widgets/pushbutton/pushbutton.h>
|
||||
#include <zwidget/widgets/checkboxlabel/checkboxlabel.h>
|
||||
#include "picopng.h"
|
||||
#include <zwidget/widgets/tabwidget/tabwidget.h>
|
||||
|
||||
// ************************************************************
|
||||
// Prototypes
|
||||
// ************************************************************
|
||||
|
||||
static std::vector<uint8_t> ReadAllBytes(const std::string& filename);
|
||||
|
||||
class LauncherWindow : public Widget
|
||||
class LauncherWindowTab1 : public Widget
|
||||
{
|
||||
public:
|
||||
LauncherWindow() : Widget(nullptr, WidgetType::Window)
|
||||
{
|
||||
Logo = new ImageBox(this);
|
||||
WelcomeLabel = new TextLabel(this);
|
||||
VersionLabel = new TextLabel(this);
|
||||
SelectLabel = new TextLabel(this);
|
||||
GeneralLabel = new TextLabel(this);
|
||||
ExtrasLabel = new TextLabel(this);
|
||||
FullscreenCheckbox = new CheckboxLabel(this);
|
||||
DisableAutoloadCheckbox = new CheckboxLabel(this);
|
||||
DontAskAgainCheckbox = new CheckboxLabel(this);
|
||||
LightsCheckbox = new CheckboxLabel(this);
|
||||
BrightmapsCheckbox = new CheckboxLabel(this);
|
||||
WidescreenCheckbox = new CheckboxLabel(this);
|
||||
PlayButton = new PushButton(this);
|
||||
ExitButton = new PushButton(this);
|
||||
GamesList = new ListView(this);
|
||||
Choices = new Dropdown(this);
|
||||
LauncherWindowTab1(Widget parent);
|
||||
void OnGeometryChanged() override;
|
||||
private:
|
||||
TextEdit* Text = nullptr;
|
||||
};
|
||||
|
||||
SetWindowBackground(Colorf::fromRgba8(51, 51, 51));
|
||||
SetWindowBorderColor(Colorf::fromRgba8(51, 51, 51));
|
||||
SetWindowCaptionColor(Colorf::fromRgba8(33, 33, 33));
|
||||
SetWindowCaptionTextColor(Colorf::fromRgba8(226, 223, 219));
|
||||
SetWindowTitle("VKDoom Launcher");
|
||||
|
||||
WelcomeLabel->SetText("Welcome to VKDoom");
|
||||
VersionLabel->SetText("Version 0xdeadbabe.");
|
||||
SelectLabel->SetText("Select which game file (IWAD) to run.");
|
||||
PlayButton->SetText("Play Game");
|
||||
ExitButton->SetText("Exit");
|
||||
|
||||
ExitButton->OnClick = []{
|
||||
DisplayWindow::ExitLoop();
|
||||
};
|
||||
|
||||
GeneralLabel->SetText("General");
|
||||
ExtrasLabel->SetText("Extra Graphics");
|
||||
FullscreenCheckbox->SetText("Fullscreen");
|
||||
DisableAutoloadCheckbox->SetText("Disable autoload");
|
||||
DontAskAgainCheckbox->SetText("Don't ask me again");
|
||||
LightsCheckbox->SetText("Lights");
|
||||
BrightmapsCheckbox->SetText("Brightmaps");
|
||||
WidescreenCheckbox->SetText("Widescreen");
|
||||
|
||||
Choices->SetMaxDisplayItems(2);
|
||||
Choices->AddItem("First");
|
||||
Choices->AddItem("Second");
|
||||
Choices->AddItem("Third");
|
||||
Choices->AddItem("Fourth");
|
||||
Choices->AddItem("Fifth");
|
||||
Choices->AddItem("Sixth");
|
||||
|
||||
Choices->OnChanged = [this](int index) {
|
||||
std::cout << "Selected " << index << ":" << Choices->GetItem(index) << std::endl;
|
||||
};
|
||||
|
||||
try
|
||||
{
|
||||
auto filedata = ReadAllBytes("banner.png");
|
||||
std::vector<unsigned char> pixels;
|
||||
unsigned long width = 0, height = 0;
|
||||
int result = decodePNG(pixels, width, height, (const unsigned char*)filedata.data(), filedata.size(), true);
|
||||
if (result == 0)
|
||||
{
|
||||
Logo->SetImage(Image::Create(width, height, ImageFormat::R8G8B8A8, pixels.data()));
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
void OnGeometryChanged() override
|
||||
{
|
||||
double y = 0.0;
|
||||
|
||||
Logo->SetFrameGeometry(0.0, y, GetWidth(), Logo->GetPreferredHeight());
|
||||
y += Logo->GetPreferredHeight();
|
||||
|
||||
y += 10.0;
|
||||
|
||||
WelcomeLabel->SetFrameGeometry(20.0, y, GetWidth() - 40.0, WelcomeLabel->GetPreferredHeight());
|
||||
y += WelcomeLabel->GetPreferredHeight();
|
||||
|
||||
VersionLabel->SetFrameGeometry(20.0, y, GetWidth() - 40.0, VersionLabel->GetPreferredHeight());
|
||||
y += VersionLabel->GetPreferredHeight();
|
||||
|
||||
y += 10.0;
|
||||
|
||||
SelectLabel->SetFrameGeometry(20.0, y, GetWidth() - 40.0 - Choices->GetPreferredWidth(), SelectLabel->GetPreferredHeight());
|
||||
y += SelectLabel->GetPreferredHeight();
|
||||
|
||||
Choices->SetFrameGeometry(GetWidth() - 20.0 - Choices->GetPreferredWidth(), y-Choices->GetPreferredHeight(), Choices->GetPreferredWidth(), Choices->GetPreferredHeight());
|
||||
|
||||
double listViewTop = y + 10.0;
|
||||
|
||||
y = GetHeight() - 15.0 - PlayButton->GetPreferredHeight();
|
||||
PlayButton->SetFrameGeometry(20.0, y, 120.0, PlayButton->GetPreferredHeight());
|
||||
ExitButton->SetFrameGeometry(GetWidth() - 20.0 - 120.0, y, 120.0, PlayButton->GetPreferredHeight());
|
||||
|
||||
y -= 20.0;
|
||||
|
||||
double panelWidth = 150.0;
|
||||
y -= DontAskAgainCheckbox->GetPreferredHeight();
|
||||
DontAskAgainCheckbox->SetFrameGeometry(20.0, y, 190.0, DontAskAgainCheckbox->GetPreferredHeight());
|
||||
WidescreenCheckbox->SetFrameGeometry(GetWidth() - 20.0 - panelWidth, y, panelWidth, WidescreenCheckbox->GetPreferredHeight());
|
||||
|
||||
y -= DisableAutoloadCheckbox->GetPreferredHeight();
|
||||
DisableAutoloadCheckbox->SetFrameGeometry(20.0, y, 190.0, DisableAutoloadCheckbox->GetPreferredHeight());
|
||||
BrightmapsCheckbox->SetFrameGeometry(GetWidth() - 20.0 - panelWidth, y, panelWidth, BrightmapsCheckbox->GetPreferredHeight());
|
||||
|
||||
y -= FullscreenCheckbox->GetPreferredHeight();
|
||||
FullscreenCheckbox->SetFrameGeometry(20.0, y, 190.0, FullscreenCheckbox->GetPreferredHeight());
|
||||
LightsCheckbox->SetFrameGeometry(GetWidth() - 20.0 - panelWidth, y, panelWidth, LightsCheckbox->GetPreferredHeight());
|
||||
|
||||
y -= GeneralLabel->GetPreferredHeight();
|
||||
GeneralLabel->SetFrameGeometry(20.0, y, 190.0, GeneralLabel->GetPreferredHeight());
|
||||
ExtrasLabel->SetFrameGeometry(GetWidth() - 20.0 - panelWidth, y, panelWidth, ExtrasLabel->GetPreferredHeight());
|
||||
|
||||
double listViewBottom = y - 10.0;
|
||||
GamesList->SetFrameGeometry(20.0, listViewTop, GetWidth() - 40.0, std::max(listViewBottom - listViewTop, 0.0));
|
||||
}
|
||||
|
||||
ImageBox* Logo = nullptr;
|
||||
class LauncherWindowTab2 : public Widget
|
||||
{
|
||||
public:
|
||||
LauncherWindowTab2(Widget parent);
|
||||
void OnGeometryChanged() override;
|
||||
private:
|
||||
TextLabel* WelcomeLabel = nullptr;
|
||||
TextLabel* VersionLabel = nullptr;
|
||||
TextLabel* SelectLabel = nullptr;
|
||||
|
|
@ -154,13 +47,261 @@ public:
|
|||
CheckboxLabel* LightsCheckbox = nullptr;
|
||||
CheckboxLabel* BrightmapsCheckbox = nullptr;
|
||||
CheckboxLabel* WidescreenCheckbox = nullptr;
|
||||
PushButton* PlayButton = nullptr;
|
||||
PushButton* ExitButton = nullptr;
|
||||
ListView* GamesList = nullptr;
|
||||
Dropdown* Choices = nullptr;
|
||||
};
|
||||
|
||||
static std::vector<uint8_t> ReadAllBytes(const std::string& filename);
|
||||
class LauncherWindowTab3 : public Widget
|
||||
{
|
||||
public:
|
||||
LauncherWindowTab3(Widget parent);
|
||||
void OnGeometryChanged() override;
|
||||
private:
|
||||
TextLabel* Label = nullptr;
|
||||
Dropdown* Choices = nullptr;
|
||||
PushButton* Popup = nullptr;
|
||||
};
|
||||
|
||||
class LauncherWindow : public Widget
|
||||
{
|
||||
public:
|
||||
LauncherWindow();
|
||||
private:
|
||||
void OnClose() override;
|
||||
void OnGeometryChanged() override;
|
||||
|
||||
ImageBox* Logo = nullptr;
|
||||
TabWidget* Pages = nullptr;
|
||||
PushButton* ExitButton = nullptr;
|
||||
|
||||
LauncherWindowTab1* Tab1 = nullptr;
|
||||
LauncherWindowTab2* Tab2 = nullptr;
|
||||
LauncherWindowTab3* Tab3 = nullptr;
|
||||
};
|
||||
|
||||
// ************************************************************
|
||||
// UI implementation
|
||||
// ************************************************************
|
||||
|
||||
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);
|
||||
|
||||
Tab1 = new LauncherWindowTab1(this);
|
||||
Tab2 = new LauncherWindowTab2(this);
|
||||
Tab3 = new LauncherWindowTab3(this);
|
||||
|
||||
Pages->AddTab(Tab1, "Welcome");
|
||||
Pages->AddTab(Tab2, "VKDoom");
|
||||
Pages->AddTab(Tab3, "ZWidgets");
|
||||
|
||||
ExitButton->SetText("Exit");
|
||||
|
||||
ExitButton->OnClick = []{
|
||||
DisplayWindow::ExitLoop();
|
||||
};
|
||||
|
||||
try
|
||||
{
|
||||
auto filedata = ReadAllBytes("banner.png");
|
||||
std::vector<unsigned char> pixels;
|
||||
unsigned long width = 0, height = 0;
|
||||
int result = decodePNG(pixels, width, height, (const unsigned char*)filedata.data(), filedata.size(), true);
|
||||
if (result == 0)
|
||||
{
|
||||
Logo->SetImage(Image::Create(width, height, ImageFormat::R8G8B8A8, pixels.data()));
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
void LauncherWindow::OnGeometryChanged()
|
||||
{
|
||||
double y = 0, h;
|
||||
|
||||
h = Logo->GetPreferredHeight();
|
||||
Logo->SetFrameGeometry(0, y, GetWidth(), h);
|
||||
y += h;
|
||||
|
||||
h = GetHeight() - y - ExitButton->GetPreferredHeight() - 40;
|
||||
Pages->SetFrameGeometry(0, y, GetWidth(), h);
|
||||
y += h + 20;
|
||||
|
||||
ExitButton->SetFrameGeometry(GetWidth() - 20 - 120, y, 120, ExitButton->GetPreferredHeight());
|
||||
}
|
||||
|
||||
void LauncherWindow::OnClose()
|
||||
{
|
||||
DisplayWindow::ExitLoop();
|
||||
}
|
||||
|
||||
LauncherWindowTab1::LauncherWindowTab1(Widget parent): Widget(nullptr)
|
||||
{
|
||||
Text = new TextEdit(this);
|
||||
|
||||
Text->SetText(
|
||||
"Welcome to VKDoom\n\n"
|
||||
"Click the tabs to look at other widgets\n\n"
|
||||
"Also, this text is editable\n"
|
||||
);
|
||||
}
|
||||
|
||||
void LauncherWindowTab1::OnGeometryChanged()
|
||||
{
|
||||
Text->SetFrameGeometry(0, 10, GetWidth(), GetHeight());
|
||||
}
|
||||
|
||||
LauncherWindowTab2::LauncherWindowTab2(Widget parent): Widget(nullptr)
|
||||
{
|
||||
WelcomeLabel = new TextLabel(this);
|
||||
VersionLabel = new TextLabel(this);
|
||||
SelectLabel = new TextLabel(this);
|
||||
GeneralLabel = new TextLabel(this);
|
||||
ExtrasLabel = new TextLabel(this);
|
||||
FullscreenCheckbox = new CheckboxLabel(this);
|
||||
DisableAutoloadCheckbox = new CheckboxLabel(this);
|
||||
DontAskAgainCheckbox = new CheckboxLabel(this);
|
||||
LightsCheckbox = new CheckboxLabel(this);
|
||||
BrightmapsCheckbox = new CheckboxLabel(this);
|
||||
WidescreenCheckbox = new CheckboxLabel(this);
|
||||
GamesList = new ListView(this);
|
||||
|
||||
WelcomeLabel->SetText("Welcome to VKDoom");
|
||||
VersionLabel->SetText("Version 0xdeadbabe.");
|
||||
SelectLabel->SetText("Select which game file (IWAD) to run.");
|
||||
|
||||
GamesList->AddItem("Doom");
|
||||
GamesList->AddItem("Doom 2: Electric Boogaloo");
|
||||
GamesList->AddItem("Doom 3D");
|
||||
GamesList->AddItem("Doom 4: The Quest for Peace");
|
||||
GamesList->AddItem("Doom on Ice");
|
||||
GamesList->AddItem("The Doom");
|
||||
GamesList->AddItem("Doom 2");
|
||||
|
||||
GeneralLabel->SetText("General");
|
||||
ExtrasLabel->SetText("Extra Graphics");
|
||||
FullscreenCheckbox->SetText("Fullscreen");
|
||||
DisableAutoloadCheckbox->SetText("Disable autoload");
|
||||
DontAskAgainCheckbox->SetText("Don't ask me again");
|
||||
LightsCheckbox->SetText("Lights");
|
||||
BrightmapsCheckbox->SetText("Brightmaps");
|
||||
WidescreenCheckbox->SetText("Widescreen");
|
||||
}
|
||||
|
||||
void LauncherWindowTab2::OnGeometryChanged()
|
||||
{
|
||||
double y = 0, h;
|
||||
|
||||
h = WelcomeLabel->GetPreferredHeight();
|
||||
WelcomeLabel->SetFrameGeometry(20, y, GetWidth() - 40, h);
|
||||
y += h;
|
||||
|
||||
h = VersionLabel->GetPreferredHeight();
|
||||
VersionLabel->SetFrameGeometry(20, y, GetWidth() - 40, h);
|
||||
y += h + 10;
|
||||
|
||||
h = SelectLabel->GetPreferredHeight();
|
||||
SelectLabel->SetFrameGeometry(20, y, GetWidth() - 40, h);
|
||||
y += h;
|
||||
|
||||
double listViewTop = y + 10, listViewBottom;
|
||||
|
||||
y = GetHeight();
|
||||
|
||||
h = DontAskAgainCheckbox->GetPreferredHeight();
|
||||
y -= h;
|
||||
DontAskAgainCheckbox->SetFrameGeometry(20, y, 190, h);
|
||||
WidescreenCheckbox->SetFrameGeometry(GetWidth() - 170, y, 150, WidescreenCheckbox->GetPreferredHeight());
|
||||
|
||||
h = DisableAutoloadCheckbox->GetPreferredHeight();
|
||||
y -= h;
|
||||
DisableAutoloadCheckbox->SetFrameGeometry(20, y, 190, h);
|
||||
BrightmapsCheckbox->SetFrameGeometry(GetWidth() - 170, y, 150, BrightmapsCheckbox->GetPreferredHeight());
|
||||
|
||||
h = FullscreenCheckbox->GetPreferredHeight();
|
||||
y -= h;
|
||||
FullscreenCheckbox->SetFrameGeometry(20, y, 190, h);
|
||||
LightsCheckbox->SetFrameGeometry(GetWidth() - 170, y, 150, LightsCheckbox->GetPreferredHeight());
|
||||
|
||||
h = GeneralLabel->GetPreferredHeight();
|
||||
y -= h;
|
||||
GeneralLabel->SetFrameGeometry(20, y, 190, GeneralLabel->GetPreferredHeight());
|
||||
ExtrasLabel->SetFrameGeometry(GetWidth() - 170, y, 150, ExtrasLabel->GetPreferredHeight());
|
||||
|
||||
listViewBottom = y - 10;
|
||||
GamesList->SetFrameGeometry(20, listViewTop, GetWidth() - 40, std::max<double>(listViewBottom - listViewTop, 0));
|
||||
}
|
||||
|
||||
LauncherWindowTab3::LauncherWindowTab3(Widget parent): Widget(nullptr)
|
||||
{
|
||||
Label = new TextLabel(this);
|
||||
Choices = new Dropdown(this);
|
||||
Popup = new PushButton(this);
|
||||
|
||||
Label->SetText("Oh my, even more widgets");
|
||||
Popup->SetText("Click me.");
|
||||
|
||||
Choices->SetMaxDisplayItems(2);
|
||||
Choices->AddItem("First");
|
||||
Choices->AddItem("Second");
|
||||
Choices->AddItem("Third");
|
||||
Choices->AddItem("Fourth");
|
||||
Choices->AddItem("Fifth");
|
||||
Choices->AddItem("Sixth");
|
||||
|
||||
Choices->OnChanged = [this](int index) {
|
||||
std::cout << "Selected " << index << ":" << Choices->GetItem(index) << std::endl;
|
||||
};
|
||||
|
||||
Popup->OnClick = []{
|
||||
std::cout << "TODO: open popup" << std::endl;
|
||||
};
|
||||
}
|
||||
|
||||
void LauncherWindowTab3::OnGeometryChanged()
|
||||
{
|
||||
double y = 0, h;
|
||||
|
||||
y += 10;
|
||||
|
||||
h = Label->GetPreferredHeight();
|
||||
Label->SetFrameGeometry(20, y, GetWidth() - 40, h);
|
||||
y += h + 10;
|
||||
|
||||
h = Choices->GetPreferredHeight();
|
||||
Choices->SetFrameGeometry(20, y, Choices->GetPreferredWidth(), h);
|
||||
y += h + 10;
|
||||
|
||||
h = Popup->GetPreferredHeight();
|
||||
Popup->SetFrameGeometry(20, y, 120, h);
|
||||
y += h;
|
||||
}
|
||||
|
||||
// ************************************************************
|
||||
// Shared code
|
||||
// ************************************************************
|
||||
|
||||
std::vector<SingleFontData> LoadWidgetFontData(const std::string& name)
|
||||
{
|
||||
|
|
@ -174,13 +315,24 @@ std::vector<uint8_t> LoadWidgetData(const std::string& name)
|
|||
return ReadAllBytes(name);
|
||||
}
|
||||
|
||||
enum class Backend {
|
||||
{
|
||||
Default, Win32, SDL2, X11, Wayland
|
||||
};
|
||||
|
||||
int example(Backend backend = Backend::Default)
|
||||
enum class Theme
|
||||
{
|
||||
WidgetTheme::SetTheme(std::make_unique<DarkWidgetTheme>());
|
||||
Default, Light, Dark
|
||||
};
|
||||
|
||||
int example(Backend backend = Backend::Default, Theme theme = Theme::Default)
|
||||
{
|
||||
// just for testing themes
|
||||
switch (theme)
|
||||
{
|
||||
case Theme::Default: WidgetTheme::SetTheme(std::make_unique<DarkWidgetTheme>()); break;
|
||||
case Theme::Dark: WidgetTheme::SetTheme(std::make_unique<DarkWidgetTheme>()); break;
|
||||
case Theme::Light: WidgetTheme::SetTheme(std::make_unique<LightWidgetTheme>()); break;
|
||||
}
|
||||
|
||||
// just for testing backends
|
||||
switch (backend)
|
||||
|
|
@ -192,40 +344,23 @@ int example(Backend backend = Backend::Default)
|
|||
case Backend::Wayland: DisplayBackend::Set(DisplayBackend::TryCreateWayland()); break;
|
||||
}
|
||||
|
||||
#if 1
|
||||
auto launcher = new LauncherWindow();
|
||||
launcher->SetFrameGeometry(100.0, 100.0, 615.0, 668.0);
|
||||
launcher->Show();
|
||||
#else
|
||||
auto mainwindow = new MainWindow();
|
||||
auto textedit = new TextEdit(mainwindow);
|
||||
textedit->SetText(R"(
|
||||
#version 460
|
||||
|
||||
in vec4 AttrPos;
|
||||
in vec4 AttrColor;
|
||||
out vec4 Color;
|
||||
|
||||
void main()
|
||||
{
|
||||
gl_Position = AttrPos;
|
||||
Color = AttrColor;
|
||||
}
|
||||
)");
|
||||
mainwindow->SetWindowTitle("ZWidget Example");
|
||||
mainwindow->SetFrameGeometry(100.0, 100.0, 1700.0, 900.0);
|
||||
mainwindow->SetCentralWidget(textedit);
|
||||
textedit->SetFocus();
|
||||
mainwindow->Show();
|
||||
#endif
|
||||
|
||||
DisplayWindow::RunLoop();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// ************************************************************
|
||||
// Platform-specific code
|
||||
// ************************************************************
|
||||
|
||||
#ifdef WIN32
|
||||
|
||||
#define WIN32_MEAN_AND_LEAN
|
||||
#define NOMINMAX
|
||||
#include <Windows.h>
|
||||
#include <stdexcept>
|
||||
|
||||
|
|
@ -277,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();
|
||||
}
|
||||
|
||||
|
|
@ -305,18 +441,25 @@ static std::vector<uint8_t> ReadAllBytes(const std::string& filename)
|
|||
|
||||
int main(int argc, const char** argv)
|
||||
{
|
||||
std::string backendStr = argc > 1? argv[1]: "";
|
||||
std::transform(backendStr.begin(), backendStr.end(), backendStr.begin(),
|
||||
[](unsigned char c){ return std::tolower(c); });
|
||||
|
||||
Backend backend = Backend::Default;
|
||||
Theme theme = Theme::Default;
|
||||
|
||||
if (backendStr == "sdl2") backend = Backend::SDL2;
|
||||
if (backendStr == "x11") backend = Backend::X11;
|
||||
if (backendStr == "wayland") backend = Backend::Wayland;
|
||||
if (backendStr == "win32") backend = Backend::Win32; // lol
|
||||
for (auto i = 1; i < argc; i++)
|
||||
{
|
||||
std::string s = argv[i];
|
||||
std::transform(s.begin(), s.end(), s.begin(),
|
||||
[](unsigned char c){ return std::tolower(c); });
|
||||
|
||||
example(backend);
|
||||
if (s == "light") { theme = Theme::Light; continue; }
|
||||
if (s == "dark") { theme = Theme::Dark; continue; }
|
||||
|
||||
if (s == "sdl2") { backend = Backend::SDL2; continue; }
|
||||
if (s == "x11") { backend = Backend::X11; continue; }
|
||||
if (s == "wayland") { backend = Backend::Wayland; continue; }
|
||||
if (s == "win32") { backend = Backend::Win32; continue; } // lol
|
||||
}
|
||||
|
||||
example(backend, theme);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -260,7 +260,7 @@ int decodePNG(std::vector<unsigned char>& out_image, unsigned long& image_width,
|
|||
readPngHeader(&in[0], size); if(error) return;
|
||||
size_t pos = 33; //first byte of the first chunk after the header
|
||||
std::vector<unsigned char> idat; //the data from idat chunks
|
||||
bool IEND = false, known_type = true;
|
||||
bool IEND = false/*, known_type = true*/;
|
||||
info.key_defined = false;
|
||||
while(!IEND) //loop through the chunks, ignoring unknown chunks and stopping at IEND chunk. IDAT data is put at the start of the in buffer
|
||||
{
|
||||
|
|
@ -312,7 +312,7 @@ int decodePNG(std::vector<unsigned char>& out_image, unsigned long& image_width,
|
|||
{
|
||||
if(!(in[pos + 0] & 32)) { error = 69; return; } //error: unknown critical chunk (5th bit of first byte of chunk type is 0)
|
||||
pos += (chunkLength + 4); //skip 4 letters and uninterpreted data of unimplemented chunk
|
||||
known_type = false;
|
||||
//known_type = false;
|
||||
}
|
||||
pos += 4; //step over CRC (which is ignored)
|
||||
}
|
||||
|
|
|
|||
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 |
|
|
@ -17,6 +17,21 @@ public:
|
|||
return { r * s, g * s, b * s, a * s };
|
||||
}
|
||||
|
||||
static Colorf fromRgba(uint32_t rgba)
|
||||
{
|
||||
return fromRgba8(
|
||||
0xff & rgba>>24,
|
||||
0xff & rgba>>16,
|
||||
0xff & rgba>>8,
|
||||
0xff & rgba
|
||||
);
|
||||
}
|
||||
|
||||
static Colorf fromRgb(uint32_t rgb)
|
||||
{
|
||||
return fromRgba(rgb<<8 | 0xff);
|
||||
}
|
||||
|
||||
uint32_t toBgra8() const
|
||||
{
|
||||
uint32_t cr = (int)(std::max(std::min(r * 255.0f, 255.0f), 0.0f));
|
||||
|
|
|
|||
|
|
@ -212,7 +212,7 @@ private:
|
|||
Colorf cursor_color;
|
||||
|
||||
std::string::size_type sel_start = 0, sel_end = 0;
|
||||
Colorf sel_foreground, sel_background = Colorf::fromRgba8(153, 201, 239);
|
||||
Colorf sel_foreground, sel_background;
|
||||
|
||||
std::string text;
|
||||
std::vector<SpanObject> objects;
|
||||
|
|
|
|||
|
|
@ -55,7 +55,23 @@ public:
|
|||
|
||||
class WidgetTheme
|
||||
{
|
||||
struct SimpleTheme {
|
||||
const Colorf bgMain; // background
|
||||
const Colorf fgMain; //
|
||||
const Colorf bgLight; // headers / inputs
|
||||
const Colorf fgLight; //
|
||||
const Colorf bgAction; // interactive elements
|
||||
const Colorf fgAction; //
|
||||
const Colorf bgHover; // hover / highlight
|
||||
const Colorf fgHover; //
|
||||
const Colorf bgActive; // click
|
||||
const Colorf fgActive; //
|
||||
const Colorf border; // around elements
|
||||
const Colorf divider; // between elements
|
||||
};
|
||||
public:
|
||||
WidgetTheme() {}
|
||||
WidgetTheme(const struct SimpleTheme &theme);
|
||||
virtual ~WidgetTheme() = default;
|
||||
|
||||
WidgetStyle* RegisterStyle(std::unique_ptr<WidgetStyle> widgetStyle, const std::string& widgetClass);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -83,6 +83,11 @@ private:
|
|||
SpanLayout layout;
|
||||
Rect box;
|
||||
bool invalidated = true;
|
||||
|
||||
Line(const TextEdit *self)
|
||||
{
|
||||
layout.SetSelectionColors(self->selectionFG, self->selectionBG);
|
||||
}
|
||||
};
|
||||
|
||||
struct ivec2
|
||||
|
|
@ -96,9 +101,10 @@ private:
|
|||
bool operator!=(const ivec2& b) const { return x != b.x || y != b.y; }
|
||||
};
|
||||
|
||||
Colorf selectionBG, selectionFG;
|
||||
Scrollbar* vert_scrollbar;
|
||||
Timer* timer = nullptr;
|
||||
std::vector<Line> lines = { Line() };
|
||||
std::vector<Line> lines = { Line{this} };
|
||||
ivec2 cursor_pos = { 0, 0 };
|
||||
int max_length = -1;
|
||||
bool mouse_selecting = false;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -549,9 +549,7 @@ void BitmapCanvas::plot(float x, float y, float alpha, const Colorf& color)
|
|||
int xx = (int)x;
|
||||
int yy = (int)y;
|
||||
|
||||
int dwidth = width;
|
||||
int dheight = height;
|
||||
uint32_t* dest = pixels.data() + xx + yy * dwidth;
|
||||
uint32_t* dest = pixels.data() + xx + yy * width;
|
||||
|
||||
uint32_t cred = (int32_t)clamp(color.r * 256.0f, 0.0f, 256.0f);
|
||||
uint32_t cgreen = (int32_t)clamp(color.g * 256.0f, 0.0f, 256.0f);
|
||||
|
|
@ -665,7 +663,6 @@ void BitmapCanvas::fillTile(float left, float top, float width, float height, Co
|
|||
return;
|
||||
|
||||
int dwidth = this->width;
|
||||
int dheight = this->height;
|
||||
uint32_t* dest = this->pixels.data();
|
||||
|
||||
int x0 = (int)left;
|
||||
|
|
@ -771,11 +768,9 @@ void BitmapCanvas::drawTile(CanvasTexture* tex, float left, float top, float wid
|
|||
|
||||
auto texture = static_cast<BitmapTexture*>(tex);
|
||||
int swidth = texture->Width;
|
||||
int sheight = texture->Height;
|
||||
const uint32_t* src = texture->Data.data();
|
||||
|
||||
int dwidth = this->width;
|
||||
int dheight = this->height;
|
||||
uint32_t* dest = this->pixels.data();
|
||||
|
||||
int x0 = (int)left;
|
||||
|
|
@ -880,11 +875,9 @@ void BitmapCanvas::drawGlyph(CanvasTexture* tex, float left, float top, float wi
|
|||
|
||||
auto texture = static_cast<BitmapTexture*>(tex);
|
||||
int swidth = texture->Width;
|
||||
int sheight = texture->Height;
|
||||
const uint32_t* src = texture->Data.data();
|
||||
|
||||
int dwidth = this->width;
|
||||
int dheight = this->height;
|
||||
uint32_t* dest = this->pixels.data();
|
||||
|
||||
int x0 = (int)left;
|
||||
|
|
|
|||
|
|
@ -255,7 +255,7 @@ PathFillRasterizer::Extent PathFillRasterizer::FindExtent(const PathScanline* sc
|
|||
|
||||
void PathFillRasterizer::Clear()
|
||||
{
|
||||
for (size_t y = first_scanline; y < last_scanline; y++)
|
||||
for (int y = first_scanline; y < last_scanline; y++)
|
||||
{
|
||||
auto& scanline = scanlines[y];
|
||||
if (!scanline.edges.empty())
|
||||
|
|
|
|||
|
|
@ -262,7 +262,7 @@ int decodePNG(std::vector<unsigned char>& out_image, unsigned long& image_width,
|
|||
readPngHeader(&in[0], size); if(error) return;
|
||||
size_t pos = 33; //first byte of the first chunk after the header
|
||||
std::vector<unsigned char> idat; //the data from idat chunks
|
||||
bool IEND = false, known_type = true;
|
||||
bool IEND = false/*, known_type = true*/;
|
||||
info.key_defined = false;
|
||||
while(!IEND) //loop through the chunks, ignoring unknown chunks and stopping at IEND chunk. IDAT data is put at the start of the in buffer
|
||||
{
|
||||
|
|
@ -314,7 +314,7 @@ int decodePNG(std::vector<unsigned char>& out_image, unsigned long& image_width,
|
|||
{
|
||||
if(!(in[pos + 0] & 32)) { error = 69; return; } //error: unknown critical chunk (5th bit of first byte of chunk type is 0)
|
||||
pos += (chunkLength + 4); //skip 4 letters and uninterpreted data of unimplemented chunk
|
||||
known_type = false;
|
||||
/*known_type = false;*/
|
||||
}
|
||||
pos += 4; //step over CRC (which is ignored)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -72,16 +72,12 @@ void SpanLayout::DrawLayout(Canvas* canvas)
|
|||
LineSegment& segment = line.segments.back();
|
||||
if (cursor_visible && segment.end <= cursor_pos)
|
||||
{
|
||||
switch (segment.type)
|
||||
{
|
||||
case object_text:
|
||||
if (segment.type == object_text)
|
||||
{
|
||||
double cursor_x = x + segment.x_position + canvas->measureText(segment.font, text.substr(segment.start, segment.end - segment.start)).width;
|
||||
double cursor_width = 1;
|
||||
canvas->fillRect(Rect::ltrb(cursor_x, y + line.ascender - segment.ascender, cursor_width, y + line.ascender + segment.descender), cursor_color);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -141,12 +141,26 @@ WidgetTheme* WidgetTheme::GetTheme()
|
|||
return CurrentTheme.get();
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
DarkWidgetTheme::DarkWidgetTheme()
|
||||
WidgetTheme::WidgetTheme(const struct SimpleTheme &theme)
|
||||
{
|
||||
|
||||
auto bgMain = theme.bgMain; // background
|
||||
auto fgMain = theme.fgMain; //
|
||||
auto bgLight = theme.bgLight; // headers / inputs
|
||||
auto fgLight = theme.fgLight; //
|
||||
auto bgAction = theme.bgAction; // interactive elements
|
||||
auto fgAction = theme.fgAction; //
|
||||
auto bgHover = theme.bgHover; // hover / highlight
|
||||
auto fgHover = theme.fgHover; //
|
||||
auto bgActive = theme.bgActive; // click
|
||||
auto fgActive = theme.fgActive; //
|
||||
auto border = theme.border; // around elements
|
||||
auto divider = theme.divider; // between elements
|
||||
|
||||
auto none = Colorf::transparent();
|
||||
|
||||
auto widget = RegisterStyle(std::make_unique<BasicWidgetStyle>(), "widget");
|
||||
auto textlabel = RegisterStyle(std::make_unique<BasicWidgetStyle>(widget), "textlabel");
|
||||
/*auto textlabel =*/ RegisterStyle(std::make_unique<BasicWidgetStyle>(widget), "textlabel");
|
||||
auto pushbutton = RegisterStyle(std::make_unique<BasicWidgetStyle>(widget), "pushbutton");
|
||||
auto lineedit = RegisterStyle(std::make_unique<BasicWidgetStyle>(widget), "lineedit");
|
||||
auto textedit = RegisterStyle(std::make_unique<BasicWidgetStyle>(widget), "textedit");
|
||||
|
|
@ -167,272 +181,177 @@ DarkWidgetTheme::DarkWidgetTheme()
|
|||
auto statusbar = RegisterStyle(std::make_unique<BasicWidgetStyle>(widget), "statusbar");
|
||||
|
||||
widget->SetString("font-family", "NotoSans");
|
||||
widget->SetColor("color", Colorf::fromRgba8(226, 223, 219));
|
||||
widget->SetColor("window-background", Colorf::fromRgba8(51, 51, 51));
|
||||
widget->SetColor("window-border", Colorf::fromRgba8(51, 51, 51));
|
||||
widget->SetColor("window-caption-color", Colorf::fromRgba8(33, 33, 33));
|
||||
widget->SetColor("window-caption-text-color", Colorf::fromRgba8(226, 223, 219));
|
||||
widget->SetColor("color", fgMain);
|
||||
widget->SetColor("window-background", bgMain);
|
||||
widget->SetColor("window-border", bgMain);
|
||||
widget->SetColor("window-caption-color", bgLight);
|
||||
widget->SetColor("window-caption-text-color", fgLight);
|
||||
|
||||
pushbutton->SetDouble("noncontent-left", 10.0);
|
||||
pushbutton->SetDouble("noncontent-top", 5.0);
|
||||
pushbutton->SetDouble("noncontent-right", 10.0);
|
||||
pushbutton->SetDouble("noncontent-bottom", 5.0);
|
||||
pushbutton->SetColor("background-color", Colorf::fromRgba8(68, 68, 68));
|
||||
pushbutton->SetColor("border-left-color", Colorf::fromRgba8(100, 100, 100));
|
||||
pushbutton->SetColor("border-top-color", Colorf::fromRgba8(100, 100, 100));
|
||||
pushbutton->SetColor("border-right-color", Colorf::fromRgba8(100, 100, 100));
|
||||
pushbutton->SetColor("border-bottom-color", Colorf::fromRgba8(100, 100, 100));
|
||||
pushbutton->SetColor("hover", "background-color", Colorf::fromRgba8(78, 78, 78));
|
||||
pushbutton->SetColor("down", "background-color", Colorf::fromRgba8(88, 88, 88));
|
||||
pushbutton->SetColor("color", fgAction);
|
||||
pushbutton->SetColor("background-color", bgAction);
|
||||
pushbutton->SetColor("border-left-color", border);
|
||||
pushbutton->SetColor("border-top-color", border);
|
||||
pushbutton->SetColor("border-right-color", border);
|
||||
pushbutton->SetColor("border-bottom-color", border);
|
||||
pushbutton->SetColor("hover", "color", fgHover);
|
||||
pushbutton->SetColor("hover", "background-color", bgHover);
|
||||
pushbutton->SetColor("down", "color", fgActive);
|
||||
pushbutton->SetColor("down", "background-color", bgActive);
|
||||
|
||||
lineedit->SetDouble("noncontent-left", 5.0);
|
||||
lineedit->SetDouble("noncontent-top", 3.0);
|
||||
lineedit->SetDouble("noncontent-right", 5.0);
|
||||
lineedit->SetDouble("noncontent-bottom", 3.0);
|
||||
lineedit->SetColor("background-color", Colorf::fromRgba8(38, 38, 38));
|
||||
lineedit->SetColor("border-left-color", Colorf::fromRgba8(100, 100, 100));
|
||||
lineedit->SetColor("border-top-color", Colorf::fromRgba8(100, 100, 100));
|
||||
lineedit->SetColor("border-right-color", Colorf::fromRgba8(100, 100, 100));
|
||||
lineedit->SetColor("border-bottom-color", Colorf::fromRgba8(100, 100, 100));
|
||||
lineedit->SetColor("selection-color", Colorf::fromRgba8(100, 100, 100));
|
||||
lineedit->SetColor("no-focus-selection-color", Colorf::fromRgba8(68, 68, 68));
|
||||
lineedit->SetColor("color", fgLight);
|
||||
lineedit->SetColor("background-color", bgLight);
|
||||
lineedit->SetColor("border-left-color", border);
|
||||
lineedit->SetColor("border-top-color", border);
|
||||
lineedit->SetColor("border-right-color", border);
|
||||
lineedit->SetColor("border-bottom-color", border);
|
||||
lineedit->SetColor("selection-color", bgHover);
|
||||
lineedit->SetColor("no-focus-selection-color", bgHover);
|
||||
|
||||
textedit->SetDouble("noncontent-left", 8.0);
|
||||
textedit->SetDouble("noncontent-top", 8.0);
|
||||
textedit->SetDouble("noncontent-right", 8.0);
|
||||
textedit->SetDouble("noncontent-bottom", 8.0);
|
||||
textedit->SetColor("background-color", Colorf::fromRgba8(38, 38, 38));
|
||||
textedit->SetColor("border-left-color", Colorf::fromRgba8(100, 100, 100));
|
||||
textedit->SetColor("border-top-color", Colorf::fromRgba8(100, 100, 100));
|
||||
textedit->SetColor("border-right-color", Colorf::fromRgba8(100, 100, 100));
|
||||
textedit->SetColor("border-bottom-color", Colorf::fromRgba8(100, 100, 100));
|
||||
textedit->SetColor("color", fgLight);
|
||||
textedit->SetColor("background-color", bgLight);
|
||||
textedit->SetColor("border-left-color", border);
|
||||
textedit->SetColor("border-top-color", border);
|
||||
textedit->SetColor("border-right-color", border);
|
||||
textedit->SetColor("border-bottom-color", border);
|
||||
textedit->SetColor("selection-color", bgHover);
|
||||
|
||||
listview->SetDouble("noncontent-left", 10.0);
|
||||
listview->SetDouble("noncontent-top", 10.0);
|
||||
listview->SetDouble("noncontent-right", 3.0);
|
||||
listview->SetDouble("noncontent-bottom", 10.0);
|
||||
listview->SetColor("background-color", Colorf::fromRgba8(38, 38, 38));
|
||||
listview->SetColor("border-left-color", Colorf::fromRgba8(100, 100, 100));
|
||||
listview->SetColor("border-top-color", Colorf::fromRgba8(100, 100, 100));
|
||||
listview->SetColor("border-right-color", Colorf::fromRgba8(100, 100, 100));
|
||||
listview->SetColor("border-bottom-color", Colorf::fromRgba8(100, 100, 100));
|
||||
listview->SetColor("selection-color", Colorf::fromRgba8(100, 100, 100));
|
||||
listview->SetColor("color", fgLight);
|
||||
listview->SetColor("background-color", bgLight);
|
||||
listview->SetColor("border-left-color", border);
|
||||
listview->SetColor("border-top-color", border);
|
||||
listview->SetColor("border-right-color", border);
|
||||
listview->SetColor("border-bottom-color", border);
|
||||
listview->SetColor("selection-color", bgHover);
|
||||
|
||||
dropdown->SetDouble("noncontent-left", 5.0);
|
||||
dropdown->SetDouble("noncontent-top", 5.0);
|
||||
dropdown->SetDouble("noncontent-right", 5.0);
|
||||
dropdown->SetDouble("noncontent-bottom", 5.0);
|
||||
dropdown->SetColor("background-color", Colorf::fromRgba8(38, 38, 38));
|
||||
dropdown->SetColor("border-left-color", Colorf::fromRgba8(100, 100, 100));
|
||||
dropdown->SetColor("border-top-color", Colorf::fromRgba8(100, 100, 100));
|
||||
dropdown->SetColor("border-right-color", Colorf::fromRgba8(100, 100, 100));
|
||||
dropdown->SetColor("border-bottom-color", Colorf::fromRgba8(100, 100, 100));
|
||||
dropdown->SetColor("selection-color", Colorf::fromRgba8(100, 100, 100));
|
||||
dropdown->SetColor("arrow-color", Colorf::fromRgba8(100, 100, 100));
|
||||
dropdown->SetColor("color", fgLight);
|
||||
dropdown->SetColor("background-color", bgLight);
|
||||
dropdown->SetColor("border-left-color", border);
|
||||
dropdown->SetColor("border-top-color", border);
|
||||
dropdown->SetColor("border-right-color", border);
|
||||
dropdown->SetColor("border-bottom-color", border);
|
||||
dropdown->SetColor("arrow-color", border);
|
||||
|
||||
scrollbar->SetColor("track-color", Colorf::fromRgba8(33, 33, 33));
|
||||
scrollbar->SetColor("thumb-color", Colorf::fromRgba8(58, 58, 58));
|
||||
scrollbar->SetColor("track-color", divider);
|
||||
scrollbar->SetColor("thumb-color", border);
|
||||
|
||||
tabbar->SetDouble("spacer-left", 20.0);
|
||||
tabbar->SetDouble("spacer-right", 20.0);
|
||||
tabbar->SetColor("background-color", Colorf::fromRgba8(33, 33, 33));
|
||||
tabbar->SetColor("background-color", bgLight);
|
||||
|
||||
tabbar_tab->SetDouble("noncontent-left", 15.0);
|
||||
tabbar_tab->SetDouble("noncontent-right", 15.0);
|
||||
tabbar_tab->SetDouble("noncontent-top", 1.0);
|
||||
tabbar_tab->SetDouble("noncontent-bottom", 1.0);
|
||||
tabbar_tab->SetColor("background-color", Colorf::fromRgba8(38, 38, 38));
|
||||
tabbar_tab->SetColor("border-left-color", Colorf::fromRgba8(68, 68, 68));
|
||||
tabbar_tab->SetColor("border-top-color", Colorf::fromRgba8(68, 68, 68));
|
||||
tabbar_tab->SetColor("border-right-color", Colorf::fromRgba8(68, 68, 68));
|
||||
tabbar_tab->SetColor("border-bottom-color", Colorf::fromRgba8(100, 100, 100));
|
||||
tabbar_tab->SetColor("hover", "background-color", Colorf::fromRgba8(45, 45, 45));
|
||||
tabbar_tab->SetColor("active", "background-color", Colorf::fromRgba8(51, 51, 51));
|
||||
tabbar_tab->SetColor("active", "border-left-color", Colorf::fromRgba8(100, 100, 100));
|
||||
tabbar_tab->SetColor("active", "border-top-color", Colorf::fromRgba8(100, 100, 100));
|
||||
tabbar_tab->SetColor("active", "border-right-color", Colorf::fromRgba8(100, 100, 100));
|
||||
tabbar_tab->SetColor("active", "border-bottom-color", Colorf::transparent());
|
||||
tabbar_tab->SetColor("color", fgMain);
|
||||
tabbar_tab->SetColor("background-color", bgMain);
|
||||
tabbar_tab->SetColor("border-left-color", divider);
|
||||
tabbar_tab->SetColor("border-top-color", divider);
|
||||
tabbar_tab->SetColor("border-right-color", divider);
|
||||
tabbar_tab->SetColor("border-bottom-color", border);
|
||||
tabbar_tab->SetColor("hover", "color", fgAction);
|
||||
tabbar_tab->SetColor("hover", "background-color", bgAction);
|
||||
tabbar_tab->SetColor("active", "background-color", bgMain);
|
||||
tabbar_tab->SetColor("active", "border-left-color", border);
|
||||
tabbar_tab->SetColor("active", "border-top-color", border);
|
||||
tabbar_tab->SetColor("active", "border-right-color", border);
|
||||
tabbar_tab->SetColor("active", "border-bottom-color", none);
|
||||
|
||||
tabbar_spacer->SetDouble("noncontent-bottom", 1.0);
|
||||
tabbar_spacer->SetColor("border-bottom-color", Colorf::fromRgba8(100, 100, 100));
|
||||
tabbar_spacer->SetColor("border-bottom-color", border);
|
||||
|
||||
tabwidget_stack->SetDouble("noncontent-left", 20.0);
|
||||
tabwidget_stack->SetDouble("noncontent-top", 5.0);
|
||||
tabwidget_stack->SetDouble("noncontent-right", 20.0);
|
||||
tabwidget_stack->SetDouble("noncontent-bottom", 5.0);
|
||||
|
||||
checkbox_label->SetColor("checked-outer-border-color", Colorf::fromRgba8(100, 100, 100));
|
||||
checkbox_label->SetColor("checked-inner-border-color", Colorf::fromRgba8(51, 51, 51));
|
||||
checkbox_label->SetColor("checked-color", Colorf::fromRgba8(226, 223, 219));
|
||||
checkbox_label->SetColor("unchecked-outer-border-color", Colorf::fromRgba8(99, 99, 99));
|
||||
checkbox_label->SetColor("unchecked-inner-border-color", Colorf::fromRgba8(51, 51, 51));
|
||||
checkbox_label->SetColor("checked-outer-border-color", border);
|
||||
checkbox_label->SetColor("checked-inner-border-color", bgMain);
|
||||
checkbox_label->SetColor("checked-color", fgMain);
|
||||
checkbox_label->SetColor("unchecked-outer-border-color", border);
|
||||
checkbox_label->SetColor("unchecked-inner-border-color", bgMain);
|
||||
|
||||
menubar->SetColor("background-color", Colorf::fromRgba8(33, 33, 33));
|
||||
toolbar->SetColor("background-color", Colorf::fromRgba8(33, 33, 33));
|
||||
statusbar->SetColor("background-color", Colorf::fromRgba8(33, 33, 33));
|
||||
menubar->SetColor("background-color", bgLight);
|
||||
toolbar->SetColor("background-color", bgLight);
|
||||
statusbar->SetColor("background-color", bgLight);
|
||||
|
||||
toolbarbutton->SetColor("hover", "background-color", Colorf::fromRgba8(78, 78, 78));
|
||||
toolbarbutton->SetColor("down", "background-color", Colorf::fromRgba8(88, 88, 88));
|
||||
toolbarbutton->SetColor("hover", "color", fgHover);
|
||||
toolbarbutton->SetColor("hover", "background-color", bgHover);
|
||||
toolbarbutton->SetColor("down", "color", fgActive);
|
||||
toolbarbutton->SetColor("down", "background-color", bgActive);
|
||||
|
||||
menubaritem->SetColor("color", Colorf::fromRgba8(226, 223, 219));
|
||||
menubaritem->SetColor("hover", "background-color", Colorf::fromRgba8(78, 78, 78));
|
||||
menubaritem->SetColor("hover", "color", Colorf::fromRgba8(0, 0, 0));
|
||||
menubaritem->SetColor("down", "background-color", Colorf::fromRgba8(88, 88, 88));
|
||||
menubaritem->SetColor("down", "color", Colorf::fromRgba8(0, 0, 0));
|
||||
menubaritem->SetColor("color", fgMain);
|
||||
menubaritem->SetColor("hover", "color", fgHover);
|
||||
menubaritem->SetColor("hover", "background-color", bgHover);
|
||||
menubaritem->SetColor("down", "color", fgActive);
|
||||
menubaritem->SetColor("down", "background-color", bgActive);
|
||||
|
||||
menu->SetDouble("noncontent-left", 5.0);
|
||||
menu->SetDouble("noncontent-top", 5.0);
|
||||
menu->SetDouble("noncontent-right", 5.0);
|
||||
menu->SetDouble("noncontent-bottom", 5.0);
|
||||
menu->SetColor("border-left-color", Colorf::fromRgba8(100, 100, 100));
|
||||
menu->SetColor("border-top-color", Colorf::fromRgba8(100, 100, 100));
|
||||
menu->SetColor("border-right-color", Colorf::fromRgba8(100, 100, 100));
|
||||
menu->SetColor("border-bottom-color", Colorf::fromRgba8(100, 100, 100));
|
||||
menu->SetColor("color", fgMain);
|
||||
menu->SetColor("background-color", bgMain);
|
||||
menu->SetColor("border-left-color", border);
|
||||
menu->SetColor("border-top-color", border);
|
||||
menu->SetColor("border-right-color", border);
|
||||
menu->SetColor("border-bottom-color", border);
|
||||
|
||||
menuitem->SetColor("hover", "background-color", Colorf::fromRgba8(78, 78, 78));
|
||||
menuitem->SetColor("down", "background-color", Colorf::fromRgba8(88, 88, 88));
|
||||
menuitem->SetColor("hover", "color", fgHover);
|
||||
menuitem->SetColor("hover", "background-color", bgHover);
|
||||
menuitem->SetColor("down", "color", fgActive);
|
||||
menuitem->SetColor("down", "background-color", bgActive);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
LightWidgetTheme::LightWidgetTheme()
|
||||
{
|
||||
auto widget = RegisterStyle(std::make_unique<BasicWidgetStyle>(), "widget");
|
||||
auto textlabel = RegisterStyle(std::make_unique<BasicWidgetStyle>(widget), "textlabel");
|
||||
auto pushbutton = RegisterStyle(std::make_unique<BasicWidgetStyle>(widget), "pushbutton");
|
||||
auto lineedit = RegisterStyle(std::make_unique<BasicWidgetStyle>(widget), "lineedit");
|
||||
auto textedit = RegisterStyle(std::make_unique<BasicWidgetStyle>(widget), "textedit");
|
||||
auto listview = RegisterStyle(std::make_unique<BasicWidgetStyle>(widget), "listview");
|
||||
auto dropdown = RegisterStyle(std::make_unique<BasicWidgetStyle>(widget), "dropdown");
|
||||
auto scrollbar = RegisterStyle(std::make_unique<BasicWidgetStyle>(widget), "scrollbar");
|
||||
auto tabbar = RegisterStyle(std::make_unique<BasicWidgetStyle>(widget), "tabbar");
|
||||
auto tabbar_tab = RegisterStyle(std::make_unique<BasicWidgetStyle>(widget), "tabbar-tab");
|
||||
auto tabbar_spacer = RegisterStyle(std::make_unique<BasicWidgetStyle>(widget), "tabbar-spacer");
|
||||
auto tabwidget_stack = RegisterStyle(std::make_unique<BasicWidgetStyle>(widget), "tabwidget-stack");
|
||||
auto checkbox_label = RegisterStyle(std::make_unique<BasicWidgetStyle>(widget), "checkbox-label");
|
||||
auto menubar = RegisterStyle(std::make_unique<BasicWidgetStyle>(widget), "menubar");
|
||||
auto menubaritem = RegisterStyle(std::make_unique<BasicWidgetStyle>(widget), "menubaritem");
|
||||
auto menu = RegisterStyle(std::make_unique<BasicWidgetStyle>(widget), "menu");
|
||||
auto menuitem = RegisterStyle(std::make_unique<BasicWidgetStyle>(widget), "menuitem");
|
||||
DarkWidgetTheme::DarkWidgetTheme(): WidgetTheme({
|
||||
Colorf::fromRgb(0x2A2A2A), // background
|
||||
Colorf::fromRgb(0xE2DFDB), //
|
||||
Colorf::fromRgb(0x212121), // headers / inputs
|
||||
Colorf::fromRgb(0xE2DFDB), //
|
||||
Colorf::fromRgb(0x444444), // interactive elements
|
||||
Colorf::fromRgb(0xFFFFFF), //
|
||||
Colorf::fromRgb(0xC83C00), // hover / highlight
|
||||
Colorf::fromRgb(0xFFFFFF), //
|
||||
Colorf::fromRgb(0xBBBBBB), // click
|
||||
Colorf::fromRgb(0x000000), //
|
||||
Colorf::fromRgb(0x646464), // around elements
|
||||
Colorf::fromRgb(0x555555) // between elements
|
||||
}) {};
|
||||
|
||||
widget->SetString("font-family", "NotoSans");
|
||||
widget->SetColor("color", Colorf::fromRgba8(0, 0, 0));
|
||||
widget->SetColor("window-background", Colorf::fromRgba8(240, 240, 240));
|
||||
widget->SetColor("window-border", Colorf::fromRgba8(100, 100, 100));
|
||||
widget->SetColor("window-caption-color", Colorf::fromRgba8(70, 70, 70));
|
||||
widget->SetColor("window-caption-text-color", Colorf::fromRgba8(226, 223, 219));
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
pushbutton->SetDouble("noncontent-left", 10.0);
|
||||
pushbutton->SetDouble("noncontent-top", 5.0);
|
||||
pushbutton->SetDouble("noncontent-right", 10.0);
|
||||
pushbutton->SetDouble("noncontent-bottom", 5.0);
|
||||
pushbutton->SetColor("background-color", Colorf::fromRgba8(210, 210, 210));
|
||||
pushbutton->SetColor("border-left-color", Colorf::fromRgba8(155, 155, 155));
|
||||
pushbutton->SetColor("border-top-color", Colorf::fromRgba8(155, 155, 155));
|
||||
pushbutton->SetColor("border-right-color", Colorf::fromRgba8(155, 155, 155));
|
||||
pushbutton->SetColor("border-bottom-color", Colorf::fromRgba8(155, 155, 155));
|
||||
pushbutton->SetColor("hover", "background-color", Colorf::fromRgba8(200, 200, 200));
|
||||
pushbutton->SetColor("down", "background-color", Colorf::fromRgba8(190, 190, 190));
|
||||
|
||||
lineedit->SetDouble("noncontent-left", 5.0);
|
||||
lineedit->SetDouble("noncontent-top", 3.0);
|
||||
lineedit->SetDouble("noncontent-right", 5.0);
|
||||
lineedit->SetDouble("noncontent-bottom", 3.0);
|
||||
lineedit->SetColor("background-color", Colorf::fromRgba8(255, 255, 255));
|
||||
lineedit->SetColor("border-left-color", Colorf::fromRgba8(155, 155, 155));
|
||||
lineedit->SetColor("border-top-color", Colorf::fromRgba8(155, 155, 155));
|
||||
lineedit->SetColor("border-right-color", Colorf::fromRgba8(155, 155, 155));
|
||||
lineedit->SetColor("border-bottom-color", Colorf::fromRgba8(155, 155, 155));
|
||||
lineedit->SetColor("selection-color", Colorf::fromRgba8(210, 210, 255));
|
||||
lineedit->SetColor("no-focus-selection-color", Colorf::fromRgba8(240, 240, 255));
|
||||
|
||||
textedit->SetDouble("noncontent-left", 8.0);
|
||||
textedit->SetDouble("noncontent-top", 8.0);
|
||||
textedit->SetDouble("noncontent-right", 8.0);
|
||||
textedit->SetDouble("noncontent-bottom", 8.0);
|
||||
textedit->SetColor("background-color", Colorf::fromRgba8(255, 255, 255));
|
||||
textedit->SetColor("border-left-color", Colorf::fromRgba8(155, 155, 155));
|
||||
textedit->SetColor("border-top-color", Colorf::fromRgba8(155, 155, 155));
|
||||
textedit->SetColor("border-right-color", Colorf::fromRgba8(155, 155, 155));
|
||||
textedit->SetColor("border-bottom-color", Colorf::fromRgba8(155, 155, 155));
|
||||
|
||||
listview->SetDouble("noncontent-left", 10.0);
|
||||
listview->SetDouble("noncontent-top", 10.0);
|
||||
listview->SetDouble("noncontent-right", 3.0);
|
||||
listview->SetDouble("noncontent-bottom", 10.0);
|
||||
listview->SetColor("background-color", Colorf::fromRgba8(230, 230, 230));
|
||||
listview->SetColor("border-left-color", Colorf::fromRgba8(155, 155, 155));
|
||||
listview->SetColor("border-top-color", Colorf::fromRgba8(155, 155, 155));
|
||||
listview->SetColor("border-right-color", Colorf::fromRgba8(155, 155, 155));
|
||||
listview->SetColor("border-bottom-color", Colorf::fromRgba8(155, 155, 155));
|
||||
listview->SetColor("selection-color", Colorf::fromRgba8(200, 200, 200));
|
||||
|
||||
dropdown->SetDouble("noncontent-left", 5.0);
|
||||
dropdown->SetDouble("noncontent-top", 5.0);
|
||||
dropdown->SetDouble("noncontent-right", 5.0);
|
||||
dropdown->SetDouble("noncontent-bottom", 5.0);
|
||||
dropdown->SetColor("background-color", Colorf::fromRgba8(230, 230, 230));
|
||||
dropdown->SetColor("border-left-color", Colorf::fromRgba8(155, 155, 155));
|
||||
dropdown->SetColor("border-top-color", Colorf::fromRgba8(155, 155, 155));
|
||||
dropdown->SetColor("border-right-color", Colorf::fromRgba8(155, 155, 155));
|
||||
dropdown->SetColor("border-bottom-color", Colorf::fromRgba8(155, 155, 155));
|
||||
dropdown->SetColor("selection-color", Colorf::fromRgba8(200, 200, 200));
|
||||
dropdown->SetColor("arrow-color", Colorf::fromRgba8(200, 200, 200));
|
||||
|
||||
scrollbar->SetColor("track-color", Colorf::fromRgba8(210, 210, 220));
|
||||
scrollbar->SetColor("thumb-color", Colorf::fromRgba8(180, 180, 180));
|
||||
|
||||
tabbar->SetDouble("spacer-left", 20.0);
|
||||
tabbar->SetDouble("spacer-right", 20.0);
|
||||
tabbar->SetColor("background-color", Colorf::fromRgba8(210, 210, 210));
|
||||
|
||||
tabbar_tab->SetDouble("noncontent-left", 15.0);
|
||||
tabbar_tab->SetDouble("noncontent-right", 15.0);
|
||||
tabbar_tab->SetDouble("noncontent-top", 1.0);
|
||||
tabbar_tab->SetDouble("noncontent-bottom", 1.0);
|
||||
tabbar_tab->SetColor("background-color", Colorf::fromRgba8(220, 220, 220));
|
||||
tabbar_tab->SetColor("border-left-color", Colorf::fromRgba8(200, 200, 200));
|
||||
tabbar_tab->SetColor("border-top-color", Colorf::fromRgba8(200, 200, 200));
|
||||
tabbar_tab->SetColor("border-right-color", Colorf::fromRgba8(200, 200, 200));
|
||||
tabbar_tab->SetColor("border-bottom-color", Colorf::fromRgba8(155, 155, 155));
|
||||
tabbar_tab->SetColor("hover", "background-color", Colorf::fromRgba8(210, 210, 210));
|
||||
tabbar_tab->SetColor("active", "background-color", Colorf::fromRgba8(240, 240, 240));
|
||||
tabbar_tab->SetColor("active", "border-left-color", Colorf::fromRgba8(155, 155, 155));
|
||||
tabbar_tab->SetColor("active", "border-top-color", Colorf::fromRgba8(155, 155, 155));
|
||||
tabbar_tab->SetColor("active", "border-right-color", Colorf::fromRgba8(155, 155, 155));
|
||||
tabbar_tab->SetColor("active", "border-bottom-color", Colorf::transparent());
|
||||
|
||||
tabbar_spacer->SetColor("border-bottom-color", Colorf::fromRgba8(155, 155, 155));
|
||||
|
||||
tabwidget_stack->SetDouble("noncontent-left", 20.0);
|
||||
tabwidget_stack->SetDouble("noncontent-top", 5.0);
|
||||
tabwidget_stack->SetDouble("noncontent-right", 20.0);
|
||||
tabwidget_stack->SetDouble("noncontent-bottom", 5.0);
|
||||
|
||||
checkbox_label->SetColor("checked-outer-border-color", Colorf::fromRgba8(155, 155, 155));
|
||||
checkbox_label->SetColor("checked-inner-border-color", Colorf::fromRgba8(200, 200, 200));
|
||||
checkbox_label->SetColor("checked-color", Colorf::fromRgba8(50, 50, 50));
|
||||
checkbox_label->SetColor("unchecked-outer-border-color", Colorf::fromRgba8(156, 156, 156));
|
||||
checkbox_label->SetColor("unchecked-inner-border-color", Colorf::fromRgba8(200, 200, 200));
|
||||
|
||||
menubar->SetColor("background-color", Colorf::fromRgba8(70, 70, 70));
|
||||
|
||||
menubaritem->SetColor("color", Colorf::fromRgba8(226, 223, 219));
|
||||
menubaritem->SetColor("hover", "background-color", Colorf::fromRgba8(200, 200, 200));
|
||||
menubaritem->SetColor("hover", "color", Colorf::fromRgba8(0, 0, 0));
|
||||
menubaritem->SetColor("down", "background-color", Colorf::fromRgba8(190, 190, 190));
|
||||
menubaritem->SetColor("down", "color", Colorf::fromRgba8(0, 0, 0));
|
||||
|
||||
menu->SetDouble("noncontent-left", 5.0);
|
||||
menu->SetDouble("noncontent-top", 5.0);
|
||||
menu->SetDouble("noncontent-right", 5.0);
|
||||
menu->SetDouble("noncontent-bottom", 5.0);
|
||||
menu->SetColor("background-color", Colorf::fromRgba8(255, 255, 255));
|
||||
menu->SetColor("border-left-color", Colorf::fromRgba8(155, 155, 155));
|
||||
menu->SetColor("border-top-color", Colorf::fromRgba8(155, 155, 155));
|
||||
menu->SetColor("border-right-color", Colorf::fromRgba8(155, 155, 155));
|
||||
menu->SetColor("border-bottom-color", Colorf::fromRgba8(155, 155, 155));
|
||||
|
||||
menuitem->SetColor("hover", "background-color", Colorf::fromRgba8(200, 200, 200));
|
||||
menuitem->SetColor("down", "background-color", Colorf::fromRgba8(190, 190, 190));
|
||||
}
|
||||
LightWidgetTheme::LightWidgetTheme(): WidgetTheme({
|
||||
Colorf::fromRgb(0xF0F0F0), // background
|
||||
Colorf::fromRgb(0x191919), //
|
||||
Colorf::fromRgb(0xFAFAFA), // headers / inputs
|
||||
Colorf::fromRgb(0x191919), //
|
||||
Colorf::fromRgb(0xC8C8C8), // interactive elements
|
||||
Colorf::fromRgb(0x000000), //
|
||||
Colorf::fromRgb(0xD2D2FF), // hover / highlight
|
||||
Colorf::fromRgb(0x000000), //
|
||||
Colorf::fromRgb(0xC7B4FF), // click
|
||||
Colorf::fromRgb(0x000000), //
|
||||
Colorf::fromRgb(0xA0A0A0), // around elements
|
||||
Colorf::fromRgb(0xB9B9B9) // between elements
|
||||
}) {};
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
@ -27,7 +27,7 @@ void Timer::Start(int timeoutMilliseconds, bool repeat)
|
|||
{
|
||||
Stop();
|
||||
|
||||
TimerId = DisplayWindow::StartTimer(timeoutMilliseconds, [=]() {
|
||||
TimerId = DisplayWindow::StartTimer(timeoutMilliseconds, [this,repeat]() {
|
||||
if (!repeat)
|
||||
Stop();
|
||||
if (FuncExpired)
|
||||
|
|
|
|||
|
|
@ -303,10 +303,10 @@ void TrueTypeFont::LoadGlyph(TTF_SimpleGlyph& g, uint32_t glyphIndex, int compos
|
|||
reader.Seek(loca.offsets[glyphIndex]);
|
||||
|
||||
ttf_int16 numberOfContours = reader.ReadInt16();
|
||||
ttf_int16 xMin = reader.ReadInt16();
|
||||
ttf_int16 yMin = reader.ReadInt16();
|
||||
ttf_int16 xMax = reader.ReadInt16();
|
||||
ttf_int16 yMax = reader.ReadInt16();
|
||||
/*ttf_int16 xMin =*/ reader.ReadInt16();
|
||||
/*ttf_int16 yMin =*/ reader.ReadInt16();
|
||||
/*ttf_int16 xMax =*/ reader.ReadInt16();
|
||||
/*ttf_int16 yMax =*/ reader.ReadInt16();
|
||||
|
||||
if (numberOfContours > 0) // Simple glyph
|
||||
{
|
||||
|
|
@ -450,7 +450,7 @@ void TrueTypeFont::LoadGlyph(TTF_SimpleGlyph& g, uint32_t glyphIndex, int compos
|
|||
|
||||
if (transform)
|
||||
{
|
||||
for (int i = childPointsOffset; i < g.points.size(); i++)
|
||||
for (int i = childPointsOffset; i < (int)g.points.size(); i++)
|
||||
{
|
||||
float x = g.points[i].x * mat2x2[0] + g.points[i].y * mat2x2[1];
|
||||
float y = g.points[i].x * mat2x2[2] + g.points[i].y * mat2x2[3];
|
||||
|
|
@ -492,7 +492,7 @@ void TrueTypeFont::LoadGlyph(TTF_SimpleGlyph& g, uint32_t glyphIndex, int compos
|
|||
dy = g.points[parentPointIndex].y - g.points[childPointIndex].y;
|
||||
}
|
||||
|
||||
for (int i = childPointsOffset; i < g.points.size(); i++)
|
||||
for (int i = childPointsOffset; i < (int)g.points.size(); i++)
|
||||
{
|
||||
g.points[i].x += dx;
|
||||
g.points[i].y += dy;
|
||||
|
|
@ -589,9 +589,9 @@ void TrueTypeFont::LoadCharacterMapEncoding(TrueTypeFileReader& reader)
|
|||
for (ttf_uint16 c = startCode; c <= endCode; c++)
|
||||
{
|
||||
int offset = idRangeOffset / 2 + (c - startCode) - ((int)subformat.segCount - i);
|
||||
if (offset >= 0 && offset < subformat.glyphIdArray.size())
|
||||
if (offset >= 0 && offset < (int)subformat.glyphIdArray.size())
|
||||
{
|
||||
int glyphId = subformat.glyphIdArray[offset];
|
||||
ttf_uint32 glyphId = subformat.glyphIdArray[offset];
|
||||
if (firstGlyph)
|
||||
{
|
||||
range.startGlyphID = glyphId;
|
||||
|
|
@ -689,9 +689,9 @@ void TTF_CMapSubtable4::Load(TrueTypeFileReader& reader)
|
|||
language = reader.ReadUInt16();
|
||||
|
||||
segCount = reader.ReadUInt16() / 2;
|
||||
ttf_uint16 searchRange = reader.ReadUInt16();
|
||||
ttf_uint16 entrySelector = reader.ReadUInt16();
|
||||
ttf_uint16 rangeShift = reader.ReadUInt16();
|
||||
/*ttf_uint16 searchRange =*/ reader.ReadUInt16();
|
||||
/*ttf_uint16 entrySelector =*/ reader.ReadUInt16();
|
||||
/*ttf_uint16 rangeShift =*/ reader.ReadUInt16();
|
||||
|
||||
endCode.reserve(segCount);
|
||||
startCode.reserve(segCount);
|
||||
|
|
@ -746,9 +746,9 @@ void TTF_TableDirectory::Load(TrueTypeFileReader& reader)
|
|||
numTables = reader.ReadUInt16();
|
||||
|
||||
// opentype spec says we can't use these for security reasons, so we pretend they never was part of the header
|
||||
ttf_uint16 searchRange = reader.ReadUInt16();
|
||||
ttf_uint16 entrySelector = reader.ReadUInt16();
|
||||
ttf_uint16 rangeShift = reader.ReadUInt16();
|
||||
/*ttf_uint16 searchRange =*/ reader.ReadUInt16();
|
||||
/*ttf_uint16 entrySelector =*/ reader.ReadUInt16();
|
||||
/*ttf_uint16 rangeShift =*/ reader.ReadUInt16();
|
||||
|
||||
for (ttf_uint16 i = 0; i < numTables; i++)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -71,7 +71,7 @@ unsigned int UTF8Reader::character()
|
|||
else
|
||||
{
|
||||
unsigned int ucs4 = (data[current_position] & bitmask_leadbyte_for_utf8[trailing_bytes]);
|
||||
for (std::string::size_type i = 0; i < trailing_bytes; i++)
|
||||
for (std::string::size_type i = 0; i < (size_t)trailing_bytes; i++)
|
||||
{
|
||||
if ((data[current_position + 1 + i] & 0xC0) == 0x80)
|
||||
ucs4 = (ucs4 << 6) + (data[current_position + 1 + i] & 0x3f);
|
||||
|
|
@ -94,7 +94,7 @@ std::string::size_type UTF8Reader::char_length()
|
|||
if (current_position + 1 + trailing_bytes > length)
|
||||
return 1;
|
||||
|
||||
for (std::string::size_type i = 0; i < trailing_bytes; i++)
|
||||
for (std::string::size_type i = 0; i < (size_t)trailing_bytes; i++)
|
||||
{
|
||||
if ((data[current_position + 1 + i] & 0xC0) != 0x80)
|
||||
return 1;
|
||||
|
|
@ -136,7 +136,7 @@ void UTF8Reader::move_to_leadbyte()
|
|||
lead_position--;
|
||||
|
||||
int trailing_bytes = trailing_bytes_for_utf8[data[lead_position]];
|
||||
if (lead_position + trailing_bytes >= current_position)
|
||||
if (lead_position + trailing_bytes >= (int)current_position)
|
||||
current_position = lead_position;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -124,7 +124,7 @@ void Dropdown::ClearItems()
|
|||
void Dropdown::SetSelectedItem(int index)
|
||||
{
|
||||
if (index < 0) index = 0;
|
||||
if (index >= (int)items.size()) index = items.size() - 1;
|
||||
if (index >= (int)items.size()) index = (int)items.size() - 1;
|
||||
|
||||
if (selectedItem == index) return;
|
||||
|
||||
|
|
@ -270,7 +270,7 @@ void Dropdown::OnGeometryChanged()
|
|||
Point pos = MapTo(Window(), Point(0,0));
|
||||
|
||||
double width = GetWidth() + GetNoncontentLeft() + GetNoncontentRight();
|
||||
double innerH = GetDisplayItems() * 25.0 + 10.0;
|
||||
double innerH = GetDisplayItems() * 20.0 + 20;
|
||||
double outerH = GetHeight();
|
||||
|
||||
pos.x -= GetNoncontentLeft();
|
||||
|
|
@ -311,14 +311,14 @@ bool Dropdown::OpenDropdown()
|
|||
|
||||
listView->SetSelectedItem(selectedItem);
|
||||
|
||||
listView->OnActivated = [=]() { OnDropdownActivated(); };
|
||||
listView->OnChanged = [=](int index) { OnDropdownActivated(); };
|
||||
listView->OnActivated = [this]() { OnDropdownActivated(); };
|
||||
listView->OnChanged = [this](int index) { OnDropdownActivated(); };
|
||||
|
||||
listView->SetFrameGeometry(
|
||||
0,
|
||||
0,
|
||||
GetWidth() + GetNoncontentLeft() + GetNoncontentRight(),
|
||||
GetDisplayItems() * 25.0 + 10.0
|
||||
GetDisplayItems() * 20.0 + 20
|
||||
);
|
||||
OnGeometryChanged();
|
||||
|
||||
|
|
|
|||
|
|
@ -9,10 +9,10 @@ LineEdit::LineEdit(Widget* parent) : Widget(parent)
|
|||
SetStyleClass("lineedit");
|
||||
|
||||
timer = new Timer(this);
|
||||
timer->FuncExpired = [=]() { OnTimerExpired(); };
|
||||
timer->FuncExpired = [this]() { OnTimerExpired(); };
|
||||
|
||||
scroll_timer = new Timer(this);
|
||||
scroll_timer->FuncExpired = [=]() { OnScrollTimerExpired(); };
|
||||
scroll_timer->FuncExpired = [this]() { OnScrollTimerExpired(); };
|
||||
|
||||
SetCursor(StandardCursor::ibeam);
|
||||
}
|
||||
|
|
@ -90,12 +90,16 @@ int LineEdit::GetCursorPos() const
|
|||
Size LineEdit::GetTextSize()
|
||||
{
|
||||
Canvas* canvas = GetCanvas();
|
||||
if (!canvas)
|
||||
return Size(0.0, 0.0);
|
||||
return GetVisualTextSize(canvas);
|
||||
}
|
||||
|
||||
Size LineEdit::GetTextSize(const std::string& str)
|
||||
{
|
||||
Canvas* canvas = GetCanvas();
|
||||
if (!canvas)
|
||||
return Size(0.0, 0.0);
|
||||
return canvas->measureText(str).size();
|
||||
}
|
||||
|
||||
|
|
@ -674,7 +678,7 @@ bool LineEdit::InsertText(int pos, const std::string& str)
|
|||
}
|
||||
|
||||
// checking if insert exceeds max length
|
||||
if (UTF8Reader::utf8_length(text) + UTF8Reader::utf8_length(str) > max_length)
|
||||
if (UTF8Reader::utf8_length(text) + UTF8Reader::utf8_length(str) > (size_t)max_length)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
|
@ -757,6 +761,9 @@ int LineEdit::GetCharacterIndex(double mouse_x)
|
|||
}
|
||||
|
||||
Canvas* canvas = GetCanvas();
|
||||
if (!canvas)
|
||||
return 0;
|
||||
|
||||
UTF8Reader utf8_reader(text.data(), text.length());
|
||||
|
||||
int seek_start = clip_start_offset;
|
||||
|
|
@ -808,7 +815,7 @@ void LineEdit::UpdateTextClipping()
|
|||
if (!canvas)
|
||||
return;
|
||||
|
||||
Size text_size = GetVisualTextSize(canvas, clip_start_offset, (int)text.size() - clip_start_offset);
|
||||
// Size text_size = GetVisualTextSize(canvas, clip_start_offset, (int)text.size() - clip_start_offset);
|
||||
|
||||
if (cursor_pos < clip_start_offset)
|
||||
clip_start_offset = cursor_pos;
|
||||
|
|
@ -822,7 +829,7 @@ void LineEdit::UpdateTextClipping()
|
|||
utf8_reader.set_position(clip_start_offset);
|
||||
utf8_reader.next();
|
||||
clip_start_offset = (int)utf8_reader.position();
|
||||
if (clip_start_offset == text.size())
|
||||
if (clip_start_offset == (int)text.size())
|
||||
break;
|
||||
cursor_rect = GetCursorRect();
|
||||
}
|
||||
|
|
@ -837,7 +844,7 @@ void LineEdit::UpdateTextClipping()
|
|||
|
||||
utf8_reader.set_position(midpoint);
|
||||
utf8_reader.move_to_leadbyte();
|
||||
if (midpoint != utf8_reader.position())
|
||||
if (midpoint != (int)utf8_reader.position())
|
||||
utf8_reader.next();
|
||||
midpoint = (int)utf8_reader.position();
|
||||
|
||||
|
|
@ -862,6 +869,8 @@ void LineEdit::UpdateTextClipping()
|
|||
Rect LineEdit::GetCursorRect()
|
||||
{
|
||||
Canvas* canvas = GetCanvas();
|
||||
if (!canvas)
|
||||
return Rect::xywh(0.0, 0.0, 0.0, 0.0);
|
||||
|
||||
int substr_end = cursor_pos - clip_start_offset;
|
||||
if (substr_end < 0)
|
||||
|
|
@ -890,6 +899,8 @@ Rect LineEdit::GetCursorRect()
|
|||
Rect LineEdit::GetSelectionRect()
|
||||
{
|
||||
Canvas* canvas = GetCanvas();
|
||||
if (!canvas)
|
||||
return Rect::xywh(0.0, 0.0, 0.0, 0.0);
|
||||
|
||||
// text before selection:
|
||||
|
||||
|
|
@ -949,6 +960,8 @@ void LineEdit::OnTimerExpired()
|
|||
void LineEdit::OnGeometryChanged()
|
||||
{
|
||||
Canvas* canvas = GetCanvas();
|
||||
if (!canvas)
|
||||
return;
|
||||
|
||||
vertical_text_align = canvas->verticalTextAlign();
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ ListView::ListView(Widget* parent) : Widget(parent)
|
|||
SetStyleClass("listview");
|
||||
|
||||
scrollbar = new Scrollbar(this);
|
||||
scrollbar->FuncScroll = [=]() { OnScrollbarScroll(); };
|
||||
scrollbar->FuncScroll = [this]() { OnScrollbarScroll(); };
|
||||
|
||||
SetColumnWidths({ 0.0 });
|
||||
}
|
||||
|
|
@ -37,7 +37,7 @@ void ListView::SetColumnWidths(const std::vector<double>& widths)
|
|||
|
||||
void ListView::AddItem(const std::string& text, int index, int column)
|
||||
{
|
||||
if (column < 0 || column >= columnwidths.size())
|
||||
if (column < 0 || column >= (int)columnwidths.size())
|
||||
return;
|
||||
|
||||
std::vector<std::string> newEntry;
|
||||
|
|
@ -47,10 +47,10 @@ void ListView::AddItem(const std::string& text, int index, int column)
|
|||
newEntry[column] = text;
|
||||
if (index >= 0)
|
||||
{
|
||||
if (index >= items.size())
|
||||
if (index >= (int)items.size())
|
||||
{
|
||||
newEntry[column] = "";
|
||||
while (items.size() < index)
|
||||
while ((int)items.size() < index)
|
||||
items.push_back(newEntry);
|
||||
|
||||
newEntry[column] = text;
|
||||
|
|
@ -71,7 +71,7 @@ void ListView::AddItem(const std::string& text, int index, int column)
|
|||
|
||||
void ListView::UpdateItem(const std::string& text, int index, int column)
|
||||
{
|
||||
if (index < 0 || index >= items.size() || column < 0 || column >= columnwidths.size())
|
||||
if (index < 0 || index >= (int)items.size() || column < 0 || column >= (int)columnwidths.size())
|
||||
return;
|
||||
|
||||
items[index][column] = text;
|
||||
|
|
@ -80,7 +80,7 @@ void ListView::UpdateItem(const std::string& text, int index, int column)
|
|||
|
||||
void ListView::RemoveItem(int index)
|
||||
{
|
||||
if (!items.size() || index >= items.size())
|
||||
if (!items.size() || index >= (int)items.size())
|
||||
return;
|
||||
|
||||
if (index < 0)
|
||||
|
|
@ -102,7 +102,7 @@ void ListView::Activate()
|
|||
|
||||
void ListView::SetSelectedItem(int index)
|
||||
{
|
||||
if (selectedItem != index && index >= 0 && index < items.size())
|
||||
if (selectedItem != index && index >= 0 && index < (int)items.size())
|
||||
{
|
||||
selectedItem = index;
|
||||
if (OnChanged) OnChanged(selectedItem);
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ void Menubar::ShowMenu(MenubarItem* item)
|
|||
if (item->GetOpenCallback())
|
||||
{
|
||||
openMenu = new Menu(this);
|
||||
openMenu->onCloseMenu = [=]() { CloseMenu(); };
|
||||
openMenu->onCloseMenu = [this]() { CloseMenu(); };
|
||||
item->GetOpenCallback()(openMenu);
|
||||
if (item->AlignRight)
|
||||
openMenu->SetRightPosition(item->MapToGlobal(Point(item->GetWidth(), item->GetHeight())));
|
||||
|
|
@ -215,7 +215,7 @@ void Menubar::OnKeyDown(InputKey key)
|
|||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
MenubarItem::MenubarItem(Menubar* menubar, std::string text, bool alignRight) : Widget(menubar), menubar(menubar), text(text), AlignRight(alignRight)
|
||||
MenubarItem::MenubarItem(Menubar* menubar, std::string text, bool alignRight) : Widget(menubar), AlignRight(alignRight), menubar(menubar), text(text)
|
||||
{
|
||||
SetStyleClass("menubaritem");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,13 +3,16 @@
|
|||
#include "core/colorf.h"
|
||||
#include <stdexcept>
|
||||
|
||||
#define HIDE_SMALL_BAR false
|
||||
#define HIDE_SMALL_THUMB true
|
||||
|
||||
Scrollbar::Scrollbar(Widget* parent) : Widget(parent)
|
||||
{
|
||||
SetStyleClass("scrollbar");
|
||||
UpdatePartPositions();
|
||||
|
||||
mouse_down_timer = new Timer(this);
|
||||
mouse_down_timer->FuncExpired = [=]() { OnTimerExpired(); };
|
||||
mouse_down_timer->FuncExpired = [this]() { OnTimerExpired(); };
|
||||
}
|
||||
|
||||
Scrollbar::~Scrollbar()
|
||||
|
|
@ -295,7 +298,15 @@ void Scrollbar::OnPaint(Canvas* canvas)
|
|||
part_button_increment.render_box(canvas, rect_button_increment);
|
||||
*/
|
||||
|
||||
canvas->fillRect(Rect::shrink(Rect::xywh(0.0, 0.0, GetWidth(), GetHeight()), 4.0, 0.0, 4.0, 0.0), GetStyleColor("track-color"));
|
||||
auto height = GetHeight();
|
||||
auto paint = rect_thumb.height < height;
|
||||
|
||||
if (HIDE_SMALL_BAR && !paint) return;
|
||||
|
||||
canvas->fillRect(Rect::shrink(Rect::xywh(0.0, 0.0, GetWidth(), height), 4.0, 0.0, 4.0, 0.0), GetStyleColor("track-color"));
|
||||
|
||||
if (HIDE_SMALL_THUMB && !paint) return;
|
||||
|
||||
canvas->fillRect(Rect::shrink(rect_thumb, 4.0, 0.0, 4.0, 0.0), GetStyleColor("thumb-color"));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ TabWidget::TabWidget(Widget* parent) : Widget(parent)
|
|||
Bar = new TabBar(this);
|
||||
PageStack = new TabWidgetStack(this);
|
||||
|
||||
Bar->OnCurrentChanged = [=]() { OnBarCurrentChanged(); };
|
||||
Bar->OnCurrentChanged = [this]() { OnBarCurrentChanged(); };
|
||||
}
|
||||
|
||||
int TabWidget::AddTab(Widget* page, const std::string& label)
|
||||
|
|
@ -127,7 +127,7 @@ int TabBar::AddTab(const std::shared_ptr<Image>& icon, const std::string& label)
|
|||
TabBarTab* tab = new TabBarTab(this);
|
||||
tab->SetIcon(icon);
|
||||
tab->SetText(label);
|
||||
tab->OnClick = [=]() { OnTabClicked(tab); };
|
||||
tab->OnClick = [this,tab]() { OnTabClicked(tab); };
|
||||
int pageIndex = (int)Tabs.size();
|
||||
Tabs.push_back(tab);
|
||||
if (CurrentIndex == -1)
|
||||
|
|
|
|||
|
|
@ -13,11 +13,14 @@ TextEdit::TextEdit(Widget* parent) : Widget(parent)
|
|||
{
|
||||
SetStyleClass("textedit");
|
||||
|
||||
selectionBG = GetStyleColor("selection-color");
|
||||
selectionFG = GetStyleColor("color");
|
||||
|
||||
timer = new Timer(this);
|
||||
timer->FuncExpired = [=]() { OnTimerExpired(); };
|
||||
timer->FuncExpired = [this]() { OnTimerExpired(); };
|
||||
|
||||
scroll_timer = new Timer(this);
|
||||
scroll_timer->FuncExpired = [=]() { OnScrollTimerExpired(); };
|
||||
scroll_timer->FuncExpired = [this]() { OnScrollTimerExpired(); };
|
||||
|
||||
SetCursor(StandardCursor::ibeam);
|
||||
|
||||
|
|
@ -175,13 +178,13 @@ void TextEdit::SetText(const std::string& text)
|
|||
std::string::size_type end = text.find('\n');
|
||||
while (end != std::string::npos)
|
||||
{
|
||||
TextEdit::Line line;
|
||||
TextEdit::Line line{this};
|
||||
line.text = text.substr(start, end - start);
|
||||
lines.push_back(line);
|
||||
start = end + 1;
|
||||
end = text.find('\n', start);
|
||||
}
|
||||
TextEdit::Line line;
|
||||
TextEdit::Line line{this};
|
||||
line.text = text.substr(start);
|
||||
lines.push_back(line);
|
||||
|
||||
|
|
@ -197,13 +200,13 @@ void TextEdit::AddText(const std::string& text)
|
|||
std::string::size_type end = text.find('\n');
|
||||
while (end != std::string::npos)
|
||||
{
|
||||
TextEdit::Line line;
|
||||
TextEdit::Line line{this};
|
||||
line.text = text.substr(start, end - start);
|
||||
lines.push_back(line);
|
||||
start = end + 1;
|
||||
end = text.find('\n', start);
|
||||
}
|
||||
TextEdit::Line line;
|
||||
TextEdit::Line line{this};
|
||||
line.text = text.substr(start);
|
||||
lines.push_back(line);
|
||||
|
||||
|
|
@ -434,7 +437,7 @@ void TextEdit::OnKeyDown(InputKey key)
|
|||
if (GetKeyState(InputKey::Shift) && selection_length == 0)
|
||||
selection_start = cursor_pos;
|
||||
|
||||
if (cursor_pos.y < lines.size() - 1)
|
||||
if (cursor_pos.y < (int)lines.size() - 1)
|
||||
{
|
||||
cursor_pos.y++;
|
||||
cursor_pos.x = std::min(lines[cursor_pos.y].text.size(), (size_t)cursor_pos.x);
|
||||
|
|
@ -578,7 +581,7 @@ void TextEdit::OnLostFocus()
|
|||
void TextEdit::CreateComponents()
|
||||
{
|
||||
vert_scrollbar = new Scrollbar(this);
|
||||
vert_scrollbar->FuncScroll = [=]() { OnVerticalScroll(); };
|
||||
vert_scrollbar->FuncScroll = [this]() { OnVerticalScroll(); };
|
||||
vert_scrollbar->SetVisible(false);
|
||||
vert_scrollbar->SetVertical();
|
||||
}
|
||||
|
|
@ -714,10 +717,10 @@ TextEdit::ivec2 TextEdit::FindNextBreakCharacter(ivec2 search_start)
|
|||
if (search_start.x >= int(lines[search_start.y].text.size()) - 1)
|
||||
return ivec2(lines[search_start.y].text.size(), search_start.y);
|
||||
|
||||
int pos = lines[search_start.y].text.find_first_of(break_characters, search_start.x);
|
||||
size_t pos = lines[search_start.y].text.find_first_of(break_characters, search_start.x);
|
||||
if (pos == std::string::npos)
|
||||
return ivec2(lines[search_start.y].text.size(), search_start.y);
|
||||
return ivec2(pos, search_start.y);
|
||||
return ivec2((int)pos, search_start.y);
|
||||
}
|
||||
|
||||
TextEdit::ivec2 TextEdit::FindPreviousBreakCharacter(ivec2 search_start)
|
||||
|
|
@ -725,10 +728,10 @@ TextEdit::ivec2 TextEdit::FindPreviousBreakCharacter(ivec2 search_start)
|
|||
search_start.x--;
|
||||
if (search_start.x <= 0)
|
||||
return ivec2(0, search_start.y);
|
||||
int pos = lines[search_start.y].text.find_last_of(break_characters, search_start.x);
|
||||
size_t pos = lines[search_start.y].text.find_last_of(break_characters, search_start.x);
|
||||
if (pos == std::string::npos)
|
||||
return ivec2(0, search_start.y);
|
||||
return ivec2(pos, search_start.y);
|
||||
return ivec2((int)pos, search_start.y);
|
||||
}
|
||||
|
||||
void TextEdit::InsertText(ivec2 pos, const std::string& str)
|
||||
|
|
@ -741,7 +744,7 @@ void TextEdit::InsertText(ivec2 pos, const std::string& str)
|
|||
}
|
||||
|
||||
// checking if insert exceeds max length
|
||||
if (ToOffset(ivec2(lines[lines.size() - 1].text.size(), lines.size() - 1)) + str.length() > max_length)
|
||||
if (ToOffset(ivec2((int)lines[lines.size() - 1].text.size(), (int)lines.size() - 1)) + str.length() > (size_t)max_length)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
|
@ -759,7 +762,7 @@ void TextEdit::InsertText(ivec2 pos, const std::string& str)
|
|||
|
||||
pos.x += next_newline - start;
|
||||
|
||||
Line line;
|
||||
Line line{this};
|
||||
line.text = lines[pos.y].text.substr(pos.x);
|
||||
lines.insert(lines.begin() + pos.y + 1, line);
|
||||
lines[pos.y].text = lines[pos.y].text.substr(0, pos.x);
|
||||
|
|
@ -836,9 +839,9 @@ void TextEdit::Del()
|
|||
lines[cursor_pos.y].invalidated = true;
|
||||
Update();
|
||||
}
|
||||
else if (cursor_pos.y + 1 < lines.size())
|
||||
else if (cursor_pos.y + 1 < (int)lines.size())
|
||||
{
|
||||
selection_start = ivec2(lines[cursor_pos.y].text.length(), cursor_pos.y);
|
||||
selection_start = ivec2((int)lines[cursor_pos.y].text.length(), cursor_pos.y);
|
||||
selection_length = 1;
|
||||
DeleteSelectedText();
|
||||
}
|
||||
|
|
@ -904,7 +907,7 @@ bool TextEdit::InputMaskAcceptsInput(ivec2 cursor_pos, const std::string& str)
|
|||
|
||||
std::string::size_type TextEdit::ToOffset(ivec2 pos) const
|
||||
{
|
||||
if (pos.y < lines.size())
|
||||
if (pos.y < (int)lines.size())
|
||||
{
|
||||
std::string::size_type offset = 0;
|
||||
for (int line = 0; line < pos.y; line++)
|
||||
|
|
@ -927,7 +930,7 @@ std::string::size_type TextEdit::ToOffset(ivec2 pos) const
|
|||
TextEdit::ivec2 TextEdit::FromOffset(std::string::size_type offset) const
|
||||
{
|
||||
int line_offset = 0;
|
||||
for (int line = 0; line < lines.size(); line++)
|
||||
for (int line = 0; line < (int)lines.size(); line++)
|
||||
{
|
||||
if (offset <= line_offset + lines[line].text.size())
|
||||
{
|
||||
|
|
@ -984,9 +987,9 @@ void TextEdit::LayoutLines(Canvas* canvas)
|
|||
line.invalidated = false;
|
||||
}
|
||||
|
||||
if (sel_start != sel_end && sel_start.y <= i && sel_end.y >= i)
|
||||
if (sel_start != sel_end && sel_start.y <= (int)i && sel_end.y >= (int)i)
|
||||
{
|
||||
line.layout.SetSelectionRange(sel_start.y < i ? 0 : sel_start.x, sel_end.y > i ? line.text.size() : sel_end.x);
|
||||
line.layout.SetSelectionRange(sel_start.y < (int)i ? 0 : sel_start.x, sel_end.y > (int)i ? line.text.size() : sel_end.x);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -996,7 +999,7 @@ void TextEdit::LayoutLines(Canvas* canvas)
|
|||
line.layout.HideCursor();
|
||||
if (HasFocus())
|
||||
{
|
||||
if (cursor_blink_visible && cursor_pos.y == i)
|
||||
if (cursor_blink_visible && cursor_pos.y == (int)i)
|
||||
{
|
||||
line.layout.SetCursorPos(cursor_pos.x);
|
||||
line.layout.SetCursorColor(textColor);
|
||||
|
|
@ -1035,6 +1038,7 @@ TextEdit::ivec2 TextEdit::GetCharacterIndex(Point mouse_wincoords)
|
|||
return ivec2(clamp(result.offset, (size_t)0, line.text.size()), i);
|
||||
case SpanLayout::HitTestResult::outside_left:
|
||||
return ivec2(0, i);
|
||||
default:
|
||||
case SpanLayout::HitTestResult::outside_right:
|
||||
return ivec2(line.text.size(), i);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
@ -73,8 +72,7 @@ std::vector<std::string> SDL2DisplayWindow::GetVulkanInstanceExtensions()
|
|||
VkSurfaceKHR SDL2DisplayWindow::CreateVulkanSurface(VkInstance instance)
|
||||
{
|
||||
VkSurfaceKHR surfaceHandle = {};
|
||||
SDL_Vulkan_CreateSurface(Handle.window, instance, &surfaceHandle);
|
||||
if (surfaceHandle)
|
||||
if (SDL_Vulkan_CreateSurface(Handle.window, instance, &surfaceHandle) != SDL_TRUE)
|
||||
throw std::runtime_error("Could not create vulkan surface");
|
||||
return surfaceHandle;
|
||||
}
|
||||
|
|
@ -84,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.
|
||||
|
|
@ -423,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;
|
||||
|
|
@ -451,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();
|
||||
}
|
||||
|
|
@ -468,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(); 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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -788,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;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
#include "win32_display_window.h"
|
||||
#include "core/widget.h"
|
||||
#include <stdexcept>
|
||||
#include <exception>
|
||||
#include <thread>
|
||||
#include <mutex>
|
||||
#include <condition_variable>
|
||||
|
|
@ -14,6 +15,65 @@ Win32OpenFileDialog::Win32OpenFileDialog(Win32DisplayWindow* owner) : owner(owne
|
|||
|
||||
bool Win32OpenFileDialog::Show()
|
||||
{
|
||||
// For some reason this can hang deep inside Win32 if we do it on the calling thread!
|
||||
bool done = false;
|
||||
std::mutex mutex;
|
||||
std::condition_variable condvar;
|
||||
bool showResult = false;
|
||||
std::exception_ptr exception;
|
||||
|
||||
std::thread thread([&]() {
|
||||
|
||||
try
|
||||
{
|
||||
showResult = ShowWorkerThread();
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
exception = std::current_exception();
|
||||
}
|
||||
|
||||
std::unique_lock lock(mutex);
|
||||
done = true;
|
||||
condvar.notify_all();
|
||||
|
||||
});
|
||||
|
||||
std::unique_lock lock(mutex);
|
||||
while (!done)
|
||||
{
|
||||
DisplayBackend::Get()->ProcessEvents();
|
||||
using namespace std::chrono_literals;
|
||||
condvar.wait_for(lock, 50ms, [&]() { return done; });
|
||||
}
|
||||
lock.unlock();
|
||||
thread.join();
|
||||
|
||||
if (exception)
|
||||
std::rethrow_exception(exception);
|
||||
|
||||
return showResult;
|
||||
}
|
||||
|
||||
bool Win32OpenFileDialog::ShowWorkerThread()
|
||||
{
|
||||
class InitCOM
|
||||
{
|
||||
public:
|
||||
InitCOM()
|
||||
{
|
||||
HRESULT result = CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED);
|
||||
if (FAILED(result))
|
||||
throw std::runtime_error("CoInitializeEx(COINIT_APARTMENTTHREADED) failed");
|
||||
}
|
||||
|
||||
~InitCOM()
|
||||
{
|
||||
CoUninitialize();
|
||||
}
|
||||
};
|
||||
InitCOM initCOM;
|
||||
|
||||
std::wstring title16 = to_utf16(title);
|
||||
std::wstring initial_directory16 = to_utf16(initial_directory);
|
||||
|
||||
|
|
@ -92,35 +152,10 @@ bool Win32OpenFileDialog::Show()
|
|||
}
|
||||
}
|
||||
|
||||
// For some reason this can hang deep inside Win32 if we do it on the calling thread!
|
||||
{
|
||||
bool done = false;
|
||||
std::mutex mutex;
|
||||
std::condition_variable condvar;
|
||||
|
||||
std::thread thread([&]() {
|
||||
|
||||
if (owner)
|
||||
result = open_dialog->Show(owner->WindowHandle.hwnd);
|
||||
else
|
||||
result = open_dialog->Show(0);
|
||||
|
||||
std::unique_lock lock(mutex);
|
||||
done = true;
|
||||
condvar.notify_all();
|
||||
|
||||
});
|
||||
|
||||
std::unique_lock lock(mutex);
|
||||
while (!done)
|
||||
{
|
||||
DisplayBackend::Get()->ProcessEvents();
|
||||
using namespace std::chrono_literals;
|
||||
condvar.wait_for(lock, 50ms, [&]() { return done; });
|
||||
}
|
||||
lock.unlock();
|
||||
thread.join();
|
||||
}
|
||||
if (owner)
|
||||
result = open_dialog->Show(owner->WindowHandle.hwnd);
|
||||
else
|
||||
result = open_dialog->Show(0);
|
||||
|
||||
if (SUCCEEDED(result))
|
||||
{
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ public:
|
|||
void SetDefaultExtension(const std::string& extension) override;
|
||||
|
||||
private:
|
||||
bool ShowWorkerThread();
|
||||
void throw_if_failed(HRESULT result, const std::string& error);
|
||||
|
||||
Win32DisplayWindow* owner = nullptr;
|
||||
|
|
|
|||
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,83 +12,24 @@
|
|||
#include <unistd.h>
|
||||
#include <iostream>
|
||||
|
||||
class X11Connection
|
||||
{
|
||||
public:
|
||||
X11Connection()
|
||||
{
|
||||
// If we ever want to support windows on multiple threads:
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
|
||||
~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;
|
||||
};
|
||||
|
||||
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)
|
||||
{
|
||||
display = GetX11Connection()->display;
|
||||
auto connection = GetX11Connection();
|
||||
display = connection->display;
|
||||
|
||||
screen = XDefaultScreen(display);
|
||||
depth = XDefaultDepth(display, screen);
|
||||
visual = XDefaultVisual(display, screen);
|
||||
colormap = XDefaultColormap(display, screen);
|
||||
|
||||
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));
|
||||
dpiScale = std::round(ppi / 96.0 * 4.0) / 4.0; // 100%, 125%, 150%, 175%, 200%, etc.
|
||||
if (char* value = XGetDefault(display, "Xft", "dpi"))
|
||||
{
|
||||
int dpi = std::atoi(value);
|
||||
if (dpi != 0)
|
||||
{
|
||||
dpiScale = dpi / 96.0;
|
||||
}
|
||||
}
|
||||
|
||||
XSetWindowAttributes attributes = {};
|
||||
attributes.backing_store = Always;
|
||||
|
|
@ -94,14 +37,32 @@ X11DisplayWindow::X11DisplayWindow(DisplayWindowHost* windowHost, bool popupWind
|
|||
attributes.save_under = popupWindow ? True : False;
|
||||
attributes.colormap = colormap;
|
||||
attributes.event_mask =
|
||||
KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask |
|
||||
EnterWindowMask | LeaveWindowMask | PointerMotionMask | KeymapStateMask |
|
||||
KeyPressMask | KeyReleaseMask |
|
||||
EnterWindowMask | LeaveWindowMask | KeymapStateMask |
|
||||
ExposureMask | StructureNotifyMask | FocusChangeMask | PropertyChangeMask;
|
||||
|
||||
if (!connection->XInput2Supported)
|
||||
{
|
||||
attributes.event_mask |= ButtonPressMask | ButtonReleaseMask | PointerMotionMask;
|
||||
}
|
||||
|
||||
unsigned long mask = CWBackingStore | CWSaveUnder | CWEventMask | CWOverrideRedirect;
|
||||
|
||||
window = XCreateWindow(display, XRootWindow(display, screen), 0, 0, 100, 100, 0, depth, InputOutput, visual, mask, &attributes);
|
||||
GetX11Connection()->windows[window] = this;
|
||||
connection->windows[window] = this;
|
||||
|
||||
if (connection->XInput2Supported)
|
||||
{
|
||||
unsigned char mask[1] = { 0 };
|
||||
XISetMask(mask, XI_ButtonPress);
|
||||
XISetMask(mask, XI_ButtonRelease);
|
||||
XISetMask(mask, XI_Motion);
|
||||
XIEventMask eventmask;
|
||||
eventmask.deviceid = connection->MasterPointerID;
|
||||
eventmask.mask_len = sizeof(mask);
|
||||
eventmask.mask = mask;
|
||||
XISelectEvents(display, window, &eventmask, 1);
|
||||
}
|
||||
|
||||
if (owner)
|
||||
{
|
||||
|
|
@ -109,59 +70,59 @@ 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);
|
||||
}
|
||||
}
|
||||
|
||||
// Create input context
|
||||
if (GetX11Connection()->xim)
|
||||
if (connection->xim)
|
||||
{
|
||||
xic = XCreateIC(
|
||||
GetX11Connection()->xim,
|
||||
connection->xim,
|
||||
XNInputStyle,
|
||||
XIMPreeditNothing | XIMStatusNothing,
|
||||
XNClientWindow, window,
|
||||
|
|
@ -188,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
|
||||
|
|
@ -226,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;
|
||||
}
|
||||
}
|
||||
|
|
@ -378,7 +421,7 @@ Rect X11DisplayWindow::GetWindowFrame() const
|
|||
unsigned int height = 0;
|
||||
unsigned int borderwidth = 0;
|
||||
unsigned int depth = 0;
|
||||
Status status = XGetGeometry(display, window, &root, &x, &y, &width, &height, &borderwidth, &depth);
|
||||
/*Status status =*/ XGetGeometry(display, window, &root, &x, &y, &width, &height, &borderwidth, &depth);
|
||||
|
||||
return Rect::xywh(x / dpiscale, y / dpiscale, width / dpiscale, height / dpiscale);
|
||||
}
|
||||
|
|
@ -394,7 +437,7 @@ Size X11DisplayWindow::GetClientSize() const
|
|||
unsigned int height = 0;
|
||||
unsigned int borderwidth = 0;
|
||||
unsigned int depth = 0;
|
||||
Status status = XGetGeometry(display, window, &root, &x, &y, &width, &height, &borderwidth, &depth);
|
||||
/*Status status =*/ XGetGeometry(display, window, &root, &x, &y, &width, &height, &borderwidth, &depth);
|
||||
|
||||
return Size(width / dpiscale, height / dpiscale);
|
||||
}
|
||||
|
|
@ -408,7 +451,7 @@ int X11DisplayWindow::GetPixelWidth() const
|
|||
unsigned int height = 0;
|
||||
unsigned int borderwidth = 0;
|
||||
unsigned int depth = 0;
|
||||
Status status = XGetGeometry(display, window, &root, &x, &y, &width, &height, &borderwidth, &depth);
|
||||
/*Status status =*/ XGetGeometry(display, window, &root, &x, &y, &width, &height, &borderwidth, &depth);
|
||||
return width;
|
||||
}
|
||||
|
||||
|
|
@ -421,7 +464,7 @@ int X11DisplayWindow::GetPixelHeight() const
|
|||
unsigned int height = 0;
|
||||
unsigned int borderwidth = 0;
|
||||
unsigned int depth = 0;
|
||||
Status status = XGetGeometry(display, window, &root, &x, &y, &width, &height, &borderwidth, &depth);
|
||||
/*Status status =*/ XGetGeometry(display, window, &root, &x, &y, &width, &height, &borderwidth, &depth);
|
||||
return height;
|
||||
}
|
||||
|
||||
|
|
@ -486,8 +529,8 @@ void X11DisplayWindow::SetCaptionTextColor(uint32_t bgra8)
|
|||
std::vector<uint8_t> X11DisplayWindow::GetWindowProperty(Atom property, Atom &actual_type, int &actual_format, unsigned long &item_count)
|
||||
{
|
||||
long read_bytes = 0;
|
||||
Atom _actual_type = actual_type;
|
||||
int _actual_format = actual_format;
|
||||
//Atom _actual_type = actual_type;
|
||||
//int _actual_format = actual_format;
|
||||
unsigned long _item_count = item_count;
|
||||
unsigned long bytes_remaining = 0;
|
||||
unsigned char *read_data = nullptr;
|
||||
|
|
@ -516,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 {};
|
||||
|
||||
|
|
@ -529,7 +573,7 @@ std::string X11DisplayWindow::GetClipboardText()
|
|||
{
|
||||
if (XCheckTypedWindowEvent(display, window, SelectionNotify, &event))
|
||||
break;
|
||||
if (!WaitForEvents(500))
|
||||
if (!connection->WaitForEvents(500))
|
||||
return {};
|
||||
}
|
||||
|
||||
|
|
@ -548,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;
|
||||
|
||||
|
|
@ -565,7 +610,7 @@ Point X11DisplayWindow::MapFromGlobal(const Point& pos) const
|
|||
int srcy = (int)std::round(pos.y * dpiscale);
|
||||
int destx = 0;
|
||||
int desty = 0;
|
||||
Bool result = XTranslateCoordinates(display, root, window, srcx, srcy, &destx, &desty, &child);
|
||||
/*Bool result =*/ XTranslateCoordinates(display, root, window, srcx, srcy, &destx, &desty, &child);
|
||||
return Point(destx / dpiscale, desty / dpiscale);
|
||||
}
|
||||
|
||||
|
|
@ -578,7 +623,7 @@ Point X11DisplayWindow::MapToGlobal(const Point& pos) const
|
|||
int srcy = (int)std::round(pos.y * dpiscale);
|
||||
int destx = 0;
|
||||
int desty = 0;
|
||||
Bool result = XTranslateCoordinates(display, window, root, srcx, srcy, &destx, &desty, &child);
|
||||
/*Bool result =*/ XTranslateCoordinates(display, window, root, srcx, srcy, &destx, &desty, &child);
|
||||
return Point(destx / dpiscale, desty / dpiscale);
|
||||
}
|
||||
|
||||
|
|
@ -587,79 +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();
|
||||
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)
|
||||
|
|
@ -703,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)
|
||||
|
|
@ -731,11 +704,13 @@ void X11DisplayWindow::OnFocusIn(XEvent* event)
|
|||
if (xic)
|
||||
XSetICFocus(xic);
|
||||
|
||||
RawInput.Focused = true;
|
||||
windowHost->OnWindowActivated();
|
||||
}
|
||||
|
||||
void X11DisplayWindow::OnFocusOut(XEvent* event)
|
||||
{
|
||||
RawInput.Focused = false;
|
||||
windowHost->OnWindowDeactivated();
|
||||
}
|
||||
|
||||
|
|
@ -949,6 +924,8 @@ void X11DisplayWindow::OnKeyRelease(XEvent* event)
|
|||
|
||||
void X11DisplayWindow::OnButtonPress(XEvent* event)
|
||||
{
|
||||
// Note: this only gets called if XInput is not available
|
||||
|
||||
InputKey key = GetInputKey(event);
|
||||
keyState[key] = true;
|
||||
windowHost->OnWindowMouseDown(GetMousePos(event), key);
|
||||
|
|
@ -958,6 +935,8 @@ void X11DisplayWindow::OnButtonPress(XEvent* event)
|
|||
|
||||
void X11DisplayWindow::OnButtonRelease(XEvent* event)
|
||||
{
|
||||
// Note: this only gets called if XInput is not available
|
||||
|
||||
InputKey key = GetInputKey(event);
|
||||
keyState[key] = false;
|
||||
windowHost->OnWindowMouseUp(GetMousePos(event), key);
|
||||
|
|
@ -965,6 +944,8 @@ void X11DisplayWindow::OnButtonRelease(XEvent* event)
|
|||
|
||||
void X11DisplayWindow::OnMotionNotify(XEvent* event)
|
||||
{
|
||||
// Note: this only gets called if XInput is not available
|
||||
|
||||
double dpiScale = GetDpiScale();
|
||||
int x = event->xmotion.x;
|
||||
int y = event->xmotion.y;
|
||||
|
|
@ -974,18 +955,134 @@ void X11DisplayWindow::OnMotionNotify(XEvent* event)
|
|||
}
|
||||
else
|
||||
{
|
||||
MouseX = ClientSizeX / 2;
|
||||
MouseY = ClientSizeY / 2;
|
||||
|
||||
if (MouseX != -1 && MouseY != -1)
|
||||
int dx = x - RawInput.LastX;
|
||||
int dy = y - RawInput.LastY;
|
||||
RawInput.LastX = x;
|
||||
RawInput.LastY = y;
|
||||
int centerX = ClientSizeX / 2;
|
||||
int centerY = ClientSizeY / 2;
|
||||
if (x != centerX || y != centerY)
|
||||
{
|
||||
windowHost->OnWindowRawMouseMove(x - MouseX, y - MouseY);
|
||||
// We still have to contain the mouse even if we are using XInput.
|
||||
// Maybe we can do that in a simpler way if XInput is available?
|
||||
XWarpPointer(display, window, window, 0, 0, ClientSizeX, ClientSizeY, centerX, centerY);
|
||||
|
||||
// Use the accelerated mouse cursor if there's no XInput 2.0 support
|
||||
if (!GetX11Connection()->XInput2Supported)
|
||||
{
|
||||
if (dx != 0 || dy != 0)
|
||||
windowHost->OnWindowRawMouseMove(dx, dy);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 false;
|
||||
|
||||
InputKey key = {};
|
||||
switch (deviceEvent->detail)
|
||||
{
|
||||
case 1: key = InputKey::LeftMouse; break;
|
||||
case 2: key = InputKey::MiddleMouse; break;
|
||||
case 3: key = InputKey::RightMouse; break;
|
||||
case 4: key = InputKey::MouseWheelUp; break;
|
||||
case 5: key = InputKey::MouseWheelDown; break;
|
||||
// case 6: key = InputKey::XButton1; break;
|
||||
// case 7: key = InputKey::XButton2; break;
|
||||
default: return false;
|
||||
}
|
||||
|
||||
// Warp pointer to the center of the window
|
||||
XWarpPointer(display, window, window, 0, 0, ClientSizeX, ClientSizeY, ClientSizeX / 2, ClientSizeY / 2);
|
||||
}
|
||||
double dpiScale = GetDpiScale();
|
||||
int x = (int)std::round(deviceEvent->event_x);
|
||||
int y = (int)std::round(deviceEvent->event_y);
|
||||
Point mousePos(x / dpiScale, y / dpiScale);
|
||||
|
||||
if (!isCursorEnabled)
|
||||
{
|
||||
// Raw input gets blocked until we ungrab the pointer. Worst design EVER.
|
||||
XUngrabPointer(display, deviceEvent->time);
|
||||
}
|
||||
|
||||
if (event->xcookie.evtype == XI_ButtonPress)
|
||||
{
|
||||
keyState[key] = true;
|
||||
windowHost->OnWindowMouseDown(mousePos, key);
|
||||
// if (lastClickWithin400ms)
|
||||
// windowHost->OnWindowMouseDoubleclick(GetMousePos(event), InputKey::LeftMouse);
|
||||
}
|
||||
else
|
||||
{
|
||||
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 false;
|
||||
|
||||
if (isCursorEnabled)
|
||||
{
|
||||
double dpiScale = GetDpiScale();
|
||||
int x = (int)std::round(deviceEvent->event_x);
|
||||
int y = (int)std::round(deviceEvent->event_y);
|
||||
Point mousePos(x / dpiScale, y / dpiScale);
|
||||
windowHost->OnWindowMouseMove(Point(x / dpiScale, y / dpiScale));
|
||||
}
|
||||
else if (RawInput.Focused)
|
||||
{
|
||||
int x = (int)std::round(deviceEvent->event_x);
|
||||
int y = (int)std::round(deviceEvent->event_y);
|
||||
int centerX = ClientSizeX / 2;
|
||||
int centerY = ClientSizeY / 2;
|
||||
if (x != centerX || y != centerY)
|
||||
{
|
||||
// We still have to contain the mouse even if we are using XInput.
|
||||
// Maybe we can do that in a simpler way?
|
||||
XWarpPointer(display, window, window, 0, 0, ClientSizeX, ClientSizeY, centerX, centerY);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
else if (!isCursorEnabled && RawInput.Focused && event->xcookie.evtype == XI_RawMotion)
|
||||
{
|
||||
std::vector<double> values;
|
||||
values.reserve(2);
|
||||
|
||||
auto rawEvent = (XIRawEvent*)event->xcookie.data;
|
||||
double *rawValuator = rawEvent->raw_values;
|
||||
for (int i = 0; i < rawEvent->valuators.mask_len * 8; i++)
|
||||
{
|
||||
if (XIMaskIsSet(rawEvent->valuators.mask, i))
|
||||
{
|
||||
values.push_back(*rawValuator);
|
||||
rawValuator++;
|
||||
}
|
||||
}
|
||||
|
||||
if (values.size() >= 2)
|
||||
{
|
||||
// Values seems to be integers for my mouse. Is that always the case?
|
||||
int dx = (int)std::round(values[0]);
|
||||
int dy = (int)std::round(values[1]);
|
||||
if (dx != 0 || dy != 0)
|
||||
windowHost->OnWindowRawMouseMove(dx, dy);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void X11DisplayWindow::OnLeaveNotify(XEvent* event)
|
||||
|
|
@ -1009,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;
|
||||
|
|
@ -1067,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
|
||||
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
#include <X11/cursorfont.h>
|
||||
#include <X11/keysymdef.h>
|
||||
#include <X11/XKBlib.h>
|
||||
#include <X11/extensions/XInput2.h>
|
||||
#include <map>
|
||||
|
||||
class X11DisplayWindow : public DisplayWindow
|
||||
|
|
@ -16,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;
|
||||
|
|
@ -59,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();
|
||||
|
||||
|
|
@ -85,6 +80,7 @@ private:
|
|||
void OnSelectionClear(XEvent* event);
|
||||
void OnSelectionNotify(XEvent* event);
|
||||
void OnSelectionRequest(XEvent* event);
|
||||
bool OnXInputEvent(XEvent* event);
|
||||
|
||||
void CreateBackbuffer(int width, int height);
|
||||
void DestroyBackbuffer();
|
||||
|
|
@ -92,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;
|
||||
|
|
@ -117,8 +108,13 @@ private:
|
|||
|
||||
int ClientSizeX = 0;
|
||||
int ClientSizeY = 0;
|
||||
int MouseX = -1;
|
||||
int MouseY = -1;
|
||||
|
||||
struct
|
||||
{
|
||||
int LastX = -1;
|
||||
int LastY = -1;
|
||||
bool Focused = false;
|
||||
} RawInput;
|
||||
|
||||
Pixmap cursor_bitmap = None;
|
||||
Cursor hidden_cursor = None;
|
||||
|
|
@ -138,5 +134,6 @@ private:
|
|||
|
||||
bool needsUpdate = false;
|
||||
|
||||
friend class X11Connection;
|
||||
friend class X11DisplayBackend;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -37,15 +37,17 @@ public:
|
|||
// [Construction / Destruction]
|
||||
// --------------------------------------------------------------------------
|
||||
|
||||
ASMJIT_INLINE CpuFeatures() noexcept { reset(); }
|
||||
ASMJIT_INLINE CpuFeatures(const CpuFeatures& other) noexcept { init(other); }
|
||||
ASMJIT_INLINE CpuFeatures() noexcept : _bits{} {}
|
||||
|
||||
ASMJIT_INLINE CpuFeatures(const CpuFeatures& other) noexcept = default;
|
||||
ASMJIT_INLINE CpuFeatures& operator=(const CpuFeatures& other) noexcept = default;
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// [Init / Reset]
|
||||
// --------------------------------------------------------------------------
|
||||
|
||||
ASMJIT_INLINE void init(const CpuFeatures& other) noexcept { ::memcpy(this, &other, sizeof(*this)); }
|
||||
ASMJIT_INLINE void reset() noexcept { ::memset(this, 0, sizeof(*this)); }
|
||||
ASMJIT_INLINE void init(const CpuFeatures& other) noexcept { *this = other; }
|
||||
ASMJIT_INLINE void reset() noexcept { *this = CpuFeatures{}; }
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// [Ops]
|
||||
|
|
@ -248,8 +250,21 @@ public:
|
|||
// [Construction / Destruction]
|
||||
// --------------------------------------------------------------------------
|
||||
|
||||
ASMJIT_INLINE CpuInfo() noexcept { reset(); }
|
||||
ASMJIT_INLINE CpuInfo(const CpuInfo& other) noexcept { init(other); }
|
||||
ASMJIT_INLINE CpuInfo() noexcept:
|
||||
_archInfo{},
|
||||
_vendorId{},
|
||||
_family{},
|
||||
_model{},
|
||||
_stepping{},
|
||||
_hwThreadsCount{},
|
||||
_features{},
|
||||
_vendorString{},
|
||||
_brandString{},
|
||||
_x86Data{}
|
||||
{}
|
||||
|
||||
ASMJIT_INLINE CpuInfo(const CpuInfo& other) noexcept = default;
|
||||
ASMJIT_INLINE CpuInfo& operator=(const CpuInfo& other) noexcept = default;
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// [Init / Reset]
|
||||
|
|
@ -260,8 +275,8 @@ public:
|
|||
_archInfo.init(archType, archMode);
|
||||
}
|
||||
|
||||
ASMJIT_INLINE void init(const CpuInfo& other) noexcept { ::memcpy(this, &other, sizeof(*this)); }
|
||||
ASMJIT_INLINE void reset() noexcept { ::memset(this, 0, sizeof(*this)); }
|
||||
ASMJIT_INLINE void init(const CpuInfo& other) noexcept { *this = other; }
|
||||
ASMJIT_INLINE void reset() noexcept { *this = CpuInfo{}; }
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// [Detect]
|
||||
|
|
|
|||
|
|
@ -734,10 +734,18 @@ public:
|
|||
// [Construction / Destruction]
|
||||
// --------------------------------------------------------------------------
|
||||
|
||||
ASMJIT_INLINE FuncDetail() noexcept { reset(); }
|
||||
ASMJIT_INLINE FuncDetail(const FuncDetail& other) noexcept {
|
||||
::memcpy(this, &other, sizeof(*this));
|
||||
}
|
||||
ASMJIT_INLINE FuncDetail() noexcept:
|
||||
_callConv{},
|
||||
_argCount{},
|
||||
_retCount{},
|
||||
_usedRegs{},
|
||||
_argStackSize{},
|
||||
_rets{},
|
||||
_args{}
|
||||
{}
|
||||
|
||||
ASMJIT_INLINE FuncDetail(const FuncDetail& other) noexcept = default;
|
||||
ASMJIT_INLINE FuncDetail& operator=(const FuncDetail& other) noexcept = default;
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// [Init / Reset]
|
||||
|
|
@ -745,7 +753,7 @@ public:
|
|||
|
||||
//! Initialize this `FuncDetail` to the given signature.
|
||||
ASMJIT_API Error init(const FuncSignature& sign);
|
||||
ASMJIT_INLINE void reset() noexcept { ::memset(this, 0, sizeof(*this)); }
|
||||
ASMJIT_INLINE void reset() noexcept { *this = FuncDetail{}; }
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// [Accessors - Calling Convention]
|
||||
|
|
@ -878,20 +886,23 @@ struct FuncFrameInfo {
|
|||
// [Construction / Destruction]
|
||||
// --------------------------------------------------------------------------
|
||||
|
||||
ASMJIT_INLINE FuncFrameInfo() noexcept { reset(); }
|
||||
ASMJIT_INLINE FuncFrameInfo() noexcept:
|
||||
_attributes{},
|
||||
_dirtyRegs{},
|
||||
_stackFrameAlignment{},
|
||||
_callFrameAlignment{},
|
||||
_stackArgsRegId{Globals::kInvalidRegId},
|
||||
_stackFrameSize{},
|
||||
_callFrameSize{}
|
||||
{}
|
||||
|
||||
ASMJIT_INLINE FuncFrameInfo(const FuncFrameInfo& other) noexcept {
|
||||
::memcpy(this, &other, sizeof(*this));
|
||||
}
|
||||
ASMJIT_INLINE FuncFrameInfo(const FuncFrameInfo& other) noexcept = default;
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// [Init / Reset]
|
||||
// --------------------------------------------------------------------------
|
||||
|
||||
ASMJIT_INLINE void reset() noexcept {
|
||||
::memset(this, 0, sizeof(*this));
|
||||
_stackArgsRegId = Globals::kInvalidRegId;
|
||||
}
|
||||
ASMJIT_INLINE void reset() noexcept { *this = FuncFrameInfo{}; }
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// [Accessors]
|
||||
|
|
@ -1180,19 +1191,18 @@ public:
|
|||
// [Construction / Destruction]
|
||||
// --------------------------------------------------------------------------
|
||||
|
||||
explicit ASMJIT_INLINE FuncArgsMapper(const FuncDetail* fd) noexcept { reset(fd); }
|
||||
ASMJIT_INLINE FuncArgsMapper(const FuncArgsMapper& other) noexcept {
|
||||
::memcpy(this, &other, sizeof(*this));
|
||||
}
|
||||
explicit ASMJIT_INLINE FuncArgsMapper(const FuncDetail* fd = nullptr) noexcept:
|
||||
_funcDetail(fd),
|
||||
_args{}
|
||||
{}
|
||||
|
||||
ASMJIT_INLINE FuncArgsMapper(const FuncArgsMapper& other) noexcept = default;
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// [Reset]
|
||||
// --------------------------------------------------------------------------
|
||||
|
||||
ASMJIT_INLINE void reset(const FuncDetail* fd = nullptr) noexcept {
|
||||
_funcDetail = fd;
|
||||
::memset(_args, 0, sizeof(_args));
|
||||
}
|
||||
ASMJIT_INLINE void reset(const FuncDetail* fd = nullptr) noexcept { *this = FuncArgsMapper(fd); }
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// [Accessors]
|
||||
|
|
|
|||
|
|
@ -101,7 +101,7 @@ struct TiedReg {
|
|||
// --------------------------------------------------------------------------
|
||||
|
||||
ASMJIT_INLINE TiedReg& operator=(const TiedReg& other) {
|
||||
::memcpy(this, &other, sizeof(TiedReg));
|
||||
::memcpy((void*)this, &other, sizeof(TiedReg));
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -221,7 +221,8 @@ void ZoneHeap::reset(Zone* zone) noexcept {
|
|||
}
|
||||
|
||||
// Zero the entire class and initialize to the given `zone`.
|
||||
::memset(this, 0, sizeof(*this));
|
||||
_dynamicBlocks = nullptr;
|
||||
::memset(_slots, 0, sizeof(_slots));
|
||||
_zone = zone;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -297,14 +297,19 @@ class ZoneHeap {
|
|||
//! Create a new `ZoneHeap`.
|
||||
//!
|
||||
//! NOTE: To use it, you must first `init()` it.
|
||||
ASMJIT_INLINE ZoneHeap() noexcept {
|
||||
::memset(this, 0, sizeof(*this));
|
||||
}
|
||||
ASMJIT_INLINE ZoneHeap() noexcept:
|
||||
_zone{},
|
||||
_slots{},
|
||||
_dynamicBlocks{}
|
||||
{}
|
||||
|
||||
//! Create a new `ZoneHeap` initialized to use `zone`.
|
||||
explicit ASMJIT_INLINE ZoneHeap(Zone* zone) noexcept {
|
||||
::memset(this, 0, sizeof(*this));
|
||||
_zone = zone;
|
||||
}
|
||||
explicit ASMJIT_INLINE ZoneHeap(Zone* zone) noexcept:
|
||||
_zone(zone),
|
||||
_slots{},
|
||||
_dynamicBlocks{}
|
||||
{}
|
||||
|
||||
//! Destroy the `ZoneHeap`.
|
||||
ASMJIT_INLINE ~ZoneHeap() noexcept { reset(); }
|
||||
|
||||
|
|
|
|||