Initial version of GTK Wayland backend (#205)

* Obtaining Gtk backend and runtime and displaying it in About box.

* .fixes

* .csometics

* Initial iteration for GTK on Wayland. Works suprisingly stable.

* Disable X11Utils when Wayland backend detected.

* Cosmetics

* .native

* First iteration of CSD.

* .working

* .working

* Calculating additional window spaced used by CSD.

* Fix max window size problem.

* Fix issue with rendering.

* .refactoring

* Fix splash screen when SSD is enable.

* Fix issue with mouse scrolling when CSD is enable.

* Not ideal fix for no keyboard input in parent window.

* Fix problem with XDisplay compilation and change name of GdkBackend to GtkBackend.

* Introduce new WAYLAND flag.

* Ctrl::GetWndScreenRect() fix for X11.

* Fix to compile on mac

---------

Co-authored-by: Zbigniew Rębacz <zbigniew.rebacz@hotmail.com>
This commit is contained in:
Zbigniew Rębacz 2025-02-02 11:47:32 +01:00 committed by GitHub
parent 8077bb9c86
commit 511ff1e991
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
18 changed files with 391 additions and 77 deletions

View file

@ -38,6 +38,9 @@
#ifndef flagGTK
#define flagGTK
#endif
#ifdef flagWAYLAND
#define GUI_GTK_WAYLAND
#endif
#define GUIPLATFORM_INCLUDE "Gtk.h"
#endif
#endif

View file

@ -91,8 +91,10 @@ file
GtkDrawOp.cpp,
GtkDrawText.cpp,
GtkDrawImage.cpp,
GdkBackend.cpp,
GdkImage.cpp,
GtkUtil.cpp,
GtkCSD.cpp,
GtkX11Util.cpp,
GtkCtrl.h,
GtkCtrl.cpp,

View file

@ -18,7 +18,8 @@ static void sCheckGuiLock()
void Ctrl::RefreshFrame(const Rect& r) {
sCheckGuiLock();
GuiLock __; // Beware: Even if we have ThreadHasGuiLock ASSERT, we still can be the main thread!
if(!IsOpen() || !IsVisible() || r.IsEmpty()) return;
if(!IsOpen() || !IsVisible() || r.IsEmpty())
return;
LTIMING("RefreshFrame");
LLOG("RefreshRect " << Name() << ' ' << r);
if(GuiPlatformRefreshFrameSpecial(r))

View file

@ -0,0 +1,67 @@
#include "CtrlCore.h"
#ifdef GUI_GTK
#ifdef GDK_WINDOWING_X11
#include <gdk/gdkx.h>
#endif
#ifdef GDK_WINDOWING_WAYLAND
#include <gdk/gdkwayland.h>
#endif
namespace Upp {
namespace GdkBackend {
Type Get()
{
static auto backend = Type::UNKNOWN;
if (backend != Type::UNKNOWN) {
return backend;
}
auto* display = gdk_display_get_default();
#ifdef GDK_WINDOWING_X11
if (GDK_IS_X11_DISPLAY(display)) {
backend = Type::X11;
return backend;
}
#endif
#ifdef GDK_WINDOWING_WAYLAND
if (GDK_IS_WAYLAND_DISPLAY(display)) {
backend = Type::WAYLAND;
return backend;
}
#endif
return backend;
}
bool IsWayland()
{
return Get() == Type::WAYLAND;
}
bool IsX11()
{
return Get() == Type::X11;
}
bool IsRunningOnWayland()
{
static bool running = GetEnv("XDG_SESSION_TYPE") == "wayland";
return running;
}
}
String ToString(GdkBackend::Type t)
{
switch (t) {
case GdkBackend::Type::X11: return "X11";
case GdkBackend::Type::WAYLAND: return "Wayland";
default: return "Unknown";
}
}
}
#endif

View file

@ -164,11 +164,51 @@ public:
~ImageGdk();
};
class GtkCSD final {
public:
static bool IsSSDSupported();
public:
GtkCSD(GdkWindowTypeHint hint);
bool IsEnable() const { return enable; }
int ExtraWidth() const { return left_margin + right_margin; }
int ExtraHeight() const { return top_margin + bottom_margin; }
int LeftMaring() const { return left_margin; }
int RightMargin() const { return right_margin; }
int TopMargin() const { return top_margin; }
int BottomMargin() const { return bottom_margin; }
private:
void FindMargins(GdkWindowTypeHint hint);
private:
int left_margin = 0, right_margin = 0, top_margin = 0, bottom_margin = 0;
bool enable = false;
};
namespace GdkBackend {
enum class Type {
X11,
WAYLAND,
UNKNOWN
};
Type Get();
bool IsX11();
bool IsWayland();
bool IsRunningOnWayland();
}
String ToString(GdkBackend::Type b);
String FilesClipFromUrisFree(gchar **uris);
String ImageClipFromPixbufUnref(GdkPixbuf *pixbuf);
bool RunningOnWayland();
GdkAtom GAtom(const String& id);
#ifdef GDK_WINDOWING_X11
@ -183,9 +223,12 @@ Vector<int> GetPropertyInts(GdkWindow *w, const char *property);
#define GUIPLATFORM_CTRL_TOP_DECLS \
GtkWidget *window; \
GtkIMContext *im_context; \
GtkWidget *header = nullptr; \
GtkWidget *drawing_area = nullptr; \
GtkIMContext *im_context = nullptr; \
GtkIMContext *im_context_simple; \
GtkIMContext *im_context_multi; \
One<GtkCSD> csd; \
int64 cursor_id; \
int id; \

View file

@ -9,7 +9,7 @@ public:
class DHCtrl : Ctrl {};
void InitGtkApp(int argc, char **argv, const char **envptr);
bool InitGtkApp(int argc, char **argv, const char **envptr);
void ExitGtkApp();
#define GUI_APP_MAIN \
@ -18,7 +18,8 @@ void GuiMainFn_(); \
int main(int argc, char **argv, const char **envptr) { \
UPP::AppInit__(argc, (const char **)argv, envptr); \
GUI_APP_MAIN_HOOK \
UPP::InitGtkApp(argc, argv, envptr); \
if (!UPP::InitGtkApp(argc, argv, envptr)) \
return -1; \
UPP::AppExecute__(GuiMainFn_); \
UPP::Ctrl::CloseTopCtrls(); \
UPP::ExitGtkApp(); \

View file

@ -66,21 +66,27 @@ void Ctrl::ThemeChanged(void *)
PostReSkin();
}
void InitGtkApp(int argc, char **argv, const char **envptr)
bool InitGtkApp(int argc, char **argv, const char **envptr)
{
LLOG(rmsecs() << " InitGtkApp");
XInitThreads(); // otherwise there are errors despide GuiLock
running_on_wayland = GetEnv("XDG_SESSION_TYPE") == "wayland";
#if GTK_CHECK_VERSION(3, 10, 0)
gdk_set_allowed_backends("x11"); // this fixes some wayland issues
String backends = "x11,wayland";
#ifdef GUI_GTK_WAYLAND
backends = "wayland,x11";
#endif
gdk_set_allowed_backends(backends);
#endif
if (!gtk_init_check(&argc, &argv)) {
Cerr() << t_("Failed to initialized GTK app!") << "\n";
return false;
}
if (GdkBackend::IsX11()) {
XInitThreads(); // otherwise there are errors despide GuiLock
}
EnterGuiMutex();
gtk_init(&argc, &argv);
Ctrl::SetUHDEnabled(true);
Ctrl::scale = 1;
@ -92,15 +98,19 @@ void InitGtkApp(int argc, char **argv, const char **envptr)
Ctrl::ReSkin();
g_timeout_add(20, (GSourceFunc) Ctrl::TimeHandler, NULL);
InstallPanicMessageBox(Ctrl::PanicMsgBox);
gdk_window_add_filter(NULL, Ctrl::RootKeyFilter, NULL);
if (GdkBackend::IsX11())
gdk_window_add_filter(NULL, Ctrl::RootKeyFilter, NULL);
#if CATCH_ERRORS
g_log_set_default_handler (CatchError, 0);
#endif
GtkSettings *settings = gtk_settings_get_default ();
GtkSettings *settings = gtk_settings_get_default();
if(settings) {
g_signal_connect_swapped(settings, "notify::gtk-theme-name", G_CALLBACK(Ctrl::ThemeChanged), NULL);
g_signal_connect_swapped(settings, "notify::gtk-application-prefer-dark-theme", G_CALLBACK(Ctrl::ThemeChanged), NULL);
}
return true;
}
void ExitGtkApp()

View file

@ -0,0 +1,82 @@
#include <CtrlCore/CtrlCore.h>
#ifdef GUI_GTK
namespace Upp {
bool GtkCSD::IsSSDSupported()
{
if (GdkBackend::IsX11()) {
return false;
}
// TODO: Rewrite to negotiate with window manager once XDG decoration protocol will be
// stable. (https://wayland.app/protocols/xdg-decoration-unstable-v1)
// NOTE: Server side decoration are optional. It might be supported by Window manager or
// not. Let's have a list of desktop environments on which we tested it works correctly.
auto desktop = GetEnv("XDG_SESSION_DESKTOP");
if (desktop == "KDE") {
return true;
//return true;
}
return false;
}
GtkCSD::GtkCSD(GdkWindowTypeHint hint)
{
if (!GdkBackend::IsWayland())
return;
if (IsSSDSupported()) {
if (hint == GDK_WINDOW_TYPE_HINT_POPUP_MENU) {
FindMargins(hint);
enable = true;
}
return;
}
if (hint == GDK_WINDOW_TYPE_HINT_COMBO) {
return;
}
FindMargins(hint);
enable = true;
}
void GtkCSD::FindMargins(GdkWindowTypeHint hint)
{
GtkWidget* win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
GtkWidget* header;
if (findarg(hint, GDK_WINDOW_TYPE_HINT_POPUP_MENU) >= 0) {
header = gtk_drawing_area_new();
gtk_widget_set_size_request(header, 1, 1);
gtk_window_set_titlebar((GtkWindow *)win, header);
} else {
header = gtk_header_bar_new();
gtk_header_bar_set_show_close_button(GTK_HEADER_BAR(header), TRUE);
gtk_window_set_titlebar((GtkWindow *)win, header);
}
GtkWidget* drawing_area = gtk_drawing_area_new();
gtk_container_add(GTK_CONTAINER(win), drawing_area);
gtk_widget_show_all(win);
gdk_window_get_origin(gtk_widget_get_window(drawing_area), &left_margin, &top_margin);
gint drawing_area_width = gtk_widget_get_allocated_width(drawing_area);
gint drawing_area_height = gtk_widget_get_allocated_height(drawing_area);
gint win_width = gtk_widget_get_allocated_width(win);
gint win_height = gtk_widget_get_allocated_height(win);
right_margin = win_width - left_margin - drawing_area_width;
bottom_margin = win_height - top_margin - drawing_area_height;
gtk_widget_destroy(drawing_area);
gtk_widget_destroy(header);
gtk_widget_destroy(win);
}
}
#endif

View file

@ -30,22 +30,42 @@ void Ctrl::Create(Ctrl *owner, bool popup)
w.gdk = nullptr;
TopWindow *tw = dynamic_cast<TopWindow *>(this);
GdkWindowTypeHint type_hint;
if(popup && !owner) {
gtk_window_set_decorated(gtk(), FALSE);
// gtk_window_set_has_frame(gtk(), FALSE);
gtk_window_set_type_hint(gtk(), GDK_WINDOW_TYPE_HINT_POPUP_MENU);
type_hint = GDK_WINDOW_TYPE_HINT_POPUP_MENU;
}
else
gtk_window_set_type_hint(gtk(), popup ? GDK_WINDOW_TYPE_HINT_COMBO
: tw && tw->tool ? GDK_WINDOW_TYPE_HINT_UTILITY
: owner ? GDK_WINDOW_TYPE_HINT_DIALOG
: GDK_WINDOW_TYPE_HINT_NORMAL);
else {
type_hint = popup ? GDK_WINDOW_TYPE_HINT_COMBO
: tw && tw->tool ? GDK_WINDOW_TYPE_HINT_UTILITY
: owner ? GDK_WINDOW_TYPE_HINT_DIALOG
: GDK_WINDOW_TYPE_HINT_NORMAL;
}
gtk_window_set_type_hint(gtk(), type_hint);
top->csd.Create(type_hint);
if (top->csd->IsEnable()) {
if (findarg(type_hint, GDK_WINDOW_TYPE_HINT_POPUP_MENU) >= 0) {
top->header = gtk_drawing_area_new();
gtk_widget_set_size_request(top->header, 1, 1);
gtk_window_set_titlebar(gtk(), top->header);
} else {
top->header = gtk_header_bar_new();
gtk_header_bar_set_show_close_button(GTK_HEADER_BAR(top->header), TRUE);
gtk_window_set_titlebar(gtk(), top->header);
}
top->drawing_area = gtk_drawing_area_new();
gtk_widget_set_can_focus(top->drawing_area, TRUE);
} else {
top->drawing_area = top->window;
}
top->cursor_id = -1;
gtk_widget_set_events(top->window, GDK_ALL_EVENTS_MASK & ~GDK_POINTER_MOTION_HINT_MASK);
g_signal_connect(top->window, "event", G_CALLBACK(GtkEvent), (gpointer)(uintptr_t)top->id);
g_signal_connect(top->window, "draw", G_CALLBACK(GtkDraw), (gpointer)(uintptr_t)top->id);
gtk_widget_set_events(top->drawing_area, GDK_ALL_EVENTS_MASK & ~GDK_POINTER_MOTION_HINT_MASK & ~GDK_SMOOTH_SCROLL_MASK);
g_signal_connect(top->drawing_area, "event", G_CALLBACK(GtkEvent), (gpointer)(uintptr_t)top->id);
g_signal_connect(top->drawing_area, "draw", G_CALLBACK(GtkDraw), (gpointer)(uintptr_t)top->id);
GdkWindowTypeHint hint = gtk_window_get_type_hint(gtk());
if(tw && findarg(hint, GDK_WINDOW_TYPE_HINT_NORMAL, GDK_WINDOW_TYPE_HINT_DIALOG, GDK_WINDOW_TYPE_HINT_UTILITY) >= 0)
@ -53,15 +73,20 @@ void Ctrl::Create(Ctrl *owner, bool popup)
Rect r = GetRect();
gtk_window_set_default_size (gtk(), LSC(r.GetWidth()), LSC(r.GetHeight()));
// TODO: Normalize
gtk_window_set_default_size(gtk(), LSC(r.GetWidth()), LSC(r.GetHeight()));
gtk_window_move(gtk(), LSC(r.left), LSC(r.top));
gtk_window_resize(gtk(), LSC(r.GetWidth()), LSC(r.GetHeight()));
if (top->header) {
gtk_container_add(GTK_CONTAINER(top->window), top->drawing_area);
gtk_widget_show_all(top->window);
} else {
gtk_widget_realize(top->window);
}
gtk_widget_realize(top->window);
w.gdk = gtk_widget_get_window(top->window);
if(owner && owner->top)
gtk_window_set_transient_for(gtk(), owner->gtk());
gtk_widget_set_app_paintable(top->window, TRUE);
@ -114,8 +139,10 @@ void Ctrl::WndDestroy()
activeCtrl = owner;
}
Top *top = GetTop();
if(top->im_context)
if(top->im_context) {
g_object_unref(top->im_context);
top->im_context = nullptr;
}
gtk_widget_destroy(top->window);
isopen = false;
popup = false;
@ -124,8 +151,14 @@ void Ctrl::WndDestroy()
int q = FindCtrl(this);
if(q >= 0)
wins.Remove(q);
if(owner)
if(owner) {
if(owner->utop->csd->IsEnable()) {
// TODO: This fix the problem with keyboard when backing to original window, but
// the previous control is not being focused like it should be.
gtk_window_set_focus(owner->gtk(), owner->utop->drawing_area);
}
owner->WndUpdate();
}
TopWindow *w = dynamic_cast<TopWindow *>(this);
if(w && w->overlapped.GetWidth() && w->overlapped.GetHeight())
SetRect(w->overlapped);

