├── LICENSE ├── README.md └── src ├── compile ├── main.cpp ├── wayland-proxy.cpp └── wayland-proxy.h /LICENSE: -------------------------------------------------------------------------------- 1 | Mozilla Public License Version 2.0 2 | ================================== 3 | 4 | 1. Definitions 5 | -------------- 6 | 7 | 1.1. "Contributor" 8 | means each individual or legal entity that creates, contributes to 9 | the creation of, or owns Covered Software. 10 | 11 | 1.2. "Contributor Version" 12 | means the combination of the Contributions of others (if any) used 13 | by a Contributor and that particular Contributor's Contribution. 14 | 15 | 1.3. "Contribution" 16 | means Covered Software of a particular Contributor. 17 | 18 | 1.4. "Covered Software" 19 | means Source Code Form to which the initial Contributor has attached 20 | the notice in Exhibit A, the Executable Form of such Source Code 21 | Form, and Modifications of such Source Code Form, in each case 22 | including portions thereof. 23 | 24 | 1.5. "Incompatible With Secondary Licenses" 25 | means 26 | 27 | (a) that the initial Contributor has attached the notice described 28 | in Exhibit B to the Covered Software; or 29 | 30 | (b) that the Covered Software was made available under the terms of 31 | version 1.1 or earlier of the License, but not also under the 32 | terms of a Secondary License. 33 | 34 | 1.6. "Executable Form" 35 | means any form of the work other than Source Code Form. 36 | 37 | 1.7. "Larger Work" 38 | means a work that combines Covered Software with other material, in 39 | a separate file or files, that is not Covered Software. 40 | 41 | 1.8. "License" 42 | means this document. 43 | 44 | 1.9. "Licensable" 45 | means having the right to grant, to the maximum extent possible, 46 | whether at the time of the initial grant or subsequently, any and 47 | all of the rights conveyed by this License. 48 | 49 | 1.10. "Modifications" 50 | means any of the following: 51 | 52 | (a) any file in Source Code Form that results from an addition to, 53 | deletion from, or modification of the contents of Covered 54 | Software; or 55 | 56 | (b) any new file in Source Code Form that contains any Covered 57 | Software. 58 | 59 | 1.11. "Patent Claims" of a Contributor 60 | means any patent claim(s), including without limitation, method, 61 | process, and apparatus claims, in any patent Licensable by such 62 | Contributor that would be infringed, but for the grant of the 63 | License, by the making, using, selling, offering for sale, having 64 | made, import, or transfer of either its Contributions or its 65 | Contributor Version. 66 | 67 | 1.12. "Secondary License" 68 | means either the GNU General Public License, Version 2.0, the GNU 69 | Lesser General Public License, Version 2.1, the GNU Affero General 70 | Public License, Version 3.0, or any later versions of those 71 | licenses. 72 | 73 | 1.13. "Source Code Form" 74 | means the form of the work preferred for making modifications. 75 | 76 | 1.14. "You" (or "Your") 77 | means an individual or a legal entity exercising rights under this 78 | License. For legal entities, "You" includes any entity that 79 | controls, is controlled by, or is under common control with You. For 80 | purposes of this definition, "control" means (a) the power, direct 81 | or indirect, to cause the direction or management of such entity, 82 | whether by contract or otherwise, or (b) ownership of more than 83 | fifty percent (50%) of the outstanding shares or beneficial 84 | ownership of such entity. 85 | 86 | 2. License Grants and Conditions 87 | -------------------------------- 88 | 89 | 2.1. Grants 90 | 91 | Each Contributor hereby grants You a world-wide, royalty-free, 92 | non-exclusive license: 93 | 94 | (a) under intellectual property rights (other than patent or trademark) 95 | Licensable by such Contributor to use, reproduce, make available, 96 | modify, display, perform, distribute, and otherwise exploit its 97 | Contributions, either on an unmodified basis, with Modifications, or 98 | as part of a Larger Work; and 99 | 100 | (b) under Patent Claims of such Contributor to make, use, sell, offer 101 | for sale, have made, import, and otherwise transfer either its 102 | Contributions or its Contributor Version. 103 | 104 | 2.2. Effective Date 105 | 106 | The licenses granted in Section 2.1 with respect to any Contribution 107 | become effective for each Contribution on the date the Contributor first 108 | distributes such Contribution. 109 | 110 | 2.3. Limitations on Grant Scope 111 | 112 | The licenses granted in this Section 2 are the only rights granted under 113 | this License. No additional rights or licenses will be implied from the 114 | distribution or licensing of Covered Software under this License. 115 | Notwithstanding Section 2.1(b) above, no patent license is granted by a 116 | Contributor: 117 | 118 | (a) for any code that a Contributor has removed from Covered Software; 119 | or 120 | 121 | (b) for infringements caused by: (i) Your and any other third party's 122 | modifications of Covered Software, or (ii) the combination of its 123 | Contributions with other software (except as part of its Contributor 124 | Version); or 125 | 126 | (c) under Patent Claims infringed by Covered Software in the absence of 127 | its Contributions. 128 | 129 | This License does not grant any rights in the trademarks, service marks, 130 | or logos of any Contributor (except as may be necessary to comply with 131 | the notice requirements in Section 3.4). 132 | 133 | 2.4. Subsequent Licenses 134 | 135 | No Contributor makes additional grants as a result of Your choice to 136 | distribute the Covered Software under a subsequent version of this 137 | License (see Section 10.2) or under the terms of a Secondary License (if 138 | permitted under the terms of Section 3.3). 139 | 140 | 2.5. Representation 141 | 142 | Each Contributor represents that the Contributor believes its 143 | Contributions are its original creation(s) or it has sufficient rights 144 | to grant the rights to its Contributions conveyed by this License. 145 | 146 | 2.6. Fair Use 147 | 148 | This License is not intended to limit any rights You have under 149 | applicable copyright doctrines of fair use, fair dealing, or other 150 | equivalents. 151 | 152 | 2.7. Conditions 153 | 154 | Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted 155 | in Section 2.1. 156 | 157 | 3. Responsibilities 158 | ------------------- 159 | 160 | 3.1. Distribution of Source Form 161 | 162 | All distribution of Covered Software in Source Code Form, including any 163 | Modifications that You create or to which You contribute, must be under 164 | the terms of this License. You must inform recipients that the Source 165 | Code Form of the Covered Software is governed by the terms of this 166 | License, and how they can obtain a copy of this License. You may not 167 | attempt to alter or restrict the recipients' rights in the Source Code 168 | Form. 169 | 170 | 3.2. Distribution of Executable Form 171 | 172 | If You distribute Covered Software in Executable Form then: 173 | 174 | (a) such Covered Software must also be made available in Source Code 175 | Form, as described in Section 3.1, and You must inform recipients of 176 | the Executable Form how they can obtain a copy of such Source Code 177 | Form by reasonable means in a timely manner, at a charge no more 178 | than the cost of distribution to the recipient; and 179 | 180 | (b) You may distribute such Executable Form under the terms of this 181 | License, or sublicense it under different terms, provided that the 182 | license for the Executable Form does not attempt to limit or alter 183 | the recipients' rights in the Source Code Form under this License. 184 | 185 | 3.3. Distribution of a Larger Work 186 | 187 | You may create and distribute a Larger Work under terms of Your choice, 188 | provided that You also comply with the requirements of this License for 189 | the Covered Software. If the Larger Work is a combination of Covered 190 | Software with a work governed by one or more Secondary Licenses, and the 191 | Covered Software is not Incompatible With Secondary Licenses, this 192 | License permits You to additionally distribute such Covered Software 193 | under the terms of such Secondary License(s), so that the recipient of 194 | the Larger Work may, at their option, further distribute the Covered 195 | Software under the terms of either this License or such Secondary 196 | License(s). 197 | 198 | 3.4. Notices 199 | 200 | You may not remove or alter the substance of any license notices 201 | (including copyright notices, patent notices, disclaimers of warranty, 202 | or limitations of liability) contained within the Source Code Form of 203 | the Covered Software, except that You may alter any license notices to 204 | the extent required to remedy known factual inaccuracies. 205 | 206 | 3.5. Application of Additional Terms 207 | 208 | You may choose to offer, and to charge a fee for, warranty, support, 209 | indemnity or liability obligations to one or more recipients of Covered 210 | Software. However, You may do so only on Your own behalf, and not on 211 | behalf of any Contributor. You must make it absolutely clear that any 212 | such warranty, support, indemnity, or liability obligation is offered by 213 | You alone, and You hereby agree to indemnify every Contributor for any 214 | liability incurred by such Contributor as a result of warranty, support, 215 | indemnity or liability terms You offer. You may include additional 216 | disclaimers of warranty and limitations of liability specific to any 217 | jurisdiction. 218 | 219 | 4. Inability to Comply Due to Statute or Regulation 220 | --------------------------------------------------- 221 | 222 | If it is impossible for You to comply with any of the terms of this 223 | License with respect to some or all of the Covered Software due to 224 | statute, judicial order, or regulation then You must: (a) comply with 225 | the terms of this License to the maximum extent possible; and (b) 226 | describe the limitations and the code they affect. Such description must 227 | be placed in a text file included with all distributions of the Covered 228 | Software under this License. Except to the extent prohibited by statute 229 | or regulation, such description must be sufficiently detailed for a 230 | recipient of ordinary skill to be able to understand it. 231 | 232 | 5. Termination 233 | -------------- 234 | 235 | 5.1. The rights granted under this License will terminate automatically 236 | if You fail to comply with any of its terms. However, if You become 237 | compliant, then the rights granted under this License from a particular 238 | Contributor are reinstated (a) provisionally, unless and until such 239 | Contributor explicitly and finally terminates Your grants, and (b) on an 240 | ongoing basis, if such Contributor fails to notify You of the 241 | non-compliance by some reasonable means prior to 60 days after You have 242 | come back into compliance. Moreover, Your grants from a particular 243 | Contributor are reinstated on an ongoing basis if such Contributor 244 | notifies You of the non-compliance by some reasonable means, this is the 245 | first time You have received notice of non-compliance with this License 246 | from such Contributor, and You become compliant prior to 30 days after 247 | Your receipt of the notice. 248 | 249 | 5.2. If You initiate litigation against any entity by asserting a patent 250 | infringement claim (excluding declaratory judgment actions, 251 | counter-claims, and cross-claims) alleging that a Contributor Version 252 | directly or indirectly infringes any patent, then the rights granted to 253 | You by any and all Contributors for the Covered Software under Section 254 | 2.1 of this License shall terminate. 255 | 256 | 5.3. In the event of termination under Sections 5.1 or 5.2 above, all 257 | end user license agreements (excluding distributors and resellers) which 258 | have been validly granted by You or Your distributors under this License 259 | prior to termination shall survive termination. 260 | 261 | ************************************************************************ 262 | * * 263 | * 6. Disclaimer of Warranty * 264 | * ------------------------- * 265 | * * 266 | * Covered Software is provided under this License on an "as is" * 267 | * basis, without warranty of any kind, either expressed, implied, or * 268 | * statutory, including, without limitation, warranties that the * 269 | * Covered Software is free of defects, merchantable, fit for a * 270 | * particular purpose or non-infringing. The entire risk as to the * 271 | * quality and performance of the Covered Software is with You. * 272 | * Should any Covered Software prove defective in any respect, You * 273 | * (not any Contributor) assume the cost of any necessary servicing, * 274 | * repair, or correction. This disclaimer of warranty constitutes an * 275 | * essential part of this License. No use of any Covered Software is * 276 | * authorized under this License except under this disclaimer. * 277 | * * 278 | ************************************************************************ 279 | 280 | ************************************************************************ 281 | * * 282 | * 7. Limitation of Liability * 283 | * -------------------------- * 284 | * * 285 | * Under no circumstances and under no legal theory, whether tort * 286 | * (including negligence), contract, or otherwise, shall any * 287 | * Contributor, or anyone who distributes Covered Software as * 288 | * permitted above, be liable to You for any direct, indirect, * 289 | * special, incidental, or consequential damages of any character * 290 | * including, without limitation, damages for lost profits, loss of * 291 | * goodwill, work stoppage, computer failure or malfunction, or any * 292 | * and all other commercial damages or losses, even if such party * 293 | * shall have been informed of the possibility of such damages. This * 294 | * limitation of liability shall not apply to liability for death or * 295 | * personal injury resulting from such party's negligence to the * 296 | * extent applicable law prohibits such limitation. Some * 297 | * jurisdictions do not allow the exclusion or limitation of * 298 | * incidental or consequential damages, so this exclusion and * 299 | * limitation may not apply to You. * 300 | * * 301 | ************************************************************************ 302 | 303 | 8. Litigation 304 | ------------- 305 | 306 | Any litigation relating to this License may be brought only in the 307 | courts of a jurisdiction where the defendant maintains its principal 308 | place of business and such litigation shall be governed by laws of that 309 | jurisdiction, without reference to its conflict-of-law provisions. 310 | Nothing in this Section shall prevent a party's ability to bring 311 | cross-claims or counter-claims. 312 | 313 | 9. Miscellaneous 314 | ---------------- 315 | 316 | This License represents the complete agreement concerning the subject 317 | matter hereof. If any provision of this License is held to be 318 | unenforceable, such provision shall be reformed only to the extent 319 | necessary to make it enforceable. Any law or regulation which provides 320 | that the language of a contract shall be construed against the drafter 321 | shall not be used to construe this License against a Contributor. 322 | 323 | 10. Versions of the License 324 | --------------------------- 325 | 326 | 10.1. New Versions 327 | 328 | Mozilla Foundation is the license steward. Except as provided in Section 329 | 10.3, no one other than the license steward has the right to modify or 330 | publish new versions of this License. Each version will be given a 331 | distinguishing version number. 332 | 333 | 10.2. Effect of New Versions 334 | 335 | You may distribute the Covered Software under the terms of the version 336 | of the License under which You originally received the Covered Software, 337 | or under the terms of any subsequent version published by the license 338 | steward. 339 | 340 | 10.3. Modified Versions 341 | 342 | If you create software not governed by this License, and you want to 343 | create a new license for such software, you may create and use a 344 | modified version of this License if you rename the license and remove 345 | any references to the name of the license steward (except to note that 346 | such modified license differs from this License). 347 | 348 | 10.4. Distributing Source Code Form that is Incompatible With Secondary 349 | Licenses 350 | 351 | If You choose to distribute Source Code Form that is Incompatible With 352 | Secondary Licenses under the terms of this version of the License, the 353 | notice described in Exhibit B of this License must be attached. 354 | 355 | Exhibit A - Source Code Form License Notice 356 | ------------------------------------------- 357 | 358 | This Source Code Form is subject to the terms of the Mozilla Public 359 | License, v. 2.0. If a copy of the MPL was not distributed with this 360 | file, You can obtain one at http://mozilla.org/MPL/2.0/. 361 | 362 | If it is not possible or desirable to put the notice in a particular 363 | file, then You may include the notice in a location (such as a LICENSE 364 | file in a relevant directory) where a recipient would be likely to look 365 | for such a notice. 366 | 367 | You may add additional accurate notices of copyright ownership. 368 | 369 | Exhibit B - "Incompatible With Secondary Licenses" Notice 370 | --------------------------------------------------------- 371 | 372 | This Source Code Form is "Incompatible With Secondary Licenses", as 373 | defined by the Mozilla Public License, v. 2.0. 374 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Wayland proxy 2 | 3 | Wayland proxy is load balancer between Wayland compositor and Wayland client. It prevents Wayland client to be 4 | disconnected by Wayland compositor if Wayland client is busy or under heavy load. 5 | 6 | This C++ implementation is based on Rust one at https://github.com/the8472/weyland-p5000 7 | 8 | See Mozilla Firefox bugs for details (https://bugzilla.mozilla.org/show_bug.cgi?id=1743144) 9 | 10 | ## Usage 11 | 12 | Wayland proxy can be run as stand alone application or as a library. Stand alone application can be build 13 | by `compile` script at `src` dir and then run Wayland application as 14 | 15 | ``` 16 | ./wayland-proxy application_path 17 | ``` 18 | 19 | Library version can be attached to your Wayland application. 20 | Create proxy **BEFORE** you connect app to Wayland display (usually `gtk_init()` or `wl_display_connect()` calls). 21 | 22 | ``` 23 | // Enable logging 24 | WaylandProxy::SetVerbose(true); 25 | 26 | // Create and run Wayland proxy in extra thread 27 | std::unique_ptr proxy = WaylandProxy::Create(); 28 | if (proxy) { 29 | proxy->RunThread(); 30 | } 31 | ``` 32 | 33 | Terminate and clean up proxy: 34 | 35 | ``` 36 | proxy = nullptr; 37 | ``` 38 | 39 | -------------------------------------------------------------------------------- /src/compile: -------------------------------------------------------------------------------- 1 | c++ -std=gnu++17 -g -O2 wayland-proxy.cpp main.cpp -o wayland-proxy -pthread 2 | -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 5 | #include "wayland-proxy.h" 6 | #include 7 | 8 | int main(int argc, char* argv[]) { 9 | if (argc < 2) { 10 | printf("Wayland proxy load balancer, run as:\n\n%s application_name\n\n", argv[0]); 11 | return 0; 12 | } 13 | WaylandProxy::SetVerbose(true); 14 | auto proxy = WaylandProxy::Create(); 15 | if (!proxy) { 16 | return 1; 17 | } 18 | return !proxy->RunChildApplication(argv+1); 19 | } 20 | -------------------------------------------------------------------------------- /src/wayland-proxy.cpp: -------------------------------------------------------------------------------- 1 | /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 5 | 6 | // This code is based on Rust implementation at 7 | // https://github.com/the8472/weyland-p5000 8 | 9 | // Version 1.2 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | #include "wayland-proxy.h" 31 | 32 | // The maximum number of fds libwayland can recvmsg at once 33 | #define MAX_LIBWAY_FDS 28 34 | #define MAX_DATA_SIZE 4096 35 | #define POLL_TIMEOUT 30000 36 | 37 | bool sPrintInfo = false; 38 | 39 | void Print(const char* aFormat, ...) { 40 | if (!sPrintInfo) { 41 | return; 42 | } 43 | va_list args; 44 | va_start(args, aFormat); 45 | vfprintf(stderr, aFormat, args); 46 | va_end(args); 47 | } 48 | 49 | void Warning(const char* aOperation) { 50 | fprintf(stderr, "Warning: %s : %s\n", aOperation, strerror(errno)); 51 | } 52 | 53 | void Error(const char* aOperation) { 54 | fprintf(stderr, "Error: %s : %s\n", aOperation, strerror(errno)); 55 | } 56 | 57 | void ErrorPlain(const char* aFormat, ...) { 58 | va_list args; 59 | va_start(args, aFormat); 60 | vfprintf(stderr, aFormat, args); 61 | va_end(args); 62 | } 63 | 64 | class WaylandMessage { 65 | public: 66 | bool Write(int aSocket); 67 | 68 | bool Loaded() const { return !mFailed && (mFds.size() || mData.size()); } 69 | bool Failed() const { return mFailed; } 70 | 71 | explicit WaylandMessage(int aSocket) { Read(aSocket); } 72 | ~WaylandMessage(); 73 | 74 | private: 75 | void Read(int aSocket); 76 | 77 | private: 78 | bool mFailed = false; 79 | 80 | std::vector mFds; 81 | std::vector mData; 82 | }; 83 | 84 | class ProxiedConnection { 85 | public: 86 | bool Init(int aChildSocket, char* aWaylandDisplay); 87 | bool IsConnected() { return mCompositorConnected; } 88 | 89 | struct pollfd* AddToPollFd(struct pollfd* aPfds); 90 | struct pollfd* LoadPollFd(struct pollfd* aPfds); 91 | 92 | // Process this connection (send/receive data). 93 | // Returns false if connection is broken and should be removed. 94 | bool Process(); 95 | 96 | ~ProxiedConnection(); 97 | 98 | private: 99 | // Try to connect to compositor. Returns false in case of fatal error. 100 | bool ConnectToCompositor(); 101 | 102 | bool TransferOrQueue( 103 | int aSourceSocket, int aSourcePollFlags, int aDestSocket, 104 | std::vector>* aMessageQueue); 105 | bool FlushQueue(int aDestSocket, int aDestPollFlags, 106 | std::vector>& aMessageQueue); 107 | 108 | // Where we should connect. 109 | // Weak pointer to parent WaylandProxy class. 110 | char* mWaylandDisplay = nullptr; 111 | 112 | // We don't have connected compositor yet. Try to connect 113 | bool mCompositorConnected = false; 114 | 115 | // Don't cycle endlessly over compositor connection 116 | int mFailedCompositorConnections = 0; 117 | static constexpr int sMaxFailedCompositorConnections = 100; 118 | 119 | // We're disconnected from app or compositor. We will close this connection. 120 | bool mFailed = false; 121 | 122 | int mCompositorSocket = -1; 123 | int mCompositorFlags = 0; 124 | 125 | int mApplicationSocket = -1; 126 | int mApplicationFlags = 0; 127 | 128 | // Stored proxied data 129 | std::vector> mToCompositorQueue; 130 | std::vector> mToApplicationQueue; 131 | }; 132 | 133 | WaylandMessage::~WaylandMessage() { 134 | for (auto const fd : mFds) { 135 | close(fd); 136 | } 137 | } 138 | 139 | void WaylandMessage::Read(int aSocket) { 140 | // We don't expect WaylandMessage re-read 141 | assert(!Loaded() && !mFailed); 142 | 143 | mData.resize(MAX_DATA_SIZE); 144 | 145 | struct msghdr msg = {0}; 146 | struct iovec iov = {mData.data(), mData.size()}; 147 | msg.msg_iov = &iov; 148 | msg.msg_iovlen = 1; 149 | 150 | char cmsgdata[(CMSG_LEN(MAX_LIBWAY_FDS * sizeof(int32_t)))] = {0}; 151 | msg.msg_control = &cmsgdata; 152 | msg.msg_controllen = sizeof(cmsgdata); 153 | 154 | ssize_t ret = recvmsg(aSocket, &msg, MSG_CMSG_CLOEXEC | MSG_DONTWAIT); 155 | if (msg.msg_flags & (MSG_CTRUNC | MSG_TRUNC)) { 156 | Error("WaylandMessage::Read() data truncated, small buffer?"); 157 | mFailed = true; 158 | return; 159 | } 160 | 161 | if (ret < 1) { 162 | switch (errno) { 163 | case EAGAIN: 164 | case EINTR: 165 | // Neither loaded nor failed, we'll try again later 166 | Print("WaylandMessage::Read() failed %s\n", strerror(errno)); 167 | return; 168 | default: 169 | Error("WaylandMessage::Read() failed"); 170 | mFailed = true; 171 | return; 172 | } 173 | } 174 | 175 | // Set correct data size 176 | mData.resize(ret); 177 | 178 | // Read cmsg 179 | struct cmsghdr* header = CMSG_FIRSTHDR(&msg); 180 | while (header) { 181 | struct cmsghdr* next = CMSG_NXTHDR(&msg, header); 182 | if (header->cmsg_level != SOL_SOCKET || header->cmsg_type != SCM_RIGHTS) { 183 | header = next; 184 | continue; 185 | } 186 | 187 | int* data = (int*)CMSG_DATA(header); 188 | int filenum = (int)((header->cmsg_len - CMSG_LEN(0)) / sizeof(int)); 189 | if (filenum > MAX_LIBWAY_FDS) { 190 | ErrorPlain("WaylandMessage::Read(): too many files to read\n"); 191 | mFailed = true; 192 | return; 193 | } 194 | for (int i = 0; i < filenum; i++) { 195 | #ifdef DEBUG 196 | int flags = fcntl(data[i], F_GETFL, 0); 197 | if (flags == -1 && errno == EBADF) { 198 | Error("WaylandMessage::Read() invalid fd"); 199 | } 200 | #endif 201 | mFds.push_back(data[i]); 202 | } 203 | header = next; 204 | } 205 | } 206 | 207 | bool WaylandMessage::Write(int aSocket) { 208 | if (!Loaded()) { 209 | return false; 210 | } 211 | 212 | struct msghdr msg = {0}; 213 | struct iovec iov = {mData.data(), mData.size()}; 214 | msg.msg_iov = &iov; 215 | msg.msg_iovlen = 1; 216 | 217 | union { 218 | char buf[CMSG_SPACE(sizeof(int) * MAX_LIBWAY_FDS)]; 219 | struct cmsghdr align; 220 | } cmsgu; 221 | memset(cmsgu.buf, 0, sizeof(cmsgu.buf)); 222 | 223 | int filenum = mFds.size(); 224 | if (filenum) { 225 | if (filenum > MAX_LIBWAY_FDS) { 226 | ErrorPlain("WaylandMessage::Write() too many files to send\n"); 227 | return false; 228 | } 229 | #ifdef DEBUG 230 | for (int i = 0; i < filenum; i++) { 231 | int flags = fcntl(mFds[i], F_GETFL, 0); 232 | if (flags == -1 && errno == EBADF) { 233 | Error("WaylandMessage::Write() invalid fd\n"); 234 | } 235 | } 236 | #endif 237 | msg.msg_control = cmsgu.buf; 238 | msg.msg_controllen = CMSG_SPACE(filenum * sizeof(int)); 239 | 240 | struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg); 241 | cmsg->cmsg_level = SOL_SOCKET; 242 | cmsg->cmsg_type = SCM_RIGHTS; 243 | cmsg->cmsg_len = CMSG_LEN(filenum * sizeof(int)); 244 | memcpy(CMSG_DATA(cmsg), mFds.data(), filenum * sizeof(int)); 245 | } 246 | 247 | ssize_t ret = sendmsg(aSocket, &msg, MSG_CMSG_CLOEXEC | MSG_DONTWAIT); 248 | if (ret < 1) { 249 | switch (errno) { 250 | case EAGAIN: 251 | case EINTR: 252 | // Neither loaded nor failed, we'll try again later 253 | Print("WaylandMessage::Write() failed %s\n", strerror(errno)); 254 | return false; 255 | default: 256 | Warning("WaylandMessage::Write() failed"); 257 | mFailed = true; 258 | return false; 259 | } 260 | } 261 | 262 | if (ret != (ssize_t)mData.size()) { 263 | Print("WaylandMessage::Write() failed to write all data! (%d vs. %d)\n", ret, 264 | mData.size()); 265 | } 266 | return true; 267 | } 268 | 269 | ProxiedConnection::~ProxiedConnection() { 270 | if (mCompositorSocket != -1) { 271 | close(mCompositorSocket); 272 | } 273 | if (mApplicationSocket != -1) { 274 | close(mApplicationSocket); 275 | } 276 | } 277 | 278 | bool ProxiedConnection::Init(int aApplicationSocket, char* aWaylandDisplay) { 279 | mWaylandDisplay = aWaylandDisplay; 280 | mApplicationSocket = aApplicationSocket; 281 | mCompositorSocket = 282 | socket(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0); 283 | if (mCompositorSocket == -1) { 284 | Error("WaylandProxy: ProxiedConnection::Init() socket()"); 285 | } 286 | bool ret = mApplicationSocket >= 0 && mCompositorSocket >= 0; 287 | Print("WaylandProxy: ProxiedConnection::Init() %s\n", ret ? "OK" : "FAILED"); 288 | return ret; 289 | } 290 | 291 | struct pollfd* ProxiedConnection::AddToPollFd(struct pollfd* aPfds) { 292 | // Listen application's requests 293 | aPfds->fd = mApplicationSocket; 294 | aPfds->events = POLLIN; 295 | 296 | // We're connected and we have data for appplication from compositor. 297 | // Add POLLOUT to request write to app socket. 298 | if (mCompositorConnected && !mToApplicationQueue.empty()) { 299 | aPfds->events |= POLLOUT; 300 | } 301 | aPfds++; 302 | 303 | aPfds->fd = mCompositorSocket; 304 | aPfds->events = 0; 305 | // We're waiting for connection or we have data for compositor 306 | if (!mCompositorConnected || !mToCompositorQueue.empty()) { 307 | aPfds->events |= POLLOUT; 308 | } 309 | if (mCompositorConnected) { 310 | aPfds->events |= POLLIN; 311 | } 312 | aPfds++; 313 | 314 | return aPfds; 315 | } 316 | 317 | struct pollfd* ProxiedConnection::LoadPollFd(struct pollfd* aPfds) { 318 | if (aPfds->fd != mApplicationSocket) { 319 | return aPfds; 320 | } 321 | mApplicationFlags = aPfds->revents; 322 | aPfds++; 323 | mCompositorFlags = aPfds->revents; 324 | aPfds++; 325 | return aPfds; 326 | } 327 | 328 | bool ProxiedConnection::ConnectToCompositor() { 329 | struct sockaddr_un addr = {}; 330 | addr.sun_family = AF_UNIX; 331 | strcpy(addr.sun_path, mWaylandDisplay); 332 | 333 | mCompositorConnected = 334 | connect(mCompositorSocket, (const struct sockaddr*)&addr, 335 | sizeof(struct sockaddr_un)) != -1; 336 | if (!mCompositorConnected) { 337 | switch (errno) { 338 | case EAGAIN: 339 | case EALREADY: 340 | case ECONNREFUSED: 341 | case EINPROGRESS: 342 | case EINTR: 343 | case EISCONN: 344 | case ETIMEDOUT: 345 | mFailedCompositorConnections++; 346 | if (mFailedCompositorConnections > sMaxFailedCompositorConnections) { 347 | Error("ConnectToCompositor() connect() failed repeatedly"); 348 | return false; 349 | } 350 | // We can recover from these errors and try again 351 | Warning("ConnectToCompositor() try again"); 352 | return true; 353 | default: 354 | Error("ConnectToCompositor() connect()"); 355 | return false; 356 | } 357 | } 358 | return true; 359 | } 360 | 361 | // Read data from aSourceSocket and try to twite them to aDestSocket. 362 | // If data write fails, append them to aMessageQueue. 363 | // Return 364 | bool ProxiedConnection::TransferOrQueue( 365 | int aSourceSocket, int aSourcePollFlags, int aDestSocket, 366 | std::vector>* aMessageQueue) { 367 | // Don't read if we don't have any data ready 368 | if (!(aSourcePollFlags & POLLIN)) { 369 | return true; 370 | } 371 | 372 | while (1) { 373 | int availableData = 0; 374 | if (ioctl(aSourceSocket, FIONREAD, &availableData) < 0) { 375 | // Broken connection, we're finished here 376 | Warning("ProxiedConnection::TransferOrQueue() broken source socket %s\n"); 377 | return false; 378 | } 379 | if (availableData == 0) { 380 | return true; 381 | } 382 | 383 | auto message = std::make_unique(aSourceSocket); 384 | if (message->Failed()) { 385 | // Failed to read message due to error 386 | return false; 387 | } 388 | if (!message->Loaded()) { 389 | // Let's try again 390 | return true; 391 | } 392 | if (!message->Write(aDestSocket)) { 393 | if (message->Failed()) { 394 | // Failed to write and we can't recover 395 | return false; 396 | } 397 | aMessageQueue->push_back(std::move(message)); 398 | } 399 | } 400 | } 401 | 402 | // Try to flush all data to aMessageQueue. 403 | bool ProxiedConnection::FlushQueue( 404 | int aDestSocket, int aDestPollFlags, 405 | std::vector>& aMessageQueue) { 406 | // Can't write to destination yet 407 | if (!(aDestPollFlags & POLLOUT) || aMessageQueue.empty()) { 408 | return true; 409 | } 410 | 411 | std::vector>::iterator message; 412 | for (message = aMessageQueue.begin(); message != aMessageQueue.end();) { 413 | if (!(*message)->Write(aDestSocket)) { 414 | // Failed to write the message, remove whole connection 415 | // as it's broken. 416 | if ((*message)->Failed()) { 417 | return false; 418 | } 419 | break; 420 | } 421 | message++; 422 | } 423 | 424 | // Remove all written messages at once. 425 | if (message != aMessageQueue.begin()) { 426 | aMessageQueue.erase(aMessageQueue.begin(), message); 427 | } 428 | 429 | return true; 430 | } 431 | 432 | bool ProxiedConnection::Process() { 433 | if (mFailed) { 434 | return false; 435 | } 436 | 437 | // Check if appplication is still listening 438 | if (mApplicationFlags & (POLLHUP | POLLERR)) { 439 | return false; 440 | } 441 | 442 | // Check if compositor is still listening 443 | if (mCompositorConnected) { 444 | if (mCompositorFlags & (POLLHUP | POLLERR)) { 445 | return false; 446 | } 447 | } else { 448 | // Try to reconnect to compositor. 449 | if (!ConnectToCompositor()) { 450 | Print("Failed to connect to compositor\n"); 451 | return false; 452 | } 453 | // We're not connected yet but ConnectToCompositor() didn't return 454 | // fatal error. Try again later. 455 | if (!mCompositorConnected) { 456 | return true; 457 | } 458 | } 459 | 460 | mFailed = 461 | !TransferOrQueue(mCompositorSocket, mCompositorFlags, mApplicationSocket, 462 | &mToApplicationQueue) || 463 | !TransferOrQueue(mApplicationSocket, mApplicationFlags, mCompositorSocket, 464 | &mToCompositorQueue) || 465 | !FlushQueue(mCompositorSocket, mCompositorFlags, mToCompositorQueue) || 466 | !FlushQueue(mApplicationSocket, mApplicationFlags, mToApplicationQueue); 467 | 468 | return !mFailed; 469 | } 470 | 471 | bool WaylandProxy::CheckWaylandDisplay(const char* aWaylandDisplay) { 472 | struct sockaddr_un addr = {}; 473 | addr.sun_family = AF_UNIX; 474 | strcpy(addr.sun_path, aWaylandDisplay); 475 | 476 | int sc = socket(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0); 477 | if (sc == -1) { 478 | Error("CheckWaylandDisplay(): failed to create socket"); 479 | return false; 480 | } 481 | 482 | bool ret = 483 | connect(sc, (const struct sockaddr*)&addr, 484 | sizeof(struct sockaddr_un)) != -1; 485 | if (!ret) { 486 | switch (errno) { 487 | case EAGAIN: 488 | case EALREADY: 489 | case ECONNREFUSED: 490 | case EINPROGRESS: 491 | case EINTR: 492 | case EISCONN: 493 | case ETIMEDOUT: 494 | // We can recover from these errors and try again 495 | ret = true; 496 | break; 497 | default: 498 | ErrorPlain( 499 | "CheckWaylandDisplay(): Failed to connect to Wayland display '%s' error: %s\n", 500 | mWaylandDisplay, strerror(errno)); 501 | break; 502 | } 503 | } 504 | 505 | close(sc); 506 | return ret; 507 | } 508 | 509 | 510 | bool WaylandProxy::SetupWaylandDisplays() { 511 | char* waylandDisplay = getenv("WAYLAND_DISPLAY_COMPOSITOR"); 512 | if (!waylandDisplay) { 513 | waylandDisplay = getenv("WAYLAND_DISPLAY"); 514 | if (!waylandDisplay || waylandDisplay[0] == '\0') { 515 | ErrorPlain("WaylandProxy::SetupWaylandDisplays(), Missing Wayland display, WAYLAND_DISPLAY is empty.\n"); 516 | return false; 517 | } 518 | } 519 | 520 | char* XDGRuntimeDir = getenv("XDG_RUNTIME_DIR"); 521 | if (!XDGRuntimeDir) { 522 | ErrorPlain("WaylandProxy::SetupWaylandDisplays() Missing XDG_RUNTIME_DIR\n"); 523 | return false; 524 | } 525 | 526 | // WAYLAND_DISPLAY can be absolute path 527 | if (waylandDisplay[0] == '/') { 528 | if (strlen(mWaylandDisplay) >= sMaxDisplayNameLen) { 529 | ErrorPlain("WaylandProxy::SetupWaylandDisplays() WAYLAND_DISPLAY is too large.\n"); 530 | return false; 531 | } 532 | strcpy(mWaylandDisplay, waylandDisplay); 533 | } else { 534 | int ret = snprintf(mWaylandDisplay, sMaxDisplayNameLen, "%s/%s", 535 | XDGRuntimeDir, waylandDisplay); 536 | if (ret < 0 || ret >= sMaxDisplayNameLen) { 537 | ErrorPlain("WaylandProxy::SetupWaylandDisplays() WAYLAND_DISPLAY/XDG_RUNTIME_DIR is too large.\n"); 538 | return false; 539 | } 540 | } 541 | 542 | if (!CheckWaylandDisplay(mWaylandDisplay)) { 543 | return false; 544 | } 545 | 546 | int ret = snprintf(mWaylandProxy, sMaxDisplayNameLen, 547 | "%s/wayland-proxy-%d", XDGRuntimeDir, getpid()); 548 | if (ret < 0 || ret >= sMaxDisplayNameLen) { 549 | ErrorPlain("WaylandProxy::SetupWaylandDisplays() WAYLAND_DISPLAY/XDG_RUNTIME_DIR is too large.\n"); 550 | return false; 551 | } 552 | 553 | // Save original Wayland display variable for potential reuse 554 | setenv("WAYLAND_DISPLAY_COMPOSITOR", waylandDisplay, /* overwrite = */ true); 555 | 556 | Info("SetupWaylandDisplays() Wayland '%s' proxy '%s'\n", 557 | mWaylandDisplay, mWaylandProxy); 558 | return true; 559 | } 560 | 561 | bool WaylandProxy::StartProxyServer() { 562 | mProxyServerSocket = 563 | socket(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0); 564 | if (mProxyServerSocket == -1) { 565 | Error("StartProxyServer(): failed to create socket"); 566 | return false; 567 | } 568 | 569 | struct sockaddr_un serverName = {0}; 570 | serverName.sun_family = AF_UNIX; 571 | strcpy(serverName.sun_path, mWaylandProxy); 572 | 573 | if (bind(mProxyServerSocket, (struct sockaddr*)&serverName, 574 | sizeof(serverName)) == -1) { 575 | Error("StartProxyServer(): bind() error"); 576 | return false; 577 | } 578 | if (listen(mProxyServerSocket, 128) == -1) { 579 | Error("StartProxyServer(): listen() error"); 580 | return false; 581 | } 582 | 583 | return true; 584 | } 585 | 586 | bool WaylandProxy::Init() { 587 | Info("Init()\n"); 588 | 589 | if (!SetupWaylandDisplays()) { 590 | return false; 591 | } 592 | 593 | if (!StartProxyServer()) { 594 | return false; 595 | } 596 | 597 | Info("Init() finished\n"); 598 | return true; 599 | } 600 | 601 | void WaylandProxy::SetWaylandProxyDisplay() { 602 | Info("SetWaylandProxyDisplay() WAYLAND_DISPLAY %s\n", mWaylandDisplay); 603 | setenv("WAYLAND_DISPLAY", mWaylandProxy, /* overwrite = */ true); 604 | } 605 | 606 | void WaylandProxy::RestoreWaylandDisplay() { 607 | unlink(mWaylandProxy); 608 | char* waylandDisplay = getenv("WAYLAND_DISPLAY_COMPOSITOR"); 609 | if (waylandDisplay) { 610 | Info("RestoreWaylandDisplay() WAYLAND_DISPLAY restored to %s\n", 611 | waylandDisplay); 612 | setenv("WAYLAND_DISPLAY", waylandDisplay, /* overwrite = */ true); 613 | unsetenv("WAYLAND_DISPLAY_COMPOSITOR"); 614 | } 615 | } 616 | 617 | bool WaylandProxy::IsChildAppTerminated() { 618 | if (!mApplicationPID) { 619 | return false; 620 | } 621 | int status = 0; 622 | int ret = waitpid(mApplicationPID, &status, WNOHANG | WUNTRACED | WCONTINUED); 623 | if (ret == 0) { 624 | return false; 625 | } 626 | if (ret == mApplicationPID) { 627 | // Child application is terminated, so quit too. 628 | return true; 629 | } 630 | bool terminate = (errno == ECHILD); 631 | Error("IsChildAppTerminated: waitpid() error"); 632 | return terminate; 633 | } 634 | 635 | bool WaylandProxy::PollConnections() { 636 | int nfds_max = mConnections.size() * 2 + 1; 637 | 638 | struct pollfd pollfds[nfds_max]; 639 | struct pollfd* addedPollfd = pollfds; 640 | 641 | for (auto const& connection : mConnections) { 642 | addedPollfd = connection->AddToPollFd(addedPollfd); 643 | } 644 | int nfds = (addedPollfd - pollfds); 645 | 646 | // If all connections are attached to compositor, add another one 647 | // for new potential connection from application. 648 | bool addNewConnection = mConnections.empty() || 649 | mConnections.back()->IsConnected(); 650 | if (addNewConnection) { 651 | addedPollfd->fd = mProxyServerSocket; 652 | addedPollfd->events = POLLIN; 653 | nfds++; 654 | } 655 | assert(addedPollfd < pollfds + nfds_max); 656 | 657 | while (1) { 658 | int ret = poll(pollfds, nfds, POLL_TIMEOUT); 659 | if (ret == 0) { 660 | // No change on fds 661 | continue; 662 | } else if (ret > 0) { 663 | // We have FD to read 664 | break; 665 | } else if (ret == -1) { 666 | switch (errno) { 667 | case EINTR: 668 | case EAGAIN: 669 | if (IsChildAppTerminated()) { 670 | return false; 671 | } 672 | continue; 673 | default: 674 | Error("Run: poll() error"); 675 | return false; 676 | } 677 | } 678 | } 679 | 680 | struct pollfd* loadedPollfd = pollfds; 681 | for (auto const& connection : mConnections) { 682 | loadedPollfd = connection->LoadPollFd(loadedPollfd); 683 | } 684 | 685 | assert(loadedPollfd == addedPollfd); 686 | assert(loadedPollfd < pollfds + nfds_max); 687 | 688 | // Create a new connection if there's a new client waiting 689 | if (addNewConnection && (loadedPollfd->revents & POLLIN)) { 690 | Info("new child connection\n"); 691 | int applicationSocket = accept4(loadedPollfd->fd, nullptr, nullptr, 692 | SOCK_NONBLOCK | SOCK_CLOEXEC); 693 | if (applicationSocket == -1) { 694 | switch (errno) { 695 | case EAGAIN: 696 | case EINTR: 697 | // Try again later 698 | break; 699 | default: 700 | Error("Faild to accept connection from application"); 701 | return false; 702 | } 703 | } else { 704 | auto connection = std::make_unique(); 705 | if (connection->Init(applicationSocket, mWaylandDisplay)) { 706 | mConnections.push_back(std::move(connection)); 707 | } 708 | } 709 | } 710 | 711 | return true; 712 | } 713 | 714 | bool WaylandProxy::ProcessConnections() { 715 | std::vector>::iterator connection; 716 | for (connection = mConnections.begin(); connection != mConnections.end();) { 717 | if (!(*connection)->Process()) { 718 | Info("remove connection\n"); 719 | connection = mConnections.erase(connection); 720 | if (mConnections.empty()) { 721 | // We removed last connection - quit. 722 | Info("removed last connection, quit\n"); 723 | return false; 724 | } 725 | } else { 726 | connection++; 727 | } 728 | } 729 | return true; 730 | } 731 | 732 | void WaylandProxy::Run() { 733 | while (!IsChildAppTerminated() && PollConnections() && ProcessConnections()) 734 | ; 735 | } 736 | 737 | WaylandProxy::~WaylandProxy() { 738 | Info("terminated\n"); 739 | if (mThreadRunning) { 740 | Info("thread is still running, terminating.\n"); 741 | mThreadRunning = false; 742 | pthread_cancel(mThread); 743 | pthread_join(mThread, nullptr); 744 | } 745 | if (mProxyServerSocket != -1) { 746 | close(mProxyServerSocket); 747 | } 748 | RestoreWaylandDisplay(); 749 | } 750 | 751 | void* WaylandProxy::RunProxyThread(WaylandProxy* aProxy) { 752 | #if defined(__linux__) || defined(__FreeBSD__) 753 | pthread_setname_np(pthread_self(), "WaylandProxy"); 754 | #endif 755 | aProxy->Run(); 756 | Print("[%d] WaylandProxy [%p]: thread exited.\n", getpid(), aProxy); 757 | return nullptr; 758 | } 759 | 760 | std::unique_ptr WaylandProxy::Create() { 761 | auto proxy = std::make_unique(); 762 | Print("[%d] WaylandProxy [%p]: Created().\n", getpid(), proxy.get()); 763 | if (!proxy->Init()) { 764 | Print("[%d] WaylandProxy [%p]: Init failed, exiting.\n", getpid(), proxy.get()); 765 | return nullptr; 766 | } 767 | return proxy; 768 | } 769 | 770 | bool WaylandProxy::RunChildApplication(char* argv[]) { 771 | if (!argv[0]) { 772 | ErrorPlain("WaylandProxy::RunChildApplication: missing application to run\n"); 773 | return false; 774 | } 775 | 776 | mApplicationPID = fork(); 777 | if (mApplicationPID == -1) { 778 | Error("WaylandProxy::RunChildApplication: fork() error"); 779 | return false; 780 | } 781 | if (mApplicationPID == 0) { 782 | SetWaylandProxyDisplay(); 783 | if (execv(argv[0], argv) == -1) { 784 | ErrorPlain( 785 | "WaylandProxy::RunChildApplication: failed to run %s error %s\n", 786 | argv[0], strerror(errno)); 787 | exit(1); 788 | } 789 | } 790 | 791 | Run(); 792 | return true; 793 | } 794 | 795 | bool WaylandProxy::RunThread() { 796 | pthread_attr_t attr; 797 | if (pthread_attr_init(&attr) != 0) { 798 | ErrorPlain("WaylandProxy::RunThread(): pthread_attr_init() failed\n"); 799 | return false; 800 | } 801 | 802 | sched_param param; 803 | if (pthread_attr_getschedparam(&attr, ¶m) == 0) { 804 | param.sched_priority = sched_get_priority_min(SCHED_RR); 805 | pthread_attr_setschedparam(&attr, ¶m); 806 | } 807 | 808 | SetWaylandProxyDisplay(); 809 | 810 | mThreadRunning = pthread_create(&mThread, nullptr, (void* (*)(void*))RunProxyThread, this) == 0; 811 | if (!mThreadRunning) { 812 | ErrorPlain("WaylandProxy::RunThread(): pthread_create() failed\n"); 813 | // If we failed to run proxy thread, set WAYLAND_DISPLAY back. 814 | RestoreWaylandDisplay(); 815 | } 816 | 817 | pthread_attr_destroy(&attr); 818 | return mThreadRunning; 819 | } 820 | 821 | void WaylandProxy::SetVerbose(bool aVerbose) { sPrintInfo = aVerbose; } 822 | 823 | void WaylandProxy::Info(const char* aFormat, ...) { 824 | if (!sPrintInfo) { 825 | return; 826 | } 827 | fprintf(stderr,"[%d] WaylandProxy [%p]: ", getpid(), this); 828 | va_list args; 829 | va_start(args, aFormat); 830 | vfprintf(stderr, aFormat, args); 831 | va_end(args); 832 | } 833 | 834 | void WaylandProxy::Warning(const char* aOperation) { 835 | fprintf(stderr, "[%d] Wayland Proxy [%p] Warning: %s : %s\n", 836 | getpid(), this, aOperation, strerror(errno)); 837 | } 838 | 839 | void WaylandProxy::Error(const char* aOperation) { 840 | fprintf(stderr, "[%d] Wayland Proxy [%p] Error: %s : %s\n", 841 | getpid(), this, aOperation, strerror(errno)); 842 | } 843 | 844 | void WaylandProxy::ErrorPlain(const char* aFormat, ...) { 845 | fprintf(stderr, "[%d] Wayland Proxy [%p] Error: ", getpid(), this); 846 | va_list args; 847 | va_start(args, aFormat); 848 | vfprintf(stderr, aFormat, args); 849 | va_end(args); 850 | } 851 | -------------------------------------------------------------------------------- /src/wayland-proxy.h: -------------------------------------------------------------------------------- 1 | /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 5 | 6 | #ifndef _wayland_proxy_h_ 7 | #define _wayland_proxy_h_ 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | class ProxiedConnection; 16 | 17 | class WaylandProxy { 18 | public: 19 | static std::unique_ptr Create(); 20 | 21 | // Launch an application with Wayland proxy set 22 | bool RunChildApplication(char* argv[]); 23 | 24 | // Run proxy as part of already running application 25 | // and set Wayland proxy display for it. 26 | bool RunThread(); 27 | 28 | // Set original Wayland display env variable and clear 29 | // proxy display file. 30 | void RestoreWaylandDisplay(); 31 | 32 | static void SetVerbose(bool aVerbose); 33 | 34 | ~WaylandProxy(); 35 | 36 | private: 37 | bool Init(); 38 | void Run(); 39 | 40 | void SetWaylandProxyDisplay(); 41 | static void* RunProxyThread(WaylandProxy* aProxy); 42 | bool CheckWaylandDisplay(const char* aWaylandDisplay); 43 | 44 | bool SetupWaylandDisplays(); 45 | bool StartProxyServer(); 46 | bool IsChildAppTerminated(); 47 | 48 | bool PollConnections(); 49 | bool ProcessConnections(); 50 | 51 | void Info(const char* aFormat, ...); 52 | void Warning(const char* aOperation); 53 | void Error(const char* aOperation); 54 | void ErrorPlain(const char* aFormat, ...); 55 | 56 | private: 57 | // List of all Compositor <-> Application connections 58 | std::vector> mConnections; 59 | int mProxyServerSocket = -1; 60 | pid_t mApplicationPID = 0; 61 | std::atomic mThreadRunning = false; 62 | pthread_t mThread; 63 | 64 | // sockaddr_un has hardcoded max len of sun_path 65 | static constexpr int sMaxDisplayNameLen = 108; 66 | // Name of Wayland display provided by compositor 67 | char mWaylandDisplay[sMaxDisplayNameLen]; 68 | // Name of Wayland display provided by us 69 | char mWaylandProxy[sMaxDisplayNameLen]; 70 | }; 71 | 72 | #endif // _wayland_proxy_h_ 73 | --------------------------------------------------------------------------------