GNU Octave  6.2.0
A high-level interpreted language, primarily intended for numerical computations, mostly compatible with Matlab
octave-qobject.cc
Go to the documentation of this file.
1 ////////////////////////////////////////////////////////////////////////
2 //
3 // Copyright (C) 2011-2021 The Octave Project Developers
4 //
5 // See the file COPYRIGHT.md in the top-level directory of this
6 // distribution or <https://octave.org/copyright/>.
7 //
8 // This file is part of Octave.
9 //
10 // Octave is free software: you can redistribute it and/or modify it
11 // under the terms of the GNU General Public License as published by
12 // the Free Software Foundation, either version 3 of the License, or
13 // (at your option) any later version.
14 //
15 // Octave is distributed in the hope that it will be useful, but
16 // WITHOUT ANY WARRANTY; without even the implied warranty of
17 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 // GNU General Public License for more details.
19 //
20 // You should have received a copy of the GNU General Public License
21 // along with Octave; see the file COPYING. If not, see
22 // <https://www.gnu.org/licenses/>.
23 //
24 ////////////////////////////////////////////////////////////////////////
25 
26 #if defined (HAVE_CONFIG_H)
27 # include "config.h"
28 #endif
29 
30 #include <utility>
31 
32 #include <QApplication>
33 #include <QClipboard>
34 #include <QFile>
35 #include <QTextCodec>
36 #include <QThread>
37 #include <QTimer>
38 #include <QTranslator>
39 
40 #include "interpreter-qobject.h"
41 #include "main-window.h"
42 #include "octave-qobject.h"
43 #include "qt-application.h"
44 #include "qt-interpreter-events.h"
45 #include "resource-manager.h"
46 #include "shortcut-manager.h"
47 
48 // Bug #55940 (Disable App Nap on Mac)
49 #if defined (Q_OS_MAC)
50 # include <objc/runtime.h>
51 # include <objc/message.h>
52 #endif
53 
54 #include "oct-env.h"
55 #include "version.h"
56 
57 #include "ovl.h"
58 
59 
60 // Bug #55940 (Disable App Nap on Mac)
61 #if defined (Q_OS_MAC)
62 static void disable_app_nap (void)
63 {
64  Class process_info_class;
65  SEL process_info_selector;
66  SEL begin_activity_with_options_selector;
67  id process_info;
68  id reason_string;
69  id osx_latencycritical_activity;
70 
71  // Option codes found at https://stackoverflow.com/questions/22784886/what-can-make-nanosleep-drift-with-exactly-10-sec-on-mac-os-x-10-9/32729281#32729281
72  unsigned long long NSActivityUserInitiatedAllowingIdleSystemSleep = 0x00FFFFFFULL;
73  unsigned long long NSActivityLatencyCritical = 0xFF00000000ULL;
74 
75  // Avoid errors on older versions of OS X
76  process_info_class = static_cast<Class> (objc_getClass ("NSProcessInfo"));
77  if (process_info_class == nil)
78  return;
79 
80  process_info_selector = sel_getUid ("processInfo");
81  if (class_getClassMethod (process_info_class, process_info_selector)
82  == nullptr)
83  return;
84 
85  begin_activity_with_options_selector = sel_getUid ("beginActivityWithOptions:reason:");
86  if (class_getInstanceMethod (process_info_class,
87  begin_activity_with_options_selector)
88  == nullptr)
89  return;
90 
91  process_info = reinterpret_cast<id (*) (id, SEL)> (objc_msgSend)
92  (reinterpret_cast<id> (process_info_class),
93  process_info_selector);
94  if (process_info == nil)
95  return;
96 
97  reason_string = reinterpret_cast<id (*) (id, SEL)> (objc_msgSend)
98  (reinterpret_cast<id> (objc_getClass ("NSString")),
99  sel_getUid ("alloc"));
100  reason_string = reinterpret_cast<id (*) (id, SEL, const char *)> (objc_msgSend)
101  (reason_string, sel_getUid ("initWithUTF8String:"),
102  "App Nap causes pause() malfunction");
103 
104  // Start an Activity that suppresses App Nap. This Activity will run for
105  // the entire duration of the Octave process. This is intentional,
106  // not a leak.
107  osx_latencycritical_activity =
108  reinterpret_cast<id (*) (id, SEL, unsigned long long, id)> (objc_msgSend)
109  (process_info,
110  begin_activity_with_options_selector,
111  NSActivityUserInitiatedAllowingIdleSystemSleep
112  | NSActivityLatencyCritical,
113  reason_string);
114 }
115 #endif
116 
117 namespace octave
118 {
119  // Disable all Qt messages by default.
120 
121  static void
122 #if defined (QTMESSAGEHANDLER_ACCEPTS_QMESSAGELOGCONTEXT)
123  message_handler (QtMsgType, const QMessageLogContext &, const QString &)
124 #else
125  message_handler (QtMsgType, const char *)
126 #endif
127  { }
128 
129  //! Reimplement QApplication::notify. Octave's own exceptions are
130  //! caught and rethrown in the interpreter thread.
131 
132  bool octave_qapplication::notify (QObject *receiver, QEvent *ev)
133  {
134  try
135  {
136  return QApplication::notify (receiver, ev);
137  }
138  catch (execution_exception& ee)
139  {
140  emit interpreter_event
141  ([ee] (void)
142  {
143  // INTERPRETER THREAD
144 
145  throw ee;
146  });
147  }
148 
149  return false;
150  }
151 
152  // We will create a QApplication object, even if START_GUI is false,
153  // so that we can use Qt widgets for plot windows when running in
154  // command-line mode. Note that we are creating an
155  // octave_qapplication object but handling it as a QApplication object
156  // because the octave_qapplication should behave identically to a
157  // QApplication object except that it overrides the notify method so
158  // we can handle forward Octave interpreter exceptions from the GUI
159  // thread to the interpreter thread.
160 
162  : QObject (), m_app_context (app_context),
163  m_argc (m_app_context.sys_argc ()),
164  m_argv (m_app_context.sys_argv ()),
165  m_qapplication (new octave_qapplication (m_argc, m_argv)),
166  m_resource_manager (), m_shortcut_manager (*this),
167  m_qt_tr (new QTranslator ()), m_gui_tr (new QTranslator ()),
168  m_qsci_tr (new QTranslator ()), m_translators_installed (false),
169  m_qt_interpreter_events (new qt_interpreter_events (*this)),
170  m_interpreter_qobj (new interpreter_qobject (*this)),
171  m_main_thread (new QThread ())
172  {
173  std::string show_gui_msgs =
174  sys::env::getenv ("OCTAVE_SHOW_GUI_MESSAGES");
175 
176  // Installing our handler suppresses the messages.
177 
178  if (show_gui_msgs.empty ())
179  {
180 #if defined (HAVE_QINSTALLMESSAGEHANDLER)
181  qInstallMessageHandler (message_handler);
182 #else
183  qInstallMsgHandler (message_handler);
184 #endif
185  }
186 
187  // Set the codec for all strings (before wizard or any GUI object)
188 #if ! defined (Q_OS_WIN32)
189  QTextCodec::setCodecForLocale (QTextCodec::codecForName ("UTF-8"));
190 #endif
191 
192 #if defined (HAVE_QT4)
193  QTextCodec::setCodecForCStrings (QTextCodec::codecForName ("UTF-8"));
194 #endif
195 
196  // Initialize global Qt application metadata.
197 
198  QCoreApplication::setApplicationName ("GNU Octave");
199  QCoreApplication::setApplicationVersion (OCTAVE_VERSION);
200 
201  // Register octave_value_list for connecting thread crossing signals.
202 
203  qRegisterMetaType<octave_value_list> ("octave_value_list");
204 
205 
206 // Bug #55940 (Disable App Nap on Mac)
207 #if defined (Q_OS_MAC)
208  // Mac App Nap feature causes pause() and sleep() to misbehave.
209  // Disable it for the entire program run.
210  disable_app_nap ();
211 #endif
212 
213  // Force left-to-right alignment (see bug #46204)
214  m_qapplication->setLayoutDirection (Qt::LeftToRight);
215 
216  connect (m_interpreter_qobj, SIGNAL (execution_finished (int)),
217  this, SLOT (handle_interpreter_execution_finished (int)));
218 
219  connect (this, SIGNAL (request_interpreter_shutdown (int)),
220  m_interpreter_qobj, SLOT (shutdown (int)));
221 
222  connect (m_interpreter_qobj, SIGNAL (shutdown_finished (int)),
223  this, SLOT (handle_interpreter_shutdown_finished (int)));
224 
225  connect (m_main_thread, SIGNAL (finished (void)),
226  m_main_thread, SLOT (deleteLater (void)));
227 
228  // Handle any interpreter_event signal from the octave_qapplication
229  // object here.
230 
231  connect (m_qapplication, SIGNAL (interpreter_event (const fcn_callback&)),
232  this, SLOT (interpreter_event (const fcn_callback&)));
233 
234  connect (m_qapplication, SIGNAL (interpreter_event (const meth_callback&)),
235  this, SLOT (interpreter_event (const meth_callback&)));
236 
237  connect (qt_link (),
238  SIGNAL (copy_image_to_clipboard_signal (const QString&, bool)),
239  this, SLOT (copy_image_to_clipboard (const QString&, bool)));
240  }
241 
243  {
244  // Note that we don't delete m_main_thread here. That is handled by
245  // deleteLater slot that is called when the m_main_thread issues a
246  // finished signal.
247 
248  delete m_interpreter_qobj;
249  delete m_qsci_tr;
250  delete m_gui_tr;
251  delete m_qt_tr;
252  delete m_qapplication;
253 
255  }
256 
258  {
260  return;
261 
263 
264  m_qapplication->installTranslator (m_qt_tr);
265  m_qapplication->installTranslator (m_gui_tr);
266  m_qapplication->installTranslator (m_qsci_tr);
267 
269  }
270 
272  {
273  // Defer initializing and executing the interpreter until after the main
274  // window and QApplication are running to prevent race conditions
275  QTimer::singleShot (0, m_interpreter_qobj, SLOT (execute (void)));
276 
277  m_interpreter_qobj->moveToThread (m_main_thread);
278 
279  m_main_thread->start ();
280  }
281 
283  {
284  return m_qapplication->exec ();
285  }
286 
288  {
289  return true;
290  }
291 
293  {
294  emit request_interpreter_shutdown (exit_status);
295  }
296 
298  {
299 #if defined (Q_OS_MAC)
300  // fprintf to stderr is needed by macOS, for poorly-understood reasons.
301  fprintf (stderr, "\n");
302 #endif
303 
304  m_main_thread->quit ();
305  m_main_thread->wait ();
306 
307  qApp->exit (exit_status);
308  }
309 
311  {
312  // The following is a direct function call across threads. It works
313  // because the it is accessing a thread-safe queue of events that
314  // are later executed by the Octave interpreter in the other thread.
315 
316  // See also the comments in interpreter-qobject.h about
317  // interpreter_qobject slots.
318 
320  }
321 
323  {
324  // The following is a direct function call across threads. It works
325  // because the it is accessing a thread-safe queue of events that
326  // are later executed by the Octave interpreter in the other thread.
327 
328  // See also the comments in interpreter-qobject.h about
329  // interpreter_qobject slots.
330 
332  }
333 
334  void base_qobject::copy_image_to_clipboard (const QString& file,
335  bool remove_file)
336  {
337  QClipboard *clipboard = QApplication::clipboard ();
338 
339  QImage img (file);
340 
341  if (img.isNull ())
342  {
343  // Report error?
344  return;
345  }
346 
347  clipboard->setImage (img);
348 
349  if (remove_file)
350  QFile::remove (file);
351  }
352 
354  : base_qobject (app_context)
355  {
356  // Get settings file.
358 
359  // After settings.
361 
362  m_qapplication->setQuitOnLastWindowClosed (false);
363 
365  }
366 
368  : base_qobject (app_context), m_main_window (new main_window (*this))
369  {
370  connect (m_interpreter_qobj, SIGNAL (ready (void)),
371  m_main_window, SLOT (handle_octave_ready (void)));
372 
373  connect (qt_link (),
374  SIGNAL (focus_window_signal (const QString&)),
375  m_main_window, SLOT (focus_window (const QString&)));
376 
377  m_app_context.gui_running (true);
378 
380  }
381 
383  {
384  delete m_main_window;
385  }
386 
388  {
389  // Currently, we forward to main_window::confirm_shutdown instead of
390  // just displaying a dialog box here because the main_window also
391  // knows about and is responsible for notifying the editor.
392 
393  return m_main_window ? m_main_window->confirm_shutdown () : true;
394  }
395 }
Base class for Octave interfaces that use Qt.
void copy_image_to_clipboard(const QString &file, bool remove_file)
void handle_interpreter_execution_finished(int)
interpreter_qobject * m_interpreter_qobj
qt_interpreter_events * qt_link(void)
qt_application & m_app_context
QTranslator * m_qt_tr
base_qobject(qt_application &app_context)
void start_main_thread(void)
resource_manager m_resource_manager
QTranslator * m_qsci_tr
void request_interpreter_shutdown(int)
void handle_interpreter_shutdown_finished(int)
QTranslator * m_gui_tr
virtual bool confirm_shutdown(void)
octave_qapplication * m_qapplication
void config_translators(void)
void interpreter_event(const fcn_callback &fcn)
cli_qobject(qt_application &app_context)
bool confirm_shutdown(void)
main_window * m_main_window
gui_qobject(qt_application &app_context)
void interpreter_event(const fcn_callback &fcn)
Represents the main window.
Definition: main-window.h:76
bool confirm_shutdown(void)
Definition: main-window.cc:284
This class is a simple wrapper around QApplication so that we can reimplement QApplication::notify.
virtual bool notify(QObject *receiver, QEvent *e) override
Reimplement QApplication::notify.
void interpreter_event(const fcn_callback &fcn)
This class inherits from the pure-virtual base class octave::application and provides an implementati...
bool gui_running(void) const
void config_translators(QTranslator *qt_tr, QTranslator *qsci_tr, QTranslator *gui_tr)
static std::string getenv(const std::string &name)
Definition: oct-env.cc:271
static void delete_c_str_vec(const char *const *)
Definition: str-vec.cc:185
#define OCTAVE_VERSION
Definition: main.in.cc:52
std::function< void(octave::interpreter &)> meth_callback
Definition: event-manager.h:47
static void message_handler(QtMsgType, const char *)
std::function< void(void)> fcn_callback
Definition: event-manager.h:46