ACloudViewer  3.9.4
A Modern Library for 3D Data Processing
BitmapWindowSystem.cpp
Go to the documentation of this file.
1 // ----------------------------------------------------------------------------
2 // - CloudViewer: www.cloudViewer.org -
3 // ----------------------------------------------------------------------------
4 // Copyright (c) 2018-2024 www.cloudViewer.org
5 // SPDX-License-Identifier: MIT
6 // ----------------------------------------------------------------------------
7 
9 
10 #include <Image.h>
11 #include <Logging.h>
12 
13 #include <chrono>
14 #include <mutex>
15 #include <queue>
16 #include <thread>
17 
23 
24 namespace cloudViewer {
25 namespace visualization {
26 namespace gui {
27 
28 namespace {
29 struct BitmapWindow {
31  Rect frame;
32  Point mouse_pos;
33  int mouse_buttons = 0;
34 
35  BitmapWindow(Window *o3dw, int width, int height)
36  : o3d_window(o3dw), frame(0, 0, width, height) {}
37 };
38 
39 struct BitmapEvent {
40  BitmapWindow *event_target;
41 
42  BitmapEvent(BitmapWindow *target) : event_target(target) {}
43  virtual ~BitmapEvent() {}
44 
45  virtual void Execute() = 0;
46 };
47 
48 struct BitmapDrawEvent : public BitmapEvent {
49  BitmapDrawEvent(BitmapWindow *target) : BitmapEvent(target) {}
50 
51  void Execute() override { event_target->o3d_window->OnDraw(); }
52 };
53 
54 struct BitmapResizeEvent : public BitmapEvent {
55  BitmapResizeEvent(BitmapWindow *target) : BitmapEvent(target) {}
56 
57  void Execute() override { event_target->o3d_window->OnResize(); }
58 };
59 
60 struct BitmapMouseEvent : public BitmapEvent {
61  MouseEvent event;
62 
63  BitmapMouseEvent(BitmapWindow *target, const MouseEvent &e)
64  : BitmapEvent(target), event(e) {}
65 
66  void Execute() override {
67  event_target->mouse_pos = Point(event.x, event.y);
68  if (event.type == MouseEvent::BUTTON_DOWN) {
69  event_target->mouse_buttons |= int(event.button.button);
70  } else if (event.type == MouseEvent::BUTTON_UP) {
71  event_target->mouse_buttons &= ~int(event.button.button);
72  }
73  event_target->o3d_window->OnMouseEvent(event);
74  }
75 };
76 
77 struct BitmapKeyEvent : public BitmapEvent {
78  KeyEvent event;
79 
80  BitmapKeyEvent(BitmapWindow *target, const KeyEvent &e)
81  : BitmapEvent(target), event(e) {}
82 
83  void Execute() override { event_target->o3d_window->OnKeyEvent(event); }
84 };
85 
86 struct BitmapTextInputEvent : public BitmapEvent {
87  std::string textUtf8; // storage for the event
88 
89  BitmapTextInputEvent(BitmapWindow *target, const TextInputEvent &e)
90  : BitmapEvent(target), textUtf8(e.utf8) {}
91 
92  void Execute() override {
93  event_target->o3d_window->OnTextInput({textUtf8.c_str()});
94  }
95 };
96 
100 struct BitmapEventQueue : public std::queue<std::shared_ptr<BitmapEvent>> {
101  using value_t = std::shared_ptr<BitmapEvent>;
102  using super = std::queue<value_t>;
103 
104  using super::empty; // not reliable
105  using super::super;
106  // pop + front needs to be atomic for thread safety. This is exception safe
107  // since shared_ptr copy ctor is noexcept, when it is returned by value.
108  value_t pop_front() {
109  std::lock_guard<std::mutex> lock(evt_q_mutex_);
110  value_t evt = super::front();
111  super::pop();
112  return evt;
113  }
114  void push(const value_t &event) {
115  if (evt_q_mutex_.try_lock()) {
116  super::push(event);
117  evt_q_mutex_.unlock();
118  }
119  }
120 
121 private:
122  std::mutex evt_q_mutex_;
123 };
124 
125 } // namespace
126 
129  BitmapEventQueue event_queue_;
130 };
131 
133  : impl_(new BitmapWindowSystem::Impl()) {
134  if (mode == Rendering::HEADLESS) {
135 #if !defined(__APPLE__) && !defined(_WIN32) && !defined(_WIN64)
137 #else
139  "BitmapWindowSystem(): HEADLESS is only supported on Linux.");
140 #endif
141  }
142 }
143 
145 
147 
148 void BitmapWindowSystem::Uninitialize() { impl_->on_draw_ = nullptr; }
149 
151  impl_->on_draw_ = callback;
152 }
153 
154 // Processes any events in the queue and sleeps till timeout_secs are pver.
155 void BitmapWindowSystem::WaitEventsTimeout(double timeout_secs) {
156  auto t_end = std::chrono::steady_clock::now() +
157  std::chrono::duration<double>(timeout_secs);
158  while (!impl_->event_queue_.empty() &&
159  std::chrono::steady_clock::now() < t_end) {
160  impl_->event_queue_.pop_front()->Execute();
161  std::this_thread::yield();
162  }
163  std::this_thread::sleep_until(t_end);
164 }
165 
167  return Size(32000, 32000);
168 }
169 
171  int width,
172  int height,
173  const char *title,
174  int flags) {
175  auto *w = new BitmapWindow(o3d_window, width, height);
176  return (OSWindow *)w;
177 }
178 
180  BitmapWindow *the_deceased = (BitmapWindow *)w;
181  // This window will soon go to its eternal repose, and since asking corpse-
182  // windows to perform events is ... unpleasant ..., we need to remove all
183  // events in the queue requested for this window. Unfortunately, std::queue
184  // seems to have fallen into the same trap as the first iteration of this
185  // code and not considered the possibility of item resources meeting an
186  // untimely end. As a result, we need to do some copying of queues.
187  BitmapEventQueue filtered_reversed;
188  while (!impl_->event_queue_.empty()) {
189  auto e = impl_->event_queue_.pop_front();
190  if (e->event_target != the_deceased) {
191  filtered_reversed.push(e);
192  }
193  }
194  // The queue is now filtered but reversed. We can empty it back into the
195  // main queue and get the original queue, but filtered of references
196  // to this dying window.
197  while (!filtered_reversed.empty()) {
198  impl_->event_queue_.push(filtered_reversed.pop_front());
199  }
200  // Requiem aeternam dona ei. Requiscat in pace.
201  delete (BitmapWindow *)w;
202 }
203 
205  auto hw = (BitmapWindow *)w;
206  impl_->event_queue_.push(std::make_shared<BitmapDrawEvent>(hw));
207 }
208 
210  auto hw = (BitmapWindow *)w;
211  impl_->event_queue_.push(std::make_shared<BitmapMouseEvent>(hw, e));
212 }
213 
215  auto hw = (BitmapWindow *)w;
216  impl_->event_queue_.push(std::make_shared<BitmapKeyEvent>(hw, e));
217 }
218 
220  const TextInputEvent &e) {
221  auto hw = (BitmapWindow *)w;
222  impl_->event_queue_.push(std::make_shared<BitmapTextInputEvent>(hw, e));
223 }
224 
225 bool BitmapWindowSystem::GetWindowIsVisible(OSWindow w) const { return false; }
226 
228 
230 
231 bool BitmapWindowSystem::IsActiveWindow(OSWindow w) const { return true; }
232 
234  return Point(((BitmapWindow *)w)->frame.x, ((BitmapWindow *)w)->frame.y);
235 }
236 
238  ((BitmapWindow *)w)->frame.x = x;
239  ((BitmapWindow *)w)->frame.y = y;
240 }
241 
243  return Size(((BitmapWindow *)w)->frame.width,
244  ((BitmapWindow *)w)->frame.height);
245 }
246 
248  BitmapWindow *hw = (BitmapWindow *)w;
249  hw->frame.width = width;
250  hw->frame.height = height;
251  hw->o3d_window->OnResize();
252 }
253 
255  return GetWindowSize(w);
256 }
257 
259  return SetWindowSize(w, size.width, size.height);
260 }
261 
263  return 1.0f;
264 }
265 
266 float BitmapWindowSystem::GetUIScaleFactor(OSWindow w) const { return 1.0f; }
267 
268 void BitmapWindowSystem::SetWindowTitle(OSWindow w, const char *title) {}
269 
271  return ((BitmapWindow *)w)->mouse_pos;
272 }
273 
275  return ((BitmapWindow *)w)->mouse_buttons;
276 }
277 
279 
281 
283  auto *renderer = new rendering::FilamentRenderer(
285  ((BitmapWindow *)w)->frame.width, ((BitmapWindow *)w)->frame.height,
287 
288  auto on_after_draw = [this, renderer, w]() {
289  if (!this->impl_->on_draw_) {
290  return;
291  }
292 
293  auto size = this->GetWindowSizePixels(w);
294  Window *window = ((BitmapWindow *)w)->o3d_window;
295 
296  auto on_pixels = [this, window](std::shared_ptr<core::Tensor> image) {
297  if (this->impl_->on_draw_) {
298  this->impl_->on_draw_(window, image);
299  }
300  };
301  renderer->RequestReadPixels(size.width, size.height, on_pixels);
302  };
303  renderer->SetOnAfterDraw(on_after_draw);
304  return renderer;
305 }
306 
308  rendering::FilamentRenderer *renderer) {
309  auto size = GetWindowSizePixels(w);
310  renderer->UpdateBitmapSwapChain(size.width, size.height);
311 }
312 
314 
315 } // namespace gui
316 } // namespace visualization
317 } // namespace cloudViewer
Point mouse_pos
Rect frame
std::string textUtf8
Window * o3d_window
MouseEvent event
int mouse_buttons
BitmapWindow * event_target
std::shared_ptr< core::Tensor > image
std::function< void(std::shared_ptr< core::Tensor >)> callback
int width
int size
int height
int Execute(void)
void SetWindowTitle(OSWindow w, const char *title) override
std::function< void(Window *, std::shared_ptr< core::Tensor >)> OnDrawCallback
void PostTextInputEvent(OSWindow w, const TextInputEvent &e)
rendering::FilamentRenderer * CreateRenderer(OSWindow w) override
void SetWindowSizePixels(OSWindow w, const Size &size) override
void WaitEventsTimeout(double timeout_secs) override
void ResizeRenderer(OSWindow w, rendering::FilamentRenderer *renderer) override
void SetWindowPos(OSWindow w, int x, int y) override
OSWindow CreateOSWindow(Window *o3d_window, int width, int height, const char *title, int flags) override
void SetWindowSize(OSWindow w, int width, int height) override
void PostMouseEvent(OSWindow w, const MouseEvent &e)
static FilamentResourceManager & GetResourceManager()
void UpdateBitmapSwapChain(int width, int height) override
#define LogWarning(...)
Definition: Logging.h:72
Generic file read and write utility for python interface.