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
An auxiliary global object used just to launch a final request to the wxSubsystem for shutdown: ...
Definition: WxSubsystem.h:121
static void pushPendingWxRequest(TRequestToWxMainThread *data)
Thread-safe method to insert a new pending request (The memory must be dinamically allocated with "ne...
void * voidPtr
Parameters, depending on OPCODE.
Definition: WxSubsystem.h:215
This class implements a config file-like interface over a memory-stored string list.
static const long ID_BTN_CANCEL
Definition: WxSubsystem.cpp:99
#define MRPT_START
Definition: exceptions.h:241
static TRequestToWxMainThread * popPendingWxRequest()
Thread-safe method to return the next pending request, or nullptr if there is none (After usage...
mrpt::gui::CDisplayWindow3D * source3D
Only one of source* can be non-nullptr, indicating the class that generated the request.
Definition: WxSubsystem.h:199
The data structure for each inter-thread request:
Definition: WxSubsystem.h:189
static int notifyWindowCreation()
Atomically increments the number of windows created with the main frame as parent.
void OnTimerProcessRequests(wxTimerEvent &event)
This method processes the pending requests from the main MRPT application thread. ...
static std::mutex * cs_listPendingWxRequests
Definition: WxSubsystem.h:307
std::thread m_wxMainThreadId
The thread ID of wxMainThread, or 0 if it is not running.
Definition: WxSubsystem.h:170
mrpt::gui::CPanelCameraSelection * panel
Definition: WxSubsystem.cpp:96
size_type size() const
Get a 2-vector with [NROWS NCOLS] (as in MATLAB command size(x))
The wx dialog for gui::CDisplayWindowPlots.
Definition: WxSubsystem.h:413
static wxBitmap getMRPTDefaultIcon()
STL namespace.
const char * mrpt_default_icon_xpm[]
static volatile CWXMainFrame * oneInstance
Definition: WxSubsystem.h:153
std::mutex m_csWxMainThreadId
The critical section for accessing "m_wxMainThreadId".
Definition: WxSubsystem.h:175
static TWxMainThreadData & GetWxMainThreadInstance()
void OnBtnOk(wxCommandEvent &event)
int mrpt_wxEntryReal(int argc, char **argv)
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
int OPCODE
Valid codes are: For CDisplayWindow:
Definition: WxSubsystem.h:283
const long ID_TIMER_WX_PROCESS_REQUESTS
static std::queue< TRequestToWxMainThread * > * listPendingWxRequests
Do not access directly to this, use the thread-safe functions.
Definition: WxSubsystem.h:306
static const long ID_BTN_OK
Definition: WxSubsystem.cpp:98
void OnMenuSelected(wxCommandEvent &ev)
wxAppConsole * mrpt_wxCreateApp()
std::promise< void > m_semWxMainThreadReady
This is signaled when wxMainThread is ready.
Definition: WxSubsystem.h:172
bool OnInit() override
void * getWxObject()
Read-only access to the wxDialog object.
The main frame of the wxWidgets application.
Definition: WxSubsystem.h:132
The wx dialog for gui::CDisplayWindow.
Definition: WxSubsystem.h:315
void OnBtnCancel(wxCommandEvent &event)
bool sourceCameraSelectDialog
Only one of source* can be non-nullptr, indicating the class that generated the request.
Definition: WxSubsystem.h:207
static void wxMainThread()
This will be the "MAIN" of wxWidgets: It starts an application object and does not end until all the ...
static void waitWxShutdownsIfNoWindows()
This method must be called in the destructor of the user class FROM THE MAIN THREAD, in order to wait for the shutdown of the wx thread if this was the last open window.
This is the global namespace for all Mobile Robot Programming Toolkit (MRPT) libraries.
const char * argv[]
This class implements the GUI thread required for the wxWidgets-based GUI.
Definition: WxSubsystem.h:96
CDisplayWindow_WXAPP & wxGetApp()
#define MRPT_END
Definition: exceptions.h:245
void getContent(std::string &str) const
Return the current contents of the virtual "config file".
static CAuxWxSubsystemShutdowner global_wxsubsystem_shutdown
Definition: WxSubsystem.h:128
A panel to select the camera input from all the formats supported by MRPT.
Definition: WxUtils.h:154
mrpt::gui::CDisplayWindowPlots * sourcePlots
Only one of source* can be non-nullptr, indicating the class that generated the request.
Definition: WxSubsystem.h:203
static bool isConsoleApp()
Will be set to true at runtime if it&#39;s not detected a running wxApp instance.
Definition: WxSubsystem.cpp:51
Classes for creating GUI windows for 2D and 3D visualization.
Definition: about_box.h:14
const int argc
mrpt::gui::CDisplayWindow * source2D
Only one of source* can be non-nullptr, indicating the class that generated the request.
Definition: WxSubsystem.h:195
bool isConsoleApp_value
Definition: WxSubsystem.cpp:50
std::string str
Parameters, depending on OPCODE.
Definition: WxSubsystem.h:211
void notifySemThreadReady()
Called by wx main thread to signal the semaphore that the wx window is built and ready.
int OnExit() override
static bool createOneInstanceMainThread()
Thread-safe method to create one single instance of the main wxWidgets thread: it will create the thr...
static int notifyWindowDestruction()
Atomically decrements the number of windows created with the main frame as parent.
static struct FontData data
Definition: gltext.cpp:144



Page generated by Doxygen 1.8.14 for MRPT 2.0.4 Git: 33de1d0ad Sat Jun 20 11:02:42 2020 +0200 at sáb jun 20 17:35:17 CEST 2020