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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 3001 - (show annotations) (download)
Sat Oct 8 22:08:39 2016 UTC (3 years, 11 months ago) by capela
File size: 13317 byte(s)
- Fixed a potential crash on the singleton/unique application
  instance setup.
1 // qsampler.cpp
2 //
3 /****************************************************************************
4 Copyright (C) 2004-2016, rncbc aka Rui Nuno Capela. All rights reserved.
5 Copyright (C) 2007, 2008 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 "qsamplerAbout.h"
24 #include "qsamplerOptions.h"
25 #include "qsamplerMainForm.h"
26
27 #include <QApplication>
28 #include <QLibraryInfo>
29 #include <QTranslator>
30 #include <QLocale>
31
32 #if defined(__APPLE__) // Toshi Nagata 20080105
33 #include <QDir>
34 #endif
35
36 #ifndef CONFIG_PREFIX
37 #define CONFIG_PREFIX "/usr/local"
38 #endif
39
40 #ifndef CONFIG_DATADIR
41 #define CONFIG_DATADIR CONFIG_PREFIX "/share"
42 #endif
43
44 #if WIN32
45 #define RELATIVE_LOCALE_DIR "/share/locale"
46 #elif defined(__APPLE__)
47 #define RELATIVE_LOCALE_DIR "/../Resources"
48 #endif
49
50
51 //-------------------------------------------------------------------------
52 // Singleton application instance stuff (Qt/X11 only atm.)
53 //
54
55 #if QT_VERSION < 0x050000
56 #if defined(Q_WS_X11)
57 #define CONFIG_X11
58 #endif
59 #else
60 #if defined(QT_X11EXTRAS_LIB)
61 #define CONFIG_X11
62 #endif
63 #endif
64
65
66 #ifdef CONFIG_X11
67 #ifdef CONFIG_XUNIQUE
68
69 #include <QX11Info>
70
71 #include <X11/Xatom.h>
72 #include <X11/Xlib.h>
73
74 #define QSAMPLER_XUNIQUE "qsamplerApplication"
75
76 #if QT_VERSION >= 0x050100
77
78 #include <xcb/xcb.h>
79 #include <xcb/xproto.h>
80
81 #include <QAbstractNativeEventFilter>
82
83 class qsamplerApplication;
84
85 class qsamplerXcbEventFilter : public QAbstractNativeEventFilter
86 {
87 public:
88
89 // Constructor.
90 qsamplerXcbEventFilter(qsamplerApplication *pApp)
91 : QAbstractNativeEventFilter(), m_pApp(pApp) {}
92
93 // XCB event filter (virtual processor).
94 bool nativeEventFilter(const QByteArray& eventType, void *message, long *);
95
96 private:
97
98 // Instance variable.
99 qsamplerApplication *m_pApp;
100 };
101
102 #endif
103
104 #endif // CONFIG_XUNIQUE
105 #endif // CONFIG_X11
106
107
108 class qsamplerApplication : public QApplication
109 {
110 public:
111
112 // Constructor.
113 qsamplerApplication(int& argc, char **argv) : QApplication(argc, argv),
114 m_pQtTranslator(0), m_pMyTranslator(0), m_pWidget(0)
115 {
116 // Load translation support.
117 QLocale loc;
118 if (loc.language() != QLocale::C) {
119 // Try own Qt translation...
120 m_pQtTranslator = new QTranslator(this);
121 QString sLocName = "qt_" + loc.name();
122 QString sLocPath = QLibraryInfo::location(QLibraryInfo::TranslationsPath);
123 if (m_pQtTranslator->load(sLocName, sLocPath)) {
124 QApplication::installTranslator(m_pQtTranslator);
125 } else {
126 #ifdef RELATIVE_LOCALE_DIR
127 sLocPath = QApplication::applicationDirPath() + RELATIVE_LOCALE_DIR;
128 if (m_pQtTranslator->load(sLocName, sLocPath)) {
129 QApplication::installTranslator(m_pQtTranslator);
130 } else {
131 #endif
132 delete m_pQtTranslator;
133 m_pQtTranslator = 0;
134 #ifdef CONFIG_DEBUG
135 qWarning("Warning: no translation found for '%s' locale: %s/%s.qm",
136 loc.name().toUtf8().constData(),
137 sLocPath.toUtf8().constData(),
138 sLocName.toUtf8().constData());
139 #endif
140 #ifdef RELATIVE_LOCALE_DIR
141 }
142 #endif
143 }
144 // Try own application translation...
145 m_pMyTranslator = new QTranslator(this);
146 sLocName = "qsampler_" + loc.name();
147 if (m_pMyTranslator->load(sLocName, sLocPath)) {
148 QApplication::installTranslator(m_pMyTranslator);
149 } else {
150 #ifdef RELATIVE_LOCALE_DIR
151 sLocPath = QApplication::applicationDirPath() + RELATIVE_LOCALE_DIR;
152 #else
153 sLocPath = CONFIG_DATADIR "/qsampler/translations";
154 #endif
155 if (m_pMyTranslator->load(sLocName, sLocPath)) {
156 QApplication::installTranslator(m_pMyTranslator);
157 } else {
158 delete m_pMyTranslator;
159 m_pMyTranslator = 0;
160 #ifdef CONFIG_DEBUG
161 qWarning("Warning: no translation found for '%s' locale: %s/%s.qm",
162 loc.name().toUtf8().constData(),
163 sLocPath.toUtf8().constData(),
164 sLocName.toUtf8().constData());
165 #endif
166 }
167 }
168 }
169 #ifdef CONFIG_X11
170 #ifdef CONFIG_XUNIQUE
171 // Instance uniqueness initialization...
172 m_pDisplay = QX11Info::display();
173 m_aUnique = XInternAtom(m_pDisplay, QSAMPLER_XUNIQUE, false);
174 XGrabServer(m_pDisplay);
175 m_wOwner = XGetSelectionOwner(m_pDisplay, m_aUnique);
176 XUngrabServer(m_pDisplay);
177 #if QT_VERSION >= 0x050100
178 m_pXcbEventFilter = new qsamplerXcbEventFilter(this);
179 installNativeEventFilter(m_pXcbEventFilter);
180 #endif
181 #endif // CONFIG_XUNIQUE
182 #endif // CONFIG_X11
183 }
184
185 // Destructor.
186 ~qsamplerApplication()
187 {
188 #ifdef CONFIG_X11
189 #ifdef CONFIG_XUNIQUE
190 #if QT_VERSION >= 0x050100
191 removeNativeEventFilter(m_pXcbEventFilter);
192 delete m_pXcbEventFilter;
193 #endif
194 #endif // CONFIG_XUNIQUE
195 #endif // CONFIG_X11
196 if (m_pMyTranslator) delete m_pMyTranslator;
197 if (m_pQtTranslator) delete m_pQtTranslator;
198 }
199
200 // Main application widget accessors.
201 void setMainWidget(QWidget *pWidget)
202 {
203 m_pWidget = pWidget;
204 #ifdef CONFIG_X11
205 #ifdef CONFIG_XUNIQUE
206 m_wOwner = m_pWidget->winId();
207 if (m_pDisplay && m_wOwner) {
208 XGrabServer(m_pDisplay);
209 XSetSelectionOwner(m_pDisplay, m_aUnique, m_wOwner, CurrentTime);
210 XUngrabServer(m_pDisplay);
211 }
212 #endif // CONFIG_XUNIQUE
213 #endif // CONFIG_X11
214 }
215
216 QWidget *mainWidget() const { return m_pWidget; }
217
218 // Check if another instance is running,
219 // and raise its proper main widget...
220 bool setup()
221 {
222 #ifdef CONFIG_X11
223 #ifdef CONFIG_XUNIQUE
224 if (m_pDisplay && m_wOwner != None) {
225 // First, notify any freedesktop.org WM
226 // that we're about to show the main widget...
227 Screen *pScreen = XDefaultScreenOfDisplay(m_pDisplay);
228 int iScreen = XScreenNumberOfScreen(pScreen);
229 XEvent ev;
230 memset(&ev, 0, sizeof(ev));
231 ev.xclient.type = ClientMessage;
232 ev.xclient.display = m_pDisplay;
233 ev.xclient.window = m_wOwner;
234 ev.xclient.message_type = XInternAtom(m_pDisplay, "_NET_ACTIVE_WINDOW", false);
235 ev.xclient.format = 32;
236 ev.xclient.data.l[0] = 0; // Source indication.
237 ev.xclient.data.l[1] = 0; // Timestamp.
238 ev.xclient.data.l[2] = 0; // Requestor's currently active window (none)
239 ev.xclient.data.l[3] = 0;
240 ev.xclient.data.l[4] = 0;
241 XSelectInput(m_pDisplay, m_wOwner, StructureNotifyMask);
242 XSendEvent(m_pDisplay, RootWindow(m_pDisplay, iScreen), false,
243 (SubstructureNotifyMask | SubstructureRedirectMask), &ev);
244 XSync(m_pDisplay, false);
245 XRaiseWindow(m_pDisplay, m_wOwner);
246 // And then, let it get caught on destination
247 // by QApplication::native/x11EventFilter...
248 const QByteArray value = QSAMPLER_XUNIQUE;
249 XChangeProperty(
250 m_pDisplay,
251 m_wOwner,
252 m_aUnique,
253 m_aUnique, 8,
254 PropModeReplace,
255 (unsigned char *) value.data(),
256 value.length());
257 // Done.
258 return true;
259 }
260 #endif // CONFIG_XUNIQUE
261 #endif // CONFIG_X11
262 return false;
263 }
264
265 #ifdef CONFIG_X11
266 #ifdef CONFIG_XUNIQUE
267 void x11PropertyNotify(Window w)
268 {
269 if (m_pDisplay && m_pWidget && m_wOwner == w) {
270 // Always check whether our property-flag is still around...
271 Atom aType;
272 int iFormat = 0;
273 unsigned long iItems = 0;
274 unsigned long iAfter = 0;
275 unsigned char *pData = 0;
276 if (XGetWindowProperty(
277 m_pDisplay,
278 m_wOwner,
279 m_aUnique,
280 0, 1024,
281 false,
282 m_aUnique,
283 &aType,
284 &iFormat,
285 &iItems,
286 &iAfter,
287 &pData) == Success
288 && aType == m_aUnique && iItems > 0 && iAfter == 0) {
289 // Avoid repeating it-self...
290 XDeleteProperty(m_pDisplay, m_wOwner, m_aUnique);
291 // Just make it always shows up fine...
292 m_pWidget->show();
293 m_pWidget->raise();
294 m_pWidget->activateWindow();
295 }
296 // Free any left-overs...
297 if (iItems > 0 && pData)
298 XFree(pData);
299 }
300 }
301 #if QT_VERSION < 0x050000
302 bool x11EventFilter(XEvent *pEv)
303 {
304 if (pEv->type == PropertyNotify
305 && pEv->xproperty.state == PropertyNewValue)
306 x11PropertyNotify(pEv->xproperty.window);
307 return QApplication::x11EventFilter(pEv);
308 }
309 #endif
310 #endif // CONFIG_XUNIQUE
311 #endif // CONFIG_X11
312
313 private:
314
315 // Translation support.
316 QTranslator *m_pQtTranslator;
317 QTranslator *m_pMyTranslator;
318
319 // Instance variables.
320 QWidget *m_pWidget;
321
322 #ifdef CONFIG_X11
323 #ifdef CONFIG_XUNIQUE
324 Display *m_pDisplay;
325 Atom m_aUnique;
326 Window m_wOwner;
327 #if QT_VERSION >= 0x050100
328 qsamplerXcbEventFilter *m_pXcbEventFilter;
329 #endif
330 #endif // CONFIG_XUNIQUE
331 #endif // CONFIG_X11
332 };
333
334
335 #ifdef CONFIG_X11
336 #ifdef CONFIG_XUNIQUE
337 #if QT_VERSION >= 0x050100
338 // XCB Event filter (virtual processor).
339 bool qsamplerXcbEventFilter::nativeEventFilter (
340 const QByteArray& eventType, void *message, long * )
341 {
342 if (eventType == "xcb_generic_event_t") {
343 xcb_property_notify_event_t *pEv
344 = static_cast<xcb_property_notify_event_t *> (message);
345 if ((pEv->response_type & ~0x80) == XCB_PROPERTY_NOTIFY
346 && pEv->state == XCB_PROPERTY_NEW_VALUE)
347 m_pApp->x11PropertyNotify(pEv->window);
348 }
349 return false;
350 }
351 #endif
352 #endif // CONFIG_XUNIQUE
353 #endif // CONFIG_X11
354
355
356 //-------------------------------------------------------------------------
357 // stacktrace - Signal crash handler.
358 //
359
360 #ifdef CONFIG_STACKTRACE
361 #if defined(__GNUC__) && defined(Q_OS_LINUX)
362
363 #include <stdio.h>
364 #include <errno.h>
365 #include <signal.h>
366 #include <unistd.h>
367 #include <sys/wait.h>
368
369 void stacktrace ( int signo )
370 {
371 pid_t pid;
372 int rc;
373 int status = 0;
374 char cmd[80];
375
376 // Reinstall default handler; prevent race conditions...
377 signal(signo, SIG_DFL);
378
379 static const char *shell = "/bin/sh";
380 static const char *format = "gdb -q --batch --pid=%d"
381 " --eval-command='thread apply all bt'";
382
383 snprintf(cmd, sizeof(cmd), format, (int) getpid());
384
385 pid = fork();
386
387 // Fork failure!
388 if (pid < 0)
389 return;
390
391 // Fork child...
392 if (pid == 0) {
393 execl(shell, shell, "-c", cmd, NULL);
394 _exit(1);
395 return;
396 }
397
398 // Parent here: wait for child to terminate...
399 do { rc = waitpid(pid, &status, 0); }
400 while ((rc < 0) && (errno == EINTR));
401
402 // Dispatch any logging, if any...
403 QApplication::processEvents(QEventLoop::AllEvents, 3000);
404
405 // Make sure everyone terminates...
406 kill(pid, SIGTERM);
407 _exit(1);
408 }
409
410 #endif
411 #endif
412
413
414 //-------------------------------------------------------------------------
415 // main - The main program trunk.
416 //
417
418 int main ( int argc, char **argv )
419 {
420 Q_INIT_RESOURCE(qsampler);
421 #ifdef CONFIG_STACKTRACE
422 #if defined(__GNUC__) && defined(Q_OS_LINUX)
423 signal(SIGILL, stacktrace);
424 signal(SIGFPE, stacktrace);
425 signal(SIGSEGV, stacktrace);
426 signal(SIGABRT, stacktrace);
427 signal(SIGBUS, stacktrace);
428 #endif
429 #endif
430 qsamplerApplication app(argc, argv);
431
432 #if defined(__APPLE__) // Toshi Nagata 20080105
433 {
434 // Set the plugin path to @exetutable_path/../plugins
435 QDir dir(QApplication::applicationDirPath());
436 dir.cdUp(); // "Contents" directory
437 QApplication::setLibraryPaths(QStringList(dir.absolutePath() + "/plugins"));
438
439 // Set the PATH environment variable to include @executable_path/../../..
440 dir.cdUp();
441 dir.cdUp();
442 QString path(getenv("PATH"));
443 path = dir.absolutePath() + ":" + path;
444 setenv("PATH", path.toUtf8().constData(), 1);
445 }
446 #endif
447
448 // Construct default settings; override with command line arguments.
449 QSampler::Options options;
450 if (!options.parse_args(app.arguments())) {
451 app.quit();
452 return 1;
453 }
454
455 // Have another instance running?
456 if (app.setup()) {
457 app.quit();
458 return 2;
459 }
460
461 // Dark themes grayed/disabled color group fix...
462 QPalette pal(app.palette());
463 if (pal.base().color().value() < 0x7f) {
464 #if QT_VERSION >= 0x050000
465 const QColor& color = pal.window().color();
466 const int iGroups = int(QPalette::Active | QPalette::Inactive) + 1;
467 for (int i = 0; i < iGroups; ++i) {
468 const QPalette::ColorGroup group = QPalette::ColorGroup(i);
469 pal.setBrush(group, QPalette::Light, color.lighter(150));
470 pal.setBrush(group, QPalette::Midlight, color.lighter(120));
471 pal.setBrush(group, QPalette::Dark, color.darker(150));
472 pal.setBrush(group, QPalette::Mid, color.darker(120));
473 pal.setBrush(group, QPalette::Shadow, color.darker(200));
474 }
475 // pal.setColor(QPalette::Disabled, QPalette::ButtonText, pal.mid().color());
476 #endif
477 pal.setColorGroup(QPalette::Disabled,
478 pal.windowText().color().darker(),
479 pal.button(),
480 pal.light(),
481 pal.dark(),
482 pal.mid(),
483 pal.text().color().darker(),
484 pal.text().color().lighter(),
485 pal.base(),
486 pal.window());
487 app.setPalette(pal);
488 }
489
490 // Set default base font...
491 if (options.iBaseFontSize > 0)
492 app.setFont(QFont(app.font().family(), options.iBaseFontSize));
493
494 // Construct, setup and show the main form.
495 QSampler::MainForm w;
496 w.setup(&options);
497 w.show();
498
499 // Settle this one as application main widget...
500 app.setMainWidget(&w);
501
502 // Register the quit signal/slot.
503 // app.connect(&app, SIGNAL(lastWindowClosed()), &app, SLOT(quit()));
504
505 return app.exec();
506 }
507
508
509 // end of qsampler.cpp

  ViewVC Help
Powered by ViewVC