From cfff488562e913673cf42ff915b242ee15a05176 Mon Sep 17 00:00:00 2001
From: zccrs <zccrs@live.com>
Date: Fri, 17 May 2019 16:30:09 +0800
Subject: [PATCH] feat: support _GTK_SHOW_WINDOW_MENU

https://github.com/linuxdeepin/internal-discussion/issues/1320
---
 atoms.cpp           |  1 +
 atoms.h             |  1 +
 events.cpp          |  8 ++++++
 rootinfo_filter.cpp | 67 +++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 77 insertions(+)

diff --git a/atoms.cpp b/atoms.cpp
index ee9e8e2..ecd4cf6 100644
--- a/atoms.cpp
+++ b/atoms.cpp
@@ -56,6 +56,7 @@ Atoms::Atoms()
     , kde_skip_close_animation(QByteArrayLiteral("_KDE_NET_WM_SKIP_CLOSE_ANIMATION"))
     , kde_screen_edge_show(QByteArrayLiteral("_KDE_NET_WM_SCREEN_EDGE_SHOW"))
     , gtk_frame_extents(QByteArrayLiteral("_GTK_FRAME_EXTENTS"))
+    , gtk_show_window_menu(QByteArrayLiteral("_GTK_SHOW_WINDOW_MENU"))
     , kwin_dbus_service(QByteArrayLiteral("_ORG_KDE_KWIN_DBUS_SERVICE"))
     , utf8_string(QByteArrayLiteral("UTF8_STRING"))
     , wl_surface_id(QByteArrayLiteral("WL_SURFACE_ID"))
diff --git a/atoms.h b/atoms.h
index 2c7b2d9..fb2a544 100644
--- a/atoms.h
+++ b/atoms.h
@@ -65,6 +65,7 @@ public:
     Xcb::Atom kde_skip_close_animation;
     Xcb::Atom kde_screen_edge_show;
     Xcb::Atom gtk_frame_extents;
+    Xcb::Atom gtk_show_window_menu;
     Xcb::Atom kwin_dbus_service;
     Xcb::Atom utf8_string;
     Xcb::Atom wl_surface_id;
diff --git a/events.cpp b/events.cpp
index 17a2606..b4f558d 100644
--- a/events.cpp
+++ b/events.cpp
@@ -661,6 +661,14 @@ void Client::clientMessageEvent(xcb_client_message_event_t *e)
         if (e->data.data32[0] == XCB_ICCCM_WM_STATE_ICONIC)
             minimize();
         return;
+    } else if (e->type == atoms->gtk_show_window_menu) {
+        // _GTK_SHOW_WINDOW_MENU
+        QPoint pos;
+
+        pos.setX(e->data.data32[1]);
+        pos.setY(e->data.data32[2]);
+
+        workspace()->showWindowMenu(QRect(pos, pos), this);
     }
 }
 
diff --git a/rootinfo_filter.cpp b/rootinfo_filter.cpp
index 1a0cb35..bef9202 100644
--- a/rootinfo_filter.cpp
+++ b/rootinfo_filter.cpp
@@ -20,6 +20,8 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 #include "rootinfo_filter.h"
 #include "netinfo.h"
 #include "virtualdesktops.h"
+#include "xcbutils.h"
+#include "atoms.h"
 
 namespace KWin
 {
@@ -30,15 +32,80 @@ RootInfoFilter::RootInfoFilter(RootInfo *parent)
 {
 }
 
+static QVector<xcb_atom_t> getNetWMAtoms(xcb_atom_t property)
+{
+    QVector<xcb_atom_t> net_wm_atoms;
+
+    xcb_window_t root = rootWindow();
+    int offset = 0;
+    int remaining = 0;
+    xcb_connection_t *xcb_connection = connection();
+
+    do {
+        xcb_get_property_cookie_t cookie = xcb_get_property(xcb_connection, false, root,
+       +                                                            property,
+       +                                                            XCB_ATOM_ATOM, offset, 1024);
+        xcb_get_property_reply_t *reply = xcb_get_property_reply(xcb_connection, cookie, NULL);
+        if (!reply)
+            break;
+
+        remaining = 0;
+
+        if (reply->type == XCB_ATOM_ATOM && reply->format == 32) {
+            int len = xcb_get_property_value_length(reply)/sizeof(xcb_atom_t);
+            xcb_atom_t *atoms = (xcb_atom_t *)xcb_get_property_value(reply);
+            int s = net_wm_atoms.size();
+            net_wm_atoms.resize(s + len);
+            memcpy(net_wm_atoms.data() + s, atoms, len*sizeof(xcb_atom_t));
+
+            remaining = reply->bytes_after;
+            offset += len;
+        }
+
+        free(reply);
+    } while (remaining > 0);
+
+    return net_wm_atoms;
+}
+
 bool RootInfoFilter::event(xcb_generic_event_t *event)
 {
     NET::Properties dirtyProtocols;
     NET::Properties2 dirtyProtocols2;
+
     m_rootInfo->event(event, &dirtyProtocols, &dirtyProtocols2);
     if (dirtyProtocols & NET::DesktopNames)
         VirtualDesktopManager::self()->save();
     if (dirtyProtocols2 & NET::WM2DesktopLayout)
         VirtualDesktopManager::self()->updateLayout();
+
+    // TODO(zccrs): 应该在 kwindowsystem 项目的 NETRootInfo 中添加
+    if ((event->response_type & ~0x80) == XCB_PROPERTY_NOTIFY) {
+        xcb_property_notify_event_t *pe = reinterpret_cast<xcb_property_notify_event_t*>(event);
+        Xcb::Atom net_support(QByteArrayLiteral("_NET_SUPPORTED"));
+        xcb_atom_t gtk_frame_extents = atoms->gtk_frame_extents;
+
+        if (pe->atom == net_support) {
+            auto old_atoms = getNetWMAtoms(net_support);
+            QVector<xcb_atom_t> new_atoms;
+
+            if (!old_atoms.contains(gtk_frame_extents)) {
+                // Append _GTK_FRAME_EXTENTS atom to _NET_SUPPORTED
+                new_atoms << gtk_frame_extents;
+            }
+
+            if (!old_atoms.contains(atoms->gtk_show_window_menu)) {
+                // Support _GTK_SHOW_WINDOW_MENU
+                new_atoms << atoms->gtk_show_window_menu;
+            }
+
+            if (!new_atoms.isEmpty()) {
+                xcb_change_property(connection(), XCB_PROP_MODE_APPEND, rootWindow(),
+                                    net_support, XCB_ATOM_ATOM, 32, new_atoms.length(), new_atoms.constData());
+            }
+        }
+    }
+
     return false;
 }
 
-- 
2.20.1

