1717
1818#include " isource.hpp"
1919#include < error.hpp>
20+ #include < functional>
2021#include " controller.hpp"
2122#include " obs-property.hpp"
2223#include " properties.hpp"
2526#include " utility.hpp"
2627
2728Nan::Persistent<v8::FunctionTemplate> osn::ISource::prototype = Nan::Persistent<v8::FunctionTemplate>();
29+ osn::ISource* sourceObject;
30+
31+ osn::ISource::~ISource ()
32+ {
33+ stop_worker ();
34+ stop_async_runner ();
35+ }
36+
37+ void osn::ISource::start_async_runner ()
38+ {
39+ if (m_async_callback)
40+ return ;
41+
42+ std::unique_lock<std::mutex> ul (m_worker_lock);
43+
44+ // Start v8/uv asynchronous runner.
45+ m_async_callback = new osn::SourceCallback ();
46+ m_async_callback->set_handler (
47+ std::bind (&ISource::callback_handler, this , std::placeholders::_1, std::placeholders::_2), nullptr );
48+ }
49+
50+ void osn::ISource::stop_async_runner ()
51+ {
52+ if (!m_async_callback)
53+ return ;
54+
55+ std::unique_lock<std::mutex> ul (m_worker_lock);
56+
57+ // Stop v8/uv asynchronous runner.
58+ m_async_callback->clear ();
59+ m_async_callback->finalize ();
60+ m_async_callback = nullptr ;
61+ }
62+
63+ void osn::ISource::callback_handler (void * data, std::shared_ptr<std::vector<SourceHotkeyInfo>> item)
64+ {
65+ v8::Isolate* isolate = v8::Isolate::GetCurrent ();
66+ v8::Local<v8::Value> args[1 ];
67+
68+ for (auto & hotkeyInfo : *item) {
69+ v8::Local<v8::Value> argv = v8::Object::New (isolate);
70+
71+ argv->ToObject ()->Set (
72+ v8::String::NewFromUtf8 (isolate, " sourceName" ),
73+ v8::String::NewFromUtf8 (isolate, hotkeyInfo.sourceName .c_str ()));
74+ argv->ToObject ()->Set (
75+ v8::String::NewFromUtf8 (isolate, " hotkeyName" ),
76+ v8::String::NewFromUtf8 (isolate, hotkeyInfo.hotkeyName .c_str ()));
77+ argv->ToObject ()->Set (
78+ v8::String::NewFromUtf8 (isolate, " hotkeyDescription" ),
79+ v8::String::NewFromUtf8 (isolate, hotkeyInfo.hotkeyDesc .c_str ()));
80+ argv->ToObject ()->Set (
81+ v8::String::NewFromUtf8 (isolate, " hotkeyId" ), v8::Number::New (isolate, hotkeyInfo.hotkeyId ));
82+ args[0 ] = argv;
83+
84+ Nan::Call (m_callback_function, 1 , args);
85+ }
86+ }
87+
88+ void osn::ISource::start_worker ()
89+ {
90+ if (!m_worker_stop)
91+ return ;
92+ // Launch worker thread.
93+ m_worker_stop = false ;
94+ m_worker = std::thread (std::bind (&osn::ISource::worker, this ));
95+ }
96+
97+ void osn::ISource::stop_worker ()
98+ {
99+ if (m_worker_stop != false )
100+ return ;
101+ // Stop worker thread.
102+ m_worker_stop = true ;
103+ if (m_worker.joinable ()) {
104+ m_worker.join ();
105+ }
106+ }
28107
29108void osn::ISource::Register (Nan::ADDON_REGISTER_FUNCTION_ARGS_TYPE target)
30109{
@@ -289,6 +368,105 @@ Nan::NAN_METHOD_RETURN_TYPE osn::ISource::GetSettings(Nan::NAN_METHOD_ARGS_TYPE
289368 return ;
290369}
291370
371+ Nan::NAN_METHOD_RETURN_TYPE osn::ISource::ConnectHotkeyCallback (const v8::FunctionCallbackInfo<v8::Value>& args)
372+ {
373+ v8::Local<v8::Function> callback;
374+ ASSERT_GET_VALUE (args[0 ], callback);
375+
376+ // Grab IPC Connection
377+ std::shared_ptr<ipc::client> conn = nullptr ;
378+ if (!(conn = GetConnection ())) {
379+ return ;
380+ }
381+
382+ // Callback
383+ sourceObject = new ISource ();
384+ sourceObject->m_callback_function .Reset (callback);
385+ sourceObject->start_async_runner ();
386+ sourceObject->set_keepalive (args.This ());
387+ sourceObject->start_worker ();
388+ args.GetReturnValue ().Set (true );
389+ }
390+
391+ Nan::NAN_METHOD_RETURN_TYPE osn::ISource::DisconnectHotkeyCallback (const v8::FunctionCallbackInfo<v8::Value>& args)
392+ {
393+ sourceObject->stop_worker ();
394+ sourceObject->stop_async_runner ();
395+ }
396+
397+ Nan::NAN_METHOD_RETURN_TYPE osn::ISource::ProcessHotkeyStatus (const v8::FunctionCallbackInfo<v8::Value>& args)
398+ {
399+ uint64_t hotkeyId;
400+ bool press;
401+ std::string sourceId;
402+
403+ ASSERT_GET_VALUE (args[0 ], hotkeyId);
404+ ASSERT_GET_VALUE (args[1 ], press);
405+ ASSERT_GET_VALUE (args[2 ], sourceId);
406+
407+ auto conn = GetConnection ();
408+ if (!conn)
409+ return ;
410+
411+ std::vector<ipc::value> response = conn->call_synchronous_helper (
412+ " Source" , " ProcessHotkeyStatus" , {ipc::value (hotkeyId), ipc::value (press), ipc::value (sourceId)});
413+
414+ if (!ValidateResponse (response))
415+ return ;
416+ }
417+
418+ void osn::ISource::worker ()
419+ {
420+ size_t totalSleepMS = 0 ;
421+
422+ while (!m_worker_stop) {
423+ auto tp_start = std::chrono::high_resolution_clock::now ();
424+
425+ // Grab IPC Connection
426+ std::shared_ptr<ipc::client> conn = nullptr ;
427+ if (!(conn = GetConnection ())) {
428+ goto do_sleep;
429+ }
430+
431+ // Call
432+ {
433+ std::vector<ipc::value> response = conn->call_synchronous_helper (" Source" , " Query" , {});
434+ if (!response.size () || (response.size () == 1 )) {
435+ goto do_sleep;
436+ }
437+
438+ ErrorCode error = (ErrorCode)response[0 ].value_union .ui64 ;
439+ if (error == ErrorCode::Ok && (response.size () - 1 ) % 4 == 0 ) /* Each entry has 4 results */ {
440+ std::shared_ptr<std::vector<SourceHotkeyInfo>> data = std::make_shared<std::vector<SourceHotkeyInfo>>();
441+
442+ // For each hotkey pair
443+ for (int i = 1 ; i < response.size (); i += 4 ) {
444+ data->push_back ({response[i].value_str ,
445+ response[i + 1 ].value_str ,
446+ response[i + 2 ].value_str ,
447+ response[i + 3 ].value_union .ui64 });
448+ }
449+
450+ m_async_callback->queue (std::move (data));
451+ }
452+ }
453+
454+ do_sleep:
455+ auto tp_end = std::chrono::high_resolution_clock::now ();
456+ auto dur = std::chrono::duration_cast<std::chrono::milliseconds>(tp_end - tp_start);
457+ totalSleepMS = m_sleep_interval - dur.count ();
458+ std::this_thread::sleep_for (std::chrono::milliseconds (totalSleepMS));
459+ }
460+ return ;
461+ }
462+
463+ void osn::ISource::set_keepalive (v8::Local<v8::Object> obj)
464+ {
465+ if (!m_async_callback)
466+ return ;
467+ m_async_callback->set_keepalive (obj);
468+ }
469+
292470Nan::NAN_METHOD_RETURN_TYPE osn::ISource::Update (Nan::NAN_METHOD_ARGS_TYPE info)
293471{
294472 v8::Local<v8::Object> json;
@@ -600,3 +778,16 @@ Nan::NAN_METHOD_RETURN_TYPE osn::ISource::SetEnabled(Nan::NAN_METHOD_ARGS_TYPE i
600778
601779 info.GetReturnValue ().Set ((bool )response[1 ].value_union .i32 == enabled);
602780}
781+
782+ INITIALIZER (nodeobs_source)
783+ {
784+ initializerFunctions.push ([](v8::Local<v8::Object> exports) {
785+ NODE_SET_METHOD (exports, " ConnectHotkeyCallback" , osn::ISource::ConnectHotkeyCallback);
786+ });
787+ initializerFunctions.push ([](v8::Local<v8::Object> exports) {
788+ NODE_SET_METHOD (exports, " DisconnectHotkeyCallback" , osn::ISource::DisconnectHotkeyCallback);
789+ });
790+ initializerFunctions.push ([](v8::Local<v8::Object> exports) {
791+ NODE_SET_METHOD (exports, " ProcessHotkeyStatus" , osn::ISource::ProcessHotkeyStatus);
792+ });
793+ }
0 commit comments