Browse Source

added connecting dialog; added error code string; added x11 helper; added rdesktop window detection

tags/v0.6
Robin Thoni 6 years ago
parent
commit
0ce2fb32ec
14 changed files with 514 additions and 17 deletions
  1. 20
    0
      dialogconnecting.cpp
  2. 28
    0
      dialogconnecting.h
  3. 54
    0
      dialogconnecting.ui
  4. 54
    2
      main.cpp
  5. 37
    3
      maindialog.cpp
  6. 11
    0
      maindialog.h
  7. 9
    2
      maindialog.ui
  8. 10
    3
      rd-launcher.pro
  9. 134
    4
      rdesktoplauncher.cpp
  10. 30
    3
      rdesktoplauncher.h
  11. 10
    0
      rdpoptions.cpp
  12. 5
    0
      rdpoptions.h
  13. 99
    0
      x11helper.cpp
  14. 13
    0
      x11helper.h

+ 20
- 0
dialogconnecting.cpp View File

@@ -0,0 +1,20 @@
1
+#include "dialogconnecting.h"
2
+#include "ui_dialogconnecting.h"
3
+
4
+DialogConnecting::DialogConnecting(QWidget *parent) :
5
+    QDialog(parent),
6
+    ui(new Ui::DialogConnecting)
7
+{
8
+    ui->setupUi(this);
9
+}
10
+
11
+DialogConnecting::~DialogConnecting()
12
+{
13
+    delete ui;
14
+}
15
+
16
+void DialogConnecting::on_pushButton_clicked()
17
+{
18
+    ui->pushButton->setEnabled(false);
19
+    emit canceled();
20
+}

+ 28
- 0
dialogconnecting.h View File

@@ -0,0 +1,28 @@
1
+#ifndef DIALOGCONNECTING_H
2
+#define DIALOGCONNECTING_H
3
+
4
+#include <QDialog>
5
+
6
+namespace Ui {
7
+class DialogConnecting;
8
+}
9
+
10
+class DialogConnecting : public QDialog
11
+{
12
+    Q_OBJECT
13
+
14
+public:
15
+    explicit DialogConnecting(QWidget *parent = 0);
16
+    ~DialogConnecting();
17
+
18
+signals:
19
+    void canceled();
20
+
21
+private slots:
22
+    void on_pushButton_clicked();
23
+
24
+private:
25
+    Ui::DialogConnecting *ui;
26
+};
27
+
28
+#endif // DIALOGCONNECTING_H

+ 54
- 0
dialogconnecting.ui View File

@@ -0,0 +1,54 @@
1
+<?xml version="1.0" encoding="UTF-8"?>
2
+<ui version="4.0">
3
+ <class>DialogConnecting</class>
4
+ <widget class="QDialog" name="DialogConnecting">
5
+  <property name="geometry">
6
+   <rect>
7
+    <x>0</x>
8
+    <y>0</y>
9
+    <width>607</width>
10
+    <height>68</height>
11
+   </rect>
12
+  </property>
13
+  <property name="windowTitle">
14
+   <string>Connexion Bureau à distance</string>
15
+  </property>
16
+  <layout class="QGridLayout" name="gridLayout">
17
+   <item row="1" column="0">
18
+    <widget class="QProgressBar" name="progressBar">
19
+     <property name="maximum">
20
+      <number>0</number>
21
+     </property>
22
+     <property name="value">
23
+      <number>0</number>
24
+     </property>
25
+     <property name="textVisible">
26
+      <bool>false</bool>
27
+     </property>
28
+     <property name="invertedAppearance">
29
+      <bool>false</bool>
30
+     </property>
31
+    </widget>
32
+   </item>
33
+   <item row="1" column="1">
34
+    <widget class="QPushButton" name="pushButton">
35
+     <property name="text">
36
+      <string>Annuler</string>
37
+     </property>
38
+    </widget>
39
+   </item>
40
+   <item row="0" column="0" colspan="2">
41
+    <widget class="QLabel" name="label">
42
+     <property name="text">
43
+      <string>Connexion en cours...</string>
44
+     </property>
45
+     <property name="alignment">
46
+      <set>Qt::AlignCenter</set>
47
+     </property>
48
+    </widget>
49
+   </item>
50
+  </layout>
51
+ </widget>
52
+ <resources/>
53
+ <connections/>
54
+</ui>

+ 54
- 2
main.cpp View File

