From 2261b6f7bad6efaca196b430857e199441fdf14e Mon Sep 17 00:00:00 2001
From: Alexander Tuxen <tuxen@silpion.de>
Date: Thu, 22 Sep 2022 22:19:16 +0200
Subject: [PATCH] first working PoC of PID based window switching approach.
 getScreen function is implemented, window management functions need to be
 adjusted.

---
 .../compositor-messages.cpp                   |  22 +++-
 multiscreen-compositor/compositor-messages.h  |   6 +-
 multiscreen-compositor/main.cpp               |  15 +++
 multiscreen-compositor/message-server.cpp     |  11 +-
 multiscreen-compositor/message-server.h       |   2 +
 .../message-serverconnection.cpp              |   4 +-
 .../message-serverconnection.h                |   2 +-
 multiscreen-compositor/qml/main.qml           | 100 ++++++++++++++----
 8 files changed, 138 insertions(+), 24 deletions(-)

diff --git a/multiscreen-compositor/compositor-messages.cpp b/multiscreen-compositor/compositor-messages.cpp
index e3bf734..07a9adc 100644
--- a/multiscreen-compositor/compositor-messages.cpp
+++ b/multiscreen-compositor/compositor-messages.cpp
@@ -1,4 +1,5 @@
 #include "compositor-messages.h"
+#include <message-serverconnection.h>
 #include <QDebug>
 
 CompositorMessages::CompositorMessages(QObject *parent)
@@ -7,17 +8,36 @@ CompositorMessages::CompositorMessages(QObject *parent)
 }
 
 void CompositorMessages::messageReceived(QString message, quint32 windowId, quint32 screenId){
-    qDebug() << "New message" << message;
+    qDebug() << "CompositorMessage::messageReceived New message" << message;
 
     if(message == "moveWindowToScreen"){
         emit moveWindowToScreen(windowId, screenId);
     }else
     if(message == "hideWindow"){
        emit hideWindow(windowId);
+       emit sendStatus(0);
     }else
     if(message == "showWindow"){
        emit showWindow(windowId);
+       emit sendStatus(0);
+    }else
+    if(message == "logWindows"){
+       emit logWindows();
+       emit sendStatus(0);
+    }else
+    if(message == "getScreen"){
+       emit getScreen(windowId);
+    }else
+    if(message == "setActiveScreen"){
+       emit setActiveScreen(screenId);
+       emit sendStatus(screenId);
     }else{
         qWarning() << "Unknown message received: " << message;
     }
 }
+
+void CompositorMessages::screenInfoReceived(int screenId){
+    qDebug() << "CompositorMessage::screenInfoReceived " << screenId;
+
+   emit sendStatus(screenId);
+}
diff --git a/multiscreen-compositor/compositor-messages.h b/multiscreen-compositor/compositor-messages.h
index f09a39a..288eb78 100644
--- a/multiscreen-compositor/compositor-messages.h
+++ b/multiscreen-compositor/compositor-messages.h
@@ -11,11 +11,15 @@ public:
 
 public slots:
     void messageReceived(QString, quint32 value0, quint32 value2);
+    void screenInfoReceived(int screenId);
 signals:
     void moveWindowToScreen(qint32 windowid, qint32 screenid);
     void hideWindow(qint32);
     void showWindow(qint32);
-
+    void logWindows(void);
+    void getScreen(qint32);
+    void sendStatus(int);
+    void setActiveScreen(quint32);
 };
 
 #endif // COMPOSITORCOMMANDS_H
