Loading...
Searching...
No Matches
Window/WindowDevice.hpp
1#pragma once
2
3#include <Gfx/GfxParameter.hpp>
4#include <Gfx/Graph/ScreenNode.hpp>
5#include <Gfx/Graph/Window.hpp>
6#include <Gfx/Settings/Model.hpp>
7#include <Gfx/Window/PointerAddresses.hpp>
8
9#include <core/application/ApplicationSettings.hpp>
10#include <score/application/ApplicationContext.hpp>
11
12#include <ossia/network/base/device.hpp>
13#include <ossia/network/base/protocol.hpp>
14#include <ossia/network/generic/generic_node.hpp>
15
16#include <ossia-qt/invoke.hpp>
17
18#include <QGuiApplication>
19
20namespace Gfx
21{
22
23static score::gfx::ScreenNode* createScreenNode(
24 SwapchainFlag swapFlag = SwapchainFlag::NoFlag,
25 SwapchainFormat swapFormat = SwapchainFormat::SDR)
26{
27 const auto& settings = score::AppContext().applicationSettings;
28 const auto& gfx_settings = score::AppContext().settings<Gfx::Settings::Model>();
29
30 auto make_configuration = [&] {
32 double rate = gfx_settings.getRate();
33 if(rate > 0)
34 conf = {.manualRenderingRate = 1000. / rate, .supportsVSync = true};
35 else
36 conf = {.manualRenderingRate = {}, .supportsVSync = true};
37 return conf;
38 };
39
40 auto node = new score::gfx::ScreenNode{
41 make_configuration(), false, (settings.autoplay && !settings.gui)};
42 node->setSwapchainFlag(swapFlag);
43 node->setSwapchainFormat(swapFormat);
44
45 QObject::connect(
46 &gfx_settings, &Gfx::Settings::Model::RateChanged, node,
47 [node, make_configuration] { node->setConfiguration(make_configuration()); });
48
49 return node;
50}
51class window_device : public ossia::net::device_base
52{
53 score::gfx::ScreenNode* m_screen{};
54 gfx_node_base m_root;
55 QObject m_qtContext;
56
57 ossia::net::parameter_base* scaled_win{};
58 ossia::net::parameter_base* gl_win{};
59 ossia::net::parameter_base* abs_win{};
60 ossia::net::parameter_base* abs_tablet_win{};
61 ossia::net::parameter_base* size_param{};
62 ossia::net::parameter_base* rendersize_param{};
63 pointer_addresses m_pointers;
64
65 void update_viewport()
66 {
67 auto v = rendersize_param->value();
68 if(auto val = v.target<ossia::vec2f>())
69 {
70 auto dom = abs_win->get_domain();
71 if((*val)[0] >= 1.f && (*val)[1] >= 1.f)
72 {
73 ossia::set_max(dom, *val);
74 abs_win->set_domain(std::move(dom));
75 abs_tablet_win->set_domain(std::move(dom));
76 }
77 else
78 {
79 // Use the normal size
80 v = size_param->value();
81 if(auto val = v.target<ossia::vec2f>())
82 {
83 auto dom = abs_win->get_domain();
84 if((*val)[0] >= 1.f && (*val)[1] >= 1.f)
85 {
86 ossia::set_max(dom, *val);
87 abs_win->set_domain(std::move(dom));
88 abs_tablet_win->set_domain(std::move(dom));
89 }
90 }
91 }
92 }
93 else
94 {
95 v = size_param->value();
96 if(auto val = v.target<ossia::vec2f>())
97 {
98 auto dom = abs_win->get_domain();
99 if((*val)[0] >= 1.f && (*val)[1] >= 1.f)
100 {
101 ossia::set_max(dom, *val);
102 abs_win->set_domain(std::move(dom));
103 abs_tablet_win->set_domain(std::move(dom));
104 }
105 }
106 }
107 }
108
109public:
110 score::gfx::ScreenNode* screen() const noexcept { return m_screen; }
112 {
113 // The callbacks go before the window closes: the tree, and with it every
114 // parameter they push to, is usually cleared by then, and closing the
115 // window can still deliver move, pointer and key events.
116 m_screen->onWindowMove = [](QPointF) { };
117 m_screen->onMouseMove = [](QPointF, QPointF) { };
118 m_screen->onTabletMove = [](QTabletEvent*) { };
119 m_screen->onPointersChanged = [] { };
120 m_screen->onKey = [](int, const QString&) { };
121 m_screen->onKeyRelease = [](int, const QString&) { };
122 m_screen->onFps = [](float) { };
123
124 if(auto w = m_screen->window())
125 w->close();
126
127 m_protocol->stop();
128
129 m_root.clear_children();
130
131 m_protocol.reset();
132 }
133
135 std::unique_ptr<gfx_protocol_base> proto, std::string name,
136 SwapchainFlag swapFlag = SwapchainFlag::NoFlag,
137 SwapchainFormat swapFormat = SwapchainFormat::SDR)
138 : ossia::net::device_base{std::move(proto)}
139 , m_screen{createScreenNode(swapFlag, swapFormat)}
140 , m_root{*this, *static_cast<gfx_protocol_base*>(m_protocol.get()), m_screen, name}
141 {
142 this->m_capabilities.change_tree = true;
143 m_screen->setTitle(QString::fromStdString(name));
144
145 {
146 auto screen_node
147 = std::make_unique<ossia::net::generic_node>("screen", *this, m_root);
148 auto screen_param = screen_node->create_parameter(ossia::val_type::STRING);
149 screen_param->set_domain(ossia::make_domain(int(0), int(100)));
150 screen_param->add_callback([this](const ossia::value& v) {
151 if(auto val = v.target<int>())
152 {
153 ossia::qt::run_async(&m_qtContext, [screen = this->m_screen, scr = *val] {
154 const auto& cur_screens = qGuiApp->screens();
155 if(ossia::valid_index(scr, cur_screens))
156 {
157 screen->setScreen(cur_screens[scr]);
158 }
159 });
160 }
161 else if(auto val = v.target<std::string>())
162 {
163 ossia::qt::run_async(&m_qtContext, [screen = this->m_screen, scr = *val] {
164 const auto& cur_screens = qGuiApp->screens();
165 for(auto s : cur_screens)
166 {
167 if(s->name() == scr.c_str())
168 {
169 screen->setScreen(s);
170 break;
171 }
172 }
173 });
174 }
175 });
176 m_root.add_child(std::move(screen_node));
177 }
178
179 {
180 struct move_window_lock
181 {
182 bool locked{};
183 };
184 auto lock = std::make_shared<move_window_lock>();
185
186 auto pos_node
187 = std::make_unique<ossia::net::generic_node>("position", *this, m_root);
188 auto pos_param = pos_node->create_parameter(ossia::val_type::VEC2F);
189 pos_param->push_value(
190 ossia::vec2f{100.f, 100.f}); // FIXME Try to detect center of screen ?
191 pos_param->add_callback([this, lock](const ossia::value& v) {
192 if(lock->locked)
193 return;
194 if(auto val = v.target<ossia::vec2f>())
195 {
196 ossia::qt::run_async(&m_qtContext, [screen = this->m_screen, v = *val, lock] {
197 screen->setPosition({(int)v[0], (int)v[1]});
198 });
199 }
200 });
201
202 m_screen->onWindowMove = [this, pos_param, lock](QPointF pos) {
203 if(lock->locked)
204 return;
205 if(const auto& w = m_screen->window())
206 {
207 lock->locked = true;
208 pos_param->set_value(ossia::vec2f{float(pos.x()), float(pos.y())});
209 lock->locked = false;
210 }
211 };
212 m_root.add_child(std::move(pos_node));
213 }
214
215 // Mouse input
216 {
217 auto node = std::make_unique<ossia::net::generic_node>("cursor", *this, m_root);
218 {
219 auto scale_node
220 = std::make_unique<ossia::net::generic_node>("scaled", *this, *node);
221 scaled_win = scale_node->create_parameter(ossia::val_type::VEC2F);
222 scaled_win->set_domain(ossia::make_domain(0.f, 1.f));
223 scaled_win->push_value(ossia::vec2f{0.f, 0.f});
224 node->add_child(std::move(scale_node));
225 }
226 {
227 // Same as scaled but with y up, as OpenGL's texture coordinates
228 auto scale_node = std::make_unique<ossia::net::generic_node>("gl", *this, *node);
229 gl_win = scale_node->create_parameter(ossia::val_type::VEC2F);
230 gl_win->set_domain(ossia::make_domain(0.f, 1.f));
231 gl_win->push_value(ossia::vec2f{0.f, 0.f});
232 node->add_child(std::move(scale_node));
233 }
234 {
235 auto abs_node
236 = std::make_unique<ossia::net::generic_node>("absolute", *this, *node);
237 abs_win = abs_node->create_parameter(ossia::val_type::VEC2F);
238 abs_win->set_domain(
239 ossia::make_domain(ossia::vec2f{0.f, 0.f}, ossia::vec2f{1280, 270.f}));
240 abs_win->push_value(ossia::vec2f{0.f, 0.f});
241 node->add_child(std::move(abs_node));
242 }
243 m_pointers.add_clip(*this, *node);
244 {
245 auto visible
246 = std::make_unique<ossia::net::generic_node>("visible", *this, *node);
247 auto param = visible->create_parameter(ossia::val_type::BOOL);
248 param->add_callback([this](const ossia::value& v) {
249 if(auto val = v.target<bool>())
250 {
251 ossia::qt::run_async(&m_qtContext, [screen = this->m_screen, v = *val] {
252 screen->setCursor(v);
253 });
254 }
255 });
256 node->add_child(std::move(visible));
257 }
258
259 m_screen->onMouseMove = [this](const QPointF screen, const QPointF win) {
260 if(const auto& w = m_screen->window())
261 {
262 const auto sz = w->size();
263 scaled_win->push_value(
264 ossia::vec2f{float(win.x() / sz.width()), float(win.y() / sz.height())});
265 gl_win->push_value(
266 ossia::vec2f{
267 float(win.x() / sz.width()), 1.f - float(win.y() / sz.height())});
268 const auto clip = score::gfx::PointerState::toClip(win, sz);
269 m_pointers.set_clip(float(clip.x()), float(clip.y()));
270 abs_win->push_value(ossia::vec2f{float(win.x()), float(win.y())});
271 }
272 };
273
274 m_root.add_child(std::move(node));
275 }
276
277 // Touch input: touch/points, touch/history
278 m_pointers.add_touch(
279 *this, m_root, static_cast<gfx_protocol_base*>(m_protocol.get())->context);
280 m_screen->onPointersChanged = [this] {
281 if(const auto& st = m_screen->pointerState())
282 m_pointers.publish(*st);
283 };
284
285 // Tablet input
286 ossia::net::parameter_base* scaled_tablet_win{};
287 {
288 auto node = std::make_unique<ossia::net::generic_node>("tablet", *this, m_root);
289 ossia::net::parameter_base* tablet_pressure{};
290 ossia::net::parameter_base* tablet_z{};
291 ossia::net::parameter_base* tablet_tan{};
292 ossia::net::parameter_base* tablet_rot{};
293 ossia::net::parameter_base* tablet_tilt_x{};
294 ossia::net::parameter_base* tablet_tilt_y{};
295 {
296 auto scale_node
297 = std::make_unique<ossia::net::generic_node>("scaled", *this, *node);
298 scaled_tablet_win = scale_node->create_parameter(ossia::val_type::VEC2F);
299 scaled_tablet_win->set_domain(ossia::make_domain(0.f, 1.f));
300 scaled_tablet_win->push_value(ossia::vec2f{0.f, 0.f});
301 node->add_child(std::move(scale_node));
302 }
303 {
304 auto abs_node
305 = std::make_unique<ossia::net::generic_node>("absolute", *this, *node);
306 abs_tablet_win = abs_node->create_parameter(ossia::val_type::VEC2F);
307 abs_tablet_win->set_domain(
308 ossia::make_domain(ossia::vec2f{0.f, 0.f}, ossia::vec2f{1280, 270.f}));
309 abs_tablet_win->push_value(ossia::vec2f{0.f, 0.f});
310 node->add_child(std::move(abs_node));
311 }
312 {
313 auto scale_node = std::make_unique<ossia::net::generic_node>("z", *this, *node);
314 tablet_z = scale_node->create_parameter(ossia::val_type::INT);
315 node->add_child(std::move(scale_node));
316 }
317 {
318 auto scale_node
319 = std::make_unique<ossia::net::generic_node>("pressure", *this, *node);
320 tablet_pressure = scale_node->create_parameter(ossia::val_type::FLOAT);
321 //tablet_pressure->set_domain(ossia::make_domain(0.f, 1.f));
322 //tablet_pressure->push_value(0.f);
323 node->add_child(std::move(scale_node));
324 }
325 {
326 auto scale_node
327 = std::make_unique<ossia::net::generic_node>("tangential", *this, *node);
328 tablet_tan = scale_node->create_parameter(ossia::val_type::FLOAT);
329 tablet_tan->set_domain(ossia::make_domain(-1.f, 1.f));
330 //tablet_tan->push_value(0.f);
331 node->add_child(std::move(scale_node));
332 }
333 {
334 auto scale_node
335 = std::make_unique<ossia::net::generic_node>("rotation", *this, *node);
336 tablet_rot = scale_node->create_parameter(ossia::val_type::FLOAT);
337 tablet_rot->set_unit(ossia::degree_u{});
338 tablet_rot->set_domain(ossia::make_domain(-180.f, 180.f));
339 node->add_child(std::move(scale_node));
340 }
341 {
342 auto scale_node
343 = std::make_unique<ossia::net::generic_node>("tilt_x", *this, *node);
344 tablet_tilt_x = scale_node->create_parameter(ossia::val_type::FLOAT);
345 tablet_tilt_x->set_domain(ossia::make_domain(-60.f, 60.f));
346 tablet_tilt_x->set_unit(ossia::degree_u{});
347 node->add_child(std::move(scale_node));
348 }
349 {
350 auto scale_node
351 = std::make_unique<ossia::net::generic_node>("tilt_y", *this, *node);
352 tablet_tilt_y = scale_node->create_parameter(ossia::val_type::FLOAT);
353 tablet_tilt_y->set_domain(ossia::make_domain(-60.f, 60.f));
354 tablet_tilt_y->set_unit(ossia::degree_u{});
355 node->add_child(std::move(scale_node));
356 }
357
358 m_screen->onTabletMove = [=, this](QTabletEvent* ev) {
359 if(const auto& w = m_screen->window())
360 {
361 const auto sz = w->size();
362 const auto win = ev->position();
363 scaled_tablet_win->push_value(
364 ossia::vec2f{float(win.x() / sz.width()), float(win.y() / sz.height())});
365 abs_tablet_win->push_value(ossia::vec2f{float(win.x()), float(win.y())});
366 tablet_pressure->push_value(ev->pressure());
367 tablet_tan->push_value(ev->tangentialPressure());
368 tablet_rot->push_value(ev->rotation());
369 tablet_z->push_value(ev->z());
370 tablet_tilt_x->push_value(ev->xTilt());
371 tablet_tilt_y->push_value(ev->yTilt());
372 }
373 };
374
375 m_root.add_child(std::move(node));
376 }
377
378 {
379 auto size_node = std::make_unique<ossia::net::generic_node>("size", *this, m_root);
380 size_param = size_node->create_parameter(ossia::val_type::VEC2F);
381 size_param->push_value(ossia::vec2f{1280.f, 720.f});
382 size_param->add_callback([this](const ossia::value& v) {
383 if(auto val = v.target<ossia::vec2f>())
384 {
385 ossia::qt::run_async(&m_qtContext, [screen = this->m_screen, v = *val] {
386 screen->setSize({(int)v[0], (int)v[1]});
387 });
388
389 update_viewport();
390 }
391 });
392
393 m_root.add_child(std::move(size_node));
394 }
395
396 {
397 auto size_node
398 = std::make_unique<ossia::net::generic_node>("rendersize", *this, m_root);
399 ossia::net::set_description(
400 *size_node, "Set to [0, 0] to use the viewport's size");
401
402 rendersize_param = size_node->create_parameter(ossia::val_type::VEC2F);
403 rendersize_param->push_value(ossia::vec2f{0.f, 0.f});
404 rendersize_param->add_callback([this](const ossia::value& v) {
405 if(auto val = v.target<ossia::vec2f>())
406 {
407 ossia::qt::run_async(&m_qtContext, [screen = this->m_screen, v = *val] {
408 screen->setRenderSize({(int)v[0], (int)v[1]});
409 });
410
411 update_viewport();
412 }
413 });
414
415 m_root.add_child(std::move(size_node));
416 }
417
418 // Keyboard input
419 {
420 auto node = std::make_unique<ossia::net::generic_node>("key", *this, m_root);
421 {
422 auto press_node
423 = std::make_unique<ossia::net::generic_node>("press", *this, *node);
424 ossia::net::parameter_base* press_param{};
425 ossia::net::parameter_base* text_param{};
426 {
427 auto code_node
428 = std::make_unique<ossia::net::generic_node>("code", *this, *press_node);
429 press_param = code_node->create_parameter(ossia::val_type::INT);
430 press_param->push_value(ossia::vec2f{0.f, 0.f});
431 press_node->add_child(std::move(code_node));
432 }
433 {
434 auto text_node
435 = std::make_unique<ossia::net::generic_node>("text", *this, *press_node);
436 text_param = text_node->create_parameter(ossia::val_type::STRING);
437 press_node->add_child(std::move(text_node));
438 }
439
440 m_screen->onKey = [press_param, text_param](int key, const QString& text) {
441 press_param->push_value(key);
442 text_param->push_value(text.toStdString());
443 };
444 node->add_child(std::move(press_node));
445 }
446 {
447 auto release_node
448 = std::make_unique<ossia::net::generic_node>("release", *this, *node);
449 ossia::net::parameter_base* press_param{};
450 ossia::net::parameter_base* text_param{};
451 {
452 auto code_node
453 = std::make_unique<ossia::net::generic_node>("code", *this, *release_node);
454 press_param = code_node->create_parameter(ossia::val_type::INT);
455 press_param->push_value(ossia::vec2f{0.f, 0.f});
456 release_node->add_child(std::move(code_node));
457 }
458 {
459 auto text_node
460 = std::make_unique<ossia::net::generic_node>("text", *this, *release_node);
461 text_param = text_node->create_parameter(ossia::val_type::STRING);
462 release_node->add_child(std::move(text_node));
463 }
464
465 m_screen->onKeyRelease
466 = [press_param, text_param](int key, const QString& text) {
467 press_param->push_value(key);
468 text_param->push_value(text.toStdString());
469 };
470 node->add_child(std::move(release_node));
471 }
472
473 m_root.add_child(std::move(node));
474 }
475
476 {
477 auto title_node
478 = std::make_unique<ossia::net::generic_node>("title", *this, m_root);
479 auto title_param = title_node->create_parameter(ossia::val_type::STRING);
480 title_param->add_callback([this](const ossia::value& v) {
481 if(auto val = v.target<std::string>())
482 {
483 ossia::qt::run_async(
484 &m_qtContext, [screen = this->m_screen, t = QString::fromStdString(*val)] {
485 screen->setTitle(t);
486 });
487 }
488 });
489 title_param->set_value(name);
490 m_root.add_child(std::move(title_node));
491 }
492
493 {
494 auto fs_node
495 = std::make_unique<ossia::net::generic_node>("fullscreen", *this, m_root);
496 auto fs_param = fs_node->create_parameter(ossia::val_type::BOOL);
497 fs_param->add_callback([this](const ossia::value& v) {
498 if(auto val = v.target<bool>())
499 {
500 ossia::qt::run_async(&m_qtContext, [screen = this->m_screen, v = *val] {
501 screen->setFullScreen(v);
502 });
503 }
504 });
505 m_root.add_child(std::move(fs_node));
506 }
507
508 {
509 auto fps_node = std::make_unique<ossia::net::generic_node>("fps", *this, m_root);
510 auto fps_param = fps_node->create_parameter(ossia::val_type::FLOAT);
511 m_screen->onFps = [fps_param](float fps) { fps_param->push_value(fps); };
512 m_root.add_child(std::move(fps_node));
513 }
514 }
515
516 const gfx_node_base& get_root_node() const override { return m_root; }
517 gfx_node_base& get_root_node() override { return m_root; }
518};
519
520}
Definition score-plugin-gfx/Gfx/Settings/Model.hpp:41
Definition GfxParameter.hpp:73
Definition GfxParameter.hpp:54
Definition Window/WindowDevice.hpp:52
Binds the rendering pipeline to ossia processes.
Definition AssetTable.cpp:7
Definition PointerAddresses.hpp:37
void add_clip(ossia::net::device_base &dev, ossia::net::node_base &cursor)
cursor/clip, under an existing cursor node.
Definition PointerAddresses.hpp:45
T & settings() const
Access a specific Settings model instance.
Definition ApplicationContext.hpp:41
const score::ApplicationSettings & applicationSettings
Access to start-up command-line settings.
Definition ApplicationContext.hpp:111
Definition OutputNode.hpp:94
static QPointF toClip(QPointF win, QSizeF sz) noexcept
Window position -> clip space over a window of size sz.
Definition PointerState.hpp:32
This node is used for rendering to a score::gfx::Window.
Definition ScreenNode.hpp:14
std::function< void()> onPointersChanged
The window's pointerState() changed (mouse or touch).
Definition ScreenNode.hpp:68