@@ -1,14 +1,66 @@
1 1
 #include "maindialog.h"
2 2
 #include <QApplication>
3 3
 #include <QTextCodec>
4
+#include <QWindow>
5
+#include <QDebug>
6
+#include <QTimer>
7
+
4 8
 
5 9
 int main(int argc, char *argv[])
6 10
 {
7 11
     QApplication a(argc, argv);
8
-    QTextCodec::setCodecForCStrings(QTextCodec::codecForName("UTF-8"));
9
-    QTextCodec::setCodecForTr(QTextCodec::codecForCStrings());
12
+//    QTextCodec::setCodecForCStrings(QTextCodec::codecForName("UTF-8"));
13
+//    QTextCodec::setCodecForTr(QTextCodec::codecForCStrings());
10 14
     MainDialog w;
11 15
     w.show();
12 16
 
17
+//    Display *dpy;
18
+//    Window rootWindow;
19
+//    Window parent;
20
+//    Window *children;
21
+//    Window *child;
22
+//    unsigned long noOfChildren;
23
+
24
+//    dpy = XOpenDisplay(NULL);
25
+//    if (!dpy) {
26
+//        return 7;
27
+//    }
28
+//    rootWindow = RootWindow(dpy, -1);
29
+
30
+
31
+//    children = (Window*)get_client_list(dpy, &noOfChildren);
32
+
33
+////    XQueryTree(dpy, rootWindow, &rootWindow, &parent, &children, &noOfChildren);
34
+//    for (int i = 0; i < noOfChildren; ++i) {
35
+//        char* nameStr;
36
+//        XFetchName(dpy, children[i], &nameStr);
37
+//        QString name(nameStr);
38
+//        XFree(nameStr);
39
+//        if (name.startsWith("rdesktop - ")) {
40
+////        if (name.startsWith("robin@gigi-lt1: /tmp")) {
41
+//            qDebug() << name;
42
+//            QWindow *window = QWindow::fromWinId(children[i]);
43
+//            window->setFlags(Qt::FramelessWindowHint);
44
+
45
+//            QWidget *widget = QWidget::createWindowContainer(window);
46
+////            QEvent e(QEvent::EmbeddingControl);
47
+////            QApplication::sendEvent(widget, &e);
48
+////            widget->show();
49
+////            QTimer* timer = new QTimer(widget);
50
+////            QObject::connect(timer, SIGNAL(timeout()), widget, SLOT(update()));
51
+////            timer->start(100);
52
+//            break;
53
+//        }
54
+//    }
55
+
56
+////    QWindow *window = QWindow::fromWinId(0x0340056f);
57
+////    window->setFlags(Qt::FramelessWindowHint);
58
+
59
+////    QWidget *widget = QWidget::createWindowContainer(window);
60
+
61
+////    QVBoxLayout *layout = new QVBoxLayout(this);
62
+////    layout->addWidget(widget);
63
+////    this->setLayout(layout);
64
+
13 65
     return a.exec();
14 66
 }

+ 37
- 3
maindialog.cpp View File

@@ -1,5 +1,6 @@
1 1
 #include <QApplication>
2 2
 #include <QDesktopWidget>
3
+#include <QUuid>
3 4
 #include <QDebug>
4 5
 #include "maindialog.h"
5 6
 #include "ui_maindialog.h"
@@ -8,7 +9,8 @@
8 9
 
9 10
 MainDialog::MainDialog(QWidget *parent) :
10 11
     QDialog(parent),
