File indexing completed on 2025-02-16 05:09:07

0001 /****************************************************************************
0002 **
0003 ** Copyright (C) 2018 The Qt Company Ltd.
0004 ** Contact: https://www.qt.io/licensing/
0005 **
0006 ** This file is part of the test suite of the Qt Toolkit.
0007 **
0008 ** $QT_BEGIN_LICENSE:GPL-EXCEPT$
0009 ** Commercial License Usage
0010 ** Licensees holding valid commercial Qt licenses may use this file in
0011 ** accordance with the commercial license agreement provided with the
0012 ** Software or, alternatively, in accordance with the terms contained in
0013 ** a written agreement between you and The Qt Company. For licensing terms
0014 ** and conditions see https://www.qt.io/terms-conditions. For further
0015 ** information use the contact form at https://www.qt.io/contact-us.
0016 **
0017 ** GNU General Public License Usage
0018 ** Alternatively, this file may be used under the terms of the GNU
0019 ** General Public License version 3 as published by the Free Software
0020 ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
0021 ** included in the packaging of this file. Please review the following
0022 ** information to ensure the GNU General Public License requirements will
0023 ** be met: https://www.gnu.org/licenses/gpl-3.0.html.
0024 **
0025 ** $QT_END_LICENSE$
0026 **
0027 ****************************************************************************/
0028 
0029 #include "corecompositor.h"
0030 #include <thread>
0031 
0032 namespace MockCompositor
0033 {
0034 CoreCompositor::CoreCompositor()
0035     : m_display(wl_display_create())
0036     , m_socketName(wl_display_add_socket_auto(m_display))
0037     , m_eventLoop(wl_display_get_event_loop(m_display))
0038 
0039     // Start dispatching
0040     , m_dispatchThread([this]() {
0041         while (m_running) {
0042             std::this_thread::sleep_for(std::chrono::milliseconds(20));
0043             dispatch();
0044         }
0045     })
0046 {
0047     qputenv("WAYLAND_DISPLAY", m_socketName);
0048     m_timer.start();
0049     Q_ASSERT(isClean());
0050 }
0051 
0052 CoreCompositor::~CoreCompositor()
0053 {
0054     auto global = m_globals.begin();
0055     while (global != m_globals.end()) {
0056         delete *global;
0057         global = m_globals.erase(global);
0058     }
0059 
0060     wl_display_destroy_clients(m_display);
0061 
0062     m_running = false;
0063     m_dispatchThread.join();
0064     wl_display_destroy(m_display);
0065 }
0066 
0067 bool CoreCompositor::isClean()
0068 {
0069     Lock lock(this);
0070     for (auto *global : std::as_const(m_globals)) {
0071         if (!global->isClean())
0072             return false;
0073     }
0074     return true;
0075 }
0076 
0077 QString CoreCompositor::dirtyMessage()
0078 {
0079     Lock lock(this);
0080     QStringList messages;
0081     for (auto *global : std::as_const(m_globals)) {
0082         if (!global->isClean())
0083             messages << (global->metaObject()->className() % QLatin1String(": ") % global->dirtyMessage());
0084     }
0085     return messages.join(", ");
0086 }
0087 
0088 void CoreCompositor::dispatch()
0089 {
0090     Lock lock(this);
0091     wl_display_flush_clients(m_display);
0092     constexpr int timeout = 0; // immediate return
0093     wl_event_loop_dispatch(m_eventLoop, timeout);
0094 }
0095 
0096 /*!
0097  * \brief Adds a new global interface for the compositor
0098  *
0099  * Takes ownership of \a global
0100  */
0101 void CoreCompositor::add(Global *global)
0102 {
0103     warnIfNotLockedByThread(Q_FUNC_INFO);
0104     m_globals.append(global);
0105 }
0106 
0107 void CoreCompositor::remove(Global *global)
0108 {
0109     warnIfNotLockedByThread(Q_FUNC_INFO);
0110     m_globals.removeAll(global);
0111     delete global;
0112 }
0113 
0114 uint CoreCompositor::nextSerial()
0115 {
0116     warnIfNotLockedByThread(Q_FUNC_INFO);
0117     return wl_display_next_serial(m_display);
0118 }
0119 
0120 uint CoreCompositor::currentTimeMilliseconds()
0121 {
0122     warnIfNotLockedByThread(Q_FUNC_INFO);
0123     return uint(m_timer.elapsed());
0124 }
0125 
0126 wl_client *CoreCompositor::client(int index)
0127 {
0128     warnIfNotLockedByThread(Q_FUNC_INFO);
0129     wl_list *clients = wl_display_get_client_list(m_display);
0130     wl_client *client = nullptr;
0131     int i = 0;
0132     wl_client_for_each(client, clients)
0133     {
0134         if (i++ == index)
0135             return client;
0136     }
0137     return nullptr;
0138 }
0139 
0140 void CoreCompositor::warnIfNotLockedByThread(const char *caller)
0141 {
0142     if (!m_lock || !m_lock->isOwnedByCurrentThread()) {
0143         qWarning() << caller << "called without locking the compositor to the current thread."
0144                    << "This means the compositor can start dispatching at any moment,"
0145                    << "potentially leading to threading issues."
0146                    << "Unless you know what you are doing you should probably fix the test"
0147                    << "by locking the compositor before accessing it (see mutex()).";
0148     }
0149 }
0150 
0151 } // namespace MockCompositor