1 /************************************************************************
2 *
3 * Copyright 2010 Jakob Leben (jakob.leben@gmail.com)
4 *
5 * This file is part of SuperCollider Qt GUI.
6 *
7 * This program is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 2 of the License, or
10 * (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
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 *
20 ************************************************************************/
21
22 #include "QcApplication.h"
23 #include "widgets/QcTreeWidget.h"
24
25 #include <PyrLexer.h>
26 #include <VMGlobals.h>
27 #include <PyrKernel.h>
28 #include <PyrSlot.h>
29 #include <GC.h>
30 #include "SC_Filesystem.hpp"
31
32 #include <QThread>
33 #include <QFileOpenEvent>
34 #include <QKeyEvent>
35 #include <QIcon>
36 #include <QMenuBar>
37 #include <QSharedPointer>
38
39 #include "hacks/hacks_mac.hpp"
40
41 #ifdef Q_OS_MAC
42 # include "../../common/SC_Apple.hpp"
43 #endif
44
45 extern bool compiledOK;
46
47 QcApplication* QcApplication::_instance = 0;
48 QMutex QcApplication::_mutex;
49
50 #ifdef Q_WS_X11
51 # include <X11/Xlib.h>
52 #endif
53
54
55 /* on x11, we need to check, if we can actually connect to the X server */
QtColliderUseGui(void)56 static bool QtColliderUseGui(void) {
57 #ifdef Q_WS_X11
58 Display* dpy = XOpenDisplay(NULL);
59 if (!dpy)
60 return false;
61 XCloseDisplay(dpy);
62 return true;
63 #else
64 return true;
65 #endif
66 }
67
68 // undefine some interfering X11 definitions
69 #undef KeyPress
70
71 bool QcApplication::_systemHasMouseWheel = false;
72
QcApplication(int & argc,char ** argv)73 QcApplication::QcApplication(int& argc, char** argv): QApplication(argc, argv, QtColliderUseGui()) {
74 _mutex.lock();
75 _instance = this;
76 _mutex.unlock();
77 this->setAttribute(Qt::AA_UseHighDpiPixmaps);
78
79 #ifdef Q_OS_MAC
80 QtCollider::Mac::DisableAutomaticWindowTabbing();
81 #endif
82
83 if (QtColliderUseGui()) { // avoid a crash on linux, if x is not available
84 QIcon icon;
85 icon.addFile(":/icons/sc-cube-128");
86 icon.addFile(":/icons/sc-cube-48");
87 icon.addFile(":/icons/sc-cube-32");
88 icon.addFile(":/icons/sc-cube-16");
89 setWindowIcon(icon);
90 createMenu();
91 }
92
93 #ifdef Q_OS_MAC
94 // On Mac, we may need to disable "App Nap", so we aren't put to sleep unexpectedly
95 SC::Apple::disableAppNap();
96 #endif
97
98 _handleCmdPeriod = SC_Filesystem::instance().getIdeName() != "scapp";
99 }
100
~QcApplication()101 QcApplication::~QcApplication() {
102 _mutex.lock();
103 _instance = 0;
104 _mutex.unlock();
105 }
106
createMenu()107 void QcApplication::createMenu() {
108 _mainMenu = QSharedPointer<QMenuBar>::create();
109
110 #ifdef Q_OS_MAC
111 // macOS registers cmd+q on menu bars by default. Here we register a handler for cmd+q that does nothing
112 // and we disable the Quit menu by default
113 auto* action = new QAction("");
114 action->setMenuRole(QAction::QuitRole);
115 action->setEnabled(false);
116 QObject::connect(action, SIGNAL(triggered()), this, SLOT(onQuit()));
117 auto* menu = new QMenu(tr("&File"));
118 menu->addAction(action);
119 _mainMenu->addMenu(menu);
120 #endif
121 }
122
onQuit()123 void QcApplication::onQuit() {
124 qWarning("[QcApplication::onQuit] CMD+Q was caught by the interpreter. "
125 "This is weird, it should not happen. "
126 "Please file an issue at https://github.com/supercollider/supercollider/issues");
127 }
128
compareThread()129 bool QcApplication::compareThread() { return gMainVMGlobals->canCallOS; }
130
interpret(const QString & str,bool print)131 void QcApplication::interpret(const QString& str, bool print) {
132 QtCollider::lockLang();
133 if (compiledOK) {
134 VMGlobals* g = gMainVMGlobals;
135
136 PyrString* strObj = newPyrString(g->gc, str.toStdString().c_str(), 0, true);
137
138 SetObject(&slotRawInterpreter(&g->process->interpreter)->cmdLine, strObj);
139 g->gc->GCWriteNew(slotRawObject(&g->process->interpreter),
140 strObj); // we know strObj is white so we can use GCWriteNew
141
142 runLibrary(print ? SC_SYM(interpretPrintCmdLine) : SC_SYM(interpretCmdLine));
143 }
144 QtCollider::unlockLang();
145 }
146
event(QEvent * event)147 bool QcApplication::event(QEvent* event) {
148 switch (event->type()) {
149 case QEvent::FileOpen: {
150 // open the file dragged onto the application icon on Mac
151 QFileOpenEvent* fe = static_cast<QFileOpenEvent*>(event);
152 interpret(QStringLiteral("Document.open(\"%1\")").arg(fe->file()), false);
153 event->accept();
154 return true;
155 }
156 default:
157 break;
158 }
159
160 return QApplication::event(event);
161 }
162
notify(QObject * object,QEvent * event)163 bool QcApplication::notify(QObject* object, QEvent* event) {
164 switch (event->type()) {
165 case QEvent::KeyPress: {
166 QKeyEvent* kevent = static_cast<QKeyEvent*>(event);
167 if (_handleCmdPeriod && (kevent->key() == Qt::Key_Period) && (kevent->modifiers() & Qt::ControlModifier)) {
168 static QString cmdPeriodCommand("CmdPeriod.run");
169 interpret(cmdPeriodCommand, false);
170 }
171 break;
172 }
173 case QEvent::Wheel: {
174 _systemHasMouseWheel = true;
175 break;
176 }
177 default:
178 break;
179 }
180
181 bool result = QApplication::notify(object, event);
182
183 #ifdef Q_OS_MAC
184 // XXX Explicitly accept all handled events so they don't propagate outside the application.
185 // This is a hack; for a not-fully-understood reason Qt past 5.7 sends these events to the
186 // native window if they aren't accepted here. This caused issue #4058. Accepting them here
187 // seems to solve the problem, but might cause other issues since it is a heavy-handed way
188 // of doing this. TODO - solve more elegantly
189 if (result)
190 event->accept();
191 #endif
192 return result;
193 }
194