/[svn]/qsampler/trunk/src/qsampler.cpp
ViewVC logotype

Contents of /qsampler/trunk/src/qsampler.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 4040 - (show annotations) (download)
Sun Jul 10 09:22:44 2022 UTC (4 weeks, 4 days ago) by capela
File size: 16349 byte(s)
- Add current system user-name to the singleton/unique application
  instance identifier. (EXPERIMENTAL)
1 // qsampler.cpp
2 //
3 /****************************************************************************
4 Copyright (C) 2004-2022, rncbc aka Rui Nuno Capela. All rights reserved.
5 Copyright (C) 2007,2008,2015,2019 Christian Schoenebeck
6
7 This program is free software; you can redistribute it and/or
8 modify it under the terms of the GNU General Public License
9 as published by the Free Software Foundation; either version 2
10 of the License, or (at your option) any later version.
11
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License along
18 with this program; if not, write to the Free Software Foundation, Inc.,
19 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20
21 *****************************************************************************/
22
23 #include "qsampler.h"
24
25 #include "qsamplerOptions.h"
26 #include "qsamplerMainForm.h"
27
28 #include "qsamplerPaletteForm.h"
29
30 #include <QDir>
31
32 #include <QStyleFactory>
33
34 #include <QLibraryInfo>
35 #include <QTranslator>
36 #include <QLocale>
37
38 #if QT_VERSION >= QT_VERSION_CHECK(5, 2, 0)
39 #include <lscp/client.h>
40 #ifdef CONFIG_LIBGIG
41 #if defined(Q_CC_GNU) || defined(Q_CC_MINGW)
42 #pragma GCC diagnostic push
43 #pragma GCC diagnostic ignored "-Wunused-parameter"
44 #endif
45 #include <gig.h>
46 #if defined(Q_CC_GNU) || defined(Q_CC_MINGW)
47 #pragma GCC diagnostic pop
48 #endif
49 #endif
50 #endif
51
52 #if defined(__APPLE__) // Toshi Nagata 20080105
53 #include <QDir>
54 #endif
55
56 #ifndef CONFIG_PREFIX
57 #define CONFIG_PREFIX "/usr/local"
58 #endif
59
60 #ifndef CONFIG_BINDIR
61 #define CONFIG_BINDIR CONFIG_PREFIX "/bin"
62 #endif
63
64 #ifndef CONFIG_DATADIR
65 #define CONFIG_DATADIR CONFIG_PREFIX "/share"
66 #endif
67
68 #ifndef CONFIG_LIBDIR
69 #if defined(__x86_64__)
70 #define CONFIG_LIBDIR CONFIG_PREFIX "/lib64"
71 #else
72 #define CONFIG_LIBDIR CONFIG_PREFIX "/lib"
73 #endif
74 #endif
75
76 #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
77 #define CONFIG_PLUGINSDIR CONFIG_LIBDIR "/qt4/plugins"
78 #elif QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
79 #define CONFIG_PLUGINSDIR CONFIG_LIBDIR "/qt5/plugins"
80 #else
81 #define CONFIG_PLUGINSDIR CONFIG_LIBDIR "/qt6/plugins"
82 #endif
83
84 #if defined(__WIN32__) || defined(_WIN32) || defined(WIN32)
85 #define RELATIVE_LOCALE_DIR "/share/locale"
86 #elif defined(__APPLE__)
87 #define RELATIVE_LOCALE_DIR "/../Resources"
88 #endif
89
90
91 //-------------------------------------------------------------------------
92 // Singleton application instance stuff (Qt/X11 only atm.)
93 //
94
95 #ifdef CONFIG_XUNIQUE
96
97 #define QSAMPLER_XUNIQUE "qsamplerApplication"
98
99 #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
100 #ifdef CONFIG_X11
101
102 #include <unistd.h> /* for gethostname() */
103
104 #include <X11/Xatom.h>
105 #include <X11/Xlib.h>
106
107 #endif // CONFIG_X11
108 #else
109 #include <QSharedMemory>
110 #include <QLocalServer>
111 #include <QLocalSocket>
112 #include <QHostInfo>
113 #endif
114
115 #endif // CONFIG_XUNIQUE
116
117
118 // Constructor.
119 qsamplerApplication::qsamplerApplication ( int& argc, char **argv )
120 : QApplication(argc, argv),
121 m_pQtTranslator(nullptr), m_pMyTranslator(nullptr), m_pWidget(nullptr)
122 {
123 #if QT_VERSION >= QT_VERSION_CHECK(5, 1, 0)
124 QApplication::setApplicationName(QSAMPLER_TITLE);
125 QApplication::setApplicationDisplayName(QSAMPLER_TITLE);
126 // QSAMPLER_TITLE " - " + QObject::tr(QSAMPLER_SUBTITLE));
127 #if QT_VERSION >= QT_VERSION_CHECK(5, 7, 0)
128 QApplication::setDesktopFileName(
129 QString("org.rncbc.%1").arg(PACKAGE_TARNAME));
130 #endif
131 QString sVersion(CONFIG_BUILD_VERSION);
132 sVersion += '\n';
133 sVersion += QString("Qt: %1").arg(qVersion());
134 #if defined(QT_STATIC)
135 sVersion += "-static";
136 #endif
137 sVersion += '\n';
138 #ifdef CONFIG_LIBGIG
139 sVersion += QString("%1: %2")
140 .arg(gig::libraryName().c_str())
141 .arg(gig::libraryVersion().c_str());
142 sVersion += '\n';
143 #endif
144 sVersion += QString("%1: %2")
145 .arg(::lscp_client_package())
146 .arg(::lscp_client_version());
147 QApplication::setApplicationVersion(sVersion);
148 #endif
149 // Load translation support.
150 QLocale loc;
151 if (loc.language() != QLocale::C) {
152 // Try own Qt translation...
153 m_pQtTranslator = new QTranslator(this);
154 QString sLocName = "qt_" + loc.name();
155 #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
156 QString sLocPath = QLibraryInfo::path(QLibraryInfo::TranslationsPath);
157 #else
158 QString sLocPath = QLibraryInfo::location(QLibraryInfo::TranslationsPath);
159 #endif
160 if (m_pQtTranslator->load(sLocName, sLocPath)) {
161 QApplication::installTranslator(m_pQtTranslator);
162 } else {
163 sLocPath = QApplication::applicationDirPath();
164 #ifdef RELATIVE_LOCALE_DIR
165 sLocPath.append(RELATIVE_LOCALE_DIR);
166 #else
167 sLocPath.remove(CONFIG_BINDIR);
168 sLocPath.append(CONFIG_DATADIR "/qsampler/translations");
169 #endif
170 if (m_pQtTranslator->load(sLocName, sLocPath)) {
171 QApplication::installTranslator(m_pQtTranslator);
172 } else {
173 delete m_pQtTranslator;
174 m_pQtTranslator = nullptr;
175 #ifdef CONFIG_DEBUG
176 qWarning("Warning: no translation found for '%s' locale: %s/%s.qm",
177 loc.name().toUtf8().constData(),
178 sLocPath.toUtf8().constData(),
179 sLocName.toUtf8().constData());
180 #endif
181 }
182 }
183 // Try own application translation...
184 m_pMyTranslator = new QTranslator(this);
185 sLocName = "qsampler_" + loc.name();
186 if (m_pMyTranslator->load(sLocName, sLocPath)) {
187 QApplication::installTranslator(m_pMyTranslator);
188 } else {
189 #ifdef RELATIVE_LOCALE_DIR
190 sLocPath = QApplication::applicationDirPath() + RELATIVE_LOCALE_DIR;
191 #else
192 sLocPath = CONFIG_DATADIR "/qsampler/translations";
193 #endif
194 if (m_pMyTranslator->load(sLocName, sLocPath)) {
195 QApplication::installTranslator(m_pMyTranslator);
196 } else {
197 delete m_pMyTranslator;
198 m_pMyTranslator = nullptr;
199 #ifdef CONFIG_DEBUG
200 qWarning("Warning: no translation found for '%s' locale: %s/%s.qm",
201 loc.name().toUtf8().constData(),
202 sLocPath.toUtf8().constData(),
203 sLocName.toUtf8().constData());
204 #endif
205 }
206 }
207 }
208 #ifdef CONFIG_XUNIQUE
209 #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
210 #ifdef CONFIG_X11
211 m_pDisplay = nullptr;
212 m_aUnique = 0;
213 m_wOwner = 0;
214 #endif // CONFIG_X11
215 #else
216 m_pMemory = nullptr;
217 m_pServer = nullptr;
218 #endif
219 #endif // CONFIG_XUNIQUE
220 }
221
222
223 // Destructor.
224 qsamplerApplication::~qsamplerApplication (void)
225 {
226 #ifdef CONFIG_XUNIQUE
227 #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
228 if (m_pServer) {
229 m_pServer->close();
230 delete m_pServer;
231 m_pServer = nullptr;
232 }
233 if (m_pMemory) {
234 delete m_pMemory;
235 m_pMemory = nullptr;
236 }
237 #endif
238 #endif // CONFIG_XUNIQUE
239 if (m_pMyTranslator) delete m_pMyTranslator;
240 if (m_pQtTranslator) delete m_pQtTranslator;
241 }
242
243
244 // Main application widget accessors.
245 void qsamplerApplication::setMainWidget ( QWidget *pWidget )
246 {
247 m_pWidget = pWidget;
248 #ifdef CONFIG_XUNIQUE
249 #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
250 #ifdef CONFIG_X11
251 m_wOwner = m_pWidget->winId();
252 if (m_pDisplay && m_wOwner) {
253 XGrabServer(m_pDisplay);
254 XSetSelectionOwner(m_pDisplay, m_aUnique, m_wOwner, CurrentTime);
255 XUngrabServer(m_pDisplay);
256 }
257 #endif // CONFIG_X11
258 #endif
259 #endif // CONFIG_XUNIQUE
260 }
261
262
263 // Check if another instance is running,
264 // and raise its proper main widget...
265 bool qsamplerApplication::setup (void)
266 {
267 #ifdef CONFIG_XUNIQUE
268 #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
269 #ifdef CONFIG_X11
270 m_pDisplay = QX11Info::display();
271 if (m_pDisplay) {
272 QString sUnique = QSAMPLER_XUNIQUE;
273 QString sUserName = QString::fromUtf8(::getenv("USER"));
274 if (sUserName.isEmpty())
275 sUserName = QString::fromUtf8(::getenv("USERNAME"));
276 if (!sUserName.isEmpty()) {
277 sUnique += ':';
278 sUnique += sUserName;
279 }
280 char szHostName[255];
281 if (::gethostname(szHostName, sizeof(szHostName)) == 0) {
282 sUnique += '@';
283 sUnique += QString::fromUtf8(szHostName);
284 }
285 m_aUnique = XInternAtom(m_pDisplay, sUnique.toUtf8().constData(), false);
286 XGrabServer(m_pDisplay);
287 m_wOwner = XGetSelectionOwner(m_pDisplay, m_aUnique);
288 XUngrabServer(m_pDisplay);
289 if (m_wOwner != None) {
290 // First, notify any freedesktop.org WM
291 // that we're about to show the main widget...
292 Screen *pScreen = XDefaultScreenOfDisplay(m_pDisplay);
293 int iScreen = XScreenNumberOfScreen(pScreen);
294 XEvent ev;
295 memset(&ev, 0, sizeof(ev));
296 ev.xclient.type = ClientMessage;
297 ev.xclient.display = m_pDisplay;
298 ev.xclient.window = m_wOwner;
299 ev.xclient.message_type = XInternAtom(m_pDisplay, "_NET_ACTIVE_WINDOW", false);
300 ev.xclient.format = 32;
301 ev.xclient.data.l[0] = 0; // Source indication.
302 ev.xclient.data.l[1] = 0; // Timestamp.
303 ev.xclient.data.l[2] = 0; // Requestor's currently active window (none)
304 ev.xclient.data.l[3] = 0;
305 ev.xclient.data.l[4] = 0;
306 XSelectInput(m_pDisplay, m_wOwner, StructureNotifyMask);
307 XSendEvent(m_pDisplay, RootWindow(m_pDisplay, iScreen), false,
308 (SubstructureNotifyMask | SubstructureRedirectMask), &ev);
309 XSync(m_pDisplay, false);
310 XRaiseWindow(m_pDisplay, m_wOwner);
311 // And then, let it get caught on destination
312 // by QApplication::native/x11EventFilter...
313 const QByteArray value = QSAMPLER_XUNIQUE;
314 XChangeProperty(
315 m_pDisplay,
316 m_wOwner,
317 m_aUnique,
318 m_aUnique, 8,
319 PropModeReplace,
320 (unsigned char *) value.data(),
321 value.length());
322 // Done.
323 return true;
324 }
325 }
326 #endif // CONFIG_X11
327 return false;
328 #else
329 m_sUnique = QCoreApplication::applicationName();
330 QString sUserName = QString::fromUtf8(::getenv("USER"));
331 if (sUserName.isEmpty())
332 sUserName = QString::fromUtf8(::getenv("USERNAME"));
333 if (!sUserName.isEmpty()) {
334 m_sUnique += ':';
335 m_sUnique += sUserName;
336 }
337 m_sUnique += '@';
338 m_sUnique += QHostInfo::localHostName();
339 #ifdef Q_OS_UNIX
340 m_pMemory = new QSharedMemory(m_sUnique);
341 m_pMemory->attach();
342 delete m_pMemory;
343 #endif
344 m_pMemory = new QSharedMemory(m_sUnique);
345 bool bServer = false;
346 const qint64 pid = QCoreApplication::applicationPid();
347 struct Data { qint64 pid; };
348 if (m_pMemory->create(sizeof(Data))) {
349 m_pMemory->lock();
350 Data *pData = static_cast<Data *> (m_pMemory->data());
351 if (pData) {
352 pData->pid = pid;
353 bServer = true;
354 }
355 m_pMemory->unlock();
356 }
357 else
358 if (m_pMemory->attach()) {
359 m_pMemory->lock(); // maybe not necessary?
360 Data *pData = static_cast<Data *> (m_pMemory->data());
361 if (pData)
362 bServer = (pData->pid == pid);
363 m_pMemory->unlock();
364 }
365 if (bServer) {
366 QLocalServer::removeServer(m_sUnique);
367 m_pServer = new QLocalServer();
368 m_pServer->setSocketOptions(QLocalServer::UserAccessOption);
369 m_pServer->listen(m_sUnique);
370 QObject::connect(m_pServer,
371 SIGNAL(newConnection()),
372 SLOT(newConnectionSlot()));
373 } else {
374 QLocalSocket socket;
375 socket.connectToServer(m_sUnique);
376 if (socket.state() == QLocalSocket::ConnectingState)
377 socket.waitForConnected(200);
378 if (socket.state() == QLocalSocket::ConnectedState) {
379 socket.write(QCoreApplication::arguments().join(' ').toUtf8());
380 socket.flush();
381 socket.waitForBytesWritten(200);
382 }
383 }
384 return !bServer;
385 #endif
386 #else
387 return false;
388 #endif // !CONFIG_XUNIQUE
389 }
390
391
392 #ifdef CONFIG_XUNIQUE
393 #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
394 #ifdef CONFIG_X11
395
396 void qsamplerApplication::x11PropertyNotify ( Window w )
397 {
398 if (m_pDisplay && m_pWidget && m_wOwner == w) {
399 // Always check whether our property-flag is still around...
400 Atom aType;
401 int iFormat = 0;
402 unsigned long iItems = 0;
403 unsigned long iAfter = 0;
404 unsigned char *pData = 0;
405 if (XGetWindowProperty(
406 m_pDisplay,
407 m_wOwner,
408 m_aUnique,
409 0, 1024,
410 false,
411 m_aUnique,
412 &aType,
413 &iFormat,
414 &iItems,
415 &iAfter,
416 &pData) == Success
417 && aType == m_aUnique && iItems > 0 && iAfter == 0) {
418 // Avoid repeating it-self...
419 XDeleteProperty(m_pDisplay, m_wOwner, m_aUnique);
420 // Just make it always shows up fine...
421 m_pWidget->show();
422 m_pWidget->raise();
423 m_pWidget->activateWindow();
424 }
425 // Free any left-overs...
426 if (iItems > 0 && pData)
427 XFree(pData);
428 }
429 }
430
431
432 bool qsamplerApplication::x11EventFilter ( XEvent *pEv )
433 {
434 if (pEv->type == PropertyNotify
435 && pEv->xproperty.state == PropertyNewValue)
436 x11PropertyNotify(pEv->xproperty.window);
437 return QApplication::x11EventFilter(pEv);
438 }
439
440 #endif // CONFIG_X11
441 #else
442
443 // Local server conection slot.
444 void qsamplerApplication::newConnectionSlot (void)
445 {
446 QLocalSocket *pSocket = m_pServer->nextPendingConnection();
447 QObject::connect(pSocket,
448 SIGNAL(readyRead()),
449 SLOT(readyReadSlot()));
450 }
451
452 // Local server data-ready slot.
453 void qsamplerApplication::readyReadSlot (void)
454 {
455 QLocalSocket *pSocket = qobject_cast<QLocalSocket *> (sender());
456 if (pSocket) {
457 const qint64 nread = pSocket->bytesAvailable();
458 if (nread > 0) {
459 const QByteArray data = pSocket->read(nread);
460 // Just make it always shows up fine...
461 m_pWidget->hide();
462 m_pWidget->show();
463 m_pWidget->raise();
464 m_pWidget->activateWindow();
465 }
466 }
467 }
468
469 #endif
470 #endif // CONFIG_XUNIQUE
471
472
473 //-------------------------------------------------------------------------
474 // stacktrace - Signal crash handler.
475 //
476
477 #ifdef CONFIG_STACKTRACE
478 #if defined(__GNUC__) && defined(Q_OS_LINUX)
479
480 #include <stdio.h>
481 #include <errno.h>
482 #include <signal.h>
483 #include <unistd.h>
484 #include <sys/wait.h>
485
486 void stacktrace ( int signo )
487 {
488 pid_t pid;
489 int rc;
490 int status = 0;
491 char cmd[80];
492
493 // Reinstall default handler; prevent race conditions...
494 ::signal(signo, SIG_DFL);
495
496 static const char *shell = "/bin/sh";
497 static const char *format = "gdb -q --batch --pid=%d"
498 " --eval-command='thread apply all bt'";
499
500 snprintf(cmd, sizeof(cmd), format, (int) getpid());
501
502 pid = fork();
503
504 // Fork failure!
505 if (pid < 0)
506 return;
507
508 // Fork child...
509 if (pid == 0) {
510 execl(shell, shell, "-c", cmd, nullptr);
511 _exit(1);
512 return;
513 }
514
515 // Parent here: wait for child to terminate...
516 do { rc = waitpid(pid, &status, 0); }
517 while ((rc < 0) && (errno == EINTR));
518
519 // Dispatch any logging, if any...
520 QApplication::processEvents(QEventLoop::AllEvents, 3000);
521
522 // Make sure everyone terminates...
523 kill(pid, SIGTERM);
524 _exit(1);
525 }
526
527 #endif
528 #endif
529
530
531 //-------------------------------------------------------------------------
532 // main - The main program trunk.
533 //
534
535 int main ( int argc, char **argv )
536 {
537 Q_INIT_RESOURCE(qsampler);
538 #ifdef CONFIG_STACKTRACE
539 #if defined(__GNUC__) && defined(Q_OS_LINUX)
540 ::signal(SIGILL, stacktrace);
541 ::signal(SIGFPE, stacktrace);
542 ::signal(SIGSEGV, stacktrace);
543 ::signal(SIGABRT, stacktrace);
544 ::signal(SIGBUS, stacktrace);
545 #endif
546 #endif
547 #if defined(Q_OS_LINUX) && !defined(CONFIG_WAYLAND)
548 ::setenv("QT_QPA_PLATFORM", "xcb", 0);
549 #endif
550 #if QT_VERSION >= QT_VERSION_CHECK(5, 6, 0)
551 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
552 QApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
553 #endif
554 #endif
555 qsamplerApplication app(argc, argv);
556
557 #if defined(__APPLE__) // Toshi Nagata 20080105
558 {
559 // Set the plugin path to @exetutable_path/../plugins
560 QDir dir(QApplication::applicationDirPath());
561 dir.cdUp(); // "Contents" directory
562 QApplication::setLibraryPaths(QStringList(dir.absolutePath() + "/plugins"));
563
564 // Set the PATH environment variable to include @executable_path/../../..
565 dir.cdUp();
566 dir.cdUp();
567 QString path(getenv("PATH"));
568 path = dir.absolutePath() + ":" + path;
569 setenv("PATH", path.toUtf8().constData(), 1);
570 }
571 #endif
572
573 // Construct default settings; override with command line arguments.
574 QSampler::Options options;
575 if (!options.parse_args(app.arguments())) {
576 app.quit();
577 return 1;
578 }
579
580 // Have another instance running?
581 if (app.setup()) {
582 app.quit();
583 return 2;
584 }
585
586 // Special custom styles...
587 if (QDir(CONFIG_PLUGINSDIR).exists())
588 app.addLibraryPath(CONFIG_PLUGINSDIR);
589 if (!options.sCustomStyleTheme.isEmpty())
590 app.setStyle(QStyleFactory::create(options.sCustomStyleTheme));
591
592 // Custom color theme (eg. "KXStudio")...
593 QPalette pal(app.palette());
594 if (QSampler::PaletteForm::namedPalette(
595 &options.settings(), options.sCustomColorTheme, pal))
596 app.setPalette(pal);
597
598 // Set default base font...
599 if (options.iBaseFontSize > 0)
600 app.setFont(QFont(app.font().family(), options.iBaseFontSize));
601
602 // Construct, setup and show the main form.
603 QSampler::MainForm w;
604 w.setup(&options);
605 w.show();
606
607 // Settle this one as application main widget...
608 app.setMainWidget(&w);
609
610 // Register the quit signal/slot.
611 // app.connect(&app, SIGNAL(lastWindowClosed()), &app, SLOT(quit()));
612
613 return app.exec();
614 }
615
616
617 // end of qsampler.cpp
618

  ViewVC Help
Powered by ViewVC