diff --git a/multiscreen-compositor/main.cpp b/multiscreen-compositor/main.cpp
index 17738a2..26fd0cb 100644
--- a/multiscreen-compositor/main.cpp
+++ b/multiscreen-compositor/main.cpp
@@ -64,6 +64,7 @@
 
 int main(int argc, char *argv[])
 {
+    QCoreApplication::setAttribute(Qt::AA_ShareOpenGLContexts, true);
     QGuiApplication app(argc, argv);
 
     QCommandLineParser parser;
@@ -90,10 +91,24 @@ int main(int argc, char *argv[])
     QObject::connect(&server, SIGNAL(newMessage(QString,quint32,quint32)),
                      &commands, SLOT(messageReceived(QString,quint32,quint32)));
 
+    QObject::connect(&commands, SIGNAL(sendStatus(int)),
+                     &server, SLOT(returnStatus(int)));
+
+
     QQmlApplicationEngine appEngine;
     appEngine.rootContext()->setContextProperty("config", config);
     appEngine.rootContext()->setContextProperty("commands", &commands);
     appEngine.load(QUrl("qrc:///qml/main.qml"));
 
+    /*
+    QQmlEngine engine;
+    QQmlComponent component(&engine, QUrl("qrc:///qml/main.qml"), nullptr);
+    QObject *object = component.create();
+
+    QObject *object = appEngine.rootObjects().constFirst()->findChild<QObject *>("screenSignal");
+    QObject::connect(object, SIGNAL(sendStatus(int)),
+                     &commands, SLOT(screenInfoReceived(int)));
+    */
+
     return app.exec();
 }
