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>
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
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
#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:98
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)
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
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:53
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:52
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: 5711e29ae Wed May 27 14:29:47 2020 +0200 at miƩ may 27 14:30:10 CEST 2020