MRPT  2.0.3
WxSubsystem.cpp
Go to the documentation of this file.
1 /* +------------------------------------------------------------------------+
2  | Mobile Robot Programming Toolkit (MRPT) |
3  | https://www.mrpt.org/ |
4  | |
5  | Copyright (c) 2005-2020, Individual contributors, see AUTHORS file |
6  | See: https://www.mrpt.org/Authors - All rights reserved. |
7  | Released under BSD License. See: https://www.mrpt.org/License |
8  +------------------------------------------------------------------------+ */
9 
10 #include "gui-precomp.h" // Precompiled headers
11 
12 #include <mrpt/config.h>
13 
17 
18 #include <mrpt/system/os.h>
19 
20 #include <mrpt/gui/WxSubsystem.h>
21 #include <mrpt/gui/WxUtils.h>
22 
23 //#define WXSUBSYSTEM_VERBOSE
24 
25 // ------------------------------------------------------------------------
26 // Defined: Try to wait for all windows & the thread to exit cleanly.
27 // Undefined: Just to a std::this_thread::sleep_for(ms) and quit crossing our
28 // fingers.
29 //
30 // Problem with the "clean way" is: As of feb/2011, I get this error
31 // at the end:
32 // ** (MRPT:11711): CRITICAL **: giop_thread_request_push: assertion `tdata !=
33 // NULL' failed
34 // ------------------------------------------------------------------------
35 //#define WXSHUTDOWN_DO_IT_CLEAN
36 
37 #if MRPT_HAS_WXWIDGETS
38 
39 using namespace mrpt;
40 using namespace mrpt::gui;
41 using namespace std;
42 
45 
46 std::queue<WxSubsystem::TRequestToWxMainThread*>*
48 std::mutex* WxSubsystem::cs_listPendingWxRequests = nullptr;
49 
51  nullptr;
52 bool isConsoleApp_value = true;
55 
56 // Auxiliary class implementation:
59 {
61  {
62 #ifdef WXSUBSYSTEM_VERBOSE
63  printf("[~CAuxWxSubsystemShutdowner] Sending 999...\n");
64 #endif
65  // Shut down:
66  try
67  {
68  auto* REQ = new WxSubsystem::TRequestToWxMainThread[1];
69  REQ->OPCODE = 999;
71 
72  // std::this_thread::sleep_for(100ms); // JL: I found no better way
73  // of doing this, sorry :-( See
74  // WxSubsystem::waitWxShutdownsIfNoWindows()
76  }
77  catch (...)
78  {
79  } // Just in case we got an out-of-mem error.
80  } // is console app.
81 
82 #ifdef WXSUBSYSTEM_VERBOSE
83  printf("[~CAuxWxSubsystemShutdowner] Deleting static objects.\n");
84 #endif
85  // This is the final point where all dynamic memory must be deleted:
86  // delete &WxSubsystem::GetWxMainThreadInstance(); // may cause crashes at
87  // app end...
90 }
91 
92 // ---------------------------------------------------------------------------------------
93 // Auxiliary dialog class for the "ask user to open a camera":
94 // ---------------------------------------------------------------------------------------
95 class CDialogAskUserForCamera : public wxDialog
96 {
97  public:
99 
100  static const long ID_BTN_OK;
101  static const long ID_BTN_CANCEL;
102 
104  : wxDialog(
105  nullptr, wxID_ANY, wxT("Select image source"), wxDefaultPosition,
106  wxDefaultSize, wxDEFAULT_DIALOG_STYLE, wxDialogNameStr)
107  {
108  auto* f1 = new wxFlexGridSizer(2, 1, 0, 0);
109  panel = new mrpt::gui::CPanelCameraSelection(this, wxID_ANY);
110  f1->Add(
111  panel, 1, wxALL | wxALIGN_BOTTOM | wxALIGN_CENTER_HORIZONTAL, 5);
112 
113  auto* f2 = new wxFlexGridSizer(1, 2, 0, 0);
114  wxButton* btnOk = new wxButton(
115  this, ID_BTN_OK, wxT("Ok"), wxDefaultPosition, wxDefaultSize);
116  wxButton* btnCancel = new wxButton(
117  this, ID_BTN_CANCEL, wxT("Cancel"), wxDefaultPosition,
118  wxDefaultSize);
119  f1->Add(f2, 1, wxALL | wxALIGN_BOTTOM | wxALIGN_CENTER_HORIZONTAL, 5);
120 
121  f2->Add(
122  btnOk, 1, wxALL | wxALIGN_BOTTOM | wxALIGN_CENTER_HORIZONTAL, 5);
123  f2->Add(
124  btnCancel, 1, wxALL | wxALIGN_BOTTOM | wxALIGN_CENTER_HORIZONTAL,
125  5);
126 
127  Bind(wxEVT_BUTTON, &CDialogAskUserForCamera::OnBtnOk, this, ID_BTN_OK);
128  Bind(
129  wxEVT_BUTTON, &CDialogAskUserForCamera::OnBtnCancel, this,
130  ID_BTN_CANCEL);
131 
132  SetSizer(f1);
133  Fit();
134 
135  btnOk->SetFocus(); // So the default params can be accepted by just
136  // pressing ENTER.
137  }
138 
139  ~CDialogAskUserForCamera() override = default;
140  void OnBtnOk(wxCommandEvent& event) { EndModal(wxID_OK); }
141  void OnBtnCancel(wxCommandEvent& event) { EndModal(wxID_CANCEL); }
142 };
143 
144 const long CDialogAskUserForCamera::ID_BTN_OK = wxNewId();
145 const long CDialogAskUserForCamera::ID_BTN_CANCEL = wxNewId();
146 
147 // ---------------------------------------------------------------------------------------
148 // The wx dummy frame:
149 // ---------------------------------------------------------------------------------------
150 BEGIN_EVENT_TABLE(WxSubsystem::CWXMainFrame, wxFrame)
151 
152 END_EVENT_TABLE()
153 
154 const long ID_TIMER_WX_PROCESS_REQUESTS = wxNewId();
155 
156 WxSubsystem::CWXMainFrame::CWXMainFrame(wxWindow* parent, wxWindowID id)
157 {
158  Create(
159  parent, id, _("MRPT-dummy frame window"), wxDefaultPosition,
160  wxSize(1, 1),
161  0, // wxDEFAULT_FRAME_STYLE,
162  _T("id"));
163 
164  if (oneInstance)
165  {
166  cerr << "[CWXMainFrame] More than one instance running!" << endl;
167  }
168  oneInstance = this;
169 
170  // ------------------------------------------------------------------------------------------
171  // Create a timer so requests from the main application thread can be
172  // processed regularly:
173  // ------------------------------------------------------------------------------------------
174  Bind(
175  wxEVT_TIMER, &CWXMainFrame::OnTimerProcessRequests, this,
177  m_theTimer = new wxTimer(this, ID_TIMER_WX_PROCESS_REQUESTS);
178 
179  m_theTimer->Start(10, true); // One-shot
180 }
181 
183 {
184 #ifdef WXSUBSYSTEM_VERBOSE
185  cout << "[CWXMainFrame] Destructor." << endl;
186 #endif
187  delete m_theTimer;
188  oneInstance = nullptr;
189 
190  // Purge all pending requests:
192  while (nullptr != (msg = popPendingWxRequest())) delete[] msg;
193 }
194 
196 {
197  std::lock_guard<std::mutex> lock(cs_windowCount);
198  return ++m_windowCount;
199 }
200 
202 {
203  int ret;
204  {
205  std::lock_guard<std::mutex> lock(cs_windowCount);
206  ret = --m_windowCount;
207  }
208 
209  if (ret == 0)
210  {
211  // That was the last window... we should close the wx subsystem:
212  if (oneInstance)
213  {
214 #ifdef WXSHUTDOWN_DO_IT_CLEAN
215  CWXMainFrame* me =
216  (CWXMainFrame*)(oneInstance); // cast away the "volatile".
217  me->Close();
218 #endif
219 
220 #ifdef WXSUBSYSTEM_VERBOSE
221  cout << "[CWXMainFrame::notifyWindowDestruction] numWindows=0. "
222  "me->Close() called."
223  << endl;
224 #endif
225  }
226  }
227 
228  return ret;
229 }
230 
231 /** Thread-safe method to return the next pending request, or nullptr if there
232  * is none (After usage, FREE the memory!)
233  */
235 {
236  if (!cs_listPendingWxRequests)
237  {
238  cs_listPendingWxRequests = new std::mutex();
239  listPendingWxRequests = new std::queue<TRequestToWxMainThread*>;
240  }
241 
242  std::lock_guard<std::mutex> locker(*cs_listPendingWxRequests);
243 
244  // Is empty?
245  if (listPendingWxRequests->empty()) return nullptr;
246 
247  TRequestToWxMainThread* ret = listPendingWxRequests->front();
248  listPendingWxRequests->pop(); // Remove from the queue
249 
250  return ret;
251 }
252 
253 /** Thread-safe method to insert a new pending request (The memory must be
254  * dinamically allocated with "new T[1]", will be freed by receiver.)
255  */
258 {
260  {
261 #ifdef WXSUBSYSTEM_VERBOSE
262  cout << "[WxSubsystem::pushPendingWxRequest] IGNORING request since "
263  "app seems already closed.\n";
264 #endif
265  delete[] data;
266  return; // wx subsystem already closed, ignore.
267  }
268 
269  if (!cs_listPendingWxRequests)
270  {
271  cs_listPendingWxRequests = new std::mutex();
272  listPendingWxRequests = new std::queue<TRequestToWxMainThread*>;
273  }
274 
275  std::lock_guard<std::mutex> locker(*cs_listPendingWxRequests);
276  listPendingWxRequests->push(data);
277 }
278 
279 /** This method processes the pending requests from the main MRPT application
280  * thread.
281  * The requests may be to create a new window, close another one, change
282  * title, etc...
283  */
285 {
286  bool app_closed = false;
287  try
288  {
290 
291 #ifdef WXSUBSYSTEM_VERBOSE
292  cout << "[OnTimerProcessRequests] Entering" << endl;
293 #endif
294 
295  // For each pending request:
296  while (nullptr != (msg = popPendingWxRequest()))
297  {
298  // Process it:
299  switch (msg->OPCODE)
300  {
301  // CREATE NEW WINDOW
302  case 200:
303  if (msg->source2D)
304  {
305  auto* wnd = new CWindowDialog(
306  msg->source2D, this, (wxWindowID)-1, msg->str,
307  wxSize(msg->x, msg->y));
308 
309  // Set the "m_hwnd" member of the window:
310  *((void**)msg->voidPtr) = (void*)wnd;
311 
312  // Signal to the constructor (still waiting) that the
313  // window is now ready so it can continue:
315 
316  wnd->Show();
317  }
318  break;
319  // UPDATE IMAGE
320  case 201:
321  if (msg->source2D)
322  {
323  auto* wnd =
324  (CWindowDialog*)
325  msg->voidPtr; // msg->source2D->getWxObject();
326  if (!wnd) break;
327  auto* img = (wxImage*)msg->voidPtr2;
328  if (!img) break;
329 
330  wnd->m_image->AssignImage(new wxBitmap(
331  *img)); // Memory will be freed by the object.
332 
333  if (wnd->m_image->GetSize().GetX() != img->GetWidth() &&
334  wnd->m_image->GetSize().GetY() != img->GetHeight())
335  {
336  wnd->m_image->SetSize(
337  img->GetWidth(), img->GetHeight());
338  wnd->m_image->SetMinSize(
339  wxSize(img->GetWidth(), img->GetHeight()));
340  wnd->m_image->SetMaxSize(
341  wxSize(img->GetWidth(), img->GetHeight()));
342  wnd->Fit();
343  // wnd->SetClientSize(img->GetWidth(),
344  // img->GetHeight());
345  }
346  delete img;
347  wnd->m_image->Refresh(false); // false: Do NOT erase
348  // background: avoid
349  // flickering
350  }
351  break;
352  // Set position
353  case 202:
354  if (msg->source2D)
355  {
356  auto* wnd =
358  if (wnd)
359  wnd->SetSize(
360  msg->x, msg->y, wxDefaultCoord, wxDefaultCoord);
361  }
362  break;
363  // Set size
364  case 203:
365  if (msg->source2D)
366  {
367  auto* wnd =
369  if (wnd) wnd->SetClientSize(msg->x, msg->y);
370  }
371  break;
372  // Set window's title:
373  case 204:
374  if (msg->source2D)
375  {
376  auto* wnd =
378  if (wnd) wnd->SetTitle(msg->str.c_str());
379  }
380  break;
381  // DESTROY EXISTING WINDOW:
382  case 299:
383  if (msg->source2D)
384  {
385  auto* wnd =
387  if (wnd)
388  {
389  // delete wnd;
390  wnd->Close();
391  }
392  }
393  break;
394 
395  // CREATE NEW WINDOW
396  case 300:
397  if (msg->source3D)
398  {
399  auto* wnd = new C3DWindowDialog(
400  msg->source3D, this, (wxWindowID)-1, msg->str,
401  wxSize(msg->x, msg->y));
402 
403  // Set the "m_hwnd" member of the window:
404  *((void**)msg->voidPtr) = (void*)wnd;
405 
406  // Signal to the constructor (still waiting) that the
407  // window is now ready so it can continue:
409 
410  wnd->Show();
411  }
412  break;
413  // Set position
414  case 302:
415  if (msg->source3D)
416  {
417  auto* wnd =
419  if (wnd)
420  wnd->SetSize(
421  msg->x, msg->y, wxDefaultCoord, wxDefaultCoord);
422  }
423  break;
424  // Set size
425  case 303:
426  if (msg->source3D)
427  {
428  auto* wnd =
430  if (wnd) wnd->SetClientSize(msg->x, msg->y);
431  }
432  break;
433  // Set window's title:
434  case 304:
435  if (msg->source3D)
436  {
437  auto* wnd =
439  if (wnd) wnd->SetTitle(msg->str.c_str());
440  }
441  break;
442  // FORCE REPAINT
443  case 350:
444  if (msg->source3D)
445  {
446  auto* wnd =
448  if (wnd)
449  {
450  wnd->Refresh(false);
451  }
452  }
453  break;
454 
455  // DESTROY EXISTING WINDOW:
456  case 399:
457  if (msg->source3D)
458  {
459  auto* wnd =
461  if (wnd)
462  {
463  // delete wnd;
464  wnd->Close();
465  }
466  }
467  break;
468 
469  // CREATE NEW WINDOW
470  case 400:
471  if (msg->sourcePlots)
472  {
473  auto* wnd = new CWindowDialogPlots(
474  msg->sourcePlots, this, (wxWindowID)-1, msg->str,
475  wxSize(msg->x, msg->y));
476 
477  // Set the "m_hwnd" member of the window:
478  *((void**)msg->voidPtr) = (void*)wnd;
479 
480  // Signal to the constructor (still waiting) that the
481  // window is now ready so it can continue:
483 
484  wnd->Show();
485  }
486  break;
487  // Set position
488  case 402:
489  if (msg->sourcePlots)
490  {
491  auto* wnd = (CWindowDialogPlots*)
492  msg->sourcePlots->getWxObject();
493  if (wnd)
494  wnd->SetSize(
495  msg->x, msg->y, wxDefaultCoord, wxDefaultCoord);
496  }
497  break;
498  // Set size
499  case 403:
500  if (msg->sourcePlots)
501  {
502  auto* wnd = (CWindowDialogPlots*)
503  msg->sourcePlots->getWxObject();
504  if (wnd) wnd->SetClientSize(msg->x, msg->y);
505  }
506  break;
507  // Set window's title:
508  case 404:
509  if (msg->sourcePlots)
510  {
511  auto* wnd = (CWindowDialogPlots*)
512  msg->sourcePlots->getWxObject();
513  if (wnd) wnd->SetTitle(msg->str.c_str());
514  }
515  break;
516  // Mouse pan
517  case 410:
518  if (msg->sourcePlots)
519  {
520  auto* wnd = (CWindowDialogPlots*)
521  msg->sourcePlots->getWxObject();
522  if (wnd) wnd->m_plot->EnableMousePanZoom(msg->boolVal);
523  }
524  break;
525  // Aspect ratio
526  case 411:
527  if (msg->sourcePlots)
528  {
529  auto* wnd = (CWindowDialogPlots*)
530  msg->sourcePlots->getWxObject();
531  if (wnd) wnd->m_plot->LockAspect(msg->boolVal);
532  }
533  break;
534 
535  // Zoom over a rectangle vectorx[0-1] & vectory[0-1]
536  case 412:
537  if (msg->sourcePlots)
538  {
539  auto* wnd = (CWindowDialogPlots*)
540  msg->sourcePlots->getWxObject();
541  if (wnd)
542  {
543  if (msg->vector_x.size() == 2 &&
544  msg->vector_y.size() == 2)
545  {
546  wnd->m_plot->Fit(
547  msg->vector_x[0], msg->vector_x[1],
548  msg->vector_y[0], msg->vector_y[1]);
549  wnd->m_plot->LockAspect(msg->boolVal);
550  }
551  }
552  }
553  break;
554  // Axis fit, with aspect ratio fix to boolVal.
555  case 413:
556  if (msg->sourcePlots)
557  {
558  auto* wnd = (CWindowDialogPlots*)
559  msg->sourcePlots->getWxObject();
560  if (wnd)
561  {
562  wnd->m_plot->LockAspect(msg->boolVal);
563  wnd->m_plot->Fit();
564  }
565  }
566  break;
567  // Clear all objects:
568  case 414:
569  if (msg->sourcePlots)
570  {
571  auto* wnd = (CWindowDialogPlots*)
572  msg->sourcePlots->getWxObject();
573  if (wnd)
574  {
575  wnd->m_plot->DelAllLayers(true, true);
576  wnd->m_plot->AddLayer(new mpScaleX());
577  wnd->m_plot->AddLayer(new mpScaleY());
578  }
579  }
580  break;
581 
582  // Create/modify 2D plot
583  case 420:
584  if (msg->sourcePlots)
585  {
586  auto* wnd = (CWindowDialogPlots*)
587  msg->sourcePlots->getWxObject();
588  if (wnd)
589  wnd->plot(
590  msg->vector_x, msg->vector_y, msg->str,
591  msg->plotName);
592  }
593  break;
594 
595  // Create/modify 2D ellipse
596  case 421:
597  if (msg->sourcePlots)
598  {
599  auto* wnd = (CWindowDialogPlots*)
600  msg->sourcePlots->getWxObject();
601  if (wnd)
602  wnd->plotEllipse(
603  msg->vector_x, msg->vector_y, msg->str,
604  msg->plotName, msg->boolVal);
605  }
606  break;
607 
608  // Create/modify bitmap image
609  case 422:
610  if (msg->sourcePlots)
611  {
612  auto* wnd = (CWindowDialogPlots*)
613  msg->sourcePlots->getWxObject();
614  if (wnd)
615  wnd->image(
616  msg->voidPtr2, msg->vector_x[0],
617  msg->vector_x[1], msg->vector_x[2],
618  msg->vector_x[3], msg->plotName);
619  }
620  break;
621 
622  // 440: Insert submenu in the popup menu. name=menu label, x=ID
623  case 440:
624  if (msg->sourcePlots)
625  {
626  auto* wnd = (CWindowDialogPlots*)
627  msg->sourcePlots->getWxObject();
628  if (wnd)
629  {
630  const long MENUITEM_ID = wxNewId();
631  // Remember the association between this ID and the
632  // user ID:
633  wnd->m_ID2ID[MENUITEM_ID] = msg->x;
634 
635  wxMenu* popupMnu = wnd->m_plot->GetPopupMenu();
636  if (wnd->m_firstSubmenu)
637  {
638  wnd->m_firstSubmenu = false;
639  popupMnu->InsertSeparator(0);
640  }
641  wxMenuItem* mnuTarget = new wxMenuItem(
642  popupMnu, MENUITEM_ID, msg->plotName.c_str(),
643  wxEmptyString, wxITEM_NORMAL);
644  popupMnu->Insert(0, mnuTarget);
645 
646  wnd->Bind(
648  wnd, MENUITEM_ID);
649  }
650  }
651  break;
652 
653  // DESTROY EXISTING WINDOW:
654  case 499:
655  if (msg->sourcePlots)
656  {
657  auto* wnd = (CWindowDialogPlots*)
658  msg->sourcePlots->getWxObject();
659  if (wnd)
660  {
661  // delete wnd;
662  wnd->Close();
663  }
664  }
665  break;
666 
667  // CREATE NEW WINDOW
668  case 700:
669  if (msg->sourceCameraSelectDialog)
670  {
671  auto* sem =
672  reinterpret_cast<std::promise<void>*>(msg->voidPtr);
673 
674  auto dlg = std::make_unique<CDialogAskUserForCamera>();
675 
676  // Signal that the window is ready:
677  sem->set_value();
678 
679  // Show
680  const bool wasOk = (dlg->ShowModal() == wxID_OK);
681 
682  // send selection to caller:
683  auto* promise = reinterpret_cast<std::promise<
685  msg->voidPtr2);
687 
688  // Parse selection as a config text block:
690  dlg->panel->writeConfigFromVideoSourcePanel(
691  "CONFIG", &c);
692  c.getContent(ret.selectedConfig);
693  ret.accepted_by_user = wasOk;
694 
695  promise->set_value(std::move(ret));
696  dlg->Close();
697  }
698  break;
699 
700  // wxSubsystem shutdown:
701  case 999:
702  {
703 #ifdef WXSUBSYSTEM_VERBOSE
704  cout << "[WxSubsystem:999] Shutdown" << endl;
705 #endif
706  app_closed = true; // Do NOT launch a timer again
709  CWXMainFrame::
710  oneInstance))
711  ->Close();
712 #ifdef WXSUBSYSTEM_VERBOSE
713  cout << "[WxSubsystem:999] Shutdown done" << endl;
714 #endif
715  }
716  break;
717 
718  } // end switch OPCODE
719 
720  // Free the memory:
721  delete[] msg;
722  } // end while
723  }
724  catch (...)
725  {
726  }
727 
728  if (!app_closed) m_theTimer->Start(10, true); // One-shot
729 }
730 
731 // ---------------------------------------------------------------------------------------
732 // MRPT Icons
733 // ---------------------------------------------------------------------------------------
734 const char* mrpt_default_icon_xpm[] = {"32 32 2 1",
735  " c None",
736  ". c #000000",
737  " ",
738  " ",
739  " ",
740  " ..... ..... ......... ",
741  " .... .... ... .... ",
742  " ..... .... ... ... ",
743  " . ... . ... ... ... ",
744  " . ... . ... ... ... ",
745  " . ... . ... ... ... ",
746  " . ... . ... ........ ",
747  " . ..... ... ... .... ",
748  " . ... ... ... .... ",
749  " . ... ... ... .... ",
750  " . .. ... ... .... ",
751  " ... . ..... ..... ..... ",
752  " ",
753  " ",
754  " ........ ........... ",
755  " ... .... .. ... .. ",
756  " ... ... . ... . ",
757  " ... ... ... ",
758  " ... ... ... ",
759  " ... ... ... ",
760  " ....... ... ",
761  " ... ... ",
762  " ... ... ",
763  " ... ... ",
764  " ... ... ",
765  " ..... ..... ",
766  " ",
767  " ",
768  " "};
769 
771 {
772 // To avoid an error in wx, always resize the icon to the expected size:
773 #ifdef _WIN32
774  const wxSize iconsSize(
775  ::GetSystemMetrics(SM_CXICON), ::GetSystemMetrics(SM_CYICON));
776  return wxBitmap(wxBitmap(mrpt_default_icon_xpm)
777  .ConvertToImage()
778  .Scale(iconsSize.x, iconsSize.y));
779 #else
780  return wxBitmap(mrpt_default_icon_xpm);
781 #endif
782 }
783 
784 // ---------------------------------------------------------------------------------------
785 // The wx app:
786 // ---------------------------------------------------------------------------------------
787 class CDisplayWindow_WXAPP : public wxApp
788 {
789  public:
790  bool OnInit() override;
791  int OnExit() override;
792 };
793 
795 {
796  // Starting in wxWidgets 2.9.0, we must reset numerics locale to "C",
797  // if we want numbers to use "." in all countries. The App::OnInit() is a
798  // perfect place to undo
799  // the default wxWidgets settings. (JL @ Sep-2009)
800  wxSetlocale(LC_NUMERIC, wxString(wxT("C")));
801 
802  wxInitAllImageHandlers();
803 
804  // cout << "[wxApp::OnInit] wxApplication OnInit called." << endl;
805 
806  // Create a dummy frame:
807  auto* Frame = new WxSubsystem::CWXMainFrame(nullptr);
808  Frame->Hide();
809 
810  // We are ready!!
811  // cout << "[wxMainThread] Signaling semaphore." << endl;
813 
814  return true;
815 }
816 
817 // This will be called when all the windows / frames are closed.
819 {
820 #ifdef WXSUBSYSTEM_VERBOSE
821  cout << "[wxApp::OnExit] wxApplication OnExit called." << endl;
822 #endif
823 
824  std::lock_guard<std::mutex> lock(
825  WxSubsystem::GetWxMainThreadInstance().m_csWxMainThreadId);
826 
827  wxApp::OnExit();
828  CleanUp();
829  return 0;
830 }
831 
832 /** This method must be called in the destructor of the user class FROM THE MAIN
833  * THREAD, in order to wait for the shutdown of the wx thread if this was the
834  * last open window.
835  */
837 {
838 #ifndef WXSHUTDOWN_DO_IT_CLEAN
839 
840 #ifdef WXSUBSYSTEM_VERBOSE
841  cout << "[WxSubsystem::waitWxShutdownsIfNoWindows] Doing a quick "
842  "std::this_thread::sleep_for(ms) and returning.\n";
843 #endif
844  std::this_thread::sleep_for(100ms);
845  return;
846 #else
847  // Just let know a global object that, at its destruction, it must ....
848  // Any open windows?
849  int nOpenWnds;
850  {
851  std::lock_guard<std::mutex> lock(CWXMainFrame::cs_windowCount);
852  nOpenWnds = CWXMainFrame::m_windowCount;
853  }
854 
855  if (!nOpenWnds && WxSubsystem::isConsoleApp())
856  {
857 #ifdef WXSUBSYSTEM_VERBOSE
858  cout << "[WxSubsystem::waitWxShutdownsIfNoWindows] Waiting for "
859  "WxWidgets thread to shutdown...\n";
860 #endif
861 
862  // Then we must be shutting down in the wx thread (we are in the main
863  // MRPT application thread)...
864  // Wait until wx is safely shut down:
865  bool done = false;
866  int maxTimeout =
867 #ifdef _DEBUG
868  30000;
869 #else
870  5000;
871 #endif
872  if (m_done.wait_for(std::chrono::milliseconds(maxTimeout)) ==
873  std::future_status::timeout)
874  {
875  cerr << "[WxSubsystem::waitWxShutdownsIfNoWindows] Timeout waiting "
876  "for WxWidgets thread to shutdown!"
877  << endl;
878  }
879  }
880 #endif
881 }
882 
883 wxAppConsole* mrpt_wxCreateApp()
884 {
885  wxAppConsole::CheckBuildOptions(WX_BUILD_OPTIONS_SIGNATURE, "your program");
886  return new CDisplayWindow_WXAPP;
887 }
888 
889 // DECLARE_APP(CDisplayWindow_WXAPP)
891 
892 // Aux. funcs used in WxSubsystem::wxMainThread
893 // --------------------------------------------------
894 int mrpt_wxEntryReal(int argc, char** argv)
895 {
896  // library initialization
897  if (!wxEntryStart(argc, argv))
898  {
899 #if wxUSE_LOG
900  // flush any log messages explaining why we failed
901  delete wxLog::SetActiveTarget(nullptr);
902 #endif
903  return -1;
904  }
905 
906  // if wxEntryStart succeeded, we must call wxEntryCleanup even if the code
907  // below returns or throws
908  try
909  {
910  // app initialization
911  if (!wxTheApp->CallOnInit())
912  return -1; // don't call OnExit() if OnInit() failed
913 
914  // app execution
915  int ret = wxTheApp->OnRun();
916 
917  {
918  wxLogNull logNo; // Skip any warning in this scope.
919 
920  wxTheApp->OnExit(); // This replaces the above callOnExit class
921  wxEntryCleanup();
922  }
923 
924  return ret;
925  }
926  catch (...)
927  {
928  wxTheApp->OnUnhandledException();
929  wxEntryCleanup();
930  return -1;
931  }
932 }
933 
934 /*---------------------------------------------------------------
935  wxMainThread
936  This will be the "MAIN" of wxWidgets: It starts an application
937  object and does not end until all the windows are closed.
938  Only for console apps, not for user GUI apps already with wx.
939  ---------------------------------------------------------------*/
941 {
942  MRPT_START
943 
944  // Prepare wxWidgets:
945  int argc = 1;
946  static const char* dummy_prog_name = "./MRPT";
947  char* argv[2] = {const_cast<char*>(dummy_prog_name), nullptr};
948 
949 #ifdef WXSUBSYSTEM_VERBOSE
950  cout << "[wxMainThread] Starting..." << endl;
951 #endif
952 
953  // Are we in a console or wxGUI application????
954  wxAppConsole* app_gui = wxApp::GetInstance();
955  if (!app_gui)
956  {
957 // We are NOT in a wx application (it's a console program)
958 // ---------------------------------------------------------
959 #ifdef WXSUBSYSTEM_VERBOSE
960  cout << "[wxMainThread] I am in a console app" << endl;
961 #endif
962  // Start a new wx application object:
963 
964  // JLBC OCT2008: wxWidgets little hack to enable console/gui mixed
965  // applications:
966  wxApp::SetInitializerFunction(
967  (wxAppInitializerFunction)mrpt_wxCreateApp);
969 
970 #ifdef WXSUBSYSTEM_VERBOSE
971  cout << "[wxMainThread] Finished" << endl;
972 #endif
973 
974  // Now this thread is ready. The main thread is free to end now:
976  }
977  else
978  {
979 // We are ALREADY in a wx application:
980 // ---------------------------------------------------------
981 #ifdef WXSUBSYSTEM_VERBOSE
982  cout << "[wxMainThread] I am in a GUI app" << endl;
983 #endif
984  wxWindow* topWin = static_cast<wxApp*>(app_gui)->GetTopWindow();
985 
986  auto* Frame = new WxSubsystem::CWXMainFrame(topWin);
987  Frame->Hide();
988 
989 // We are ready!!
990 #ifdef WXSUBSYSTEM_VERBOSE
991  cout << "[wxMainThread] Signaling semaphore." << endl;
992 #endif
994  .m_semWxMainThreadReady.set_value();
995  }
996 
997  MRPT_END
998 }
999 
1001 {
1002  // static TWxMainThreadData dat;
1003  // Create as dynamic memory, since it'll be deleted in
1004  // CAuxWxSubsystemShutdowner:
1005  static TWxMainThreadData* dat = nullptr;
1006  static bool first_creat = true;
1007  if (!dat && first_creat)
1008  {
1009  first_creat = false;
1010  dat = new TWxMainThreadData;
1011  }
1012  return *dat;
1013 }
1014 
1015 /*---------------------------------------------------------------
1016  createOneInstanceMainThread
1017  ---------------------------------------------------------------*/
1019 {
1022  std::lock_guard<std::mutex> lock(wxmtd.m_csWxMainThreadId);
1023 
1024  wxAppConsole* app_con = wxApp::GetInstance();
1025  if (app_con && wxmtd.m_wxMainThreadId.get_id() == std::thread::id())
1026  {
1027  // We are NOT in a console application: There is already a wxApp
1028  // instance running and it's not us.
1029  isConsoleApp_value = false;
1030  // cout << "[createOneInstanceMainThread] Mode: User GUI." << endl;
1032  {
1033  // Create our main hidden frame:
1034  wxWindow* topWin = static_cast<wxApp*>(app_con)->GetTopWindow();
1035 
1036  auto* Frame = new WxSubsystem::CWXMainFrame(topWin);
1037  // Frame->Show();
1038  // SetTopWindow(Frame);
1039  Frame->Hide();
1040  }
1041  }
1042  else
1043  {
1044  // cout << "[createOneInstanceMainThread] Mode: Console." << endl;
1045  isConsoleApp_value = true;
1046  if (wxmtd.m_wxMainThreadId.get_id() == std::thread::id())
1047  {
1048 #ifdef WXSUBSYSTEM_VERBOSE
1049  printf(
1050  "[WxSubsystem::createOneInstanceMainThread] Launching "
1051  "wxMainThread() thread...\n");
1052 #endif
1053  // Create a thread for message processing there:
1054  wxmtd.m_wxMainThreadId = std::thread(wxMainThread);
1055 
1056  int maxTimeout =
1057 #ifdef _DEBUG
1058  30000;
1059 #else
1060  5000;
1061 #endif
1062 
1063  // If we have an "MRPT_WXSUBSYS_TIMEOUT_MS" environment variable,
1064  // use that timeout instead:
1065  const char* envVal = getenv("MRPT_WXSUBSYS_TIMEOUT_MS");
1066  if (envVal) maxTimeout = atoi(envVal);
1067 
1068  if (wxmtd.m_semWxMainThreadReady.get_future().wait_for(
1069  std::chrono::milliseconds(maxTimeout)) ==
1070  std::future_status::timeout) // A few secs should be enough...
1071  {
1072  cerr << "[WxSubsystem::createOneInstanceMainThread] Timeout "
1073  "waiting wxApplication to start up!"
1074  << endl;
1075  return false;
1076  }
1077  }
1078  }
1079 
1080  return true; // OK
1081 }
1082 
1083 #endif // MRPT_HAS_WXWIDGETS
mrpt_default_icon_xpm
const char * mrpt_default_icon_xpm[]
Definition: WxSubsystem.cpp:734
os.h
mrpt::gui::WxSubsystem::TRequestToWxMainThread::sourcePlots
mrpt::gui::CDisplayWindowPlots * sourcePlots
Only one of source* can be non-nullptr, indicating the class that generated the request.
Definition: WxSubsystem.h:203
mrpt::gui::WxSubsystem::TRequestToWxMainThread::voidPtr
void * voidPtr
Parameters, depending on OPCODE.
Definition: WxSubsystem.h:215
mrpt::opengl::internal::data
static struct FontData data
Definition: gltext.cpp:144
mrpt_wxEntryReal
int mrpt_wxEntryReal(int argc, char **argv)
Definition: WxSubsystem.cpp:894
mrpt::gui::CPanelCameraSelection
A panel to select the camera input from all the formats supported by MRPT.
Definition: WxUtils.h:154
mrpt::gui::WxSubsystem::createOneInstanceMainThread
static bool createOneInstanceMainThread()
Thread-safe method to create one single instance of the main wxWidgets thread: it will create the thr...
Definition: WxSubsystem.cpp:1018
mrpt::gui::CWindowDialog
The wx dialog for gui::CDisplayWindow.
Definition: WxSubsystem.h:315
CDialogAskUserForCamera::CDialogAskUserForCamera
CDialogAskUserForCamera()
Definition: WxSubsystem.cpp:103
CDialogAskUserForCamera::ID_BTN_CANCEL
static const long ID_BTN_CANCEL
Definition: WxSubsystem.cpp:101
mrpt::gui::WxSubsystem::pushPendingWxRequest
static void pushPendingWxRequest(TRequestToWxMainThread *data)
Thread-safe method to insert a new pending request (The memory must be dinamically allocated with "ne...
Definition: WxSubsystem.cpp:256
WxSubsystem.h
mrpt::gui::detail::TReturnAskUserOpenCamera
Definition: WxUtils.h:278
CDialogAskUserForCamera::panel
mrpt::gui::CPanelCameraSelection * panel
Definition: WxSubsystem.cpp:98
mrpt::gui::WxSubsystem::CWXMainFrame::m_windowCount
static int m_windowCount
Definition: WxSubsystem.h:157
mrpt::gui::WxSubsystem::listPendingWxRequests
static std::queue< TRequestToWxMainThread * > * listPendingWxRequests
Do not access directly to this, use the thread-safe functions.
Definition: WxSubsystem.h:306
mrpt::gui::CWindowDialogPlots::OnMenuSelected
void OnMenuSelected(wxCommandEvent &ev)
Definition: CDisplayWindowPlots.cpp:248
mrpt::gui::WxSubsystem::CAuxWxSubsystemShutdowner
An auxiliary global object used just to launch a final request to the wxSubsystem for shutdown:
Definition: WxSubsystem.h:121
mrpt::gui::WxSubsystem::TWxMainThreadData::m_done
std::promise< void > m_done
Definition: WxSubsystem.h:173
mrpt
This is the global namespace for all Mobile Robot Programming Toolkit (MRPT) libraries.
Definition: BaseAppDataSource.h:15
CDisplayWindow_WXAPP::OnInit
bool OnInit() override
Definition: WxSubsystem.cpp:794
mrpt::gui::WxSubsystem::wxMainThread
static void wxMainThread()
This will be the "MAIN" of wxWidgets: It starts an application object and does not end until all the ...
Definition: WxSubsystem.cpp:940
CDialogAskUserForCamera::OnBtnOk
void OnBtnOk(wxCommandEvent &event)
Definition: WxSubsystem.cpp:140
mrpt::gui::WxSubsystem::isConsoleApp
static bool isConsoleApp()
Will be set to true at runtime if it's not detected a running wxApp instance.
Definition: WxSubsystem.cpp:53
CDialogAskUserForCamera::OnBtnCancel
void OnBtnCancel(wxCommandEvent &event)
Definition: WxSubsystem.cpp:141
CDialogAskUserForCamera
Definition: WxSubsystem.cpp:95
CDisplayWindow3D.h
mrpt::gui::WxSubsystem::TWxMainThreadData::m_semWxMainThreadReady
std::promise< void > m_semWxMainThreadReady
This is signaled when wxMainThread is ready.
Definition: WxSubsystem.h:172
mrpt::gui::WxSubsystem
This class implements the GUI thread required for the wxWidgets-based GUI.
Definition: WxSubsystem.h:96
mrpt::gui::WxSubsystem::GetWxMainThreadInstance
static TWxMainThreadData & GetWxMainThreadInstance()
Definition: WxSubsystem.cpp:1000
WxUtils.h
mrpt::gui::WxSubsystem::TRequestToWxMainThread::vector_x
mrpt::math::CVectorFloat vector_x
Definition: WxSubsystem.h:218
mrpt::gui::WxSubsystem::getMRPTDefaultIcon
static wxBitmap getMRPTDefaultIcon()
Definition: WxSubsystem.cpp:770
mrpt::gui::WxSubsystem::CWXMainFrame::notifyWindowCreation
static int notifyWindowCreation()
Atomically increments the number of windows created with the main frame as parent.
Definition: WxSubsystem.cpp:195
mrpt::gui::WxSubsystem::TRequestToWxMainThread::source2D
mrpt::gui::CDisplayWindow * source2D
Only one of source* can be non-nullptr, indicating the class that generated the request.
Definition: WxSubsystem.h:195
mrpt::gui::CBaseGUIWindow::notifySemThreadReady
void notifySemThreadReady()
Called by wx main thread to signal the semaphore that the wx window is built and ready.
Definition: CBaseGUIWindow.cpp:205
mrpt::gui::WxSubsystem::CWXMainFrame::notifyWindowDestruction
static int notifyWindowDestruction()
Atomically decrements the number of windows created with the main frame as parent.
Definition: WxSubsystem.cpp:201
mrpt::gui::WxSubsystem::CAuxWxSubsystemShutdowner::~CAuxWxSubsystemShutdowner
~CAuxWxSubsystemShutdowner()
Definition: WxSubsystem.cpp:58
mrpt::gui::WxSubsystem::TRequestToWxMainThread::str
std::string str
Parameters, depending on OPCODE.
Definition: WxSubsystem.h:211
mrpt::gui::WxSubsystem::TRequestToWxMainThread
The data structure for each inter-thread request:
Definition: WxSubsystem.h:189
wxGetApp
CDisplayWindow_WXAPP & wxGetApp()
mrpt::gui::WxSubsystem::TWxMainThreadData
Definition: WxSubsystem.h:167
mrpt::gui::WxSubsystem::TRequestToWxMainThread::boolVal
bool boolVal
Definition: WxSubsystem.h:217
mrpt::gui::WxSubsystem::CAuxWxSubsystemShutdowner::CAuxWxSubsystemShutdowner
CAuxWxSubsystemShutdowner()
MRPT_START
#define MRPT_START
Definition: exceptions.h:241
CDialogAskUserForCamera::ID_BTN_OK
static const long ID_BTN_OK
Definition: WxSubsystem.cpp:100
mrpt::gui::WxSubsystem::popPendingWxRequest
static TRequestToWxMainThread * popPendingWxRequest()
Thread-safe method to return the next pending request, or nullptr if there is none (After usage,...
Definition: WxSubsystem.cpp:234
mrpt::gui::WxSubsystem::CWXMainFrame::OnTimerProcessRequests
void OnTimerProcessRequests(wxTimerEvent &event)
This method processes the pending requests from the main MRPT application thread.
Definition: WxSubsystem.cpp:284
mrpt::gui::WxSubsystem::TRequestToWxMainThread::OPCODE
int OPCODE
Valid codes are: For CDisplayWindow:
Definition: WxSubsystem.h:283
mrpt::gui::WxSubsystem::global_wxsubsystem_shutdown
static CAuxWxSubsystemShutdowner global_wxsubsystem_shutdown
Definition: WxSubsystem.h:128
mrpt::gui::C3DWindowDialog
Definition: WxSubsystem.h:381
mrpt::gui::WxSubsystem::TRequestToWxMainThread::y
int y
Definition: WxSubsystem.h:216
mrpt::gui::WxSubsystem::TRequestToWxMainThread::plotName
std::string plotName
Definition: WxSubsystem.h:219
mrpt::gui::WxSubsystem::TWxMainThreadData::m_csWxMainThreadId
std::mutex m_csWxMainThreadId
The critical section for accessing "m_wxMainThreadId".
Definition: WxSubsystem.h:175
argv
const char * argv[]
Definition: RawlogGrabberApp_unittest.cpp:40
gui-precomp.h
mrpt::gui::WxSubsystem::cs_listPendingWxRequests
static std::mutex * cs_listPendingWxRequests
Definition: WxSubsystem.h:307
mrpt::config::CConfigFileMemory::getContent
void getContent(std::string &str) const
Return the current contents of the virtual "config file".
Definition: CConfigFileMemory.cpp:65
mrpt_wxCreateApp
wxAppConsole * mrpt_wxCreateApp()
Definition: WxSubsystem.cpp:883
mrpt::gui
Classes for creating GUI windows for 2D and 3D visualization.
Definition: about_box.h:14
mrpt::gui::WxSubsystem::CWXMainFrame
The main frame of the wxWidgets application.
Definition: WxSubsystem.h:132
mrpt::math::CVectorDynamic::size
size_type size() const
Get a 2-vector with [NROWS NCOLS] (as in MATLAB command size(x))
Definition: CVectorDynamic.h:141
mrpt::gui::WxSubsystem::TRequestToWxMainThread::sourceCameraSelectDialog
bool sourceCameraSelectDialog
Only one of source* can be non-nullptr, indicating the class that generated the request.
Definition: WxSubsystem.h:207
mrpt::gui::detail::TReturnAskUserOpenCamera::accepted_by_user
bool accepted_by_user
Definition: WxUtils.h:281
mrpt::gui::WxSubsystem::waitWxShutdownsIfNoWindows
static void waitWxShutdownsIfNoWindows()
This method must be called in the destructor of the user class FROM THE MAIN THREAD,...
Definition: WxSubsystem.cpp:836
argc
const int argc
Definition: RawlogGrabberApp_unittest.cpp:41
mrpt::gui::WxSubsystem::CWXMainFrame::cs_windowCount
static std::mutex cs_windowCount
Definition: WxSubsystem.h:156
MRPT_END
#define MRPT_END
Definition: exceptions.h:245
CDisplayWindow_WXAPP
Definition: WxSubsystem.cpp:787
mrpt::gui::WxSubsystem::TRequestToWxMainThread::x
int x
Definition: WxSubsystem.h:216
mrpt::gui::WxSubsystem::TRequestToWxMainThread::vector_y
mrpt::math::CVectorFloat vector_y
Definition: WxSubsystem.h:218
mrpt::config::CConfigFileMemory
This class implements a config file-like interface over a memory-stored string list.
Definition: config/CConfigFileMemory.h:36
mrpt::gui::CBaseGUIWindow::getWxObject
void * getWxObject()
Read-only access to the wxDialog object.
Definition: CBaseGUIWindow.h:77
CDisplayWindowPlots.h
mrpt::gui::WxSubsystem::TRequestToWxMainThread::source3D
mrpt::gui::CDisplayWindow3D * source3D
Only one of source* can be non-nullptr, indicating the class that generated the request.
Definition: WxSubsystem.h:199
isConsoleApp_value
bool isConsoleApp_value
Definition: WxSubsystem.cpp:52
ID_TIMER_WX_PROCESS_REQUESTS
const long ID_TIMER_WX_PROCESS_REQUESTS
Definition: WxSubsystem.cpp:154
mrpt::gui::WxSubsystem::CWXMainFrame::oneInstance
static volatile CWXMainFrame * oneInstance
Definition: WxSubsystem.h:153
CDisplayWindow.h
mrpt::gui::CWindowDialogPlots
The wx dialog for gui::CDisplayWindowPlots.
Definition: WxSubsystem.h:413
mrpt::gui::detail::TReturnAskUserOpenCamera::selectedConfig
std::string selectedConfig
Definition: WxUtils.h:280
mrpt::gui::WxSubsystem::CWXMainFrame::~CWXMainFrame
~CWXMainFrame() override
Definition: WxSubsystem.cpp:182
mrpt::gui::WxSubsystem::TRequestToWxMainThread::voidPtr2
void * voidPtr2
Definition: WxSubsystem.h:215
CDisplayWindow_WXAPP::OnExit
int OnExit() override
Definition: WxSubsystem.cpp:818
mrpt::gui::WxSubsystem::TWxMainThreadData::m_wxMainThreadId
std::thread m_wxMainThreadId
The thread ID of wxMainThread, or 0 if it is not running.
Definition: WxSubsystem.h:170



Page generated by Doxygen 1.8.17 for MRPT 2.0.3 at Fri May 15 15:49:54 UTC 2020