From e1d85b2db06b839e69c6203144aa8718eb99a348 Mon Sep 17 00:00:00 2001
From: Aline Gondim Santos <aline.gondimsantos@savoirfairelinux.com>
Date: Tue, 10 Jan 2023 15:52:36 -0300
Subject: [PATCH] window sharing: track window name change

If a window changes its name, we should share the parent process windows.
This can happen with chrome, visual studio code and file explorer for example.

+ Fixes non opening SelectScreen window when Jami language changes.

Change-Id: Ib16992dde08e6a9fa191c9fb1cbc4efd304efe6d
---
 daemon                                        |  2 +-
 src/app/avadapter.cpp                         |  9 ++--
 src/app/avadapter.h                           |  4 +-
 src/app/mainview/components/CallActionBar.qml | 28 +++++------
 src/app/mainview/components/SelectScreen.qml  |  8 +--
 src/libclient/api/callmodel.h                 |  3 +-
 src/libclient/avmodel.cpp                     |  4 +-
 src/libclient/callmodel.cpp                   | 50 +++++++++++++++++--
 src/libclient/qtwrapper/conversions_wrap.hpp  |  2 +-
 src/libclient/qtwrapper/videomanager_wrap.h   |  2 +-
 10 files changed, 79 insertions(+), 33 deletions(-)

diff --git a/daemon b/daemon
index eb9c52cee..3382b2436 160000
--- a/daemon
+++ b/daemon
@@ -1 +1 @@
-Subproject commit eb9c52cee45660f68334adb32a23d6f743d6bcf4
+Subproject commit 3382b2436017d7044e343dcc1e9190595f2bcc8b
diff --git a/src/app/avadapter.cpp b/src/app/avadapter.cpp
index 80a02a172..23df74a82 100644
--- a/src/app/avadapter.cpp
+++ b/src/app/avadapter.cpp
@@ -233,9 +233,9 @@ AvAdapter::shareScreenArea(unsigned x, unsigned y, unsigned width, unsigned heig
 }
 
 void
-AvAdapter::shareWindow(const QString& windowId)
+AvAdapter::shareWindow(const QString& windowProcessId, const QString& windowId)
 {
-    auto resource = lrcInstance_->getCurrentCallModel()->getDisplay(windowId);
+    auto resource = lrcInstance_->getCurrentCallModel()->getDisplay(windowProcessId, windowId);
     auto callId = lrcInstance_->getCurrentCallId();
 
     muteCamera_ = !isCapturing();
@@ -243,9 +243,8 @@ AvAdapter::shareWindow(const QString& windowId)
         ->addMedia(callId, resource, lrc::api::CallModel::MediaRequestType::SCREENSHARING);
 }
 
-#pragma optimize("", off)
 QString
-AvAdapter::getSharingResource(int screenId = -2, const QString& windowId = "")
+AvAdapter::getSharingResource(int screenId, const QString& windowProcessId, const QString& windowId)
 {
     if (screenId == -1) {
         const auto arrangementRect = getAllScreensBoundingRect();
@@ -276,7 +275,7 @@ AvAdapter::getSharingResource(int screenId = -2, const QString& windowId = "")
                                                                rect.height()
                                                                    * screen->devicePixelRatio());
     } else if (!windowId.isEmpty()) {
-        return lrcInstance_->getCurrentCallModel()->getDisplay(windowId);
+        return lrcInstance_->getCurrentCallModel()->getDisplay(windowProcessId, windowId);
     }
 
     return "";
diff --git a/src/app/avadapter.h b/src/app/avadapter.h
index abd011296..6472a3d18 100644
--- a/src/app/avadapter.h
+++ b/src/app/avadapter.h
@@ -86,10 +86,10 @@ protected:
     Q_INVOKABLE void shareScreenArea(unsigned x, unsigned y, unsigned width, unsigned height);
 
     // Select window to display.
-    Q_INVOKABLE void shareWindow(const QString& windowId);
+    Q_INVOKABLE void shareWindow(const QString& windowProcessId, const QString& windowId);
 
     // Returns the screensharing resource
-    Q_INVOKABLE QString getSharingResource(int screenId, const QString& key);
+    Q_INVOKABLE QString getSharingResource(int screenId = -2, const QString& windowProcessId = "", const QString& key = "");
 
     Q_INVOKABLE void getListWindows();
 