diff --git a/multiscreen-compositor/message-server.cpp b/multiscreen-compositor/message-server.cpp
index 9b4a85a..532be30 100644
--- a/multiscreen-compositor/message-server.cpp
+++ b/multiscreen-compositor/message-server.cpp
@@ -18,6 +18,7 @@ bool MessageServer::listen() {
     server = new QLocalServer(this);
     while(true)
     {
+        // FIXME: This isn't working correctly. When the compositor is stopped, the socket isn't deleted and this logic can't handle that.
         if (server->listen(socketName)) {
             break;
         }
@@ -51,6 +52,8 @@ void MessageServer::handleConnection()
     MessageServerConnection *c = new MessageServerConnection(server->nextPendingConnection(), this);
     connect(c, &MessageServerConnection::disconnected, this, &MessageServer::connectionGone);
     connect(c, &MessageServerConnection::newMessage, this, &MessageServer::messageReceived);
+    //connect(c, &MessageServerConnection::newMessage, this, &MessageServer::messageReceived);
+    connect(this, &MessageServer::sendStatus, c, &MessageServerConnection::sendStatus);
     connections.append(c);
 }
 
@@ -62,6 +65,12 @@ void MessageServer::connectionGone(MessageServerConnection* c){
 
 void MessageServer::messageReceived(QString m, quint32 value0, quint32 value1)
 {
-    qDebug() << "Got message" << m << value0 << value1;
+    qDebug() << "MessageServer::messageReceived Got message" << m << value0 << value1;
     emit newMessage(m , value0, value1);
 }
+
+void MessageServer::returnStatus(int status)
+{
+    qDebug() <<"returning Status " << status;
+    emit sendStatus(status);
+}
diff --git a/multiscreen-compositor/message-server.h b/multiscreen-compositor/message-server.h
index f85d0db..46e97e0 100644
--- a/multiscreen-compositor/message-server.h
+++ b/multiscreen-compositor/message-server.h
@@ -17,11 +17,13 @@ public slots:
 
 signals:
     void newMessage(QString, quint32 value0, quint32 value2);
+    void sendStatus(int);
 
 private slots:
     void handleConnection(void);
     void connectionGone(MessageServerConnection *c);
     void messageReceived(QString, quint32, quint32);
+    void returnStatus(int);
 
 private:
     static const QString socketName;
diff --git a/multiscreen-compositor/message-serverconnection.cpp b/multiscreen-compositor/message-serverconnection.cpp
index 7b10b29..d853fad 100644
--- a/multiscreen-compositor/message-serverconnection.cpp
+++ b/multiscreen-compositor/message-serverconnection.cpp
@@ -20,7 +20,7 @@ void MessageServerConnection::readData(void){
     QString message;    
     quint32 value0;
     quint32 value1;
-    qDebug() << "Client send data: " << socket->bytesAvailable();
+    qDebug() << "Client sent data: " << socket->bytesAvailable();
 
     while(socket->bytesAvailable() > 0){
         // Use transaction to make sure to receive a complete message
@@ -39,7 +39,7 @@ void MessageServerConnection::readData(void){
         emit newMessage(message , value0, value1);
 
         // Reply status to client
-        sendStatus(0);
+        //sendStatus(0);
 
         if(socket->bytesAvailable() > 0){
             qDebug() << "Still bytes available: " << socket->bytesAvailable();
diff --git a/multiscreen-compositor/message-serverconnection.h b/multiscreen-compositor/message-serverconnection.h
index 15981ab..909303c 100644
--- a/multiscreen-compositor/message-serverconnection.h
+++ b/multiscreen-compositor/message-serverconnection.h
@@ -10,6 +10,7 @@ class MessageServerConnection : public QObject
     Q_OBJECT
 public:
     explicit MessageServerConnection( QLocalSocket *socket, QObject *parent = nullptr);
+    void sendStatus(int status);
 
 signals:
     void disconnected(MessageServerConnection*);
@@ -23,7 +24,6 @@ private:
     QLocalSocket *socket;
     QDataStream socketReadData;
 
-    void sendStatus(int status);    
 };
 
 #endif // SERVERCONNECTION_H
diff --git a/multiscreen-compositor/qml/main.qml b/multiscreen-compositor/qml/main.qml
index b3cd503..822ad06 100644
--- a/multiscreen-compositor/qml/main.qml
+++ b/multiscreen-compositor/qml/main.qml
@@ -55,10 +55,12 @@ import QtQuick.Window 2.3 as Window
 import QtWayland.Compositor 1.3
 import QtQml.Models 2.1
 
+//import WindowAssignment 1.0
+
 WaylandCompositor {
     id: comp
 
-    property int current_screen: 0
+    property int active_screen: 0
     property var activeSurfaces: []
 
     Instantiator {
@@ -121,24 +123,26 @@ WaylandCompositor {
     }
 
     onSurfaceRequested: {
-       console.log("Surface requested", client, id, version);
+       console.log("Surface requested: client", client, "id", id, "version", version, "pid", client.processId);
     }
 
     function createShellSurfaceItem(shellSurface, output) {
+        console.log("createShellSurfaceItem: Creating surface", shellSurface, "on output", output);
         var item = chromeComponent.createObject(output.surfaceArea, {
             "shellSurface": shellSurface,
         });
     }
 
     function handleShellSurfaceCreated(shellSurface) {
-        if (current_screen+1 > screens.count-1)
-                current_screen = 0;
-        else
-                current_screen++;
-        createShellSurfaceItem(shellSurface, screens.objectAt(current_screen));
+        console.log("handleShellSurfaceCreated: Adding surface to applist", shellSurface, "on screen", active_screen,
+            "pid", shellSurface.surface.client.processId);
+        createShellSurfaceItem(shellSurface, screens.objectAt(active_screen));
+        var surfaceOnScreen = ({ 'surface': shellSurface, 'screen': active_screen });
+        activeSurfaces.push(surfaceOnScreen);
     }
 
     function createIviSurfaceItem(iviSurface, output) {
+        console.log("createIviSurfaceItem: surface", iviSurface, "output", output);
         var item = iviChromeComponent.createObject(output.surfaceArea, {
             "shellSurface": iviSurface,
         });
@@ -153,6 +157,7 @@ WaylandCompositor {
         // We select the screen by evaluating the iviId of the app,
         // if more than one screen is present.
         // If the unique ivi id is greater or equal 2000 we select screen 2
+        console.log("handleIviSurfaceCreated: surface", iviSurface, "id", iviSurface.iviId);
         var item;
         if (screens.count > 1) {
             if (iviSurface.iviId > 1999)
@@ -164,8 +169,8 @@ WaylandCompositor {
         }
 
         console.log("Add to applist: ", item, "id", iviSurface.iviId);
-        activeSurfaces.push(item);
-        //console.log("AppId here: ", shellSurface.toplevel.appId)
+        var surfaceOnScreen = ({ 'surface': item, 'screen': active_screen });
+        activeSurfaces.push(surfaceOnScreen);
     }
 
     // =============================================
@@ -176,13 +181,37 @@ WaylandCompositor {
         console.log("appList length: ", activeSurfaces.length);
         for (var i=0; i < activeSurfaces.length; i++) {
             var item = activeSurfaces[i];
-            var id = item.shellSurface.iviId;
-            console.log("Surface", i, id, item.shellSurface, item.shellSurface.surface)
-            if( item.shellSurface.surface === surface){
-                console.log("Remove surface from list")
-                activeSurfaces = Array.from(activeSurfaces).filter(e => e !== item)
-            }
+            if (item.surface){
+                var id;
+
+                if (item.surface.shellSurface){
+                    id = item.surface.shellSurface.iviId;
+                    console.log("Testing IVI surface", i, id, item.surface)
+                    if( item.surface.shellSurface.surface === surface){
+                        console.log("Removing IVI surface from list", surface)
+                        activeSurfaces = Array.from(activeSurfaces).filter(e => e !== item)
+                        console.log("Removed surface from Applist:", surface);
+                    }
+                }
+                else if ((item.surface.surface) && (item.surface.surface.client.processId)){
+                    id = item.surface.surface.client.processId;
+                    console.log("Testing XDG surface", i, item.surface)
+                    if( item.surface.surface === surface){
+                        console.log("Removing XDG surface from list", surface)
+                        activeSurfaces = Array.from(activeSurfaces).filter(e => e !== item)
+                    }
+                }
+                else
+                {
+                    console.log("ERROR! applist index", i, "object has vanished!");
+                    console.log("Removing lost surface from applist!")
+                    activeSurfaces = Array.from(activeSurfaces).filter((e, idx) => idx !== i)
+                }
+
 
+            }else{
+                console.log("surface not defined!", item.surface);
+            }
         }
     }
 
@@ -193,9 +222,13 @@ WaylandCompositor {
         console.log("Lookung for surface with id", id);
         for (var i=0; i < activeSurfaces.length; i++) {
             var item = activeSurfaces[i];
-            if( id === item.shellSurface.iviId){
-                console.log("Surface", i, id, item.shellSurface, item.shellSurface.surface)
-                return item;
+            if( id === item.surface.shellSurface.iviId){
+                console.log("found IVI Shell Surface", i, id, item.surface.shellSurface, item.surface.shellSurface.surface)
+                return item.surface;
+            }
+            else if( id === item.surface.surface.client.processId){
+                console.log("found XDG Shell Surface", i, id, item.surface.shellSurface, item.surface.shellSurface.surface)
+                return item.surface;
             }
         }
         console.log("Failed to find surface by id", id)
@@ -240,5 +273,36 @@ WaylandCompositor {
             console.log("Received a showWindow Signal", windowid)
             windowVisibility(windowid, true)
         }
+        function onLogWindows(){
+            console.log("Active surfaces:")
+            for (var i=0; i < activeSurfaces.length; i++) {
+                var item = activeSurfaces[i];
+                console.log("- Surface", i, ": ", item.surface, "on", item.screen)
+            }
+        }
+        function onGetScreen(windowid){
+            console.log("Received a getScreen Signal for window ID: ", windowid)
+            var screenId = -1;
+            for (var i=0; i < activeSurfaces.length; i++) {
+                var item = activeSurfaces[i];
+                if (item.surface) {
+                    if( windowid === item.surface.surface.client.processId){
+                        console.log("Surface", i, item.surface, "pid", item.surface.surface.client.processId, "on screen", item.screen)
+                        screenId = item.screen;
+                    }
+                }
+
+            }
+            commands.screenInfoReceived(screenId)
+        }
+
+        function onSetActiveScreen(screenId)
+        {
+            if (screenId <= screens.count)
+            {
+                console.log("setting active screen", screenId);
+                active_screen = screenId;
+            }
+        }
     }
 }
-- 
GitLab