42 ossia::net::parameter_base* fps_param{};
43 ossia::net::parameter_base* rendersize_param{};
45 struct PerWindowParams
47 ossia::net::parameter_base* scaled_cursor{};
48 ossia::net::parameter_base* gl_cursor{};
49 ossia::net::parameter_base* abs_cursor{};
50 ossia::net::parameter_base* size_param{};
51 ossia::net::parameter_base* pos_param{};
52 ossia::net::parameter_base* source_pos{};
53 ossia::net::parameter_base* source_size{};
54 ossia::net::parameter_base* key_press_code{};
55 ossia::net::parameter_base* key_press_text{};
56 ossia::net::parameter_base* key_release_code{};
57 ossia::net::parameter_base* key_release_text{};
59 std::vector<QMetaObject::Connection> connections;
61 std::vector<PerWindowParams> m_perWindow;
63 void update_viewport(PerWindowParams& pw)
65 if(!rendersize_param || !pw.abs_cursor)
68 auto v = rendersize_param->value();
69 if(
auto val = v.target<ossia::vec2f>())
71 if((*val)[0] >= 1.f && (*val)[1] >= 1.f)
73 auto dom = pw.abs_cursor->get_domain();
74 ossia::set_max(dom, *val);
75 pw.abs_cursor->set_domain(std::move(dom));
77 else if(pw.size_param)
79 v = pw.size_param->value();
80 if(
auto sz = v.target<ossia::vec2f>())
82 if((*sz)[0] >= 1.f && (*sz)[1] >= 1.f)
84 auto dom = pw.abs_cursor->get_domain();
85 ossia::set_max(dom, *sz);
86 pw.abs_cursor->set_domain(std::move(dom));
95 const std::vector<OutputMapping>& mappings,
96 std::unique_ptr<gfx_protocol_base> proto, std::string name,
97 SwapchainFlag swapFlag = SwapchainFlag::NoFlag,
98 SwapchainFormat swapFormat = SwapchainFormat::SDR)
99 : ossia::net::device_base{std::move(proto)}
100 , m_node{createMultiWindowNode(mappings, swapFlag, swapFormat)}
101 , m_root{*
this, *
static_cast<gfx_protocol_base*
>(m_protocol.get()), m_node, name}
103 this->m_capabilities.change_tree =
true;
106 m_node->onWindowsCreated = [
this] { connectWindowSignals(); };
110 auto fps_node = std::make_unique<ossia::net::generic_node>(
"fps", *
this, m_root);
111 fps_param = fps_node->create_parameter(ossia::val_type::FLOAT);
112 m_node->onFps = [
this](
float fps) { fps_param->push_value(fps); };
113 m_root.add_child(std::move(fps_node));
119 = std::make_unique<ossia::net::generic_node>(
"rendersize", *
this, m_root);
120 ossia::net::set_description(*rs_node,
"Set to [0, 0] to use the viewport's size");
121 rendersize_param = rs_node->create_parameter(ossia::val_type::VEC2F);
122 rendersize_param->push_value(ossia::vec2f{0.f, 0.f});
123 rendersize_param->add_callback([
this](
const ossia::value& v) {
124 if(
auto val = v.target<ossia::vec2f>())
126 m_node->setRenderSize({(int)(*val)[0], (int)(*val)[1]});
127 for(
auto& pw : m_perWindow)
131 m_root.add_child(std::move(rs_node));
135 m_perWindow.resize(mappings.size());
137 for(
int i = 0; i < (int)mappings.size(); ++i)
139 auto& pw = m_perWindow[i];
140 const auto& mapping = mappings[i];
141 const auto idx_str = std::to_string(i);
143 auto win_node = std::make_unique<ossia::net::generic_node>(idx_str, *
this, m_root);
148 = std::make_unique<ossia::net::generic_node>(
"cursor", *
this, *win_node);
150 auto scale_node = std::make_unique<ossia::net::generic_node>(
151 "scaled", *
this, *cursor_node);
152 pw.scaled_cursor = scale_node->create_parameter(ossia::val_type::VEC2F);
153 pw.scaled_cursor->set_domain(ossia::make_domain(0.f, 1.f));
154 pw.scaled_cursor->push_value(ossia::vec2f{0.f, 0.f});
155 cursor_node->add_child(std::move(scale_node));
159 = std::make_unique<ossia::net::generic_node>(
"gl", *
this, *cursor_node);
160 pw.gl_cursor = scale_node->create_parameter(ossia::val_type::VEC2F);
161 pw.gl_cursor->set_domain(ossia::make_domain(0.f, 1.f));
162 pw.gl_cursor->push_value(ossia::vec2f{0.f, 0.f});
163 cursor_node->add_child(std::move(scale_node));
166 auto abs_node = std::make_unique<ossia::net::generic_node>(
167 "absolute", *
this, *cursor_node);
168 pw.abs_cursor = abs_node->create_parameter(ossia::val_type::VEC2F);
169 pw.abs_cursor->set_domain(
171 ossia::vec2f{0.f, 0.f}, ossia::vec2f{
172 (float)mapping.windowSize.width(),
173 (
float)mapping.windowSize.height()}));
174 pw.abs_cursor->push_value(ossia::vec2f{0.f, 0.f});
175 cursor_node->add_child(std::move(abs_node));
177 win_node->add_child(std::move(cursor_node));
183 = std::make_unique<ossia::net::generic_node>(
"size", *
this, *win_node);
184 pw.size_param = size_node->create_parameter(ossia::val_type::VEC2F);
185 pw.size_param->push_value(
187 (float)mapping.windowSize.width(), (float)mapping.windowSize.height()});
188 pw.size_param->add_callback([
this, i](
const ossia::value& v) {
189 if(
auto val = v.target<ossia::vec2f>())
191 ossia::qt::run_async(&m_qtContext, [
this, i, v = *val] {
192 const auto& outputs = m_node->windowOutputs();
193 if(i < (
int)outputs.size())
195 if(auto& w = outputs[i].window)
196 w->resize(QSize{(int)v[0], (int)v[1]});
199 if(i < (
int)m_perWindow.size())
200 update_viewport(m_perWindow[i]);
203 win_node->add_child(std::move(size_node));
209 = std::make_unique<ossia::net::generic_node>(
"position", *
this, *win_node);
210 pw.pos_param = pos_node->create_parameter(ossia::val_type::VEC2F);
211 pw.pos_param->push_value(
213 (float)mapping.windowPosition.x(), (float)mapping.windowPosition.y()});
214 pw.pos_param->add_callback([
this, i](
const ossia::value& v) {
215 if(
auto val = v.target<ossia::vec2f>())
217 ossia::qt::run_async(&m_qtContext, [
this, i, v = *val] {
218 const auto& outputs = m_node->windowOutputs();
219 if(i < (
int)outputs.size())
221 if(auto& w = outputs[i].window)
222 w->setPosition(QPoint{(int)v[0], (int)v[1]});
227 win_node->add_child(std::move(pos_node));
233 = std::make_unique<ossia::net::generic_node>(
"fullscreen", *
this, *win_node);
234 auto fs_param = fs_node->create_parameter(ossia::val_type::BOOL);
235 fs_param->push_value(mapping.fullscreen);
236 fs_param->add_callback([
this, i](
const ossia::value& v) {
237 if(
auto val = v.target<
bool>())
239 ossia::qt::run_async(&m_qtContext, [this, i, v = *val] {
240 const auto& outputs = m_node->windowOutputs();
241 if(i < (int)outputs.size())
243 if(auto& w = outputs[i].window)
254 win_node->add_child(std::move(fs_node));
260 = std::make_unique<ossia::net::generic_node>(
"screen", *
this, *win_node);
261 auto screen_param = screen_node->create_parameter(ossia::val_type::STRING);
262 screen_param->push_value(
263 mapping.screenIndex >= 0 ? std::to_string(mapping.screenIndex)
265 screen_param->add_callback([
this, i](
const ossia::value& v) {
266 auto apply = [
this, i](
const std::string& scr) {
267 const auto& outputs = m_node->windowOutputs();
268 if(i >= (
int)outputs.size())
270 auto& w = outputs[i].window;
274 const auto& cur_screens = qGuiApp->screens();
278 int idx = QString::fromStdString(scr).toInt(&ok);
281 if(ossia::valid_index(idx, cur_screens))
282 w->setScreen(cur_screens[idx]);
287 for(
auto s : cur_screens)
289 if(s->name().toStdString() == scr)
297 if(
auto val = v.target<std::string>())
299 ossia::qt::run_async(
300 &m_qtContext, [apply, scr = *val] { apply(scr); });
302 else if(
auto val = v.target<
int>())
304 ossia::qt::run_async(
305 &m_qtContext, [apply, scr = std::to_string(*val)] { apply(scr); });
308 win_node->add_child(std::move(screen_node));
314 = std::make_unique<ossia::net::generic_node>(
"source", *
this, *win_node);
316 auto pos_node = std::make_unique<ossia::net::generic_node>(
317 "position", *
this, *source_node);
318 ossia::net::set_description(*pos_node,
"UV position of source rect (0-1)");
319 pw.source_pos = pos_node->create_parameter(ossia::val_type::VEC2F);
320 pw.source_pos->set_domain(ossia::make_domain(0.f, 1.f));
321 pw.source_pos->push_value(
323 (float)mapping.sourceRect.x(), (float)mapping.sourceRect.y()});
324 pw.source_pos->add_callback([
this, i](
const ossia::value& v) {
325 if(
auto val = v.target<ossia::vec2f>())
327 const auto& outputs = m_node->windowOutputs();
328 if(i < (
int)outputs.size())
330 auto r = outputs[i].sourceRect;
331 r.moveTopLeft(QPointF{(*val)[0], (*val)[1]});
332 m_node->setSourceRect(i, r);
336 source_node->add_child(std::move(pos_node));
340 = std::make_unique<ossia::net::generic_node>(
"size", *
this, *source_node);
341 ossia::net::set_description(*sz_node,
"UV size of source rect (0-1)");
342 pw.source_size = sz_node->create_parameter(ossia::val_type::VEC2F);
343 pw.source_size->set_domain(ossia::make_domain(0.f, 1.f));
344 pw.source_size->push_value(
346 (float)mapping.sourceRect.width(),
347 (float)mapping.sourceRect.height()});
348 pw.source_size->add_callback([
this, i](
const ossia::value& v) {
349 if(
auto val = v.target<ossia::vec2f>())
351 const auto& outputs = m_node->windowOutputs();
352 if(i < (
int)outputs.size())
354 auto r = outputs[i].sourceRect;
355 r.setSize(QSizeF{(*val)[0], (*val)[1]});
356 m_node->setSourceRect(i, r);
360 source_node->add_child(std::move(sz_node));
362 win_node->add_child(std::move(source_node));
368 = std::make_unique<ossia::net::generic_node>(
"blend", *
this, *win_node);
378 = {{
"left", 0, mapping.blendLeft.width, mapping.blendLeft.gamma},
379 {
"right", 1, mapping.blendRight.width, mapping.blendRight.gamma},
380 {
"top", 2, mapping.blendTop.width, mapping.blendTop.gamma},
381 {
"bottom", 3, mapping.blendBottom.width, mapping.blendBottom.gamma}};
383 for(
auto& [sname, side, initW, initG] : sides)
386 = std::make_unique<ossia::net::generic_node>(sname, *
this, *blend_node);
389 = std::make_unique<ossia::net::generic_node>(
"width", *
this, *side_node);
390 ossia::net::set_description(*w_node,
"Blend width in UV space (0-0.5)");
391 auto w_param = w_node->create_parameter(ossia::val_type::FLOAT);
392 w_param->set_domain(ossia::make_domain(0.f, 0.5f));
393 w_param->push_value(initW);
394 w_param->add_callback([
this, i, side](
const ossia::value& v) {
395 if(
auto val = v.target<
float>())
397 const auto& outputs = m_node->windowOutputs();
398 if(i < (int)outputs.size())
404 gamma = outputs[i].blendLeft.gamma;
407 gamma = outputs[i].blendRight.gamma;
410 gamma = outputs[i].blendTop.gamma;
413 gamma = outputs[i].blendBottom.gamma;
416 m_node->setEdgeBlend(i, side, *val, gamma);
420 side_node->add_child(std::move(w_node));
424 = std::make_unique<ossia::net::generic_node>(
"gamma", *
this, *side_node);
425 ossia::net::set_description(*g_node,
"Blend curve exponent (0.1-4.0)");
426 auto g_param = g_node->create_parameter(ossia::val_type::FLOAT);
427 g_param->set_domain(ossia::make_domain(0.1f, 4.f));
428 g_param->push_value(initG);
429 g_param->add_callback([
this, i, side](
const ossia::value& v) {
430 if(
auto val = v.target<
float>())
432 const auto& outputs = m_node->windowOutputs();
433 if(i < (int)outputs.size())
439 width = outputs[i].blendLeft.width;
442 width = outputs[i].blendRight.width;
445 width = outputs[i].blendTop.width;
448 width = outputs[i].blendBottom.width;
451 m_node->setEdgeBlend(i, side, width, *val);
455 side_node->add_child(std::move(g_node));
457 blend_node->add_child(std::move(side_node));
459 win_node->add_child(std::move(blend_node));
465 = std::make_unique<ossia::net::generic_node>(
"key", *
this, *win_node);
470 = std::make_unique<ossia::net::generic_node>(
"press", *
this, *key_node);
473 = std::make_unique<ossia::net::generic_node>(
"code", *
this, *press_node);
474 pw.key_press_code = code_node->create_parameter(ossia::val_type::INT);
475 press_node->add_child(std::move(code_node));
479 = std::make_unique<ossia::net::generic_node>(
"text", *
this, *press_node);
480 pw.key_press_text = text_node->create_parameter(ossia::val_type::STRING);
481 press_node->add_child(std::move(text_node));
483 key_node->add_child(std::move(press_node));
489 = std::make_unique<ossia::net::generic_node>(
"release", *
this, *key_node);
491 auto code_node = std::make_unique<ossia::net::generic_node>(
492 "code", *
this, *release_node);
493 pw.key_release_code = code_node->create_parameter(ossia::val_type::INT);
494 release_node->add_child(std::move(code_node));
497 auto text_node = std::make_unique<ossia::net::generic_node>(
498 "text", *
this, *release_node);
499 pw.key_release_text = text_node->create_parameter(ossia::val_type::STRING);
500 release_node->add_child(std::move(text_node));
502 key_node->add_child(std::move(release_node));
505 win_node->add_child(std::move(key_node));
508 m_root.add_child(std::move(win_node));
513 void connectWindowSignals()
515 const auto& outputs = m_node->windowOutputs();
516 for(
int i = 0; i < (int)outputs.size() && i < (int)m_perWindow.size(); ++i)
518 auto& pw = m_perWindow[i];
519 const auto& wo = outputs[i];
523 auto* w = wo.window.get();
526 pw.connections.push_back(
528 w, &score::gfx::Window::mouseMove,
529 [&pw, w](
const QPointF screen,
const QPointF win) {
531 if(sz.width() > 0 && sz.height() > 0)
533 pw.scaled_cursor->push_value(
534 ossia::vec2f{float(win.x() / sz.width()), float(win.y() / sz.height())});
535 pw.gl_cursor->push_value(
537 float(win.x() / sz.width()), 1.f - float(win.y() / sz.height())});
538 pw.abs_cursor->push_value(ossia::vec2f{float(win.x()), float(win.y())});
553 pw.connections.push_back(
554 QObject::connect(w, &score::gfx::Window::key, [&pw](
int k,
const QString& t) {
555 pw.key_press_code->push_value(k);
556 pw.key_press_text->push_value(t.toStdString());
558 pw.connections.push_back(
560 w, &score::gfx::Window::keyRelease, [&pw](
int k,
const QString& t) {
561 pw.key_release_code->push_value(k);
562 pw.key_release_text->push_value(t.toStdString());
567 ~multiwindow_device()
570 for(
auto& pw : m_perWindow)
571 for(auto& c : pw.connections)
572 QObject::disconnect(c);
575 m_node->onWindowsCreated = [] { };
576 m_node->onFps = [](float) { };
578 m_root.clear_children();
582 const gfx_node_base& get_root_node()
const override {
return m_root; }
583 gfx_node_base& get_root_node()
override {
return m_root; }