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  Connect(
128  ID_BTN_OK, wxEVT_COMMAND_BUTTON_CLICKED,
129  (wxObjectEventFunction)&CDialogAskUserForCamera::OnBtnOk);
130  Connect(
131  ID_BTN_CANCEL, wxEVT_COMMAND_BUTTON_CLICKED,
132  (wxObjectEventFunction)&CDialogAskUserForCamera::OnBtnCancel);
133 
134  SetSizer(f1);
135  Fit();
136 
137  btnOk->SetFocus(); // So the default params can be accepted by just
138  // pressing ENTER.
139  }
140 
141  ~CDialogAskUserForCamera() override = default;
142  void OnBtnOk(wxCommandEvent& event) { EndModal(wxID_OK); }
143  void OnBtnCancel(wxCommandEvent& event) { EndModal(wxID_CANCEL); }
144 };
145 
146 const long CDialogAskUserForCamera::ID_BTN_OK = wxNewId();
147 const long CDialogAskUserForCamera::ID_BTN_CANCEL = wxNewId();
148 
149 // ---------------------------------------------------------------------------------------
150 // The wx dummy frame:
151 // ---------------------------------------------------------------------------------------
152 BEGIN_EVENT_TABLE(WxSubsystem::CWXMainFrame, wxFrame)
153 
154 END_EVENT_TABLE()
155 
156 const long ID_TIMER_WX_PROCESS_REQUESTS = wxNewId();
157 
158 WxSubsystem::CWXMainFrame::CWXMainFrame(wxWindow* parent, wxWindowID id)
159 {
160  Create(
161  parent, id, _("MRPT-dummy frame window"), wxDefaultPosition,
162  wxSize(1, 1),
163  0, // wxDEFAULT_FRAME_STYLE,
164  _T("id"));
165 
166  if (oneInstance)
167  {
168  cerr << "[CWXMainFrame] More than one instance running!" << endl;
169  }
170  oneInstance = this;
171 
172  // ------------------------------------------------------------------------------------------
173  // Create a timer so requests from the main application thread can be
174  // processed regularly:
175  // ------------------------------------------------------------------------------------------
176  Connect(
177  ID_TIMER_WX_PROCESS_REQUESTS, wxEVT_TIMER,
178  (wxObjectEventFunction)&CWXMainFrame::OnTimerProcessRequests);
179  m_theTimer = new wxTimer(this, ID_TIMER_WX_PROCESS_REQUESTS);
180 
181  m_theTimer->Start(10, true); // One-shot
182 }
183 
185 {
186 #ifdef WXSUBSYSTEM_VERBOSE
187  cout << "[CWXMainFrame] Destructor." << endl;
188 #endif
189  delete m_theTimer;
190  oneInstance = nullptr;
191 
192  // Purge all pending requests:
194  while (nullptr != (msg = popPendingWxRequest())) delete[] msg;
195 }
196 
198 {
199  std::lock_guard<std::mutex> lock(cs_windowCount);
200  return ++m_windowCount;
201 }
202 
204 {
205  int ret;
206  {
207  std::lock_guard<std::mutex> lock(cs_windowCount);
208  ret = --m_windowCount;
209  }
210 
211  if (ret == 0)
212  {
213  // That was the last window... we should close the wx subsystem:
214  if (oneInstance)
215  {
216 #ifdef WXSHUTDOWN_DO_IT_CLEAN
217  CWXMainFrame* me =
218  (CWXMainFrame*)(oneInstance); // cast away the "volatile".
219  me->Close();
220 #endif
221 
222 #ifdef WXSUBSYSTEM_VERBOSE
223  cout << "[CWXMainFrame::notifyWindowDestruction] numWindows=0. "
224  "me->Close() called."
225  << endl;
226 #endif
227  }
228  }
229 
230  return ret;
231 }
232 
233 /** Thread-safe method to return the next pending request, or nullptr if there
234  * is none (After usage, FREE the memory!)
235  */
237 {
238  if (!cs_listPendingWxRequests)
239  {
240  cs_listPendingWxRequests = new std::mutex();
241  listPendingWxRequests = new std::queue<TRequestToWxMainThread*>;
242  }
243 
244  std::lock_guard<std::mutex> locker(*cs_listPendingWxRequests);
245 
246  // Is empty?
247  if (listPendingWxRequests->empty()) return nullptr;
248 
249  TRequestToWxMainThread* ret = listPendingWxRequests->front();
250  listPendingWxRequests->pop(); // Remove from the queue
251 
252  return ret;
253 }
254 
255 /** Thread-safe method to insert a new pending request (The memory must be
256  * dinamically allocated with "new T[1]", will be freed by receiver.)
257  */
260 {
262  {
263 #ifdef WXSUBSYSTEM_VERBOSE
264  cout << "[WxSubsystem::pushPendingWxRequest] IGNORING request since "
265  "app seems already closed.\n";
266 #endif
267  delete[] data;
268  return; // wx subsystem already closed, ignore.
269  }
270 
271  if (!cs_listPendingWxRequests)
272  {
273  cs_listPendingWxRequests = new std::mutex();
274  listPendingWxRequests = new std::queue<TRequestToWxMainThread*>;
275  }
276 
277  std::lock_guard<std::mutex> locker(*cs_listPendingWxRequests);
278  listPendingWxRequests->push(data);
279 }
280 
281 /** This method processes the pending requests from the main MRPT application
282  * thread.
283  * The requests may be to create a new window, close another one, change
284  * title, etc...
285  */
287 {
288  bool app_closed = false;
289  try
290  {
292 
293 #ifdef WXSUBSYSTEM_VERBOSE
294  cout << "[OnTimerProcessRequests] Entering" << endl;
295 #endif
296 
297  // For each pending request:
298  while (nullptr != (msg = popPendingWxRequest()))
299  {
300  // Process it:
301  switch (msg->OPCODE)
302  {
303  // CREATE NEW WINDOW
304  case 200:
305  if (msg->source2D)
306  {
307  auto* wnd = new CWindowDialog(
308  msg->source2D, this, (wxWindowID)-1, msg->str,
309  wxSize(msg->x, msg->y));
310 
311  // Set the "m_hwnd" member of the window:
312  *((void**)msg->voidPtr) = (void*)wnd;
313 
314  // Signal to the constructor (still waiting) that the
315  // window is now ready so it can continue:
317 
318  wnd->Show();
319  }
320  break;
321  // UPDATE IMAGE
322  case 201:
323  if (msg->source2D)
324  {
325  auto* wnd =
326  (CWindowDialog*)
327  msg->voidPtr; // msg->source2D->getWxObject();
328  if (!wnd) break;
329  auto* img = (wxImage*)msg->voidPtr2;
330  if (!img) break;
331 
332  wnd->m_image->AssignImage(new wxBitmap(
333  *img)); // Memory will be freed by the object.
334 
335  if (wnd->m_image->GetSize().GetX() != img->GetWidth() &&
336  wnd->m_image->GetSize().GetY() != img->GetHeight())
337  {
338  wnd->m_image->SetSize(
339  img->GetWidth(), img->GetHeight());
340  wnd->m_image->SetMinSize(
341  wxSize(img->GetWidth(), img->GetHeight()));
342  wnd->m_image->SetMaxSize(
343  wxSize(img->GetWidth(), img->GetHeight()));
344  wnd->Fit();
345  // wnd->SetClientSize(img->GetWidth(),
346  // img->GetHeight());
347  }
348  delete img;
349  wnd->m_image->Refresh(false); // false: Do NOT erase
350  // background: avoid
351  // flickering
352  }
353  break;
354  // Set position
355  case 202:
356  if (msg->source2D)
357  {
358  auto* wnd =
360  if (wnd)
361  wnd->SetSize(
362  msg->x, msg->y, wxDefaultCoord, wxDefaultCoord);
363  }
364  break;
365  // Set size
366  case 203:
367  if (msg->source2D)
368  {
369  auto* wnd =
371  if (wnd) wnd->SetClientSize(msg->x, msg->y);
372  }
373  break;
374  // Set window's title:
375  case 204:
376  if (msg->source2D)
377  {
378  auto* wnd =
380  if (wnd) wnd->SetTitle(msg->str.c_str());
381  }
382  break;
383  // DESTROY EXISTING WINDOW:
384  case 299:
385  if (msg->source2D)
386  {
387  auto* wnd =
389  if (wnd)
390  {
391  // delete wnd;
392  wnd->Close();
393  }
394  }
395  break;
396 
397  // CREATE NEW WINDOW
398  case 300:
399  if (msg->source3D)
400  {
401  auto* wnd = new C3DWindowDialog(
402  msg->source3D, this, (wxWindowID)-1, msg->str,
403  wxSize(msg->x, msg->y));
404 
405  // Set the "m_hwnd" member of the window:
406  *((void**)msg->voidPtr) = (void*)wnd;
407 
408  // Signal to the constructor (still waiting) that the
409  // window is now ready so it can continue:
411 
412  wnd->Show();
413  }
414  break;
415  // Set position
416  case 302:
417  if (msg->source3D)
418  {
419  auto* wnd =
421  if (wnd)
422  wnd->SetSize(
423  msg->x, msg->y, wxDefaultCoord, wxDefaultCoord);
424  }
425  break;
426  // Set size
427  case 303:
428  if (msg->source3D)
429  {
430  auto* wnd =
432  if (wnd) wnd->SetClientSize(msg->x, msg->y);
433  }
434  break;
435  // Set window's title:
436  case 304:
437  if (msg->source3D)
438  {
439  auto* wnd =
441  if (wnd) wnd->SetTitle(msg->str.c_str());
442  }
443  break;
444  // FORCE REPAINT
445  case 350:
446  if (msg->source3D)
447  {
448  auto* wnd =
450  if (wnd)
451  {
452  wnd->Refresh(false);
453  }
454  }
455  break;
456  // Add a 2D text message: vector_x: [0]:x, [1]:y, [2,3,4]:R G B,
457  // "x": enum of desired font. "y": unique index, "str": String.
458  case 360:
459  if (msg->source3D)
460  {
461  auto* wnd =
463  if (wnd)
464  {
465  wnd->addTextMessage(
466  msg->vector_x[0], msg->vector_x[1], msg->str,
468  msg->vector_x[2], msg->vector_x[3],
469  msg->vector_x[4]),
470  size_t(msg->y),
472  }
473  }
474  break;
475  // Clear 2D text messages
476  case 361:
477  if (msg->source3D)
478  {
479  auto* wnd =
481  if (wnd)
482  {
483  wnd->clearTextMessages();
484  }
485  }
486  break;
487  // Add a 2D text message: vector_x: [0]:x, [1]:y, [2,3,4]:R G B,
488  // "x": enum of desired font. "y": unique index, "str": String.
489  case 362:
490  if (msg->source3D)
491  {
492  auto* wnd =
494  if (wnd)
495  {
496  wnd->addTextMessage(
497  msg->vector_x[0], msg->vector_x[1], msg->str,
499  msg->vector_x[2], msg->vector_x[3],
500  msg->vector_x[4]),
501  msg->plotName, msg->vector_x[5],
503  size_t(msg->y), msg->vector_x[6],
504  msg->vector_x[7], msg->vector_x[8] != 0,
506  msg->vector_x[9], msg->vector_x[10],
507  msg->vector_x[11]));
508  }
509  }
510  break;
511 
512  // DESTROY EXISTING WINDOW:
513  case 399:
514  if (msg->source3D)
515  {
516  auto* wnd =
518  if (wnd)
519  {
520  // delete wnd;
521  wnd->Close();
522  }
523  }
524  break;
525 
526  // CREATE NEW WINDOW
527  case 400:
528  if (msg->sourcePlots)
529  {
530  auto* wnd = new CWindowDialogPlots(
531  msg->sourcePlots, this, (wxWindowID)-1, msg->str,
532  wxSize(msg->x, msg->y));
533 
534  // Set the "m_hwnd" member of the window:
535  *((void**)msg->voidPtr) = (void*)wnd;
536 
537  // Signal to the constructor (still waiting) that the
538  // window is now ready so it can continue:
540 
541  wnd->Show();
542  }
543  break;
544  // Set position
545  case 402:
546  if (msg->sourcePlots)
547  {
548  auto* wnd = (CWindowDialogPlots*)
549  msg->sourcePlots->getWxObject();
550  if (wnd)
551  wnd->SetSize(
552  msg->x, msg->y, wxDefaultCoord, wxDefaultCoord);
553  }
554  break;
555  // Set size
556  case 403:
557  if (msg->sourcePlots)
558  {
559  auto* wnd = (CWindowDialogPlots*)
560  msg->sourcePlots->getWxObject();
561  if (wnd) wnd->SetClientSize(msg->x, msg->y);
562  }
563  break;
564  // Set window's title:
565  case 404:
566  if (msg->sourcePlots)
567  {
568  auto* wnd = (CWindowDialogPlots*)
569  msg->sourcePlots->getWxObject();
570  if (wnd) wnd->SetTitle(msg->str.c_str());
571  }
572  break;
573  // Mouse pan
574  case 410:
575  if (msg->sourcePlots)
576  {
577  auto* wnd = (CWindowDialogPlots*)
578  msg->sourcePlots->getWxObject();
579  if (wnd) wnd->m_plot->EnableMousePanZoom(msg->boolVal);
580  }
581  break;
582  // Aspect ratio
583  case 411:
584  if (msg->sourcePlots)
585  {
586  auto* wnd = (CWindowDialogPlots*)
587  msg->sourcePlots->getWxObject();
588  if (wnd) wnd->m_plot->LockAspect(msg->boolVal);
589  }
590  break;
591 
592  // Zoom over a rectangle vectorx[0-1] & vectory[0-1]
593  case 412:
594  if (msg->sourcePlots)
595  {
596  auto* wnd = (CWindowDialogPlots*)
597  msg->sourcePlots->getWxObject();
598  if (wnd)
599  {
600  if (msg->vector_x.size() == 2 &&
601  msg->vector_y.size() == 2)
602  {
603  wnd->m_plot->Fit(
604  msg->vector_x[0], msg->vector_x[1],
605  msg->vector_y[0], msg->vector_y[1]);
606  wnd->m_plot->LockAspect(msg->boolVal);
607  }
608  }
609  }
610  break;
611  // Axis fit, with aspect ratio fix to boolVal.
612  case 413:
613  if (msg->sourcePlots)
614  {
615  auto* wnd = (CWindowDialogPlots*)
616  msg->sourcePlots->getWxObject();
617  if (wnd)
618  {
619  wnd->m_plot->LockAspect(msg->boolVal);
620  wnd->m_plot->Fit();
621  }
622  }
623  break;
624  // Clear all objects:
625  case 414:
626  if (msg->sourcePlots)
627  {
628  auto* wnd = (CWindowDialogPlots*)
629  msg->sourcePlots->getWxObject();
630  if (wnd)
631  {
632  wnd->m_plot->DelAllLayers(true, true);
633  wnd->m_plot->AddLayer(new mpScaleX());
634  wnd->m_plot->AddLayer(new mpScaleY());
635  }
636  }
637  break;
638 
639  // Create/modify 2D plot
640  case 420:
641  if (msg->sourcePlots)
642  {
643  auto* wnd = (CWindowDialogPlots*)
644  msg->sourcePlots->getWxObject();
645  if (wnd)
646  wnd->plot(
647  msg->vector_x, msg->vector_y, msg->str,
648  msg->plotName);
649  }
650  break;
651 
652  // Create/modify 2D ellipse
653  case 421:
654  if (msg->sourcePlots)
655  {
656  auto* wnd = (CWindowDialogPlots*)
657  msg->sourcePlots->getWxObject();
658  if (wnd)
659  wnd->plotEllipse(
660  msg->vector_x, msg->vector_y, msg->str,
661  msg->plotName, msg->boolVal);
662  }
663  break;
664 
665  // Create/modify bitmap image
666  case 422:
667  if (msg->sourcePlots)
668  {
669  auto* wnd = (CWindowDialogPlots*)
670  msg->sourcePlots->getWxObject();
671  if (wnd)
672  wnd->image(
673  msg->voidPtr2, msg->vector_x[0],
674  msg->vector_x[1], msg->vector_x[2],
675  msg->vector_x[3], msg->plotName);
676  }
677  break;
678 
679  // 440: Insert submenu in the popup menu. name=menu label, x=ID
680  case 440:
681  if (msg->sourcePlots)
682  {
683  auto* wnd = (CWindowDialogPlots*)
684  msg->sourcePlots->getWxObject();
685  if (wnd)
686  {
687  const long MENUITEM_ID = wxNewId();
688  // Remember the association between this ID and the
689  // user ID:
690  wnd->m_ID2ID[MENUITEM_ID] = msg->x;
691 
692  wxMenu* popupMnu = wnd->m_plot->GetPopupMenu();
693  if (wnd->m_firstSubmenu)
694  {
695  wnd->m_firstSubmenu = false;
696  popupMnu->InsertSeparator(0);
697  }
698  wxMenuItem* mnuTarget = new wxMenuItem(
699  popupMnu, MENUITEM_ID, msg->plotName.c_str(),
700  wxEmptyString, wxITEM_NORMAL);
701  popupMnu->Insert(0, mnuTarget);
702 
703  wnd->Connect(
704  MENUITEM_ID, wxEVT_COMMAND_MENU_SELECTED,
705  (wxObjectEventFunction)&CWindowDialogPlots::
706  OnMenuSelected);
707  }
708  }
709  break;
710 
711  // DESTROY EXISTING WINDOW:
712  case 499:
713  if (msg->sourcePlots)
714  {
715  auto* wnd = (CWindowDialogPlots*)
716  msg->sourcePlots->getWxObject();
717  if (wnd)
718  {
719  // delete wnd;
720  wnd->Close();
721  }
722  }
723  break;
724 
725  // CREATE NEW WINDOW
726  case 700:
727  if (msg->sourceCameraSelectDialog)
728  {
729  auto* sem =
730  reinterpret_cast<std::promise<void>*>(msg->voidPtr);
731 
732  auto dlg = std::make_unique<CDialogAskUserForCamera>();
733 
734  // Signal that the window is ready:
735  sem->set_value();
736 
737  // Show
738  const bool wasOk = (dlg->ShowModal() == wxID_OK);
739 
740  // send selection to caller:
741  auto* promise = reinterpret_cast<std::promise<
743  msg->voidPtr2);
745 
746  // Parse selection as a config text block:
748  dlg->panel->writeConfigFromVideoSourcePanel(
749  "CONFIG", &c);
750  c.getContent(ret.selectedConfig);
751  ret.accepted_by_user = wasOk;
752 
753  promise->set_value(std::move(ret));
754  dlg->Close();
755  }
756  break;
757 
758  // wxSubsystem shutdown:
759  case 999:
760  {
761 #ifdef WXSUBSYSTEM_VERBOSE
762  cout << "[WxSubsystem:999] Shutdown" << endl;
763 #endif
764  app_closed = true; // Do NOT launch a timer again
767  CWXMainFrame::
768  oneInstance))
769  ->Close();
770 #ifdef WXSUBSYSTEM_VERBOSE
771  cout << "[WxSubsystem:999] Shutdown done" << endl;
772 #endif
773  }
774  break;
775 
776  } // end switch OPCODE
777 
778  // Free the memory:
779  delete[] msg;
780  } // end while
781  }
782  catch (...)
783  {
784  }
785 
786  if (!app_closed) m_theTimer->Start(10, true); // One-shot
787 }
788 
789 // ---------------------------------------------------------------------------------------
790 // MRPT Icons
791 // ---------------------------------------------------------------------------------------
792 const char* mrpt_default_icon_xpm[] = {"32 32 2 1",
793  " c None",
794  ". c #000000",
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  " ",
825  " ",
826  " "};
827 
829 {
830 // To avoid an error in wx, always resize the icon to the expected size:
831 #ifdef _WIN32
832  const wxSize iconsSize(
833  ::GetSystemMetrics(SM_CXICON), ::GetSystemMetrics(SM_CYICON));
834  return wxBitmap(wxBitmap(mrpt_default_icon_xpm)
835  .ConvertToImage()
836  .Scale(iconsSize.x, iconsSize.y));
837 #else
838  return wxBitmap(mrpt_default_icon_xpm);
839 #endif
840 }
841 
842 // ---------------------------------------------------------------------------------------
843 // The wx app:
844 // ---------------------------------------------------------------------------------------
845 class CDisplayWindow_WXAPP : public wxApp
846 {
847  public:
848  bool OnInit() override;
849  int OnExit() override;
850 };
851 
853 {
854  // Starting in wxWidgets 2.9.0, we must reset numerics locale to "C",
855  // if we want numbers to use "." in all countries. The App::OnInit() is a
856  // perfect place to undo
857  // the default wxWidgets settings. (JL @ Sep-2009)
858  wxSetlocale(LC_NUMERIC, wxString(wxT("C")));
859 
860  wxInitAllImageHandlers();
861 
862  // cout << "[wxApp::OnInit] wxApplication OnInit called." << endl;
863 
864  // Create a dummy frame:
865  auto* Frame = new WxSubsystem::CWXMainFrame(nullptr);
866  Frame->Hide();
867 
868  // We are ready!!
869  // cout << "[wxMainThread] Signaling semaphore." << endl;
871 
872  return true;
873 }
874 
875 // This will be called when all the windows / frames are closed.
877 {
878 #ifdef WXSUBSYSTEM_VERBOSE
879  cout << "[wxApp::OnExit] wxApplication OnExit called." << endl;
880 #endif
881 
882  std::lock_guard<std::mutex> lock(
883  WxSubsystem::GetWxMainThreadInstance().m_csWxMainThreadId);
884 
885  wxApp::OnExit();
886  CleanUp();
887  return 0;
888 }
889 
890 /** This method must be called in the destructor of the user class FROM THE MAIN
891  * THREAD, in order to wait for the shutdown of the wx thread if this was the
892  * last open window.
893  */
895 {
896 #ifndef WXSHUTDOWN_DO_IT_CLEAN
897 
898 #ifdef WXSUBSYSTEM_VERBOSE
899  cout << "[WxSubsystem::waitWxShutdownsIfNoWindows] Doing a quick "
900  "std::this_thread::sleep_for(ms) and returning.\n";
901 #endif
902  std::this_thread::sleep_for(100ms);
903  return;
904 #else
905  // Just let know a global object that, at its destruction, it must ....
906  // Any open windows?
907  int nOpenWnds;
908  {
909  std::lock_guard<std::mutex> lock(CWXMainFrame::cs_windowCount);
910  nOpenWnds = CWXMainFrame::m_windowCount;
911  }
912 
913  if (!nOpenWnds && WxSubsystem::isConsoleApp())
914  {
915 #ifdef WXSUBSYSTEM_VERBOSE
916  cout << "[WxSubsystem::waitWxShutdownsIfNoWindows] Waiting for "
917  "WxWidgets thread to shutdown...\n";
918 #endif
919 
920  // Then we must be shutting down in the wx thread (we are in the main
921  // MRPT application thread)...
922  // Wait until wx is safely shut down:
923  bool done = false;
924  int maxTimeout =
925 #ifdef _DEBUG
926  30000;
927 #else
928  5000;
929 #endif
930  if (m_done.wait_for(std::chrono::milliseconds(maxTimeout)) ==
931  std::future_status::timeout)
932  {
933  cerr << "[WxSubsystem::waitWxShutdownsIfNoWindows] Timeout waiting "
934  "for WxWidgets thread to shutdown!"
935  << endl;
936  }
937  }
938 #endif
939 }
940 
941 wxAppConsole* mrpt_wxCreateApp()
942 {
943  wxAppConsole::CheckBuildOptions(WX_BUILD_OPTIONS_SIGNATURE, "your program");
944  return new CDisplayWindow_WXAPP;
945 }
946 
947 // DECLARE_APP(CDisplayWindow_WXAPP)
949 
950 // Aux. funcs used in WxSubsystem::wxMainThread
951 // --------------------------------------------------
952 int mrpt_wxEntryReal(int argc, char** argv)
953 {
954  // library initialization
955  if (!wxEntryStart(argc, argv))
956  {
957 #if wxUSE_LOG
958  // flush any log messages explaining why we failed
959  delete wxLog::SetActiveTarget(nullptr);
960 #endif
961  return -1;
962  }
963 
964  // if wxEntryStart succeeded, we must call wxEntryCleanup even if the code
965  // below returns or throws
966  try
967  {
968  // app initialization
969  if (!wxTheApp->CallOnInit())
970  return -1; // don't call OnExit() if OnInit() failed
971 
972  // app execution
973  int ret = wxTheApp->OnRun();
974 
975  {
976  wxLogNull logNo; // Skip any warning in this scope.
977 
978  wxTheApp->OnExit(); // This replaces the above callOnExit class
979  wxEntryCleanup();
980  }
981 
982  return ret;
983  }
984  catch (...)
985  {
986  wxTheApp->OnUnhandledException();
987  wxEntryCleanup();
988  return -1;
989  }
990 }
991 
992 /*---------------------------------------------------------------
993  wxMainThread
994  This will be the "MAIN" of wxWidgets: It starts an application
995  object and does not end until all the windows are closed.
996  Only for console apps, not for user GUI apps already with wx.
997  ---------------------------------------------------------------*/
999 {
1000  MRPT_START
1001 
1002  // Prepare wxWidgets:
1003  int argc = 1;
1004  static const char* dummy_prog_name = "./MRPT";
1005  char* argv[2] = {const_cast<char*>(dummy_prog_name), nullptr};
1006 
1007 #ifdef WXSUBSYSTEM_VERBOSE
1008  cout << "[wxMainThread] Starting..." << endl;
1009 #endif
1010 
1011  // Are we in a console or wxGUI application????
1012  wxAppConsole* app_gui = wxApp::GetInstance();
1013  if (!app_gui)
1014  {
1015 // We are NOT in a wx application (it's a console program)
1016 // ---------------------------------------------------------
1017 #ifdef WXSUBSYSTEM_VERBOSE
1018  cout << "[wxMainThread] I am in a console app" << endl;
1019 #endif
1020  // Start a new wx application object:
1021 
1022  // JLBC OCT2008: wxWidgets little hack to enable console/gui mixed
1023  // applications:
1024  wxApp::SetInitializerFunction(
1025  (wxAppInitializerFunction)mrpt_wxCreateApp);
1026  mrpt_wxEntryReal(argc, argv);
1027 
1028 #ifdef WXSUBSYSTEM_VERBOSE
1029  cout << "[wxMainThread] Finished" << endl;
1030 #endif
1031 
1032  // Now this thread is ready. The main thread is free to end now:
1034  }
1035  else
1036  {
1037 // We are ALREADY in a wx application:
1038 // ---------------------------------------------------------
1039 #ifdef WXSUBSYSTEM_VERBOSE
1040  cout << "[wxMainThread] I am in a GUI app" << endl;
1041 #endif
1042  wxWindow* topWin = static_cast<wxApp*>(app_gui)->GetTopWindow();
1043 
1044  auto* Frame = new WxSubsystem::CWXMainFrame(topWin);
1045  Frame->Hide();
1046 
1047 // We are ready!!
1048 #ifdef WXSUBSYSTEM_VERBOSE
1049  cout << "[wxMainThread] Signaling semaphore." << endl;
1050 #endif
1052  .m_semWxMainThreadReady.set_value();
1053  }
1054 
1055  MRPT_END
1056 }
1057 
1059 {
1060  // static TWxMainThreadData dat;
1061  // Create as dynamic memory, since it'll be deleted in
1062  // CAuxWxSubsystemShutdowner:
1063  static TWxMainThreadData* dat = nullptr;
1064  static bool first_creat = true;
1065  if (!dat && first_creat)
1066  {
1067  first_creat = false;
1068  dat = new TWxMainThreadData;
1069  }
1070  return *dat;
1071 }
1072 
1073 /*---------------------------------------------------------------
1074  createOneInstanceMainThread
1075  ---------------------------------------------------------------*/
1077 {
1080  std::lock_guard<std::mutex> lock(wxmtd.m_csWxMainThreadId);
1081 
1082  wxAppConsole* app_con = wxApp::GetInstance();
1083  if (app_con && wxmtd.m_wxMainThreadId.get_id() == std::thread::id())
1084  {
1085  // We are NOT in a console application: There is already a wxApp
1086  // instance running and it's not us.
1087  isConsoleApp_value = false;
1088  // cout << "[createOneInstanceMainThread] Mode: User GUI." << endl;
1090  {
1091  // Create our main hidden frame:
1092  wxWindow* topWin = static_cast<wxApp*>(app_con)->GetTopWindow();
1093 
1094  auto* Frame = new WxSubsystem::CWXMainFrame(topWin);
1095  // Frame->Show();
1096  // SetTopWindow(Frame);
1097  Frame->Hide();
1098  }
1099  }
1100  else
1101  {
1102  // cout << "[createOneInstanceMainThread] Mode: Console." << endl;
1103  isConsoleApp_value = true;
1104  if (wxmtd.m_wxMainThreadId.get_id() == std::thread::id())
1105  {
1106 #ifdef WXSUBSYSTEM_VERBOSE
1107  printf(
1108  "[WxSubsystem::createOneInstanceMainThread] Launching "
1109  "wxMainThread() thread...\n");
1110 #endif
1111  // Create a thread for message processing there:
1112  wxmtd.m_wxMainThreadId = std::thread(wxMainThread);
1113 
1114  int maxTimeout =
1115 #ifdef _DEBUG
1116  30000;
1117 #else
1118  5000;
1119 #endif
1120 
1121  // If we have an "MRPT_WXSUBSYS_TIMEOUT_MS" environment variable,
1122  // use that timeout instead:
1123  const char* envVal = getenv("MRPT_WXSUBSYS_TIMEOUT_MS");
1124  if (envVal) maxTimeout = atoi(envVal);
1125 
1126  if (wxmtd.m_semWxMainThreadReady.get_future().wait_for(
1127  std::chrono::milliseconds(maxTimeout)) ==
1128  std::future_status::timeout) // A few secs should be enough...
1129  {
1130  cerr << "[WxSubsystem::createOneInstanceMainThread] Timeout "
1131  "waiting wxApplication to start up!"
1132  << endl;
1133  return false;
1134  }
1135  }
1136  }
1137 
1138  return true; // OK
1139 }
1140 
1141 #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
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
const GLubyte * c
Definition: glext.h:6406
GLint GLvoid * img
Definition: glext.h:3769
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.
This class implements the GUI thread required for the wxWidgets-based GUI.
Definition: WxSubsystem.h:96
CDisplayWindow_WXAPP & wxGetApp()
GLuint id
Definition: glext.h:3920
#define MRPT_END
Definition: exceptions.h:245
A RGB color - floats in the range [0,1].
Definition: TColor.h:77
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
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.
GLsizei GLsizei GLenum GLenum const GLvoid * data
Definition: glext.h:3550
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: 8fe78517f Sun Jul 14 19:43:28 2019 +0200 at lun oct 28 02:10:00 CET 2019