diff --git a/src/app/mainview/components/CallActionBar.qml b/src/app/mainview/components/CallActionBar.qml
index 3f9ed4d59..1ffec3e1c 100644
--- a/src/app/mainview/components/CallActionBar.qml
+++ b/src/app/mainview/components/CallActionBar.qml
@@ -117,21 +117,21 @@ Control {
             property int popupMode: CallActionBar.ActionPopupMode.ListElement
             property var listModel: ListModel {
                 id: shareModel
-
-                Component.onCompleted: {
-                    shareModel.append({"Name": JamiStrings.shareScreen,
-                                       "IconSource": JamiResources.laptop_black_24dp_svg})
-                    if (Qt.platform.os.toString() !== "osx") {
-                        shareModel.append({"Name": JamiStrings.shareWindow,
-                                            "IconSource" : JamiResources.window_black_24dp_svg})
-                    }
-                    if (Qt.platform.os.toString() !== "windows") {  // temporarily disable for windows
-                        shareModel.append({"Name": JamiStrings.shareScreenArea,
-                                        "IconSource" : JamiResources.share_area_black_24dp_svg})
-                    }
-                    shareModel.append({"Name": JamiStrings.shareFile,
-                                       "IconSource" : JamiResources.file_black_24dp_svg})
+            }
+            onTriggered: {
+                shareModel.clear()
+                shareModel.append({"Name": JamiStrings.shareScreen,
+                                    "IconSource": JamiResources.laptop_black_24dp_svg})
+                if (Qt.platform.os.toString() !== "osx") {
+                    shareModel.append({"Name": JamiStrings.shareWindow,
+                                        "IconSource" : JamiResources.window_black_24dp_svg})
+                }
+                if (Qt.platform.os.toString() !== "windows") {  // temporarily disable for windows
+                    shareModel.append({"Name": JamiStrings.shareScreenArea,
+                                    "IconSource" : JamiResources.share_area_black_24dp_svg})
                 }
+                shareModel.append({"Name": JamiStrings.shareFile,
+                                    "IconSource" : JamiResources.file_black_24dp_svg})
             }
             function accept(index) {
                 switch(shareModel.get(index).Name) {
diff --git a/src/app/mainview/components/SelectScreen.qml b/src/app/mainview/components/SelectScreen.qml
index 6488e3276..dcd35feaa 100644
--- a/src/app/mainview/components/SelectScreen.qml
+++ b/src/app/mainview/components/SelectScreen.qml
@@ -167,7 +167,7 @@ Window {
                             }
                             Component.onCompleted: {
                                 if (visible) {
-                                    const rId = AvAdapter.getSharingResource(index, "")
+                                    const rId = AvAdapter.getSharingResource(index)
                                     if (rId !== "") {
                                         rendererId = VideoDevices.startDevice(rId)
                                     }
@@ -237,7 +237,7 @@ Window {
                         }
                         Component.onCompleted: {
                             if (visible) {
-                                const rId = AvAdapter.getSharingResource(-1, "")
+                                const rId = AvAdapter.getSharingResource(-1)
                                 if (rId !== "") {
                                     rendererId = VideoDevices.startDevice(rId)
                                 }
@@ -315,7 +315,7 @@ Window {
                             }
                             Component.onCompleted: {
                                 if (visible) {
-                                    const rId = AvAdapter.getSharingResource(-2, AvAdapter.windowsIds[index - Qt.application.screens.length])
+                                    const rId = AvAdapter.getSharingResource(-2, AvAdapter.windowsIds[index - Qt.application.screens.length], AvAdapter.windowsNames[index - Qt.application.screens.length])
                                     if (rId !== "") {
                                         rendererId = VideoDevices.startDevice(rId)
                                     }
@@ -376,7 +376,7 @@ Window {
                     if (selectedScreenNumber < Qt.application.screens.length)
                         AvAdapter.shareEntireScreen(selectedScreenNumber)
                     else {
-                        AvAdapter.shareWindow(AvAdapter.windowsIds[selectedScreenNumber - Qt.application.screens.length])
+                        AvAdapter.shareWindow(AvAdapter.windowsIds[selectedScreenNumber - Qt.application.screens.length], AvAdapter.windowsNames[selectedScreenNumber - Qt.application.screens.length])
                     }
                 }
                 root.close()
diff --git a/src/libclient/api/callmodel.h b/src/libclient/api/callmodel.h
index 282c300e8..f6fa50ad5 100644
--- a/src/libclient/api/callmodel.h
+++ b/src/libclient/api/callmodel.h
@@ -386,9 +386,10 @@ public:
     QString getDisplay(int idx, int x, int y, int w, int h);
     /**
      * Get the current window resource string
+     * @param windowProcessId
      * @param windowId
      */
-    QString getDisplay(const QString& windowId);
+    QString getDisplay(const QString& windowProcessId, const QString& windowId);
 
     void emplaceConversationConference(const QString& callId);
 
diff --git a/src/libclient/avmodel.cpp b/src/libclient/avmodel.cpp
index a33dec1b2..3ddd4ec00 100644
--- a/src/libclient/avmodel.cpp
+++ b/src/libclient/avmodel.cpp
@@ -618,7 +618,9 @@ CbEnumAltTab(HWND hwnd, LPARAM lParam)
     if (keys.indexOf(name) > 0) {
         return FALSE;
     } else {
-        windowList->insert(name, name);
+        DWORD processId;
+        GetWindowThreadProcessId(hwnd, &processId);
+        windowList->insert(name, QVariant::fromValue(processId));
     }
 
     return TRUE;
diff --git a/src/libclient/callmodel.cpp b/src/libclient/callmodel.cpp
index 0c425dfe8..9ef31038f 100644
--- a/src/libclient/callmodel.cpp
+++ b/src/libclient/callmodel.cpp
@@ -55,6 +55,11 @@
 #include <random>
 #include <map>
 
+#ifdef WIN32
+#define NOMINMAX
+#include "Windows.h"
+#endif
+
 using namespace libjami::Media;
 
 constexpr static const char HARDWARE_ACCELERATION[] = "HARDWARE_ACCELERATION";
@@ -942,8 +947,35 @@ CallModel::getDisplay(int idx, int x, int y, int w, int h)
         .arg(h);
 }
 
+
+
+#ifdef WIN32
+BOOL CALLBACK
+EnumWindowsProcMy(HWND hwnd, LPARAM lParam)
+{
+    std::pair<DWORD, QString>* dataPair = reinterpret_cast<std::pair<DWORD, QString>*>(
+        lParam);
+    DWORD lpdwProcessId;
+    if (auto parent = GetWindow(hwnd, GW_OWNER))
+        GetWindowThreadProcessId(parent, &lpdwProcessId);
+    else
+        GetWindowThreadProcessId(hwnd, &lpdwProcessId);
+    int len = GetWindowTextLength(hwnd) + 1;
+    std::vector<wchar_t> buf(len);
+    GetWindowText(hwnd, &buf[0], len);
+
+    if (lpdwProcessId == dataPair->first) {
+        if (!IsWindowVisible(hwnd))
+            return TRUE;
+        dataPair->second = QString::fromStdWString(&buf[0]);
+        return FALSE;
+    }
+    return TRUE;
+}
+#endif
+
 QString
-CallModel::getDisplay(const QString& windowId)
+CallModel::getDisplay(const QString& windowProcessId, const QString& windowId)
 {
     QString sep = libjami::Media::VideoProtocolPrefix::SEPARATOR;
     QString ret{};
@@ -951,13 +983,25 @@ CallModel::getDisplay(const QString& windowId)
     ret = QString("%1%2:+0,0 window-id:%3")
         .arg(libjami::Media::VideoProtocolPrefix::DISPLAY)
         .arg(sep)
-        .arg(windowId);
+        .arg(windowProcessId);
 #endif
 #ifdef WIN32
+    // If window changed the name we must look for the parent process window
+    QString newWindowId = windowId;
+    auto hwnd = FindWindow(NULL, windowId.toStdWString().c_str());
+    if (!hwnd) {
+        std::pair<DWORD, QString> idName(windowProcessId.toInt(), {});
+        LPARAM lParam = reinterpret_cast<LPARAM>(&idName);
+        EnumWindows(EnumWindowsProcMy, lParam);
+        if (!idName.second.isEmpty()) {
+            newWindowId = idName.second;
+        }
+    }
+
     ret = QString("%1%2:+0,0 window-id:title=%3")
         .arg(libjami::Media::VideoProtocolPrefix::DISPLAY)
         .arg(sep)
-        .arg(windowId);
+        .arg(newWindowId);
 #endif
     return ret;
 }
diff --git a/src/libclient/qtwrapper/conversions_wrap.hpp b/src/libclient/qtwrapper/conversions_wrap.hpp
index 9e9dd1fb3..e814e5427 100644
--- a/src/libclient/qtwrapper/conversions_wrap.hpp
+++ b/src/libclient/qtwrapper/conversions_wrap.hpp
@@ -68,7 +68,7 @@ convertMap(const std::map<std::string, std::string>& m)
 {
     MapStringString temp;
     for (const auto& [key, value] : m) {
-        temp[QString(key.c_str())] = QString(value.c_str());
+        temp[QString(key.c_str())] = QString::fromLatin1(QByteArray::fromStdString(value));
     }
     return temp;
 }
diff --git a/src/libclient/qtwrapper/videomanager_wrap.h b/src/libclient/qtwrapper/videomanager_wrap.h
index 4a2b68f4c..f93a85c21 100644
--- a/src/libclient/qtwrapper/videomanager_wrap.h
+++ b/src/libclient/qtwrapper/videomanager_wrap.h
@@ -126,7 +126,7 @@ public Q_SLOTS: // METHODS
     QString openVideoInput(const QString& resource)
     {
 #ifdef ENABLE_VIDEO
-        return QByteArray::fromStdString(libjami::openVideoInput(resource.toLatin1().toStdString()));
+        return QString::fromLatin1(QByteArray::fromStdString(libjami::openVideoInput(resource.toStdString())));
 #endif
     }
 
-- 
GitLab