11
-    ui(new Ui::MainDialog)
12
+    ui(new Ui::MainDialog),
13
+    m_launcher(0)
12 14
 {
13 15
     ui->setupUi(this);
14 16
     connect(ui->lineComputer->lineEdit(), SIGNAL(returnPressed()), ui->btnConnect, SLOT(animateClick()));
@@ -126,10 +128,25 @@ void MainDialog::loadAll()
126 128
 
127 129
 void MainDialog::on_btnConnect_clicked()
128 130
 {
131
+    if (m_launcher) {
132
+        return;
133
+    }
134
+
135
+    ui->lblExitStatus->clear();
136
+
129 137
     RdpOptions opt = getRdpOptions();
130 138
     RdpOptionsHelper::save(opt);
131
-    RDesktopLauncher launcher;
132
-    launcher.start(opt);
139
+    opt.setWindowTitle("rdesktop " + QUuid::createUuid().toString());
140
+
141
+    m_launcher = new RDesktopLauncher(this);
142
+    connect(m_launcher, SIGNAL(finished(int)), this, SLOT(onProcessFinished(int)));
143
+    connect(m_launcher, SIGNAL(windowFound(WId)), this, SLOT(onWindowFound(WId)));
144
+    m_launcher->start(opt);
145
+
146
+    m_dlgConnecting = new DialogConnecting(this);
147
+    connect(m_dlgConnecting, SIGNAL(canceled()), m_launcher, SLOT(kill()));
148
+    m_dlgConnecting->setModal(true);
149
+    m_dlgConnecting->show();
133 150
 }
134 151
 
135 152
 void MainDialog::textChanged(QString)
@@ -189,3 +206,20 @@ void MainDialog::on_btnRemoveSession_clicked()
189 206
     RdpOptionsHelper::remove(getRdpOptions());
190 207
     loadAll();
191 208
 }
209
+
210
+void MainDialog::onProcessFinished(int status)
211
+{
212
+    ui->lblExitStatus->setText(RDesktopLauncher::getExitStatusString(status));
213
+    m_launcher->deleteLater();
214
+    m_launcher = 0;
215
+    m_dlgConnecting->deleteLater();
216
+    m_dlgConnecting = 0;
217
+    show();
218
+}
219
+
220
+void MainDialog::onWindowFound(WId wid)
221
+{
222
+    m_dlgConnecting->deleteLater();
223
+    m_dlgConnecting = 0;
224
+    hide();
225
+}

+ 11
- 0
maindialog.h View File

@@ -3,11 +3,14 @@
3 3
 
4 4
 #include <QDialog>
5 5
 #include "rdpoptions.h"
6
+#include "dialogconnecting.h"
6 7
 
7 8
 namespace Ui {
8 9
 class MainDialog;
9 10
 }
10 11
 
12
+class RDesktopLauncher;
13
+
11 14
 class MainDialog : public QDialog
12 15
 {
13 16
     Q_OBJECT
@@ -42,12 +45,20 @@ private slots:
42 45
 
43 46
     void on_btnRemoveSession_clicked();
44 47
 
48
+    void onProcessFinished(int status);
49
+
50
+    void onWindowFound(WId wid);
51
+
45 52
 private:
46 53
     Ui::MainDialog *ui;
47 54
 
48 55
     QList<QSize> m_resolutions;
49 56
 
50 57
     QList<RdpOptions> m_rdpOptions;
58
+
59
+    RDesktopLauncher* m_launcher;
60
+
61
+    DialogConnecting* m_dlgConnecting;
51 62
 };
52 63
 
53 64
 #endif // MAINDIALOG_H

+ 9
- 2
maindialog.ui View File

@@ -6,8 +6,8 @@
6 6
    <rect>
7 7
     <x>0</x>
8 8
     <y>0</y>
9
-    <width>467</width>
10
-    <height>557</height>
9
+    <width>480</width>
10
+    <height>560</height>
11 11
    </rect>
12 12
   </property>
13 13
   <property name="windowTitle">
@@ -334,6 +334,13 @@
334 334
          </property>
335 335
         </spacer>
336 336
        </item>
337
+       <item>
338
+        <widget class="QLabel" name="lblExitStatus">
339
+         <property name="text">
340
+          <string/>
341
+         </property>
342
+        </widget>
343
+       </item>
337 344
       </layout>
338 345
      </widget>
339 346
      <widget class="QWidget" name="tabDisplay">

+ 10
- 3
rd-launcher.pro View File

@@ -16,14 +16,21 @@ SOURCES += main.cpp\
16 16
         maindialog.cpp \
17 17
     rdpoptions.cpp \
18 18
     rdesktoplauncher.cpp \
19
-    rdpoptionshelper.cpp
19
+    rdpoptionshelper.cpp \
20
+    dialogconnecting.cpp \
21
+    x11helper.cpp
20 22
 
21 23
 HEADERS  += maindialog.h \
22 24
     rdpoptions.h \
23 25
     rdesktoplauncher.h \
24
-    rdpoptionshelper.h
26
+    rdpoptionshelper.h \
27
+    dialogconnecting.h \
28
+    x11helper.h
25 29
 
