Wayland and XWayland fallback implementation uses XDP in order to query picked color from desktop.
159 lines
5.0 KiB
C++
159 lines
5.0 KiB
C++
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
|
|
|
|
#if PLATFORM_LINUX
|
|
|
|
#include "Engine/Platform/Types.h"
|
|
#include "Engine/Platform/ScreenUtilities.h"
|
|
#include "Engine/Core/Math/Vector2.h"
|
|
#include "Engine/Core/Delegate.h"
|
|
#include "Engine/Core/Log.h"
|
|
#include "Engine/Core/Math/Vector4.h"
|
|
#include "Engine/Profiler/ProfilerCPU.h"
|
|
#include "Engine/Platform/Linux/LinuxPlatform.h"
|
|
#include "Engine/Platform/Linux/IncludeX11.h"
|
|
|
|
#include <libportal/portal-enums.h>
|
|
#include <libportal/screenshot.h>
|
|
|
|
Delegate<Color32> ScreenUtilitiesBase::PickColorDone;
|
|
|
|
namespace PortalImpl
|
|
{
|
|
XdpPortal* Portal = nullptr;
|
|
int64 MainLoopReady = 0;
|
|
|
|
gpointer GLibMainLoop(gpointer data);
|
|
void PickColorCallback(GObject* source, GAsyncResult* result, gpointer data);
|
|
}
|
|
|
|
Color32 LinuxScreenUtilities::GetColorAt(const Float2& pos)
|
|
{
|
|
X11::Display* display = (X11::Display*)Platform::GetXDisplay();
|
|
if (display)
|
|
{
|
|
int defaultScreen = X11::XDefaultScreen(display);
|
|
X11::Window rootWindow = X11::XRootWindow(display, defaultScreen);
|
|
X11::XImage* image = X11::XGetImage(display, rootWindow, (int)pos.X, (int)pos.Y, 1, 1, AllPlanes, XYPixmap);
|
|
if (image)
|
|
{
|
|
X11::XColor color;
|
|
color.pixel = XGetPixel(image, 0, 0);
|
|
X11::XFree(image);
|
|
|
|
X11::XQueryColor(display, X11::XDefaultColormap(display, defaultScreen), &color);
|
|
|
|
Color32 outputColor;
|
|
outputColor.R = color.red / 256;
|
|
outputColor.G = color.green / 256;
|
|
outputColor.B = color.blue / 256;
|
|
outputColor.A = 255;
|
|
return outputColor;
|
|
}
|
|
else
|
|
{
|
|
// XWayland doesn't support XGetImage
|
|
}
|
|
}
|
|
|
|
return Color32::Transparent;
|
|
}
|
|
|
|
void OnScreenUtilsXEventCallback(void* eventPtr)
|
|
{
|
|
X11::XEvent* event = (X11::XEvent*) eventPtr;
|
|
X11::Display* display = (X11::Display*)Platform::GetXDisplay();
|
|
if (event->type == ButtonPress)
|
|
{
|
|
const Float2 cursorPos = Platform::GetMousePosition();
|
|
const Color32 colorPicked = ScreenUtilities::GetColorAt(cursorPos);
|
|
X11::XUngrabPointer(display, CurrentTime);
|
|
ScreenUtilities::PickColorDone(colorPicked);
|
|
LinuxPlatform::xEventReceived.Unbind(OnScreenUtilsXEventCallback);
|
|
}
|
|
}
|
|
|
|
void LinuxScreenUtilities::PickColor()
|
|
{
|
|
PROFILE_CPU();
|
|
X11::Display* display = (X11::Display*)Platform::GetXDisplay();
|
|
if (display)
|
|
{
|
|
X11::Window rootWindow = X11::XRootWindow(display, X11::XDefaultScreen(display));
|
|
|
|
X11::Cursor cursor = XCreateFontCursor(display, 130);
|
|
int grabbedPointer = X11::XGrabPointer(display, rootWindow, 0, ButtonPressMask, GrabModeAsync, GrabModeAsync, rootWindow, cursor, CurrentTime);
|
|
if (grabbedPointer != GrabSuccess)
|
|
{
|
|
LOG(Error, "Failed to grab cursor for events.");
|
|
X11::XFreeCursor(display, cursor);
|
|
return;
|
|
}
|
|
|
|
X11::XFreeCursor(display, cursor);
|
|
LinuxPlatform::xEventReceived.Bind(OnScreenUtilsXEventCallback);
|
|
return;
|
|
}
|
|
|
|
if (PortalImpl::MainLoopReady == 0)
|
|
{
|
|
// Initialize portal
|
|
GError* error = nullptr;
|
|
PortalImpl::Portal = xdp_portal_initable_new(&error);
|
|
if (error != nullptr)
|
|
{
|
|
PortalImpl::MainLoopReady = 2;
|
|
LOG(Error, "Failed to initialize XDP Portal");
|
|
return;
|
|
}
|
|
|
|
// Run the GLib main loop in other thread in order to process asynchronous callbacks
|
|
g_thread_new(nullptr, PortalImpl::GLibMainLoop, nullptr);
|
|
while (Platform::AtomicRead(&PortalImpl::MainLoopReady) != 1)
|
|
Platform::Sleep(1);
|
|
}
|
|
|
|
if (PortalImpl::Portal != nullptr)
|
|
{
|
|
// Enter color picking mode, the callback receives the final color
|
|
xdp_portal_pick_color(PortalImpl::Portal, nullptr, nullptr, PortalImpl::PickColorCallback, nullptr);
|
|
}
|
|
}
|
|
|
|
gpointer PortalImpl::GLibMainLoop(gpointer data)
|
|
{
|
|
GMainContext* mainContext = g_main_context_get_thread_default();
|
|
GMainLoop* mainLoop = g_main_loop_new(mainContext, false);
|
|
|
|
Platform::AtomicStore(&PortalImpl::MainLoopReady, 1);
|
|
|
|
g_main_loop_run(mainLoop);
|
|
g_main_loop_unref(mainLoop);
|
|
return nullptr;
|
|
}
|
|
|
|
void PortalImpl::PickColorCallback(GObject* source, GAsyncResult* result, gpointer data)
|
|
{
|
|
GError* error = nullptr;
|
|
GVariant* variant = xdp_portal_pick_color_finish(PortalImpl::Portal, result, &error);
|
|
if (error)
|
|
{
|
|
if (g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
|
|
LOG(Info, "XDP Portal pick color cancelled");
|
|
else
|
|
LOG(Error, "XDP Portal pick color failed: {}", String(error->message));
|
|
return;
|
|
}
|
|
|
|
// The color is stored in a triple double variant, extract the values
|
|
Double4 colorDouble;
|
|
g_variant_get(variant, "(ddd)", &colorDouble.X, &colorDouble.Y, &colorDouble.Z);
|
|
g_variant_unref(variant);
|
|
colorDouble.W = 1.0f;
|
|
Vector4 colorVector = colorDouble;
|
|
Color32 color = Color32(colorVector);
|
|
|
|
ScreenUtilities::PickColorDone(color);
|
|
}
|
|
|
|
#endif
|