diff --git a/uppsrc/CtrlCore/CtrlCore.h b/uppsrc/CtrlCore/CtrlCore.h index 209942633..6d16e2884 100644 --- a/uppsrc/CtrlCore/CtrlCore.h +++ b/uppsrc/CtrlCore/CtrlCore.h @@ -38,6 +38,9 @@ #ifndef flagGTK #define flagGTK #endif + #ifdef flagWAYLAND + #define GUI_GTK_WAYLAND + #endif #define GUIPLATFORM_INCLUDE "Gtk.h" #endif #endif diff --git a/uppsrc/CtrlCore/CtrlCore.upp b/uppsrc/CtrlCore/CtrlCore.upp index f498c239d..f20659b4e 100644 --- a/uppsrc/CtrlCore/CtrlCore.upp +++ b/uppsrc/CtrlCore/CtrlCore.upp @@ -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, diff --git a/uppsrc/CtrlCore/CtrlDraw.cpp b/uppsrc/CtrlCore/CtrlDraw.cpp index 7dab14eb5..283ec5a6c 100644 --- a/uppsrc/CtrlCore/CtrlDraw.cpp +++ b/uppsrc/CtrlCore/CtrlDraw.cpp @@ -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)) diff --git a/uppsrc/CtrlCore/GdkBackend.cpp b/uppsrc/CtrlCore/GdkBackend.cpp new file mode 100644 index 000000000..b67e620fc --- /dev/null +++ b/uppsrc/CtrlCore/GdkBackend.cpp @@ -0,0 +1,67 @@ +#include "CtrlCore.h" + +#ifdef GUI_GTK + +#ifdef GDK_WINDOWING_X11 +#include +#endif +#ifdef GDK_WINDOWING_WAYLAND +#include +#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 diff --git a/uppsrc/CtrlCore/Gtk.h b/uppsrc/CtrlCore/Gtk.h index 1b25eca0b..9932b9267 100644 --- a/uppsrc/CtrlCore/Gtk.h +++ b/uppsrc/CtrlCore/Gtk.h @@ -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 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 csd; \ int64 cursor_id; \ int id; \ diff --git a/uppsrc/CtrlCore/GtkAfter.h b/uppsrc/CtrlCore/GtkAfter.h index d15cda88d..1f8346ea5 100644 --- a/uppsrc/CtrlCore/GtkAfter.h +++ b/uppsrc/CtrlCore/GtkAfter.h @@ -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(); \ diff --git a/uppsrc/CtrlCore/GtkApp.cpp b/uppsrc/CtrlCore/GtkApp.cpp index 5471eda34..cd62d4711 100644 --- a/uppsrc/CtrlCore/GtkApp.cpp +++ b/uppsrc/CtrlCore/GtkApp.cpp @@ -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() diff --git a/uppsrc/CtrlCore/GtkCSD.cpp b/uppsrc/CtrlCore/GtkCSD.cpp new file mode 100644 index 000000000..941143099 --- /dev/null +++ b/uppsrc/CtrlCore/GtkCSD.cpp @@ -0,0 +1,82 @@ +#include + +#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 diff --git a/uppsrc/CtrlCore/GtkCreate.cpp b/uppsrc/CtrlCore/GtkCreate.cpp index 3a5680e93..edb222741 100644 --- a/uppsrc/CtrlCore/GtkCreate.cpp +++ b/uppsrc/CtrlCore/GtkCreate.cpp @@ -30,22 +30,42 @@ void Ctrl::Create(Ctrl *owner, bool popup) w.gdk = nullptr; TopWindow *tw = dynamic_cast(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(this); if(w && w->overlapped.GetWidth() && w->overlapped.GetHeight()) SetRect(w->overlapped); diff --git a/uppsrc/CtrlCore/GtkCtrl.cpp b/uppsrc/CtrlCore/GtkCtrl.cpp index 75cf19c47..f846757bd 100644 --- a/uppsrc/CtrlCore/GtkCtrl.cpp +++ b/uppsrc/CtrlCore/GtkCtrl.cpp @@ -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 diff --git a/uppsrc/CtrlCore/GtkCtrl.h b/uppsrc/CtrlCore/GtkCtrl.h index f77436787..fdb3d7033 100644 --- a/uppsrc/CtrlCore/GtkCtrl.h +++ b/uppsrc/CtrlCore/GtkCtrl.h @@ -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> hotkey; static Vector keyhot; static Vector modhot; diff --git a/uppsrc/CtrlCore/GtkEvent.cpp b/uppsrc/CtrlCore/GtkEvent.cpp index 70a9c1fa3..35e9d6911 100644 --- a/uppsrc/CtrlCore/GtkEvent.cpp +++ b/uppsrc/CtrlCore/GtkEvent.cpp @@ -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 diff --git a/uppsrc/CtrlCore/GtkTop.cpp b/uppsrc/CtrlCore/GtkTop.cpp index c23a6909d..d4d746dc5 100644 --- a/uppsrc/CtrlCore/GtkTop.cpp +++ b/uppsrc/CtrlCore/GtkTop.cpp @@ -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) { diff --git a/uppsrc/CtrlCore/GtkWnd.cpp b/uppsrc/CtrlCore/GtkWnd.cpp index fcb38bbf3..2522568a0 100644 --- a/uppsrc/CtrlCore/GtkWnd.cpp +++ b/uppsrc/CtrlCore/GtkWnd.cpp @@ -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& 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 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 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) { diff --git a/uppsrc/CtrlCore/GtkX11Util.cpp b/uppsrc/CtrlCore/GtkX11Util.cpp index 9250827b9..af18398c1 100644 --- a/uppsrc/CtrlCore/GtkX11Util.cpp +++ b/uppsrc/CtrlCore/GtkX11Util.cpp @@ -1,17 +1,14 @@ -#include "CtrlCore.h" +#include #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 GetPropertyInts(GdkWindow *w, const char *property) { + if (GdkBackend::IsWayland()) + return {}; // Not supported on Wayland... + GuiLock __; Vector 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 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 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 diff --git a/uppsrc/CtrlCore/X11App.cpp b/uppsrc/CtrlCore/X11App.cpp index f613b9792..cbb891f6d 100644 --- a/uppsrc/CtrlCore/X11App.cpp +++ b/uppsrc/CtrlCore/X11App.cpp @@ -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); diff --git a/uppsrc/ide/About.cpp b/uppsrc/ide/About.cpp index 7fcddc30d..d6a44ac69 100644 --- a/uppsrc/ide/About.cpp +++ b/uppsrc/ide/About.cpp @@ -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"; diff --git a/uppsrc/ide/ide.upp b/uppsrc/ide/ide.upp index 16bd4de88..b15678ec4 100644 --- a/uppsrc/ide/ide.upp +++ b/uppsrc/ide/ide.upp @@ -134,6 +134,7 @@ mainconfig "" = "GUI PEAKMEM", "" = "GUI HEAPLOG", "" = "GUI X11", + "" = "GUI GTK WAYLAND", "" = "GUI DEBUGCODE"; custom() "",