From 2b3ad824c8031e58134b3f79b91e4c9db8c6b791 Mon Sep 17 00:00:00 2001
From: agsantos <aline.gondimsantos@savoirfairelinux.com>
Date: Mon, 29 Nov 2021 14:57:33 -0500
Subject: [PATCH] x11: get windows name and ids

GitLab: https://git.jami.net/savoirfairelinux/jami-project/-/issues/1294

Change-Id: Icf117d50b9ad3e6cf0d329252be371c3a856e96b
---
 src/api/avmodel.h      | 10 ++++-
 src/api/newcallmodel.h |  7 +++
 src/avmodel.cpp        | 99 ++++++++++++++++++++++++++++++++++++++++++
 src/newcallmodel.cpp   | 10 +++++
 4 files changed, 124 insertions(+), 2 deletions(-)

diff --git a/src/api/avmodel.h b/src/api/avmodel.h
index 51945f41..321066ce 100644
--- a/src/api/avmodel.h
+++ b/src/api/avmodel.h
@@ -253,18 +253,24 @@ public:
      * @throw std::out_of_range if not found
      */
     const video::Renderer& getRenderer(const QString& id) const;
+    /**
+     * Get the list of available windows ids
+     * X11: a id is of the form 0x0000000
+     * @return map with windows names and ids
+     */
+    const QVariantMap getListWindows() const;
     /**
      * set to true to receive AVFrames from render
      */
     void useAVFrame(bool useAVFrame);
     /**
      * set current using device
-     * @ param device name
+     * @param device name
      */
     Q_INVOKABLE void setCurrentVideoCaptureDevice(const QString& currentVideoCaptureDevice);
     /**
      * set current using device
-     * @ return current using device name
+     * @return current using device name
      */
     Q_INVOKABLE QString getCurrentVideoCaptureDevice() const;
     /**
diff --git a/src/api/newcallmodel.h b/src/api/newcallmodel.h
index 2a70620c..456705b4 100644
--- a/src/api/newcallmodel.h
+++ b/src/api/newcallmodel.h
@@ -345,6 +345,7 @@ public:
     video::RenderedDevice getCurrentRenderedDevice(const QString& call_id) const;
 
     /**
+     * @deprecated
      * Render a file to the call id specified
      * @param uri the path of the file
      * @param callId
@@ -369,6 +370,12 @@ public:
      */
     QString getDisplay(int idx, int x, int y, int w, int h);
     /**
+     * Get the current window resource string
+     * @param windowId
+     */
+    QString getDisplay(const QString& windowId);
+    /**
+     * @deprecated
      * Render the current display to the call id specified
      * @param idx of the display
      * @param x top left of the area
diff --git a/src/avmodel.cpp b/src/avmodel.cpp
index 653bc370..f9864e8c 100644
--- a/src/avmodel.cpp
+++ b/src/avmodel.cpp
@@ -46,6 +46,10 @@
 #include "dbus/videomanager.h"
 #include "authority/storagehelper.h"
 
+#if defined(Q_OS_UNIX) && !defined(__APPLE__)
+#include <xcb/xcb.h>
+#endif
+
 namespace lrc {
 
 using namespace api;
@@ -540,6 +544,101 @@ AVModel::getRenderer(const QString& id) const
     return *pimpl_->renderers_[id];
 }
 
+#if defined(Q_OS_UNIX) && !defined(__APPLE__)
+static xcb_atom_t
+getAtom(xcb_connection_t* c, const std::string& atomName)
+{
+    xcb_intern_atom_cookie_t atom_cookie = xcb_intern_atom(c, 0, atomName.size(), atomName.c_str());
+    if (auto* rep = xcb_intern_atom_reply(c, atom_cookie, nullptr)) {
+        xcb_atom_t atom = rep->atom;
+        free(rep);
+        return atom;
+    }
+    return {};
+}
+#endif
+
+const QVariantMap
+AVModel::getListWindows() const
+{
+    QMap<QString, QVariant> ret {};
+
+#if defined(Q_OS_UNIX) && !defined(__APPLE__)
+    std::unique_ptr<xcb_connection_t, void (*)(xcb_connection_t*)> c(xcb_connect(nullptr, nullptr),
+                                                                     [](xcb_connection_t* ptr) {
+                                                                         xcb_disconnect(ptr);
+                                                                     });
+
+    if (xcb_connection_has_error(c.get())) {
+        qDebug() << "xcb connection has error";
+        return ret;
+    }
+
+    auto atomNetClient = getAtom(c.get(), "_NET_CLIENT_LIST");
+    auto atomWMVisibleName = getAtom(c.get(), "_NET_WM_NAME");
+    if (!atomNetClient || !atomWMVisibleName)
+        return ret;
+
+    auto* screen = xcb_setup_roots_iterator(xcb_get_setup(c.get())).data;
+
+    xcb_get_property_cookie_t propCookieList = xcb_get_property(c.get(),
+                                                                0,
+                                                                screen->root,
+                                                                atomNetClient,
+                                                                XCB_GET_PROPERTY_TYPE_ANY,
+                                                                0,
+                                                                100);
+
+    using propertyPtr
+        = std::unique_ptr<xcb_get_property_reply_t, void (*)(xcb_get_property_reply_t*)>;
+
+    xcb_generic_error_t* e;
+    propertyPtr replyPropList(xcb_get_property_reply(c.get(), propCookieList, &e),
+                              [](auto* ptr) { free(ptr); });
+    if (e) {
+        qDebug() << "Error: " << e->error_code;
+        free(e);
+    }
+    if (replyPropList.get()) {
+        int valueLegth = xcb_get_property_value_length(replyPropList.get());
+        if (valueLegth) {
+            auto* win = static_cast<xcb_window_t*>(xcb_get_property_value(replyPropList.get()));
+            for (int i = 0; i < valueLegth / 4; i++) {
+                xcb_get_property_cookie_t prop_cookie = xcb_get_property(c.get(),
+                                                                         0,
+                                                                         win[i],
+                                                                         atomWMVisibleName,
+                                                                         XCB_GET_PROPERTY_TYPE_ANY,
+                                                                         0,
+                                                                         1000);
+                propertyPtr replyProp {xcb_get_property_reply(c.get(), prop_cookie, &e),
+                                       [](auto* ptr) {
+                                           free(ptr);
+                                       }};
+                if (e) {
+                    qDebug() << "Error: " << e->error_code;
+                    free(e);
+                }
+                if (replyProp.get()) {
+                    int valueLegth2 = xcb_get_property_value_length(replyProp.get());
+                    if (valueLegth2) {
+                        auto name = QString::fromUtf8(
+                            reinterpret_cast<char*>(xcb_get_property_value(replyProp.get())));
+                        name.truncate(valueLegth2);
+                        if (ret.find(name) != ret.end())
+                            name += QString(" - 0x%1").arg(win[i], 0, 16);
+                        ret.insert(name, QVariant(QString("0x%1").arg(win[i], 0, 16)));
+                    }
+                }
+            }
+        }
+    }
+    return ret;
+#else
+    return ret;
+#endif
+}
+
 void
 AVModel::setCurrentVideoCaptureDevice(const QString& currentVideoCaptureDevice)
 {
diff --git a/src/newcallmodel.cpp b/src/newcallmodel.cpp
index 8b36a672..6bcd9d5f 100644
--- a/src/newcallmodel.cpp
+++ b/src/newcallmodel.cpp
@@ -838,6 +838,16 @@ NewCallModel::getDisplay(int idx, int x, int y, int w, int h)
         .arg(h);
 }
 
+QString
+NewCallModel::getDisplay(const QString& windowId)
+{
+    QString sep = DRing::Media::VideoProtocolPrefix::SEPARATOR;
+    return QString("%1%2:+0,0 window-id:%3")
+        .arg(DRing::Media::VideoProtocolPrefix::DISPLAY)
+        .arg(sep)
+        .arg(windowId);
+}
+
 void
 NewCallModel::setDisplay(int idx, int x, int y, int w, int h, const QString& callId)
 {
-- 
GitLab