1/***************************************************************************
2**
3** Copyright (C) 2014 BlackBerry Limited. All rights reserved.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the examples of the QtBluetooth module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:BSD$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** BSD License Usage
18** Alternatively, you may use this file under the terms of the BSD license
19** as follows:
20**
21** "Redistribution and use in source and binary forms, with or without
22** modification, are permitted provided that the following conditions are
23** met:
24** * Redistributions of source code must retain the above copyright
25** notice, this list of conditions and the following disclaimer.
26** * Redistributions in binary form must reproduce the above copyright
27** notice, this list of conditions and the following disclaimer in
28** the documentation and/or other materials provided with the
29** distribution.
30** * Neither the name of The Qt Company Ltd nor the names of its
31** contributors may be used to endorse or promote products derived
32** from this software without specific prior written permission.
33**
34**
35** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
36** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
37** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
38** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
39** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
40** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
41** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
42** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
43** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
44** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
45** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
46**
47** $QT_END_LICENSE$
48**
49****************************************************************************/
50
51#include "pingpong.h"
52#include <QDebug>
53#ifdef Q_OS_ANDROID
54#include <QtAndroid>
55#endif
56
57PingPong::PingPong():
58 m_serverInfo(0), socket(0), discoveryAgent(0), interval(5), m_resultLeft(0), m_resultRight(0),
59 m_showDialog(false), m_role(0), m_proportionX(0), m_proportionY(0), m_serviceFound(false)
60{
61 m_timer = new QTimer(this);
62 connect(sender: m_timer, signal: &QTimer::timeout, receiver: this, slot: &PingPong::update);
63}
64
65PingPong::~PingPong()
66{
67 m_timer->stop();
68 delete socket;
69 delete discoveryAgent;
70}
71
72void PingPong::startGame()
73{
74 m_showDialog = false;
75 emit showDialogChanged();
76 //! [Start the game]
77 if (m_role == 1)
78 updateDirection();
79
80 m_timer->start(msec: 50);
81 //! [Start the game]
82}
83
84void PingPong::update()
85{
86 QByteArray size;
87 // Server is only updating the coordinates
88 //! [Updating coordinates]
89 if (m_role == 1) {
90 checkBoundaries();
91 m_ballPreviousX = m_ballX;
92 m_ballPreviousY = m_ballY;
93 m_ballY = m_direction*(m_ballX+interval) - m_direction*m_ballX + m_ballY;
94 m_ballX = m_ballX + interval;
95
96 size.setNum(n: m_ballX);
97 size.append(c: ' ');
98 QByteArray size1;
99 size1.setNum(n: m_ballY);
100 size.append(a: size1);
101 size.append(c: ' ');
102 size1.setNum(n: m_leftBlockY);
103 size.append(a: size1);
104 size.append(s: " \n");
105 socket->write(data: size.constData());
106 emit ballChanged();
107 }
108 else if (m_role == 2) {
109 size.setNum(n: m_rightBlockY);
110 size.append(s: " \n");
111 socket->write(data: size.constData());
112 }
113 //! [Updating coordinates]
114}
115
116
117
118void PingPong::setSize(const float &x, const float &y)
119{
120 m_boardWidth = x;
121 m_boardHeight = y;
122 m_targetX = m_boardWidth;
123 m_targetY = m_boardHeight/2;
124 m_ballPreviousX = m_ballX = m_boardWidth/2;
125 m_ballPreviousY = m_ballY = m_boardHeight - m_boardWidth/54;
126 emit ballChanged();
127}
128
129void PingPong::updateBall(const float &bX, const float &bY)
130{
131 m_ballX = bX;
132 m_ballY = bY;
133}
134
135void PingPong::updateLeftBlock(const float &lY)
136{
137 m_leftBlockY = lY;
138}
139
140void PingPong::updateRightBlock(const float &rY)
141{
142 m_rightBlockY = rY;
143}
144
145void PingPong::checkBoundaries()
146{
147 float ballWidth = m_boardWidth/54;
148 float blockSize = m_boardWidth/27;
149 float blockHeight = m_boardHeight/5;
150 //! [Checking the boundaries]
151 if (((m_ballX + ballWidth) > (m_boardWidth - blockSize)) && ((m_ballY + ballWidth) < (m_rightBlockY + blockHeight))
152 && (m_ballY > m_rightBlockY)) {
153 m_targetY = 2 * m_ballY - m_ballPreviousY;
154 m_targetX = m_ballPreviousX;
155 interval = -5;
156 updateDirection();
157 }
158 else if ((m_ballX < blockSize) && ((m_ballY + ballWidth) < (m_leftBlockY + blockHeight))
159 && (m_ballY > m_leftBlockY)) {
160 m_targetY = 2 * m_ballY - m_ballPreviousY;
161 m_targetX = m_ballPreviousX;
162 interval = 5;
163 updateDirection();
164 }
165 else if (m_ballY < 0 || (m_ballY + ballWidth > m_boardHeight)) {
166 m_targetY = m_ballPreviousY;
167 m_targetX = m_ballX + interval;
168 updateDirection();
169 }
170 //! [Checking the boundaries]
171 else if ((m_ballX + ballWidth) > m_boardWidth) {
172 m_resultLeft++;
173 m_targetX = m_boardWidth;
174 m_targetY = m_boardHeight/2;
175 m_ballPreviousX = m_ballX = m_boardWidth/2;
176 m_ballPreviousY = m_ballY = m_boardHeight - m_boardWidth/54;
177
178 updateDirection();
179 checkResult();
180 QByteArray result;
181 result.append(s: "result ");
182 QByteArray res;
183 res.setNum(n: m_resultLeft);
184 result.append(a: res);
185 result.append(c: ' ');
186 res.setNum(n: m_resultRight);
187 result.append(a: res);
188 result.append(s: " \n");
189 socket->write(data: result);
190 qDebug() << result;
191 emit resultChanged();
192 }
193 else if (m_ballX < 0) {
194 m_resultRight++;
195 m_targetX = 0;
196 m_targetY = m_boardHeight/2;
197 m_ballPreviousX = m_ballX = m_boardWidth/2;
198 m_ballPreviousY = m_ballY = m_boardHeight - m_boardWidth/54;
199 updateDirection();
200 checkResult();
201 QByteArray result;
202 result.append(s: "result ");
203 QByteArray res;
204 res.setNum(n: m_resultLeft);
205 result.append(a: res);
206 result.append(c: ' ');
207 res.setNum(n: m_resultRight);
208 result.append(a: res);
209 result.append(s: " \n");
210 socket->write(data: result);
211 emit resultChanged();
212 }
213}
214
215void PingPong::checkResult()
216{
217 if (m_resultRight == 10 && m_role == 2) {
218 setMessage("Game over. You win!");
219 m_timer->stop();
220 }
221 else if (m_resultRight == 10 && m_role == 1) {
222 setMessage("Game over. You lose!");
223 m_timer->stop();
224 }
225 else if (m_resultLeft == 10 && m_role == 1) {
226 setMessage("Game over. You win!");
227 m_timer->stop();
228 }
229 else if (m_resultLeft == 10 && m_role == 2) {
230 setMessage("Game over. You lose!");
231 m_timer->stop();
232 }
233}
234
235void PingPong::updateDirection()
236{
237 m_direction = (m_targetY - m_ballY)/(m_targetX - m_ballX);
238}
239
240void PingPong::startServer()
241{
242 setMessage(QStringLiteral("Starting the server"));
243 //! [Starting the server]
244 m_serverInfo = new QBluetoothServer(QBluetoothServiceInfo::RfcommProtocol, this);
245 connect(sender: m_serverInfo, signal: &QBluetoothServer::newConnection,
246 receiver: this, slot: &PingPong::clientConnected);
247 connect(sender: m_serverInfo, signal: QOverload<QBluetoothServer::Error>::of(ptr: &QBluetoothServer::error),
248 receiver: this, slot: &PingPong::serverError);
249 const QBluetoothUuid uuid(serviceUuid);
250
251 m_serviceInfo = m_serverInfo->listen(uuid, QStringLiteral("PingPong server"));
252 //! [Starting the server]
253 setMessage(QStringLiteral("Server started, waiting for the client. You are the left player."));
254 // m_role is set to 1 if it is a server
255 m_role = 1;
256 emit roleChanged();
257}
258
259void PingPong::startClient()
260{
261 //! [Searching for the service]
262 discoveryAgent = new QBluetoothServiceDiscoveryAgent(QBluetoothAddress());
263
264 connect(sender: discoveryAgent, signal: &QBluetoothServiceDiscoveryAgent::serviceDiscovered,
265 receiver: this, slot: &PingPong::addService);
266 connect(sender: discoveryAgent, signal: &QBluetoothServiceDiscoveryAgent::finished,
267 receiver: this, slot: &PingPong::done);
268 connect(sender: discoveryAgent, signal: QOverload<QBluetoothServiceDiscoveryAgent::Error>::of(ptr: &QBluetoothServiceDiscoveryAgent::error),
269 receiver: this, slot: &PingPong::serviceScanError);
270#ifdef Q_OS_ANDROID //see QTBUG-61392
271 if (QtAndroid::androidSdkVersion() >= 23)
272 discoveryAgent->setUuidFilter(QBluetoothUuid(androidUuid));
273 else
274 discoveryAgent->setUuidFilter(QBluetoothUuid(serviceUuid));
275#else
276 discoveryAgent->setUuidFilter(QBluetoothUuid(serviceUuid));
277#endif
278 discoveryAgent->start(mode: QBluetoothServiceDiscoveryAgent::FullDiscovery);
279
280 //! [Searching for the service]
281 setMessage(QStringLiteral("Starting server discovery. You are the right player"));
282 // m_role is set to 2 if it is a client
283 m_role = 2;
284 emit roleChanged();
285}
286
287void PingPong::clientConnected()
288{
289 //! [Initiating server socket]
290 if (!m_serverInfo->hasPendingConnections()) {
291 setMessage("FAIL: expected pending server connection");
292 return;
293 }
294 socket = m_serverInfo->nextPendingConnection();
295 if (!socket)
296 return;
297 socket->setParent(this);
298 connect(sender: socket, signal: &QBluetoothSocket::readyRead,
299 receiver: this, slot: &PingPong::readSocket);
300 connect(sender: socket, signal: &QBluetoothSocket::disconnected,
301 receiver: this, slot: &PingPong::clientDisconnected);
302 connect(sender: socket, signal: QOverload<QBluetoothSocket::SocketError>::of(ptr: &QBluetoothSocket::error),
303 receiver: this, slot: &PingPong::socketError);
304
305 //! [Initiating server socket]
306 setMessage(QStringLiteral("Client connected."));
307
308 QByteArray size;
309 size.setNum(n: m_boardWidth);
310 size.append(c: ' ');
311 QByteArray size1;
312 size1.setNum(n: m_boardHeight);
313 size.append(a: size1);
314 size.append(s: " \n");
315 socket->write(data: size.constData());
316
317}
318
319void PingPong::clientDisconnected()
320{
321 setMessage(QStringLiteral("Client disconnected"));
322 m_timer->stop();
323}
324
325void PingPong::socketError(QBluetoothSocket::SocketError error)
326{
327 Q_UNUSED(error);
328 m_timer->stop();
329}
330
331void PingPong::serverError(QBluetoothServer::Error error)
332{
333 Q_UNUSED(error);
334 m_timer->stop();
335}
336
337void PingPong::done()
338{
339 qDebug() << "Service scan done";
340 if (!m_serviceFound)
341 setMessage("PingPong service not found");
342}
343
344void PingPong::addService(const QBluetoothServiceInfo &service)
345{
346 if (m_serviceFound)
347 return;
348 m_serviceFound = true;
349
350 setMessage("Service found. Stopping discovery and creating connection...");
351 discoveryAgent->stop();
352 //! [Connecting the socket]
353 socket = new QBluetoothSocket(QBluetoothServiceInfo::RfcommProtocol);
354 socket->connectToService(service);
355
356 connect(sender: socket, signal: &QBluetoothSocket::readyRead, receiver: this, slot: &PingPong::readSocket);
357 connect(sender: socket, signal: &QBluetoothSocket::connected, receiver: this, slot: &PingPong::serverConnected);
358 connect(sender: socket, signal: &QBluetoothSocket::disconnected, receiver: this, slot: &PingPong::serverDisconnected);
359 //! [Connecting the socket]
360}
361
362void PingPong::serviceScanError(QBluetoothServiceDiscoveryAgent::Error error)
363{
364 setMessage(QStringLiteral("Scanning error") + error);
365}
366
367bool PingPong::showDialog() const
368{
369 return m_showDialog;
370}
371
372QString PingPong::message() const
373{
374 return m_message;
375}
376
377void PingPong::serverConnected()
378{
379 setMessage("Server Connected");
380 QByteArray size;
381 size.setNum(n: m_boardWidth);
382 size.append(c: ' ');
383 QByteArray size1;
384 size1.setNum(n: m_boardHeight);
385 size.append(a: size1);
386 size.append(s: " \n");
387 socket->write(data: size.constData());
388}
389
390void PingPong::serverDisconnected()
391{
392 setMessage("Server Disconnected");
393 m_timer->stop();
394}
395
396void PingPong::readSocket()
397{
398 if (!socket)
399 return;
400 const char sep = ' ';
401 QByteArray line;
402 while (socket->canReadLine()) {
403 line = socket->readLine();
404 //qDebug() << QString::fromUtf8(line.constData(), line.length());
405 if (line.contains(c: "result")) {
406 QList<QByteArray> result = line.split(sep);
407 if (result.size() > 2) {
408 QByteArray leftSide = result.at(i: 1);
409 QByteArray rightSide = result.at(i: 2);
410 m_resultLeft = leftSide.toInt();
411 m_resultRight = rightSide.toInt();
412 emit resultChanged();
413 checkResult();
414 }
415 }
416 }
417 if ((m_proportionX == 0 || m_proportionY == 0)) {
418 QList<QByteArray> boardSize = line.split(sep);
419 if (boardSize.size() > 1) {
420 QByteArray boardWidth = boardSize.at(i: 0);
421 QByteArray boardHeight = boardSize.at(i: 1);
422 m_proportionX = m_boardWidth/boardWidth.toFloat();
423 m_proportionY = m_boardHeight/boardHeight.toFloat();
424 setMessage("Screen adjusted. Get ready!");
425 QTimer::singleShot(msec: 3000, receiver: this, SLOT(startGame()));
426 }
427 }
428 else if (m_role == 1) {
429 QList<QByteArray> boardSize = line.split(sep);
430 if (boardSize.size() > 1) {
431 QByteArray rightBlockY = boardSize.at(i: 0);
432 m_rightBlockY = m_proportionY * rightBlockY.toFloat();
433 emit rightBlockChanged();
434 }
435 }
436 else if (m_role == 2) {
437 QList<QByteArray> boardSize = line.split(sep);
438 if (boardSize.size() > 2) {
439 QByteArray ballX = boardSize.at(i: 0);
440 QByteArray ballY = boardSize.at(i: 1);
441 QByteArray leftBlockY = boardSize.at(i: 2);
442 m_ballX = m_proportionX * ballX.toFloat();
443 m_ballY = m_proportionY * ballY.toFloat();
444 m_leftBlockY = m_proportionY * leftBlockY.toFloat();
445 emit leftBlockChanged();
446 emit ballChanged();
447 }
448 }
449}
450
451void PingPong::setMessage(const QString &message)
452{
453 m_showDialog = true;
454 m_message = message;
455 emit showDialogChanged();
456}
457
458int PingPong::role() const
459{
460 return m_role;
461}
462
463int PingPong::leftResult() const
464{
465 return m_resultLeft;
466}
467
468int PingPong::rightResult() const
469{
470 return m_resultRight;
471}
472
473float PingPong::ballX() const
474{
475 return m_ballX;
476}
477
478float PingPong::ballY() const
479{
480 return m_ballY;
481}
482
483
484float PingPong::leftBlockY() const
485{
486 return m_leftBlockY;
487}
488
489float PingPong::rightBlockY() const
490{
491 return m_rightBlockY;
492}
493

source code of qtconnectivity/examples/bluetooth/pingpong/pingpong.cpp