• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

ossia / score / 32923660642

26 Aug 2026 02:40AM UTC coverage: 23.574% (+0.4%) from 23.21%
32923660642

push

github

jcelerier
gfx: add missing guards on message processing for texture / buffer / geometry

0 of 6 new or added lines in 1 file covered. (0.0%)

4413 existing lines in 83 files now uncovered.

53181 of 225592 relevant lines covered (23.57%)

61000.63 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

68.0
/src/plugins/score-lib-device/Device/Protocol/DeviceInterface.cpp
1
// This is an open source non-commercial project. Dear PVS-Studio, please check
2
// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
3
#include "DeviceInterface.hpp"
4

5
#include <Device/Address/AddressSettings.hpp>
6
#include <Device/Node/DeviceNode.hpp>
7

8
#include <score/application/ApplicationContext.hpp>
9
#include <score/tools/std/String.hpp>
10

11
#include <core/application/ApplicationSettings.hpp>
12

13
#include <ossia/detail/logger.hpp>
14
#include <ossia/network/base/device.hpp>
15
#include <ossia/network/base/parameter.hpp>
16
#include <ossia/network/base/protocol.hpp>
17
#include <ossia/network/common/network_logger.hpp>
18
#include <ossia/network/context.hpp>
19
#include <ossia/network/domain/domain.hpp>
20

21
#include <ossia-qt/invoke.hpp>
22
#include <ossia-qt/name_utils.hpp>
23

24
#include <boost/asio/io_context.hpp>
25
#include <boost/asio/post.hpp>
26
#include <boost/asio/strand.hpp>
27
#include <boost/asio/use_future.hpp>
28

29
#include <QCoreApplication>
30
#include <QMenu>
31

32
#include <spdlog/sinks/sink.h>
33