26
-FORMS    += maindialog.ui
30
+FORMS    += maindialog.ui \
31
+    dialogconnecting.ui
32
+
33
+LIBS += -lX11
27 34
 
28 35
 RESOURCES += \
29 36
     rc.qrc

+ 134
- 4
rdesktoplauncher.cpp View File

@@ -1,14 +1,21 @@
1
-#include "rdesktoplauncher.h"
2 1
 #include <QStringList>
3
-#include <QProcess>
2
+#include "rdesktoplauncher.h"
3
+#include "x11helper.h"
4
+
4 5
 #include <QDebug>
5 6
 
6
-RDesktopLauncher::RDesktopLauncher()
7
+RDesktopLauncher::RDesktopLauncher(QObject *parent)
8
+    : QObject(parent)
9
+    , m_process(0)
10
+    , m_windowLookUpTimer(0)
7 11
 {
8 12
 }
9 13
 
10 14
 void RDesktopLauncher::start(RdpOptions options)
11 15
 {
16
+    if (m_process) {
17
+        return;
18
+    }
12 19
     QStringList args;
13 20
     args.append("-u");
14 21
     args.append(options.username());
@@ -54,7 +61,130 @@ void RDesktopLauncher::start(RdpOptions options)
54 61
     args.append("-r");
55 62
     args.append("sound:local:alsa");
56 63
 
64
+    if (!options.windowTitle().isEmpty()) {
65
+        args.append("-T");
66
+        args.append(options.windowTitle());
67
+    }
68
+
57 69
     args.append(options.host());
58 70
 
59
-    QProcess::startDetached("rdesktop", args);
71
+    m_options = options;
72
+
73
+    m_process = new QProcess(this);
74
+    connect(m_process, SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(onProcessFinished(int, QProcess::ExitStatus)));
75
+    m_process->start("rdesktop", args);
76
+
77
+    m_windowLookUpTimer = new QTimer(this);
78
+    connect(m_windowLookUpTimer, SIGNAL(timeout()), this, SLOT(lookUpWindow()));
79
+    m_windowLookUpTimer->start(500);
80
+}
81
+
82
+QString RDesktopLauncher::getExitStatusString(int status)
83
+{
84
+    switch (status) {
85
+    case 0:
86
+        return tr("RDP session terminated normally");
87
+    case 1:
88
+        return tr("Server initiated disconnect (also returned for logoff by XP joined to a domain)");
89
+    case 2:
90
+        return tr("Server initiated logoff");
91
+    case 3:
92
+        return tr("Server idle timeout reached");
93
+    case 4:
94
+        return tr("Server logon timeout reached");
95
+    case 5:
96
+        return tr("The session was replaced");
97
+    case 6:
98
+        return tr("The server is out of memory");
99
+    case 7:
100
+        return tr("The server denied the connection");
101
+    case 8:
102
+        return tr("The server denied the connection for security reason");
103
+    case 9:
104
+        return tr("The user cannot connect to the server due to insufficient access privileges");
105
+    case 10:
106
+        return tr("The server does not accept saved user credentials and requires that the user enter their credentials for each connection");
107
+    case 11:
108
+        return tr("Disconnect initiated by administration tool");
109
+    case 12:
110
+        return tr("Disconnect initiated by user");
111
+    case 16:
112
+        return tr("Internal licensing error");
113
+    case 17:
114
+        return tr("No license server available");
115
+    case 18:
116
+        return tr("No valid license available");
117
+    case 19:
118
+        return tr("Invalid licensing message");
119
+    case 20:
120
+        return tr("Hardware id doesn't match software license");
121
+    case 21:
122
+        return tr("Client license error");
123
+    case 22:
124
+        return tr("Network error during licensing protocol");
125
+    case 23:
126
+        return tr("Licensing protocol was not completed");
127
+    case 24:
128
+        return tr("Incorrect client license enryption");
129
+    case 25:
130
+        return tr("Can't upgrade license");
131
+    case 26:
132
+        return tr("The server is not licensed to accept remote connections");
133
+    case 62:
134
+        return tr("The local client window was closed");
135
+    case 63:
136
+        return tr("Some other, unknown error occured");
137
+    case 64:
138
+        return tr("Command line usage error");
139
+    case 69:
140
+        return tr("A service or resource (such as memory) is unavailable");
141
+    case 70:
142
+        return tr("An internal software error has been detected");
143
+    case 71:
144
+        return tr("Operating system error");
145
+    case 76:
146
+        return tr("Protocol error or unable to connect to remote host");
147
+    default:
148
+        return tr("Unknown error");
149
+    }
150
+}
151
+
152
+void RDesktopLauncher::kill()
153
+{
154
+    clearWindowLookUpTimer();
155
+    if (m_process) {
156
+        m_process->kill();
157
+    }
158
+}
159
+
160
+void RDesktopLauncher::onProcessFinished(int exitCode, QProcess::ExitStatus exitStatus)
161
+{
162
+    clearWindowLookUpTimer();
163
+    m_process->deleteLater();
164
+    m_process = 0;
165
+    emit finished(exitCode);
166
+}
167
+
168
+void RDesktopLauncher::lookUpWindow()
169
+{
170
+    QMap<WId, QString> windows = X11Helper::getRootWindows();
171
+    QList<WId> keys = windows.keys();
172
+    for (int i = 0; i < keys.size(); ++i) {
173
+        WId key = keys[i];
174
+        QString name = windows[key];
175
+        if (name.startsWith(m_options.windowTitle())) {
176
+            clearWindowLookUpTimer();
177
+            emit windowFound(key);
178
+            return;
179
+        }
180
+    }
181
+}
182
+
183
+void RDesktopLauncher::clearWindowLookUpTimer()
184
+{
185
+    if (m_windowLookUpTimer) {
186
+        m_windowLookUpTimer->stop();
187
+        m_windowLookUpTimer->deleteLater();
188
+        m_windowLookUpTimer = 0;
189
+    }
60 190
 }

