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

Annotation of /qsampler/trunk/src/qsampler.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2839 - (hide 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 capela 2074 // qsampler.cpp
2     //
3     /****************************************************************************
4 capela 2839 Copyright (C) 2004-2015, rncbc aka Rui Nuno Capela. All rights reserved.
5 capela 2074 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 capela 2459
32 capela 2074 #if defined(__APPLE__) // Toshi Nagata 20080105
33     #include <QDir>
34     #endif
35    
36 capela 2282 #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 persson 2210 #if WIN32
46     #define RELATIVE_LOCALE_DIR "/share/locale"
47     #elif defined(__APPLE__)
48     #define RELATIVE_LOCALE_DIR "/../Resources"
49     #endif
50 capela 2074
51 persson 2210
52 capela 2074 //-------------------------------------------------------------------------
53     // Singleton application instance stuff (Qt/X11 only atm.)
54     //
55    
56 capela 2839 #if QT_VERSION < 0x050000
57 capela 2074 #if defined(Q_WS_X11)
58 capela 2839 #define CONFIG_X11
59     #endif
60     #else
61     #if defined(QT_X11EXTRAS_LIB)
62     #define CONFIG_X11
63     #endif
64     #endif
65 capela 2074
66 capela 2839
67     #ifdef CONFIG_X11
68     #ifdef CONFIG_XUNIQUE
69    
70 capela 2074 #include <QX11Info>
71    
72     #include <X11/Xatom.h>
73     #include <X11/Xlib.h>
74    
75 capela 2839 #define QSAMPLER_XUNIQUE "qsamplerApplication"
76 capela 2074
77 capela 2839 #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 capela 2074 #endif
104    
105 capela 2839 #endif // CONFIG_XUNIQUE
106     #endif // CONFIG_X11
107    
108    
109 capela 2074 class qsamplerApplication : public QApplication
110     {
111     public:
112    
113     // Constructor.
114     qsamplerApplication(int& argc, char **argv) : QApplication(argc, argv),
115 capela 2839 m_pQtTranslator(0), m_pMyTranslator(0), m_pWidget(0)
116 capela 2074 {
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 capela 2631 #ifdef RELATIVE_LOCALE_DIR
128 persson 2210 sLocPath = QApplication::applicationDirPath() + RELATIVE_LOCALE_DIR;
129 persson 2144 if (m_pQtTranslator->load(sLocName, sLocPath)) {
130     QApplication::installTranslator(m_pQtTranslator);
131     } else {
132 capela 2631 #endif
133 capela 2074 delete m_pQtTranslator;
134     m_pQtTranslator = 0;
135 capela 2631 #ifdef CONFIG_DEBUG
136 capela 2074 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 capela 2631 #endif
141     #ifdef RELATIVE_LOCALE_DIR
142 persson 2144 }
143 capela 2631 #endif
144 capela 2074 }
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 capela 2631 #ifdef RELATIVE_LOCALE_DIR
152 persson 2210 sLocPath = QApplication::applicationDirPath() + RELATIVE_LOCALE_DIR;
153 capela 2631 #else
154     sLocPath = CONFIG_DATADIR "/qsampler/translations";
155     #endif
156 capela 2074 if (m_pMyTranslator->load(sLocName, sLocPath)) {
157     QApplication::installTranslator(m_pMyTranslator);
158     } else {
159     delete m_pMyTranslator;
160     m_pMyTranslator = 0;
161 capela 2631 #ifdef CONFIG_DEBUG
162 capela 2074 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 capela 2631 #endif
167 capela 2074 }
168     }
169     }
170 capela 2839 #ifdef CONFIG_X11
171     #ifdef CONFIG_XUNIQUE
172     // Instance uniqueness initialization...
173 capela 2074 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 capela 2839 #if QT_VERSION >= 0x050100
179     m_pXcbEventFilter = new qsamplerXcbEventFilter(this);
180     installNativeEventFilter(m_pXcbEventFilter);
181 capela 2074 #endif
182 capela 2839 #endif // CONFIG_XUNIQUE
183     #endif // CONFIG_X11
184 capela 2074 }
185    
186     // Destructor.
187     ~qsamplerApplication()
188     {
189 capela 2839 #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 capela 2074 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 capela 2839 #ifdef CONFIG_X11
206     #ifdef CONFIG_XUNIQUE
207 capela 2074 XGrabServer(m_pDisplay);
208     m_wOwner = m_pWidget->winId();
209     XSetSelectionOwner(m_pDisplay, m_aUnique, m_wOwner, CurrentTime);
210     XUngrabServer(m_pDisplay);
211 capela 2839 #endif // CONFIG_XUNIQUE
212     #endif // CONFIG_X11
213 capela 2074 }
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 capela 2839 #ifdef CONFIG_X11
222     #ifdef CONFIG_XUNIQUE
223 capela 2074 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 capela 2839 // by QApplication::native/x11EventFilter...
247     const QByteArray value = QSAMPLER_XUNIQUE;
248 capela 2074 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 capela 2839 #endif // CONFIG_XUNIQUE
260     #endif // CONFIG_X11
261 capela 2074 return false;
262     }
263    
264 capela 2839 #ifdef CONFIG_X11
265     #ifdef CONFIG_XUNIQUE
266     void x11PropertyNotify(Window w)
267 capela 2074 {
268 capela 2839 if (m_pWidget && m_wOwner == w) {
269 capela 2074 // 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 capela 2839 }
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 capela 2074 return QApplication::x11EventFilter(pEv);
307     }
308     #endif
309 capela 2839 #endif // CONFIG_XUNIQUE
310     #endif // CONFIG_X11
311 capela 2074
312     private:
313    
314     // Translation support.
315     QTranslator *m_pQtTranslator;
316     QTranslator *m_pMyTranslator;
317    
318     // Instance variables.
319     QWidget *m_pWidget;
320    
321 capela 2839 #ifdef CONFIG_X11
322     #ifdef CONFIG_XUNIQUE
323 capela 2074 Display *m_pDisplay;
324     Atom m_aUnique;
325     Window m_wOwner;
326 capela 2839 #if QT_VERSION >= 0x050100
327     qsamplerXcbEventFilter *m_pXcbEventFilter;
328 capela 2074 #endif
329 capela 2839 #endif // CONFIG_XUNIQUE
330     #endif // CONFIG_X11
331 capela 2074 };
332    
333    
334 capela 2839 #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 capela 2074 //-------------------------------------------------------------------------
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 capela 2341 #include <unistd.h>
366 capela 2074 #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 capela 2181 static const char *format = "gdb -q --batch --pid=%d"
380     " --eval-command='thread apply all bt'";
381 capela 2074
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 capela 2415 #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 capela 2074 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 capela 2677 app.setFont(QFont(app.font().family(), options.iBaseFontSize));
492 capela 2074
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