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

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

Parent Directory Parent Directory | Revision Log Revision Log


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

  ViewVC Help
Powered by ViewVC