+ 30
- 3
rdesktoplauncher.h View File

@@ -1,15 +1,42 @@
1 1
 #ifndef RDESKTOPLAUNCHER_H
2 2
 #define RDESKTOPLAUNCHER_H
3 3
 
4
+#include <QProcess>
5
+#include <QWidget>
6
+#include <QTimer>
4 7
 #include "rdpoptions.h"
5 8
 
6
-class RDesktopLauncher
9
+class RDesktopLauncher : public QObject
7 10
 {
11
+    Q_OBJECT
8 12
 public:
9
-    RDesktopLauncher();
13
+    RDesktopLauncher(QObject* parent = 0);
10 14
 
11
-public:
12 15
     void start(RdpOptions options);
16
+
17
+    static QString getExitStatusString(int status);
18
+
19
+signals:
20
+    void finished(int status);
21
+
22
+    void windowFound(WId);
23
+
24
+public slots:
25
+    void kill();
26
+
27
+private slots:
28
+    void onProcessFinished(int, QProcess::ExitStatus);
29
+
30
+    void lookUpWindow();
31
+
32
+    void clearWindowLookUpTimer();
33
+
34
+private:
35
+    QProcess* m_process;
36
+
37
+    QTimer* m_windowLookUpTimer;
38
+
39
+    RdpOptions m_options;
13 40
 };
14 41
 
15 42
 #endif // RDESKTOPLAUNCHER_H

+ 10
- 0
rdpoptions.cpp View File

@@ -174,6 +174,16 @@ QString RdpOptions::visibleName() const
174 174
     return str;
175 175
 }
176 176
 
177
+QString RdpOptions::windowTitle() const
178
+{
179
+    return m_windowTitle;
180
+}
181
+
182
+void RdpOptions::setWindowTitle(const QString &windowTitle)
183
+{
184
+    m_windowTitle = windowTitle;
185
+}
186
+
177 187
 
178 188
 
179 189
 

+ 5
- 0
rdpoptions.h View File

@@ -76,6 +76,9 @@ public:
76 76
     QString keymap() const;
77 77
     void setKeymap(const QString &keymap);
78 78
 
79
+    QString windowTitle() const;
80
+    void setWindowTitle(const QString &windowTitle);
81
+
79 82
     QString visibleName() const;
80 83
 
81 84
 private:
@@ -110,6 +113,8 @@ private:
110 113
     bool m_autoReconnect;
111 114
 
112 115
     QString m_keymap;
116
+
117
+    QString m_windowTitle;
113 118
 };
114 119
 
115 120
 #endif // RDPOPTIONS_H

+ 99
- 0
x11helper.cpp View File

