1 /*
2     KWin - the KDE window manager
3     This file is part of the KDE project.
4 
5     SPDX-FileCopyrightText: 2018 Roman Gilg <subdiff@gmail.com>
6 
7     SPDX-License-Identifier: GPL-2.0-or-later
8 */
9 #ifndef KWIN_XWL_TRANSFER
10 #define KWIN_XWL_TRANSFER
11 
12 #include <QObject>
13 #include <QSocketNotifier>
14 #include <QVector>
15 
16 #include <xcb/xcb.h>
17 
18 namespace KWayland
19 {
20 namespace Client
21 {
22 class DataDevice;
23 class DataSource;
24 }
25 }
26 namespace KWaylandServer
27 {
28 class DataDeviceInterface;
29 }
30 
31 namespace KWin
32 {
33 namespace Xwl
34 {
35 
36 /**
37  * Represents for an arbitrary selection a data transfer between
38  * sender and receiver.
39  *
40  * Lives for the duration of the transfer and must be cleaned up
41  * externally afterwards. For that the owner should connect to the
42  * @c finished() signal.
43  */
44 class Transfer : public QObject
45 {
46     Q_OBJECT
47 
48 public:
49     Transfer(xcb_atom_t selection,
50              qint32 fd,
51              xcb_timestamp_t timestamp,
52              QObject *parent = nullptr);
53 
54     virtual bool handlePropertyNotify(xcb_property_notify_event_t *event) = 0;
55     void timeout();
timestamp()56     xcb_timestamp_t timestamp() const {
57         return m_timestamp;
58     }
59 
60 Q_SIGNALS:
61     void finished();
62 
63 protected:
64     void endTransfer();
65 
atom()66     xcb_atom_t atom() const {
67         return m_atom;
68     }
fd()69     qint32 fd() const {
70         return m_fd;
71     }
72 
setIncr(bool set)73     void setIncr(bool set) {
74         m_incr = set;
75     }
incr()76     bool incr() const {
77         return m_incr;
78     }
resetTimeout()79     void resetTimeout() {
80         m_timeout = false;
81     }
82     void createSocketNotifier(QSocketNotifier::Type type);
83     void clearSocketNotifier();
socketNotifier()84     QSocketNotifier *socketNotifier() const {
85         return m_notifier;
86     }
87 private:
88     void closeFd();
89 
90     xcb_atom_t m_atom;
91     qint32 m_fd;
92     xcb_timestamp_t m_timestamp = XCB_CURRENT_TIME;
93 
94     QSocketNotifier *m_notifier = nullptr;
95     bool m_incr = false;
96     bool m_timeout = false;
97 
98     Q_DISABLE_COPY(Transfer)
99 };
100 
101 /**
102  * Represents a transfer from a Wayland native source to an X window.
103  */
104 class TransferWltoX : public Transfer
105 {
106     Q_OBJECT
107 
108 public:
109     TransferWltoX(xcb_atom_t selection,
110                   xcb_selection_request_event_t *request,
111                   qint32 fd,
112                   QObject *parent = nullptr);
113     ~TransferWltoX() override;
114 
115     void startTransferFromSource();
116     bool handlePropertyNotify(xcb_property_notify_event_t *event) override;
117 
118 Q_SIGNALS:
119     void selectionNotify(xcb_selection_request_event_t *event, bool success);
120 
121 private:
122     void startIncr();
123     void readWlSource();
124     int flushSourceData();
125     void handlePropertyDelete();
126 
127     xcb_selection_request_event_t *m_request = nullptr;
128 
129     /* contains all received data portioned in chunks
130      * TODO: explain second QPair component
131      */
132     QVector<QPair<QByteArray, int> > m_chunks;
133 
134     bool m_propertyIsSet = false;
135     bool m_flushPropertyOnDelete = false;
136 
137     Q_DISABLE_COPY(TransferWltoX)
138 };
139 
140 /**
141  * Helper class for X to Wl transfers.
142  */
143 class DataReceiver
144 {
145 public:
146     virtual ~DataReceiver();
147 
148     void transferFromProperty(xcb_get_property_reply_t *reply);
149 
150 
151     virtual void setData(const char *value, int length);
152     QByteArray data() const;
153 
154     void partRead(int length);
155 
156 protected:
setDataInternal(QByteArray data)157     void setDataInternal(QByteArray data) {
158         m_data = data;
159     }
160 
161 private:
162     xcb_get_property_reply_t *m_propertyReply = nullptr;
163     int m_propertyStart = 0;
164     QByteArray m_data;
165 };
166 
167 /**
168  * Compatibility receiver for clients only
169  * supporting the NETSCAPE_URL scheme (Firefox)
170  */
171 class NetscapeUrlReceiver : public DataReceiver
172 {
173 public:
174     void setData(const char *value, int length) override;
175 };
176 
177 /**
178  * Compatibility receiver for clients only
179  * supporting the text/x-moz-url scheme (Chromium on own drags)
180  */
181 class MozUrlReceiver : public DataReceiver
182 {
183 public:
184     void setData(const char *value, int length) override;
185 };
186 
187 /**
188  * Represents a transfer from an X window to a Wayland native client.
189  */
190 class TransferXtoWl : public Transfer
191 {
192     Q_OBJECT
193 
194 public:
195     TransferXtoWl(xcb_atom_t selection,
196                   xcb_atom_t target,
197                   qint32 fd,
198                   xcb_timestamp_t timestamp, xcb_window_t parentWindow,
199                   QObject *parent = nullptr);
200     ~TransferXtoWl() override;
201 
202     bool handleSelectionNotify(xcb_selection_notify_event_t *event);
203     bool handlePropertyNotify(xcb_property_notify_event_t *event) override;
204 
205 private:
206     void dataSourceWrite();
207     void startTransfer();
208     void getIncrChunk();
209 
210     xcb_window_t m_window;
211     DataReceiver *m_receiver = nullptr;
212 
213     Q_DISABLE_COPY(TransferXtoWl)
214 };
215 
216 } // namespace Xwl
217 } // namespace KWin
218 
219 #endif
220