OSSIA
Open Scenario System for Interactive Application
Loading...
Searching...
No Matches
qml_tcp_inbound_socket.hpp
1#pragma once
2#include <ossia/network/context.hpp>
3#include <ossia/network/sockets/tcp_socket.hpp>
4
5#include <ossia-qt/protocols/utils.hpp>
6
7#include <QJSValue>
8#include <QObject>
9#include <QQmlEngine>
10
11#include <nano_observer.hpp>
12
13#include <verdigris>
14
15namespace ossia::qt
16{
17class qml_tcp_connection
18 : public QObject
19 , public Nano::Observer
20{
21 W_OBJECT(qml_tcp_connection)
22public:
23 explicit qml_tcp_connection(
24 ossia::net::tcp_listener listener, boost::asio::io_context& ctx)
25 : m_context{ctx}
26 , m_listener{std::move(listener)}
27 {
28 }
29 inline boost::asio::io_context& context() noexcept { return m_context; }
30
31 void write(QByteArray buffer)
32 {
33 run_on_asio_thread(
34 { m_listener.write(boost::asio::const_buffer(buffer.data(), buffer.size())); });
35 }
36 W_SLOT(write)
37
38 void close(QByteArray buffer)
39 {
40 run_on_asio_thread({ m_listener.close(); });
41 }
42 W_SLOT(close)
43
44 void receive(QJSValue v)
45 {
46 onBytes = v;
47 do_receive();
48 }
49 W_SLOT(receive)
50 void do_receive()
51 {
52 auto& socket = m_listener.m_socket;
53 socket.async_read_some(
54 boost::asio::buffer(m_data, sizeof(m_data)),
55 [self
56 = QPointer{this}](boost::system::error_code ec, std::size_t bytes_transferred) {
57 if(!ec)
58 {
59 auto buf = QByteArray(self->m_data, bytes_transferred);
60 ossia::qt::run_async(self.get(), [self, buf] {
61 if(!self.get())
62 return;
63 if(self->onBytes.isCallable())
64 {
65 self->onBytes.call({qjsEngine(self.get())->toScriptValue(buf)});
66 }
67 }, Qt::AutoConnection);
68 self->do_receive();
69 }
70 else
71 {
72 ossia::qt::run_async(self.get(), [self] {
73 if(self && self->onClose.isCallable())
74 self->onClose.call();
75 });
76 }
77 });
78 }
79
80 QJSValue onBytes;
81 QJSValue onClose;
82 W_PROPERTY(QJSValue, onClose W_MEMBER onClose);
83
84private:
85 boost::asio::io_context& m_context;
86 ossia::net::tcp_listener m_listener;
87 char m_data[4096];
88};
89
90class qml_tcp_inbound_socket
91 : public QObject
92 , public Nano::Observer
93{
94 W_OBJECT(qml_tcp_inbound_socket)
95public:
96 qml_tcp_inbound_socket(
97 const ossia::net::inbound_socket_configuration& conf, boost::asio::io_context& ctx)
98 : server{conf, ctx}
99 {
100 }
101
102 inline boost::asio::io_context& context() noexcept { return server.m_context; }
103
104 void open()
105 {
106 m_open = true;
107 accept();
108 if(onOpen.isCallable())
109 onOpen.call({qjsEngine(this)->newQObject(this)});
110 }
111
112 void close()
113 {
114 m_open = false;
115 server.m_acceptor.close();
116 if(onClose.isCallable())
117 onClose.call();
118 }
119 W_SLOT(close)
120
121 void on_close()
122 {
123 m_open = false;
124 run_on_qt_thread({ onClose.call(); });
125 }
126
127 QJSValue onOpen;
128 QJSValue onClose;
129 QJSValue onError;
130 QJSValue onConnection;
131 ossia::net::tcp_server server;
132
133private:
134 void accept()
135 {
136 server.m_acceptor.async_accept(
137 [self = QPointer{this}](
138 boost::system::error_code ec, ossia::net::tcp_server::proto::socket socket) {
139 if(!self->m_open)
140 return;
141 if(!ec)
142 {
143 ossia::qt::run_async(self.get(), [self, socket = std::move(socket)]() mutable {
144 auto conn = new qml_tcp_connection{
145 ossia::net::tcp_listener{std::move(socket)}, self->server.m_context};
146
147 if(self->onConnection.isCallable())
148 {
149 self->onConnection.call({qjsEngine(self.get())->newQObject(conn)});
150 }
151 });
152 self->accept();
153 }
154 });
155 }
156
157 std::atomic_bool m_open{};
158};
159
160}
Definition qml_device.cpp:43