@@ -0,0 +1,99 @@
1
+#include "x11helper.h"
2
+#include <X11/Xlib.h>
3
+#include <X11/Xutil.h>
4
+#include <X11/Xatom.h>
5
+#include <X11/Xresource.h>
6
+
7
+static char *get_property (Display *disp, Window win,
8
+        Atom xa_prop_type, char *prop_name, unsigned long *size) {
9
+    Atom xa_prop_name;
10
+    Atom xa_ret_type;
11
+    int ret_format;
12
+    unsigned long ret_nitems;
13
+    unsigned long ret_bytes_after;
14
+    unsigned long tmp_size;
15
+    unsigned char *ret_prop;
16
+    char *ret;
17
+
18
+    xa_prop_name = XInternAtom(disp, prop_name, False);
19
+
20
+    if (XGetWindowProperty(disp, win, xa_prop_name, 0, 200 / 4, False,
21
+            xa_prop_type, &xa_ret_type, &ret_format,
22
+            &ret_nitems, &ret_bytes_after, &ret_prop) != Success) {
23
+        return NULL;
24
+    }
25
+
26
+    if (xa_ret_type != xa_prop_type) {
27
+        XFree(ret_prop);
28
+        return NULL;
29
+    }
30
+
31
+    /* null terminate the result to make string handling easier */
32
+    tmp_size = (ret_format / 8) * ret_nitems;
33
+    /* Correct 64 Architecture implementation of 32 bit data */
34
+    if(ret_format==32) tmp_size *= sizeof(long)/4;
35
+    ret = (char*)malloc(tmp_size + 1);
36
+    memcpy(ret, ret_prop, tmp_size);
37
+    ret[tmp_size] = '\0';
38
+
39
+    if (size) {
40
+        *size = tmp_size;
41
+    }
42
+
43
+    XFree(ret_prop);
44
+    return ret;
45
+}
46
+
47
+static Window *get_client_list (Display *disp, unsigned long *size) {
48
+    Window *client_list;
49
+
50
+    if ((client_list = (Window *)get_property(disp, DefaultRootWindow(disp),
51
+                    XA_WINDOW, (char*)"_NET_CLIENT_LIST", size)) == NULL) {
52
+        if ((client_list = (Window *)get_property(disp, DefaultRootWindow(disp),
53
+                        XA_CARDINAL, (char*)"_WIN_CLIENT_LIST", size)) == NULL) {
54
+            return NULL;
55
+        }
56
+    }
57
+
58
+    return client_list;
59
+}
60
+
61
+QMap<WId, QString> X11Helper::getRootWindows()
62
+{
63
+    QMap<WId, QString> windows;
64
+    Display *dpy;
65
+    Window *children;
66
+    unsigned long noOfChildren;
67
+
68
+    dpy = XOpenDisplay(NULL);
69
+    if (!dpy) {
70
+        return windows;
71
+    }
72
+
73
+    children = (Window*)get_client_list(dpy, &noOfChildren);
74
+    if (!children) {
75
+        return windows;
76
+    }
77
+    for (unsigned long i = 0; i < noOfChildren; ++i) {
78
+        char* nameStr;
79
+        XFetchName(dpy, children[i], &nameStr);
80
+        QString name(nameStr);
81
+        XFree(nameStr);
82
+        if (!name.isEmpty()) {
83
+            windows.insert(children[i], name);
84
+//        if (name.startsWith("robin@gigi-lt1: /tmp")) {
85
+//            qDebug() << name;
86
+//            QWindow *window = QWindow::fromWinId(children[i]);
87
+//            window->setFlags(Qt::FramelessWindowHint);
88
+
89
+//            QWidget *widget = QWidget::createWindowContainer(window);
90
+//            QEvent e(QEvent::EmbeddingControl);
91
+//            QApplication::sendEvent(widget, &e);
92
+//            widget->show();
93
+//            QTimer* timer = new QTimer(widget);
94
+//            QObject::connect(timer, SIGNAL(timeout()), widget, SLOT(update()));
95
+//            timer->start(100);
96
+        }
97
+    }
98
+    return windows;
99
+}

+ 13
- 0
x11helper.h View File

@@ -0,0 +1,13 @@
1
+#ifndef X11HELPER_H
2
+#define X11HELPER_H
3
+
4
+#include <QMap>
5
+#include <QWidget>
6
+
7
+class X11Helper
8
+{
9
+public:
10
+    static QMap<WId, QString> getRootWindows();
11
+};
12
+
13
+#endif // X11HELPER_H

Loading…
Cancel
Save