View file

@ -2,6 +2,10 @@
#ifdef GUI_GTK
#ifndef PLATFORM_OPENBSD
#undef CurrentTime
#endif
#define LLOG(x) // DLOG(x)
namespace Upp {
@ -76,8 +80,6 @@ GtkWindow *Ctrl::gtk() const
return top ? (GtkWindow *)top->window : NULL;
}
}
#endif

View file

@ -168,7 +168,7 @@ _DBG_
guint time, gpointer user_data, bool paste);
static bool ProcessInvalids();
friend void InitGtkApp(int argc, char **argv, const char **envptr);
friend bool InitGtkApp(int argc, char **argv, const char **envptr);
friend void GuiPlatformGripResize(TopWindow *q);
public: // really private:
@ -187,7 +187,7 @@ public: // really private:
static Gclipboard& gclipboard();
static Gclipboard& gselection();
static String RenderPrimarySelection(const Value& fmt);
static Vector<Event<>> hotkey;
static Vector<dword> keyhot;
static Vector<dword> modhot;

View file

@ -2,6 +2,10 @@
#ifdef GUI_GTK
#ifndef PLATFORM_OPENBSD
#undef CurrentTime
#endif
namespace Upp {
#define LLOG(x) // DLOG(x)
@ -89,6 +93,7 @@ gboolean Ctrl::GtkDraw(GtkWidget *widget, cairo_t *cr, gpointer user_data)
SystemDraw w(cr);
painting = true;
double x1, y1, x2, y2;
cairo_clip_extents (cr, &x1, &y1, &x2, &y2);
Rect r = RectC((int)x1, (int)y1, (int)ceil(x2 - x1), (int)ceil(y2 - y1));
@ -136,7 +141,7 @@ gboolean Ctrl::GtkEvent(GtkWidget *widget, GdkEvent *event, gpointer user_data)
p->CancelPreedit();
if(p) {
Top *top = p->GetTop();
if(top) {
if(top && top->im_context) {
if(((GdkEventFocus *)event)->in)
gtk_im_context_focus_in(top->im_context);
else

View file

@ -14,7 +14,7 @@ Rect Ctrl::GetFrameMargins()
return frameMargins != Rect(0, 0, 0, 0) ? frameMargins : Rect(8, 32, 8, 8);
}
void TopWindow::SyncSizeHints()
void TopWindow::SyncSizeHints()
{
GuiLock __;
if(!top)
@ -25,13 +25,13 @@ void TopWindow::SyncSizeHints()
Size sz = sz0;
if(sizeable)
sz = GetMinSize();
m.min_width = LSC(sz.cx);
m.min_height = LSC(sz.cy);
m.min_width = LSC(sz.cx + utop->csd->ExtraWidth());
m.min_height = LSC(sz.cy + utop->csd->ExtraHeight());
sz = sz0;
if(sizeable)
sz = GetMaxSize();
m.max_width = LSC(sz.cx);
m.max_height = LSC(sz.cy);
m.max_width = LSC(sz.cx + utop->csd->ExtraWidth());
m.max_height = LSC(sz.cy + utop->csd->ExtraHeight());
gtk_window_set_resizable(gtk(), sizeable);
Top *top = GetTop();
if(top) {

View file

@ -111,7 +111,7 @@ void Ctrl::SetMouseCursor(const Image& image)
if(c && topctrl->IsOpen()) {
gdk_window_set_cursor(topctrl->gdk(), c);
g_object_unref(c);
if(RunningOnWayland()) // wayland is broken, need some paint to change the cursor...
if(GdkBackend::IsX11() && GdkBackend::IsRunningOnWayland()) // wayland is broken, need some paint to change the cursor...
topctrl->Refresh(0, 0, 1, 1);
gdk_display_flush(gdk_display_get_default()); // Make it visible immediately
}
@ -153,14 +153,28 @@ void Ctrl::UnregisterSystemHotKey(int id)
Rect Ctrl::GetWndScreenRect() const
{
GuiLock __;
if(IsOpen()) {
gint x, y;
gdk_window_get_position(gdk(), &x, &y);
gint width = gdk_window_get_width(gdk());
gint height = gdk_window_get_height(gdk());
return SCL(x, y, width, height);
if(!IsOpen()) {
return Null;
}
return Null;
gint x, y;
gint width, height;
if (GdkBackend::IsWayland()) {
if(top && utop->csd->IsEnable()) {
gdk_window_get_origin(gtk_widget_get_window(utop->drawing_area), &x, &y);
width = gtk_widget_get_allocated_width(utop->drawing_area);
height = gtk_widget_get_allocated_height(utop->drawing_area);
} else {
gdk_window_get_geometry(gdk(), &x, &y, &width, &height);
}
} else {
gdk_window_get_position(gdk(), &x, &y);
width = gdk_window_get_width(gdk());
height = gdk_window_get_height(gdk());
}
return SCL(x, y, width, height);
}
void Ctrl::WndShow(bool b)
@ -169,7 +183,6 @@ void Ctrl::WndShow(bool b)
LLOG("WndShow " << Name() << ", " << b);
Top *top = GetTop();
if(IsOpen() && top) {
if(b)
gtk_widget_show_now(top->window);
else
@ -197,20 +210,24 @@ Rect Ctrl::GetWorkArea() const
void Ctrl::GetWorkArea(Array<Rect>& rc)
{
GuiLock __;
rc.Clear();
#if GTK_CHECK_VERSION(3, 22, 0)
GdkDisplay *s = gdk_display_get_default();
int n = gdk_display_get_n_monitors(s);
rc.Clear();
Vector<int> netwa;
for(int i = 0; i < n; i++) {
GdkRectangle rr;
gdk_monitor_get_workarea(gdk_display_get_monitor(s, i), &rr);
auto *pMonitor = gdk_display_get_monitor(s, i);
if (GdkBackend::IsWayland()) {
gdk_monitor_get_geometry(pMonitor, &rr);
} else {
gdk_monitor_get_workarea(pMonitor, &rr);
}
rc.Add(SCL(rr.x, rr.y, rr.width, rr.height));
}
#else
GdkScreen *s = gdk_screen_get_default();
int n = gdk_screen_get_n_monitors(s);
rc.Clear();
Vector<int> netwa;
for(int i = 0; i < n; i++) {
GdkRectangle rr;
@ -236,19 +253,45 @@ Rect Ctrl::GetVirtualWorkArea()
Rect Ctrl::GetVirtualScreenArea()
{
GuiLock __;
auto pRootWindow = gdk_screen_get_root_window(gdk_screen_get_default());
if (!pRootWindow) {
ASSERT("Failed to obtain root window!");
return Rect();
}
#if GTK_CHECK_VERSION(3, 22, 0)
if (GdkBackend::IsWayland()) {
GdkRectangle rr;
auto *pDisplay = gdk_display_get_default();
auto *pMonitor = gdk_display_get_monitor_at_window(pDisplay, pRootWindow);
if (!pMonitor) {
ASSERT("Failed to obtain monitor!");
return Rect();
}
gdk_monitor_get_geometry(pMonitor, &rr);
return SCL(rr.x, rr.y, rr.width, rr.height);
}
#endif
if (GdkBackend::IsWayland()) {
ASSERT("GTK Wayland backend not supported before 3.22 GTK version.");
return Rect();
}
gint x, y, width, height;
gdk_window_get_geometry(gdk_screen_get_root_window(gdk_screen_get_default()),
&x, &y, &width, &height);
Rect r = SCL(x, y, width, height);
return r;
gdk_window_get_geometry(pRootWindow, &x, &y, &width, &height);
return SCL(x, y, width, height);
}
Rect Ctrl::GetPrimaryWorkArea()
{
GuiLock __;
#if GTK_CHECK_VERSION(3, 22, 0)
if (GdkBackend::IsWayland()) {
// NOTE: WorkArea on Wayland is not available... Window manager decides where to put
// windows.
return GetVirtualScreenArea();
}
GdkRectangle rr;
gdk_monitor_get_workarea(gdk_display_get_primary_monitor(gdk_display_get_default()), &rr);
auto* display = gdk_display_get_default();
gdk_monitor_get_workarea(gdk_display_get_primary_monitor(display), &rr);
return SCL(rr.x, rr.y, rr.width, rr.height);
#else
static Rect r;
@ -384,16 +427,28 @@ void WakeUpGuiThread();
void Ctrl::WndInvalidateRect(const Rect& r)
{
GuiLock __;
LLOG("WndInvalidateRect " << r);
Rect nr = r;
if (top && utop->csd->IsEnable()) {
gint x, y;
gdk_window_get_origin(gtk_widget_get_window(utop->drawing_area), &x, &y);
nr.left += x;
nr.top += y;
nr.right += x;
nr.bottom += y;
}
Rect rr;
if(scale > 1) {
rr.left = r.left / 2;
rr.top = r.top / 2;
rr.right = (r.right + 1) / 2;
rr.bottom = (r.bottom + 1) / 2;
rr.left = nr.left / 2;
rr.top = nr.top / 2;
rr.right = (nr.right + 1) / 2;
rr.bottom = (nr.bottom + 1) / 2;
}
else
rr = r;
rr = nr;
// as gtk3 dropped thread locking, we need to push invalid rectangles onto main loop
for(Win& win : wins) {

View file

@ -1,17 +1,14 @@
#include "CtrlCore.h"
#include <CtrlCore/CtrlCore.h>
#ifdef GUI_GTK
#ifdef GDK_WINDOWING_X11
namespace Upp {
#define Time XTime
#define Font XFont
#define Display XDisplay
#define Picture XPicture
#ifndef PLATFORM_OPENBSD // avoid warning
#undef CurrentTime
#define CurrentTime XCurrentTime
#endif
@ -20,13 +17,14 @@ namespace Upp {
#undef Picture
#undef Time
#undef Font
#undef Display
#ifndef PLATFORM_OPENBSD // avoid warning
#undef CurrentTime
#endif
XDisplay *Xdisplay()
namespace Upp {
_XDisplay *Xdisplay()
{
return GDK_DISPLAY_XDISPLAY(gdk_display_get_default());
}
@ -89,6 +87,9 @@ Atom XAtom(const char *name)
Vector<int> GetPropertyInts(GdkWindow *w, const char *property)
{
if (GdkBackend::IsWayland())
return {}; // Not supported on Wayland...
GuiLock __;
Vector<int> result;
String p = GetProperty(GDK_WINDOW_XID(w), XAtom(property), AnyPropertyType);
@ -114,6 +115,9 @@ dword X11mods(dword key)
int Ctrl::RegisterSystemHotKey(dword key, Function<void ()> cb)
{
if (GdkBackend::IsWayland())
return -1; // Not supported on Wayland...
GuiLock __;
ASSERT(key >= K_DELTA);
gdk_x11_display_error_trap_push(gdk_display_get_default());
@ -142,6 +146,9 @@ int Ctrl::RegisterSystemHotKey(dword key, Function<void ()> cb)
void Ctrl::UnregisterSystemHotKey(int id)
{
if (GdkBackend::IsWayland())
return; // Not supported on Wayland...
GuiLock __;
if(id >= 0 && id < hotkey.GetCount() && hotkey[id]) {
gdk_x11_display_error_trap_push(gdk_display_get_default());
@ -158,6 +165,9 @@ void Ctrl::UnregisterSystemHotKey(int id)
GdkFilterReturn Ctrl::RootKeyFilter(GdkXEvent *xevent, GdkEvent *Xevent, gpointer data)
{
if (GdkBackend::IsWayland())
return GDK_FILTER_CONTINUE; // Not supported on Wayland...
XEvent *event = (XEvent *)xevent;
if(event->type == KeyPress)
for(int i = 0; i < hotkey.GetCount(); i++)
@ -172,5 +182,4 @@ GdkFilterReturn Ctrl::RootKeyFilter(GdkXEvent *xevent, GdkEvent *Xevent, gpointe
}
#endif
#endif

View file

@ -420,7 +420,7 @@ void Ctrl::InitX11(const char *display)
_NET_Supported().Add(nets[i]);
Font::SetDefaultFont(Arial(12));
ReSkin();
GUI_GlobalStyle_Write(GUISTYLE_XP);

View file

@ -72,7 +72,7 @@ String SplashCtrl::GenerateVersionInfo(bool qtf, bool about)
#endif
#ifdef GUI_GTK
h << " Gtk";
h << " Gtk::" << ToString(GdkBackend::Get());
#endif
#ifdef FLATPAK
h << " Flatpak";

View file

@ -134,6 +134,7 @@ mainconfig
"" = "GUI PEAKMEM",
"" = "GUI HEAPLOG",
"" = "GUI X11",
"" = "GUI GTK WAYLAND",
"" = "GUI DEBUGCODE";
custom() "",