34
#include <wobjectimpl.h>
35
W_OBJECT_IMPL(Device::DeviceInterface)
2,669✔
36
namespace Device
37
{
38
struct DeviceSettings;
39

40
static void ToAddress_rec(State::Address& addr, const ossia::net::node_base* cur, int N)
141,188✔
41
{
42
  if(auto padre = cur->get_parent())
141,188✔
43
  {
44
    ToAddress_rec(addr, padre, N + 1);
109,425✔
45
    addr.path.push_back(QString::fromStdString(cur->get_name()));
109,425✔
46
  }
109,425✔
47
  else
48
  {
49
    // The last node is the root node "/", which by convention
50
    // has the same name than the device
51
    addr.path.reserve(N);
31,763✔
52
    addr.device = QString::fromStdString(cur->get_name());
31,763✔
53
  }
54
}
141,188✔
55

56
static State::Address ToAddress(const ossia::net::node_base& node)
31,763✔
57
{
58
  State::Address addr;
31,763✔
59
  const ossia::net::node_base* cur = &node;
31,763✔
60

61
  ToAddress_rec(addr, cur, 1);
31,763✔
62
  return addr;
31,763✔
63
}
31,763✔
64

65
ossia::net::node_base*
66
findNodeFromPath(const QStringList& path, ossia::net::device_base& dev)
4,989✔
67
{
68
  using namespace ossia;
69
  // Find the relevant node to add in the device
70
  ossia::net::node_base* node = &dev.get_root_node();
4,989✔
71
  for(int i = 0; i < path.size(); i++)
19,346✔
72
  {
73
    auto cld = node->find_child(path[i]);
14,719✔
74
    if(cld)
14,719✔
75
      node = cld;
14,357✔
76
    else
77
    {
78
      qDebug() << "looking for" << path << " -- last found: " << node->get_name().c_str()
724✔
79
               << "\n";
362✔
80
      return {};
362✔
81
    }
82
  }
14,357✔
83

84
  return node;
4,627✔
85
}
4,989✔
86

87
using small_node_vec = ossia::small_vector<const Device::Node*, 16>;
88
static void getPath(small_node_vec& v, const Device::Node* cur)
9✔
89
{
90
  auto p = cur->parent();
9✔
91
  if(p && !p->is<Device::DeviceSettings>())
9✔
92
  {
93
    getPath(v, p);
×
94
  }
×
95
  v.push_back(cur);
9✔
96
}
9✔
97

98
ossia::net::node_base*
99
findNodeFromPath(const Device::Node& path, ossia::net::device_base& dev)
9✔
100
{
101
  using namespace ossia;
102
  // Find the relevant node to add in the device
103
  ossia::net::node_base* node = &dev.get_root_node();
9✔
104
  if(!path.is<Device::DeviceSettings>())
9✔
105
  {
106
    // First fill the vector of nodes
107
    ossia::small_vector<const Device::Node*, 16> vec;
9✔
108
    getPath(vec, &path);
9✔
109

110
    for(std::size_t i = 0; i < vec.size(); i++)
18✔
111
    {
112
      auto cld = node->find_child(vec[i]->displayName());
9✔
113
      if(cld)
9✔
114
        node = cld;
9✔
115
      else
116
      {
117
        qDebug() << "looking for" << Device::address(path).address << " " << i << " "
×
118
                 << vec.size() << " " << vec[i]->displayName()
×
119
                 << " -- last found: " << node->get_name().c_str() << "\n";
×
120
        return {};
×
121
      }
122
    }
9✔
123
    return node;
9✔
124
  }
9✔
125
  else
126
  {
127
    return &dev.get_root_node();
×
128
  }
129
}
9✔
130

131
static Device::AddressSettings ToAddressSettings(const ossia::net::node_base& node)
12,476✔
132
{
133
  Device::AddressSettings s;
12,476✔
134
  const auto& addr = node.get_parameter();
12,476✔
135

136
  if(addr)
12,476✔
137
  {
138
    //addr->request_value();
139

140
    s.name = QString::fromStdString(node.get_name());
12,402✔
141

142
    if(hasEditableValue(addr->get_type()))
12,402✔
143
    {
144
      s.ioType = ossia::access_mode::BI; // addr->get_access();
12,401✔
145
      s.clipMode = addr->get_bounding();
12,401✔
146
      s.repetitionFilter = addr->get_repetition_filter();
12,401✔
147
      s.unit = addr->get_unit();
12,401✔
148
      s.extendedAttributes = node.get_extended_attributes();
12,401✔
149
      s.domain = addr->get_domain();
12,401✔
150

151
      try
152
      {
153
        s.value = addr->value();
12,401✔
154
      }
12,401✔
155
      catch(...)
156
      {
157
        s.value = ossia::init_value(addr->get_value_type());
×
158
      }
×
159

160
      if(addr->get_type() == ossia::parameter_type::AUDIO && !s.domain.get())
12,401✔
161
        s.domain = ossia::make_domain(0.f, 1.f);
2✔
162
    }
12,401✔
163
  }
12,402✔
164
  else
165
  {
166
    s.name = QString::fromStdString(node.get_name());
74✔
167
  }
168
  return s;
12,476✔
169
}
12,476✔
170

171
static void updateOSSIAAddress(
65✔
172
    const Device::FullAddressSettings& settings, ossia::net::parameter_base& addr)
173
{
174
  SCORE_ASSERT(settings.ioType);
65✔
175
  addr.set_access(*settings.ioType);
65✔
176

177
  addr.set_bounding(settings.clipMode);
65✔
178

179
  addr.set_repetition_filter(ossia::repetition_filter(settings.repetitionFilter));
65✔
180

181
  addr.set_value_type(settings.value.get_type());
65✔
182

183
  addr.set_value(settings.value);
65✔
184

185
  addr.set_domain(settings.domain);
65✔
186

187
  addr.set_unit(settings.unit);
65✔
188

189
  addr.get_node().set_extended_attributes(settings.extendedAttributes);
65✔
190
}
65✔
191

192
static void createOSSIAAddress(
65✔
193
    const Device::FullAddressSettings& settings, ossia::net::node_base& node)
194
{
195
  if(!settings.value.v)
65✔
196
    return;
×
197

198
  auto addr = node.create_parameter(settings.value.get_type());
65✔
199
  if(addr)
65✔
200
    updateOSSIAAddress(settings, *addr);
65✔
201
}
65✔
202

203
Device::Node ToDeviceExplorer(const ossia::net::node_base& ossia_node)
129✔
204
{
205
  Device::Node score_node{ToAddressSettings(ossia_node), nullptr};
129✔
206
  {
207
    const auto& cld = ossia_node.children();
129✔
208
    score_node.reserve(cld.size());
129✔
209

210
    // 2. Recurse on the children
211
    for(const auto& ossia_child : cld)
135✔
212
    {
213
      if(!ossia::net::get_hidden(*ossia_child) && !ossia::net::get_zombie(*ossia_child))
6✔
214
      {
215
        auto child_n = ToDeviceExplorer(*ossia_child);
6✔
216
        child_n.setParent(&score_node);
6✔
217
        score_node.push_back(std::move(child_n));
6✔
218
      }
6✔
219
    }
220
  }
129✔
221
  return score_node;
129✔
222
}
129✔
223

224
ossia::net::node_base*
225
createNodeFromPath(const QStringList& path, ossia::net::device_base& dev)
65✔
226
{
227
  using namespace ossia;
228
  // Find the relevant node to add in the device
229
  ossia::net::node_base* node = &dev.get_root_node();
65✔
230
  for(int i = 0; i < path.size(); i++)
94✔
231
  {
232
    auto cld = node->find_child(path[i]);
65✔
233
    if(!cld)
65✔
234
    {
235
      // We have to start adding sub-nodes from here.
236
      ossia::net::node_base* parentnode = node;
36✔
237
      for(int k = i; k < path.size(); k++)
72✔
238
      {
239
        auto path_k = path[k].toStdString();
36✔
240
        auto newNode = parentnode->create_child(path_k);
36✔
241
        SCORE_ASSERT(newNode);
36✔
242
        if(path_k != newNode->get_name())
36✔
243
        {
244
          qDebug() << "Warning! " << path[k]
×
245
                   << " was requested as node name but backend converted to  "
×
246
                   << newNode->get_name().c_str()
×
247
                   << "was obtained. Check for special characters.";
×
248
          parentnode->remove_child(*newNode);
×
249
          return nullptr;
×
250
          /*
251
          for (const auto& node : parentnode->children())
252
          {
253
            qDebug() << node->get_name().c_str();
254
          }*/
255
        }
256
        if(k == path.size() - 1)
36✔
257
        {
258
          node = newNode;
36✔
259
        }
36✔
260
        else
261
        {
262
          parentnode = newNode;
×
263
        }
264
      }
36✔
265

266
      break;
36✔
267
    }
268
    else
269
    {
270
      node = cld;
29✔
271
    }
272
  }
29✔
273

274
  return node;
65✔
275
}
65✔
276

277
bool DeviceInterface::connected() const
652✔
278
{
279
  return bool(getDevice());
652✔
280
}
281

282
void DeviceInterface::addAddress(const FullAddressSettings& settings)
123✔
283
{
284
  using namespace ossia;
285
  if(!m_capas.canAddNode)
123✔
286
  {
287
    return; // TODO return bool instead, and check in the node update proxy ?
×
288
  }
289

290
  if(auto dev = getDevice())
123✔
291
  {
292
    // Create the node. It is added into the device.
293
    ossia::net::node_base* node = createNodeFromPath(settings.address.path, *dev);
65✔
294
    if(node)
65✔
295
    {
296
      // Populate the node with a parameter (if it isn't a no_value_t).
297
      createOSSIAAddress(settings, *node);
65✔
298
    }
65✔
299
  }
65✔
300
}
123✔
301

302
bool DeviceInterface::isLearning() const
×
303
{
304
  return false;
×
305
}
306

307
void DeviceInterface::setLearning(bool) { }
×
308

309
QMimeData* DeviceInterface::mimeData() const
×
310
{
311
  return nullptr;
×
312
}
313

314
void DeviceInterface::setupContextMenu(QMenu&) const { }
×
315

316
DeviceInterface::DeviceInterface(Device::DeviceSettings s)
1,488✔
317
    : m_settings(std::move(s))
372✔
318
{
744✔
319
}
×
320

321
DeviceInterface::~DeviceInterface() { }
372✔
322

323
const Device::DeviceSettings& DeviceInterface::settings() const noexcept
838✔
324
{
325
  return m_settings;
838✔
326
}
327

328
const QString& DeviceInterface::name() const noexcept
386✔
329
{
330
  return settings().name;
386✔
331
}
332

333
void DeviceInterface::addNode(const Device::Node& n)
123✔
334
{
335
  auto full = Device::FullAddressSettings::make<Device::FullAddressSettings::as_parent>(
123✔
336
      n.get<Device::AddressSettings>(), Device::address(*n.parent()));
123✔
337

338
  // Add in the device implementation
339
  addAddress(full);
123✔
340

341
  for(const auto& child : n)
123✔
342
  {
343
    addNode(child);
×
344
  }
345
}
123✔
346

347
DeviceCapas DeviceInterface::capabilities() const noexcept
493✔
348
{
349
  return m_capas;
493✔
350
}
351

352
DeviceResources DeviceInterface::usedResources() const noexcept
×
353
{
354
  return {};
×
355
}
356

357
void DeviceInterface::disconnect()
210✔
358
{
359
  // Whatever the device advertises, the callbacks enableCallbacks() installed
360
  // must go before the nodes do: clear_children() below would otherwise report
361
  // every node as removed and the explorer would drop the device's tree. This
362
  // also resets the flag, so that the device rebuilt by reconnect() gets its
363
  // callbacks back.
364
  disableCallbacks();
210✔
365

366
  m_callbacks.clear();
210✔
367
  if(auto dev = getDevice())
210✔
368
  {
369
    auto& root = dev->get_root_node();
46✔
370
    root.clear_children();
46✔
371
  }
46✔
372
}
210✔
373

UNCOV
374
void DeviceInterface::recreate(const Device::Node&) { }
×
375

376
void DeviceInterface::updateSettings(const Device::DeviceSettings& newsettings)
26✔
377
{
378
  // TODO we have to maintain the prior connection state
379
  // if we were disconnected, we stay disconnected
380
  // else we reconnect. See in Minuit / MIDI also.
381
  if(auto dev = getDevice())
26✔
382
  {
383
    // First we save the existing nodes.
384
    Device::Node score_device{settings(), nullptr};
26✔
385

386
    // Recurse on the children
387
    {
388
      const auto& ossia_children = dev->get_root_node().children();
26✔
389
      score_device.reserve(ossia_children.size());
26✔
390
      for(const auto& node : ossia_children)
85✔
391
      {
392
        score_device.push_back(ToDeviceExplorer(*node.get()));
59✔
393
      }
394
    }
26✔
395

396
    // We change the settings safely
397
    disconnect();
26✔
398

399
    m_settings = newsettings;
26✔
400

401
    auto con_handle = std::make_shared<QMetaObject::Connection>();
26✔
402
    *con_handle = connect(
26✔
403
        this, &Device::DeviceInterface::deviceChanged, this,
404
        [this, con_handle, data = std::move(score_device)](auto oldd, auto newd) {
77✔
405
      if(newd)
51✔
406
      {
407
        recreate(std::move(data));
25✔
408
        // The new ossia nodes carry no callbacks: listen again to what the
409
        // explorer shows.
410
        restoreListening();
25✔
411
        QObject::disconnect(*con_handle);
25✔
412
      }
25✔
413
        });
51✔
414

415
    reconnect();
26✔
416
  }
26✔
417
  else
418
  {
419
    // We're already disconnected
UNCOV
420
    m_settings = newsettings;
×
421
  }
422
}
26✔
423

424
void DeviceInterface::updateAddress(
10✔
425
    const State::Address& currentAddr, const Device::FullAddressSettings& settings)
426
{
427
  if(auto dev = getDevice())
10✔
428
  {
429
    if(auto node = findNodeFromPath(currentAddr.path, *dev))
×
430
    {
431
      bool is_listening = m_callbacks.find(currentAddr) != m_callbacks.end();
×
432

UNCOV
433
      if(!settings.value.valid())
×
434
      {
435
        if(is_listening)
×
436
        {
437
          // Remove callbacks
438
          auto it = m_callbacks.find(currentAddr);
×
439
          if(it != m_callbacks.end())
×
440
          {
441
            it->second.first->remove_callback(it->second.second);
×
442
            m_callbacks.erase(it);
×
UNCOV
443
          }
×
444

445
          is_listening = false;
×
446
        }
×
447

448
        // Remove param
449
        node->remove_parameter();
×
450
      }
×
451
      else
452
      {
UNCOV
453
        if(auto p = node->get_parameter())
×
UNCOV
454
          updateOSSIAAddress(settings, *p);
×
455
        else
456
          createOSSIAAddress(settings, *node);
×
457
      }
458

459
      auto newName = settings.address.path.last();
×
460
      if(!latin_compare(newName, node->get_name()))
×
461
      {
UNCOV
462
        renameListening_impl(currentAddr, newName);
×
UNCOV
463
        node->set_name(newName.toStdString());
×
464
      }
×
UNCOV
465
    }
×
466
    else
467
    {
UNCOV
468
      qDebug() << "updateAddress: Warning ! did not find" << currentAddr;
×
469
    }
UNCOV
470
  }
×
471
}
10✔
472

473
void DeviceInterface::removeListening_impl(
10✔
474
    ossia::net::node_base& node, State::Address addr)
475
{
476
  // Find & remove our callback
477
  auto it = m_callbacks.find(addr);
10✔
478
  if(it != m_callbacks.end())
10✔
479
  {
480
    it->second.first->remove_callback(it->second.second);
2✔
481
    m_callbacks.erase(it);
2✔
482
  }
2✔
483

484
  // Recurse
485
  for(const auto& child : node.children())
16✔
486
  {
487
    State::Address sub_addr = addr;
6✔
488
    sub_addr.path += QString::fromStdString(child->get_name());
6✔
489
    removeListening_impl(*child.get(), std::move(sub_addr));
6✔
490
  }
6✔
491
}
10✔
492

493
void DeviceInterface::removeListening_impl(
×
494
    ossia::net::node_base& node, State::Address addr, std::vector<State::Address>& vec)
495
{
496
  // Find & remove our callback
497
  auto it = m_callbacks.find(addr);
×
498
  if(it != m_callbacks.end())
×
499
  {
UNCOV
500
    it->second.first->remove_callback(it->second.second);
×
UNCOV
501
    m_callbacks.erase(it);
×
502
    vec.push_back(addr);
×
UNCOV
503
  }
×
504

505
  // Recurse
506
  for(const auto& child : node.children())
×
507
  {
508
    State::Address sub_addr = addr;
×
UNCOV
509
    sub_addr.path += QString::fromStdString(child->get_name());
×
UNCOV
510
    removeListening_impl(*child.get(), std::move(sub_addr));
×
UNCOV
511
  }
×
UNCOV
512
}
×
513
bool is_parent(const State::Address& parent, const State::Address& child)
2,004✔
514
{
515
  const auto p_size = parent.path.size();
2,004✔
516
  if(child.path.size() < p_size)
2,004✔
517
    return false;
638✔
518
  for(int i = 0; i < p_size; i++)
2,859✔
519
  {
520
    if(child.path[i] != parent.path[i])
2,232✔
521
      return false;
739✔
522
  }
1,493✔
523
  return true;
627✔
524
}
2,004✔
525

526
void DeviceInterface::renameListening_impl(
68✔
527
    const State::Address& parent, const QString& newName)
528
{
529
  // Store the elements that are renamed
530
  std::vector<std::pair<State::Address, callback_pair>> saved_elts;
68✔
531
  for(auto it = m_callbacks.begin(); it != m_callbacks.end();)
2,072✔
532
  {
533
    if(is_parent(parent, it->first))
2,004✔
534
    {
535
      State::Address addr = it->first;
627✔
536
      addr.path[parent.path.size() - 1] = newName;
627✔
537
      saved_elts.push_back({std::move(addr), it->second});
627✔
538
      it = m_callbacks.erase(it);
627✔
539
    }
627✔
540
    else
541
    {
542
      ++it;
1,377✔
543
    }
544
  }
545

546
  // Put things back after renaming
547
  for(auto&& p : std::move(saved_elts))
695✔
548
  {
549
    p.second.first->replace_callback(
627✔
550
        p.second.second,
627✔
551
        [this, addr = p.first](const ossia::value& val) { valueUpdated(addr, val); });
627✔
552
    m_callbacks.insert(std::move(p));
627✔
553
  }
554
}
68✔
555

556
namespace
557
{
558
struct in_sink final : public spdlog::sinks::sink
559
{
560
  const DeviceInterface& m_dev;
561
  in_sink(const DeviceInterface& dev)
×
UNCOV
562
      : m_dev{dev}
×
563
  {
×
564
  }
×
UNCOV
565
  void log(const spdlog::details::log_msg& msg) override
×
566
  {
567
    m_dev.logInbound(QString::fromLatin1(msg.payload.data(), msg.payload.size()));
×
568
  }
×
569

UNCOV
570
  void flush() override { }
×
UNCOV
571
  void set_pattern(const std::string& pattern) override { }
×
UNCOV
572
  void set_formatter(std::unique_ptr<spdlog::formatter> sink_formatter) override { }
×
573
};
574
struct out_sink final : public spdlog::sinks::sink
575
{
576
  const DeviceInterface& m_dev;
577
  out_sink(const DeviceInterface& dev)
×
UNCOV
578
      : m_dev{dev}
×
579
  {
×
580
  }
×
UNCOV
581
  void log(const spdlog::details::log_msg& msg) override
×
582
  {
583
    m_dev.logOutbound(QString::fromLatin1(msg.payload.data(), msg.payload.size()));
×
584
  }
×
585

UNCOV
586
  void flush() override { }
×
UNCOV
587
  void set_pattern(const std::string& pattern) override { }
×
UNCOV
588
  void set_formatter(std::unique_ptr<spdlog::formatter> sink_formatter) override { }
×
589
};
590
}
591
void DeviceInterface::setLogging_impl(DeviceLogging b) const
825✔
592
{
593
  if(auto dev = getDevice())
825✔
594
  {
595
    switch(b)
820✔
596
    {
597
      case DeviceLogging::LogNothing: {
598
        dev->get_protocol().set_logger({});
820✔
599
        return;
820✔
600
      }
601
      case DeviceLogging::LogEverything: {
602
        ossia::net::network_logger logger;
×
UNCOV
603
        logger.inbound_logger = std::make_shared<ossia::logger_type>(
×
604
            "in_logger", std::make_shared<in_sink>(*this));
×
605
        logger.outbound_logger = std::make_shared<ossia::logger_type>(
×
606
            "out_logger", std::make_shared<out_sink>(*this));
×
607

608
        logger.inbound_logger->set_pattern("%v");
×
UNCOV
609
        logger.inbound_logger->set_level(spdlog::level::info);
×
610
        logger.outbound_logger->set_pattern("%v");
×
UNCOV
611
        logger.outbound_logger->set_level(spdlog::level::info);
×
612
        dev->get_protocol().set_logger(std::move(logger));
×
613
        break;
614
      }
×
615
      case DeviceLogging::LogUnfolded: {
616
        ossia::net::network_logger logger;
×
UNCOV
617
        logger.inbound_listened_logger = std::make_shared<ossia::logger_type>(
×
618
            "in_logger", std::make_shared<in_sink>(*this));
×
619
        logger.outbound_listened_logger = std::make_shared<ossia::logger_type>(
×
620
            "out_logger", std::make_shared<out_sink>(*this));
×
621

622
        logger.inbound_listened_logger->set_pattern("%v");
×
UNCOV
623
        logger.inbound_listened_logger->set_level(spdlog::level::info);
×
624
        logger.outbound_listened_logger->set_pattern("%v");
×
UNCOV
625
        logger.outbound_listened_logger->set_level(spdlog::level::info);
×
626
        dev->get_protocol().set_logger(std::move(logger));
×
627
        break;
UNCOV
628
      }
×
629
    }
UNCOV
630
  }
×
631
}
825✔
632

633
void DeviceInterface::enableCallbacks()
168✔
634
{
635
  if(!m_callbacksEnabled)
168✔
636
  {
637
    auto dev = getDevice();
168✔
638
    if(dev)
168✔
639
    {
640
      dev->on_node_created.connect<&DeviceInterface::nodeCreated>(this);
168✔
641
      dev->on_node_removing.connect<&DeviceInterface::nodeRemoving>(this);
168✔
642
      dev->on_node_renamed.connect<&DeviceInterface::nodeRenamed>(this);
168✔
643
      dev->on_parameter_created.connect<&DeviceInterface::addressCreated>(this);
168✔
644
      dev->on_parameter_removing.connect<&DeviceInterface::addressRemoved>(this);
168✔
645
      dev->on_attribute_modified.connect<&DeviceInterface::addressUpdated>(this);
168✔
646
    }
168✔
647
    m_callbacksEnabled = true;
168✔
648
  }
168✔
649
}
168✔
650

651
void DeviceInterface::disableCallbacks()
212✔
652
{
653
  if(m_callbacksEnabled)
212✔
654
  {
655
    auto dev = getDevice();
6✔
656
    if(dev)
6✔
657
    {
658
      dev->on_node_created.disconnect<&DeviceInterface::nodeCreated>(this);
6✔
659
      dev->on_node_removing.disconnect<&DeviceInterface::nodeRemoving>(this);
6✔
660
      dev->on_node_renamed.disconnect<&DeviceInterface::nodeRenamed>(this);
6✔
661
      dev->on_parameter_created.disconnect<&DeviceInterface::addressCreated>(this);
6✔
662
      dev->on_parameter_removing.disconnect<&DeviceInterface::addressRemoved>(this);
6✔
663
      dev->on_attribute_modified.disconnect<&DeviceInterface::addressUpdated>(this);
6✔
664
    }
6✔
665
    m_callbacksEnabled = false;
6✔
666
  }
6✔
667
}
212✔
668

669
Device::Node DeviceInterface::simple_refresh()
30✔
670
{
671
  Device::Node score_device{settings(), nullptr};
30✔
672

673
  // Recurse on the children
674
  const auto& ossia_children = getDevice()->get_root_node().children();
30✔
675
  score_device.reserve(ossia_children.size());
30✔
676
  for(const auto& node : ossia_children)
90✔
677
  {
678
    score_device.push_back(ToDeviceExplorer(*node.get()));
60✔
679
  }
680

681
  score_device.get<Device::DeviceSettings>().name
30✔
682
      = QString::fromStdString(getDevice()->get_name());
60✔
683

684
  return score_device;
30✔
685
}
30✔
686

687
void DeviceInterface::removeNode(const State::Address& address)
×
688
{
689
  using namespace ossia;
690
  if(!m_capas.canRemoveNode)
×
UNCOV
691
    return;
×
692
  if(auto dev = getDevice())
×
693
  {
UNCOV
694
    if(auto node = findNodeFromPath(address.path, *dev))
×
695
    {
UNCOV
696
      auto parent = node->get_parent();
×
697
      if(parent->has_child(*node))
×
698
      {
699
        /* If we are listening to this node, we recursively
700
         * remove listening to all the children. */
UNCOV
701
        removeListening_impl(*node, address);
×
702

703
        // TODO !! if we remove nodes while recording
704
        // (or anything involving a registered listening state), there will be
705
        // crashes.
706
        // The Device Explorer should be locked for edition during recording /
707
        // playing.
UNCOV
708
        parent->remove_child(node->get_name());
×
709
      }
×
UNCOV
710
    }
×
711
    else
712
    {
UNCOV
713
      qDebug() << "Warning ! removeNode: did not find" << address;
×
714
    }
UNCOV
715
  }
×
UNCOV
716
}
×
717

718
Device::Node DeviceInterface::refresh()
2✔
719
{
720
  Device::Node device_node{settings(), nullptr};
2✔
721

722
  if(auto dev = getDevice())
2✔
723
  {
724
    auto& root = dev->get_root_node();
2✔
725
    // Clear the listening
726
    removeListening_impl(root, State::Address{m_settings.name, {}});
2✔
727

728
    disableCallbacks();
2✔
729
    m_callbacks.clear();
2✔
730

731
    auto fut = dev->get_protocol().update_async(root);
2✔
732
    auto res = fut.wait_for(std::chrono::seconds(1));
2✔
733
    if(res == std::future_status::ready)
2✔
734
    {
735
      // Make a device explorer node from the current state of the device.
736
      // First make the node corresponding to the root node.
737

738
      // Recurse on the children
739
      const auto& children = root.children();
2✔
740
      device_node.reserve(children.size());
2✔
741
      for(const auto& node : children)
2✔
742
      {
UNCOV
743
        device_node.push_back(ToDeviceExplorer(*node.get()));
×
744
      }
745
    }
2✔
746
    enableCallbacks();
2✔
747

748
    device_node.get<Device::DeviceSettings>().name = settings().name;
2✔
749
  }
2✔
750

751
  return device_node;
2✔
752
}
2✔
753

754
std::optional<ossia::value> DeviceInterface::refresh(const State::Address& address)
×
755
{
UNCOV
756
  if(auto dev = getDevice())
×
757
  {
UNCOV
758
    auto node = findNodeFromPath(address.path, *dev);
×
759
    if(node)
×
760
    {
761
      if(auto addr = node->get_parameter())
×
762
      {
UNCOV
763
        return addr->fetch_value();
×
764
      }
765
    }
×
UNCOV
766
  }
×
767

UNCOV
768
  return {};
×
UNCOV
769
}
×
770

771
void DeviceInterface::request(const Device::Node& address)
9✔
772
{
773
  if(auto dev = getDevice())
9✔
774
  {
775
    auto node = findNodeFromPath(address, *dev);
9✔
776
    if(node)
9✔
777
    {
778
      if(auto addr = node->get_parameter())
9✔
779
      {
780
        addr->request_value();
9✔
781
      }
9✔
782
    }
9✔
783
  }
9✔
784
}
9✔
785

786
Device::Node DeviceInterface::getNode(const State::Address& address) const
×
787
{
788
  if(auto dev = getDevice())
×
789
  {
UNCOV
790
    auto ossia_node = findNodeFromPath(address.path, *dev);
×
791
    if(ossia_node)
×
792
      return ToDeviceExplorer(*ossia_node);
×
UNCOV
793
  }
×
794

UNCOV
795
  return {};
×
UNCOV
796
}
×
797

798
Device::Node DeviceInterface::getNodeWithoutChildren(const State::Address& address) const
2✔
799
{
800
  if(auto dev = getDevice())
2✔
801
  {
802
    auto ossia_node = findNodeFromPath(address.path, *dev);
2✔
803
    if(ossia_node)
2✔
804
    {
805
      return Device::Node{ToAddressSettings(*ossia_node), nullptr};
2✔
806
    }
UNCOV
807
  }
×
808

UNCOV
809
  return {};
×
810
}
2✔
811

812
void DeviceInterface::setListening(const State::Address& addr, bool b)
5,253✔
813
{
814
  static const bool gui = score::AppContext().applicationSettings.gui;
5,253✔
815
  if(!gui)
5,253✔
816
    return;
203✔
817

818
  // Remember the request even when the device is not connected, or the
819
  // address does not exist yet: restoreListening() applies it once it does.
820
  if(b)
5,050✔
821
    m_listeningRequests.insert(addr);
4,982✔
822
  else
823
    m_listeningRequests.erase(addr);
68✔
824

825
  listen_impl(addr, b);
5,050✔
826
}
5,253✔
827

828
std::vector<State::Address> DeviceInterface::listeningRequests() const
4✔
829
{
830
  return std::vector<State::Address>(
4✔
831
      m_listeningRequests.begin(), m_listeningRequests.end());
4✔
UNCOV
832
}
×
833

834
void DeviceInterface::restoreListening()
372✔
835
{
836
  static const bool gui = score::AppContext().applicationSettings.gui;
372✔
837
  if(!gui)
372✔
838
    return;
16✔
839
  if(!getDevice())
356✔
UNCOV
840
    return;
×
841

842
  for(const auto& addr : m_listeningRequests)
512✔
843
    listen_impl(addr, true);
156✔
844
}
372✔
845

846
bool DeviceInterface::listen_impl(const State::Address& addr, bool b)
5,206✔
847
{
848
  if(auto dev = getDevice())
5,206✔
849
  {
850
    // First check if the address is already listening
851
    // so that we don't have to go through the tree.
852
    auto cb_it = m_callbacks.find(addr);
5,148✔
853

854
    // Nothing to stop: do not even look the node up (it may well be gone)
855
    if(!b && cb_it == m_callbacks.end())
5,148✔
856
      return false;
4✔
857

858
    ossia::net::parameter_base* ossia_addr{};
5,144✔
859
    if(cb_it == m_callbacks.end())
5,144✔
860
    {
861
      auto n = findNodeFromPath(addr.path, *dev);
4,987✔
862
      if(!n)
4,987✔
863
      {
864
        return false;
362✔
865
      }
866

867
      ossia_addr = n->get_parameter();
4,625✔
868
      if(!ossia_addr)
4,625✔
UNCOV
869
        return false;
×
870
    }
4,625✔
871
    else
872
    {
873
      ossia_addr = cb_it->second.first;
157✔
874
      if(!ossia_addr)
157✔
875
      {
UNCOV
876
        m_callbacks.erase(cb_it);
×
UNCOV
877
        return false;
×
878
      }
879
    }
880

881
    SCORE_ASSERT(bool(ossia_addr));
4,782✔
882

883
    // If we want to enable listening
884
    // and the address wasn't already listening
885
    if(b)
4,782✔
886
    {
887
      if(cb_it == m_callbacks.end())
4,775✔
888
      {
889
        m_callbacks.insert(
9,250✔
890
            {addr,
9,250✔
891
             {ossia_addr,
4,625✔
892
              ossia_addr->add_callback(
4,625✔
893
                  [this, addr](const ossia::value& val) { valueUpdated(addr, val); })}});
4,627✔
894
      }
4,625✔
895

896
      // FIXME crash here when refresh audio device
897
      valueUpdated(addr, ossia_addr->value());
4,775✔
898
    }
4,775✔
899
    else
900
    {
901
      // If we can disable listening
902
      if(cb_it != m_callbacks.end())
7✔
903
      {
904
        ossia_addr->remove_callback(cb_it->second.second);
7✔
905
        m_callbacks.erase(cb_it);
7✔
906
      }
7✔
907
    }
908
    return true;
4,782✔
909
  }
910
  return false;
58✔
911
}
5,206✔
912

913
std::vector<State::Address> DeviceInterface::listening() const
18✔
914
{
915
  if(!connected())
18✔
916
    return {};
1✔
917

918
  static const bool gui = score::AppContext().applicationSettings.gui;
17✔
919
  if(!gui)
17✔
UNCOV
920
    return {};
×
921

922
  std::vector<State::Address> addrs;
17✔
923
  addrs.reserve(m_callbacks.size());
17✔
924

925
  for(const auto& elt : m_callbacks)
31✔
926
  {
927
    addrs.push_back(elt.first);
14✔
928
  }
929

930
  return addrs;
17✔
931
}
35✔
932

UNCOV
933
void DeviceInterface::addToListening(const std::vector<State::Address>& addresses)
×
934
{
935
  if(!connected())
×
936
    return;
×
937

938
  static const bool gui = score::AppContext().applicationSettings.gui;
×
UNCOV
939
  if(!gui)
×
940
    return;
×
941

UNCOV
942
  for(const auto& addr : addresses)
×
943
  {
UNCOV
944
    SCORE_ASSERT(addr.device == this->settings().name);
×
UNCOV
945
    setListening(addr, true);
×
946
  }
UNCOV
947
}
×
948

949
void DeviceInterface::sendMessage(const State::Address& addr, const ossia::value& v)
9✔
950
{
951
  if(auto dev = getDevice())
9✔
952
  {
UNCOV
953
    if(auto node = findNodeFromPath(addr.path, *dev))
×
954
    {
955
      auto addr = node->get_parameter();
×
956
      if(addr)
×
957
      {
UNCOV
958
        addr->push_value(v);
×
959
      }
×
UNCOV
960
    }
×
961
    else
962
    {
UNCOV
963
      qDebug() << "Warning! sendMessage: did not find" << addr;
×
964
    }
UNCOV
965
  }
×
966
}
9✔
967

968
bool DeviceInterface::isLogging() const
665✔
969
{
970
  return m_logging;
665✔
971
}
972

973
void DeviceInterface::setLogging(DeviceLogging b)
41✔
974
{
975
  if(!connected())
41✔
976
  {
977
    m_logging = b;
21✔
978
    return;
21✔
979
  }
980

981
  if(b == m_logging)
20✔
982
    return;
20✔
983

UNCOV
984
  m_logging = b;
×
UNCOV
985
  setLogging_impl(m_logging);
×
986
}
41✔
987

988
OwningDeviceInterface::~OwningDeviceInterface() { }
41✔
989

990
void OwningDeviceInterface::replaceDevice(ossia::net::device_base* d)
×
991
{
992
  disconnect();
×
UNCOV
993
  m_dev.reset(d);
×
994
  deviceChanged(nullptr, d);
×
UNCOV
995
  m_owned = false;
×
996
}
×
997

UNCOV
998
void OwningDeviceInterface::releaseDevice()
×
999
{
UNCOV
1000
  DeviceInterface::disconnect();
×
UNCOV
1001
  deviceChanged(m_dev.get(), nullptr);
×
1002
  // FIXME instead it should just be shared_ptrs in player m_dev.release();
UNCOV
1003
}
×
1004

1005
void OwningDeviceInterface::disconnect()
205✔
1006
{
1007
  if(m_owned)
205✔
1008
  {
1009
    DeviceInterface::disconnect();
205✔
1010
    // TODO why not auto dev = m_dev; ... like in MIDIDevice ?
1011
    deviceChanged(m_dev.get(), nullptr);
205✔
1012
    m_dev.reset();
205✔
1013
  }
205✔
1014
}
205✔
1015

1016
void DeviceInterface::nodeCreated(const ossia::net::node_base& n)
6,924✔
1017
{
1018
  pathAdded(ToAddress(n));
6,924✔
1019
}
6,924✔
1020

1021
void DeviceInterface::nodeRemoving(const ossia::net::node_base& n)
5,312✔
1022
{
1023
  pathRemoved(ToAddress(n));
5,312✔
1024
}
5,312✔
1025

1026
void DeviceInterface::nodeRenamed(
229✔
1027
    const ossia::net::node_base& node, std::string old_name)
1028
{
1029
  if(!node.get_parent())
229✔
1030
    return;
161✔
1031

1032
  State::Address currentAddress = ToAddress(*node.get_parent());
68✔
1033
  currentAddress.path.push_back(QString::fromStdString(old_name));
68✔
1034

1035
  Device::AddressSettings as = ToAddressSettings(node);
68✔
1036
  as.name = QString::fromStdString(node.get_name());
68✔
1037

1038
  renameListening_impl(currentAddress, as.name);
68✔
1039

1040
  pathUpdated(currentAddress, as);
68✔
1041
}
229✔
1042

1043
void DeviceInterface::addressCreated(const ossia::net::parameter_base& addr)
5,201✔
1044
{
1045
  State::Address currentAddress = ToAddress(addr.get_node());
5,201✔
1046
  Device::AddressSettings as = ToAddressSettings(addr.get_node());
5,201✔
1047
  pathUpdated(currentAddress, as);
5,201✔
1048
  ossia::qt::run_async(
5,201✔
1049
      QCoreApplication::instance(), [self = QPointer{this}, addr = currentAddress] {
10,402✔
1050
    if(self)
5,201✔
1051
      self->setListening(addr, true);
5,172✔
1052
  });
5,201✔
1053
}
5,201✔
1054

1055
void DeviceInterface::addressUpdated(
7,076✔
1056
    const ossia::net::node_base& node, ossia::string_view key)
1057
{
1058
  const bool hidden = (ossia::net::get_zombie(node) || ossia::net::get_hidden(node));
7,076✔
1059

1060
  State::Address currentAddress = ToAddress(node);
7,076✔
1061
  if(hidden)
7,076✔
1062
  {
UNCOV
1063
    pathRemoved(currentAddress);
×
UNCOV
1064
    return;
×
1065
  }
1066

1067
  Device::AddressSettings as = ToAddressSettings(node);
7,076✔
1068

1069
  pathUpdated(currentAddress, as);
7,076✔
1070
}
7,076✔
1071

1072
void DeviceInterface::addressRemoved(const ossia::net::parameter_base& addr)
3,591✔
1073
{
1074
  auto address = ToAddress(addr.get_node());
3,591✔
1075

1076
  auto cb_it = m_callbacks.find(address);
3,591✔
1077
  if(cb_it != m_callbacks.end())
3,591✔
1078
  {
1079
    m_callbacks.erase(cb_it);
3,077✔
1080
  }
3,077✔
1081
  auto& node = addr.get_node();
3,591✔
1082
  State::Address currentAddress = ToAddress(node);
3,591✔
1083
  Device::AddressSettings as;
3,591✔
1084
  as.name = QString::fromStdString(node.get_name());
3,591✔
1085
  pathUpdated(currentAddress, as);
3,591✔
1086
}
3,591✔
1087

1088
void releaseDevice(
×
1089
    ossia::net::network_context& ctx, std::unique_ptr<ossia::net::device_base> dd)
1090
{
UNCOV
1091
  releaseDevice(ctx, std::shared_ptr(std::move(dd)));
×
UNCOV
1092
}
×
1093

1094
void releaseDevice(
76✔
1095
    ossia::net::network_context& ctx, std::shared_ptr<ossia::net::device_base> shared_dd)
1096
{
1097
  if(shared_dd)
76✔
1098
  {
1099
    auto strand = boost::asio::make_strand(ctx.context);
×
1100
    boost::asio::dispatch(strand, [dev = shared_dd] { dev->get_protocol().stop(); });
×
1101

1102
    std::future<void> wait1 = boost::asio::dispatch(strand, boost::asio::use_future);
×
1103
    if(auto res = wait1.wait_for(std::chrono::seconds(1));
×
UNCOV
1104
       res != std::future_status::ready)
×
1105
    {
UNCOV
1106
      qDebug() << "Device deletion: asio thread seems stuck (1)";
×
1107
    }
×
1108

1109
    boost::asio::dispatch(strand, [d = std::move(shared_dd)]() mutable { d.reset(); });
×
1110

1111
    std::future<void> wait2 = boost::asio::dispatch(strand, boost::asio::use_future);
×
1112
    if(auto res = wait2.wait_for(std::chrono::seconds(1));
×
1113
       res != std::future_status::ready)
×
1114
    {
UNCOV
1115
      qDebug() << "Device deletion: asio thread seems stuck (2)";
×
UNCOV
1116
    }
×
UNCOV
1117
  }
×
1118
}
76✔
1119
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc