TTKMusicPlayer  3.7.0.0
TTKMusicPlayer imitates Kugou UI, the music player uses of qmmp core library based on Qt for windows and linux
ttklocalpeer.cpp
Go to the documentation of this file.
1 #include "ttklocalpeer.h"
2 #include "ttklockedfile.h"
3 
4 #include <QCoreApplication>
5 #include <QDataStream>
6 #include <QTime>
7 #include <QLocalServer>
8 #include <QLocalSocket>
9 #include <QDir>
10 
11 #if TTK_QT_VERSION_CHECK(5,0,0)
12 # include <QRegularExpression>
13 #endif
14 
15 #if defined(Q_OS_WIN)
16 # include <QLibrary>
17 # define WIN32_LEAN_AND_MEAN
18 # include <qt_windows.h>
19 typedef BOOL (WINAPI *PProcessIdToSessionId)(DWORD, DWORD*);
20 static PProcessIdToSessionId pProcessIdToSessionId = 0;
21 #endif
22 #if defined(Q_OS_UNIX)
23 # include <sys/types.h>
24 # include <time.h>
25 # include <unistd.h>
26 #endif
27 
28 namespace TTKLockedPrivate {
29 #include "ttklockedfile.cpp"
30 #if defined(Q_OS_WIN)
31 # include "ttklockedfile_win.cpp"
32 #else
33 # include "ttklockedfile_unix.cpp"
34 #endif
35 }
36 
40 class TTKLocalPeerPrivate : public TTKPrivate<TTKLocalPeer>
41 {
42 public:
45 
46  QString m_id;
47  QString m_socketName;
48  QLocalServer *m_server;
50  static const char *m_ack;
51 };
52 const char *TTKLocalPeerPrivate::m_ack = "ack";
53 
55  : m_server(nullptr)
56 {
57 
58 }
59 
61 {
62  delete m_server;
63 }
64 
65 
66 
67 TTKLocalPeer::TTKLocalPeer(QObject *parent, const QString &id)
68  : QObject(parent)
69 {
72 
73  QString prefix = d->m_id = id;
74  if(prefix.isEmpty())
75  {
76  d->m_id = QCoreApplication::applicationFilePath();
77 #if defined(Q_OS_WIN)
78  d->m_id = d->m_id.toLower();
79 #endif
80  prefix = d->m_id.section(QLatin1Char('/'), -1);
81  }
82 #if TTK_QT_VERSION_CHECK(5,0,0)
83  prefix.remove(QRegularExpression("[^a-zA-Z]"));
84 #else
85  prefix.remove(QRegExp("[^a-zA-Z]"));
86 #endif
87  prefix.truncate(6);
88 
89  const QByteArray &idc = d->m_id.toUtf8();
90 #if TTK_QT_VERSION_CHECK(6,0,0)
91  quint16 idNum = qChecksum(QByteArrayView(idc.constData(), idc.length()));
92 #else
93  quint16 idNum = qChecksum(idc.constData(), idc.length());
94 #endif
95  d->m_socketName = QLatin1String("qtsingleapp-") + prefix + QLatin1Char('-') + QString::number(idNum, 16);
96 
97 #if defined(Q_OS_WIN)
98  if(!pProcessIdToSessionId)
99  {
100  QLibrary lib("kernel32");
101  pProcessIdToSessionId = (PProcessIdToSessionId)lib.resolve("ProcessIdToSessionId");
102  }
103  if(pProcessIdToSessionId)
104  {
105  DWORD sessionId = 0;
106  pProcessIdToSessionId(GetCurrentProcessId(), &sessionId);
107  d->m_socketName += QLatin1Char('-') + QString::number(sessionId, 16);
108  }
109 #else
110  d->m_socketName += QLatin1Char('-') + QString::number(::getuid(), 16);
111 #endif
112 
113  d->m_server = new QLocalServer(this);
114  const QString &lockName = QDir(QDir::tempPath()).absolutePath() + QLatin1Char('/') + d->m_socketName + QLatin1String("-lockfile");
115  d->m_lockFile.setFileName(lockName);
116  d->m_lockFile.open(QIODevice::ReadWrite);
117 }
118 
120 {
122  if(d->m_lockFile.isLocked())
123  {
124  return false;
125  }
126 
127  if(!d->m_lockFile.lock(TTKLockedPrivate::TTKLockedFile::WriteLock, false))
128  {
129  return true;
130  }
131 
132  bool res = d->m_server->listen(d->m_socketName);
133 #if defined(Q_OS_UNIX) && TTK_QT_VERSION_CHECK(4,5,0)
134  // ### Workaround
135  if(!res && d->m_server->serverError() == QAbstractSocket::AddressInUseError)
136  {
137  QFile::remove(QDir::cleanPath(QDir::tempPath()) + QLatin1Char('/') + d->m_socketName);
138  res = d->m_server->listen(d->m_socketName);
139  }
140 #endif
141  if(!res)
142  {
143  TTK_WARN_STREAM("Application: listen on local socket failed, " << qPrintable(d->m_server->errorString()));
144  }
145 
146  connect(d->m_server, SIGNAL(newConnection()), SLOT(receiveConnection()));
147  return false;
148 }
149 
150 bool TTKLocalPeer::sendMessage(const QString &message, int timeout) const
151 {
153  if(!isClient())
154  {
155  return false;
156  }
157 
158  QLocalSocket socket;
159  bool connOk = false;
160  for(int i = 0; i < 2; ++i)
161  {
162  // Try twice, in case the other instance is just starting up
163  socket.connectToServer(d->m_socketName);
164  connOk = socket.waitForConnected(timeout / 2);
165  if(connOk || i)
166  {
167  break;
168  }
169 
170  constexpr int ms = 250;
171 #if defined(Q_OS_WIN)
172  Sleep(DWORD(ms));
173 #else
174  struct timespec ts = { ms / 1000, (ms % 1000) * 1000 * 1000 };
175  nanosleep(&ts, nullptr);
176 #endif
177  }
178  if(!connOk)
179  {
180  return false;
181  }
182 
183  const QByteArray uMsg(message.toUtf8());
184  QDataStream ds(&socket);
185  ds.writeBytes(uMsg.constData(), uMsg.length());
186 
187  bool res = socket.waitForBytesWritten(timeout);
188  if(res)
189  {
190  res &= socket.waitForReadyRead(timeout); // wait for ack
191  if(res)
192  {
193  res &= (socket.read(qstrlen(d->m_ack)) == d->m_ack);
194  }
195  }
196  return res;
197 }
198 
200 {
202  return d->m_id;
203 }
204 
206 {
208  QLocalSocket *socket = d->m_server->nextPendingConnection();
209  if(!socket)
210  {
211  return;
212  }
213 
214  while(true)
215  {
216  if(socket->state() == QLocalSocket::UnconnectedState)
217  {
218  TTK_WARN_STREAM("QtLocalPeer: Peer disconnected");
219  delete socket;
220  return;
221  }
222 
223  if(socket->bytesAvailable() >= qint64(sizeof(quint32)))
224  {
225  break;
226  }
227  socket->waitForReadyRead();
228  }
229 
230  QDataStream ds(socket);
231  QByteArray uMsg;
232  quint32 remaining;
233  ds >> remaining;
234  uMsg.resize(remaining);
235 
236  int got = 0;
237  char *uMsgBuf = uMsg.data();
238  do
239  {
240  got = ds.readRawData(uMsgBuf, remaining);
241  remaining -= got;
242  uMsgBuf += got;
243  } while(remaining && got >= 0 && socket->waitForReadyRead(2000));
244 
245  if(got < 0)
246  {
247  TTK_WARN_STREAM("TTKLocalPeer: Message reception failed " << socket->errorString().toLatin1().constData());
248  delete socket;
249  return;
250  }
251 
252  const QString message(QString::fromUtf8(uMsg));
253  socket->write(d->m_ack, qstrlen(d->m_ack));
254  socket->waitForBytesWritten(1000);
255  socket->waitForDisconnected(1000); // make sure client reads ack
256  delete socket;
257 
258  Q_EMIT messageReceived(message); //### (might take a long time to return)
259 }
The namespace of locked private.
The class of the ttk local file.
Definition: ttklockedfile.h:35
TTKLocalPeer(QObject *parent=nullptr, const QString &id={})
void receiveConnection()
The class of the ttk local peer.
Definition: ttklocalpeer.h:29
The class of the ttk local peer private.
#define qPrintable(s)
Definition: ttkqtglobal.h:36
static const char * m_ack
#define TTK_WARN_STREAM(msg)
Definition: ttklogger.h:68
TTKLockedPrivate::TTKLockedFile m_lockFile
QLocalServer * m_server
void messageReceived(const QString &message)
bool isClient() const
QString applicationId() const
#define TTK_INIT_PRIVATE(Class)
Definition: ttkprivate.h:33
The class of the ttk private base.
Definition: ttkprivate.h:48
bool sendMessage(const QString &message, int timeout) const
#define TTK_D(Class)
Definition: ttkprivate.h:41