Mercurial > hg > orthanc-stone
view Samples/Sdl/RtViewer/RtViewerSdl.cpp @ 1392:ffdb82850e98
Sdl run loop in /Common (might revert if need to customize)
Segregation ongoing between sdl wasm
Both samples ok SDL msvc v141 x64
author | Benjamin Golinvaux <bgo@osimis.io> |
---|---|
date | Tue, 28 Apr 2020 13:52:21 +0200 |
parents | 5c83be3a6be5 |
children | 27e0a00bd3e8 |
line wrap: on
line source
/** * 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 <http://www.gnu.org/licenses/>. **/ #include "RtViewer.h" #include "../SdlHelpers.h" #include "SampleHelpers.h" #include <Framework/StoneException.h> #include <Framework/StoneInitialization.h> #include <Framework/OpenGL/SdlOpenGLContext.h> #include <boost/program_options.hpp> #include <SDL.h> #include <string> static void GLAPIENTRY OpenGLMessageCallback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar* message, const void* userParam) { if (severity != GL_DEBUG_SEVERITY_NOTIFICATION) { fprintf(stderr, "GL CALLBACK: %s type = 0x%x, severity = 0x%x, message = %s\n", (type == GL_DEBUG_TYPE_ERROR ? "** GL ERROR **" : ""), type, severity, message); } } namespace OrthancStone { void RtViewerApp::ProcessOptions(int argc, char* argv[]) { namespace po = boost::program_options; po::options_description desc("Usage:"); desc.add_options() ("loglevel", po::value<std::string>()->default_value("WARNING"), "You can choose WARNING, INFO or TRACE for the logging level: Errors and warnings will always be displayed. (default: WARNING)") ("orthanc", po::value<std::string>()->default_value("http://localhost:8042"), "Base URL of the Orthanc instance") ("ctseries", po::value<std::string>()->default_value("a04ecf01-79b2fc33-58239f7e-ad9db983-28e81afa"), "Orthanc ID of the CT series to load") ("rtdose", po::value<std::string>()->default_value("830a69ff-8e4b5ee3-b7f966c8-bccc20fb-d322dceb"), "Orthanc ID of the RTDOSE instance to load") ("rtstruct", po::value<std::string>()->default_value("54460695-ba3885ee-ddf61ac0-f028e31d-a6e474d9"), "Orthanc ID of the RTSTRUCT instance to load") ; po::variables_map vm; try { po::store(po::parse_command_line(argc, argv, desc), vm); po::notify(vm); } catch (std::exception& e) { std::cerr << "Please check your command line options! (\"" << e.what() << "\")" << std::endl; } if (vm.count("loglevel") > 0) { std::string logLevel = vm["loglevel"].as<std::string>(); OrthancStoneHelpers::SetLogLevel(logLevel); } if (vm.count("orthanc") > 0) { // maybe check URL validity here orthancUrl_ = vm["orthanc"].as<std::string>(); } if (vm.count("ctseries") > 0) { ctSeriesId_ = vm["ctseries"].as<std::string>(); } if (vm.count("rtdose") > 0) { doseInstanceId_ = vm["rtdose"].as<std::string>(); } if (vm.count("rtstruct") > 0) { rtStructInstanceId_ = vm["rtstruct"].as<std::string>(); } } void RtViewerApp::RunSdl(int argc, char* argv[]) { ProcessOptions(argc, argv); { std::unique_ptr<IViewport::ILock> lock(viewport_->Lock()); ViewportController& controller = lock->GetController(); Scene2D& scene = controller.GetScene(); ICompositor& compositor = lock->GetCompositor(); // False means we do NOT let a hi-DPI aware desktop managedr treat this as a legacy application that requires // scaling. controller.FitContent(compositor.GetCanvasWidth(), compositor.GetCanvasHeight()); glEnable(GL_DEBUG_OUTPUT); glDebugMessageCallback(OpenGLMessageCallback, 0); compositor.SetFont(0, Orthanc::EmbeddedResources::UBUNTU_FONT, FONT_SIZE_0, Orthanc::Encoding_Latin1); compositor.SetFont(1, Orthanc::EmbeddedResources::UBUNTU_FONT, FONT_SIZE_1, Orthanc::Encoding_Latin1); } /** Create the shared loaders context */ loadersContext_.reset(new GenericLoadersContext(1, 4, 1)); loadersContext_->StartOracle(); /** It is very important that the Oracle (responsible for network I/O) be started before creating and firing the loaders, for any command scheduled by the loader before the oracle is started will be lost. */ PrepareLoadersAndSlicers(); OrthancStone::DefaultViewportInteractor interactor; OrthancStoneHelpers::SdlRunLoop(viewport_, interactor); loadersContext_->StopOracle(); } #if 0 void RtViewerApp::HandleApplicationEvent( const SDL_Event& event) { //DisplayInfoText(); std::unique_ptr<IViewport::ILock> lock(viewport_->Lock()); ViewportController& controller = lock->GetController(); Scene2D& scene = controller.GetScene(); ICompositor& compositor = lock->GetCompositor(); if (event.type == SDL_MOUSEMOTION) { int scancodeCount = 0; const uint8_t* keyboardState = SDL_GetKeyboardState(&scancodeCount); if (activeTracker_.get() == NULL && SDL_SCANCODE_LALT < scancodeCount && keyboardState[SDL_SCANCODE_LALT]) { // The "left-ctrl" key is down, while no tracker is present // Let's display the info text PointerEvent e; e.AddPosition(compositor.GetPixelCenterCoordinates( event.button.x, event.button.y)); DisplayFloatingCtrlInfoText(e); } else { HideInfoText(); //LOG(TRACE) << "(event.type == SDL_MOUSEMOTION)"; if (activeTracker_.get() != NULL) { //LOG(TRACE) << "(activeTracker_.get() != NULL)"; PointerEvent e; e.AddPosition(compositor.GetPixelCenterCoordinates( event.button.x, event.button.y)); //LOG(TRACE) << "event.button.x = " << event.button.x << " " << // "event.button.y = " << event.button.y; LOG(TRACE) << "activeTracker_->PointerMove(e); " << e.GetMainPosition().GetX() << " " << e.GetMainPosition().GetY(); activeTracker_->PointerMove(e); if (!activeTracker_->IsAlive()) activeTracker_.reset(); } } } else if (event.type == SDL_MOUSEBUTTONUP) { if (activeTracker_) { PointerEvent e; e.AddPosition(compositor.GetPixelCenterCoordinates(event.button.x, event.button.y)); activeTracker_->PointerUp(e); if (!activeTracker_->IsAlive()) activeTracker_.reset(); } } else if (event.type == SDL_MOUSEBUTTONDOWN) { PointerEvent e; e.AddPosition(compositor.GetPixelCenterCoordinates( event.button.x, event.button.y)); if (activeTracker_) { activeTracker_->PointerDown(e); if (!activeTracker_->IsAlive()) activeTracker_.reset(); } else { // we ATTEMPT to create a tracker if need be activeTracker_ = CreateSuitableTracker(event, e); } } else if (event.type == SDL_KEYDOWN && event.key.repeat == 0 /* Ignore key bounce */) { switch (event.key.keysym.sym) { case SDLK_ESCAPE: if (activeTracker_) { activeTracker_->Cancel(); if (!activeTracker_->IsAlive()) activeTracker_.reset(); } break; case SDLK_r: UpdateLayers(); { std::unique_ptr<IViewport::ILock> lock(viewport_->Lock()); lock->Invalidate(); } break; case SDLK_s: compositor.FitContent(scene); break; case SDLK_t: if (!activeTracker_) SelectNextTool(); else { LOG(WARNING) << "You cannot change the active tool when an interaction" " is taking place"; } break; case SDLK_z: LOG(TRACE) << "SDLK_z has been pressed. event.key.keysym.mod == " << event.key.keysym.mod; if (event.key.keysym.mod & KMOD_CTRL) { if (controller.CanUndo()) { LOG(TRACE) << "Undoing..."; controller.Undo(); } else { LOG(WARNING) << "Nothing to undo!!!"; } } break; case SDLK_y: LOG(TRACE) << "SDLK_y has been pressed. event.key.keysym.mod == " << event.key.keysym.mod; if (event.key.keysym.mod & KMOD_CTRL) { if (controller.CanRedo()) { LOG(TRACE) << "Redoing..."; controller.Redo(); } else { LOG(WARNING) << "Nothing to redo!!!"; } } break; case SDLK_c: TakeScreenshot( "screenshot.png", compositor.GetCanvasWidth(), compositor.GetCanvasHeight()); break; default: break; } } else if (viewport_->IsRefreshEvent(event)) { // the viewport has been invalidated and requires repaint viewport_->Paint(); } } #endif #if 0 void RtViewerApp::RunSdl(int argc, char* argv[]) { ProcessOptions(argc, argv); { std::unique_ptr<IViewport::ILock> lock(viewport_->Lock()); ViewportController& controller = lock->GetController(); Scene2D& scene = controller.GetScene(); ICompositor& compositor = lock->GetCompositor(); // False means we do NOT let a hi-DPI aware desktop managedr treat this as a legacy application that requires // scaling. controller.FitContent(compositor.GetCanvasWidth(), compositor.GetCanvasHeight()); glEnable(GL_DEBUG_OUTPUT); glDebugMessageCallback(OpenGLMessageCallback, 0); compositor.SetFont(0, Orthanc::EmbeddedResources::UBUNTU_FONT, FONT_SIZE_0, Orthanc::Encoding_Latin1); compositor.SetFont(1, Orthanc::EmbeddedResources::UBUNTU_FONT, FONT_SIZE_1, Orthanc::Encoding_Latin1); } //////// from loader loadersContext_.reset(new GenericLoadersContext(1, 4, 1)); loadersContext_->StartOracle(); /** It is very important that the Oracle (responsible for network I/O) be started before creating and firing the loaders, for any command scheduled by the loader before the oracle is started will be lost. */ PrepareLoadersAndSlicers(); bool stopApplication = false; while (!stopApplication) { SDL_Event event; while (!stopApplication && SDL_PollEvent(&event)) { if (event.type == SDL_QUIT) { stopApplication = true; break; } else if (event.type == SDL_WINDOWEVENT && event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED) { DisableTracker(); } else if (event.type == SDL_KEYDOWN && event.key.repeat == 0 /* Ignore key bounce */) { switch (event.key.keysym.sym) { case SDLK_f: // TODO: implement GetWindow to be able to do: // viewport_->GetWindow().ToggleMaximize(); ORTHANC_ASSERT(false, "Please implement GetWindow()"); break; case SDLK_q: stopApplication = true; break; default: break; } } // the code above is rather application-neutral. // the following call handles events specific to the application HandleApplicationEvent(event); } SDL_Delay(1); } loadersContext_->StopOracle(); } #endif #if 0 boost::shared_ptr<IFlexiblePointerTracker> RtViewerApp::CreateSuitableTracker( const SDL_Event& event, const PointerEvent& e) { std::unique_ptr<IViewport::ILock> lock(viewport_->Lock()); ViewportController& controller = lock->GetController(); Scene2D& scene = controller.GetScene(); ICompositor& compositor = lock->GetCompositor(); using namespace Orthanc; switch (event.button.button) { case SDL_BUTTON_MIDDLE: return boost::shared_ptr<IFlexiblePointerTracker>(new PanSceneTracker (viewport_, e)); case SDL_BUTTON_RIGHT: return boost::shared_ptr<IFlexiblePointerTracker>(new ZoomSceneTracker (viewport_, e, compositor.GetCanvasHeight())); case SDL_BUTTON_LEFT: { //LOG(TRACE) << "CreateSuitableTracker: case SDL_BUTTON_LEFT:"; // TODO: we need to iterate on the set of measuring tool and perform // a hit test to check if a tracker needs to be created for edition. // Otherwise, depending upon the active tool, we might want to create // a "measuring tool creation" tracker // TODO: if there are conflicts, we should prefer a tracker that // pertains to the type of measuring tool currently selected (TBD?) boost::shared_ptr<IFlexiblePointerTracker> hitTestTracker = TrackerHitTest(e); if (hitTestTracker != NULL) { //LOG(TRACE) << "hitTestTracker != NULL"; return hitTestTracker; } else { switch (currentTool_) { case RtViewerGuiTool_Rotate: //LOG(TRACE) << "Creating RotateSceneTracker"; return boost::shared_ptr<IFlexiblePointerTracker>(new RotateSceneTracker(viewport_, e)); case RtViewerGuiTool_Pan: return boost::shared_ptr<IFlexiblePointerTracker>(new PanSceneTracker(viewport_, e)); case RtViewerGuiTool_Zoom: return boost::shared_ptr<IFlexiblePointerTracker>(new ZoomSceneTracker(viewport_, e, compositor.GetCanvasHeight())); //case GuiTool_AngleMeasure: // return new AngleMeasureTracker(GetScene(), e); //case GuiTool_CircleMeasure: // return new CircleMeasureTracker(GetScene(), e); //case GuiTool_EllipseMeasure: // return new EllipseMeasureTracker(GetScene(), e); case RtViewerGuiTool_LineMeasure: return boost::shared_ptr<IFlexiblePointerTracker>(new CreateLineMeasureTracker(viewport_, e)); case RtViewerGuiTool_AngleMeasure: return boost::shared_ptr<IFlexiblePointerTracker>(new CreateAngleMeasureTracker(viewport_, e)); case RtViewerGuiTool_CircleMeasure: LOG(ERROR) << "Not implemented yet!"; return boost::shared_ptr<IFlexiblePointerTracker>(); case RtViewerGuiTool_EllipseMeasure: LOG(ERROR) << "Not implemented yet!"; return boost::shared_ptr<IFlexiblePointerTracker>(); default: throw OrthancException(ErrorCode_InternalError, "Wrong tool!"); } } } default: return boost::shared_ptr<IFlexiblePointerTracker>(); } } #endif } boost::weak_ptr<OrthancStone::RtViewerApp> g_app; /** * IMPORTANT: The full arguments to "main()" are needed for SDL on * Windows. Otherwise, one gets the linking error "undefined reference * to `SDL_main'". https://wiki.libsdl.org/FAQWindows **/ int main(int argc, char* argv[]) { using namespace OrthancStone; StoneInitialize(); try { boost::shared_ptr<RtViewerApp> app = RtViewerApp::Create(); g_app = app; app->RunSdl(argc,argv); } catch (Orthanc::OrthancException& e) { LOG(ERROR) << "EXCEPTION: " << e.What(); } StoneFinalize(); return 0; }