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