# HG changeset patch # User Alain Mazy # Date 1588185914 -7200 # Node ID 419d0320c3442be43401f958e9e9dd5c5c36bba7 # Parent ff8d2e46ac63f61b0c56e9215d5ffb2e79012930 moved Platforms into Deprecated diff -r ff8d2e46ac63 -r 419d0320c344 Deprecated/Platforms/Generic/DelayedCallCommand.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Platforms/Generic/DelayedCallCommand.cpp Wed Apr 29 20:45:14 2020 +0200 @@ -0,0 +1,64 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2020 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + **/ + + +#include "DelayedCallCommand.h" +#include "boost/thread/thread.hpp" + +#include + +namespace Deprecated +{ + DelayedCallCommand::DelayedCallCommand(MessageHandler* callback, // takes ownership + unsigned int timeoutInMs, + Orthanc::IDynamicObject* payload /* takes ownership */, + OrthancStone::NativeStoneApplicationContext& context + ) : + callback_(callback), + payload_(payload), + context_(context), + expirationTimePoint_(boost::posix_time::microsec_clock::local_time() + boost::posix_time::milliseconds(timeoutInMs)), + timeoutInMs_(timeoutInMs) + { + } + + + void DelayedCallCommand::Execute() + { + while (boost::posix_time::microsec_clock::local_time() < expirationTimePoint_) + { + boost::this_thread::sleep(boost::posix_time::milliseconds(1)); + } + } + + void DelayedCallCommand::Commit() + { + // We want to make sure that, i.e, the UpdateThread is not + // triggered while we are updating the "model" with the result of + // an OracleCommand + OrthancStone::NativeStoneApplicationContext::GlobalMutexLocker lock(context_); + + if (callback_.get() != NULL) + { + IDelayedCallExecutor::TimeoutMessage message; // TODO: add payload + callback_->Apply(message); + } + } +} diff -r ff8d2e46ac63 -r 419d0320c344 Deprecated/Platforms/Generic/DelayedCallCommand.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Platforms/Generic/DelayedCallCommand.h Wed Apr 29 20:45:14 2020 +0200 @@ -0,0 +1,56 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2020 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + **/ + + +#pragma once + +#include "IOracleCommand.h" + +#include "../../Framework/Deprecated/Toolbox/IDelayedCallExecutor.h" +#include "../../Framework/Messages/IObservable.h" +#include "../../Framework/Messages/ICallable.h" +#include "../../Applications/Generic/NativeStoneApplicationContext.h" + +#include + +namespace Deprecated +{ + class DelayedCallCommand : public IOracleCommand, OrthancStone::IObservable + { + protected: + std::unique_ptr > callback_; + std::unique_ptr payload_; + OrthancStone::NativeStoneApplicationContext& context_; + boost::posix_time::ptime expirationTimePoint_; + unsigned int timeoutInMs_; + + public: + DelayedCallCommand(MessageHandler* callback, // takes ownership + unsigned int timeoutInMs, + Orthanc::IDynamicObject* payload /* takes ownership */, + OrthancStone::NativeStoneApplicationContext& context + ); + + virtual void Execute(); + + virtual void Commit(); + }; + +} diff -r ff8d2e46ac63 -r 419d0320c344 Deprecated/Platforms/Generic/IOracleCommand.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Platforms/Generic/IOracleCommand.h Wed Apr 29 20:45:14 2020 +0200 @@ -0,0 +1,42 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2020 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + **/ + + +#pragma once + +#include + +namespace Deprecated +{ + class IOracleCommand : public Orthanc::IDynamicObject + { + public: + virtual ~IOracleCommand() + { + } + + // This part of the command can be invoked simultaneously, and + // must not modify the Stone context + virtual void Execute() = 0; + + // This part of the command must be invoked in mutual exclusion + virtual void Commit() = 0; + }; +} diff -r ff8d2e46ac63 -r 419d0320c344 Deprecated/Platforms/Generic/Oracle.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Platforms/Generic/Oracle.cpp Wed Apr 29 20:45:14 2020 +0200 @@ -0,0 +1,212 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2020 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + **/ + + +#include "Oracle.h" + +#include +#include +#include + +#include +#include +#include + +namespace Deprecated +{ + class Oracle::PImpl + { + private: + enum State + { + State_Init, + State_Started, + State_Stopped + }; + + boost::mutex oracleMutex_; + State state_; + std::vector threads_; + Orthanc::SharedMessageQueue queue_; + + static void Worker(PImpl* that) + { + for (;;) + { + State state; + + { + boost::mutex::scoped_lock lock(that->oracleMutex_); + state = that->state_; + } + + if (state == State_Stopped) + { + break; + } + + std::unique_ptr item(that->queue_.Dequeue(100)); + if (item.get() != NULL) + { + IOracleCommand& command = dynamic_cast(*item); + try + { + command.Execute(); + } + catch (Orthanc::OrthancException& /*ex*/) + { + // this is probably a curl error that has been triggered. We may just ignore it. + // The command.success_ will stay at false and this will be handled in the command.Commit + } + + // Random sleeping to test + //boost::this_thread::sleep(boost::posix_time::milliseconds(50 * (1 + rand() % 10))); + + command.Commit(); + } + } + } + + public: + PImpl(unsigned int threadCount) : + state_(State_Init), + threads_(threadCount) + { + } + + ~PImpl() + { + if (state_ == State_Started) + { + LOG(ERROR) << "You should have manually called Oracle::Stop()"; + Stop(); + } + } + + Orthanc::SharedMessageQueue& GetQueue() + { + return queue_; + } + + void Submit(IOracleCommand* command) + { + std::unique_ptr protection(command); + + if (command == NULL) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); + } + + boost::mutex::scoped_lock lock(oracleMutex_); + + switch (state_) + { + case State_Init: + case State_Started: + queue_.Enqueue(protection.release()); + break; + + case State_Stopped: + LOG(ERROR) << "Cannot schedule a request to the Oracle after having " + << "called Oracle::Stop()"; + break; + + default: + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); + } + + } + + void Start() + { + boost::mutex::scoped_lock lock(oracleMutex_); + + if (state_ != State_Init) + { + LOG(ERROR) << "Oracle::PImpl::Start: (state_ != State_Init)"; + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); + } + + for (size_t i = 0; i < threads_.size(); i++) + { + threads_[i] = new boost::thread(Worker, this); + } + + state_ = State_Started; + } + + void Stop() + { + { + boost::mutex::scoped_lock lock(oracleMutex_); + + if (state_ != State_Started) + { + LOG(ERROR) << "Oracle::PImpl::Stop(): (state_ != State_Started)"; + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); + } + + state_ = State_Stopped; + } + + for (size_t i = 0; i < threads_.size(); i++) + { + if (threads_[i] != NULL) + { + if (threads_[i]->joinable()) + { + threads_[i]->join(); + } + + delete threads_[i]; + } + } + } + }; + + + Oracle::Oracle(unsigned int threadCount) : + pimpl_(new PImpl(threadCount)) + { + } + + void Oracle::Start() + { + pimpl_->Start(); + } + + + void Oracle::Submit(IOracleCommand* command) + { + pimpl_->Submit(command); + } + + + void Oracle::Stop() + { + pimpl_->Stop(); + } + + + void Oracle::WaitEmpty() + { + pimpl_->GetQueue().WaitEmpty(50); + } +} diff -r ff8d2e46ac63 -r 419d0320c344 Deprecated/Platforms/Generic/Oracle.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Platforms/Generic/Oracle.h Wed Apr 29 20:45:14 2020 +0200 @@ -0,0 +1,48 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2020 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + **/ + + +#pragma once + +#include "IOracleCommand.h" + +#include + +namespace Deprecated +{ + class Oracle : public boost::noncopyable + { + private: + class PImpl; + + boost::shared_ptr pimpl_; + + public: + Oracle(unsigned int threadCount); + + void Start(); + + void Submit(IOracleCommand* command); + + void WaitEmpty(); // For unit tests + + void Stop(); + }; +} diff -r ff8d2e46ac63 -r 419d0320c344 Deprecated/Platforms/Generic/OracleDelayedCallExecutor.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Platforms/Generic/OracleDelayedCallExecutor.h Wed Apr 29 20:45:14 2020 +0200 @@ -0,0 +1,52 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2020 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + **/ + + +#pragma once + +#include "../../Framework/Deprecated/Toolbox/IDelayedCallExecutor.h" +#include "Oracle.h" +#include "../../Applications/Generic/NativeStoneApplicationContext.h" +#include "DelayedCallCommand.h" + +namespace Deprecated +{ + // The OracleTimeout executes callbacks after a delay. + class OracleDelayedCallExecutor : public IDelayedCallExecutor + { + private: + Oracle& oracle_; + OrthancStone::NativeStoneApplicationContext& context_; + + public: + OracleDelayedCallExecutor(Oracle& oracle, + OrthancStone::NativeStoneApplicationContext& context) : + oracle_(oracle), + context_(context) + { + } + + virtual void Schedule(MessageHandler* callback, + unsigned int timeoutInMs = 1000) + { + oracle_.Submit(new DelayedCallCommand(callback, timeoutInMs, NULL, context_)); + } + }; +} diff -r ff8d2e46ac63 -r 419d0320c344 Deprecated/Platforms/Generic/OracleWebService.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Platforms/Generic/OracleWebService.cpp Wed Apr 29 20:45:14 2020 +0200 @@ -0,0 +1,80 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2020 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + **/ + + +#include "OracleWebService.h" +#include "../../Framework/Deprecated/Toolbox/IWebService.h" + +namespace Deprecated +{ + + + class OracleWebService::WebServiceCachedGetCommand : public IOracleCommand, OrthancStone::IObservable + { + protected: + std::unique_ptr > successCallback_; + std::unique_ptr payload_; + boost::shared_ptr cachedMessage_; + OrthancStone::NativeStoneApplicationContext& context_; + + public: + WebServiceCachedGetCommand(MessageHandler* successCallback, // takes ownership + boost::shared_ptr cachedMessage, + Orthanc::IDynamicObject* payload /* takes ownership */, + OrthancStone::NativeStoneApplicationContext& context + ) : + successCallback_(successCallback), + payload_(payload), + cachedMessage_(cachedMessage), + context_(context) + { + } + + virtual void Execute() + { + // nothing to do, everything is in the commit + } + + virtual void Commit() + { + // We want to make sure that, i.e, the UpdateThread is not + // triggered while we are updating the "model" with the result of + // a WebServiceCommand + OrthancStone::NativeStoneApplicationContext::GlobalMutexLocker lock(context_); + + IWebService::HttpRequestSuccessMessage successMessage(cachedMessage_->GetUri(), + cachedMessage_->GetAnswer(), + cachedMessage_->GetAnswerSize(), + cachedMessage_->GetAnswerHttpHeaders(), + payload_.get()); + + successCallback_->Apply(successMessage); + } + }; + + void OracleWebService::NotifyHttpSuccessLater(boost::shared_ptr cachedMessage, + Orthanc::IDynamicObject* payload, // takes ownership + MessageHandler* successCallback) + { + oracle_.Submit(new WebServiceCachedGetCommand(successCallback, cachedMessage, payload, context_)); + } + + +} diff -r ff8d2e46ac63 -r 419d0320c344 Deprecated/Platforms/Generic/OracleWebService.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Platforms/Generic/OracleWebService.h Wed Apr 29 20:45:14 2020 +0200 @@ -0,0 +1,92 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2020 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + **/ + + +#pragma once + +#include "../../Framework/Deprecated/Toolbox/BaseWebService.h" +#include "Oracle.h" +#include "WebServiceGetCommand.h" +#include "WebServicePostCommand.h" +#include "WebServiceDeleteCommand.h" +#include "../../Applications/Generic/NativeStoneApplicationContext.h" + +namespace Deprecated +{ + // The OracleWebService performs HTTP requests in a native environment. + // It uses a thread pool to handle multiple HTTP requests in a same time. + // It works asynchronously to mimick the behaviour of the WebService running in a WASM environment. + class OracleWebService : public BaseWebService + { + private: + Oracle& oracle_; + OrthancStone::NativeStoneApplicationContext& context_; + Orthanc::WebServiceParameters parameters_; + + class WebServiceCachedGetCommand; + + public: + OracleWebService(Oracle& oracle, + const Orthanc::WebServiceParameters& parameters, + OrthancStone::NativeStoneApplicationContext& context) : + oracle_(oracle), + context_(context), + parameters_(parameters) + { + } + + virtual void PostAsync(const std::string& uri, + const HttpHeaders& headers, + const std::string& body, + Orthanc::IDynamicObject* payload, // takes ownership + MessageHandler* successCallback, // takes ownership + MessageHandler* failureCallback = NULL, // takes ownership + unsigned int timeoutInSeconds = 60) + { + oracle_.Submit(new WebServicePostCommand(successCallback, failureCallback, parameters_, uri, headers, timeoutInSeconds, body, payload, context_)); + } + + virtual void DeleteAsync(const std::string& uri, + const HttpHeaders& headers, + Orthanc::IDynamicObject* payload, + MessageHandler* successCallback, + MessageHandler* failureCallback = NULL, + unsigned int timeoutInSeconds = 60) + { + oracle_.Submit(new WebServiceDeleteCommand(successCallback, failureCallback, parameters_, uri, headers, timeoutInSeconds, payload, context_)); + } + + protected: + virtual void GetAsyncInternal(const std::string& uri, + const HttpHeaders& headers, + Orthanc::IDynamicObject* payload, // takes ownership + MessageHandler* successCallback, // takes ownership + MessageHandler* failureCallback = NULL,// takes ownership + unsigned int timeoutInSeconds = 60) + { + oracle_.Submit(new WebServiceGetCommand(successCallback, failureCallback, parameters_, uri, headers, timeoutInSeconds, payload, context_)); + } + + virtual void NotifyHttpSuccessLater(boost::shared_ptr cachedHttpMessage, + Orthanc::IDynamicObject* payload, // takes ownership + MessageHandler* successCallback); + + }; +} diff -r ff8d2e46ac63 -r 419d0320c344 Deprecated/Platforms/Generic/WebServiceCommandBase.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Platforms/Generic/WebServiceCommandBase.cpp Wed Apr 29 20:45:14 2020 +0200 @@ -0,0 +1,69 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2020 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + **/ + + +#include "WebServiceCommandBase.h" + +#include + +namespace Deprecated +{ + WebServiceCommandBase::WebServiceCommandBase(MessageHandler* successCallback, + MessageHandler* failureCallback, + const Orthanc::WebServiceParameters& parameters, + const std::string& url, + const IWebService::HttpHeaders& headers, + unsigned int timeoutInSeconds, + Orthanc::IDynamicObject* payload /* takes ownership */, + OrthancStone::NativeStoneApplicationContext& context) : + successCallback_(successCallback), + failureCallback_(failureCallback), + parameters_(parameters), + url_(url), + headers_(headers), + payload_(payload), + success_(false), + httpStatus_(Orthanc::HttpStatus_None), + context_(context), + timeoutInSeconds_(timeoutInSeconds) + { + } + + + void WebServiceCommandBase::Commit() + { + // We want to make sure that, i.e, the UpdateThread is not + // triggered while we are updating the "model" with the result of + // a WebServiceCommand + OrthancStone::NativeStoneApplicationContext::GlobalMutexLocker lock(context_); + + if (success_ && successCallback_.get() != NULL) + { + IWebService::HttpRequestSuccessMessage message + (url_, answer_.c_str(), answer_.size(), answerHeaders_, payload_.get()); + successCallback_->Apply(message); + } + else if (!success_ && failureCallback_.get() != NULL) + { + IWebService::HttpRequestErrorMessage message(url_, httpStatus_, payload_.get()); + failureCallback_->Apply(message); + } + } +} diff -r ff8d2e46ac63 -r 419d0320c344 Deprecated/Platforms/Generic/WebServiceCommandBase.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Platforms/Generic/WebServiceCommandBase.h Wed Apr 29 20:45:14 2020 +0200 @@ -0,0 +1,69 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2020 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + **/ + + +#pragma once + +#include "IOracleCommand.h" + +#include "../../Framework/Deprecated/Toolbox/IWebService.h" +#include "../../Framework/Messages/IObservable.h" +#include "../../Framework/Messages/ICallable.h" +#include "../../Applications/Generic/NativeStoneApplicationContext.h" + +#include + +#include + +namespace Deprecated +{ + class WebServiceCommandBase : public IOracleCommand, OrthancStone::IObservable + { + protected: + std::unique_ptr > successCallback_; + std::unique_ptr > failureCallback_; + Orthanc::WebServiceParameters parameters_; + std::string url_; + IWebService::HttpHeaders headers_; + std::unique_ptr payload_; + bool success_; + Orthanc::HttpStatus httpStatus_; + std::string answer_; + IWebService::HttpHeaders answerHeaders_; + OrthancStone::NativeStoneApplicationContext& context_; + unsigned int timeoutInSeconds_; + + public: + WebServiceCommandBase(MessageHandler* successCallback, // takes ownership + MessageHandler* failureCallback, // takes ownership + const Orthanc::WebServiceParameters& parameters, + const std::string& url, + const IWebService::HttpHeaders& headers, + unsigned int timeoutInSeconds, + Orthanc::IDynamicObject* payload /* takes ownership */, + OrthancStone::NativeStoneApplicationContext& context + ); + + virtual void Execute() = 0; + + virtual void Commit(); + }; + +} diff -r ff8d2e46ac63 -r 419d0320c344 Deprecated/Platforms/Generic/WebServiceDeleteCommand.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Platforms/Generic/WebServiceDeleteCommand.cpp Wed Apr 29 20:45:14 2020 +0200 @@ -0,0 +1,56 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2020 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + **/ + + +#include "WebServiceDeleteCommand.h" + +#include + +namespace Deprecated +{ + WebServiceDeleteCommand::WebServiceDeleteCommand(MessageHandler* successCallback, // takes ownership + MessageHandler* failureCallback, // takes ownership + const Orthanc::WebServiceParameters& parameters, + const std::string& url, + const Deprecated::IWebService::HttpHeaders& headers, + unsigned int timeoutInSeconds, + Orthanc::IDynamicObject* payload /* takes ownership */, + OrthancStone::NativeStoneApplicationContext& context) : + WebServiceCommandBase(successCallback, failureCallback, parameters, url, headers, timeoutInSeconds, payload, context) + { + } + + void WebServiceDeleteCommand::Execute() + { + Orthanc::HttpClient client(parameters_, "/"); + client.SetUrl(url_); + client.SetTimeout(timeoutInSeconds_); + client.SetMethod(Orthanc::HttpMethod_Delete); + + for (Deprecated::IWebService::HttpHeaders::const_iterator it = headers_.begin(); it != headers_.end(); it++ ) + { + client.AddHeader(it->first, it->second); + } + + success_ = client.Apply(answer_, answerHeaders_); + httpStatus_ = client.GetLastStatus(); + } + +} diff -r ff8d2e46ac63 -r 419d0320c344 Deprecated/Platforms/Generic/WebServiceDeleteCommand.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Platforms/Generic/WebServiceDeleteCommand.h Wed Apr 29 20:45:14 2020 +0200 @@ -0,0 +1,42 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2020 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + **/ + + +#pragma once + +#include "WebServiceCommandBase.h" + +namespace Deprecated +{ + class WebServiceDeleteCommand : public WebServiceCommandBase + { + public: + WebServiceDeleteCommand(MessageHandler* successCallback, // takes ownership + MessageHandler* failureCallback, // takes ownership + const Orthanc::WebServiceParameters& parameters, + const std::string& url, + const IWebService::HttpHeaders& headers, + unsigned int timeoutInSeconds, + Orthanc::IDynamicObject* payload /* takes ownership */, + OrthancStone::NativeStoneApplicationContext& context); + + virtual void Execute(); + }; +} diff -r ff8d2e46ac63 -r 419d0320c344 Deprecated/Platforms/Generic/WebServiceGetCommand.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Platforms/Generic/WebServiceGetCommand.cpp Wed Apr 29 20:45:14 2020 +0200 @@ -0,0 +1,57 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2020 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + **/ + + +#include "WebServiceGetCommand.h" + +#include + +namespace Deprecated +{ + WebServiceGetCommand::WebServiceGetCommand(MessageHandler* successCallback, // takes ownership + MessageHandler* failureCallback, // takes ownership + const Orthanc::WebServiceParameters& parameters, + const std::string& url, + const IWebService::HttpHeaders& headers, + unsigned int timeoutInSeconds, + Orthanc::IDynamicObject* payload /* takes ownership */, + OrthancStone::NativeStoneApplicationContext& context) : + WebServiceCommandBase(successCallback, failureCallback, parameters, url, headers, timeoutInSeconds, payload, context) + { + } + + + void WebServiceGetCommand::Execute() + { + Orthanc::HttpClient client(parameters_, "/"); + client.SetUrl(url_); + client.SetTimeout(timeoutInSeconds_); + client.SetMethod(Orthanc::HttpMethod_Get); + + for (IWebService::HttpHeaders::const_iterator it = headers_.begin(); it != headers_.end(); it++ ) + { + client.AddHeader(it->first, it->second); + } + + success_ = client.Apply(answer_, answerHeaders_); + httpStatus_ = client.GetLastStatus(); + } + +} diff -r ff8d2e46ac63 -r 419d0320c344 Deprecated/Platforms/Generic/WebServiceGetCommand.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Platforms/Generic/WebServiceGetCommand.h Wed Apr 29 20:45:14 2020 +0200 @@ -0,0 +1,43 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2020 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + **/ + + +#pragma once + +#include "WebServiceCommandBase.h" + +namespace Deprecated +{ + class WebServiceGetCommand : public WebServiceCommandBase + { + public: + WebServiceGetCommand(MessageHandler* successCallback, // takes ownership + MessageHandler* failureCallback, // takes ownership + const Orthanc::WebServiceParameters& parameters, + const std::string& url, + const IWebService::HttpHeaders& headers, + unsigned int timeoutInSeconds, + Orthanc::IDynamicObject* payload /* takes ownership */, + OrthancStone::NativeStoneApplicationContext& context); + + virtual void Execute(); + }; + +} diff -r ff8d2e46ac63 -r 419d0320c344 Deprecated/Platforms/Generic/WebServicePostCommand.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Platforms/Generic/WebServicePostCommand.cpp Wed Apr 29 20:45:14 2020 +0200 @@ -0,0 +1,58 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2020 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + **/ + + +#include "WebServicePostCommand.h" + +#include + +namespace Deprecated +{ + WebServicePostCommand::WebServicePostCommand(MessageHandler* successCallback, // takes ownership + MessageHandler* failureCallback, // takes ownership + const Orthanc::WebServiceParameters& parameters, + const std::string& url, + const Deprecated::IWebService::HttpHeaders& headers, + unsigned int timeoutInSeconds, + const std::string& body, + Orthanc::IDynamicObject* payload /* takes ownership */, + OrthancStone::NativeStoneApplicationContext& context) : + WebServiceCommandBase(successCallback, failureCallback, parameters, url, headers, timeoutInSeconds, payload, context), + body_(body) + { + } + + void WebServicePostCommand::Execute() + { + Orthanc::HttpClient client(parameters_, "/"); + client.SetUrl(url_); + client.SetTimeout(timeoutInSeconds_); + client.SetMethod(Orthanc::HttpMethod_Post); + client.GetBody().swap(body_); + + for (Deprecated::IWebService::HttpHeaders::const_iterator it = headers_.begin(); it != headers_.end(); it++ ) + { + client.AddHeader(it->first, it->second); + } + + success_ = client.Apply(answer_, answerHeaders_); + httpStatus_ = client.GetLastStatus(); + } +} diff -r ff8d2e46ac63 -r 419d0320c344 Deprecated/Platforms/Generic/WebServicePostCommand.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Platforms/Generic/WebServicePostCommand.h Wed Apr 29 20:45:14 2020 +0200 @@ -0,0 +1,46 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2020 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + **/ + + +#pragma once + +#include "WebServiceCommandBase.h" + +namespace Deprecated +{ + class WebServicePostCommand : public WebServiceCommandBase + { + protected: + std::string body_; + + public: + WebServicePostCommand(MessageHandler* successCallback, // takes ownership + MessageHandler* failureCallback, // takes ownership + const Orthanc::WebServiceParameters& parameters, + const std::string& url, + const IWebService::HttpHeaders& headers, + unsigned int timeoutInSeconds, + const std::string& body, + Orthanc::IDynamicObject* payload /* takes ownership */, + OrthancStone::NativeStoneApplicationContext& context); + + virtual void Execute(); + }; +} diff -r ff8d2e46ac63 -r 419d0320c344 Deprecated/Platforms/Wasm/Defaults.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Platforms/Wasm/Defaults.cpp Wed Apr 29 20:45:14 2020 +0200 @@ -0,0 +1,416 @@ +#include "Defaults.h" + +#include "WasmWebService.h" +#include "WasmDelayedCallExecutor.h" +#include "../../Framework/Deprecated/Widgets/TestCairoWidget.h" +#include +#include +#include +#include +#include +#include + +#include + + +static unsigned int width_ = 0; +static unsigned int height_ = 0; + +/**********************************/ + +static std::unique_ptr application; +static std::unique_ptr applicationWasmAdapter = NULL; +static std::unique_ptr context; +static OrthancStone::StartupParametersBuilder startupParametersBuilder; +static OrthancStone::MessageBroker broker; + +static OrthancStone::ViewportContentChangedObserver viewportContentChangedObserver_(broker); +static OrthancStone::StatusBar statusBar_; + +static std::list> viewports_; + +std::shared_ptr FindViewportSharedPtr(ViewportHandle viewport) { + for (const auto& v : viewports_) { + if (v.get() == viewport) { + return v; + } + } + assert(false); + return std::shared_ptr(); +} + +#ifdef __cplusplus +extern "C" { +#endif + +#if 0 + // rewrite malloc/free in order to monitor allocations. We actually only monitor large allocations (like images ...) + + size_t bigChunksTotalSize = 0; + std::map allocatedBigChunks; + + extern void* emscripten_builtin_malloc(size_t bytes); + extern void emscripten_builtin_free(void* mem); + + void * __attribute__((noinline)) malloc(size_t size) + { + void *ptr = emscripten_builtin_malloc(size); + if (size > 100000) + { + bigChunksTotalSize += size; + printf("++ Allocated %zu bytes, got %p. (%zu MB consumed by big chunks)\n", size, ptr, bigChunksTotalSize/(1024*1024)); + allocatedBigChunks[ptr] = size; + } + return ptr; + } + + void __attribute__((noinline)) free(void *ptr) + { + emscripten_builtin_free(ptr); + + std::map::iterator it = allocatedBigChunks.find(ptr); + if (it != allocatedBigChunks.end()) + { + bigChunksTotalSize -= it->second; + printf("-- Freed %zu bytes at %p. (%zu MB consumed by big chunks)\n", it->second, ptr, bigChunksTotalSize/(1024*1024)); + allocatedBigChunks.erase(it); + } + } +#endif // 0 + + using namespace OrthancStone; + + // when WASM needs a C++ viewport + ViewportHandle EMSCRIPTEN_KEEPALIVE CreateCppViewport() { + + std::shared_ptr viewport(new Deprecated::WidgetViewport(broker)); + printf("viewport %x\n", (int)viewport.get()); + + viewports_.push_back(viewport); + + printf("There are now %lu viewports in C++\n", viewports_.size()); + + viewport->SetStatusBar(statusBar_); + + viewport->RegisterObserverCallback( + new Callable + (viewportContentChangedObserver_, &ViewportContentChangedObserver::OnViewportChanged)); + + return viewport.get(); + } + + // when WASM does not need a viewport anymore, it should release it + void EMSCRIPTEN_KEEPALIVE ReleaseCppViewport(ViewportHandle viewport) { + viewports_.remove_if([viewport](const std::shared_ptr& v) { return v.get() == viewport;}); + + printf("There are now %lu viewports in C++\n", viewports_.size()); + } + + void EMSCRIPTEN_KEEPALIVE CreateWasmApplication(ViewportHandle viewport) { + printf("Initializing Stone\n"); + OrthancStone::StoneInitialize(); + printf("CreateWasmApplication\n"); + + application.reset(CreateUserApplication(broker)); + applicationWasmAdapter.reset(CreateWasmApplicationAdapter(broker, application.get())); + Deprecated::WasmWebService::SetBroker(broker); + Deprecated::WasmDelayedCallExecutor::SetBroker(broker); + + startupParametersBuilder.Clear(); + } + + void EMSCRIPTEN_KEEPALIVE SetStartupParameter(const char* keyc, + const char* value) { + startupParametersBuilder.SetStartupParameter(keyc, value); + } + + void EMSCRIPTEN_KEEPALIVE StartWasmApplication(const char* baseUri) { + + printf("StartWasmApplication\n"); + + Orthanc::Logging::SetErrorWarnInfoTraceLoggingFunctions( + stone_console_error, stone_console_warning, + stone_console_info, stone_console_trace); + + // recreate a command line from uri arguments and parse it + boost::program_options::variables_map parameters; + boost::program_options::options_description options; + application->DeclareStartupOptions(options); + startupParametersBuilder.GetStartupParameters(parameters, options); + + context.reset(new OrthancStone::StoneApplicationContext(broker)); + context->SetOrthancBaseUrl(baseUri); + printf("Base URL to Orthanc API: [%s]\n", baseUri); + context->SetWebService(Deprecated::WasmWebService::GetInstance()); + context->SetDelayedCallExecutor(Deprecated::WasmDelayedCallExecutor::GetInstance()); + application->Initialize(context.get(), statusBar_, parameters); + application->InitializeWasm(); + +// viewport->SetSize(width_, height_); + printf("StartWasmApplication - completed\n"); + } + + bool EMSCRIPTEN_KEEPALIVE WasmIsTraceLevelEnabled() + { + return Orthanc::Logging::IsTraceLevelEnabled(); + } + + bool EMSCRIPTEN_KEEPALIVE WasmIsInfoLevelEnabled() + { + return Orthanc::Logging::IsInfoLevelEnabled(); + } + + void EMSCRIPTEN_KEEPALIVE WasmDoAnimation() + { + for (auto viewport : viewports_) { + // TODO Only launch the JavaScript timer if "HasAnimation()" + if (viewport->HasAnimation()) + { + viewport->DoAnimation(); + } + + } + + } + + + void EMSCRIPTEN_KEEPALIVE ViewportSetSize(ViewportHandle viewport, unsigned int width, unsigned int height) + { + width_ = width; + height_ = height; + + viewport->SetSize(width, height); + } + + int EMSCRIPTEN_KEEPALIVE ViewportRender(ViewportHandle viewport, + unsigned int width, + unsigned int height, + uint8_t* data) + { + viewportContentChangedObserver_.Reset(); + + //printf("ViewportRender called %dx%d\n", width, height); + if (width == 0 || + height == 0) + { + return 1; + } + + Orthanc::ImageAccessor surface; + surface.AssignWritable(Orthanc::PixelFormat_BGRA32, width, height, 4 * width, data); + + viewport->Render(surface); + + // Convert from BGRA32 memory layout (only color mode supported by + // Cairo, which corresponds to CAIRO_FORMAT_ARGB32) to RGBA32 (as + // expected by HTML5 canvas). This simply amounts to swapping the + // B and R channels. + uint8_t* p = data; + for (unsigned int y = 0; y < height; y++) { + for (unsigned int x = 0; x < width; x++) { + uint8_t tmp = p[0]; + p[0] = p[2]; + p[2] = tmp; + + p += 4; + } + } + + return 1; + } + + + void EMSCRIPTEN_KEEPALIVE ViewportMouseDown(ViewportHandle viewport, + unsigned int rawButton, + int x, + int y, + unsigned int rawModifiers) + { + OrthancStone::MouseButton button; + switch (rawButton) + { + case 0: + button = OrthancStone::MouseButton_Left; + break; + + case 1: + button = OrthancStone::MouseButton_Middle; + break; + + case 2: + button = OrthancStone::MouseButton_Right; + break; + + default: + return; // Unknown button + } + + viewport->MouseDown(button, x, y, OrthancStone::KeyboardModifiers_None, std::vector()); + } + + + void EMSCRIPTEN_KEEPALIVE ViewportMouseWheel(ViewportHandle viewport, + int deltaY, + int x, + int y, + int isControl) + { + if (deltaY != 0) + { + OrthancStone::MouseWheelDirection direction = (deltaY < 0 ? + OrthancStone::MouseWheelDirection_Up : + OrthancStone::MouseWheelDirection_Down); + OrthancStone::KeyboardModifiers modifiers = OrthancStone::KeyboardModifiers_None; + + if (isControl != 0) + { + modifiers = OrthancStone::KeyboardModifiers_Control; + } + + viewport->MouseWheel(direction, x, y, modifiers); + } + } + + + void EMSCRIPTEN_KEEPALIVE ViewportMouseMove(ViewportHandle viewport, + int x, + int y) + { + viewport->MouseMove(x, y, std::vector()); + } + + void GetTouchVector(std::vector& output, + int touchCount, + float x0, + float y0, + float x1, + float y1, + float x2, + float y2) + { + // TODO: it might be nice to try to pass all the x0,y0 coordinates as arrays but that's not so easy to pass array between JS and C++ + if (touchCount > 0) + { + output.push_back(Deprecated::Touch(x0, y0)); + } + if (touchCount > 1) + { + output.push_back(Deprecated::Touch(x1, y1)); + } + if (touchCount > 2) + { + output.push_back(Deprecated::Touch(x2, y2)); + } + + } + + void EMSCRIPTEN_KEEPALIVE ViewportTouchStart(ViewportHandle viewport, + int touchCount, + float x0, + float y0, + float x1, + float y1, + float x2, + float y2) + { + // printf("touch start with %d touches\n", touchCount); + + std::vector touches; + GetTouchVector(touches, touchCount, x0, y0, x1, y1, x2, y2); + viewport->TouchStart(touches); + } + + void EMSCRIPTEN_KEEPALIVE ViewportTouchMove(ViewportHandle viewport, + int touchCount, + float x0, + float y0, + float x1, + float y1, + float x2, + float y2) + { + // printf("touch move with %d touches\n", touchCount); + + std::vector touches; + GetTouchVector(touches, touchCount, x0, y0, x1, y1, x2, y2); + viewport->TouchMove(touches); + } + + void EMSCRIPTEN_KEEPALIVE ViewportTouchEnd(ViewportHandle viewport, + int touchCount, + float x0, + float y0, + float x1, + float y1, + float x2, + float y2) + { + // printf("touch end with %d touches remaining\n", touchCount); + + std::vector touches; + GetTouchVector(touches, touchCount, x0, y0, x1, y1, x2, y2); + viewport->TouchEnd(touches); + } + + void EMSCRIPTEN_KEEPALIVE ViewportKeyPressed(ViewportHandle viewport, + int key, + const char* keyChar, + bool isShiftPressed, + bool isControlPressed, + bool isAltPressed) + + { + OrthancStone::KeyboardModifiers modifiers = OrthancStone::KeyboardModifiers_None; + if (isShiftPressed) { + modifiers = static_cast(modifiers + OrthancStone::KeyboardModifiers_Shift); + } + if (isControlPressed) { + modifiers = static_cast(modifiers + OrthancStone::KeyboardModifiers_Control); + } + if (isAltPressed) { + modifiers = static_cast(modifiers + OrthancStone::KeyboardModifiers_Alt); + } + + char c = 0; + if (keyChar != NULL && key == OrthancStone::KeyboardKeys_Generic) { + c = keyChar[0]; + } + viewport->KeyPressed(static_cast(key), c, modifiers); + } + + + void EMSCRIPTEN_KEEPALIVE ViewportMouseUp(ViewportHandle viewport) + { + viewport->MouseUp(); + } + + + void EMSCRIPTEN_KEEPALIVE ViewportMouseEnter(ViewportHandle viewport) + { + viewport->MouseEnter(); + } + + + void EMSCRIPTEN_KEEPALIVE ViewportMouseLeave(ViewportHandle viewport) + { + viewport->MouseLeave(); + } + + const char* EMSCRIPTEN_KEEPALIVE SendSerializedMessageToStoneApplication(const char* message) + { + static std::string output; // we don't want the string to be deallocated when we return to JS code so we always use the same string (this is fine since JS is single-thread) + + //printf("SendSerializedMessageToStoneApplication\n"); + //printf("%s", message); + + if (applicationWasmAdapter.get() != NULL) { + applicationWasmAdapter->HandleSerializedMessageFromWeb(output, std::string(message)); + return output.c_str(); + } + printf("This Stone application does not have a Web Adapter, unable to send messages"); + return NULL; + } + +#ifdef __cplusplus +} +#endif diff -r ff8d2e46ac63 -r 419d0320c344 Deprecated/Platforms/Wasm/Defaults.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Platforms/Wasm/Defaults.h Wed Apr 29 20:45:14 2020 +0200 @@ -0,0 +1,82 @@ +#pragma once + +#include + +#include "../../Framework/Deprecated/Viewport/WidgetViewport.h" +#include "../../Framework/Deprecated/Widgets/LayoutWidget.h" +#include +#include + +typedef Deprecated::WidgetViewport* ViewportHandle; // the objects exchanged between JS and C++ + +#ifdef __cplusplus +extern "C" { +#endif + + // JS methods accessible from C++ + extern void ScheduleWebViewportRedrawFromCpp(ViewportHandle cppViewportHandle); + extern void UpdateStoneApplicationStatusFromCppWithString(const char* statusUpdateMessage); + extern void UpdateStoneApplicationStatusFromCppWithSerializedMessage(const char* statusUpdateMessage); + extern void stone_console_error(const char*); + extern void stone_console_warning(const char*); + extern void stone_console_info(const char*); + extern void stone_console_trace(const char*); + + // C++ methods accessible from JS + extern void EMSCRIPTEN_KEEPALIVE CreateWasmApplication(ViewportHandle cppViewportHandle); + extern void EMSCRIPTEN_KEEPALIVE SetStartupParameter(const char* keyc, const char* value); + + +#ifdef __cplusplus +} +#endif + +// these methods must be implemented in the custom app "mainWasm.cpp" +extern OrthancStone::IStoneApplication* CreateUserApplication(OrthancStone::MessageBroker& broker); +extern OrthancStone::WasmPlatformApplicationAdapter* CreateWasmApplicationAdapter(OrthancStone::MessageBroker& broker, OrthancStone::IStoneApplication* application); + +namespace OrthancStone { + + // default Observer to trigger Viewport redraw when something changes in the Viewport + class ViewportContentChangedObserver : public IObserver + { + private: + // Flag to avoid flooding JavaScript with redundant Redraw requests + bool isScheduled_; + + public: + ViewportContentChangedObserver(MessageBroker& broker) : + IObserver(broker), + isScheduled_(false) + { + } + + void Reset() + { + isScheduled_ = false; + } + + void OnViewportChanged(const Deprecated::IViewport::ViewportChangedMessage& message) + { + if (!isScheduled_) + { + ScheduleWebViewportRedrawFromCpp((ViewportHandle)&message.GetOrigin()); // loosing constness when transmitted to Web + isScheduled_ = true; + } + } + }; + + // default status bar to log messages on the console/stdout + class StatusBar : public Deprecated::IStatusBar + { + public: + virtual void ClearMessage() + { + } + + virtual void SetMessage(const std::string& message) + { + printf("%s\n", message.c_str()); + } + }; +} diff -r ff8d2e46ac63 -r 419d0320c344 Deprecated/Platforms/Wasm/WasmDelayedCallExecutor.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Platforms/Wasm/WasmDelayedCallExecutor.cpp Wed Apr 29 20:45:14 2020 +0200 @@ -0,0 +1,46 @@ +#include "WasmDelayedCallExecutor.h" +#include "json/value.h" +#include "json/writer.h" +#include + +#ifdef __cplusplus +extern "C" { +#endif + + extern void WasmDelayedCallExecutor_Schedule(void* callable, + unsigned int timeoutInMs + /*void* payload*/); + + void EMSCRIPTEN_KEEPALIVE WasmDelayedCallExecutor_ExecuteCallback(void* callable + //void* payload + ) + { + if (callable == NULL) + { + throw; + } + else + { + reinterpret_cast*>(callable)-> + Apply(Deprecated::IDelayedCallExecutor::TimeoutMessage()); // uri, reinterpret_cast(payload))); + } + } + + +#ifdef __cplusplus +} +#endif + + + +namespace Deprecated +{ + OrthancStone::MessageBroker* WasmDelayedCallExecutor::broker_ = NULL; + + + void WasmDelayedCallExecutor::Schedule(OrthancStone::MessageHandler* callback, + unsigned int timeoutInMs) + { + WasmDelayedCallExecutor_Schedule(callback, timeoutInMs); + } +} diff -r ff8d2e46ac63 -r 419d0320c344 Deprecated/Platforms/Wasm/WasmDelayedCallExecutor.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Platforms/Wasm/WasmDelayedCallExecutor.h Wed Apr 29 20:45:14 2020 +0200 @@ -0,0 +1,40 @@ +#pragma once + +#include "../../Framework/Deprecated/Toolbox/IDelayedCallExecutor.h" +#include + +namespace Deprecated +{ + class WasmDelayedCallExecutor : public IDelayedCallExecutor + { + private: + static OrthancStone::MessageBroker* broker_; + + // Private constructor => Singleton design pattern + WasmDelayedCallExecutor(OrthancStone::MessageBroker& broker) : + IDelayedCallExecutor(broker) + { + } + + public: + static WasmDelayedCallExecutor& GetInstance() + { + if (broker_ == NULL) + { + printf("WasmDelayedCallExecutor::GetInstance(): broker not initialized\n"); + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); + } + static WasmDelayedCallExecutor instance(*broker_); + return instance; + } + + static void SetBroker(OrthancStone::MessageBroker& broker) + { + broker_ = &broker; + } + + virtual void Schedule(OrthancStone::MessageHandler* callback, + unsigned int timeoutInMs = 1000); + + }; +} diff -r ff8d2e46ac63 -r 419d0320c344 Deprecated/Platforms/Wasm/WasmDelayedCallExecutor.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Platforms/Wasm/WasmDelayedCallExecutor.js Wed Apr 29 20:45:14 2020 +0200 @@ -0,0 +1,7 @@ +mergeInto(LibraryManager.library, { + WasmDelayedCallExecutor_Schedule: function(callable, timeoutInMs/*, payload*/) { + setTimeout(function() { + window.WasmDelayedCallExecutor_ExecuteCallback(callable/*, payload*/); + }, timeoutInMs); + } +}); diff -r ff8d2e46ac63 -r 419d0320c344 Deprecated/Platforms/Wasm/WasmPlatformApplicationAdapter.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Platforms/Wasm/WasmPlatformApplicationAdapter.cpp Wed Apr 29 20:45:14 2020 +0200 @@ -0,0 +1,59 @@ +#include "WasmPlatformApplicationAdapter.h" + +#include "Framework/StoneException.h" +#include +#include "Platforms/Wasm/Defaults.h" + +namespace OrthancStone +{ + WasmPlatformApplicationAdapter::WasmPlatformApplicationAdapter(MessageBroker& broker, IStoneApplication& application) + : IObserver(broker), + application_(application) + { + } + + void WasmPlatformApplicationAdapter::HandleSerializedMessageFromWeb(std::string& output, const std::string& input) + { + try + { + application_.HandleSerializedMessage(input.c_str()); + } + catch (StoneException& exc) + { + printf("Error while handling message from web (error code = %d):\n", exc.GetErrorCode()); + printf("While interpreting input: '%s'\n", input.c_str()); + output = std::string("ERROR : "); + } + catch (std::exception& exc) + { + printf("Error while handling message from web (error text = %s):\n", exc.what()); + printf("While interpreting input: '%s'\n", input.c_str()); + output = std::string("ERROR : "); + } + } + + void WasmPlatformApplicationAdapter::NotifyStatusUpdateFromCppToWebWithString(const std::string& statusUpdateMessage) + { + try + { + UpdateStoneApplicationStatusFromCppWithString(statusUpdateMessage.c_str()); + } + catch (...) + { + printf("Error while handling string message to web\n"); + } + } + + void WasmPlatformApplicationAdapter::NotifyStatusUpdateFromCppToWebWithSerializedMessage(const std::string& statusUpdateMessage) + { + try + { + UpdateStoneApplicationStatusFromCppWithSerializedMessage(statusUpdateMessage.c_str()); + } + catch (...) + { + printf("Error while handling serialized message to web\n"); + } + } + +} diff -r ff8d2e46ac63 -r 419d0320c344 Deprecated/Platforms/Wasm/WasmPlatformApplicationAdapter.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Platforms/Wasm/WasmPlatformApplicationAdapter.h Wed Apr 29 20:45:14 2020 +0200 @@ -0,0 +1,19 @@ +#pragma once + +#include +#include +#include + +namespace OrthancStone +{ + class WasmPlatformApplicationAdapter : public IObserver + { + IStoneApplication& application_; + public: + WasmPlatformApplicationAdapter(MessageBroker& broker, IStoneApplication& application); + + virtual void HandleSerializedMessageFromWeb(std::string& output, const std::string& input); + virtual void NotifyStatusUpdateFromCppToWebWithString(const std::string& statusUpdateMessage); + virtual void NotifyStatusUpdateFromCppToWebWithSerializedMessage(const std::string& statusUpdateMessage); + }; +} \ No newline at end of file diff -r ff8d2e46ac63 -r 419d0320c344 Deprecated/Platforms/Wasm/WasmViewport.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Platforms/Wasm/WasmViewport.cpp Wed Apr 29 20:45:14 2020 +0200 @@ -0,0 +1,13 @@ +#include "WasmViewport.h" + +#include +#include + +std::vector> wasmViewports; + +void AttachWidgetToWasmViewport(const char* htmlCanvasId, Deprecated::IWidget* centralWidget) { + std::shared_ptr viewport(CreateWasmViewportFromCpp(htmlCanvasId)); + viewport->SetCentralWidget(centralWidget); + + wasmViewports.push_back(viewport); +} diff -r ff8d2e46ac63 -r 419d0320c344 Deprecated/Platforms/Wasm/WasmViewport.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Platforms/Wasm/WasmViewport.h Wed Apr 29 20:45:14 2020 +0200 @@ -0,0 +1,18 @@ +#pragma once + +#include "../../Framework/Deprecated/Viewport/WidgetViewport.h" + +#include + +#ifdef __cplusplus +extern "C" { +#endif + + // JS methods accessible from C++ + extern Deprecated::WidgetViewport* CreateWasmViewportFromCpp(const char* htmlCanvasId); + +#ifdef __cplusplus +} +#endif + +extern void AttachWidgetToWasmViewport(const char* htmlCanvasId, Deprecated::IWidget* centralWidget); diff -r ff8d2e46ac63 -r 419d0320c344 Deprecated/Platforms/Wasm/WasmWebService.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Platforms/Wasm/WasmWebService.cpp Wed Apr 29 20:45:14 2020 +0200 @@ -0,0 +1,168 @@ +#include "WasmWebService.h" +#include "json/value.h" +#include "json/writer.h" +#include +#include + +struct CachedSuccessNotification +{ + boost::shared_ptr cachedMessage; + std::unique_ptr payload; + OrthancStone::MessageHandler* successCallback; +}; + + +#ifdef __cplusplus +extern "C" { +#endif + + extern void WasmWebService_GetAsync(void* callableSuccess, + void* callableFailure, + const char* uri, + const char* headersInJsonString, + void* payload, + unsigned int timeoutInSeconds); + + extern void WasmWebService_ScheduleLaterCachedSuccessNotification(void* brol); + + extern void WasmWebService_PostAsync(void* callableSuccess, + void* callableFailure, + const char* uri, + const char* headersInJsonString, + const void* body, + size_t bodySize, + void* payload, + unsigned int timeoutInSeconds); + + extern void WasmWebService_DeleteAsync(void* callableSuccess, + void* callableFailure, + const char* uri, + const char* headersInJsonString, + void* payload, + unsigned int timeoutInSeconds); + + void EMSCRIPTEN_KEEPALIVE WasmWebService_NotifyError(void* failureCallable, + const char* uri, + unsigned int httpStatus, + void* payload) + { + if (failureCallable != NULL) + { + reinterpret_cast*>(failureCallable)-> + Apply(Deprecated::IWebService::HttpRequestErrorMessage(uri, static_cast(httpStatus), reinterpret_cast(payload))); + } + } + + void EMSCRIPTEN_KEEPALIVE WasmWebService_NotifyCachedSuccess(void* notification_) + { + // notification has been allocated in C++ and passed to JS. It must be deleted by this method + std::unique_ptr notification(reinterpret_cast(notification_)); + + notification->successCallback->Apply(Deprecated::IWebService::HttpRequestSuccessMessage( + notification->cachedMessage->GetUri(), + notification->cachedMessage->GetAnswer(), + notification->cachedMessage->GetAnswerSize(), + notification->cachedMessage->GetAnswerHttpHeaders(), + notification->payload.get() + )); + } + + void EMSCRIPTEN_KEEPALIVE WasmWebService_NotifySuccess(void* successCallable, + const char* uri, + const void* body, + size_t bodySize, + const char* answerHeaders, + void* payload) + { + if (successCallable != NULL) + { + Deprecated::IWebService::HttpHeaders headers; + + // TODO - Parse "answerHeaders" + //printf("TODO: parse headers [%s]\n", answerHeaders); + + reinterpret_cast*>(successCallable)-> + Apply(Deprecated::IWebService::HttpRequestSuccessMessage(uri, body, bodySize, headers, + reinterpret_cast(payload))); + } + } + +#ifdef __cplusplus +} +#endif + + + +namespace Deprecated +{ + OrthancStone::MessageBroker* WasmWebService::broker_ = NULL; + + void ToJsonString(std::string& output, const IWebService::HttpHeaders& headers) + { + Json::Value jsonHeaders; + for (IWebService::HttpHeaders::const_iterator it = headers.begin(); it != headers.end(); it++ ) + { + jsonHeaders[it->first] = it->second; + } + + Json::StreamWriterBuilder builder; + std::unique_ptr writer(builder.newStreamWriter()); + std::ostringstream outputStr; + + writer->write(jsonHeaders, &outputStr); + output = outputStr.str(); + } + + void WasmWebService::PostAsync(const std::string& relativeUri, + const HttpHeaders& headers, + const std::string& body, + Orthanc::IDynamicObject* payload, + OrthancStone::MessageHandler* successCallable, + OrthancStone::MessageHandler* failureCallable, + unsigned int timeoutInSeconds) + { + std::string headersInJsonString; + ToJsonString(headersInJsonString, headers); + WasmWebService_PostAsync(successCallable, failureCallable, relativeUri.c_str(), headersInJsonString.c_str(), + body.c_str(), body.size(), payload, timeoutInSeconds); + } + + void WasmWebService::DeleteAsync(const std::string& relativeUri, + const HttpHeaders& headers, + Orthanc::IDynamicObject* payload, + OrthancStone::MessageHandler* successCallable, + OrthancStone::MessageHandler* failureCallable, + unsigned int timeoutInSeconds) + { + std::string headersInJsonString; + ToJsonString(headersInJsonString, headers); + WasmWebService_DeleteAsync(successCallable, failureCallable, relativeUri.c_str(), headersInJsonString.c_str(), + payload, timeoutInSeconds); + } + + void WasmWebService::GetAsyncInternal(const std::string &relativeUri, + const HttpHeaders &headers, + Orthanc::IDynamicObject *payload, + OrthancStone::MessageHandler *successCallable, + OrthancStone::MessageHandler *failureCallable, + unsigned int timeoutInSeconds) + { + std::string headersInJsonString; + ToJsonString(headersInJsonString, headers); + WasmWebService_GetAsync(successCallable, failureCallable, relativeUri.c_str(), + headersInJsonString.c_str(), payload, timeoutInSeconds); + } + + void WasmWebService::NotifyHttpSuccessLater(boost::shared_ptr cachedMessage, + Orthanc::IDynamicObject* payload, // takes ownership + OrthancStone::MessageHandler* successCallback) + { + CachedSuccessNotification* notification = new CachedSuccessNotification(); // allocated on the heap, it will be passed to JS and deleted when coming back to C++ + notification->cachedMessage = cachedMessage; + notification->payload.reset(payload); + notification->successCallback = successCallback; + + WasmWebService_ScheduleLaterCachedSuccessNotification(notification); + } + +} diff -r ff8d2e46ac63 -r 419d0320c344 Deprecated/Platforms/Wasm/WasmWebService.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Platforms/Wasm/WasmWebService.h Wed Apr 29 20:45:14 2020 +0200 @@ -0,0 +1,62 @@ +#pragma once + +#include "../../Framework/Deprecated/Toolbox/BaseWebService.h" +#include + +namespace Deprecated +{ +class WasmWebService : public BaseWebService +{ +private: + static OrthancStone::MessageBroker *broker_; + + // Private constructor => Singleton design pattern + WasmWebService(OrthancStone::MessageBroker &broker) : BaseWebService(broker) + { + } + +public: + static WasmWebService &GetInstance() + { + if (broker_ == NULL) + { + printf("WasmWebService::GetInstance(): broker not initialized\n"); + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); + } + static WasmWebService instance(*broker_); + return instance; + } + + static void SetBroker(OrthancStone::MessageBroker &broker) + { + broker_ = &broker; + } + + virtual void PostAsync(const std::string &uri, + const HttpHeaders &headers, + const std::string &body, + Orthanc::IDynamicObject *payload, + OrthancStone::MessageHandler *successCallable, + OrthancStone::MessageHandler *failureCallable = NULL, + unsigned int timeoutInSeconds = 60); + + virtual void DeleteAsync(const std::string &uri, + const HttpHeaders &headers, + Orthanc::IDynamicObject *payload, + OrthancStone::MessageHandler *successCallable, + OrthancStone::MessageHandler *failureCallable = NULL, + unsigned int timeoutInSeconds = 60); + +protected: + virtual void GetAsyncInternal(const std::string &uri, + const HttpHeaders &headers, + Orthanc::IDynamicObject *payload, + OrthancStone::MessageHandler *successCallable, + OrthancStone::MessageHandler *failureCallable = NULL, + unsigned int timeoutInSeconds = 60); + + virtual void NotifyHttpSuccessLater(boost::shared_ptr cachedHttpMessage, + Orthanc::IDynamicObject *payload, // takes ownership + OrthancStone::MessageHandler *successCallback); +}; +} // namespace Deprecated diff -r ff8d2e46ac63 -r 419d0320c344 Deprecated/Platforms/Wasm/WasmWebService.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Platforms/Wasm/WasmWebService.js Wed Apr 29 20:45:14 2020 +0200 @@ -0,0 +1,108 @@ +mergeInto(LibraryManager.library, { + WasmWebService_GetAsync: function(callableSuccess, callableFailure, url, headersInJsonString, payload, timeoutInSeconds) { + // Directly use XMLHttpRequest (no jQuery) to retrieve the raw binary data + // http://www.henryalgus.com/reading-binary-files-using-jquery-ajax/ + var xhr = new XMLHttpRequest(); + var url_ = UTF8ToString(url); + var headersInJsonString_ = UTF8ToString(headersInJsonString); + + xhr.open('GET', url_, true); + xhr.responseType = 'arraybuffer'; + xhr.timeout = timeoutInSeconds * 1000; + var headers = JSON.parse(headersInJsonString_); + for (var key in headers) { + xhr.setRequestHeader(key, headers[key]); + } + //console.log(xhr); + xhr.onreadystatechange = function() { + if (this.readyState == XMLHttpRequest.DONE) { + if (xhr.status === 200) { + var s = xhr.getAllResponseHeaders(); + var headers = _malloc(s.length + 1); + stringToUTF8(s, headers, s.length + 1); + + // TODO - Is "new Uint8Array()" necessary? This copies the + // answer to the WebAssembly stack, hence necessitating + // increasing the TOTAL_STACK parameter of Emscripten + window.WasmWebService_NotifySuccess(callableSuccess, url_, new Uint8Array(this.response), + this.response.byteLength, headers, payload); + } else { + window.WasmWebService_NotifyError(callableFailure, url_, xhr.status, payload); + } + } + } + + xhr.send(); + }, + + WasmWebService_ScheduleLaterCachedSuccessNotification: function (brol) { + setTimeout(function() { + window.WasmWebService_NotifyCachedSuccess(brol); + }, 0); + }, + + WasmWebService_PostAsync: function(callableSuccess, callableFailure, url, headersInJsonString, body, bodySize, payload, timeoutInSeconds) { + var xhr = new XMLHttpRequest(); + var url_ = UTF8ToString(url); + var headersInJsonString_ = UTF8ToString(headersInJsonString); + xhr.open('POST', url_, true); + xhr.timeout = timeoutInSeconds * 1000; + xhr.responseType = 'arraybuffer'; + xhr.setRequestHeader('Content-type', 'application/octet-stream'); + + var headers = JSON.parse(headersInJsonString_); + for (var key in headers) { + xhr.setRequestHeader(key, headers[key]); + } + + xhr.onreadystatechange = function() { + if (this.readyState == XMLHttpRequest.DONE) { + if (xhr.status === 200) { + var s = xhr.getAllResponseHeaders(); + var headers = _malloc(s.length + 1); + stringToUTF8(s, headers, s.length + 1); + + window.WasmWebService_NotifySuccess(callableSuccess, url_, new Uint8Array(this.response), + this.response.byteLength, headers, payload); + } else { + window.WasmWebService_NotifyError(callableFailure, url_, xhr.status, payload); + } + } + } + + xhr.send(new Uint8ClampedArray(HEAPU8.buffer, body, bodySize)); + }, + + WasmWebService_DeleteAsync: function(callableSuccess, callableFailure, url, headersInJsonString, payload, timeoutInSeconds) { + var xhr = new XMLHttpRequest(); + var url_ = UTF8ToString(url); + var headersInJsonString_ = UTF8ToString(headersInJsonString); + xhr.open('DELETE', url_, true); + xhr.timeout = timeoutInSeconds * 1000; + xhr.responseType = 'arraybuffer'; + xhr.setRequestHeader('Content-type', 'application/octet-stream'); + + var headers = JSON.parse(headersInJsonString_); + for (var key in headers) { + xhr.setRequestHeader(key, headers[key]); + } + + xhr.onreadystatechange = function() { + if (this.readyState == XMLHttpRequest.DONE) { + if (xhr.status === 200) { + var s = xhr.getAllResponseHeaders(); + var headers = _malloc(s.length + 1); + stringToUTF8(s, headers, s.length + 1); + + window.WasmWebService_NotifySuccess(callableSuccess, url_, new Uint8Array(this.response), + this.response.byteLength, headers, payload); + } else { + window.WasmWebService_NotifyError(callableFailure, url_, xhr.status, payload); + } + } + } + + xhr.send(); + } + +}); diff -r ff8d2e46ac63 -r 419d0320c344 Deprecated/Platforms/Wasm/default-library.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Platforms/Wasm/default-library.js Wed Apr 29 20:45:14 2020 +0200 @@ -0,0 +1,49 @@ +// this file contains the JS method you want to expose to C++ code + +mergeInto(LibraryManager.library, { + + ScheduleWebViewportRedrawFromCpp: function(cppViewportHandle) { + window.ScheduleWebViewportRedraw(cppViewportHandle); + }, + + CreateWasmViewportFromCpp: function(htmlCanvasId) { + return window.CreateWasmViewport(htmlCanvasId); + }, + + // each time the StoneApplication updates its status, it may signal it + // through this method. i.e, to change the status of a button in the web interface + UpdateStoneApplicationStatusFromCppWithString: function(statusUpdateMessage) { + var statusUpdateMessage_ = UTF8ToString(statusUpdateMessage); + window.UpdateWebApplicationWithString(statusUpdateMessage_); + }, + + // same, but with a serialized message + UpdateStoneApplicationStatusFromCppWithSerializedMessage: function(statusUpdateMessage) { + var statusUpdateMessage_ = UTF8ToString(statusUpdateMessage); + window.UpdateWebApplicationWithSerializedMessage(statusUpdateMessage_); + }, + + // These functions are called from C++ (through an extern declaration) + // and call the standard logger that, here, routes to the console. + + stone_console_error : function(message) { + var text = UTF8ToString(message); + window.errorFromCpp(text); + }, + + stone_console_warning : function(message) { + var text = UTF8ToString(message); + window.warningFromCpp(text); + }, + + stone_console_info: function(message) { + var text = UTF8ToString(message); + window.infoFromCpp(text); + }, + + stone_console_trace : function(message) { + var text = UTF8ToString(message); + window.debugFromCpp(text); + } + +}); diff -r ff8d2e46ac63 -r 419d0320c344 Deprecated/Platforms/Wasm/logger.ts --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Platforms/Wasm/logger.ts Wed Apr 29 20:45:14 2020 +0200 @@ -0,0 +1,111 @@ +export enum LogSource { + Cpp, + Typescript +} + +export class StandardConsoleLogger { + public showSource: boolean = true; + + public debug(...args: any[]): void { + this._debug(LogSource.Typescript, ...args); + } + + public debugFromCpp(...args: any[]): void { + this._debug(LogSource.Cpp, ...args); + } + + public info(...args: any[]): void { + this._info(LogSource.Typescript, ...args); + } + + public infoFromCpp(message: string): void { + this._info(LogSource.Cpp, message); + } + + public warning(...args: any[]): void { + this._warning(LogSource.Typescript, ...args); + } + + public warningFromCpp(message: string): void { + this._warning(LogSource.Cpp, message); + } + + public error(...args: any[]): void { + this._error(LogSource.Typescript, ...args); + } + + public errorFromCpp(message: string): void { + this._error(LogSource.Cpp, message); + } + + public _debug(source: LogSource, ...args: any[]): void { + if (( window).IsTraceLevelEnabled) + { + if (( window).IsTraceLevelEnabled()) + { + var output = this.getOutput(source, args); + console.debug(...output); + } + } + } + + private _info(source: LogSource, ...args: any[]): void { + if (( window).IsInfoLevelEnabled) + { + if (( window).IsInfoLevelEnabled()) + { + var output = this.getOutput(source, args); + console.info(...output); + } + } + } + + public _warning(source: LogSource, ...args: any[]): void { + var output = this.getOutput(source, args); + console.warn(...output); + } + + public _error(source: LogSource, ...args: any[]): void { + var output = this.getOutput(source, args); + console.error(...output); + } + + + private getOutput(source: LogSource, args: any[]): any[] { + var prefix = this.getPrefix(); + var prefixAndSource = Array(); + + if (prefix != null) { + prefixAndSource = [prefix]; + } + + if (this.showSource) { + if (source == LogSource.Typescript) { + prefixAndSource = [...prefixAndSource, "TS "]; + } else if (source == LogSource.Cpp) { + prefixAndSource = [...prefixAndSource, "C++"]; + } + } + + if (prefixAndSource.length > 0) { + prefixAndSource = [...prefixAndSource, "|"]; + } + + return [...prefixAndSource, ...args]; + } + + protected getPrefix(): string | null { + return null; + } +} + +export class TimeConsoleLogger extends StandardConsoleLogger { + protected getPrefix(): string { + let now = new Date(); + let timeString = now.getHours().toString().padStart(2, "0") + ":" + now.getMinutes().toString().padStart(2, "0") + ":" + now.getSeconds().toString().padStart(2, "0") + "." + now.getMilliseconds().toString().padStart(3, "0"); + return timeString; + } +} + +export var defaultLogger: StandardConsoleLogger = new TimeConsoleLogger(); + diff -r ff8d2e46ac63 -r 419d0320c344 Deprecated/Platforms/Wasm/stone-framework-loader.ts --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Platforms/Wasm/stone-framework-loader.ts Wed Apr 29 20:45:14 2020 +0200 @@ -0,0 +1,95 @@ +/** + * This file contains primitives to interface with WebAssembly and + * with the Stone framework. + **/ +import * as Logger from './logger' + +export declare type InitializationCallback = () => void; + +//export declare var StoneFrameworkModule : any; +export var StoneFrameworkModule : any; + +//const ASSETS_FOLDER : string = "assets/lib"; +//const WASM_FILENAME : string = "orthanc-framework"; + +export class Framework +{ + private static singleton_ : Framework = null; + private static wasmModuleName_ : string = null; + + public static Configure(wasmModuleName: string) { + this.wasmModuleName_ = wasmModuleName; + } + + private constructor(verbose : boolean) + { + //this.ccall('Initialize', null, [ 'number' ], [ verbose ]); + } + + + public ccall( name: string, + returnType: string, + argTypes: Array, + argValues: Array) : any + { + return ( window).StoneFrameworkModule.ccall(name, returnType, argTypes, argValues); + } + + + public cwrap( name: string, + returnType: string, + argTypes: Array) : any + { + return ( window).StoneFrameworkModule.cwrap(name, returnType, argTypes); + } + + + public static GetInstance() : Framework + { + if (Framework.singleton_ == null) { + throw new Error('The WebAssembly module is not loaded yet'); + } else { + return Framework.singleton_; + } + } + + + public static Initialize( verbose: boolean, + callback: InitializationCallback) + { + Logger.defaultLogger.debug('Initializing WebAssembly Module'); + + ( window).errorFromCpp = function(text:any) { Logger.defaultLogger.errorFromCpp(text); }; + ( window).warningFromCpp = function(text:any) { Logger.defaultLogger.warningFromCpp(text); }; + ( window).infoFromCpp = function(text:any) { Logger.defaultLogger.infoFromCpp(text); }; + ( window).debugFromCpp = function(text:any) { Logger.defaultLogger.debugFromCpp(text); }; + + // ( window). + ( window).StoneFrameworkModule = { + preRun: [ + function() { + Logger.defaultLogger.debug('Loading the Stone Framework using WebAssembly'); + } + ], + postRun: [ + function() { + // This function is called by ".js" wrapper once the ".wasm" + // WebAssembly module has been loaded and compiled by the + // browser + Logger.defaultLogger.debug('WebAssembly is ready'); + Framework.singleton_ = new Framework(verbose); + callback(); + } + ], + totalDependencies: 0 + }; + + // Dynamic loading of the JavaScript wrapper around WebAssembly + var script = document.createElement('script'); + script.type = 'application/javascript'; + //script.src = "orthanc-stone.js"; // ASSETS_FOLDER + '/' + WASM_FILENAME + '.js'; + script.src = this.wasmModuleName_ + ".js";// "OrthancStoneSimpleViewer.js"; // ASSETS_FOLDER + '/' + WASM_FILENAME + '.js'; + script.async = true; + document.head.appendChild(script); + } +} diff -r ff8d2e46ac63 -r 419d0320c344 Deprecated/Platforms/Wasm/tsconfig-stone.json --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Platforms/Wasm/tsconfig-stone.json Wed Apr 29 20:45:14 2020 +0200 @@ -0,0 +1,8 @@ +{ + "include" : [ + "stone-framework-loader.ts", + "logger.ts", + "wasm-application-runner.ts", + "wasm-viewport.ts" + ] +} diff -r ff8d2e46ac63 -r 419d0320c344 Deprecated/Platforms/Wasm/wasm-application-runner.ts --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Platforms/Wasm/wasm-application-runner.ts Wed Apr 29 20:45:14 2020 +0200 @@ -0,0 +1,141 @@ +import * as Stone from './stone-framework-loader' +import * as StoneViewport from './wasm-viewport' +import * as Logger from './logger' + +if (!('WebAssembly' in window)) { + alert('Sorry, your browser does not support WebAssembly :('); +} + +//var StoneFrameworkModule : Stone.Framework = (window).StoneFrameworkModule; +//export declare var StoneFrameworkModule : Stone.Framework; + +// global functions +var WasmWebService_NotifyError: Function = null; +var WasmWebService_NotifySuccess: Function = null; +var WasmWebService_NotifyCachedSuccess: Function = null; +var WasmDelayedCallExecutor_ExecuteCallback: Function = null; +var WasmDoAnimation: Function = null; +var SetStartupParameter: Function = null; +var CreateWasmApplication: Function = null; +export var CreateCppViewport: Function = null; +var ReleaseCppViewport: Function = null; +var StartWasmApplication: Function = null; +export var SendSerializedMessageToStoneApplication: Function = null; + +var auxiliaryParameters : Map = null; + +export function SetApplicationParameters(params : Map) { + if (auxiliaryParameters != null) { + console.warn("wasm-application-runner.SetApplicationParameters: about to overwrite the existing application parameters!") + } + auxiliaryParameters = params; +} + +function DoAnimationThread() { + if (WasmDoAnimation != null) { + WasmDoAnimation(); + } + + // Update the viewport content every 100ms if need be + setTimeout(DoAnimationThread, 100); +} + + +function GetUriParameters(): Map { + var parameters = window.location.search.substr(1); + + if (parameters != null && + parameters != '') { + var result = new Map(); + var tokens = parameters.split('&'); + + for (var i = 0; i < tokens.length; i++) { + var tmp = tokens[i].split('='); + if (tmp.length == 2) { + result[tmp[0]] = decodeURIComponent(tmp[1]); + } else if(tmp.length == 1) { + // if there is no '=', we treat ot afterwards as a flag-style param + result[tmp[0]] = ""; + } + } + return result; + } + else { + return new Map(); + } +} + +// function UpdateWebApplication(statusUpdateMessage: string) { +// console.log(statusUpdateMessage); +// } + +function _InitializeWasmApplication(orthancBaseUrl: string): void { + + CreateWasmApplication(); + + // transmit the API-specified parameters to the app before initializing it + for (let key in auxiliaryParameters) { + if (auxiliaryParameters.hasOwnProperty(key)) { + Logger.defaultLogger.debug( + `About to call SetStartupParameter("${key}","${auxiliaryParameters[key]}")`); + SetStartupParameter(key, auxiliaryParameters[key]); + } + } + + // parse uri and transmit the URI parameters to the app before initializing it + let parameters = GetUriParameters(); + + for (let key in parameters) { + if (parameters.hasOwnProperty(key)) { + Logger.defaultLogger.debug( + `About to call SetStartupParameter("${key}","${parameters[key]}")`); + SetStartupParameter(key, parameters[key]); + } + } + + StartWasmApplication(orthancBaseUrl); + + // trigger a first resize of the canvas that has just been initialized + StoneViewport.WasmViewport.ResizeAll(); + + DoAnimationThread(); +} + +export function InitializeWasmApplication(wasmModuleName: string, orthancBaseUrl: string) { + + Stone.Framework.Configure(wasmModuleName); + + // Wait for the Orthanc Framework to be initialized (this initializes + // the WebAssembly environment) and then, create and initialize the Wasm application + Stone.Framework.Initialize(true, function () { + + Logger.defaultLogger.debug("Connecting C++ methods to JS methods"); + + SetStartupParameter = ( window).StoneFrameworkModule.cwrap('SetStartupParameter', null, ['string', 'string']); + CreateWasmApplication = ( window).StoneFrameworkModule.cwrap('CreateWasmApplication', null, ['number']); + CreateCppViewport = ( window).StoneFrameworkModule.cwrap('CreateCppViewport', 'number', []); + ReleaseCppViewport = ( window).StoneFrameworkModule.cwrap('ReleaseCppViewport', null, ['number']); + StartWasmApplication = ( window).StoneFrameworkModule.cwrap('StartWasmApplication', null, ['string']); + ( window).IsTraceLevelEnabled = ( window).StoneFrameworkModule.cwrap('WasmIsTraceLevelEnabled', 'boolean', null); + ( window).IsInfoLevelEnabled = ( window).StoneFrameworkModule.cwrap('WasmIsInfoLevelEnabled', 'boolean', null); + + ( window).WasmWebService_NotifyCachedSuccess = ( window).StoneFrameworkModule.cwrap('WasmWebService_NotifyCachedSuccess', null, ['number']); + ( window).WasmWebService_NotifySuccess = ( window).StoneFrameworkModule.cwrap('WasmWebService_NotifySuccess', null, ['number', 'string', 'array', 'number', 'number']); + ( window).WasmWebService_NotifyError = ( window).StoneFrameworkModule.cwrap('WasmWebService_NotifyError', null, ['number', 'string', 'number', 'number']); + ( window).WasmDelayedCallExecutor_ExecuteCallback = ( window).StoneFrameworkModule.cwrap('WasmDelayedCallExecutor_ExecuteCallback', null, ['number']); + // no need to put this into the globals for it's only used in this very module + WasmDoAnimation = ( window).StoneFrameworkModule.cwrap('WasmDoAnimation', null, []); + + SendSerializedMessageToStoneApplication = ( window).StoneFrameworkModule.cwrap('SendSerializedMessageToStoneApplication', 'string', ['string']); + + Logger.defaultLogger.debug("Connecting C++ methods to JS methods - done"); + + _InitializeWasmApplication(orthancBaseUrl); + }); +} + + +// exports.InitializeWasmApplication = InitializeWasmApplication; + + + diff -r ff8d2e46ac63 -r 419d0320c344 Deprecated/Platforms/Wasm/wasm-viewport.ts --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Deprecated/Platforms/Wasm/wasm-viewport.ts Wed Apr 29 20:45:14 2020 +0200 @@ -0,0 +1,359 @@ +import * as wasmApplicationRunner from './wasm-application-runner' +import * as Logger from './logger' + +var isPendingRedraw = false; + +function ScheduleWebViewportRedraw(cppViewportHandle: any) : void +{ + if (!isPendingRedraw) { + isPendingRedraw = true; + Logger.defaultLogger.debug('Scheduling a refresh of the viewport, as its content changed'); + window.requestAnimationFrame(function() { + isPendingRedraw = false; + let viewport = WasmViewport.GetFromCppViewport(cppViewportHandle); + if (viewport) { + viewport.Redraw(); + } + }); + } +} + +(window).ScheduleWebViewportRedraw = ScheduleWebViewportRedraw; + +declare function UTF8ToString(v: any): string; + +function CreateWasmViewport(htmlCanvasId: string) : any { + var cppViewportHandle = wasmApplicationRunner.CreateCppViewport(); + var canvasId = UTF8ToString(htmlCanvasId); + var webViewport = new WasmViewport(( window).StoneFrameworkModule, canvasId, cppViewportHandle); // viewports are stored in a static map in WasmViewport -> won't be deleted + webViewport.Initialize(); + + return cppViewportHandle; +} + +(window).CreateWasmViewport = CreateWasmViewport; + +export class WasmViewport { + + private static viewportsMapByCppHandle_ : Map = new Map(); // key = the C++ handle + private static viewportsMapByCanvasId_ : Map = new Map(); // key = the canvasId + + private module_ : any; + private canvasId_ : string; + private htmlCanvas_ : HTMLCanvasElement; + private context_ : CanvasRenderingContext2D | null; + private imageData_ : any = null; + private renderingBuffer_ : any = null; + + private touchGestureInProgress_: boolean = false; + private touchCount_: number = 0; + private touchGestureLastCoordinates_: [number, number][] = []; // last x,y coordinates of each touch + + private touchZoom_ : any = false; + private touchTranslation_ : any = false; + + private ViewportSetSize : Function; + private ViewportRender : Function; + private ViewportMouseDown : Function; + private ViewportMouseMove : Function; + private ViewportMouseUp : Function; + private ViewportMouseEnter : Function; + private ViewportMouseLeave : Function; + private ViewportMouseWheel : Function; + private ViewportKeyPressed : Function; + private ViewportTouchStart : Function; + private ViewportTouchMove : Function; + private ViewportTouchEnd : Function; + + private pimpl_ : any; // Private pointer to the underlying WebAssembly C++ object + + public constructor(module: any, canvasId: string, cppViewport: any) { + + this.pimpl_ = cppViewport; + WasmViewport.viewportsMapByCppHandle_[this.pimpl_] = this; + WasmViewport.viewportsMapByCanvasId_[canvasId] = this; + + this.module_ = module; + this.canvasId_ = canvasId; + this.htmlCanvas_ = document.getElementById(this.canvasId_) as HTMLCanvasElement; + if (this.htmlCanvas_ == null) { + Logger.defaultLogger.error("Can not create WasmViewport, did not find the canvas whose id is '", this.canvasId_, "'"); + } + this.context_ = this.htmlCanvas_.getContext('2d'); + + this.ViewportSetSize = this.module_.cwrap('ViewportSetSize', null, [ 'number', 'number', 'number' ]); + this.ViewportRender = this.module_.cwrap('ViewportRender', null, [ 'number', 'number', 'number', 'number' ]); + this.ViewportMouseDown = this.module_.cwrap('ViewportMouseDown', null, [ 'number', 'number', 'number', 'number', 'number' ]); + this.ViewportMouseMove = this.module_.cwrap('ViewportMouseMove', null, [ 'number', 'number', 'number' ]); + this.ViewportMouseUp = this.module_.cwrap('ViewportMouseUp', null, [ 'number' ]); + this.ViewportMouseEnter = this.module_.cwrap('ViewportMouseEnter', null, [ 'number' ]); + this.ViewportMouseLeave = this.module_.cwrap('ViewportMouseLeave', null, [ 'number' ]); + this.ViewportMouseWheel = this.module_.cwrap('ViewportMouseWheel', null, [ 'number', 'number', 'number', 'number', 'number' ]); + this.ViewportKeyPressed = this.module_.cwrap('ViewportKeyPressed', null, [ 'number', 'number', 'string', 'number', 'number' ]); + this.ViewportTouchStart = this.module_.cwrap('ViewportTouchStart', null, [ 'number', 'number', 'number', 'number', 'number', 'number', 'number' ]); + this.ViewportTouchMove = this.module_.cwrap('ViewportTouchMove', null, [ 'number', 'number', 'number', 'number', 'number', 'number', 'number' ]); + this.ViewportTouchEnd = this.module_.cwrap('ViewportTouchEnd', null, [ 'number', 'number', 'number', 'number', 'number', 'number', 'number' ]); + } + + public GetCppViewport() : number { + return this.pimpl_; + } + + public static GetFromCppViewport(cppViewportHandle: number) : WasmViewport | null { + if (WasmViewport.viewportsMapByCppHandle_[cppViewportHandle] !== undefined) { + return WasmViewport.viewportsMapByCppHandle_[cppViewportHandle]; + } + Logger.defaultLogger.error("WasmViewport not found !"); + return null; + } + + public static GetFromCanvasId(canvasId: string) : WasmViewport | null { + if (WasmViewport.viewportsMapByCanvasId_[canvasId] !== undefined) { + return WasmViewport.viewportsMapByCanvasId_[canvasId]; + } + Logger.defaultLogger.error("WasmViewport not found !"); + return null; + } + + public static ResizeAll() { + for (let canvasId in WasmViewport.viewportsMapByCanvasId_) { + WasmViewport.viewportsMapByCanvasId_[canvasId].Resize(); + } + } + + public Redraw() { + if (this.imageData_ === null || + this.renderingBuffer_ === null || + this.ViewportRender(this.pimpl_, + this.imageData_.width, + this.imageData_.height, + this.renderingBuffer_) == 0) { + Logger.defaultLogger.error('The rendering has failed'); + } else { + // Create an accessor to the rendering buffer (i.e. create a + // "window" above the heap of the WASM module), then copy it to + // the ImageData object + this.imageData_.data.set(new Uint8ClampedArray( + this.module_.HEAPU8.buffer, + this.renderingBuffer_, + this.imageData_.width * this.imageData_.height * 4)); + + if (this.context_) { + this.context_.putImageData(this.imageData_, 0, 0); + } + } + } + + public Resize() { + if (this.imageData_ != null && + (this.imageData_.width != window.innerWidth || + this.imageData_.height != window.innerHeight)) { + this.imageData_ = null; + } + + // width/height is defined by the parent width/height + if (this.htmlCanvas_.parentElement) { + this.htmlCanvas_.width = this.htmlCanvas_.parentElement.offsetWidth; + this.htmlCanvas_.height = this.htmlCanvas_.parentElement.offsetHeight; + + Logger.defaultLogger.debug("resizing WasmViewport: ", this.htmlCanvas_.width, "x", this.htmlCanvas_.height); + + if (this.imageData_ === null && this.context_) { + this.imageData_ = this.context_.getImageData(0, 0, this.htmlCanvas_.width, this.htmlCanvas_.height); + this.ViewportSetSize(this.pimpl_, this.htmlCanvas_.width, this.htmlCanvas_.height); + + if (this.renderingBuffer_ != null) { + this.module_._free(this.renderingBuffer_); + } + + this.renderingBuffer_ = this.module_._malloc(this.imageData_.width * this.imageData_.height * 4); + } else { + this.ViewportSetSize(this.pimpl_, this.htmlCanvas_.width, this.htmlCanvas_.height); + } + + this.Redraw(); + } + } + + public Initialize() { + + // Force the rendering of the viewport for the first time + this.Resize(); + + var that : WasmViewport = this; + // Register an event listener to call the Resize() function + // each time the window is resized. + window.addEventListener('resize', function(event) { + that.Resize(); + }, false); + + this.htmlCanvas_.addEventListener('contextmenu', function(event) { + // Prevent right click on the canvas + event.preventDefault(); + }, false); + + this.htmlCanvas_.addEventListener('mouseleave', function(event) { + that.ViewportMouseLeave(that.pimpl_); + }); + + this.htmlCanvas_.addEventListener('mouseenter', function(event) { + that.ViewportMouseEnter(that.pimpl_); + }); + + this.htmlCanvas_.addEventListener('mousedown', function(event) { + var x = event.pageX - this.offsetLeft; + var y = event.pageY - this.offsetTop; + + that.ViewportMouseDown(that.pimpl_, event.button, x, y, 0 /* TODO detect modifier keys*/); + }); + + this.htmlCanvas_.addEventListener('mousemove', function(event) { + var x = event.pageX - this.offsetLeft; + var y = event.pageY - this.offsetTop; + that.ViewportMouseMove(that.pimpl_, x, y); + }); + + this.htmlCanvas_.addEventListener('mouseup', function(event) { + that.ViewportMouseUp(that.pimpl_); + }); + + window.addEventListener('keydown', function(event) { + var keyChar: string | null = event.key; + var keyCode = event.keyCode + if (keyChar.length == 1) { + keyCode = 0; // maps to OrthancStone::KeyboardKeys_Generic + } else { + keyChar = null; + } +// console.log("key: ", keyCode, keyChar); + that.ViewportKeyPressed(that.pimpl_, keyCode, keyChar, event.shiftKey, event.ctrlKey, event.altKey); + }); + + this.htmlCanvas_.addEventListener('wheel', function(event) { + var x = event.pageX - this.offsetLeft; + var y = event.pageY - this.offsetTop; + that.ViewportMouseWheel(that.pimpl_, event.deltaY, x, y, event.ctrlKey); + event.preventDefault(); + }, {passive: false}); // must not be passive if calling event.preventDefault, ie to cancel scroll or zoom of the whole interface + + this.htmlCanvas_.addEventListener('touchstart', function(event: TouchEvent) { + // don't propagate events to the whole body (this could zoom the entire page instead of zooming the viewport) + event.preventDefault(); + event.stopPropagation(); + + // TODO: find a way to pass the coordinates as an array between JS and C++ + var x0 = 0; + var y0 = 0; + var x1 = 0; + var y1 = 0; + var x2 = 0; + var y2 = 0; + if (event.targetTouches.length > 0) { + x0 = event.targetTouches[0].pageX; + y0 = event.targetTouches[0].pageY; + } + if (event.targetTouches.length > 1) { + x1 = event.targetTouches[1].pageX; + y1 = event.targetTouches[1].pageY; + } + if (event.targetTouches.length > 2) { + x2 = event.targetTouches[2].pageX; + y2 = event.targetTouches[2].pageY; + } + + that.ViewportTouchStart(that.pimpl_, event.targetTouches.length, x0, y0, x1, y1, x2, y2); + }, {passive: false}); // must not be passive if calling event.preventDefault, ie to cancel scroll or zoom of the whole interface + + this.htmlCanvas_.addEventListener('touchend', function(event) { + // don't propagate events to the whole body (this could zoom the entire page instead of zooming the viewport) + event.preventDefault(); + event.stopPropagation(); + + // TODO: find a way to pass the coordinates as an array between JS and C++ + var x0 = 0; + var y0 = 0; + var x1 = 0; + var y1 = 0; + var x2 = 0; + var y2 = 0; + if (event.targetTouches.length > 0) { + x0 = event.targetTouches[0].pageX; + y0 = event.targetTouches[0].pageY; + } + if (event.targetTouches.length > 1) { + x1 = event.targetTouches[1].pageX; + y1 = event.targetTouches[1].pageY; + } + if (event.targetTouches.length > 2) { + x2 = event.targetTouches[2].pageX; + y2 = event.targetTouches[2].pageY; + } + + that.ViewportTouchEnd(that.pimpl_, event.targetTouches.length, x0, y0, x1, y1, x2, y2); + }); + + this.htmlCanvas_.addEventListener('touchmove', function(event: TouchEvent) { + + // don't propagate events to the whole body (this could zoom the entire page instead of zooming the viewport) + event.preventDefault(); + event.stopPropagation(); + + + // TODO: find a way to pass the coordinates as an array between JS and C++ + var x0 = 0; + var y0 = 0; + var x1 = 0; + var y1 = 0; + var x2 = 0; + var y2 = 0; + if (event.targetTouches.length > 0) { + x0 = event.targetTouches[0].pageX; + y0 = event.targetTouches[0].pageY; + } + if (event.targetTouches.length > 1) { + x1 = event.targetTouches[1].pageX; + y1 = event.targetTouches[1].pageY; + } + if (event.targetTouches.length > 2) { + x2 = event.targetTouches[2].pageX; + y2 = event.targetTouches[2].pageY; + } + + that.ViewportTouchMove(that.pimpl_, event.targetTouches.length, x0, y0, x1, y1, x2, y2); + return; + + }, {passive: false}); // must not be passive if calling event.preventDefault, ie to cancel scroll or zoom of the whole interface + } + + public ResetTouch() { + if (this.touchTranslation_ || + this.touchZoom_) { + this.ViewportMouseUp(this.pimpl_); + } + + this.touchTranslation_ = false; + this.touchZoom_ = false; + } + + public GetTouchTranslation(event: any) { + var touch = event.targetTouches[0]; + return [ + touch.pageX, + touch.pageY + ]; + } + + public GetTouchZoom(event: any) { + var touch1 = event.targetTouches[0]; + var touch2 = event.targetTouches[1]; + var dx = (touch1.pageX - touch2.pageX); + var dy = (touch1.pageY - touch2.pageY); + var d = Math.sqrt(dx * dx + dy * dy); + return [ + (touch1.pageX + touch2.pageX) / 2.0, + (touch1.pageY + touch2.pageY) / 2.0, + d + ]; + } + +} diff -r ff8d2e46ac63 -r 419d0320c344 Platforms/Generic/DelayedCallCommand.cpp --- a/Platforms/Generic/DelayedCallCommand.cpp Wed Apr 29 20:44:31 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,64 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2020 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - **/ - - -#include "DelayedCallCommand.h" -#include "boost/thread/thread.hpp" - -#include - -namespace Deprecated -{ - DelayedCallCommand::DelayedCallCommand(MessageHandler* callback, // takes ownership - unsigned int timeoutInMs, - Orthanc::IDynamicObject* payload /* takes ownership */, - OrthancStone::NativeStoneApplicationContext& context - ) : - callback_(callback), - payload_(payload), - context_(context), - expirationTimePoint_(boost::posix_time::microsec_clock::local_time() + boost::posix_time::milliseconds(timeoutInMs)), - timeoutInMs_(timeoutInMs) - { - } - - - void DelayedCallCommand::Execute() - { - while (boost::posix_time::microsec_clock::local_time() < expirationTimePoint_) - { - boost::this_thread::sleep(boost::posix_time::milliseconds(1)); - } - } - - void DelayedCallCommand::Commit() - { - // We want to make sure that, i.e, the UpdateThread is not - // triggered while we are updating the "model" with the result of - // an OracleCommand - OrthancStone::NativeStoneApplicationContext::GlobalMutexLocker lock(context_); - - if (callback_.get() != NULL) - { - IDelayedCallExecutor::TimeoutMessage message; // TODO: add payload - callback_->Apply(message); - } - } -} diff -r ff8d2e46ac63 -r 419d0320c344 Platforms/Generic/DelayedCallCommand.h --- a/Platforms/Generic/DelayedCallCommand.h Wed Apr 29 20:44:31 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,56 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2020 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - **/ - - -#pragma once - -#include "IOracleCommand.h" - -#include "../../Framework/Deprecated/Toolbox/IDelayedCallExecutor.h" -#include "../../Framework/Messages/IObservable.h" -#include "../../Framework/Messages/ICallable.h" -#include "../../Applications/Generic/NativeStoneApplicationContext.h" - -#include - -namespace Deprecated -{ - class DelayedCallCommand : public IOracleCommand, OrthancStone::IObservable - { - protected: - std::unique_ptr > callback_; - std::unique_ptr payload_; - OrthancStone::NativeStoneApplicationContext& context_; - boost::posix_time::ptime expirationTimePoint_; - unsigned int timeoutInMs_; - - public: - DelayedCallCommand(MessageHandler* callback, // takes ownership - unsigned int timeoutInMs, - Orthanc::IDynamicObject* payload /* takes ownership */, - OrthancStone::NativeStoneApplicationContext& context - ); - - virtual void Execute(); - - virtual void Commit(); - }; - -} diff -r ff8d2e46ac63 -r 419d0320c344 Platforms/Generic/IOracleCommand.h --- a/Platforms/Generic/IOracleCommand.h Wed Apr 29 20:44:31 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,42 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2020 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - **/ - - -#pragma once - -#include - -namespace Deprecated -{ - class IOracleCommand : public Orthanc::IDynamicObject - { - public: - virtual ~IOracleCommand() - { - } - - // This part of the command can be invoked simultaneously, and - // must not modify the Stone context - virtual void Execute() = 0; - - // This part of the command must be invoked in mutual exclusion - virtual void Commit() = 0; - }; -} diff -r ff8d2e46ac63 -r 419d0320c344 Platforms/Generic/Oracle.cpp --- a/Platforms/Generic/Oracle.cpp Wed Apr 29 20:44:31 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,212 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2020 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - **/ - - -#include "Oracle.h" - -#include -#include -#include - -#include -#include -#include - -namespace Deprecated -{ - class Oracle::PImpl - { - private: - enum State - { - State_Init, - State_Started, - State_Stopped - }; - - boost::mutex oracleMutex_; - State state_; - std::vector threads_; - Orthanc::SharedMessageQueue queue_; - - static void Worker(PImpl* that) - { - for (;;) - { - State state; - - { - boost::mutex::scoped_lock lock(that->oracleMutex_); - state = that->state_; - } - - if (state == State_Stopped) - { - break; - } - - std::unique_ptr item(that->queue_.Dequeue(100)); - if (item.get() != NULL) - { - IOracleCommand& command = dynamic_cast(*item); - try - { - command.Execute(); - } - catch (Orthanc::OrthancException& /*ex*/) - { - // this is probably a curl error that has been triggered. We may just ignore it. - // The command.success_ will stay at false and this will be handled in the command.Commit - } - - // Random sleeping to test - //boost::this_thread::sleep(boost::posix_time::milliseconds(50 * (1 + rand() % 10))); - - command.Commit(); - } - } - } - - public: - PImpl(unsigned int threadCount) : - state_(State_Init), - threads_(threadCount) - { - } - - ~PImpl() - { - if (state_ == State_Started) - { - LOG(ERROR) << "You should have manually called Oracle::Stop()"; - Stop(); - } - } - - Orthanc::SharedMessageQueue& GetQueue() - { - return queue_; - } - - void Submit(IOracleCommand* command) - { - std::unique_ptr protection(command); - - if (command == NULL) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); - } - - boost::mutex::scoped_lock lock(oracleMutex_); - - switch (state_) - { - case State_Init: - case State_Started: - queue_.Enqueue(protection.release()); - break; - - case State_Stopped: - LOG(ERROR) << "Cannot schedule a request to the Oracle after having " - << "called Oracle::Stop()"; - break; - - default: - throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); - } - - } - - void Start() - { - boost::mutex::scoped_lock lock(oracleMutex_); - - if (state_ != State_Init) - { - LOG(ERROR) << "Oracle::PImpl::Start: (state_ != State_Init)"; - throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); - } - - for (size_t i = 0; i < threads_.size(); i++) - { - threads_[i] = new boost::thread(Worker, this); - } - - state_ = State_Started; - } - - void Stop() - { - { - boost::mutex::scoped_lock lock(oracleMutex_); - - if (state_ != State_Started) - { - LOG(ERROR) << "Oracle::PImpl::Stop(): (state_ != State_Started)"; - throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); - } - - state_ = State_Stopped; - } - - for (size_t i = 0; i < threads_.size(); i++) - { - if (threads_[i] != NULL) - { - if (threads_[i]->joinable()) - { - threads_[i]->join(); - } - - delete threads_[i]; - } - } - } - }; - - - Oracle::Oracle(unsigned int threadCount) : - pimpl_(new PImpl(threadCount)) - { - } - - void Oracle::Start() - { - pimpl_->Start(); - } - - - void Oracle::Submit(IOracleCommand* command) - { - pimpl_->Submit(command); - } - - - void Oracle::Stop() - { - pimpl_->Stop(); - } - - - void Oracle::WaitEmpty() - { - pimpl_->GetQueue().WaitEmpty(50); - } -} diff -r ff8d2e46ac63 -r 419d0320c344 Platforms/Generic/Oracle.h --- a/Platforms/Generic/Oracle.h Wed Apr 29 20:44:31 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,48 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2020 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - **/ - - -#pragma once - -#include "IOracleCommand.h" - -#include - -namespace Deprecated -{ - class Oracle : public boost::noncopyable - { - private: - class PImpl; - - boost::shared_ptr pimpl_; - - public: - Oracle(unsigned int threadCount); - - void Start(); - - void Submit(IOracleCommand* command); - - void WaitEmpty(); // For unit tests - - void Stop(); - }; -} diff -r ff8d2e46ac63 -r 419d0320c344 Platforms/Generic/OracleDelayedCallExecutor.h --- a/Platforms/Generic/OracleDelayedCallExecutor.h Wed Apr 29 20:44:31 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,52 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2020 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - **/ - - -#pragma once - -#include "../../Framework/Deprecated/Toolbox/IDelayedCallExecutor.h" -#include "Oracle.h" -#include "../../Applications/Generic/NativeStoneApplicationContext.h" -#include "DelayedCallCommand.h" - -namespace Deprecated -{ - // The OracleTimeout executes callbacks after a delay. - class OracleDelayedCallExecutor : public IDelayedCallExecutor - { - private: - Oracle& oracle_; - OrthancStone::NativeStoneApplicationContext& context_; - - public: - OracleDelayedCallExecutor(Oracle& oracle, - OrthancStone::NativeStoneApplicationContext& context) : - oracle_(oracle), - context_(context) - { - } - - virtual void Schedule(MessageHandler* callback, - unsigned int timeoutInMs = 1000) - { - oracle_.Submit(new DelayedCallCommand(callback, timeoutInMs, NULL, context_)); - } - }; -} diff -r ff8d2e46ac63 -r 419d0320c344 Platforms/Generic/OracleWebService.cpp --- a/Platforms/Generic/OracleWebService.cpp Wed Apr 29 20:44:31 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,80 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2020 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - **/ - - -#include "OracleWebService.h" -#include "../../Framework/Deprecated/Toolbox/IWebService.h" - -namespace Deprecated -{ - - - class OracleWebService::WebServiceCachedGetCommand : public IOracleCommand, OrthancStone::IObservable - { - protected: - std::unique_ptr > successCallback_; - std::unique_ptr payload_; - boost::shared_ptr cachedMessage_; - OrthancStone::NativeStoneApplicationContext& context_; - - public: - WebServiceCachedGetCommand(MessageHandler* successCallback, // takes ownership - boost::shared_ptr cachedMessage, - Orthanc::IDynamicObject* payload /* takes ownership */, - OrthancStone::NativeStoneApplicationContext& context - ) : - successCallback_(successCallback), - payload_(payload), - cachedMessage_(cachedMessage), - context_(context) - { - } - - virtual void Execute() - { - // nothing to do, everything is in the commit - } - - virtual void Commit() - { - // We want to make sure that, i.e, the UpdateThread is not - // triggered while we are updating the "model" with the result of - // a WebServiceCommand - OrthancStone::NativeStoneApplicationContext::GlobalMutexLocker lock(context_); - - IWebService::HttpRequestSuccessMessage successMessage(cachedMessage_->GetUri(), - cachedMessage_->GetAnswer(), - cachedMessage_->GetAnswerSize(), - cachedMessage_->GetAnswerHttpHeaders(), - payload_.get()); - - successCallback_->Apply(successMessage); - } - }; - - void OracleWebService::NotifyHttpSuccessLater(boost::shared_ptr cachedMessage, - Orthanc::IDynamicObject* payload, // takes ownership - MessageHandler* successCallback) - { - oracle_.Submit(new WebServiceCachedGetCommand(successCallback, cachedMessage, payload, context_)); - } - - -} diff -r ff8d2e46ac63 -r 419d0320c344 Platforms/Generic/OracleWebService.h --- a/Platforms/Generic/OracleWebService.h Wed Apr 29 20:44:31 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,92 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2020 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - **/ - - -#pragma once - -#include "../../Framework/Deprecated/Toolbox/BaseWebService.h" -#include "Oracle.h" -#include "WebServiceGetCommand.h" -#include "WebServicePostCommand.h" -#include "WebServiceDeleteCommand.h" -#include "../../Applications/Generic/NativeStoneApplicationContext.h" - -namespace Deprecated -{ - // The OracleWebService performs HTTP requests in a native environment. - // It uses a thread pool to handle multiple HTTP requests in a same time. - // It works asynchronously to mimick the behaviour of the WebService running in a WASM environment. - class OracleWebService : public BaseWebService - { - private: - Oracle& oracle_; - OrthancStone::NativeStoneApplicationContext& context_; - Orthanc::WebServiceParameters parameters_; - - class WebServiceCachedGetCommand; - - public: - OracleWebService(Oracle& oracle, - const Orthanc::WebServiceParameters& parameters, - OrthancStone::NativeStoneApplicationContext& context) : - oracle_(oracle), - context_(context), - parameters_(parameters) - { - } - - virtual void PostAsync(const std::string& uri, - const HttpHeaders& headers, - const std::string& body, - Orthanc::IDynamicObject* payload, // takes ownership - MessageHandler* successCallback, // takes ownership - MessageHandler* failureCallback = NULL, // takes ownership - unsigned int timeoutInSeconds = 60) - { - oracle_.Submit(new WebServicePostCommand(successCallback, failureCallback, parameters_, uri, headers, timeoutInSeconds, body, payload, context_)); - } - - virtual void DeleteAsync(const std::string& uri, - const HttpHeaders& headers, - Orthanc::IDynamicObject* payload, - MessageHandler* successCallback, - MessageHandler* failureCallback = NULL, - unsigned int timeoutInSeconds = 60) - { - oracle_.Submit(new WebServiceDeleteCommand(successCallback, failureCallback, parameters_, uri, headers, timeoutInSeconds, payload, context_)); - } - - protected: - virtual void GetAsyncInternal(const std::string& uri, - const HttpHeaders& headers, - Orthanc::IDynamicObject* payload, // takes ownership - MessageHandler* successCallback, // takes ownership - MessageHandler* failureCallback = NULL,// takes ownership - unsigned int timeoutInSeconds = 60) - { - oracle_.Submit(new WebServiceGetCommand(successCallback, failureCallback, parameters_, uri, headers, timeoutInSeconds, payload, context_)); - } - - virtual void NotifyHttpSuccessLater(boost::shared_ptr cachedHttpMessage, - Orthanc::IDynamicObject* payload, // takes ownership - MessageHandler* successCallback); - - }; -} diff -r ff8d2e46ac63 -r 419d0320c344 Platforms/Generic/WebServiceCommandBase.cpp --- a/Platforms/Generic/WebServiceCommandBase.cpp Wed Apr 29 20:44:31 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,69 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2020 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - **/ - - -#include "WebServiceCommandBase.h" - -#include - -namespace Deprecated -{ - WebServiceCommandBase::WebServiceCommandBase(MessageHandler* successCallback, - MessageHandler* failureCallback, - const Orthanc::WebServiceParameters& parameters, - const std::string& url, - const IWebService::HttpHeaders& headers, - unsigned int timeoutInSeconds, - Orthanc::IDynamicObject* payload /* takes ownership */, - OrthancStone::NativeStoneApplicationContext& context) : - successCallback_(successCallback), - failureCallback_(failureCallback), - parameters_(parameters), - url_(url), - headers_(headers), - payload_(payload), - success_(false), - httpStatus_(Orthanc::HttpStatus_None), - context_(context), - timeoutInSeconds_(timeoutInSeconds) - { - } - - - void WebServiceCommandBase::Commit() - { - // We want to make sure that, i.e, the UpdateThread is not - // triggered while we are updating the "model" with the result of - // a WebServiceCommand - OrthancStone::NativeStoneApplicationContext::GlobalMutexLocker lock(context_); - - if (success_ && successCallback_.get() != NULL) - { - IWebService::HttpRequestSuccessMessage message - (url_, answer_.c_str(), answer_.size(), answerHeaders_, payload_.get()); - successCallback_->Apply(message); - } - else if (!success_ && failureCallback_.get() != NULL) - { - IWebService::HttpRequestErrorMessage message(url_, httpStatus_, payload_.get()); - failureCallback_->Apply(message); - } - } -} diff -r ff8d2e46ac63 -r 419d0320c344 Platforms/Generic/WebServiceCommandBase.h --- a/Platforms/Generic/WebServiceCommandBase.h Wed Apr 29 20:44:31 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,69 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2020 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - **/ - - -#pragma once - -#include "IOracleCommand.h" - -#include "../../Framework/Deprecated/Toolbox/IWebService.h" -#include "../../Framework/Messages/IObservable.h" -#include "../../Framework/Messages/ICallable.h" -#include "../../Applications/Generic/NativeStoneApplicationContext.h" - -#include - -#include - -namespace Deprecated -{ - class WebServiceCommandBase : public IOracleCommand, OrthancStone::IObservable - { - protected: - std::unique_ptr > successCallback_; - std::unique_ptr > failureCallback_; - Orthanc::WebServiceParameters parameters_; - std::string url_; - IWebService::HttpHeaders headers_; - std::unique_ptr payload_; - bool success_; - Orthanc::HttpStatus httpStatus_; - std::string answer_; - IWebService::HttpHeaders answerHeaders_; - OrthancStone::NativeStoneApplicationContext& context_; - unsigned int timeoutInSeconds_; - - public: - WebServiceCommandBase(MessageHandler* successCallback, // takes ownership - MessageHandler* failureCallback, // takes ownership - const Orthanc::WebServiceParameters& parameters, - const std::string& url, - const IWebService::HttpHeaders& headers, - unsigned int timeoutInSeconds, - Orthanc::IDynamicObject* payload /* takes ownership */, - OrthancStone::NativeStoneApplicationContext& context - ); - - virtual void Execute() = 0; - - virtual void Commit(); - }; - -} diff -r ff8d2e46ac63 -r 419d0320c344 Platforms/Generic/WebServiceDeleteCommand.cpp --- a/Platforms/Generic/WebServiceDeleteCommand.cpp Wed Apr 29 20:44:31 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,56 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2020 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - **/ - - -#include "WebServiceDeleteCommand.h" - -#include - -namespace Deprecated -{ - WebServiceDeleteCommand::WebServiceDeleteCommand(MessageHandler* successCallback, // takes ownership - MessageHandler* failureCallback, // takes ownership - const Orthanc::WebServiceParameters& parameters, - const std::string& url, - const Deprecated::IWebService::HttpHeaders& headers, - unsigned int timeoutInSeconds, - Orthanc::IDynamicObject* payload /* takes ownership */, - OrthancStone::NativeStoneApplicationContext& context) : - WebServiceCommandBase(successCallback, failureCallback, parameters, url, headers, timeoutInSeconds, payload, context) - { - } - - void WebServiceDeleteCommand::Execute() - { - Orthanc::HttpClient client(parameters_, "/"); - client.SetUrl(url_); - client.SetTimeout(timeoutInSeconds_); - client.SetMethod(Orthanc::HttpMethod_Delete); - - for (Deprecated::IWebService::HttpHeaders::const_iterator it = headers_.begin(); it != headers_.end(); it++ ) - { - client.AddHeader(it->first, it->second); - } - - success_ = client.Apply(answer_, answerHeaders_); - httpStatus_ = client.GetLastStatus(); - } - -} diff -r ff8d2e46ac63 -r 419d0320c344 Platforms/Generic/WebServiceDeleteCommand.h --- a/Platforms/Generic/WebServiceDeleteCommand.h Wed Apr 29 20:44:31 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,42 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2020 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - **/ - - -#pragma once - -#include "WebServiceCommandBase.h" - -namespace Deprecated -{ - class WebServiceDeleteCommand : public WebServiceCommandBase - { - public: - WebServiceDeleteCommand(MessageHandler* successCallback, // takes ownership - MessageHandler* failureCallback, // takes ownership - const Orthanc::WebServiceParameters& parameters, - const std::string& url, - const IWebService::HttpHeaders& headers, - unsigned int timeoutInSeconds, - Orthanc::IDynamicObject* payload /* takes ownership */, - OrthancStone::NativeStoneApplicationContext& context); - - virtual void Execute(); - }; -} diff -r ff8d2e46ac63 -r 419d0320c344 Platforms/Generic/WebServiceGetCommand.cpp --- a/Platforms/Generic/WebServiceGetCommand.cpp Wed Apr 29 20:44:31 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,57 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2020 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - **/ - - -#include "WebServiceGetCommand.h" - -#include - -namespace Deprecated -{ - WebServiceGetCommand::WebServiceGetCommand(MessageHandler* successCallback, // takes ownership - MessageHandler* failureCallback, // takes ownership - const Orthanc::WebServiceParameters& parameters, - const std::string& url, - const IWebService::HttpHeaders& headers, - unsigned int timeoutInSeconds, - Orthanc::IDynamicObject* payload /* takes ownership */, - OrthancStone::NativeStoneApplicationContext& context) : - WebServiceCommandBase(successCallback, failureCallback, parameters, url, headers, timeoutInSeconds, payload, context) - { - } - - - void WebServiceGetCommand::Execute() - { - Orthanc::HttpClient client(parameters_, "/"); - client.SetUrl(url_); - client.SetTimeout(timeoutInSeconds_); - client.SetMethod(Orthanc::HttpMethod_Get); - - for (IWebService::HttpHeaders::const_iterator it = headers_.begin(); it != headers_.end(); it++ ) - { - client.AddHeader(it->first, it->second); - } - - success_ = client.Apply(answer_, answerHeaders_); - httpStatus_ = client.GetLastStatus(); - } - -} diff -r ff8d2e46ac63 -r 419d0320c344 Platforms/Generic/WebServiceGetCommand.h --- a/Platforms/Generic/WebServiceGetCommand.h Wed Apr 29 20:44:31 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,43 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2020 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - **/ - - -#pragma once - -#include "WebServiceCommandBase.h" - -namespace Deprecated -{ - class WebServiceGetCommand : public WebServiceCommandBase - { - public: - WebServiceGetCommand(MessageHandler* successCallback, // takes ownership - MessageHandler* failureCallback, // takes ownership - const Orthanc::WebServiceParameters& parameters, - const std::string& url, - const IWebService::HttpHeaders& headers, - unsigned int timeoutInSeconds, - Orthanc::IDynamicObject* payload /* takes ownership */, - OrthancStone::NativeStoneApplicationContext& context); - - virtual void Execute(); - }; - -} diff -r ff8d2e46ac63 -r 419d0320c344 Platforms/Generic/WebServicePostCommand.cpp --- a/Platforms/Generic/WebServicePostCommand.cpp Wed Apr 29 20:44:31 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,58 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2020 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - **/ - - -#include "WebServicePostCommand.h" - -#include - -namespace Deprecated -{ - WebServicePostCommand::WebServicePostCommand(MessageHandler* successCallback, // takes ownership - MessageHandler* failureCallback, // takes ownership - const Orthanc::WebServiceParameters& parameters, - const std::string& url, - const Deprecated::IWebService::HttpHeaders& headers, - unsigned int timeoutInSeconds, - const std::string& body, - Orthanc::IDynamicObject* payload /* takes ownership */, - OrthancStone::NativeStoneApplicationContext& context) : - WebServiceCommandBase(successCallback, failureCallback, parameters, url, headers, timeoutInSeconds, payload, context), - body_(body) - { - } - - void WebServicePostCommand::Execute() - { - Orthanc::HttpClient client(parameters_, "/"); - client.SetUrl(url_); - client.SetTimeout(timeoutInSeconds_); - client.SetMethod(Orthanc::HttpMethod_Post); - client.GetBody().swap(body_); - - for (Deprecated::IWebService::HttpHeaders::const_iterator it = headers_.begin(); it != headers_.end(); it++ ) - { - client.AddHeader(it->first, it->second); - } - - success_ = client.Apply(answer_, answerHeaders_); - httpStatus_ = client.GetLastStatus(); - } -} diff -r ff8d2e46ac63 -r 419d0320c344 Platforms/Generic/WebServicePostCommand.h --- a/Platforms/Generic/WebServicePostCommand.h Wed Apr 29 20:44:31 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,46 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2020 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - **/ - - -#pragma once - -#include "WebServiceCommandBase.h" - -namespace Deprecated -{ - class WebServicePostCommand : public WebServiceCommandBase - { - protected: - std::string body_; - - public: - WebServicePostCommand(MessageHandler* successCallback, // takes ownership - MessageHandler* failureCallback, // takes ownership - const Orthanc::WebServiceParameters& parameters, - const std::string& url, - const IWebService::HttpHeaders& headers, - unsigned int timeoutInSeconds, - const std::string& body, - Orthanc::IDynamicObject* payload /* takes ownership */, - OrthancStone::NativeStoneApplicationContext& context); - - virtual void Execute(); - }; -} diff -r ff8d2e46ac63 -r 419d0320c344 Platforms/Wasm/Defaults.cpp --- a/Platforms/Wasm/Defaults.cpp Wed Apr 29 20:44:31 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,416 +0,0 @@ -#include "Defaults.h" - -#include "WasmWebService.h" -#include "WasmDelayedCallExecutor.h" -#include "../../Framework/Deprecated/Widgets/TestCairoWidget.h" -#include -#include -#include -#include -#include -#include - -#include - - -static unsigned int width_ = 0; -static unsigned int height_ = 0; - -/**********************************/ - -static std::unique_ptr application; -static std::unique_ptr applicationWasmAdapter = NULL; -static std::unique_ptr context; -static OrthancStone::StartupParametersBuilder startupParametersBuilder; -static OrthancStone::MessageBroker broker; - -static OrthancStone::ViewportContentChangedObserver viewportContentChangedObserver_(broker); -static OrthancStone::StatusBar statusBar_; - -static std::list> viewports_; - -std::shared_ptr FindViewportSharedPtr(ViewportHandle viewport) { - for (const auto& v : viewports_) { - if (v.get() == viewport) { - return v; - } - } - assert(false); - return std::shared_ptr(); -} - -#ifdef __cplusplus -extern "C" { -#endif - -#if 0 - // rewrite malloc/free in order to monitor allocations. We actually only monitor large allocations (like images ...) - - size_t bigChunksTotalSize = 0; - std::map allocatedBigChunks; - - extern void* emscripten_builtin_malloc(size_t bytes); - extern void emscripten_builtin_free(void* mem); - - void * __attribute__((noinline)) malloc(size_t size) - { - void *ptr = emscripten_builtin_malloc(size); - if (size > 100000) - { - bigChunksTotalSize += size; - printf("++ Allocated %zu bytes, got %p. (%zu MB consumed by big chunks)\n", size, ptr, bigChunksTotalSize/(1024*1024)); - allocatedBigChunks[ptr] = size; - } - return ptr; - } - - void __attribute__((noinline)) free(void *ptr) - { - emscripten_builtin_free(ptr); - - std::map::iterator it = allocatedBigChunks.find(ptr); - if (it != allocatedBigChunks.end()) - { - bigChunksTotalSize -= it->second; - printf("-- Freed %zu bytes at %p. (%zu MB consumed by big chunks)\n", it->second, ptr, bigChunksTotalSize/(1024*1024)); - allocatedBigChunks.erase(it); - } - } -#endif // 0 - - using namespace OrthancStone; - - // when WASM needs a C++ viewport - ViewportHandle EMSCRIPTEN_KEEPALIVE CreateCppViewport() { - - std::shared_ptr viewport(new Deprecated::WidgetViewport(broker)); - printf("viewport %x\n", (int)viewport.get()); - - viewports_.push_back(viewport); - - printf("There are now %lu viewports in C++\n", viewports_.size()); - - viewport->SetStatusBar(statusBar_); - - viewport->RegisterObserverCallback( - new Callable - (viewportContentChangedObserver_, &ViewportContentChangedObserver::OnViewportChanged)); - - return viewport.get(); - } - - // when WASM does not need a viewport anymore, it should release it - void EMSCRIPTEN_KEEPALIVE ReleaseCppViewport(ViewportHandle viewport) { - viewports_.remove_if([viewport](const std::shared_ptr& v) { return v.get() == viewport;}); - - printf("There are now %lu viewports in C++\n", viewports_.size()); - } - - void EMSCRIPTEN_KEEPALIVE CreateWasmApplication(ViewportHandle viewport) { - printf("Initializing Stone\n"); - OrthancStone::StoneInitialize(); - printf("CreateWasmApplication\n"); - - application.reset(CreateUserApplication(broker)); - applicationWasmAdapter.reset(CreateWasmApplicationAdapter(broker, application.get())); - Deprecated::WasmWebService::SetBroker(broker); - Deprecated::WasmDelayedCallExecutor::SetBroker(broker); - - startupParametersBuilder.Clear(); - } - - void EMSCRIPTEN_KEEPALIVE SetStartupParameter(const char* keyc, - const char* value) { - startupParametersBuilder.SetStartupParameter(keyc, value); - } - - void EMSCRIPTEN_KEEPALIVE StartWasmApplication(const char* baseUri) { - - printf("StartWasmApplication\n"); - - Orthanc::Logging::SetErrorWarnInfoTraceLoggingFunctions( - stone_console_error, stone_console_warning, - stone_console_info, stone_console_trace); - - // recreate a command line from uri arguments and parse it - boost::program_options::variables_map parameters; - boost::program_options::options_description options; - application->DeclareStartupOptions(options); - startupParametersBuilder.GetStartupParameters(parameters, options); - - context.reset(new OrthancStone::StoneApplicationContext(broker)); - context->SetOrthancBaseUrl(baseUri); - printf("Base URL to Orthanc API: [%s]\n", baseUri); - context->SetWebService(Deprecated::WasmWebService::GetInstance()); - context->SetDelayedCallExecutor(Deprecated::WasmDelayedCallExecutor::GetInstance()); - application->Initialize(context.get(), statusBar_, parameters); - application->InitializeWasm(); - -// viewport->SetSize(width_, height_); - printf("StartWasmApplication - completed\n"); - } - - bool EMSCRIPTEN_KEEPALIVE WasmIsTraceLevelEnabled() - { - return Orthanc::Logging::IsTraceLevelEnabled(); - } - - bool EMSCRIPTEN_KEEPALIVE WasmIsInfoLevelEnabled() - { - return Orthanc::Logging::IsInfoLevelEnabled(); - } - - void EMSCRIPTEN_KEEPALIVE WasmDoAnimation() - { - for (auto viewport : viewports_) { - // TODO Only launch the JavaScript timer if "HasAnimation()" - if (viewport->HasAnimation()) - { - viewport->DoAnimation(); - } - - } - - } - - - void EMSCRIPTEN_KEEPALIVE ViewportSetSize(ViewportHandle viewport, unsigned int width, unsigned int height) - { - width_ = width; - height_ = height; - - viewport->SetSize(width, height); - } - - int EMSCRIPTEN_KEEPALIVE ViewportRender(ViewportHandle viewport, - unsigned int width, - unsigned int height, - uint8_t* data) - { - viewportContentChangedObserver_.Reset(); - - //printf("ViewportRender called %dx%d\n", width, height); - if (width == 0 || - height == 0) - { - return 1; - } - - Orthanc::ImageAccessor surface; - surface.AssignWritable(Orthanc::PixelFormat_BGRA32, width, height, 4 * width, data); - - viewport->Render(surface); - - // Convert from BGRA32 memory layout (only color mode supported by - // Cairo, which corresponds to CAIRO_FORMAT_ARGB32) to RGBA32 (as - // expected by HTML5 canvas). This simply amounts to swapping the - // B and R channels. - uint8_t* p = data; - for (unsigned int y = 0; y < height; y++) { - for (unsigned int x = 0; x < width; x++) { - uint8_t tmp = p[0]; - p[0] = p[2]; - p[2] = tmp; - - p += 4; - } - } - - return 1; - } - - - void EMSCRIPTEN_KEEPALIVE ViewportMouseDown(ViewportHandle viewport, - unsigned int rawButton, - int x, - int y, - unsigned int rawModifiers) - { - OrthancStone::MouseButton button; - switch (rawButton) - { - case 0: - button = OrthancStone::MouseButton_Left; - break; - - case 1: - button = OrthancStone::MouseButton_Middle; - break; - - case 2: - button = OrthancStone::MouseButton_Right; - break; - - default: - return; // Unknown button - } - - viewport->MouseDown(button, x, y, OrthancStone::KeyboardModifiers_None, std::vector()); - } - - - void EMSCRIPTEN_KEEPALIVE ViewportMouseWheel(ViewportHandle viewport, - int deltaY, - int x, - int y, - int isControl) - { - if (deltaY != 0) - { - OrthancStone::MouseWheelDirection direction = (deltaY < 0 ? - OrthancStone::MouseWheelDirection_Up : - OrthancStone::MouseWheelDirection_Down); - OrthancStone::KeyboardModifiers modifiers = OrthancStone::KeyboardModifiers_None; - - if (isControl != 0) - { - modifiers = OrthancStone::KeyboardModifiers_Control; - } - - viewport->MouseWheel(direction, x, y, modifiers); - } - } - - - void EMSCRIPTEN_KEEPALIVE ViewportMouseMove(ViewportHandle viewport, - int x, - int y) - { - viewport->MouseMove(x, y, std::vector()); - } - - void GetTouchVector(std::vector& output, - int touchCount, - float x0, - float y0, - float x1, - float y1, - float x2, - float y2) - { - // TODO: it might be nice to try to pass all the x0,y0 coordinates as arrays but that's not so easy to pass array between JS and C++ - if (touchCount > 0) - { - output.push_back(Deprecated::Touch(x0, y0)); - } - if (touchCount > 1) - { - output.push_back(Deprecated::Touch(x1, y1)); - } - if (touchCount > 2) - { - output.push_back(Deprecated::Touch(x2, y2)); - } - - } - - void EMSCRIPTEN_KEEPALIVE ViewportTouchStart(ViewportHandle viewport, - int touchCount, - float x0, - float y0, - float x1, - float y1, - float x2, - float y2) - { - // printf("touch start with %d touches\n", touchCount); - - std::vector touches; - GetTouchVector(touches, touchCount, x0, y0, x1, y1, x2, y2); - viewport->TouchStart(touches); - } - - void EMSCRIPTEN_KEEPALIVE ViewportTouchMove(ViewportHandle viewport, - int touchCount, - float x0, - float y0, - float x1, - float y1, - float x2, - float y2) - { - // printf("touch move with %d touches\n", touchCount); - - std::vector touches; - GetTouchVector(touches, touchCount, x0, y0, x1, y1, x2, y2); - viewport->TouchMove(touches); - } - - void EMSCRIPTEN_KEEPALIVE ViewportTouchEnd(ViewportHandle viewport, - int touchCount, - float x0, - float y0, - float x1, - float y1, - float x2, - float y2) - { - // printf("touch end with %d touches remaining\n", touchCount); - - std::vector touches; - GetTouchVector(touches, touchCount, x0, y0, x1, y1, x2, y2); - viewport->TouchEnd(touches); - } - - void EMSCRIPTEN_KEEPALIVE ViewportKeyPressed(ViewportHandle viewport, - int key, - const char* keyChar, - bool isShiftPressed, - bool isControlPressed, - bool isAltPressed) - - { - OrthancStone::KeyboardModifiers modifiers = OrthancStone::KeyboardModifiers_None; - if (isShiftPressed) { - modifiers = static_cast(modifiers + OrthancStone::KeyboardModifiers_Shift); - } - if (isControlPressed) { - modifiers = static_cast(modifiers + OrthancStone::KeyboardModifiers_Control); - } - if (isAltPressed) { - modifiers = static_cast(modifiers + OrthancStone::KeyboardModifiers_Alt); - } - - char c = 0; - if (keyChar != NULL && key == OrthancStone::KeyboardKeys_Generic) { - c = keyChar[0]; - } - viewport->KeyPressed(static_cast(key), c, modifiers); - } - - - void EMSCRIPTEN_KEEPALIVE ViewportMouseUp(ViewportHandle viewport) - { - viewport->MouseUp(); - } - - - void EMSCRIPTEN_KEEPALIVE ViewportMouseEnter(ViewportHandle viewport) - { - viewport->MouseEnter(); - } - - - void EMSCRIPTEN_KEEPALIVE ViewportMouseLeave(ViewportHandle viewport) - { - viewport->MouseLeave(); - } - - const char* EMSCRIPTEN_KEEPALIVE SendSerializedMessageToStoneApplication(const char* message) - { - static std::string output; // we don't want the string to be deallocated when we return to JS code so we always use the same string (this is fine since JS is single-thread) - - //printf("SendSerializedMessageToStoneApplication\n"); - //printf("%s", message); - - if (applicationWasmAdapter.get() != NULL) { - applicationWasmAdapter->HandleSerializedMessageFromWeb(output, std::string(message)); - return output.c_str(); - } - printf("This Stone application does not have a Web Adapter, unable to send messages"); - return NULL; - } - -#ifdef __cplusplus -} -#endif diff -r ff8d2e46ac63 -r 419d0320c344 Platforms/Wasm/Defaults.h --- a/Platforms/Wasm/Defaults.h Wed Apr 29 20:44:31 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,82 +0,0 @@ -#pragma once - -#include - -#include "../../Framework/Deprecated/Viewport/WidgetViewport.h" -#include "../../Framework/Deprecated/Widgets/LayoutWidget.h" -#include -#include - -typedef Deprecated::WidgetViewport* ViewportHandle; // the objects exchanged between JS and C++ - -#ifdef __cplusplus -extern "C" { -#endif - - // JS methods accessible from C++ - extern void ScheduleWebViewportRedrawFromCpp(ViewportHandle cppViewportHandle); - extern void UpdateStoneApplicationStatusFromCppWithString(const char* statusUpdateMessage); - extern void UpdateStoneApplicationStatusFromCppWithSerializedMessage(const char* statusUpdateMessage); - extern void stone_console_error(const char*); - extern void stone_console_warning(const char*); - extern void stone_console_info(const char*); - extern void stone_console_trace(const char*); - - // C++ methods accessible from JS - extern void EMSCRIPTEN_KEEPALIVE CreateWasmApplication(ViewportHandle cppViewportHandle); - extern void EMSCRIPTEN_KEEPALIVE SetStartupParameter(const char* keyc, const char* value); - - -#ifdef __cplusplus -} -#endif - -// these methods must be implemented in the custom app "mainWasm.cpp" -extern OrthancStone::IStoneApplication* CreateUserApplication(OrthancStone::MessageBroker& broker); -extern OrthancStone::WasmPlatformApplicationAdapter* CreateWasmApplicationAdapter(OrthancStone::MessageBroker& broker, OrthancStone::IStoneApplication* application); - -namespace OrthancStone { - - // default Observer to trigger Viewport redraw when something changes in the Viewport - class ViewportContentChangedObserver : public IObserver - { - private: - // Flag to avoid flooding JavaScript with redundant Redraw requests - bool isScheduled_; - - public: - ViewportContentChangedObserver(MessageBroker& broker) : - IObserver(broker), - isScheduled_(false) - { - } - - void Reset() - { - isScheduled_ = false; - } - - void OnViewportChanged(const Deprecated::IViewport::ViewportChangedMessage& message) - { - if (!isScheduled_) - { - ScheduleWebViewportRedrawFromCpp((ViewportHandle)&message.GetOrigin()); // loosing constness when transmitted to Web - isScheduled_ = true; - } - } - }; - - // default status bar to log messages on the console/stdout - class StatusBar : public Deprecated::IStatusBar - { - public: - virtual void ClearMessage() - { - } - - virtual void SetMessage(const std::string& message) - { - printf("%s\n", message.c_str()); - } - }; -} diff -r ff8d2e46ac63 -r 419d0320c344 Platforms/Wasm/WasmDelayedCallExecutor.cpp --- a/Platforms/Wasm/WasmDelayedCallExecutor.cpp Wed Apr 29 20:44:31 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,46 +0,0 @@ -#include "WasmDelayedCallExecutor.h" -#include "json/value.h" -#include "json/writer.h" -#include - -#ifdef __cplusplus -extern "C" { -#endif - - extern void WasmDelayedCallExecutor_Schedule(void* callable, - unsigned int timeoutInMs - /*void* payload*/); - - void EMSCRIPTEN_KEEPALIVE WasmDelayedCallExecutor_ExecuteCallback(void* callable - //void* payload - ) - { - if (callable == NULL) - { - throw; - } - else - { - reinterpret_cast*>(callable)-> - Apply(Deprecated::IDelayedCallExecutor::TimeoutMessage()); // uri, reinterpret_cast(payload))); - } - } - - -#ifdef __cplusplus -} -#endif - - - -namespace Deprecated -{ - OrthancStone::MessageBroker* WasmDelayedCallExecutor::broker_ = NULL; - - - void WasmDelayedCallExecutor::Schedule(OrthancStone::MessageHandler* callback, - unsigned int timeoutInMs) - { - WasmDelayedCallExecutor_Schedule(callback, timeoutInMs); - } -} diff -r ff8d2e46ac63 -r 419d0320c344 Platforms/Wasm/WasmDelayedCallExecutor.h --- a/Platforms/Wasm/WasmDelayedCallExecutor.h Wed Apr 29 20:44:31 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,40 +0,0 @@ -#pragma once - -#include "../../Framework/Deprecated/Toolbox/IDelayedCallExecutor.h" -#include - -namespace Deprecated -{ - class WasmDelayedCallExecutor : public IDelayedCallExecutor - { - private: - static OrthancStone::MessageBroker* broker_; - - // Private constructor => Singleton design pattern - WasmDelayedCallExecutor(OrthancStone::MessageBroker& broker) : - IDelayedCallExecutor(broker) - { - } - - public: - static WasmDelayedCallExecutor& GetInstance() - { - if (broker_ == NULL) - { - printf("WasmDelayedCallExecutor::GetInstance(): broker not initialized\n"); - throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); - } - static WasmDelayedCallExecutor instance(*broker_); - return instance; - } - - static void SetBroker(OrthancStone::MessageBroker& broker) - { - broker_ = &broker; - } - - virtual void Schedule(OrthancStone::MessageHandler* callback, - unsigned int timeoutInMs = 1000); - - }; -} diff -r ff8d2e46ac63 -r 419d0320c344 Platforms/Wasm/WasmDelayedCallExecutor.js --- a/Platforms/Wasm/WasmDelayedCallExecutor.js Wed Apr 29 20:44:31 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,7 +0,0 @@ -mergeInto(LibraryManager.library, { - WasmDelayedCallExecutor_Schedule: function(callable, timeoutInMs/*, payload*/) { - setTimeout(function() { - window.WasmDelayedCallExecutor_ExecuteCallback(callable/*, payload*/); - }, timeoutInMs); - } -}); diff -r ff8d2e46ac63 -r 419d0320c344 Platforms/Wasm/WasmPlatformApplicationAdapter.cpp --- a/Platforms/Wasm/WasmPlatformApplicationAdapter.cpp Wed Apr 29 20:44:31 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,59 +0,0 @@ -#include "WasmPlatformApplicationAdapter.h" - -#include "Framework/StoneException.h" -#include -#include "Platforms/Wasm/Defaults.h" - -namespace OrthancStone -{ - WasmPlatformApplicationAdapter::WasmPlatformApplicationAdapter(MessageBroker& broker, IStoneApplication& application) - : IObserver(broker), - application_(application) - { - } - - void WasmPlatformApplicationAdapter::HandleSerializedMessageFromWeb(std::string& output, const std::string& input) - { - try - { - application_.HandleSerializedMessage(input.c_str()); - } - catch (StoneException& exc) - { - printf("Error while handling message from web (error code = %d):\n", exc.GetErrorCode()); - printf("While interpreting input: '%s'\n", input.c_str()); - output = std::string("ERROR : "); - } - catch (std::exception& exc) - { - printf("Error while handling message from web (error text = %s):\n", exc.what()); - printf("While interpreting input: '%s'\n", input.c_str()); - output = std::string("ERROR : "); - } - } - - void WasmPlatformApplicationAdapter::NotifyStatusUpdateFromCppToWebWithString(const std::string& statusUpdateMessage) - { - try - { - UpdateStoneApplicationStatusFromCppWithString(statusUpdateMessage.c_str()); - } - catch (...) - { - printf("Error while handling string message to web\n"); - } - } - - void WasmPlatformApplicationAdapter::NotifyStatusUpdateFromCppToWebWithSerializedMessage(const std::string& statusUpdateMessage) - { - try - { - UpdateStoneApplicationStatusFromCppWithSerializedMessage(statusUpdateMessage.c_str()); - } - catch (...) - { - printf("Error while handling serialized message to web\n"); - } - } - -} diff -r ff8d2e46ac63 -r 419d0320c344 Platforms/Wasm/WasmPlatformApplicationAdapter.h --- a/Platforms/Wasm/WasmPlatformApplicationAdapter.h Wed Apr 29 20:44:31 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,19 +0,0 @@ -#pragma once - -#include -#include -#include - -namespace OrthancStone -{ - class WasmPlatformApplicationAdapter : public IObserver - { - IStoneApplication& application_; - public: - WasmPlatformApplicationAdapter(MessageBroker& broker, IStoneApplication& application); - - virtual void HandleSerializedMessageFromWeb(std::string& output, const std::string& input); - virtual void NotifyStatusUpdateFromCppToWebWithString(const std::string& statusUpdateMessage); - virtual void NotifyStatusUpdateFromCppToWebWithSerializedMessage(const std::string& statusUpdateMessage); - }; -} \ No newline at end of file diff -r ff8d2e46ac63 -r 419d0320c344 Platforms/Wasm/WasmViewport.cpp --- a/Platforms/Wasm/WasmViewport.cpp Wed Apr 29 20:44:31 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,13 +0,0 @@ -#include "WasmViewport.h" - -#include -#include - -std::vector> wasmViewports; - -void AttachWidgetToWasmViewport(const char* htmlCanvasId, Deprecated::IWidget* centralWidget) { - std::shared_ptr viewport(CreateWasmViewportFromCpp(htmlCanvasId)); - viewport->SetCentralWidget(centralWidget); - - wasmViewports.push_back(viewport); -} diff -r ff8d2e46ac63 -r 419d0320c344 Platforms/Wasm/WasmViewport.h --- a/Platforms/Wasm/WasmViewport.h Wed Apr 29 20:44:31 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,18 +0,0 @@ -#pragma once - -#include "../../Framework/Deprecated/Viewport/WidgetViewport.h" - -#include - -#ifdef __cplusplus -extern "C" { -#endif - - // JS methods accessible from C++ - extern Deprecated::WidgetViewport* CreateWasmViewportFromCpp(const char* htmlCanvasId); - -#ifdef __cplusplus -} -#endif - -extern void AttachWidgetToWasmViewport(const char* htmlCanvasId, Deprecated::IWidget* centralWidget); diff -r ff8d2e46ac63 -r 419d0320c344 Platforms/Wasm/WasmWebService.cpp --- a/Platforms/Wasm/WasmWebService.cpp Wed Apr 29 20:44:31 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,168 +0,0 @@ -#include "WasmWebService.h" -#include "json/value.h" -#include "json/writer.h" -#include -#include - -struct CachedSuccessNotification -{ - boost::shared_ptr cachedMessage; - std::unique_ptr payload; - OrthancStone::MessageHandler* successCallback; -}; - - -#ifdef __cplusplus -extern "C" { -#endif - - extern void WasmWebService_GetAsync(void* callableSuccess, - void* callableFailure, - const char* uri, - const char* headersInJsonString, - void* payload, - unsigned int timeoutInSeconds); - - extern void WasmWebService_ScheduleLaterCachedSuccessNotification(void* brol); - - extern void WasmWebService_PostAsync(void* callableSuccess, - void* callableFailure, - const char* uri, - const char* headersInJsonString, - const void* body, - size_t bodySize, - void* payload, - unsigned int timeoutInSeconds); - - extern void WasmWebService_DeleteAsync(void* callableSuccess, - void* callableFailure, - const char* uri, - const char* headersInJsonString, - void* payload, - unsigned int timeoutInSeconds); - - void EMSCRIPTEN_KEEPALIVE WasmWebService_NotifyError(void* failureCallable, - const char* uri, - unsigned int httpStatus, - void* payload) - { - if (failureCallable != NULL) - { - reinterpret_cast*>(failureCallable)-> - Apply(Deprecated::IWebService::HttpRequestErrorMessage(uri, static_cast(httpStatus), reinterpret_cast(payload))); - } - } - - void EMSCRIPTEN_KEEPALIVE WasmWebService_NotifyCachedSuccess(void* notification_) - { - // notification has been allocated in C++ and passed to JS. It must be deleted by this method - std::unique_ptr notification(reinterpret_cast(notification_)); - - notification->successCallback->Apply(Deprecated::IWebService::HttpRequestSuccessMessage( - notification->cachedMessage->GetUri(), - notification->cachedMessage->GetAnswer(), - notification->cachedMessage->GetAnswerSize(), - notification->cachedMessage->GetAnswerHttpHeaders(), - notification->payload.get() - )); - } - - void EMSCRIPTEN_KEEPALIVE WasmWebService_NotifySuccess(void* successCallable, - const char* uri, - const void* body, - size_t bodySize, - const char* answerHeaders, - void* payload) - { - if (successCallable != NULL) - { - Deprecated::IWebService::HttpHeaders headers; - - // TODO - Parse "answerHeaders" - //printf("TODO: parse headers [%s]\n", answerHeaders); - - reinterpret_cast*>(successCallable)-> - Apply(Deprecated::IWebService::HttpRequestSuccessMessage(uri, body, bodySize, headers, - reinterpret_cast(payload))); - } - } - -#ifdef __cplusplus -} -#endif - - - -namespace Deprecated -{ - OrthancStone::MessageBroker* WasmWebService::broker_ = NULL; - - void ToJsonString(std::string& output, const IWebService::HttpHeaders& headers) - { - Json::Value jsonHeaders; - for (IWebService::HttpHeaders::const_iterator it = headers.begin(); it != headers.end(); it++ ) - { - jsonHeaders[it->first] = it->second; - } - - Json::StreamWriterBuilder builder; - std::unique_ptr writer(builder.newStreamWriter()); - std::ostringstream outputStr; - - writer->write(jsonHeaders, &outputStr); - output = outputStr.str(); - } - - void WasmWebService::PostAsync(const std::string& relativeUri, - const HttpHeaders& headers, - const std::string& body, - Orthanc::IDynamicObject* payload, - OrthancStone::MessageHandler* successCallable, - OrthancStone::MessageHandler* failureCallable, - unsigned int timeoutInSeconds) - { - std::string headersInJsonString; - ToJsonString(headersInJsonString, headers); - WasmWebService_PostAsync(successCallable, failureCallable, relativeUri.c_str(), headersInJsonString.c_str(), - body.c_str(), body.size(), payload, timeoutInSeconds); - } - - void WasmWebService::DeleteAsync(const std::string& relativeUri, - const HttpHeaders& headers, - Orthanc::IDynamicObject* payload, - OrthancStone::MessageHandler* successCallable, - OrthancStone::MessageHandler* failureCallable, - unsigned int timeoutInSeconds) - { - std::string headersInJsonString; - ToJsonString(headersInJsonString, headers); - WasmWebService_DeleteAsync(successCallable, failureCallable, relativeUri.c_str(), headersInJsonString.c_str(), - payload, timeoutInSeconds); - } - - void WasmWebService::GetAsyncInternal(const std::string &relativeUri, - const HttpHeaders &headers, - Orthanc::IDynamicObject *payload, - OrthancStone::MessageHandler *successCallable, - OrthancStone::MessageHandler *failureCallable, - unsigned int timeoutInSeconds) - { - std::string headersInJsonString; - ToJsonString(headersInJsonString, headers); - WasmWebService_GetAsync(successCallable, failureCallable, relativeUri.c_str(), - headersInJsonString.c_str(), payload, timeoutInSeconds); - } - - void WasmWebService::NotifyHttpSuccessLater(boost::shared_ptr cachedMessage, - Orthanc::IDynamicObject* payload, // takes ownership - OrthancStone::MessageHandler* successCallback) - { - CachedSuccessNotification* notification = new CachedSuccessNotification(); // allocated on the heap, it will be passed to JS and deleted when coming back to C++ - notification->cachedMessage = cachedMessage; - notification->payload.reset(payload); - notification->successCallback = successCallback; - - WasmWebService_ScheduleLaterCachedSuccessNotification(notification); - } - -} diff -r ff8d2e46ac63 -r 419d0320c344 Platforms/Wasm/WasmWebService.h --- a/Platforms/Wasm/WasmWebService.h Wed Apr 29 20:44:31 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,62 +0,0 @@ -#pragma once - -#include "../../Framework/Deprecated/Toolbox/BaseWebService.h" -#include - -namespace Deprecated -{ -class WasmWebService : public BaseWebService -{ -private: - static OrthancStone::MessageBroker *broker_; - - // Private constructor => Singleton design pattern - WasmWebService(OrthancStone::MessageBroker &broker) : BaseWebService(broker) - { - } - -public: - static WasmWebService &GetInstance() - { - if (broker_ == NULL) - { - printf("WasmWebService::GetInstance(): broker not initialized\n"); - throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); - } - static WasmWebService instance(*broker_); - return instance; - } - - static void SetBroker(OrthancStone::MessageBroker &broker) - { - broker_ = &broker; - } - - virtual void PostAsync(const std::string &uri, - const HttpHeaders &headers, - const std::string &body, - Orthanc::IDynamicObject *payload, - OrthancStone::MessageHandler *successCallable, - OrthancStone::MessageHandler *failureCallable = NULL, - unsigned int timeoutInSeconds = 60); - - virtual void DeleteAsync(const std::string &uri, - const HttpHeaders &headers, - Orthanc::IDynamicObject *payload, - OrthancStone::MessageHandler *successCallable, - OrthancStone::MessageHandler *failureCallable = NULL, - unsigned int timeoutInSeconds = 60); - -protected: - virtual void GetAsyncInternal(const std::string &uri, - const HttpHeaders &headers, - Orthanc::IDynamicObject *payload, - OrthancStone::MessageHandler *successCallable, - OrthancStone::MessageHandler *failureCallable = NULL, - unsigned int timeoutInSeconds = 60); - - virtual void NotifyHttpSuccessLater(boost::shared_ptr cachedHttpMessage, - Orthanc::IDynamicObject *payload, // takes ownership - OrthancStone::MessageHandler *successCallback); -}; -} // namespace Deprecated diff -r ff8d2e46ac63 -r 419d0320c344 Platforms/Wasm/WasmWebService.js --- a/Platforms/Wasm/WasmWebService.js Wed Apr 29 20:44:31 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,108 +0,0 @@ -mergeInto(LibraryManager.library, { - WasmWebService_GetAsync: function(callableSuccess, callableFailure, url, headersInJsonString, payload, timeoutInSeconds) { - // Directly use XMLHttpRequest (no jQuery) to retrieve the raw binary data - // http://www.henryalgus.com/reading-binary-files-using-jquery-ajax/ - var xhr = new XMLHttpRequest(); - var url_ = UTF8ToString(url); - var headersInJsonString_ = UTF8ToString(headersInJsonString); - - xhr.open('GET', url_, true); - xhr.responseType = 'arraybuffer'; - xhr.timeout = timeoutInSeconds * 1000; - var headers = JSON.parse(headersInJsonString_); - for (var key in headers) { - xhr.setRequestHeader(key, headers[key]); - } - //console.log(xhr); - xhr.onreadystatechange = function() { - if (this.readyState == XMLHttpRequest.DONE) { - if (xhr.status === 200) { - var s = xhr.getAllResponseHeaders(); - var headers = _malloc(s.length + 1); - stringToUTF8(s, headers, s.length + 1); - - // TODO - Is "new Uint8Array()" necessary? This copies the - // answer to the WebAssembly stack, hence necessitating - // increasing the TOTAL_STACK parameter of Emscripten - window.WasmWebService_NotifySuccess(callableSuccess, url_, new Uint8Array(this.response), - this.response.byteLength, headers, payload); - } else { - window.WasmWebService_NotifyError(callableFailure, url_, xhr.status, payload); - } - } - } - - xhr.send(); - }, - - WasmWebService_ScheduleLaterCachedSuccessNotification: function (brol) { - setTimeout(function() { - window.WasmWebService_NotifyCachedSuccess(brol); - }, 0); - }, - - WasmWebService_PostAsync: function(callableSuccess, callableFailure, url, headersInJsonString, body, bodySize, payload, timeoutInSeconds) { - var xhr = new XMLHttpRequest(); - var url_ = UTF8ToString(url); - var headersInJsonString_ = UTF8ToString(headersInJsonString); - xhr.open('POST', url_, true); - xhr.timeout = timeoutInSeconds * 1000; - xhr.responseType = 'arraybuffer'; - xhr.setRequestHeader('Content-type', 'application/octet-stream'); - - var headers = JSON.parse(headersInJsonString_); - for (var key in headers) { - xhr.setRequestHeader(key, headers[key]); - } - - xhr.onreadystatechange = function() { - if (this.readyState == XMLHttpRequest.DONE) { - if (xhr.status === 200) { - var s = xhr.getAllResponseHeaders(); - var headers = _malloc(s.length + 1); - stringToUTF8(s, headers, s.length + 1); - - window.WasmWebService_NotifySuccess(callableSuccess, url_, new Uint8Array(this.response), - this.response.byteLength, headers, payload); - } else { - window.WasmWebService_NotifyError(callableFailure, url_, xhr.status, payload); - } - } - } - - xhr.send(new Uint8ClampedArray(HEAPU8.buffer, body, bodySize)); - }, - - WasmWebService_DeleteAsync: function(callableSuccess, callableFailure, url, headersInJsonString, payload, timeoutInSeconds) { - var xhr = new XMLHttpRequest(); - var url_ = UTF8ToString(url); - var headersInJsonString_ = UTF8ToString(headersInJsonString); - xhr.open('DELETE', url_, true); - xhr.timeout = timeoutInSeconds * 1000; - xhr.responseType = 'arraybuffer'; - xhr.setRequestHeader('Content-type', 'application/octet-stream'); - - var headers = JSON.parse(headersInJsonString_); - for (var key in headers) { - xhr.setRequestHeader(key, headers[key]); - } - - xhr.onreadystatechange = function() { - if (this.readyState == XMLHttpRequest.DONE) { - if (xhr.status === 200) { - var s = xhr.getAllResponseHeaders(); - var headers = _malloc(s.length + 1); - stringToUTF8(s, headers, s.length + 1); - - window.WasmWebService_NotifySuccess(callableSuccess, url_, new Uint8Array(this.response), - this.response.byteLength, headers, payload); - } else { - window.WasmWebService_NotifyError(callableFailure, url_, xhr.status, payload); - } - } - } - - xhr.send(); - } - -}); diff -r ff8d2e46ac63 -r 419d0320c344 Platforms/Wasm/default-library.js --- a/Platforms/Wasm/default-library.js Wed Apr 29 20:44:31 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,49 +0,0 @@ -// this file contains the JS method you want to expose to C++ code - -mergeInto(LibraryManager.library, { - - ScheduleWebViewportRedrawFromCpp: function(cppViewportHandle) { - window.ScheduleWebViewportRedraw(cppViewportHandle); - }, - - CreateWasmViewportFromCpp: function(htmlCanvasId) { - return window.CreateWasmViewport(htmlCanvasId); - }, - - // each time the StoneApplication updates its status, it may signal it - // through this method. i.e, to change the status of a button in the web interface - UpdateStoneApplicationStatusFromCppWithString: function(statusUpdateMessage) { - var statusUpdateMessage_ = UTF8ToString(statusUpdateMessage); - window.UpdateWebApplicationWithString(statusUpdateMessage_); - }, - - // same, but with a serialized message - UpdateStoneApplicationStatusFromCppWithSerializedMessage: function(statusUpdateMessage) { - var statusUpdateMessage_ = UTF8ToString(statusUpdateMessage); - window.UpdateWebApplicationWithSerializedMessage(statusUpdateMessage_); - }, - - // These functions are called from C++ (through an extern declaration) - // and call the standard logger that, here, routes to the console. - - stone_console_error : function(message) { - var text = UTF8ToString(message); - window.errorFromCpp(text); - }, - - stone_console_warning : function(message) { - var text = UTF8ToString(message); - window.warningFromCpp(text); - }, - - stone_console_info: function(message) { - var text = UTF8ToString(message); - window.infoFromCpp(text); - }, - - stone_console_trace : function(message) { - var text = UTF8ToString(message); - window.debugFromCpp(text); - } - -}); diff -r ff8d2e46ac63 -r 419d0320c344 Platforms/Wasm/logger.ts --- a/Platforms/Wasm/logger.ts Wed Apr 29 20:44:31 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,111 +0,0 @@ -export enum LogSource { - Cpp, - Typescript -} - -export class StandardConsoleLogger { - public showSource: boolean = true; - - public debug(...args: any[]): void { - this._debug(LogSource.Typescript, ...args); - } - - public debugFromCpp(...args: any[]): void { - this._debug(LogSource.Cpp, ...args); - } - - public info(...args: any[]): void { - this._info(LogSource.Typescript, ...args); - } - - public infoFromCpp(message: string): void { - this._info(LogSource.Cpp, message); - } - - public warning(...args: any[]): void { - this._warning(LogSource.Typescript, ...args); - } - - public warningFromCpp(message: string): void { - this._warning(LogSource.Cpp, message); - } - - public error(...args: any[]): void { - this._error(LogSource.Typescript, ...args); - } - - public errorFromCpp(message: string): void { - this._error(LogSource.Cpp, message); - } - - public _debug(source: LogSource, ...args: any[]): void { - if (( window).IsTraceLevelEnabled) - { - if (( window).IsTraceLevelEnabled()) - { - var output = this.getOutput(source, args); - console.debug(...output); - } - } - } - - private _info(source: LogSource, ...args: any[]): void { - if (( window).IsInfoLevelEnabled) - { - if (( window).IsInfoLevelEnabled()) - { - var output = this.getOutput(source, args); - console.info(...output); - } - } - } - - public _warning(source: LogSource, ...args: any[]): void { - var output = this.getOutput(source, args); - console.warn(...output); - } - - public _error(source: LogSource, ...args: any[]): void { - var output = this.getOutput(source, args); - console.error(...output); - } - - - private getOutput(source: LogSource, args: any[]): any[] { - var prefix = this.getPrefix(); - var prefixAndSource = Array(); - - if (prefix != null) { - prefixAndSource = [prefix]; - } - - if (this.showSource) { - if (source == LogSource.Typescript) { - prefixAndSource = [...prefixAndSource, "TS "]; - } else if (source == LogSource.Cpp) { - prefixAndSource = [...prefixAndSource, "C++"]; - } - } - - if (prefixAndSource.length > 0) { - prefixAndSource = [...prefixAndSource, "|"]; - } - - return [...prefixAndSource, ...args]; - } - - protected getPrefix(): string | null { - return null; - } -} - -export class TimeConsoleLogger extends StandardConsoleLogger { - protected getPrefix(): string { - let now = new Date(); - let timeString = now.getHours().toString().padStart(2, "0") + ":" + now.getMinutes().toString().padStart(2, "0") + ":" + now.getSeconds().toString().padStart(2, "0") + "." + now.getMilliseconds().toString().padStart(3, "0"); - return timeString; - } -} - -export var defaultLogger: StandardConsoleLogger = new TimeConsoleLogger(); - diff -r ff8d2e46ac63 -r 419d0320c344 Platforms/Wasm/stone-framework-loader.ts --- a/Platforms/Wasm/stone-framework-loader.ts Wed Apr 29 20:44:31 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,95 +0,0 @@ -/** - * This file contains primitives to interface with WebAssembly and - * with the Stone framework. - **/ -import * as Logger from './logger' - -export declare type InitializationCallback = () => void; - -//export declare var StoneFrameworkModule : any; -export var StoneFrameworkModule : any; - -//const ASSETS_FOLDER : string = "assets/lib"; -//const WASM_FILENAME : string = "orthanc-framework"; - -export class Framework -{ - private static singleton_ : Framework = null; - private static wasmModuleName_ : string = null; - - public static Configure(wasmModuleName: string) { - this.wasmModuleName_ = wasmModuleName; - } - - private constructor(verbose : boolean) - { - //this.ccall('Initialize', null, [ 'number' ], [ verbose ]); - } - - - public ccall( name: string, - returnType: string, - argTypes: Array, - argValues: Array) : any - { - return ( window).StoneFrameworkModule.ccall(name, returnType, argTypes, argValues); - } - - - public cwrap( name: string, - returnType: string, - argTypes: Array) : any - { - return ( window).StoneFrameworkModule.cwrap(name, returnType, argTypes); - } - - - public static GetInstance() : Framework - { - if (Framework.singleton_ == null) { - throw new Error('The WebAssembly module is not loaded yet'); - } else { - return Framework.singleton_; - } - } - - - public static Initialize( verbose: boolean, - callback: InitializationCallback) - { - Logger.defaultLogger.debug('Initializing WebAssembly Module'); - - ( window).errorFromCpp = function(text:any) { Logger.defaultLogger.errorFromCpp(text); }; - ( window).warningFromCpp = function(text:any) { Logger.defaultLogger.warningFromCpp(text); }; - ( window).infoFromCpp = function(text:any) { Logger.defaultLogger.infoFromCpp(text); }; - ( window).debugFromCpp = function(text:any) { Logger.defaultLogger.debugFromCpp(text); }; - - // ( window). - ( window).StoneFrameworkModule = { - preRun: [ - function() { - Logger.defaultLogger.debug('Loading the Stone Framework using WebAssembly'); - } - ], - postRun: [ - function() { - // This function is called by ".js" wrapper once the ".wasm" - // WebAssembly module has been loaded and compiled by the - // browser - Logger.defaultLogger.debug('WebAssembly is ready'); - Framework.singleton_ = new Framework(verbose); - callback(); - } - ], - totalDependencies: 0 - }; - - // Dynamic loading of the JavaScript wrapper around WebAssembly - var script = document.createElement('script'); - script.type = 'application/javascript'; - //script.src = "orthanc-stone.js"; // ASSETS_FOLDER + '/' + WASM_FILENAME + '.js'; - script.src = this.wasmModuleName_ + ".js";// "OrthancStoneSimpleViewer.js"; // ASSETS_FOLDER + '/' + WASM_FILENAME + '.js'; - script.async = true; - document.head.appendChild(script); - } -} diff -r ff8d2e46ac63 -r 419d0320c344 Platforms/Wasm/tsconfig-stone.json --- a/Platforms/Wasm/tsconfig-stone.json Wed Apr 29 20:44:31 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,8 +0,0 @@ -{ - "include" : [ - "stone-framework-loader.ts", - "logger.ts", - "wasm-application-runner.ts", - "wasm-viewport.ts" - ] -} diff -r ff8d2e46ac63 -r 419d0320c344 Platforms/Wasm/wasm-application-runner.ts --- a/Platforms/Wasm/wasm-application-runner.ts Wed Apr 29 20:44:31 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,141 +0,0 @@ -import * as Stone from './stone-framework-loader' -import * as StoneViewport from './wasm-viewport' -import * as Logger from './logger' - -if (!('WebAssembly' in window)) { - alert('Sorry, your browser does not support WebAssembly :('); -} - -//var StoneFrameworkModule : Stone.Framework = (window).StoneFrameworkModule; -//export declare var StoneFrameworkModule : Stone.Framework; - -// global functions -var WasmWebService_NotifyError: Function = null; -var WasmWebService_NotifySuccess: Function = null; -var WasmWebService_NotifyCachedSuccess: Function = null; -var WasmDelayedCallExecutor_ExecuteCallback: Function = null; -var WasmDoAnimation: Function = null; -var SetStartupParameter: Function = null; -var CreateWasmApplication: Function = null; -export var CreateCppViewport: Function = null; -var ReleaseCppViewport: Function = null; -var StartWasmApplication: Function = null; -export var SendSerializedMessageToStoneApplication: Function = null; - -var auxiliaryParameters : Map = null; - -export function SetApplicationParameters(params : Map) { - if (auxiliaryParameters != null) { - console.warn("wasm-application-runner.SetApplicationParameters: about to overwrite the existing application parameters!") - } - auxiliaryParameters = params; -} - -function DoAnimationThread() { - if (WasmDoAnimation != null) { - WasmDoAnimation(); - } - - // Update the viewport content every 100ms if need be - setTimeout(DoAnimationThread, 100); -} - - -function GetUriParameters(): Map { - var parameters = window.location.search.substr(1); - - if (parameters != null && - parameters != '') { - var result = new Map(); - var tokens = parameters.split('&'); - - for (var i = 0; i < tokens.length; i++) { - var tmp = tokens[i].split('='); - if (tmp.length == 2) { - result[tmp[0]] = decodeURIComponent(tmp[1]); - } else if(tmp.length == 1) { - // if there is no '=', we treat ot afterwards as a flag-style param - result[tmp[0]] = ""; - } - } - return result; - } - else { - return new Map(); - } -} - -// function UpdateWebApplication(statusUpdateMessage: string) { -// console.log(statusUpdateMessage); -// } - -function _InitializeWasmApplication(orthancBaseUrl: string): void { - - CreateWasmApplication(); - - // transmit the API-specified parameters to the app before initializing it - for (let key in auxiliaryParameters) { - if (auxiliaryParameters.hasOwnProperty(key)) { - Logger.defaultLogger.debug( - `About to call SetStartupParameter("${key}","${auxiliaryParameters[key]}")`); - SetStartupParameter(key, auxiliaryParameters[key]); - } - } - - // parse uri and transmit the URI parameters to the app before initializing it - let parameters = GetUriParameters(); - - for (let key in parameters) { - if (parameters.hasOwnProperty(key)) { - Logger.defaultLogger.debug( - `About to call SetStartupParameter("${key}","${parameters[key]}")`); - SetStartupParameter(key, parameters[key]); - } - } - - StartWasmApplication(orthancBaseUrl); - - // trigger a first resize of the canvas that has just been initialized - StoneViewport.WasmViewport.ResizeAll(); - - DoAnimationThread(); -} - -export function InitializeWasmApplication(wasmModuleName: string, orthancBaseUrl: string) { - - Stone.Framework.Configure(wasmModuleName); - - // Wait for the Orthanc Framework to be initialized (this initializes - // the WebAssembly environment) and then, create and initialize the Wasm application - Stone.Framework.Initialize(true, function () { - - Logger.defaultLogger.debug("Connecting C++ methods to JS methods"); - - SetStartupParameter = ( window).StoneFrameworkModule.cwrap('SetStartupParameter', null, ['string', 'string']); - CreateWasmApplication = ( window).StoneFrameworkModule.cwrap('CreateWasmApplication', null, ['number']); - CreateCppViewport = ( window).StoneFrameworkModule.cwrap('CreateCppViewport', 'number', []); - ReleaseCppViewport = ( window).StoneFrameworkModule.cwrap('ReleaseCppViewport', null, ['number']); - StartWasmApplication = ( window).StoneFrameworkModule.cwrap('StartWasmApplication', null, ['string']); - ( window).IsTraceLevelEnabled = ( window).StoneFrameworkModule.cwrap('WasmIsTraceLevelEnabled', 'boolean', null); - ( window).IsInfoLevelEnabled = ( window).StoneFrameworkModule.cwrap('WasmIsInfoLevelEnabled', 'boolean', null); - - ( window).WasmWebService_NotifyCachedSuccess = ( window).StoneFrameworkModule.cwrap('WasmWebService_NotifyCachedSuccess', null, ['number']); - ( window).WasmWebService_NotifySuccess = ( window).StoneFrameworkModule.cwrap('WasmWebService_NotifySuccess', null, ['number', 'string', 'array', 'number', 'number']); - ( window).WasmWebService_NotifyError = ( window).StoneFrameworkModule.cwrap('WasmWebService_NotifyError', null, ['number', 'string', 'number', 'number']); - ( window).WasmDelayedCallExecutor_ExecuteCallback = ( window).StoneFrameworkModule.cwrap('WasmDelayedCallExecutor_ExecuteCallback', null, ['number']); - // no need to put this into the globals for it's only used in this very module - WasmDoAnimation = ( window).StoneFrameworkModule.cwrap('WasmDoAnimation', null, []); - - SendSerializedMessageToStoneApplication = ( window).StoneFrameworkModule.cwrap('SendSerializedMessageToStoneApplication', 'string', ['string']); - - Logger.defaultLogger.debug("Connecting C++ methods to JS methods - done"); - - _InitializeWasmApplication(orthancBaseUrl); - }); -} - - -// exports.InitializeWasmApplication = InitializeWasmApplication; - - - diff -r ff8d2e46ac63 -r 419d0320c344 Platforms/Wasm/wasm-viewport.ts --- a/Platforms/Wasm/wasm-viewport.ts Wed Apr 29 20:44:31 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,359 +0,0 @@ -import * as wasmApplicationRunner from './wasm-application-runner' -import * as Logger from './logger' - -var isPendingRedraw = false; - -function ScheduleWebViewportRedraw(cppViewportHandle: any) : void -{ - if (!isPendingRedraw) { - isPendingRedraw = true; - Logger.defaultLogger.debug('Scheduling a refresh of the viewport, as its content changed'); - window.requestAnimationFrame(function() { - isPendingRedraw = false; - let viewport = WasmViewport.GetFromCppViewport(cppViewportHandle); - if (viewport) { - viewport.Redraw(); - } - }); - } -} - -(window).ScheduleWebViewportRedraw = ScheduleWebViewportRedraw; - -declare function UTF8ToString(v: any): string; - -function CreateWasmViewport(htmlCanvasId: string) : any { - var cppViewportHandle = wasmApplicationRunner.CreateCppViewport(); - var canvasId = UTF8ToString(htmlCanvasId); - var webViewport = new WasmViewport(( window).StoneFrameworkModule, canvasId, cppViewportHandle); // viewports are stored in a static map in WasmViewport -> won't be deleted - webViewport.Initialize(); - - return cppViewportHandle; -} - -(window).CreateWasmViewport = CreateWasmViewport; - -export class WasmViewport { - - private static viewportsMapByCppHandle_ : Map = new Map(); // key = the C++ handle - private static viewportsMapByCanvasId_ : Map = new Map(); // key = the canvasId - - private module_ : any; - private canvasId_ : string; - private htmlCanvas_ : HTMLCanvasElement; - private context_ : CanvasRenderingContext2D | null; - private imageData_ : any = null; - private renderingBuffer_ : any = null; - - private touchGestureInProgress_: boolean = false; - private touchCount_: number = 0; - private touchGestureLastCoordinates_: [number, number][] = []; // last x,y coordinates of each touch - - private touchZoom_ : any = false; - private touchTranslation_ : any = false; - - private ViewportSetSize : Function; - private ViewportRender : Function; - private ViewportMouseDown : Function; - private ViewportMouseMove : Function; - private ViewportMouseUp : Function; - private ViewportMouseEnter : Function; - private ViewportMouseLeave : Function; - private ViewportMouseWheel : Function; - private ViewportKeyPressed : Function; - private ViewportTouchStart : Function; - private ViewportTouchMove : Function; - private ViewportTouchEnd : Function; - - private pimpl_ : any; // Private pointer to the underlying WebAssembly C++ object - - public constructor(module: any, canvasId: string, cppViewport: any) { - - this.pimpl_ = cppViewport; - WasmViewport.viewportsMapByCppHandle_[this.pimpl_] = this; - WasmViewport.viewportsMapByCanvasId_[canvasId] = this; - - this.module_ = module; - this.canvasId_ = canvasId; - this.htmlCanvas_ = document.getElementById(this.canvasId_) as HTMLCanvasElement; - if (this.htmlCanvas_ == null) { - Logger.defaultLogger.error("Can not create WasmViewport, did not find the canvas whose id is '", this.canvasId_, "'"); - } - this.context_ = this.htmlCanvas_.getContext('2d'); - - this.ViewportSetSize = this.module_.cwrap('ViewportSetSize', null, [ 'number', 'number', 'number' ]); - this.ViewportRender = this.module_.cwrap('ViewportRender', null, [ 'number', 'number', 'number', 'number' ]); - this.ViewportMouseDown = this.module_.cwrap('ViewportMouseDown', null, [ 'number', 'number', 'number', 'number', 'number' ]); - this.ViewportMouseMove = this.module_.cwrap('ViewportMouseMove', null, [ 'number', 'number', 'number' ]); - this.ViewportMouseUp = this.module_.cwrap('ViewportMouseUp', null, [ 'number' ]); - this.ViewportMouseEnter = this.module_.cwrap('ViewportMouseEnter', null, [ 'number' ]); - this.ViewportMouseLeave = this.module_.cwrap('ViewportMouseLeave', null, [ 'number' ]); - this.ViewportMouseWheel = this.module_.cwrap('ViewportMouseWheel', null, [ 'number', 'number', 'number', 'number', 'number' ]); - this.ViewportKeyPressed = this.module_.cwrap('ViewportKeyPressed', null, [ 'number', 'number', 'string', 'number', 'number' ]); - this.ViewportTouchStart = this.module_.cwrap('ViewportTouchStart', null, [ 'number', 'number', 'number', 'number', 'number', 'number', 'number' ]); - this.ViewportTouchMove = this.module_.cwrap('ViewportTouchMove', null, [ 'number', 'number', 'number', 'number', 'number', 'number', 'number' ]); - this.ViewportTouchEnd = this.module_.cwrap('ViewportTouchEnd', null, [ 'number', 'number', 'number', 'number', 'number', 'number', 'number' ]); - } - - public GetCppViewport() : number { - return this.pimpl_; - } - - public static GetFromCppViewport(cppViewportHandle: number) : WasmViewport | null { - if (WasmViewport.viewportsMapByCppHandle_[cppViewportHandle] !== undefined) { - return WasmViewport.viewportsMapByCppHandle_[cppViewportHandle]; - } - Logger.defaultLogger.error("WasmViewport not found !"); - return null; - } - - public static GetFromCanvasId(canvasId: string) : WasmViewport | null { - if (WasmViewport.viewportsMapByCanvasId_[canvasId] !== undefined) { - return WasmViewport.viewportsMapByCanvasId_[canvasId]; - } - Logger.defaultLogger.error("WasmViewport not found !"); - return null; - } - - public static ResizeAll() { - for (let canvasId in WasmViewport.viewportsMapByCanvasId_) { - WasmViewport.viewportsMapByCanvasId_[canvasId].Resize(); - } - } - - public Redraw() { - if (this.imageData_ === null || - this.renderingBuffer_ === null || - this.ViewportRender(this.pimpl_, - this.imageData_.width, - this.imageData_.height, - this.renderingBuffer_) == 0) { - Logger.defaultLogger.error('The rendering has failed'); - } else { - // Create an accessor to the rendering buffer (i.e. create a - // "window" above the heap of the WASM module), then copy it to - // the ImageData object - this.imageData_.data.set(new Uint8ClampedArray( - this.module_.HEAPU8.buffer, - this.renderingBuffer_, - this.imageData_.width * this.imageData_.height * 4)); - - if (this.context_) { - this.context_.putImageData(this.imageData_, 0, 0); - } - } - } - - public Resize() { - if (this.imageData_ != null && - (this.imageData_.width != window.innerWidth || - this.imageData_.height != window.innerHeight)) { - this.imageData_ = null; - } - - // width/height is defined by the parent width/height - if (this.htmlCanvas_.parentElement) { - this.htmlCanvas_.width = this.htmlCanvas_.parentElement.offsetWidth; - this.htmlCanvas_.height = this.htmlCanvas_.parentElement.offsetHeight; - - Logger.defaultLogger.debug("resizing WasmViewport: ", this.htmlCanvas_.width, "x", this.htmlCanvas_.height); - - if (this.imageData_ === null && this.context_) { - this.imageData_ = this.context_.getImageData(0, 0, this.htmlCanvas_.width, this.htmlCanvas_.height); - this.ViewportSetSize(this.pimpl_, this.htmlCanvas_.width, this.htmlCanvas_.height); - - if (this.renderingBuffer_ != null) { - this.module_._free(this.renderingBuffer_); - } - - this.renderingBuffer_ = this.module_._malloc(this.imageData_.width * this.imageData_.height * 4); - } else { - this.ViewportSetSize(this.pimpl_, this.htmlCanvas_.width, this.htmlCanvas_.height); - } - - this.Redraw(); - } - } - - public Initialize() { - - // Force the rendering of the viewport for the first time - this.Resize(); - - var that : WasmViewport = this; - // Register an event listener to call the Resize() function - // each time the window is resized. - window.addEventListener('resize', function(event) { - that.Resize(); - }, false); - - this.htmlCanvas_.addEventListener('contextmenu', function(event) { - // Prevent right click on the canvas - event.preventDefault(); - }, false); - - this.htmlCanvas_.addEventListener('mouseleave', function(event) { - that.ViewportMouseLeave(that.pimpl_); - }); - - this.htmlCanvas_.addEventListener('mouseenter', function(event) { - that.ViewportMouseEnter(that.pimpl_); - }); - - this.htmlCanvas_.addEventListener('mousedown', function(event) { - var x = event.pageX - this.offsetLeft; - var y = event.pageY - this.offsetTop; - - that.ViewportMouseDown(that.pimpl_, event.button, x, y, 0 /* TODO detect modifier keys*/); - }); - - this.htmlCanvas_.addEventListener('mousemove', function(event) { - var x = event.pageX - this.offsetLeft; - var y = event.pageY - this.offsetTop; - that.ViewportMouseMove(that.pimpl_, x, y); - }); - - this.htmlCanvas_.addEventListener('mouseup', function(event) { - that.ViewportMouseUp(that.pimpl_); - }); - - window.addEventListener('keydown', function(event) { - var keyChar: string | null = event.key; - var keyCode = event.keyCode - if (keyChar.length == 1) { - keyCode = 0; // maps to OrthancStone::KeyboardKeys_Generic - } else { - keyChar = null; - } -// console.log("key: ", keyCode, keyChar); - that.ViewportKeyPressed(that.pimpl_, keyCode, keyChar, event.shiftKey, event.ctrlKey, event.altKey); - }); - - this.htmlCanvas_.addEventListener('wheel', function(event) { - var x = event.pageX - this.offsetLeft; - var y = event.pageY - this.offsetTop; - that.ViewportMouseWheel(that.pimpl_, event.deltaY, x, y, event.ctrlKey); - event.preventDefault(); - }, {passive: false}); // must not be passive if calling event.preventDefault, ie to cancel scroll or zoom of the whole interface - - this.htmlCanvas_.addEventListener('touchstart', function(event: TouchEvent) { - // don't propagate events to the whole body (this could zoom the entire page instead of zooming the viewport) - event.preventDefault(); - event.stopPropagation(); - - // TODO: find a way to pass the coordinates as an array between JS and C++ - var x0 = 0; - var y0 = 0; - var x1 = 0; - var y1 = 0; - var x2 = 0; - var y2 = 0; - if (event.targetTouches.length > 0) { - x0 = event.targetTouches[0].pageX; - y0 = event.targetTouches[0].pageY; - } - if (event.targetTouches.length > 1) { - x1 = event.targetTouches[1].pageX; - y1 = event.targetTouches[1].pageY; - } - if (event.targetTouches.length > 2) { - x2 = event.targetTouches[2].pageX; - y2 = event.targetTouches[2].pageY; - } - - that.ViewportTouchStart(that.pimpl_, event.targetTouches.length, x0, y0, x1, y1, x2, y2); - }, {passive: false}); // must not be passive if calling event.preventDefault, ie to cancel scroll or zoom of the whole interface - - this.htmlCanvas_.addEventListener('touchend', function(event) { - // don't propagate events to the whole body (this could zoom the entire page instead of zooming the viewport) - event.preventDefault(); - event.stopPropagation(); - - // TODO: find a way to pass the coordinates as an array between JS and C++ - var x0 = 0; - var y0 = 0; - var x1 = 0; - var y1 = 0; - var x2 = 0; - var y2 = 0; - if (event.targetTouches.length > 0) { - x0 = event.targetTouches[0].pageX; - y0 = event.targetTouches[0].pageY; - } - if (event.targetTouches.length > 1) { - x1 = event.targetTouches[1].pageX; - y1 = event.targetTouches[1].pageY; - } - if (event.targetTouches.length > 2) { - x2 = event.targetTouches[2].pageX; - y2 = event.targetTouches[2].pageY; - } - - that.ViewportTouchEnd(that.pimpl_, event.targetTouches.length, x0, y0, x1, y1, x2, y2); - }); - - this.htmlCanvas_.addEventListener('touchmove', function(event: TouchEvent) { - - // don't propagate events to the whole body (this could zoom the entire page instead of zooming the viewport) - event.preventDefault(); - event.stopPropagation(); - - - // TODO: find a way to pass the coordinates as an array between JS and C++ - var x0 = 0; - var y0 = 0; - var x1 = 0; - var y1 = 0; - var x2 = 0; - var y2 = 0; - if (event.targetTouches.length > 0) { - x0 = event.targetTouches[0].pageX; - y0 = event.targetTouches[0].pageY; - } - if (event.targetTouches.length > 1) { - x1 = event.targetTouches[1].pageX; - y1 = event.targetTouches[1].pageY; - } - if (event.targetTouches.length > 2) { - x2 = event.targetTouches[2].pageX; - y2 = event.targetTouches[2].pageY; - } - - that.ViewportTouchMove(that.pimpl_, event.targetTouches.length, x0, y0, x1, y1, x2, y2); - return; - - }, {passive: false}); // must not be passive if calling event.preventDefault, ie to cancel scroll or zoom of the whole interface - } - - public ResetTouch() { - if (this.touchTranslation_ || - this.touchZoom_) { - this.ViewportMouseUp(this.pimpl_); - } - - this.touchTranslation_ = false; - this.touchZoom_ = false; - } - - public GetTouchTranslation(event: any) { - var touch = event.targetTouches[0]; - return [ - touch.pageX, - touch.pageY - ]; - } - - public GetTouchZoom(event: any) { - var touch1 = event.targetTouches[0]; - var touch2 = event.targetTouches[1]; - var dx = (touch1.pageX - touch2.pageX); - var dy = (touch1.pageY - touch2.pageY); - var d = Math.sqrt(dx * dx + dy * dy); - return [ - (touch1.pageX + touch2.pageX) / 2.0, - (touch1.pageY + touch2.pageY) / 2.0, - d - ]; - } - -}