MRPT  1.9.9
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-2019, 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  // Add a 2D text message: vector_x: [0]:x, [1]:y, [2,3,4]:R G B,
455  // "x": enum of desired font. "y": unique index, "str": String.
456  case 360:
457  if (msg->source3D)
458  {
459  auto* wnd =
461  if (wnd)
462  {
463  wnd->addTextMessage(
464  msg->vector_x[0], msg->vector_x[1], msg->str,
466  msg->vector_x[2], msg->vector_x[3],
467  msg->vector_x[4]),
468  size_t(msg->y),
470  }
471  }
472  break;
473  // Clear 2D text messages
474  case 361:
475  if (msg->source3D)
476  {
477  auto* wnd =
479  if (wnd)
480  {
481  wnd->clearTextMessages();
482  }
483  }
484  break;
485  // Add a 2D text message: vector_x: [0]:x, [1]:y, [2,3,4]:R G B,
486  // "x": enum of desired font. "y": unique index, "str": String.
487  case 362:
488  if (msg->source3D)
489  {
490  auto* wnd =
492  if (wnd)
493  {
494  wnd->addTextMessage(
495  msg->vector_x[0], msg->vector_x[1], msg->str,
497  msg->vector_x[2], msg->vector_x[3],
498  msg->vector_x[4]),
499  msg->plotName, msg->vector_x[5],
501  size_t(msg->y), msg->vector_x[6],
502  msg->vector_x[7], msg->vector_x[8] != 0,
504  msg->vector_x[9], msg->vector_x[10],
505  msg->vector_x[11]));
506  }
507  }
508  break;
509 
510  // DESTROY EXISTING WINDOW:
511  case 399:
512  if (msg->source3D)
513  {
514  auto* wnd =
516  if (wnd)
517  {
518  // delete wnd;
519  wnd->Close();
520  }
521  }
522  break;
523 
524  // CREATE NEW WINDOW
525  case 400:
526  if (msg->sourcePlots)
527  {
528  auto* wnd = new CWindowDialogPlots(
529  msg->sourcePlots, this, (wxWindowID)-1, msg->str,
530  wxSize(msg->x, msg->y));
531 
532  // Set the "m_hwnd" member of the window:
533  *((void**)msg->voidPtr) = (void*)wnd;
534 
535  // Signal to the constructor (still waiting) that the
536  // window is now ready so it can continue:
538 
539  wnd->Show();
540  }
541  break;
542  // Set position
543  case 402:
544  if (msg->sourcePlots)
545  {
546  auto* wnd = (CWindowDialogPlots*)
547  msg->sourcePlots->getWxObject();
548  if (wnd)
549  wnd->SetSize(
550  msg->x, msg->y, wxDefaultCoord, wxDefaultCoord);
551  }
552  break;
553  // Set size
554  case 403:
555  if (msg->sourcePlots)
556  {
557  auto* wnd = (CWindowDialogPlots*)
558  msg->sourcePlots->getWxObject();
559  if (wnd) wnd->SetClientSize(msg->x, msg->y);
560  }
561  break;
562  // Set window's title:
563  case 404:
564  if (msg->sourcePlots)
565  {
566  auto* wnd = (CWindowDialogPlots*)
567  msg->sourcePlots->getWxObject();
568  if (wnd) wnd->SetTitle(msg->str.c_str());
569  }
570  break;
571  // Mouse pan
572  case 410:
573  if (msg->sourcePlots)
574  {
575  auto* wnd = (CWindowDialogPlots*)
576  msg->sourcePlots->getWxObject();
577  if (wnd) wnd->m_plot->EnableMousePanZoom(msg->boolVal);
578  }
579  break;
580  // Aspect ratio
581  case 411:
582  if (msg->sourcePlots)
583  {
584  auto* wnd = (CWindowDialogPlots*)
585  msg->sourcePlots->getWxObject();
586  if (wnd) wnd->m_plot->LockAspect(msg->boolVal);
587  }
588  break;
589 
590  // Zoom over a rectangle vectorx[0-1] & vectory[0-1]
591  case 412:
592  if (msg->sourcePlots)
593  {
594  auto* wnd = (CWindowDialogPlots*)
595  msg->sourcePlots->getWxObject();
596  if (wnd)
597  {
598  if (msg->vector_x.size() == 2 &&
599  msg->vector_y.size() == 2)
600  {
601  wnd->m_plot->Fit(
602  msg->vector_x[0], msg->vector_x[1],
603  msg->vector_y[0], msg->vector_y[1]);
604  wnd->m_plot->LockAspect(msg->boolVal);
605  }
606  }
607  }
608  break;
609  // Axis fit, with aspect ratio fix to boolVal.
610  case 413:
611  if (msg->sourcePlots)
612  {
613  auto* wnd = (CWindowDialogPlots*)
614  msg->sourcePlots->getWxObject();
615  if (wnd)
616  {
617  wnd->m_plot->LockAspect(msg->boolVal);
618  wnd->m_plot->Fit();
619  }
620  }
621  break;
622  // Clear all objects:
623  case 414:
624  if (msg->sourcePlots)
625  {
626  auto* wnd = (CWindowDialogPlots*)
627  msg->sourcePlots->getWxObject();
628  if (wnd)
629  {
630  wnd->m_plot->DelAllLayers(true, true);
631  wnd->m_plot->AddLayer(new mpScaleX());
632  wnd->m_plot->AddLayer(new mpScaleY());
633  }
634  }
635  break;
636 
637  // Create/modify 2D plot
638  case 420:
639  if (msg->sourcePlots)
640  {
641  auto* wnd = (CWindowDialogPlots*)
642  msg->sourcePlots->getWxObject();
643  if (wnd)
644  wnd->plot(
645  msg->vector_x, msg->vector_y, msg->str,
646  msg->plotName);
647  }
648  break;
649 
650  // Create/modify 2D ellipse
651  case 421:
652  if (msg->sourcePlots)
653  {
654  auto* wnd = (CWindowDialogPlots*)
655  msg->sourcePlots->getWxObject();
656  if (wnd)
657  wnd->plotEllipse(
658  msg->vector_x, msg->vector_y, msg->str,
659  msg->plotName, msg->boolVal);
660  }
661  break;
662 
663  // Create/modify bitmap image
664  case 422:
665  if (msg->sourcePlots)
666  {
667  auto* wnd = (CWindowDialogPlots*)
668  msg->sourcePlots->getWxObject();
669  if (wnd)
670  wnd->image(
671  msg->voidPtr2, msg->vector_x[0],
672  msg->vector_x[1], msg->vector_x[2],
673  msg->vector_x[3], msg->plotName);
674  }
675  break;
676 
677  // 440: Insert submenu in the popup menu. name=menu label, x=ID
678  case 440:
679  if (msg->sourcePlots)
680  {
681  auto* wnd = (CWindowDialogPlots*)
682  msg->sourcePlots->getWxObject();
683  if (wnd)
684  {
685  const long MENUITEM_ID = wxNewId();
686  // Remember the association between this ID and the
687  // user ID:
688  wnd->m_ID2ID[MENUITEM_ID] = msg->x;
689 
690  wxMenu* popupMnu = wnd->m_plot->GetPopupMenu();
691  if (wnd->m_firstSubmenu)
692  {
693  wnd->m_firstSubmenu = false;
694  popupMnu->InsertSeparator(0);
695  }
696  wxMenuItem* mnuTarget = new wxMenuItem(
697  popupMnu, MENUITEM_ID, msg->plotName.c_str(),
698  wxEmptyString, wxITEM_NORMAL);
699  popupMnu->Insert(0, mnuTarget);
700 
701  wnd->Bind(
703  wnd, MENUITEM_ID);
704  }
705  }
706  break;
707 
708  // DESTROY EXISTING WINDOW:
709  case 499:
710  if (msg->sourcePlots)
711  {
712  auto* wnd = (CWindowDialogPlots*)
713  msg->sourcePlots->getWxObject();
714  if (wnd)
715  {
716  // delete wnd;
717  wnd->Close();
718  }
719  }
720  break;
721 
722  // CREATE NEW WINDOW
723  case 700:
724  if (msg->sourceCameraSelectDialog)
725  {
726  auto* sem =
727  reinterpret_cast<std::promise<void>*>(msg->voidPtr);
728 
729  auto dlg = std::make_unique<CDialogAskUserForCamera>();
730 
731  // Signal that the window is ready:
732  sem->set_value();
733 
734  // Show
735  const bool wasOk = (dlg->ShowModal() == wxID_OK);
736 
737  // send selection to caller:
738  auto* promise = reinterpret_cast<std::promise<
740  msg->voidPtr2);
742 
743  // Parse selection as a config text block:
745  dlg->panel->writeConfigFromVideoSourcePanel(
746  "CONFIG", &c);
747  c.getContent(ret.selectedConfig);
748  ret.accepted_by_user = wasOk;
749 
750  promise->set_value(std::move(ret));
751  dlg->Close();
752  }
753  break;
754 
755  // wxSubsystem shutdown:
756  case 999:
757  {
758 #ifdef WXSUBSYSTEM_VERBOSE
759  cout << "[WxSubsystem:999] Shutdown" << endl;
760 #endif
761  app_closed = true; // Do NOT launch a timer again
764  CWXMainFrame::
765  oneInstance))
766  ->Close();
767 #ifdef WXSUBSYSTEM_VERBOSE
768  cout << "[WxSubsystem:999] Shutdown done" << endl;
769 #endif
770  }
771  break;
772 
773  } // end switch OPCODE
774 
775  // Free the memory:
776  delete[] msg;
777  } // end while
778  }
779  catch (...)
780  {
781  }
782 
783  if (!app_closed) m_theTimer->Start(10, true); // One-shot
784 }
785 
786 // ---------------------------------------------------------------------------------------
787 // MRPT Icons
788 // ---------------------------------------------------------------------------------------
789 const char* mrpt_default_icon_xpm[] = {"32 32 2 1",
790  " c None",
791  ". c #000000",
792  " ",
793  " ",
794  " ",
795  " ..... ..... ......... ",
796  " .... .... ... .... ",
797  " ..... .... ... ... ",
798  " . ... . ... ... ... ",
799  " . ... . ... ... ... ",
800  " . ... . ... ... ... ",
801  " . ... . ... ........ ",
802  " . ..... ... ... .... ",
803  " . ... ... ... .... ",
804  " . ... ... ... .... ",
805  " . .. ... ... .... ",
806  " ... . ..... ..... ..... ",
807  " ",
808  " ",
809  " ........ ........... ",
810  " ... .... .. ... .. ",
811  " ... ... . ... . ",
812  " ... ... ... ",
813  " ... ... ... ",
814  " ... ... ... ",
815  " ....... ... ",
816  " ... ... ",
817  " ... ... ",
818  " ... ... ",
819  " ... ... ",
820  " ..... ..... ",
821  " ",
822  " ",
823  " "};
824 
826 {
827 // To avoid an error in wx, always resize the icon to the expected size:
828 #ifdef _WIN32
829  const wxSize iconsSize(
830  ::GetSystemMetrics(SM_CXICON), ::GetSystemMetrics(SM_CYICON));
831  return wxBitmap(wxBitmap(mrpt_default_icon_xpm)
832  .ConvertToImage()
833  .Scale(iconsSize.x, iconsSize.y));
834 #else
835  return wxBitmap(mrpt_default_icon_xpm);
836 #endif
837 }
838 
839 // ---------------------------------------------------------------------------------------
840 // The wx app:
841 // ---------------------------------------------------------------------------------------
842 class CDisplayWindow_WXAPP : public wxApp
843 {
844  public:
845  bool OnInit() override;
846  int OnExit() override;
847 };
848 
850 {
851  // Starting in wxWidgets 2.9.0, we must reset numerics locale to "C",
852  // if we want numbers to use "." in all countries. The App::OnInit() is a
853  // perfect place to undo
854  // the default wxWidgets settings. (JL @ Sep-2009)
855  wxSetlocale(LC_NUMERIC, wxString(wxT("C")));
856 
857  wxInitAllImageHandlers();
858 
859  // cout << "[wxApp::OnInit] wxApplication OnInit called." << endl;
860 
861  // Create a dummy frame:
862  auto* Frame = new WxSubsystem::CWXMainFrame(nullptr);
863  Frame->Hide();
864 
865  // We are ready!!
866  // cout << "[wxMainThread] Signaling semaphore." << endl;
868 
869  return true;
870 }
871 
872 // This will be called when all the windows / frames are closed.
874 {
875 #ifdef WXSUBSYSTEM_VERBOSE
876  cout << "[wxApp::OnExit] wxApplication OnExit called." << endl;
877 #endif
878 
879  std::lock_guard<std::mutex> lock(
880  WxSubsystem::GetWxMainThreadInstance().m_csWxMainThreadId);
881 
882  wxApp::OnExit();
883  CleanUp();
884  return 0;
885 }
886 
887 /** This method must be called in the destructor of the user class FROM THE MAIN
888  * THREAD, in order to wait for the shutdown of the wx thread if this was the
889  * last open window.
890  */
892 {
893 #ifndef WXSHUTDOWN_DO_IT_CLEAN
894 
895 #ifdef WXSUBSYSTEM_VERBOSE
896  cout << "[WxSubsystem::waitWxShutdownsIfNoWindows] Doing a quick "
897  "std::this_thread::sleep_for(ms) and returning.\n";
898 #endif
899  std::this_thread::sleep_for(100ms);
900  return;
901 #else
902  // Just let know a global object that, at its destruction, it must ....
903  // Any open windows?
904  int nOpenWnds;
905  {
906  std::lock_guard<std::mutex> lock(CWXMainFrame::cs_windowCount);
907  nOpenWnds = CWXMainFrame::m_windowCount;
908  }
909 
910  if (!nOpenWnds && WxSubsystem::isConsoleApp())
911  {
912 #ifdef WXSUBSYSTEM_VERBOSE
913  cout << "[WxSubsystem::waitWxShutdownsIfNoWindows] Waiting for "
914  "WxWidgets thread to shutdown...\n";
915 #endif
916 
917  // Then we must be shutting down in the wx thread (we are in the main
918  // MRPT application thread)...
919  // Wait until wx is safely shut down:
920  bool done = false;
921  int maxTimeout =
922 #ifdef _DEBUG
923  30000;
924 #else
925  5000;
926 #endif
927  if (m_done.wait_for(std::chrono::milliseconds(maxTimeout)) ==
928  std::future_status::timeout)
929  {
930  cerr << "[WxSubsystem::waitWxShutdownsIfNoWindows] Timeout waiting "
931  "for WxWidgets thread to shutdown!"
932  << endl;
933  }
934  }
935 #endif
936 }
937 
938 wxAppConsole* mrpt_wxCreateApp()
939 {
940  wxAppConsole::CheckBuildOptions(WX_BUILD_OPTIONS_SIGNATURE, "your program");
941  return new CDisplayWindow_WXAPP;
942 }
943 
944 // DECLARE_APP(CDisplayWindow_WXAPP)
946 
947 // Aux. funcs used in WxSubsystem::wxMainThread
948 // --------------------------------------------------
949 int mrpt_wxEntryReal(int argc, char** argv)
950 {
951  // library initialization
952  if (!wxEntryStart(argc, argv))
953  {
954 #if wxUSE_LOG
955  // flush any log messages explaining why we failed
956  delete wxLog::SetActiveTarget(nullptr);
957 #endif
958  return -1;
959  }
960 
961  // if wxEntryStart succeeded, we must call wxEntryCleanup even if the code
962  // below returns or throws
963  try
964  {
965  // app initialization
966  if (!wxTheApp->CallOnInit())
967  return -1; // don't call OnExit() if OnInit() failed
968 
969  // app execution
970  int ret = wxTheApp->OnRun();
971 
972  {
973  wxLogNull logNo; // Skip any warning in this scope.
974 
975  wxTheApp->OnExit(); // This replaces the above callOnExit class
976  wxEntryCleanup();
977  }
978 
979  return ret;
980  }
981  catch (...)
982  {
983  wxTheApp->OnUnhandledException();
984  wxEntryCleanup();
985  return -1;
986  }
987 }
988 
989 /*---------------------------------------------------------------
990  wxMainThread
991  This will be the "MAIN" of wxWidgets: It starts an application
992  object and does not end until all the windows are closed.
993  Only for console apps, not for user GUI apps already with wx.
994  ---------------------------------------------------------------*/
996 {
997  MRPT_START
998 
999  // Prepare wxWidgets:
1000  int argc = 1;
1001  static const char* dummy_prog_name = "./MRPT";
1002  char* argv[2] = {const_cast<char*>(dummy_prog_name), nullptr};
1003 
1004 #ifdef WXSUBSYSTEM_VERBOSE
1005  cout << "[wxMainThread] Starting..." << endl;
1006 #endif
1007 
1008  // Are we in a console or wxGUI application????
1009  wxAppConsole* app_gui = wxApp::GetInstance();
1010  if (!app_gui)
1011  {
1012 // We are NOT in a wx application (it's a console program)
1013 // ---------------------------------------------------------
1014 #ifdef WXSUBSYSTEM_VERBOSE
1015  cout << "[wxMainThread] I am in a console app" << endl;
1016 #endif
1017  // Start a new wx application object:
1018 
1019  // JLBC OCT2008: wxWidgets little hack to enable console/gui mixed
1020  // applications:
1021  wxApp::SetInitializerFunction(
1022  (wxAppInitializerFunction)mrpt_wxCreateApp);
1024 
1025 #ifdef WXSUBSYSTEM_VERBOSE
1026  cout << "[wxMainThread] Finished" << endl;
1027 #endif
1028 
1029  // Now this thread is ready. The main thread is free to end now:
1031  }
1032  else
1033  {
1034 // We are ALREADY in a wx application:
1035 // ---------------------------------------------------------
1036 #ifdef WXSUBSYSTEM_VERBOSE
1037  cout << "[wxMainThread] I am in a GUI app" << endl;
1038 #endif
1039  wxWindow* topWin = static_cast<wxApp*>(app_gui)->GetTopWindow();
1040 
1041  auto* Frame = new WxSubsystem::CWXMainFrame(topWin);
1042  Frame->Hide();
1043 
1044 // We are ready!!
1045 #ifdef WXSUBSYSTEM_VERBOSE
1046  cout << "[wxMainThread] Signaling semaphore." << endl;
1047 #endif
1049  .m_semWxMainThreadReady.set_value();
1050  }
1051 
1052  MRPT_END
1053 }
1054 
1056 {
1057  // static TWxMainThreadData dat;
1058  // Create as dynamic memory, since it'll be deleted in
1059  // CAuxWxSubsystemShutdowner:
1060  static TWxMainThreadData* dat = nullptr;
1061  static bool first_creat = true;
1062  if (!dat && first_creat)
1063  {
1064  first_creat = false;
1065  dat = new TWxMainThreadData;
1066  }
1067  return *dat;
1068 }
1069 
1070 /*---------------------------------------------------------------
1071  createOneInstanceMainThread
1072  ---------------------------------------------------------------*/
1074 {
1077  std::lock_guard<std::mutex> lock(wxmtd.m_csWxMainThreadId);
1078 
1079  wxAppConsole* app_con = wxApp::GetInstance();
1080  if (app_con && wxmtd.m_wxMainThreadId.get_id() == std::thread::id())
1081  {
1082  // We are NOT in a console application: There is already a wxApp
1083  // instance running and it's not us.
1084  isConsoleApp_value = false;
1085  // cout << "[createOneInstanceMainThread] Mode: User GUI." << endl;
1087  {
1088  // Create our main hidden frame:
1089  wxWindow* topWin = static_cast<wxApp*>(app_con)->GetTopWindow();
1090 
1091  auto* Frame = new WxSubsystem::CWXMainFrame(topWin);
1092  // Frame->Show();
1093  // SetTopWindow(Frame);
1094  Frame->Hide();
1095  }
1096  }
1097  else
1098  {
1099  // cout << "[createOneInstanceMainThread] Mode: Console." << endl;
1100  isConsoleApp_value = true;
1101  if (wxmtd.m_wxMainThreadId.get_id() == std::thread::id())
1102  {
1103 #ifdef WXSUBSYSTEM_VERBOSE
1104  printf(
1105  "[WxSubsystem::createOneInstanceMainThread] Launching "
1106  "wxMainThread() thread...\n");
1107 #endif
1108  // Create a thread for message processing there:
1109  wxmtd.m_wxMainThreadId = std::thread(wxMainThread);
1110 
1111  int maxTimeout =
1112 #ifdef _DEBUG
1113  30000;
1114 #else
1115  5000;
1116 #endif
1117 
1118  // If we have an "MRPT_WXSUBSYS_TIMEOUT_MS" environment variable,
1119  // use that timeout instead:
1120  const char* envVal = getenv("MRPT_WXSUBSYS_TIMEOUT_MS");
1121  if (envVal) maxTimeout = atoi(envVal);
1122 
1123  if (wxmtd.m_semWxMainThreadReady.get_future().wait_for(
1124  std::chrono::milliseconds(maxTimeout)) ==
1125  std::future_status::timeout) // A few secs should be enough...
1126  {
1127  cerr << "[WxSubsystem::createOneInstanceMainThread] Timeout "
1128  "waiting wxApplication to start up!"
1129  << endl;
1130  return false;
1131  }
1132  }
1133  }
1134 
1135  return true; // OK
1136 }
1137 
1138 #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:309
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:428
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:285
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:308
static const long ID_BTN_OK
void OnMenuSelected(wxCommandEvent &ev)
TOpenGLFont
Existing fonts for 2D texts in mrpt::opengl methods.
Definition: opengl_fonts.h:22
wxAppConsole * mrpt_wxCreateApp()
std::promise< void > m_semWxMainThreadReady
This is signaled when wxMainThread is ready.
Definition: WxSubsystem.h:172
bool OnInit() override
TOpenGLFontStyle
Different style for vectorized font rendering.
Definition: opengl_fonts.h:33
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:317
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
A RGB color - floats in the range [0,1].
Definition: TColor.h:78
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.



Page generated by Doxygen 1.8.14 for MRPT 1.9.9 Git: 5ec5114f2 Thu Feb 20 07:54:08 2020 +0100 at jue feb 20 08:00:10 CET 2020