- Qt 5.3
- Qt Bluetooth
- Bluetooth QML Ping Pong example
- pingpong.cpp Example File
- Qt 5.3.2 Reference Documentation

# pingpong.cpp Example File

pingpong/pingpong.cpp

    /***************************************************************************
    **
    ** Copyright (C) 2014 BlackBerry Limited. All rights reserved.
    ** Contact: http://www.qt-project.org/legal
    **
    ** This file is part of the examples of the QtBluetooth module of the Qt Toolkit.
    **
    ** $QT_BEGIN_LICENSE:BSD$
    ** You may use this file under the terms of the BSD license as follows:
    **
    ** "Redistribution and use in source and binary forms, with or without
    ** modification, are permitted provided that the following conditions are
    ** met:
    **   * Redistributions of source code must retain the above copyright
    **     notice, this list of conditions and the following disclaimer.
    **   * Redistributions in binary form must reproduce the above copyright
    **     notice, this list of conditions and the following disclaimer in
    **     the documentation and/or other materials provided with the
    **     distribution.
    **   * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names
    **     of its contributors may be used to endorse or promote products derived
    **     from this software without specific prior written permission.
    **
    **
    ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
    ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
    ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
    ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
    ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
    ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
    ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
    ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
    ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
    ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
    ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
    **
    ** $QT_END_LICENSE$
    **
    ****************************************************************************/

    #include "pingpong.h"
    #include <QDebug>

    PingPong::PingPong():
        m_serverInfo(0), socket(0), discoveryAgent(0), interval(5), m_resultLeft(0), m_resultRight(0),
        m_showDialog(false), m_role(0), m_proportionX(0), m_proportionY(0), m_serviceFound(false)
    {
        m_timer = new QTimer(this);
        connect(m_timer, SIGNAL(timeout()), this, SLOT(update()));
    }

    PingPong::~PingPong()
    {
        delete m_timer;
        delete m_serverInfo;
        delete socket;
        delete discoveryAgent;
    }

    void PingPong::startGame()
    {
        m_showDialog = false;
        Q_EMIT showDialogChanged();
        if (m_role == 1)
            updateDirection();

        m_timer->start(50);
    }

    void PingPong::update()
    {
        QByteArray size;
        // Server is only updating the coordinates
        if (m_role == 1) {
            checkBoundaries();
            m_ballPreviousX = m_ballX;
            m_ballPreviousY = m_ballY;
            m_ballY = m_direction*(m_ballX+interval) - m_direction*m_ballX + m_ballY;
            m_ballX = m_ballX + interval;

            size.setNum(m_ballX);
            size.append(' ');
            QByteArray size1;
            size1.setNum(m_ballY);
            size.append(size1);
            size.append(' ');
            size1.setNum(m_leftBlockY);
            size.append(size1);
            size.append(" \n");
            socket->write(size.constData());
            Q_EMIT ballChanged();
        }
        else if (m_role == 2) {
            size.setNum(m_rightBlockY);
            size.append(" \n");
            socket->write(size.constData());
        }
    }

    void PingPong::setSize(const float &x, const float &y)
    {
        m_boardWidth = x;
        m_boardHeight = y;
        m_targetX = m_boardWidth;
        m_targetY = m_boardHeight/2;
        m_ballPreviousX = m_ballX = m_boardWidth/2;
        m_ballPreviousY = m_ballY = m_boardHeight - m_boardWidth/54;
        Q_EMIT ballChanged();
    }

    void PingPong::updateBall(const float &bX, const float &bY)
    {
        m_ballX = bX;
        m_ballY = bY;
    }

    void PingPong::updateLeftBlock(const float &lY)
    {
        m_leftBlockY = lY;
    }

    void PingPong::updateRightBlock(const float &rY)
    {
        m_rightBlockY = rY;
    }

    void PingPong::checkBoundaries()
    {
        float ballWidth = m_boardWidth/54;
        float blockSize = m_boardWidth/27;
        float blockHeight = m_boardHeight/5;
        if (((m_ballX + ballWidth) > (m_boardWidth - blockSize)) && ((m_ballY + ballWidth) < (m_rightBlockY + blockHeight))
                && (m_ballY > m_rightBlockY)) {
            m_targetY = 2 * m_ballY - m_ballPreviousY;
            m_targetX = m_ballPreviousX;
            interval = -5;
            updateDirection();
        }
        else if ((m_ballX < blockSize) && ((m_ballY + ballWidth) < (m_leftBlockY + blockHeight))
                 && (m_ballY > m_leftBlockY)) {
            m_targetY = 2 * m_ballY - m_ballPreviousY;
            m_targetX = m_ballPreviousX;
            interval = 5;
            updateDirection();
        }
        else if (m_ballY < 0 || (m_ballY + ballWidth > m_boardHeight)) {
            m_targetY = m_ballPreviousY;
            m_targetX = m_ballX + interval;
            updateDirection();
        }
        else if ((m_ballX + ballWidth) > m_boardWidth) {
            m_resultLeft++;
            m_targetX = m_boardWidth;
            m_targetY = m_boardHeight/2;
            m_ballPreviousX = m_ballX = m_boardWidth/2;
            m_ballPreviousY = m_ballY = m_boardHeight - m_boardWidth/54;

            updateDirection();
            checkResult();
            QByteArray result;
            result.append("result ");
            QByteArray res;
            res.setNum(m_resultLeft);
            result.append(res);
            result.append(' ');
            res.setNum(m_resultRight);
            result.append(res);
            result.append(" \n");
            socket->write(result);
            qDebug() << result;
            Q_EMIT resultChanged();
        }
        else if (m_ballX < 0) {
            m_resultRight++;
            m_targetX = 0;
            m_targetY = m_boardHeight/2;
            m_ballPreviousX = m_ballX = m_boardWidth/2;
            m_ballPreviousY = m_ballY = m_boardHeight - m_boardWidth/54;
            updateDirection();
            checkResult();
            QByteArray result;
            result.append("result ");
            QByteArray res;
            res.setNum(m_resultLeft);
            result.append(res);
            result.append(' ');
            res.setNum(m_resultRight);
            result.append(res);
            result.append(" \n");
            socket->write(result);
            Q_EMIT resultChanged();
        }
    }

    void PingPong::checkResult()
    {
        if (m_resultRight == 10 && m_role == 2) {
            setMessage("Game over. You win!");
            m_timer->stop();
        }
        else if (m_resultRight == 10 && m_role == 1) {
            setMessage("Game over. You lose!");
            m_timer->stop();
        }
        else if (m_resultLeft == 10 && m_role == 1) {
            setMessage("Game over. You win!");
            m_timer->stop();
        }
        else if (m_resultLeft == 10 && m_role == 2) {
            setMessage("Game over. You lose!");
            m_timer->stop();
        }
    }

    void PingPong::updateDirection()
    {
        m_direction = (m_targetY - m_ballY)/(m_targetX - m_ballX);
    }

    void PingPong::startServer()
    {
        setMessage(QStringLiteral("Starting the server"));
        m_serverInfo = new QBluetoothServer(QBluetoothServiceInfo::RfcommProtocol, this);
        connect(m_serverInfo, SIGNAL(newConnection()), this, SLOT(clientConnected()));
        connect(m_serverInfo, SIGNAL(error(QBluetoothServer::Error)),
                this, SLOT(serverError(QBluetoothServer::Error)));
        const QBluetoothUuid uuid(serviceUuid);

        m_serverInfo->listen(uuid, QStringLiteral("PingPong server"));
        setMessage(QStringLiteral("Server started, waiting for the client. You are the left player."));
        // m_role is set to 1 if it is a server
        m_role = 1;
        Q_EMIT roleChanged();
    }

    void PingPong::startClient()
    {
        discoveryAgent = new QBluetoothServiceDiscoveryAgent(QBluetoothAddress());

        connect(discoveryAgent, SIGNAL(serviceDiscovered(QBluetoothServiceInfo)),
                this, SLOT(addService(QBluetoothServiceInfo)));
        connect(discoveryAgent, SIGNAL(finished()), this, SLOT(done()));
        connect(discoveryAgent, SIGNAL(error(QBluetoothServiceDiscoveryAgent::Error)),
                this, SLOT(serviceScanError(QBluetoothServiceDiscoveryAgent::Error)));
        discoveryAgent->setUuidFilter(QBluetoothUuid(serviceUuid));
        discoveryAgent->start(QBluetoothServiceDiscoveryAgent::FullDiscovery);
        setMessage(QStringLiteral("Starting server discovery. You are the right player"));
        // m_role is set to 2 if it is a client
        m_role = 2;
        Q_EMIT roleChanged();
    }

    void PingPong::clientConnected()
    {
        if (!m_serverInfo->hasPendingConnections()) {
            setMessage("FAIL: expected pending server connection");
            return;
        }
        socket = m_serverInfo->nextPendingConnection();
        if (!socket)
            return;
        socket->setParent(this);
        connect(socket, SIGNAL(readyRead()), this, SLOT(readSocket()));
        connect(socket, SIGNAL(disconnected()), this, SLOT(clientDisconnected()));
        connect(socket, SIGNAL(error(QBluetoothSocket::SocketError)),
                this, SLOT(socketError(QBluetoothSocket::SocketError)));
        setMessage(QStringLiteral("Client connected."));

        QByteArray size;
        size.setNum(m_boardWidth);
        size.append(' ');
        QByteArray size1;
        size1.setNum(m_boardHeight);
        size.append(size1);
        size.append(" \n");
        socket->write(size.constData());

    }

    void PingPong::clientDisconnected()
    {
        setMessage(QStringLiteral("Client disconnected"));
        m_timer->stop();
    }

    void PingPong::socketError(QBluetoothSocket::SocketError error)
    {
        Q_UNUSED(error);
        m_timer->stop();
    }

    void PingPong::serverError(QBluetoothServer::Error error)
    {
        Q_UNUSED(error);
        m_timer->stop();
    }

    void PingPong::done()
    {
        qDebug() << "Service scan done";
        if (!m_serviceFound)
            setMessage("PingPong service not found");
    }

    void PingPong::addService(const QBluetoothServiceInfo &service)
    {
        setMessage("Service found. Setting parameters...");
        socket = new QBluetoothSocket(QBluetoothServiceInfo::L2capProtocol);
        socket->connectToService(service);

        connect(socket, SIGNAL(readyRead()), this, SLOT(readSocket()));
        connect(socket, SIGNAL(connected()), this, SLOT(serverConnected()));
        connect(socket, SIGNAL(disconnected()), this, SLOT(serverDisconnected()));
        m_serviceFound = true;
    }

    void PingPong::serviceScanError(QBluetoothServiceDiscoveryAgent::Error error)
    {
        setMessage(QStringLiteral("Scanning error") + error);
    }

    bool PingPong::showDialog() const
    {
        return m_showDialog;
    }

    QString PingPong::message() const
    {
        return m_message;
    }

    void PingPong::serverConnected()
    {
        setMessage("Server Connected");
        QByteArray size;
        size.setNum(m_boardWidth);
        size.append(' ');
        QByteArray size1;
        size1.setNum(m_boardHeight);
        size.append(size1);
        size.append(" \n");
        socket->write(size.constData());
    }

    void PingPong::serverDisconnected()
    {
        setMessage("Server Disconnected");
        m_timer->stop();
    }

    void PingPong::readSocket()
    {
        if (!socket)
            return;
        const char sep = ' ';
        QByteArray line;
        while (socket->canReadLine()) {
            line = socket->readLine();
            //qDebug() << QString::fromUtf8(line.constData(), line.length());
            if (line.contains("result")) {
                QList<QByteArray> result = line.split(sep);
                if (result.size() > 2) {
                    QByteArray leftSide = result.at(1);
                    QByteArray rightSide = result.at(2);
                    m_resultLeft = leftSide.toInt();
                    m_resultRight = rightSide.toInt();
                    Q_EMIT resultChanged();
                    checkResult();
                }
            }
        }
        if ((m_proportionX == 0 || m_proportionY == 0)) {
            QList<QByteArray> boardSize = line.split(sep);
            if (boardSize.size() > 1) {
                QByteArray boardWidth = boardSize.at(0);
                QByteArray boardHeight = boardSize.at(1);
                m_proportionX = m_boardWidth/boardWidth.toFloat();
                m_proportionY = m_boardHeight/boardHeight.toFloat();
                setMessage("Screen adjusted. Get ready!");
                QTimer::singleShot(3000, this, SLOT(startGame()));
            }
        }
        else if (m_role == 1) {
            QList<QByteArray> boardSize = line.split(sep);
            if (boardSize.size() > 1) {
                QByteArray rightBlockY = boardSize.at(0);
                m_rightBlockY = m_proportionY * rightBlockY.toFloat();
                Q_EMIT rightBlockChanged();
            }
        }
        else if (m_role == 2) {
            QList<QByteArray> boardSize = line.split(sep);
            if (boardSize.size() > 2) {
                QByteArray ballX = boardSize.at(0);
                QByteArray ballY = boardSize.at(1);
                QByteArray leftBlockY = boardSize.at(2);
                m_ballX = m_proportionX * ballX.toFloat();
                m_ballY = m_proportionY * ballY.toFloat();
                m_leftBlockY = m_proportionY * leftBlockY.toFloat();
                Q_EMIT leftBlockChanged();
                Q_EMIT ballChanged();
            }
        }
    }

    void PingPong::setMessage(const QString &message)
    {
        m_showDialog = true;
        m_message = message;
        Q_EMIT showDialogChanged();
    }

    int PingPong::role() const
    {
        return m_role;
    }

    int PingPong::leftResult() const
    {
        return m_resultLeft;
    }

    int PingPong::rightResult() const
    {
        return m_resultRight;
    }

    float PingPong::ballX() const
    {
        return m_ballX;
    }

    float PingPong::ballY() const
    {
        return m_ballY;
    }

    float PingPong::leftBlockY() const
    {
        return m_leftBlockY;
    }

    float PingPong::rightBlockY() const
    {
        return m_rightBlockY;
    }