diff -Nru simgear-2017.3.1+dfsg/CMakeLists.txt simgear-2018.1.1+dfsg/CMakeLists.txt --- simgear-2017.3.1+dfsg/CMakeLists.txt 2017-09-20 07:51:07.000000000 +0000 +++ simgear-2018.1.1+dfsg/CMakeLists.txt 2018-04-06 19:43:17.000000000 +0000 @@ -7,6 +7,9 @@ if(POLICY CMP0042) cmake_policy(SET CMP0042 NEW) endif() + if(POLICY CMP0067) + cmake_policy(SET CMP0067 NEW) + endif() endif() @@ -49,7 +52,7 @@ # add a dependency on the versino file set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS version) -set(FIND_LIBRARY_USE_LIB64_PATHS ON) +set_property(GLOBAL PROPERTY FIND_LIBRARY_USE_LIB64_PATHS TRUE) # use simgear version also as the SO version (if building SOs) SET(SIMGEAR_SOVERSION ${SIMGEAR_VERSION}) @@ -135,6 +138,16 @@ # keep the compatability link option as the default option(OSG_FSTREAM_EXPORT_FIXED "Set to ON if the osgDB fstream export patch is applied" OFF) +if (CMAKE_COMPILER_IS_GNUCXX OR CLANG) + if (CMAKE_VERSION VERSION_LESS 3.1) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++${CMAKE_CXX_STANDARD}") + elseif (CMAKE_VERSION VERSION_LESS 3.8) + # policy CMP0067 (try_compile does not honor CMAKE_CXX_STANDARD) + set(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} -std=c++${CMAKE_CXX_STANDARD}") + endif() +endif() + + if (MSVC) GET_FILENAME_COMPONENT(PARENT_DIR ${PROJECT_BINARY_DIR} PATH) if (CMAKE_CL_64) @@ -343,7 +356,7 @@ # isnan might not be real symbol, so can't check using function_exists check_cxx_source_compiles( - "#include + "#include int main() { return std::isnan(0.0);} " HAVE_STD_ISNAN) @@ -351,14 +364,35 @@ message(FATAL_ERROR "Your compiler lacks C++11 std::isnan, please update it") endif() +# Check if the implementation in the C++ standard library is usable. +# This is necessary because g++ 4.8 lies about its C++11 compliance: its +# is utterly unusable, cf. [1]. +# The big preprocessor test essentially comes from [2], and gcc upstream devs +# appear to back it (see comments following [2], as well as [3]). +# +# [1] https://stackoverflow.com/a/12665408/4756009 +# [2] https://stackoverflow.com/a/41186162/4756009 +# [3] https://gcc.gnu.org/bugzilla/show_bug.cgi?id=78905 +check_cxx_source_compiles( + "#include + + int main() { + #if __cplusplus >= 201103L && \ + (!defined(__GLIBCXX__) || \ + (__cplusplus >= 201402L) || \ + defined(_GLIBCXX_REGEX_DFS_QUANTIFIERS_LIMIT) || \ + defined(_GLIBCXX_REGEX_STATE_LIMIT) || \ + (defined(_GLIBCXX_RELEASE) && _GLIBCXX_RELEASE > 4)) + #else + nullptr = void; // intentionally trigger a compilation error + #endif + }" + HAVE_WORKING_STD_REGEX) + if(CMAKE_COMPILER_IS_GNUCXX) set(WARNING_FLAGS_CXX "-Wall -fPIC") set(WARNING_FLAGS_C "-Wall -fPIC") - if (CMAKE_VERSION VERSION_LESS 3.1) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") - endif() - if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.4) message(WARNING "GCC 4.4 will be required soon, please upgrade") endif() @@ -386,10 +420,6 @@ # fix Boost compilation :( set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++") - if (CMAKE_VERSION VERSION_LESS 3.1) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") - endif() - if(ENABLE_SIMD) if (X86 OR X86_64) set(CMAKE_C_FLAGS_RELEASE "-O3 -msse2 -mfpmath=sse") @@ -462,6 +492,8 @@ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${WARNING_FLAGS_CXX} ${MSVC_FLAGS} ${BOOST_CXX_FLAGS}") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${MSVC_LD_FLAGS}") +include(CheckCXXFeatures) + # use BEFORE to ensure local directories are used first, # ahead of system-installed libs include_directories(BEFORE ${PROJECT_BINARY_DIR}/simgear) diff -Nru simgear-2017.3.1+dfsg/CMakeModules/CheckCXXFeatures.cmake simgear-2018.1.1+dfsg/CMakeModules/CheckCXXFeatures.cmake --- simgear-2017.3.1+dfsg/CMakeModules/CheckCXXFeatures.cmake 1970-01-01 00:00:00.000000000 +0000 +++ simgear-2018.1.1+dfsg/CMakeModules/CheckCXXFeatures.cmake 2018-04-06 19:43:17.000000000 +0000 @@ -0,0 +1,30 @@ +check_cxx_source_compiles(" + #include + #include + std::make_index_sequence<0> t; + int main() {}" HAVE_STD_INDEX_SEQUENCE +) + +check_cxx_source_compiles(" + #include + std::remove_cv_t t; + int main() {}" HAVE_STD_REMOVE_CV_T +) + +check_cxx_source_compiles(" + #include + std::remove_cvref_t t; + int main() {}" HAVE_STD_REMOVE_CVREF_T +) + +check_cxx_source_compiles(" + #include + std::enable_if_t t; + int main() {}" HAVE_STD_ENABLE_IF_T +) + +check_cxx_source_compiles(" + #include + std::bool_constant t; + int main() {}" HAVE_STD_BOOL_CONSTANT +) \ No newline at end of file diff -Nru simgear-2017.3.1+dfsg/CMakeModules/FindAAX.cmake simgear-2018.1.1+dfsg/CMakeModules/FindAAX.cmake --- simgear-2017.3.1+dfsg/CMakeModules/FindAAX.cmake 2017-09-20 07:51:07.000000000 +0000 +++ simgear-2018.1.1+dfsg/CMakeModules/FindAAX.cmake 2018-04-06 19:43:17.000000000 +0000 @@ -10,12 +10,13 @@ # # Created by Erik Hofman. -FIND_PATH(AAX_INCLUDE_DIR aax/aeonwave.hpp +FIND_PATH(AAX_INCLUDE_DIR aax/aax.h HINTS $ENV{AAXDIR} $ENV{ProgramFiles}/aax $ENV{ProgramFiles}/AeonWave $ENV{ProgramFiles}/Adalin/AeonWave + ${CMAKE_SOURCE_DIR}/aax PATH_SUFFIXES include PATHS ~/Library/Frameworks @@ -26,23 +27,35 @@ ) FIND_LIBRARY(AAX_LIBRARY - NAMES AAX aax AAX32 libAAX32 + NAMES AAX aax AAX32 HINTS $ENV{AAXDIR} $ENV{ProgramFiles}/AAX $ENV{ProgramFiles}/AeonWave $ENV{ProgramFiles}/Adalin/AeonWave + ${CMAKE_BUILD_DIR}/aax PATH_SUFFIXES bin lib lib/${CMAKE_LIBRARY_ARCHITECTURE} lib64 libs64 libs libs/Win32 libs/Win64 PATHS ~/Library/Frameworks /Library/Frameworks + /usr/local /usr /opt - /usr/local ) -SET(AAX_FOUND "NO") IF(AAX_LIBRARY AND AAX_INCLUDE_DIR) SET(AAX_FOUND "YES") +ELSE(AAX_LIBRARY AND AAX_INCLUDE_DIR) + IF(NOT AAX_INCLUDE_DIR) + MESSAGE(FATAL_ERROR "Unable to find the AAX library development files.") + SET(AAX_FOUND "NO") + ENDIF(NOT AAX_INCLUDE_DIR) + IF(NOT AAX_LIBRARY) + IF(SINGLE_PACKAGE) + SET(AAX_LIBRARY "${aax_BUILD_DIR}/aax/AAX32.dll") + SET(AAX_FOUND "YES") + ELSE(SINGLE_PACKAGE) + ENDIF(SINGLE_PACKAGE) + ENDIF(NOT AAX_LIBRARY) ENDIF(AAX_LIBRARY AND AAX_INCLUDE_DIR) diff -Nru simgear-2017.3.1+dfsg/debian/changelog simgear-2018.1.1+dfsg/debian/changelog --- simgear-2017.3.1+dfsg/debian/changelog 2018-03-13 10:24:51.000000000 +0000 +++ simgear-2018.1.1+dfsg/debian/changelog 2018-04-22 10:22:17.000000000 +0000 @@ -1,3 +1,12 @@ +simgear (1:2018.1.1+dfsg-1) unstable; urgency=medium + + * New upstream version 2018.1.1+dfsg + - Merge all packaging changes from experimental uploads + * Add myself to Uploaders + * Update Standards-Version to 4.1.4, no changes needed + + -- Dr. Tobias Quathamer Sun, 22 Apr 2018 12:22:17 +0200 + simgear (1:2017.3.1+dfsg-3) unstable; urgency=medium * Team upload. @@ -19,6 +28,42 @@ -- Dr. Tobias Quathamer Tue, 13 Mar 2018 11:24:51 +0100 +simgear (1:2018.1.1~rc+dfsg-3) experimental; urgency=medium + + * Team upload. + * Switch Vcs-URLs to salsa.d.o + + -- Dr. Tobias Quathamer Mon, 12 Mar 2018 12:41:46 +0100 + +simgear (1:2018.1.1~rc+dfsg-2) experimental; urgency=medium + + * Team upload. + * Remove unneeded Build-Depends: freeglut3-dev, libcurl-dev, + libgl-dev, libglu-dev, libalut-dev, libice-dev, libjpeg-dev, + libsm-dev, libx11-dev, libxext-dev, libxi-dev, libxmu-dev, libxt-dev + * Remove outdated versioned Build-Depends on cmake + * Revert back to libopenscenegraph 3.2 on armel and armhf. + The libopenscenegraph-3.4 version results in FTBFS errors + on those two architectures. + * Remove patch use_debian_udns, has been included upstream since + version 2016.3.1 + * Fix two typos in patch descriptions + + -- Dr. Tobias Quathamer Fri, 09 Mar 2018 17:41:54 +0100 + +simgear (1:2018.1.1~rc+dfsg-1) experimental; urgency=medium + + * Team upload. + * New upstream version 2018.1.1~rc+dfsg + - Refresh patches + - Remove patch correct_http_test, no longer needed + * Use wrap-and-sort for d/control + * Use debhelper v11 + * Update d/copyright + * Update Standards-Version to 4.1.3, no changes needed + + -- Dr. Tobias Quathamer Wed, 07 Mar 2018 23:12:51 +0100 + simgear (1:2017.3.1+dfsg-2) unstable; urgency=medium * Team upload. @@ -750,7 +795,3 @@ plib's method of doing same... -- Ove Kaaven Tue, 6 Feb 2001 13:06:57 +0100 - -Local variables: -add-log-mailing-address "ovek@arcticnet.no" -End: diff -Nru simgear-2017.3.1+dfsg/debian/control simgear-2018.1.1+dfsg/debian/control --- simgear-2017.3.1+dfsg/debian/control 2018-03-13 10:21:40.000000000 +0000 +++ simgear-2018.1.1+dfsg/debian/control 2018-04-22 10:21:52.000000000 +0000 @@ -2,7 +2,9 @@ Section: libs Priority: optional Maintainer: Debian FlightGear Crew -Uploaders: Ove Kaaven , Markus Wanner +Uploaders: Ove Kaaven , + Markus Wanner , + Dr. Tobias Quathamer Build-Depends: cmake, debhelper (>= 11~), libboost-dev, @@ -16,7 +18,7 @@ libudns-dev, libutfcpp-dev, zlib1g-dev -Standards-Version: 4.1.3 +Standards-Version: 4.1.4 Homepage: http://www.simgear.org/ Vcs-Browser: https://salsa.debian.org/debian/simgear Vcs-Git: https://salsa.debian.org/debian/simgear.git diff -Nru simgear-2017.3.1+dfsg/debian/patches/correct_http_test.patch simgear-2018.1.1+dfsg/debian/patches/correct_http_test.patch --- simgear-2017.3.1+dfsg/debian/patches/correct_http_test.patch 2018-03-13 10:23:34.000000000 +0000 +++ simgear-2018.1.1+dfsg/debian/patches/correct_http_test.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,51 +0,0 @@ -Description: Correct use of remove_if - After remove_if, the objects to remove aren't guaranteed to be valid, - anymore. Therefore, delete the channel object before invalidating its - pointer. -Author: Markus Wanner -Date: 2017-01-24 -Forwarded: no - ---- a/simgear/io/test_HTTP.hxx -+++ b/simgear/io/test_HTTP.hxx -@@ -183,12 +183,19 @@ - int requestContentLength; - }; - -+// A predicate for remove_if with the neat side effect of freeing the -+// NetChannel object. - class EraseIfClosed - { - public: - bool operator()(simgear::NetChannel* chan) const - { -- return chan->isClosed(); -+ if (chan->isClosed()) { -+ delete chan; -+ return true; -+ } else { -+ return false; -+ } - } - }; - -@@ -231,15 +238,10 @@ - { - _poller.poll(); - -- typename std::vector::iterator it; -- it = std::remove_if(_channels.begin(), _channels.end(), EraseIfClosed()); -- -- for (typename std::vector::iterator it2 = it; it2 != _channels.end(); ++it2) { -- delete *it2; -- } -- -- _channels.erase(it, _channels.end()); -- -+ _channels.erase( -+ std::remove_if(_channels.begin(), _channels.end(), EraseIfClosed()), -+ _channels.end() -+ ); - } - - int connectCount() diff -Nru simgear-2017.3.1+dfsg/debian/patches/disable_force_shared.patch simgear-2018.1.1+dfsg/debian/patches/disable_force_shared.patch --- simgear-2017.3.1+dfsg/debian/patches/disable_force_shared.patch 2018-03-13 10:23:34.000000000 +0000 +++ simgear-2018.1.1+dfsg/debian/patches/disable_force_shared.patch 2018-04-22 10:01:22.000000000 +0000 @@ -7,7 +7,7 @@ Last-Update: 2016-09-20 --- a/CMakeLists.txt +++ b/CMakeLists.txt -@@ -239,8 +239,7 @@ +@@ -252,8 +252,7 @@ find_package(CURL REQUIRED) if (SYSTEM_EXPAT) @@ -17,7 +17,7 @@ find_package(EXPAT REQUIRED) else() -@@ -506,8 +505,7 @@ +@@ -538,8 +537,7 @@ if(ENABLE_DNS) if(SYSTEM_UDNS) diff -Nru simgear-2017.3.1+dfsg/debian/patches/no_3rdparty.patch simgear-2018.1.1+dfsg/debian/patches/no_3rdparty.patch --- simgear-2017.3.1+dfsg/debian/patches/no_3rdparty.patch 2018-03-13 10:23:34.000000000 +0000 +++ simgear-2018.1.1+dfsg/debian/patches/no_3rdparty.patch 2018-04-22 10:01:22.000000000 +0000 @@ -7,7 +7,7 @@ Last-Update: 2016-05-29 --- a/CMakeLists.txt +++ b/CMakeLists.txt -@@ -518,7 +518,6 @@ +@@ -550,7 +550,6 @@ endif() diff -Nru simgear-2017.3.1+dfsg/debian/patches/series simgear-2018.1.1+dfsg/debian/patches/series --- simgear-2017.3.1+dfsg/debian/patches/series 2018-03-13 10:23:34.000000000 +0000 +++ simgear-2018.1.1+dfsg/debian/patches/series 2018-04-22 10:01:22.000000000 +0000 @@ -4,4 +4,3 @@ disable_force_shared.patch disable_dns_test.patch spelling_fixes.patch -correct_http_test.patch diff -Nru simgear-2017.3.1+dfsg/debian/patches/spelling_fixes.patch simgear-2018.1.1+dfsg/debian/patches/spelling_fixes.patch --- simgear-2017.3.1+dfsg/debian/patches/spelling_fixes.patch 2018-03-13 10:23:34.000000000 +0000 +++ simgear-2018.1.1+dfsg/debian/patches/spelling_fixes.patch 2018-04-22 10:01:22.000000000 +0000 @@ -3,7 +3,7 @@ Author: Markus Wanner --- a/simgear/canvas/elements/CanvasGroup.cxx +++ b/simgear/canvas/elements/CanvasGroup.cxx -@@ -121,7 +121,7 @@ +@@ -118,7 +118,7 @@ ( SG_GENERAL, SG_WARN, diff -Nru simgear-2017.3.1+dfsg/DoxygenMain.cxx simgear-2018.1.1+dfsg/DoxygenMain.cxx --- simgear-2017.3.1+dfsg/DoxygenMain.cxx 2017-09-20 07:51:07.000000000 +0000 +++ simgear-2018.1.1+dfsg/DoxygenMain.cxx 2018-04-06 19:43:17.000000000 +0000 @@ -2,6 +2,10 @@ documentation. It has a .cxx extension so that emacs will happily autoindent correctly. */ +/** + * \namespace simgear + * \brief \ref index "SimGear" main namespace. + */ /** \mainpage SimGear * Simulation, Visualization, and Game development libraries. diff -Nru simgear-2017.3.1+dfsg/package/openSUSE/README simgear-2018.1.1+dfsg/package/openSUSE/README --- simgear-2017.3.1+dfsg/package/openSUSE/README 2017-09-20 07:51:07.000000000 +0000 +++ simgear-2018.1.1+dfsg/package/openSUSE/README 1970-01-01 00:00:00.000000000 +0000 @@ -1,23 +0,0 @@ -Building a SimGear RPM package for openSUSE - -(Last tested with openSUSE 11.4+12.1) - -This directory contains the files which, along with -the source code tar files, can be used to build -an RPM package targeted at an openSUSE Linux system. - -To build SimGear from source do the following: - -1. obtain simgear-2.8.0.tar.bz2 (adapt version if - necessary) and copy it into ~/rpmbuild/SOURCES - -2. look in the BuildRequires section of SimGear.spec - and check that all the packages referred to are - installed (note, some of these packages may be part - of openSUSE's "games" repository). - -3. run 'rpmbuild -ba simgear.spec' and find the RPM - build result in ~/rpmbuild/RPMS - -That's all! - diff -Nru simgear-2017.3.1+dfsg/package/openSUSE/SimGear.spec simgear-2018.1.1+dfsg/package/openSUSE/SimGear.spec --- simgear-2017.3.1+dfsg/package/openSUSE/SimGear.spec 2017-09-20 07:51:07.000000000 +0000 +++ simgear-2018.1.1+dfsg/package/openSUSE/SimGear.spec 1970-01-01 00:00:00.000000000 +0000 @@ -1,63 +0,0 @@ -Summary: Simulator Construction Gear -Name: SimGear -Version: 2.8.0 -Release: 1 -License: LGPL -URL: http://www.flightgear.org -Group: Amusements/Games/3D/Simulation -Source: http://mirrors.ibiblio.org/pub/mirrors/flightgear/ftp/Source/simgear-%{version}.tar.bz2 -BuildRoot: %{_tmppath}/%{name}-%{version}-build - -BuildRequires: gcc, gcc-c++, cmake -BuildRequires: libopenal1-soft, openal-soft -BuildRequires: libOpenSceneGraph-devel >= 3.0 -BuildRequires: zlib, zlib-devel -BuildRequires: libjpeg62, libjpeg62-devel -BuildRequires: boost-devel >= 1.37 -BuildRequires: subversion-devel, libapr1-devel -Requires: OpenSceneGraph-plugins >= 3.0 - -%description -This package contains a tools and libraries useful for constructing -simulation and visualization applications such as FlightGear or TerraGear. - -%package devel -Group: Development/Libraries/Other -Summary: Development header files for SimGear -Requires: SimGear = %{version} - -%description devel -Development headers and libraries for building applications against SimGear. - -%prep -%setup -T -q -n simgear-%{version} -b 0 - -%build -export CFLAGS="$RPM_OPT_FLAGS" -export CXXFLAGS="$RPM_OPT_FLAGS" -# build SHARED simgear libraries -cmake -DCMAKE_INSTALL_PREFIX=%{_prefix} -DSIMGEAR_SHARED:BOOL=ON -DENABLE_TESTS:BOOL=OFF -DJPEG_FACTORY:BOOL=ON -make %{?_smp_mflags} - -%install -make DESTDIR=%{buildroot} install - -%post -p /sbin/ldconfig - -%postun -p /sbin/ldconfig - -%files -%defattr (-, root, root, -) -%doc AUTHORS COPYING ChangeLog NEWS README -%{_libdir}/libSimGear*.so.* - -%files devel -%defattr(-,root,root,-) -%dir %{_includedir}/simgear -%{_includedir}/simgear/* -%{_libdir}/libSimGear*.so - -%changelog -* Mon Jul 02 2012 thorstenb@flightgear.org -- Initial version - diff -Nru simgear-2017.3.1+dfsg/package/RedHat/README simgear-2018.1.1+dfsg/package/RedHat/README --- simgear-2017.3.1+dfsg/package/RedHat/README 2017-09-20 07:51:07.000000000 +0000 +++ simgear-2018.1.1+dfsg/package/RedHat/README 1970-01-01 00:00:00.000000000 +0000 @@ -1,13 +0,0 @@ -Building a SimGear RPM package for Red Hat - -Please see the "package/openSUSE" directory for an -example how to build a SimGear RPM package with -shared SimGear libraries. - -You may need to adapt the names (exact spelling) of some -of the package dependencies in the openSUSE RPM spec, -since these may slightly differ for Red Hat. - -(If you have a working and tested Red Hat RPM spec, -you're welcome to contribute it to this project.) - diff -Nru simgear-2017.3.1+dfsg/simgear/bucket/test_bucket.cxx simgear-2018.1.1+dfsg/simgear/bucket/test_bucket.cxx --- simgear-2017.3.1+dfsg/simgear/bucket/test_bucket.cxx 2017-09-20 07:51:07.000000000 +0000 +++ simgear-2018.1.1+dfsg/simgear/bucket/test_bucket.cxx 2018-04-06 19:43:17.000000000 +0000 @@ -53,7 +53,7 @@ SG_CHECK_EQUAL(b1.gen_index(), 3040320); SG_CHECK_EQUAL(b1.gen_base_path(), "e000n50/e005n55"); SG_VERIFY(b1.isValid()); - + SGBucket b2(-10.1, -43.8); SG_CHECK_EQUAL(b2.get_chunk_lon(), -11); SG_CHECK_EQUAL(b2.get_chunk_lat(), -44); @@ -62,7 +62,7 @@ SG_CHECK_EQUAL(b2.get_y(), 1); SG_CHECK_EQUAL(b2.gen_base_path(), "w020s50/w011s44"); SG_VERIFY(b2.isValid()); - + SGBucket b3(123.48, 9.01); SG_CHECK_EQUAL(b3.get_chunk_lon(), 123); SG_CHECK_EQUAL(b3.get_chunk_lat(), 9); @@ -70,10 +70,10 @@ SG_CHECK_EQUAL(b3.get_y(), 0); SG_CHECK_EQUAL(b3.gen_base_path(), "e120n00/e123n09"); SG_VERIFY(b3.isValid()); - + SGBucket defBuck; SG_VERIFY(!defBuck.isValid()); - + b3.make_bad(); SG_VERIFY(!b3.isValid()); @@ -87,19 +87,47 @@ SG_CHECK_EQUAL(atAntiMeridian2.get_chunk_lon(), -180); SG_CHECK_EQUAL(atAntiMeridian2.get_x(), 0); -// check comparisom operator overload + // check comparison operator overload SGBucket b4(5.11, 55.1); SG_VERIFY(b1 == b4); // should be equal SG_VERIFY(b1 == b1); SG_VERIFY(b1 != defBuck); SG_VERIFY(b1 != b2); - -// check wrapping/clipping of inputs + + // check wrapping/clipping of inputs SGBucket wrapMeridian(-200.0, 45.0); SG_CHECK_EQUAL(wrapMeridian.get_chunk_lon(), 160); SGBucket clipPole(48.9, 91); SG_CHECK_EQUAL(clipPole.get_chunk_lat(), 89); + + // test override of a bucket's geod + auto geod = SGGeod::fromDegFt(-86.678, 36.1248, 599.0); +#ifndef NO_DEPRECATED_API + SGBucket bna_airport; + bna_airport.set_bucket(geod); +#else + SGBucket bna_airport(geod); +#endif + SG_VERIFY(bna_airport.isValid()); + SG_CHECK_EQUAL(bna_airport.get_chunk_lon(), -87); // left origin of the 1-degree chunk + SG_CHECK_EQUAL(bna_airport.get_chunk_lat(), 36); // bottom origin of the 1-degree chunk + SG_CHECK_EQUAL(bna_airport.get_x(), 1); // buckets are 0.25 deg wide at the W87 parallel + // we're 0.322 deg from the origin (second bucket) + SG_CHECK_EQUAL(bna_airport.get_y(), 0); // buckets are always 0.125 deg tall + // we're 0.1248 deg from the origin (first bucket) + SG_CHECK_EQUAL(bna_airport.gen_base_path(), "w090n30/w087n36"); + SG_CHECK_EQUAL_EP2(bna_airport.get_width_m(), 22479.1, 0.1); + SG_CHECK_EQUAL_EP2(bna_airport.get_height_m(), 13914.9, 0.1); + SG_CHECK_EQUAL(bna_airport.gen_index_str(), "1531777"); // 0x175F81 = b01011101|01111110|000|001 + // = 93-180 | 126-90 | 0 | 1 + // = -87 | 36 | 0 | 1 + + // test stream output + cout << "[TEST] BNA Airport: " << bna_airport << endl; + auto center = bna_airport.get_center(); + cout << "[TEST] BNA lon: " << center.getLongitudeDeg() << endl; + cout << "[TEST] BNA lat: " << center.getLatitudeDeg() << endl; } void testPolar() @@ -110,17 +138,17 @@ SG_CHECK_EQUAL(b1.get_chunk_lon(), 0); SG_CHECK_EQUAL(b1.get_x(), 0); SG_CHECK_EQUAL(b1.get_y(), 7); - + SG_CHECK_EQUAL_EP(b1.get_highest_lat(), 90.0); SG_CHECK_EQUAL_EP(b1.get_width_m(), 10.0); - + SG_CHECK_EQUAL(b2.get_chunk_lat(), 89); SG_CHECK_EQUAL(b2.get_chunk_lon(), 0); SG_CHECK_EQUAL(b2.get_x(), 0); SG_CHECK_EQUAL(b2.get_y(), 7); - + SG_CHECK_EQUAL(b1.gen_index(), b2.gen_index()); - + SGGeod actualNorthPole1 = b1.get_corner(2); SGGeod actualNorthPole2 = b1.get_corner(3); SG_CHECK_EQUAL_EP(actualNorthPole1.getLatitudeDeg(), 90.0); @@ -131,11 +159,11 @@ SGBucket b3(-2, 89.88); SGBucket b4(-7, 89.88); SG_CHECK_EQUAL(b3.gen_index(), b4.gen_index()); - + // south pole SGBucket b5(-170, -89.88); SGBucket b6(-179, -89.88); - + SG_CHECK_EQUAL(b5.get_chunk_lat(), -90); SG_CHECK_EQUAL(b5.get_chunk_lon(), -180); SG_CHECK_EQUAL(b5.get_x(), 0); @@ -143,17 +171,16 @@ SG_CHECK_EQUAL(b5.gen_index(), b6.gen_index()); SG_CHECK_EQUAL_EP(b5.get_highest_lat(), -90.0); SG_CHECK_EQUAL_EP(b5.get_width_m(), 10.0); - + SGGeod actualSouthPole1 = b5.get_corner(0); SGGeod actualSouthPole2 = b5.get_corner(1); SG_CHECK_EQUAL_EP(actualSouthPole1.getLatitudeDeg(), -90.0); SG_CHECK_EQUAL_EP(actualSouthPole1.getLongitudeDeg(), -180); SG_CHECK_EQUAL_EP(actualSouthPole2.getLatitudeDeg(), -90.0); SG_CHECK_EQUAL_EP(actualSouthPole2.getLongitudeDeg(), -168); - + SGBucket b7(200, 89.88); SG_CHECK_EQUAL(b7.get_chunk_lon(), -168); - } // test the tiles just below the pole (between 86 & 89 degrees N/S) @@ -167,7 +194,7 @@ SGBucket b3(176.1, 88.5); SG_CHECK_EQUAL(b3.get_chunk_lon(), 176); - + SGBucket b4(-178, 88.5); SG_CHECK_EQUAL(b4.get_chunk_lon(), -180); } @@ -181,7 +208,7 @@ SG_CHECK_EQUAL(b1.get_chunk_lon(), -60); SG_CHECK_EQUAL(b1.get_x(), 1); SG_CHECK_EQUAL(b1.get_y(), 7); - + // offset vertically SGBucket b2(b1.sibling(0, 1)); SG_CHECK_EQUAL(b2.get_chunk_lat(), 22); @@ -190,7 +217,7 @@ SG_CHECK_EQUAL(b2.get_y(), 0); SG_CHECK_EQUAL(b2.gen_index(), sgBucketOffset(-59.8, 21.9, 0, 1)); - + // offset vertically and horizontally. We compute horizontal (x) // movement at the target latitude, so this should move 0.25 * -3 degrees, // NOT 0.125 * -3 degrees. @@ -199,7 +226,7 @@ SG_CHECK_EQUAL(b3.get_chunk_lon(), -61); SG_CHECK_EQUAL(b3.get_x(), 1); SG_CHECK_EQUAL(b3.get_y(), 0); - + SG_CHECK_EQUAL(b3.gen_index(), sgBucketOffset(-59.8, 21.9, -3, 1)); } @@ -210,40 +237,40 @@ SG_CHECK_EQUAL(b1.get_chunk_lon(), -12); SG_CHECK_EQUAL(b1.get_x(), 0); SG_CHECK_EQUAL(b1.get_y(), 3); - + // offset horizontally SGBucket b2(b1.sibling(-2, 0)); SG_CHECK_EQUAL(b2.get_chunk_lat(), -90); SG_CHECK_EQUAL(b2.get_chunk_lon(), -36); SG_CHECK_EQUAL(b2.get_x(), 0); SG_CHECK_EQUAL(b2.get_y(), 3); - + SG_CHECK_EQUAL(b2.gen_index(), sgBucketOffset(-11.7, -89.6, -2, 0)); - -// offset and wrap + + // offset and wrap SGBucket b3(-170, 89.1); SGBucket b4(b3.sibling(-1, 0)); SG_CHECK_EQUAL(b4.get_chunk_lat(), 89); SG_CHECK_EQUAL(b4.get_chunk_lon(), 168); SG_CHECK_EQUAL(b4.get_x(), 0); SG_CHECK_EQUAL(b4.get_y(), 0); - + SG_CHECK_EQUAL(b4.gen_index(), sgBucketOffset(-170, 89.1, -1, 0)); - + SGBucket b5(177, 87.3); SGBucket b6(b5.sibling(1, 1)); SG_CHECK_EQUAL(b6.get_chunk_lat(), 87); SG_CHECK_EQUAL(b6.get_chunk_lon(), -180); SG_CHECK_EQUAL(b6.get_x(), 0); SG_CHECK_EQUAL(b6.get_y(), 3); - + SG_CHECK_EQUAL(b6.gen_index(), sgBucketOffset(177, 87.3, 1, 1)); // offset vertically towards the pole SGBucket b7(b1.sibling(0, -5)); SG_VERIFY(!b7.isValid()); - + SG_VERIFY(!SGBucket(0, 90).sibling(0, 1).isValid()); } @@ -256,29 +283,80 @@ SG_CHECK_EQUAL(b1.get_chunk_lon(), -180); SG_CHECK_EQUAL(b1.get_x(), 1); SG_CHECK_EQUAL(b1.get_y(), 6); - + SGBucket b2(b1.sibling(-2, 0)); SG_CHECK_EQUAL(b2.get_chunk_lat(), 16); SG_CHECK_EQUAL(b2.get_chunk_lon(), 179); SG_CHECK_EQUAL(b2.get_x(), 7); SG_CHECK_EQUAL(b2.get_y(), 6); SG_CHECK_EQUAL(b2.gen_index(), sgBucketOffset(-179.8, 16.8, -2, 0)); - - +} + +void testSiblings() +{ + SGBucket bna_airport(-86.678, 36.1248); + SG_VERIFY(bna_airport.isValid()); + + // retrieve the sibling two positions north-east of my position + auto sib1 = bna_airport.sibling(2, 2); + SG_CHECK_EQUAL(sib1.get_chunk_lon(), bna_airport.get_chunk_lon()); + SG_CHECK_EQUAL(sib1.get_chunk_lat(), bna_airport.get_chunk_lat()); + SG_CHECK_EQUAL(sib1.get_x(), 3); // my x-pos (1) + 2 = 3 + SG_CHECK_EQUAL(sib1.get_y(), 2); // my y-pos (0) + 2 = 2 + SG_CHECK_EQUAL(sib1.gen_base_path(), bna_airport.gen_base_path()); + + // retrieve the one sibling two positions to the north-east + std::vector siblings; + bna_airport.siblings(2, 2, siblings); + SG_CHECK_EQUAL(siblings.size(), static_cast::size_type>(1)); + siblings.clear(); + + // retrieve the one sibling at the chunk origin of sib1 + sib1.siblings(-2, -2, siblings); + SG_CHECK_EQUAL(siblings.size(), static_cast::size_type>(1)); + siblings.clear(); + + // calculate delta between two buckets + int dx = 0; + int dy = 0; + sgBucketDiff(bna_airport, sib1, &dx, &dy); + SG_CHECK_EQUAL(dx, 2); + SG_CHECK_EQUAL(dy, 2); + + // retrieve all siblings between two geodetic locations + auto geod_bna = SGGeod::fromDegFt(-86.678, 36.1248, 599.0); + auto geod_m54 = SGGeod::fromDegFt(-86.317, 36.1908, 122.0); + sgGetBuckets(geod_bna, geod_m54, siblings); + SG_CHECK_EQUAL(siblings.size(), static_cast::size_type>(4)); + siblings.clear(); + + // edge cases + + // ensure you cannot retrieve the sibling of an invalid bucket + SGBucket bad; + auto bad_sib = bad.sibling(1, 1); + SG_CHECK_EQUAL(bad_sib.get_chunk_lon(), -1000); + SG_CHECK_EQUAL(bad.siblings(2, 2, siblings), 0); + + // if we drop below the 22nd parallel, the bucket widths are half the size + // expect this to retrieve two buckets + bna_airport.siblings(0, -160, siblings); + SG_CHECK_EQUAL(siblings.size(), static_cast::size_type>(2)); + siblings.clear(); } int main(int argc, char* argv[]) { testBucketSpans(); - + testBasic(); testPolar(); testNearPolar(); testOffset(); testOffsetWrap(); testPolarOffset(); - + testSiblings(); + cout << "all tests passed OK" << endl; return 0; // passed } - diff -Nru simgear-2017.3.1+dfsg/simgear/canvas/Canvas.cxx simgear-2018.1.1+dfsg/simgear/canvas/Canvas.cxx --- simgear-2017.3.1+dfsg/simgear/canvas/Canvas.cxx 2017-09-20 07:51:07.000000000 +0000 +++ simgear-2018.1.1+dfsg/simgear/canvas/Canvas.cxx 2018-04-06 19:43:17.000000000 +0000 @@ -22,8 +22,10 @@ #include "CanvasEventManager.hxx" #include "CanvasEventVisitor.hxx" #include "CanvasPlacement.hxx" + #include #include +#include #include #include @@ -32,9 +34,6 @@ #include #include -#include -#include - namespace simgear { namespace canvas @@ -64,18 +63,9 @@ //---------------------------------------------------------------------------- Canvas::Canvas(SGPropertyNode* node): PropertyBasedElement(node), - _canvas_mgr(0), _event_manager(new EventManager), - _size_x(-1), - _size_y(-1), - _view_width(-1), - _view_height(-1), _status(node, "status"), - _status_msg(node, "status-msg"), - _sampling_dirty(false), - _render_dirty(true), - _visible(true), - _render_always(false) + _status_msg(node, "status-msg") { _status = 0; setStatusFlags(MISSING_SIZE_X | MISSING_SIZE_Y); @@ -236,6 +226,10 @@ if( _status & STATUS_DIRTY ) { + // Retrieve reference here, to ensure the scene group is not deleted while + // creating the new texture and camera + osg::ref_ptr root_scene_group = _root_group->getSceneGroup(); + _texture.setSize(_size_x, _size_y); if( !_texture.serviceable() ) @@ -261,7 +255,7 @@ parseColor(_node->getStringValue("background"), clear_color); camera->setClearColor(clear_color); - camera->addChild(_root_group->getMatrixTransform()); + camera->addChild(root_scene_group); if( _texture.serviceable() ) { @@ -281,7 +275,7 @@ if( _visible || _render_always ) { - BOOST_FOREACH(CanvasWeakPtr canvas_weak, _child_canvases) + for(auto& canvas_weak: _child_canvases) { // TODO should we check if the image the child canvas is displayed // within is really visible? @@ -293,7 +287,7 @@ if( _render_dirty ) { // Also mark all canvases this canvas is displayed within as dirty - BOOST_FOREACH(CanvasWeakPtr canvas_weak, _parent_canvases) + for(auto& canvas_weak: _parent_canvases) { CanvasPtr canvas = canvas_weak.lock(); if( canvas ) @@ -522,8 +516,8 @@ { const std::string& name = node->getNameString(); - if( boost::starts_with(name, "status") - || boost::starts_with(name, "data-") ) + if( strutils::starts_with(name, "status") + || strutils::starts_with(name, "data-") ) return; _render_dirty = true; @@ -538,7 +532,7 @@ if( !placements.empty() ) { bool placement_dirty = false; - BOOST_FOREACH(PlacementPtr& placement, placements) + for(auto& placement: placements) { // check if change can be directly handled by placement if( placement->getProps() == node->getParent() diff -Nru simgear-2017.3.1+dfsg/simgear/canvas/Canvas.hxx simgear-2018.1.1+dfsg/simgear/canvas/Canvas.hxx --- simgear-2017.3.1+dfsg/simgear/canvas/Canvas.hxx 2017-09-20 07:51:07.000000000 +0000 +++ simgear-2018.1.1+dfsg/simgear/canvas/Canvas.hxx 2018-04-06 19:43:17.000000000 +0000 @@ -33,7 +33,7 @@ #include #include -#include +#include #include namespace simgear @@ -211,21 +211,21 @@ protected: - CanvasMgr *_canvas_mgr; + CanvasMgr *_canvas_mgr {nullptr}; - boost::scoped_ptr _event_manager; + std::unique_ptr _event_manager; - int _size_x, - _size_y, - _view_width, - _view_height; + int _size_x {-1}, + _size_y {-1}, + _view_width {-1}, + _view_height {-1}; PropertyObject _status; PropertyObject _status_msg; - bool _sampling_dirty, - _render_dirty, - _visible; + bool _sampling_dirty {false}, + _render_dirty {true}, + _visible {true}; ODGauge _texture; @@ -235,7 +235,9 @@ ElementWeakPtr _focus_element; CullCallbackPtr _cull_callback; - bool _render_always; //!< Used to disable automatic lazy rendering (culling) + + /** Used to disable automatic lazy rendering (culling) */ + bool _render_always {false}; std::vector _dirty_placements; std::vector _placements; diff -Nru simgear-2017.3.1+dfsg/simgear/canvas/CanvasWindow.cxx simgear-2018.1.1+dfsg/simgear/canvas/CanvasWindow.cxx --- simgear-2017.3.1+dfsg/simgear/canvas/CanvasWindow.cxx 2017-09-20 07:51:07.000000000 +0000 +++ simgear-2018.1.1+dfsg/simgear/canvas/CanvasWindow.cxx 2018-04-06 19:43:17.000000000 +0000 @@ -22,13 +22,11 @@ #include "CanvasWindow.hxx" #include +#include #include #include -#include -#include - namespace simgear { namespace canvas @@ -43,9 +41,6 @@ const Style& parent_style, Element* parent ): Image(canvas, node, parent_style, parent), - _attributes_dirty(0), - _resizable(false), - _capture_events(true), _resize_top(node, "resize-top"), _resize_right(node, "resize-right"), _resize_bottom(node, "resize-bottom"), @@ -92,7 +87,7 @@ _capture_events = node->getBoolValue(); else if( name == "decoration-border" ) parseDecorationBorder(node->getStringValue()); - else if( boost::starts_with(name, "shadow-") + else if( strutils::starts_with(name, "shadow-") || name == "content-size" ) _attributes_dirty |= DECORATION; else @@ -104,15 +99,9 @@ } //---------------------------------------------------------------------------- - osg::Group* Window::getGroup() - { - return getMatrixTransform(); - } - - //---------------------------------------------------------------------------- const SGVec2 Window::getPosition() const { - const osg::Matrix& m = getMatrixTransform()->getMatrix(); + auto const& m = getMatrix(); return SGVec2( m(3, 0), m(3, 1) ); } diff -Nru simgear-2017.3.1+dfsg/simgear/canvas/CanvasWindow.hxx simgear-2018.1.1+dfsg/simgear/canvas/CanvasWindow.hxx --- simgear-2017.3.1+dfsg/simgear/canvas/CanvasWindow.hxx 2017-09-20 07:51:07.000000000 +0000 +++ simgear-2018.1.1+dfsg/simgear/canvas/CanvasWindow.hxx 2018-04-06 19:43:17.000000000 +0000 @@ -70,7 +70,6 @@ virtual void update(double delta_time_sec); virtual void valueChanged(SGPropertyNode* node); - osg::Group* getGroup(); const SGVec2 getPosition() const; const SGRect getScreenRegion() const; @@ -104,7 +103,7 @@ DECORATION = 1 }; - uint32_t _attributes_dirty; + uint32_t _attributes_dirty {0}; CanvasPtr _canvas_decoration; CanvasWeakPtr _canvas_content; @@ -113,8 +112,8 @@ ImagePtr _image_content, _image_shadow; - bool _resizable, - _capture_events; + bool _resizable {false}, + _capture_events {true}; PropertyObject _resize_top, _resize_right, diff -Nru simgear-2017.3.1+dfsg/simgear/canvas/elements/CanvasElement.cxx simgear-2018.1.1+dfsg/simgear/canvas/elements/CanvasElement.cxx --- simgear-2017.3.1+dfsg/simgear/canvas/elements/CanvasElement.cxx 2017-09-20 07:51:07.000000000 +0000 +++ simgear-2018.1.1+dfsg/simgear/canvas/elements/CanvasElement.cxx 2018-04-06 19:43:17.000000000 +0000 @@ -31,10 +31,6 @@ #include #include -#include -#include -#include - #include #include #include @@ -214,22 +210,22 @@ //---------------------------------------------------------------------------- void Element::onDestroy() { - if( !_transform.valid() ) + if( !_scene_group.valid() ) return; // The transform node keeps a reference on this element, so ensure it is // deleted. - BOOST_FOREACH(osg::Group* parent, _transform->getParents()) + for(osg::Group* parent: _scene_group->getParents()) { - parent->removeChild(_transform.get()); + parent->removeChild(_scene_group.get()); } // Hide in case someone still holds a reference setVisible(false); removeListener(); - _parent = 0; - _transform = 0; + _parent = nullptr; + _scene_group = nullptr; } //---------------------------------------------------------------------------- @@ -247,29 +243,8 @@ //---------------------------------------------------------------------------- void Element::update(double dt) { - if( !isVisible() ) - return; - - // Trigger matrix update - getMatrix(); - - // Update bounding box on manual update (manual updates pass zero dt) - if( dt == 0 && _drawable ) - _drawable->getBound(); - - if( (_attributes_dirty & BLEND_FUNC) && _transform.valid() ) - { - parseBlendFunc( - _transform->getOrCreateStateSet(), - _node->getChild("blend-source"), - _node->getChild("blend-destination"), - _node->getChild("blend-source-rgb"), - _node->getChild("blend-destination-rgb"), - _node->getChild("blend-source-alpha"), - _node->getChild("blend-destination-alpha") - ); - _attributes_dirty &= ~BLEND_FUNC; - } + if( isVisible() ) + updateImpl(dt); } //---------------------------------------------------------------------------- @@ -341,7 +316,8 @@ if( listeners == _listener.end() ) return false; - BOOST_FOREACH(EventListener const& listener, listeners->second) + for(auto const& listener: listeners->second) + { try { listener(event); @@ -354,6 +330,7 @@ "canvas::Element: event handler error: '" << ex.what() << "'" ); } + } return true; } @@ -395,9 +372,9 @@ getBoundingBox() #endif .contains(osg::Vec3f(local_pos, 0)); - else if( _transform.valid() ) + else if( _scene_group.valid() ) // ... for other elements, i.e. groups only a bounding sphere is available - return _transform->getBound().contains(osg::Vec3f(parent_pos, 0)); + return _scene_group->getBound().contains(osg::Vec3f(parent_pos, 0)); else return false; } @@ -406,34 +383,32 @@ //---------------------------------------------------------------------------- void Element::setVisible(bool visible) { - if( _transform.valid() ) + if( _scene_group.valid() ) // TODO check if we need another nodemask - _transform->setNodeMask(visible ? 0xffffffff : 0); + _scene_group->setNodeMask(visible ? 0xffffffff : 0); } //---------------------------------------------------------------------------- bool Element::isVisible() const { - return _transform.valid() && _transform->getNodeMask() != 0; - } - - //---------------------------------------------------------------------------- - osg::MatrixTransform* Element::getMatrixTransform() - { - return _transform.get(); + return _scene_group.valid() && _scene_group->getNodeMask() != 0; } //---------------------------------------------------------------------------- - osg::MatrixTransform const* Element::getMatrixTransform() const + osg::MatrixTransform* Element::getSceneGroup() const { - return _transform.get(); + return _scene_group.get(); } //---------------------------------------------------------------------------- osg::Vec2f Element::posToLocal(const osg::Vec2f& pos) const { - getMatrix(); - const osg::Matrix& m = _transform->getInverseMatrix(); + if( !_scene_group ) + // TODO log warning? + return pos; + + updateMatrix(); + const osg::Matrix& m = _scene_group->getInverseMatrix(); return osg::Vec2f ( m(0, 0) * pos[0] + m(1, 0) * pos[1] + m(3, 0), @@ -486,9 +461,6 @@ { if( child->getNameString() == NAME_TRANSFORM ) { - if( !_transform.valid() ) - return; - if( child->getIndex() >= static_cast(_transform_types.size()) ) { SG_LOG @@ -525,7 +497,7 @@ if( parent == _node ) { const std::string& name = child->getNameString(); - if( boost::starts_with(name, "data-") ) + if( strutils::starts_with(name, "data-") ) return; else if( StyleInfo const* style_info = getStyleInfo(name) ) { @@ -540,7 +512,7 @@ } else if( name == "update" ) return update(0); - else if( boost::starts_with(name, "blend-") ) + else if( strutils::starts_with(name, "blend-") ) return (void)(_attributes_dirty |= BLEND_FUNC); } else if( parent @@ -564,6 +536,10 @@ //---------------------------------------------------------------------------- void Element::setClip(const std::string& clip) { + if( !_scene_group ) + // TODO warn? + return; + osg::StateSet* ss = getOrCreateStateSet(); if( !ss ) return; @@ -577,8 +553,8 @@ // TODO generalize CSS property parsing const std::string RECT("rect("); - if( !boost::ends_with(clip, ")") - || !boost::starts_with(clip, RECT) ) + if( !strutils::ends_with(clip, ")") + || !strutils::starts_with(clip, RECT) ) { SG_LOG(SG_GENERAL, SG_WARN, "Canvas: invalid clip: " << clip); return; @@ -618,7 +594,7 @@ } if( !_scissor ) - _scissor = new RelativeScissor(_transform.get()); + _scissor = new RelativeScissor(_scene_group.get()); // , , , _scissor->x() = values[3]; @@ -642,26 +618,26 @@ _scissor->_coord_reference = rf; } - //---------------------------------------------------------------------------- - void Element::setRotation(unsigned int index, double r) - { - _node->getChild(NAME_TRANSFORM, index, true)->setDoubleValue("rot", r); - } + //---------------------------------------------------------------------------- + void Element::setRotation(unsigned int index, double r) + { + _node->getChild(NAME_TRANSFORM, index, true)->setDoubleValue("rot", r); + } - //---------------------------------------------------------------------------- - void Element::setTranslation(unsigned int index, double x, double y) - { - SGPropertyNode* tf = _node->getChild(NAME_TRANSFORM, index, true); - tf->getChild("t", 0, true)->setDoubleValue(x); - tf->getChild("t", 1, true)->setDoubleValue(y); - } + //---------------------------------------------------------------------------- + void Element::setTranslation(unsigned int index, double x, double y) + { + SGPropertyNode* tf = _node->getChild(NAME_TRANSFORM, index, true); + tf->getChild("t", 0, true)->setDoubleValue(x); + tf->getChild("t", 1, true)->setDoubleValue(y); + } - //---------------------------------------------------------------------------- - void Element::setTransformEnabled(unsigned int index, bool enabled) - { - SGPropertyNode* tf = _node->getChild(NAME_TRANSFORM, index, true); - tf->setBoolValue("enabled", enabled); - } + //---------------------------------------------------------------------------- + void Element::setTransformEnabled(unsigned int index, bool enabled) + { + SGPropertyNode* tf = _node->getChild(NAME_TRANSFORM, index, true); + tf->setBoolValue("enabled", enabled); + } //---------------------------------------------------------------------------- osg::BoundingBox Element::getBoundingBox() const @@ -675,8 +651,8 @@ osg::BoundingBox bb; - if( _transform.valid() ) - bb.expandBy(_transform->getBound()); + if( _scene_group.valid() ) + bb.expandBy( _scene_group->getBound() ); return bb; } @@ -710,73 +686,11 @@ //---------------------------------------------------------------------------- osg::Matrix Element::getMatrix() const { - if( !_transform ) + if( !_scene_group ) return osg::Matrix::identity(); - if( !(_attributes_dirty & TRANSFORM) ) - return _transform->getMatrix(); - - osg::Matrix m; - for( size_t i = 0; i < _transform_types.size(); ++i ) - { - // Skip unused indizes... - if( _transform_types[i] == TT_NONE ) - continue; - - SGPropertyNode* tf_node = _node->getChild("tf", i, true); - if (!tf_node->getBoolValue("enabled", true)) { - continue; // skip disabled transforms - } - - // Build up the matrix representation of the current transform node - osg::Matrix tf; - switch( _transform_types[i] ) - { - case TT_MATRIX: - tf = osg::Matrix( tf_node->getDoubleValue("m[0]", 1), - tf_node->getDoubleValue("m[1]", 0), - 0, - tf_node->getDoubleValue("m[6]", 0), - - tf_node->getDoubleValue("m[2]", 0), - tf_node->getDoubleValue("m[3]", 1), - 0, - tf_node->getDoubleValue("m[7]", 0), - - 0, - 0, - 1, - 0, - - tf_node->getDoubleValue("m[4]", 0), - tf_node->getDoubleValue("m[5]", 0), - 0, - tf_node->getDoubleValue("m[8]", 1) ); - break; - case TT_TRANSLATE: - tf.makeTranslate( osg::Vec3f( tf_node->getDoubleValue("t[0]", 0), - tf_node->getDoubleValue("t[1]", 0), - 0 ) ); - break; - case TT_ROTATE: - tf.makeRotate( tf_node->getDoubleValue("rot", 0), 0, 0, 1 ); - break; - case TT_SCALE: - { - float sx = tf_node->getDoubleValue("s[0]", 1); - // sy defaults to sx... - tf.makeScale( sx, tf_node->getDoubleValue("s[1]", sx), 1 ); - break; - } - default: - break; - } - m.postMult( tf ); - } - _transform->setMatrix(m); - _attributes_dirty &= ~TRANSFORM; - - return m; + updateMatrix(); + return _scene_group->getMatrix(); } //---------------------------------------------------------------------------- @@ -790,11 +704,8 @@ PropertyBasedElement(node), _canvas( canvas ), _parent( parent ), - _attributes_dirty( 0 ), - _transform( new osg::MatrixTransform ), - _style( parent_style ), - _scissor( 0 ), - _drawable( 0 ) + _scene_group( new osg::MatrixTransform ), + _style( parent_style ) { staticInit(); @@ -806,15 +717,15 @@ ); // Ensure elements are drawn in order they appear in the element tree - _transform->getOrCreateStateSet() - ->setRenderBinDetails - ( - 0, - "PreOrderBin", - osg::StateSet::OVERRIDE_RENDERBIN_DETAILS - ); + _scene_group + ->getOrCreateStateSet() + ->setRenderBinDetails( + 0, + "PreOrderBin", + osg::StateSet::OVERRIDE_RENDERBIN_DETAILS + ); - _transform->setUserData( new OSGUserData(this) ); + _scene_group->setUserData( new OSGUserData(this) ); } //---------------------------------------------------------------------------- @@ -902,11 +813,21 @@ void Element::setDrawable( osg::Drawable* drawable ) { _drawable = drawable; - assert( _drawable ); - osg::ref_ptr geode = new osg::Geode; + if( !_drawable ) + { + SG_LOG(SG_GL, SG_WARN, "canvas::Element::setDrawable: NULL drawable"); + return; + } + if( !_scene_group ) + { + SG_LOG(SG_GL, SG_WARN, "canvas::Element::setDrawable: NULL scenegroup"); + return; + } + + auto geode = new osg::Geode; geode->addDrawable(_drawable); - _transform->addChild(geode); + _scene_group->addChild(geode); } //---------------------------------------------------------------------------- @@ -914,18 +835,109 @@ { if( _drawable.valid() ) return _drawable->getOrCreateStateSet(); - if( _transform.valid() ) - return _transform->getOrCreateStateSet(); - - return 0; + else if( _scene_group.valid() ) + return _scene_group->getOrCreateStateSet(); + else + return nullptr; } //---------------------------------------------------------------------------- void Element::setupStyle() { - BOOST_FOREACH( Style::value_type style, _style ) + for(auto const& style: _style) setStyle(style.second); } + //---------------------------------------------------------------------------- + void Element::updateMatrix() const + { + if( !(_attributes_dirty & TRANSFORM) || !_scene_group ) + return; + + osg::Matrix m; + for( size_t i = 0; i < _transform_types.size(); ++i ) + { + // Skip unused indizes... + if( _transform_types[i] == TT_NONE ) + continue; + + SGPropertyNode* tf_node = _node->getChild("tf", i, true); + if (!tf_node->getBoolValue("enabled", true)) { + continue; // skip disabled transforms + } + + // Build up the matrix representation of the current transform node + osg::Matrix tf; + switch( _transform_types[i] ) + { + case TT_MATRIX: + tf = osg::Matrix( tf_node->getDoubleValue("m[0]", 1), + tf_node->getDoubleValue("m[1]", 0), + 0, + tf_node->getDoubleValue("m[6]", 0), + + tf_node->getDoubleValue("m[2]", 0), + tf_node->getDoubleValue("m[3]", 1), + 0, + tf_node->getDoubleValue("m[7]", 0), + + 0, + 0, + 1, + 0, + + tf_node->getDoubleValue("m[4]", 0), + tf_node->getDoubleValue("m[5]", 0), + 0, + tf_node->getDoubleValue("m[8]", 1) ); + break; + case TT_TRANSLATE: + tf.makeTranslate( osg::Vec3f( tf_node->getDoubleValue("t[0]", 0), + tf_node->getDoubleValue("t[1]", 0), + 0 ) ); + break; + case TT_ROTATE: + tf.makeRotate( tf_node->getDoubleValue("rot", 0), 0, 0, 1 ); + break; + case TT_SCALE: + { + float sx = tf_node->getDoubleValue("s[0]", 1); + // sy defaults to sx... + tf.makeScale( sx, tf_node->getDoubleValue("s[1]", sx), 1 ); + break; + } + default: + break; + } + m.postMult( tf ); + } + _scene_group->setMatrix(m); + _attributes_dirty &= ~TRANSFORM; + } + + //---------------------------------------------------------------------------- + void Element::updateImpl(double dt) + { + updateMatrix(); + + // Update bounding box on manual update (manual updates pass zero dt) + if( dt == 0 && _drawable ) + _drawable->getBound(); + + if( (_attributes_dirty & BLEND_FUNC) ) + { + parseBlendFunc( + _scene_group->getOrCreateStateSet(), + _node->getChild("blend-source"), + _node->getChild("blend-destination"), + _node->getChild("blend-source-rgb"), + _node->getChild("blend-destination-rgb"), + _node->getChild("blend-source-alpha"), + _node->getChild("blend-destination-alpha") + ); + _attributes_dirty &= ~BLEND_FUNC; + } + } + } // namespace canvas } // namespace simgear diff -Nru simgear-2017.3.1+dfsg/simgear/canvas/elements/CanvasElement.hxx simgear-2018.1.1+dfsg/simgear/canvas/elements/CanvasElement.hxx --- simgear-2017.3.1+dfsg/simgear/canvas/elements/CanvasElement.hxx 2017-09-20 07:51:07.000000000 +0000 +++ simgear-2018.1.1+dfsg/simgear/canvas/elements/CanvasElement.hxx 2018-04-06 19:43:17.000000000 +0000 @@ -24,13 +24,13 @@ #include #include #include // for uint32_t +#include #include #include #include #include -#include namespace osg { @@ -49,6 +49,7 @@ public PropertyBasedElement { public: + using SceneGroupWeakPtr = osg::observer_ptr; /** * Store pointer to window as user data @@ -142,8 +143,11 @@ */ virtual bool isVisible() const; - osg::MatrixTransform* getMatrixTransform(); - osg::MatrixTransform const* getMatrixTransform() const; + /** + * Get the according group in the OSG scene graph + */ + // TODO ref_ptr + osg::MatrixTransform* getSceneGroup() const; /** * Transform position to local coordinages. @@ -217,13 +221,14 @@ */ template static - typename boost::enable_if< - boost::is_base_of, + std::enable_if_t< + std::is_base_of::value, ElementPtr - >::type create( const CanvasWeakPtr& canvas, - const SGPropertyNode_ptr& node, - const Style& style = Style(), - Element* parent = NULL ) + > + create( const CanvasWeakPtr& canvas, + const SGPropertyNode_ptr& node, + const Style& style = Style(), + Element* parent = NULL ) { return ElementPtr( new Derived(canvas, node, style, parent) ); } @@ -251,13 +256,13 @@ CanvasWeakPtr _canvas; ElementWeakPtr _parent; - mutable uint32_t _attributes_dirty; + mutable uint32_t _attributes_dirty = 0; - osg::observer_ptr _transform; - std::vector _transform_types; + SceneGroupWeakPtr _scene_group; + std::vector _transform_types; Style _style; - RelativeScissor *_scissor; + RelativeScissor *_scissor = nullptr; typedef std::vector Listener; typedef std::map ListenerMap; @@ -584,6 +589,10 @@ void setupStyle(); + void updateMatrix() const; + + virtual void updateImpl(double dt); + private: osg::ref_ptr _drawable; diff -Nru simgear-2017.3.1+dfsg/simgear/canvas/elements/CanvasGroup.cxx simgear-2018.1.1+dfsg/simgear/canvas/elements/CanvasGroup.cxx --- simgear-2017.3.1+dfsg/simgear/canvas/elements/CanvasGroup.cxx 2017-09-20 07:51:07.000000000 +0000 +++ simgear-2018.1.1+dfsg/simgear/canvas/elements/CanvasGroup.cxx 2018-04-06 19:43:17.000000000 +0000 @@ -23,13 +23,10 @@ #include "CanvasMap.hxx" #include "CanvasPath.hxx" #include "CanvasText.hxx" + #include #include -#include -#include -#include - namespace simgear { namespace canvas @@ -48,7 +45,7 @@ ElementFactories Group::_child_factories; const std::string Group::TYPE_NAME = "group"; - void warnTransformExpired(const char* member_name) + void warnSceneGroupExpired(const char* member_name) { SG_LOG( SG_GENERAL, SG_WARN, @@ -135,63 +132,57 @@ //---------------------------------------------------------------------------- ElementPtr Group::getElementById(const std::string& id) { - if( !_transform.valid() ) + if( !_scene_group.valid() ) { - warnTransformExpired("getElementById"); - return ElementPtr(); + warnSceneGroupExpired("getElementById"); + return {}; } - std::vector groups; - for(size_t i = 0; i < _transform->getNumChildren(); ++i) + // TODO check search algorithm. Not completely breadth-first and might be + // possible with using less dynamic memory + std::vector child_groups; + for(size_t i = 0; i < _scene_group->getNumChildren(); ++i) { const ElementPtr& el = getChildByIndex(i); if( el->get("id") == id ) return el; - Group* group = dynamic_cast(el.get()); - if( group ) - groups.push_back(group); + if( Group* child_group = dynamic_cast(el.get()) ) + child_groups.push_back(child_group); } - BOOST_FOREACH( GroupPtr group, groups ) + for(auto group: child_groups) { - ElementPtr el = group->getElementById(id); - if( el ) + if( ElementPtr el = group->getElementById(id) ) return el; } - return ElementPtr(); + return {}; } //---------------------------------------------------------------------------- void Group::clearEventListener() { - if( !_transform.valid() ) - return warnTransformExpired("clearEventListener"); - - for(size_t i = 0; i < _transform->getNumChildren(); ++i) - getChildByIndex(i)->clearEventListener(); - Element::clearEventListener(); - } - //---------------------------------------------------------------------------- - void Group::update(double dt) - { - for(size_t i = 0; i < _transform->getNumChildren(); ++i) - getChildByIndex(i)->update(dt); + if( !_scene_group.valid() ) + return warnSceneGroupExpired("clearEventListener"); - Element::update(dt); + for(size_t i = 0; i < _scene_group->getNumChildren(); ++i) + getChildByIndex(i)->clearEventListener(); } //---------------------------------------------------------------------------- bool Group::traverse(EventVisitor& visitor) { - // Iterate in reverse order as last child is displayed on top - for(size_t i = _transform->getNumChildren(); i --> 0;) + if( _scene_group.valid() ) { - if( getChildByIndex(i)->accept(visitor) ) - return true; + // Iterate in reverse order as last child is displayed on top + for(size_t i = _scene_group->getNumChildren(); i --> 0;) + { + if( getChildByIndex(i)->accept(visitor) ) + return true; + } } return false; } @@ -206,13 +197,13 @@ bool handled = setStyleImpl(style, style_info); if( style_info->inheritable ) { - if( !_transform.valid() ) + if( !_scene_group.valid() ) { - warnTransformExpired("setStyle"); + warnSceneGroupExpired("setStyle"); return false; } - for(size_t i = 0; i < _transform->getNumChildren(); ++i) + for(size_t i = 0; i < _scene_group->getNumChildren(); ++i) handled |= getChildByIndex(i)->setStyle(style, style_info); } @@ -222,26 +213,20 @@ //---------------------------------------------------------------------------- osg::BoundingBox Group::getTransformedBounds(const osg::Matrix& m) const { - osg::BoundingBox bb; - if( !_transform.valid() ) + if( !_scene_group.valid() ) { - warnTransformExpired("getTransformedBounds"); - return bb; + warnSceneGroupExpired("getTransformedBounds"); + return {}; } - for(size_t i = 0; i < _transform->getNumChildren(); ++i) + osg::BoundingBox bb; + for(size_t i = 0; i < _scene_group->getNumChildren(); ++i) { - const ElementPtr& child = getChildByIndex(i); - if( !child->getMatrixTransform()->getNodeMask() ) + auto child = getChildByIndex(i); + if( !child || !child->isVisible() ) continue; - bb.expandBy - ( - child->getTransformedBounds - ( - child->getMatrixTransform()->getMatrix() * m - ) - ); + bb.expandBy( child->getTransformedBounds(child->getMatrix() * m) ); } return bb; @@ -258,6 +243,15 @@ } //---------------------------------------------------------------------------- + void Group::updateImpl(double dt) + { + Element::updateImpl(dt); + + for(size_t i = 0; i < _scene_group->getNumChildren(); ++i) + getChildByIndex(i)->update(dt); + } + + //---------------------------------------------------------------------------- void Group::childAdded(SGPropertyNode* child) { if( child->getParent() != _node ) @@ -266,13 +260,13 @@ ElementFactory child_factory = getChildFactory( child->getNameString() ); if( child_factory ) { - if( !_transform.valid() ) - return warnTransformExpired("childAdded"); + if( !_scene_group.valid() ) + return warnSceneGroupExpired("childAdded"); ElementPtr element = child_factory(_canvas, child, _style, this); // Add to osg scene graph... - _transform->addChild( element->getMatrixTransform() ); + _scene_group->addChild(element->getSceneGroup()); // ...and ensure correct ordering handleZIndexChanged(element); @@ -293,7 +287,7 @@ if( getChildFactory(node->getNameString()) ) { - if( !_transform.valid() ) + if( !_scene_group.valid() ) // If transform is destroyed also all children are destroyed, so we can // not do anything here. return; @@ -323,7 +317,7 @@ void Group::childChanged(SGPropertyNode* node) { SGPropertyNode* parent = node->getParent(); - SGPropertyNode* grand_parent = parent ? parent->getParent() : NULL; + SGPropertyNode* grand_parent = parent ? parent->getParent() : nullptr; if( grand_parent == _node && node->getNameString() == "z-index" ) @@ -333,16 +327,18 @@ //---------------------------------------------------------------------------- void Group::handleZIndexChanged(ElementPtr child, int z_index) { - if( !child || !_transform.valid() ) + if( !child || !_scene_group.valid() ) return; - osg::ref_ptr tf = child->getMatrixTransform(); - size_t index = _transform->getChildIndex(tf), + // Keep reference to prevent deleting while removing and re-inserting later + osg::ref_ptr tf = child->getSceneGroup(); + + size_t index = _scene_group->getChildIndex(tf), index_new = index; for(;; ++index_new) { - if( index_new + 1 == _transform->getNumChildren() ) + if( index_new + 1 == _scene_group->getNumChildren() ) break; // Move to end of block with same index (= move upwards until the next @@ -369,8 +365,8 @@ return; } - _transform->removeChild(index); - _transform->insertChild(index_new, tf); + _scene_group->removeChild(index); + _scene_group->insertChild(index_new, tf); SG_LOG ( @@ -383,24 +379,27 @@ //---------------------------------------------------------------------------- ElementPtr Group::getChildByIndex(size_t index) const { - assert(_transform.valid()); - OSGUserData* ud = - static_cast(_transform->getChild(index)->getUserData()); - assert(ud); - return ud->element; + assert( _scene_group.valid() ); + + auto child = _scene_group->getChild(index); + if( !child ) + return {}; + + auto ud = static_cast(child->getUserData()); + return ud ? ud->element : ElementPtr(); } //---------------------------------------------------------------------------- ElementPtr Group::findChild( const SGPropertyNode* node, const std::string& id ) const { - if( !_transform.valid() ) + if( !_scene_group.valid() ) { - warnTransformExpired("findChild"); - return ElementPtr(); + warnSceneGroupExpired("findChild"); + return {}; } - for(size_t i = 0; i < _transform->getNumChildren(); ++i) + for(size_t i = 0; i < _scene_group->getNumChildren(); ++i) { ElementPtr el = getChildByIndex(i); @@ -416,7 +415,7 @@ } } - return ElementPtr(); + return {}; } } // namespace canvas diff -Nru simgear-2017.3.1+dfsg/simgear/canvas/elements/CanvasGroup.hxx simgear-2018.1.1+dfsg/simgear/canvas/elements/CanvasGroup.hxx --- simgear-2017.3.1+dfsg/simgear/canvas/elements/CanvasGroup.hxx 2017-09-20 07:51:07.000000000 +0000 +++ simgear-2018.1.1+dfsg/simgear/canvas/elements/CanvasGroup.hxx 2018-04-06 19:43:17.000000000 +0000 @@ -88,8 +88,6 @@ virtual void clearEventListener(); - virtual void update(double dt); - virtual bool traverse(EventVisitor& visitor); virtual bool setStyle( const SGPropertyNode* child, @@ -107,6 +105,8 @@ */ virtual ElementFactory getChildFactory(const std::string& type) const; + virtual void updateImpl(double dt); + virtual void childAdded(SGPropertyNode * child); virtual void childRemoved(SGPropertyNode * child); virtual void childChanged(SGPropertyNode * child); diff -Nru simgear-2017.3.1+dfsg/simgear/canvas/elements/CanvasImage.cxx simgear-2018.1.1+dfsg/simgear/canvas/elements/CanvasImage.cxx --- simgear-2017.3.1+dfsg/simgear/canvas/elements/CanvasImage.cxx 2017-09-20 07:51:07.000000000 +0000 +++ simgear-2018.1.1+dfsg/simgear/canvas/elements/CanvasImage.cxx 2018-04-06 19:43:17.000000000 +0000 @@ -117,9 +117,7 @@ ElementWeakPtr parent ): Element(canvas, node, parent_style, parent), _texture(new osg::Texture2D), - _node_src_rect( node->getNode("source", 0, true) ), - _src_rect(0,0), - _region(0,0) + _node_src_rect( node->getNode("source", 0, true) ) { staticInit(); @@ -157,22 +155,207 @@ //---------------------------------------------------------------------------- Image::~Image() { - if( _http_request ) { - Canvas::getSystemAdapter()->getHTTPClient()->cancelRequest(_http_request, "image destroyed"); + if( _http_request ) + { + Canvas::getSystemAdapter() + ->getHTTPClient() + ->cancelRequest(_http_request, "image destroyed"); + } + } + + //---------------------------------------------------------------------------- + void Image::valueChanged(SGPropertyNode* child) + { + // If the image is switched from invisible to visible, and it shows a + // canvas, we need to delay showing it by one frame to ensure the canvas is + // updated before the image is displayed. + // + // As canvas::Element handles and filters changes to the "visible" property + // we can not check this in Image::childChanged but instead have to override + // Element::valueChanged. + if( !isVisible() + && child->getParent() == _node + && child->getNameString() == "visible" + && child->getBoolValue() ) + { + CullCallback* cb = +#if OSG_VERSION_LESS_THAN(3,3,2) + static_cast +#else + dynamic_cast +#endif + ( _geom->getCullCallback() ); + + if( cb ) + cb->cullNextFrame(); + } + + Element::valueChanged(child); + } + + //---------------------------------------------------------------------------- + void Image::setSrcCanvas(CanvasPtr canvas) + { + CanvasPtr src_canvas = _src_canvas.lock(), + self_canvas = _canvas.lock(); + + if( src_canvas ) + src_canvas->removeParentCanvas(self_canvas); + if( self_canvas ) + self_canvas->removeChildCanvas(src_canvas); + + _src_canvas = src_canvas = canvas; + _attributes_dirty |= SRC_CANVAS; + _geom->setCullCallback(canvas ? new CullCallback(canvas) : 0); + + if( src_canvas ) + { + setupDefaultDimensions(); + + if( self_canvas ) + { + self_canvas->addChildCanvas(src_canvas); + src_canvas->addParentCanvas(self_canvas); } + } + } + + //---------------------------------------------------------------------------- + CanvasWeakPtr Image::getSrcCanvas() const + { + return _src_canvas; + } + + //---------------------------------------------------------------------------- + void Image::setImage(osg::ref_ptr img) + { + // remove canvas... + setSrcCanvas( CanvasPtr() ); + + _texture->setResizeNonPowerOfTwoHint(false); + _texture->setImage(img); + _texture->setWrap(osg::Texture::WRAP_S, osg::Texture::REPEAT); + _texture->setWrap(osg::Texture::WRAP_T, osg::Texture::REPEAT); + _geom->getOrCreateStateSet() + ->setTextureAttributeAndModes(0, _texture); + + if( img ) + setupDefaultDimensions(); + } + + //---------------------------------------------------------------------------- + void Image::setFill(const std::string& fill) + { + osg::Vec4 color(1,1,1,1); + if( !fill.empty() // If no color is given default to white + && !parseColor(fill, color) ) + return; + setFill(color); + } + + //---------------------------------------------------------------------------- + void Image::setFill(const osg::Vec4& color) + { + _colors->front() = color; + _colors->dirty(); + } + + + //---------------------------------------------------------------------------- + void Image::setOutset(const std::string& outset) + { + _outset = CSSBorder::parse(outset); + _attributes_dirty |= DEST_SIZE; + } + + //---------------------------------------------------------------------------- + void Image::setPreserveAspectRatio(const std::string& scale) + { + _preserve_aspect_ratio = SVGpreserveAspectRatio::parse(scale); + _attributes_dirty |= SRC_RECT; + } + + //---------------------------------------------------------------------------- + void Image::setSourceRect(const SGRect& sourceRect) + { + _attributes_dirty |= SRC_RECT; + _src_rect = sourceRect; + } + + //---------------------------------------------------------------------------- + void Image::setSlice(const std::string& slice) + { + _slice = CSSBorder::parse(slice); + _attributes_dirty |= SRC_RECT | DEST_SIZE; + } + + //---------------------------------------------------------------------------- + void Image::setSliceWidth(const std::string& width) + { + _slice_width = CSSBorder::parse(width); + _attributes_dirty |= DEST_SIZE; + } + + //---------------------------------------------------------------------------- + const SGRect& Image::getRegion() const + { + return _region; + } + + //---------------------------------------------------------------------------- + bool Image::handleEvent(const EventPtr& event) + { + bool handled = Element::handleEvent(event); + + CanvasPtr src_canvas = _src_canvas.lock(); + if( !src_canvas ) + return handled; + + if( MouseEventPtr mouse_event = dynamic_cast(event.get()) ) + { + mouse_event.reset( new MouseEvent(*mouse_event) ); + + mouse_event->client_pos = mouse_event->local_pos + - toOsg(_region.getMin()); + + osg::Vec2f size(_region.width(), _region.height()); + if( _outset.isValid() ) + { + CSSBorder::Offsets outset = + _outset.getAbsOffsets(getTextureDimensions()); + + mouse_event->client_pos += osg::Vec2f(outset.l, outset.t); + size.x() += outset.l + outset.r; + size.y() += outset.t + outset.b; + } + + // Scale event pos according to canvas view size vs. displayed/screen size + mouse_event->client_pos.x() *= src_canvas->getViewWidth() / size.x(); + mouse_event->client_pos.y() *= src_canvas->getViewHeight()/ size.y(); + mouse_event->local_pos = mouse_event->client_pos; + + handled |= src_canvas->handleMouseEvent(mouse_event); + } + else if( KeyboardEventPtr keyboard_event = + dynamic_cast(event.get()) ) + { + handled |= src_canvas->handleKeyboardEvent(keyboard_event); + } + + return handled; } //---------------------------------------------------------------------------- - void Image::update(double dt) + void Image::updateImpl(double dt) { - Element::update(dt); + Element::updateImpl(dt); osg::Texture2D* texture = dynamic_cast ( _geom->getOrCreateStateSet() ->getTextureAttribute(0, osg::StateAttribute::TEXTURE) ); - simgear::canvas::CanvasPtr canvas = _src_canvas.lock(); + auto canvas = _src_canvas.lock(); if( (_attributes_dirty & SRC_CANVAS) // check if texture has changed (eg. due to resizing) @@ -403,188 +586,6 @@ } //---------------------------------------------------------------------------- - void Image::valueChanged(SGPropertyNode* child) - { - // If the image is switched from invisible to visible, and it shows a - // canvas, we need to delay showing it by one frame to ensure the canvas is - // updated before the image is displayed. - // - // As canvas::Element handles and filters changes to the "visible" property - // we can not check this in Image::childChanged but instead have to override - // Element::valueChanged. - if( !isVisible() - && child->getParent() == _node - && child->getNameString() == "visible" - && child->getBoolValue() ) - { - CullCallback* cb = -#if OSG_VERSION_LESS_THAN(3,3,2) - static_cast -#else - dynamic_cast -#endif - ( _geom->getCullCallback() ); - - if( cb ) - cb->cullNextFrame(); - } - - Element::valueChanged(child); - } - - //---------------------------------------------------------------------------- - void Image::setSrcCanvas(CanvasPtr canvas) - { - CanvasPtr src_canvas = _src_canvas.lock(), - self_canvas = _canvas.lock(); - - if( src_canvas ) - src_canvas->removeParentCanvas(self_canvas); - if( self_canvas ) - self_canvas->removeChildCanvas(src_canvas); - - _src_canvas = src_canvas = canvas; - _attributes_dirty |= SRC_CANVAS; - _geom->setCullCallback(canvas ? new CullCallback(canvas) : 0); - - if( src_canvas ) - { - setupDefaultDimensions(); - - if( self_canvas ) - { - self_canvas->addChildCanvas(src_canvas); - src_canvas->addParentCanvas(self_canvas); - } - } - } - - //---------------------------------------------------------------------------- - CanvasWeakPtr Image::getSrcCanvas() const - { - return _src_canvas; - } - - //---------------------------------------------------------------------------- - void Image::setImage(osg::ref_ptr img) - { - // remove canvas... - setSrcCanvas( CanvasPtr() ); - - _texture->setResizeNonPowerOfTwoHint(false); - _texture->setImage(img); - _texture->setWrap(osg::Texture::WRAP_S, osg::Texture::REPEAT); - _texture->setWrap(osg::Texture::WRAP_T, osg::Texture::REPEAT); - _geom->getOrCreateStateSet() - ->setTextureAttributeAndModes(0, _texture); - - if( img ) - setupDefaultDimensions(); - } - - //---------------------------------------------------------------------------- - void Image::setFill(const std::string& fill) - { - osg::Vec4 color(1,1,1,1); - if( !fill.empty() // If no color is given default to white - && !parseColor(fill, color) ) - return; - setFill(color); - } - - //---------------------------------------------------------------------------- - void Image::setFill(const osg::Vec4& color) - { - _colors->front() = color; - _colors->dirty(); - } - - - //---------------------------------------------------------------------------- - void Image::setOutset(const std::string& outset) - { - _outset = CSSBorder::parse(outset); - _attributes_dirty |= DEST_SIZE; - } - - //---------------------------------------------------------------------------- - void Image::setPreserveAspectRatio(const std::string& scale) - { - _preserve_aspect_ratio = SVGpreserveAspectRatio::parse(scale); - _attributes_dirty |= SRC_RECT; - } - - //---------------------------------------------------------------------------- - void Image::setSourceRect(const SGRect& sourceRect) - { - _attributes_dirty |= SRC_RECT; - _src_rect = sourceRect; - } - - //---------------------------------------------------------------------------- - void Image::setSlice(const std::string& slice) - { - _slice = CSSBorder::parse(slice); - _attributes_dirty |= SRC_RECT | DEST_SIZE; - } - - //---------------------------------------------------------------------------- - void Image::setSliceWidth(const std::string& width) - { - _slice_width = CSSBorder::parse(width); - _attributes_dirty |= DEST_SIZE; - } - - //---------------------------------------------------------------------------- - const SGRect& Image::getRegion() const - { - return _region; - } - - //---------------------------------------------------------------------------- - bool Image::handleEvent(const EventPtr& event) - { - bool handled = Element::handleEvent(event); - - CanvasPtr src_canvas = _src_canvas.lock(); - if( !src_canvas ) - return handled; - - if( MouseEventPtr mouse_event = dynamic_cast(event.get()) ) - { - mouse_event.reset( new MouseEvent(*mouse_event) ); - - mouse_event->client_pos = mouse_event->local_pos - - toOsg(_region.getMin()); - - osg::Vec2f size(_region.width(), _region.height()); - if( _outset.isValid() ) - { - CSSBorder::Offsets outset = - _outset.getAbsOffsets(getTextureDimensions()); - - mouse_event->client_pos += osg::Vec2f(outset.l, outset.t); - size.x() += outset.l + outset.r; - size.y() += outset.t + outset.b; - } - - // Scale event pos according to canvas view size vs. displayed/screen size - mouse_event->client_pos.x() *= src_canvas->getViewWidth() / size.x(); - mouse_event->client_pos.y() *= src_canvas->getViewHeight()/ size.y(); - mouse_event->local_pos = mouse_event->client_pos; - - handled |= src_canvas->handleMouseEvent(mouse_event); - } - else if( KeyboardEventPtr keyboard_event = - dynamic_cast(event.get()) ) - { - handled |= src_canvas->handleKeyboardEvent(keyboard_event); - } - - return handled; - } - - //---------------------------------------------------------------------------- void Image::childChanged(SGPropertyNode* child) { const std::string& name = child->getNameString(); @@ -634,7 +635,9 @@ // Abort pending request if( _http_request ) { - Canvas::getSystemAdapter()->getHTTPClient()->cancelRequest(_http_request, "setting new image"); + Canvas::getSystemAdapter() + ->getHTTPClient() + ->cancelRequest(_http_request, "setting new image"); _http_request.reset(); } diff -Nru simgear-2017.3.1+dfsg/simgear/canvas/elements/CanvasImage.hxx simgear-2018.1.1+dfsg/simgear/canvas/elements/CanvasImage.hxx --- simgear-2017.3.1+dfsg/simgear/canvas/elements/CanvasImage.hxx 2017-09-20 07:51:07.000000000 +0000 +++ simgear-2018.1.1+dfsg/simgear/canvas/elements/CanvasImage.hxx 2018-04-06 19:43:17.000000000 +0000 @@ -53,7 +53,6 @@ ElementWeakPtr parent = 0 ); virtual ~Image(); - virtual void update(double dt); virtual void valueChanged(SGPropertyNode* child); void setSrcCanvas(CanvasPtr canvas); @@ -100,8 +99,8 @@ * */ void setSourceRect(const SGRect& sourceRect); - protected: + protected: enum ImageAttributes { SRC_RECT = LAST_ATTRIBUTE << 1, // Source image rectangle @@ -109,6 +108,8 @@ SRC_CANVAS = DEST_SIZE << 1 }; + virtual void updateImpl(double dt); + virtual void childChanged(SGPropertyNode * child); void setupDefaultDimensions(); @@ -134,9 +135,9 @@ osg::ref_ptr _texCoords; osg::ref_ptr _colors; - SGPropertyNode *_node_src_rect; - SGRect _src_rect, - _region; + SGPropertyNode *_node_src_rect = nullptr; + SGRect _src_rect {0, 0}, + _region {0, 0}; SVGpreserveAspectRatio _preserve_aspect_ratio; diff -Nru simgear-2017.3.1+dfsg/simgear/canvas/elements/CanvasMap.cxx simgear-2018.1.1+dfsg/simgear/canvas/elements/CanvasMap.cxx --- simgear-2017.3.1+dfsg/simgear/canvas/elements/CanvasMap.cxx 2017-09-20 07:51:07.000000000 +0000 +++ simgear-2018.1.1+dfsg/simgear/canvas/elements/CanvasMap.cxx 2018-04-06 19:43:17.000000000 +0000 @@ -18,13 +18,14 @@ // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA #include + #include "CanvasMap.hxx" #include "map/geo_node_pair.hxx" #include "map/projection.hxx" -#include +#include -#include +#include #define LOG_GEO_RET(msg) \ {\ @@ -47,6 +48,12 @@ const std::string GEO = "-geo"; const std::string HDG = "hdg"; const std::string Map::TYPE_NAME = "map"; + const std::string WEB_MERCATOR = "webmercator"; + const std::string REF_LAT = "ref-lat"; + const std::string REF_LON = "ref-lon"; + const std::string SCREEN_RANGE = "screen-range"; + const std::string RANGE = "range"; + const std::string PROJECTION = "projection"; //---------------------------------------------------------------------------- void Map::staticInit() @@ -64,12 +71,11 @@ const SGPropertyNode_ptr& node, const Style& parent_style, ElementWeakPtr parent ): - Group(canvas, node, parent_style, parent), - // TODO make projection configurable - _projection(new SansonFlamsteedProjection), - _projection_dirty(true) + Group(canvas, node, parent_style, parent) { staticInit(); + + projectionNodeChanged(node->getChild(PROJECTION)); } //---------------------------------------------------------------------------- @@ -79,13 +85,13 @@ } //---------------------------------------------------------------------------- - void Map::update(double dt) + void Map::updateImpl(double dt) { - for( GeoNodes::iterator it = _geo_nodes.begin(); - it != _geo_nodes.end(); - ++it ) + Group::updateImpl(dt); + + for(auto& it: _geo_nodes) { - GeoNodePair* geo_node = it->second.get(); + GeoNodePair* geo_node = it.second.get(); if( !geo_node->isComplete() || (!geo_node->isDirty() && !_projection_dirty) ) continue; @@ -107,14 +113,12 @@ geo_node->setDirty(false); } _projection_dirty = false; - - Group::update(dt); } //---------------------------------------------------------------------------- void Map::childAdded(SGPropertyNode* parent, SGPropertyNode* child) { - if( boost::ends_with(child->getNameString(), GEO) ) + if( strutils::ends_with(child->getNameString(), GEO) ) _geo_nodes[child].reset(new GeoNodePair()); else if( parent != _node && child->getNameString() == HDG ) _hdg_nodes.insert(child); @@ -125,7 +129,7 @@ //---------------------------------------------------------------------------- void Map::childRemoved(SGPropertyNode* parent, SGPropertyNode* child) { - if( boost::ends_with(child->getNameString(), GEO) ) + if( strutils::ends_with(child->getNameString(), GEO) ) // TODO remove from other node _geo_nodes.erase(child); else if( parent != _node && child->getName() == HDG ) @@ -147,7 +151,7 @@ { const std::string& name = child->getNameString(); - if( boost::ends_with(name, GEO) ) + if( strutils::ends_with(name, GEO) ) return geoNodeChanged(child); else if( name == HDG ) return hdgNodeChanged(child); @@ -162,10 +166,10 @@ if( child->getParent() != _node ) return Group::childChanged(child); - if( child->getNameString() == "ref-lat" - || child->getNameString() == "ref-lon" ) - _projection->setWorldPosition( _node->getDoubleValue("ref-lat"), - _node->getDoubleValue("ref-lon") ); + if( child->getNameString() == REF_LAT + || child->getNameString() == REF_LON ) + _projection->setWorldPosition( _node->getDoubleValue(REF_LAT), + _node->getDoubleValue(REF_LON) ); else if( child->getNameString() == HDG ) { _projection->setOrientation(child->getFloatValue()); @@ -174,15 +178,42 @@ ++it ) hdgNodeChanged(*it); } - else if( child->getNameString() == "range" ) + else if( child->getNameString() == RANGE ) _projection->setRange(child->getDoubleValue()); - else if( child->getNameString() == "screen-range" ) + else if( child->getNameString() == SCREEN_RANGE ) _projection->setScreenRange(child->getDoubleValue()); + else if( child->getNameString() == PROJECTION ) + projectionNodeChanged(child); else return Group::childChanged(child); _projection_dirty = true; } + + //---------------------------------------------------------------------------- + void Map::projectionNodeChanged(SGPropertyNode* child) + { + if(child && child->getStringValue() == WEB_MERCATOR) + _projection = std::make_shared(); + else + _projection = std::make_shared(); + + _projection->setWorldPosition(_node->getDoubleValue(REF_LAT), + _node->getDoubleValue(REF_LON)); + + // Only set existing properties to prevent using 0 instead of default values + + if( auto heading = _node->getChild(HDG) ) + _projection->setOrientation(heading->getFloatValue()); + + if( auto screen_range = _node->getChild(SCREEN_RANGE) ) + _projection->setScreenRange(screen_range->getDoubleValue()); + + if( auto range = _node->getChild(RANGE) ) + _projection->setRange(range->getDoubleValue()); + + _projection_dirty = true; + } //---------------------------------------------------------------------------- void Map::geoNodeChanged(SGPropertyNode* child) diff -Nru simgear-2017.3.1+dfsg/simgear/canvas/elements/CanvasMap.hxx simgear-2018.1.1+dfsg/simgear/canvas/elements/CanvasMap.hxx --- simgear-2017.3.1+dfsg/simgear/canvas/elements/CanvasMap.hxx 2017-09-20 07:51:07.000000000 +0000 +++ simgear-2018.1.1+dfsg/simgear/canvas/elements/CanvasMap.hxx 2018-04-06 19:43:17.000000000 +0000 @@ -22,9 +22,9 @@ #include "CanvasGroup.hxx" -#include -#include -#include +#include +#include +#include namespace simgear { @@ -45,45 +45,41 @@ ElementWeakPtr parent = 0 ); virtual ~Map(); - virtual void update(double dt); - - virtual void childAdded( SGPropertyNode * parent, - SGPropertyNode * child ); - virtual void childRemoved( SGPropertyNode * parent, - SGPropertyNode * child ); - virtual void valueChanged(SGPropertyNode * child); - protected: + virtual void updateImpl(double dt); - virtual void childChanged(SGPropertyNode * child); + void updateProjection(SGPropertyNode* type_node); - typedef boost::unordered_map< SGPropertyNode*, - boost::shared_ptr - > GeoNodes; - typedef boost::unordered_set NodeSet; + virtual void childAdded( SGPropertyNode* parent, + SGPropertyNode* child ); + virtual void childRemoved( SGPropertyNode* parent, + SGPropertyNode* child ); + virtual void valueChanged(SGPropertyNode* child); + virtual void childChanged(SGPropertyNode* child); + + using GeoNodes = + std::unordered_map>; + using NodeSet = std::unordered_set; GeoNodes _geo_nodes; NodeSet _hdg_nodes; - boost::shared_ptr _projection; - bool _projection_dirty; + std::shared_ptr _projection; + bool _projection_dirty = false; struct GeoCoord { - GeoCoord(): - type(INVALID), - value(0) - {} enum { INVALID, LATITUDE, LONGITUDE - } type; - double value; + } type = INVALID; + double value = 0; }; - void geoNodeChanged(SGPropertyNode * child); - void hdgNodeChanged(SGPropertyNode * child); + void projectionNodeChanged(SGPropertyNode* child); + void geoNodeChanged(SGPropertyNode* child); + void hdgNodeChanged(SGPropertyNode* child); GeoCoord parseGeoCoord(const std::string& val) const; }; diff -Nru simgear-2017.3.1+dfsg/simgear/canvas/elements/CanvasPath.cxx simgear-2018.1.1+dfsg/simgear/canvas/elements/CanvasPath.cxx --- simgear-2017.3.1+dfsg/simgear/canvas/elements/CanvasPath.cxx 2017-09-20 07:51:07.000000000 +0000 +++ simgear-2018.1.1+dfsg/simgear/canvas/elements/CanvasPath.cxx 2018-04-06 19:43:17.000000000 +0000 @@ -206,23 +206,13 @@ return SGVec2f(-1.0f, -1.0f); } - //---------------------------------------------------------------------------- - + //---------------------------------------------------------------------------- class Path::PathDrawable: public osg::Drawable { public: PathDrawable(Path* path): - _path_element(path), - _path(VG_INVALID_HANDLE), - _paint(VG_INVALID_HANDLE), - _paint_fill(VG_INVALID_HANDLE), - _attributes_dirty(~0), - _mode(0), - _fill_rule(VG_EVEN_ODD), - _stroke_width(1), - _stroke_linecap(VG_CAP_BUTT), - _stroke_linejoin(VG_JOIN_MITER) + _path_element(path) { setSupportsDisplayList(false); setDataVariance(Object::DYNAMIC); @@ -284,6 +274,16 @@ } /** + * Set path fill opacity (Only used if fill is not "none") + */ + void setFillOpacity(float opacity) + { + _fill_opacity = + static_cast(SGMiscf::clip(opacity, 0.f, 1.f) * 255); + _attributes_dirty |= FILL_COLOR; + } + + /** * Set path fill rule ("pseudo-nonzero" or "evenodd") * * @warning As the current nonzero implementation causes sever artifacts @@ -310,7 +310,7 @@ else if( parseColor(stroke, _stroke_color) ) { _mode |= VG_STROKE_PATH; - _attributes_dirty |= STROKE_COLOR; + _attributes_dirty |= STROKE_COLOR; } else { @@ -324,6 +324,16 @@ } /** + * Set path stroke opacity (only used if stroke is not "none") + */ + void setStrokeOpacity(float opacity) + { + _stroke_opacity = + static_cast(SGMiscf::clip(opacity, 0.f, 1.f) * 255); + _attributes_dirty |= STROKE_COLOR; + } + + /** * Set stroke width */ void setStrokeWidth(float width) @@ -390,31 +400,22 @@ osg::StateAttribute const* blend_func = state->getLastAppliedAttribute(osg::StateAttribute::BLENDFUNC); - // Initialize/Update the paint - if( _attributes_dirty & STROKE_COLOR ) - { - if( _paint == VG_INVALID_HANDLE ) - _paint = vgCreatePaint(); - - vgSetParameterfv(_paint, VG_PAINT_COLOR, 4, _stroke_color._v); - - _attributes_dirty &= ~STROKE_COLOR; - } - - // Initialize/update fill paint - if( _attributes_dirty & FILL_COLOR ) + // Setup paint + if( _mode & VG_STROKE_PATH ) { - if( _paint_fill == VG_INVALID_HANDLE ) - _paint_fill = vgCreatePaint(); + // Initialize/Update the paint + if( _attributes_dirty & STROKE_COLOR ) + { + if( _paint == VG_INVALID_HANDLE ) + _paint = vgCreatePaint(); - vgSetParameterfv(_paint_fill, VG_PAINT_COLOR, 4, _fill_color._v); + auto color = _stroke_color; + color.a() *= _stroke_opacity / 255.f; + vgSetParameterfv(_paint, VG_PAINT_COLOR, 4, color._v); - _attributes_dirty &= ~FILL_COLOR; - } + _attributes_dirty &= ~STROKE_COLOR; + } - // Setup paint - if( _mode & VG_STROKE_PATH ) - { vgSetPaint(_paint, VG_STROKE_PATH); vgSetf(VG_STROKE_LINE_WIDTH, _stroke_width); @@ -426,6 +427,19 @@ } if( _mode & VG_FILL_PATH ) { + // Initialize/update fill paint + if( _attributes_dirty & FILL_COLOR ) + { + if( _paint_fill == VG_INVALID_HANDLE ) + _paint_fill = vgCreatePaint(); + + auto color = _fill_color; + color.a() *= _fill_opacity / 255.f; + vgSetParameterfv(_paint_fill, VG_PAINT_COLOR, 4, color._v); + + _attributes_dirty &= ~FILL_COLOR; + } + vgSetPaint(_paint_fill, VG_FILL_PATH); vgSeti(VG_FILL_RULE, _fill_rule); @@ -579,22 +593,24 @@ Path *_path_element; - mutable VGPath _path; - mutable VGPaint _paint; - mutable VGPaint _paint_fill; - mutable uint32_t _attributes_dirty; + mutable VGPath _path {VG_INVALID_HANDLE}; + mutable VGPaint _paint {VG_INVALID_HANDLE}; + mutable VGPaint _paint_fill {VG_INVALID_HANDLE}; + mutable uint32_t _attributes_dirty {~0u}; CmdList _cmds; CoordList _coords; - VGbitfield _mode; + VGbitfield _mode {0}; osg::Vec4f _fill_color; - VGFillRule _fill_rule; + uint8_t _fill_opacity {255}; + VGFillRule _fill_rule {VG_EVEN_ODD}; osg::Vec4f _stroke_color; - VGfloat _stroke_width; + uint8_t _stroke_opacity {255}; + VGfloat _stroke_width {1}; std::vector _stroke_dash; - VGCapStyle _stroke_linecap; - VGJoinStyle _stroke_linejoin; + VGCapStyle _stroke_linecap {VG_CAP_BUTT}; + VGJoinStyle _stroke_linejoin {VG_JOIN_MITER}; osg::Vec3f transformPoint( const osg::Matrix& m, osg::Vec2f pos ) const @@ -670,8 +686,10 @@ PathDrawableRef Path::*path = &Path::_path; addStyle("fill", "color", &PathDrawable::setFill, path); + addStyle("fill-opacity", "numeric", &PathDrawable::setFillOpacity, path); addStyle("fill-rule", "", &PathDrawable::setFillRule, path); addStyle("stroke", "color", &PathDrawable::setStroke, path); + addStyle("stroke-opacity", "numeric", &PathDrawable::setStrokeOpacity, path); addStyle("stroke-width", "numeric", &PathDrawable::setStrokeWidth, path); addStyle("stroke-dasharray", "", &PathDrawable::setStrokeDashArray, path); addStyle("stroke-linecap", "", &PathDrawable::setStrokeLinecap, path); @@ -701,91 +719,68 @@ } //---------------------------------------------------------------------------- - void Path::update(double dt) + osg::BoundingBox Path::getTransformedBounds(const osg::Matrix& m) const { - if( _attributes_dirty & (CMDS | COORDS) ) - { - _path->setSegments - ( - _node->getChildValues("cmd"), - _node->getChildValues("coord") - ); - - _attributes_dirty &= ~(CMDS | COORDS); - } - - // SVG path overrides manual cmd/coord specification - if ( _hasSVG && (_attributes_dirty & SVG)) - { - CmdList cmds; - CoordList coords; - parseSVGPathToVGPath(_node->getStringValue("svg"), cmds, coords); - _path->setSegments(cmds, coords); - _attributes_dirty &= ~SVG; - } - - if ( _hasRect &&(_attributes_dirty & RECT)) - { - parseRectToVGPath(); - _attributes_dirty &= ~RECT; - - } - - Element::update(dt); + return _path->getTransformedBounds(m); } //---------------------------------------------------------------------------- - osg::BoundingBox Path::getTransformedBounds(const osg::Matrix& m) const + Path& Path::addSegment(uint8_t cmd, std::initializer_list coords) { - return _path->getTransformedBounds(m); + _node->addChild("cmd")->setIntValue(cmd); + + for(float coord: coords) + _node->addChild("coord")->setFloatValue(coord); + + return *this; } //---------------------------------------------------------------------------- Path& Path::moveTo(float x_abs, float y_abs) { - return addSegment(VG_MOVE_TO_ABS, x_abs, y_abs); + return addSegment(VG_MOVE_TO_ABS, {x_abs, y_abs}); } //---------------------------------------------------------------------------- Path& Path::move(float x_rel, float y_rel) { - return addSegment(VG_MOVE_TO_REL, x_rel, y_rel); + return addSegment(VG_MOVE_TO_REL, {x_rel, y_rel}); } //---------------------------------------------------------------------------- Path& Path::lineTo(float x_abs, float y_abs) { - return addSegment(VG_LINE_TO_ABS, x_abs, y_abs); + return addSegment(VG_LINE_TO_ABS, {x_abs, y_abs}); } //---------------------------------------------------------------------------- Path& Path::line(float x_rel, float y_rel) { - return addSegment(VG_LINE_TO_REL, x_rel, y_rel); + return addSegment(VG_LINE_TO_REL, {x_rel, y_rel}); } //---------------------------------------------------------------------------- Path& Path::horizTo(float x_abs) { - return addSegment(VG_HLINE_TO_ABS, x_abs); + return addSegment(VG_HLINE_TO_ABS, {x_abs}); } //---------------------------------------------------------------------------- Path& Path::horiz(float x_rel) { - return addSegment(VG_HLINE_TO_REL, x_rel); + return addSegment(VG_HLINE_TO_REL, {x_rel}); } //---------------------------------------------------------------------------- Path& Path::vertTo(float y_abs) { - return addSegment(VG_VLINE_TO_ABS, y_abs); + return addSegment(VG_VLINE_TO_ABS, {y_abs}); } //---------------------------------------------------------------------------- Path& Path::vert(float y_rel) { - return addSegment(VG_VLINE_TO_REL, y_rel); + return addSegment(VG_VLINE_TO_REL, {y_rel}); } //---------------------------------------------------------------------------- @@ -829,35 +824,69 @@ } //---------------------------------------------------------------------------- + void Path::updateImpl(double dt) + { + Element::updateImpl(dt); + + if( _attributes_dirty & (CMDS | COORDS) ) + { + _path->setSegments + ( + _node->getChildValues("cmd"), + _node->getChildValues("coord") + ); + + _attributes_dirty &= ~(CMDS | COORDS); + } + + // SVG path overrides manual cmd/coord specification + if( _hasSVG && (_attributes_dirty & SVG) ) + { + CmdList cmds; + CoordList coords; + parseSVGPathToVGPath(_node->getStringValue("svg"), cmds, coords); + _path->setSegments(cmds, coords); + _attributes_dirty &= ~SVG; + } + + if( _hasRect &&(_attributes_dirty & RECT) ) + { + parseRectToVGPath(); + _attributes_dirty &= ~RECT; + } + } + + //---------------------------------------------------------------------------- void Path::childChanged(SGPropertyNode* child) { - const std::string& name = child->getNameString(); - const std::string &prName = child->getParent()->getNameString(); + const std::string& name = child->getNameString(); + const std::string &prName = child->getParent()->getNameString(); - if (simgear::strutils::starts_with(name, "border-")) - { - _attributes_dirty |= RECT; - return; - } + if( strutils::starts_with(name, "border-") ) + { + _attributes_dirty |= RECT; + return; + } - if (prName == "rect") { - _hasRect = true; - if (name == "left") { - _rect.setLeft(child->getDoubleValue()); - } else if (name == "top") { - _rect.setTop(child->getDoubleValue()); - } else if (name == "right") { - _rect.setRight(child->getDoubleValue()); - } else if (name == "bottom") { - _rect.setBottom(child->getDoubleValue()); - } else if (name == "width") { - _rect.setWidth(child->getDoubleValue()); - } else if (name == "height") { - _rect.setHeight(child->getDoubleValue()); - } - _attributes_dirty |= RECT; - return; + if (prName == "rect") + { + _hasRect = true; + if (name == "left") { + _rect.setLeft(child->getDoubleValue()); + } else if (name == "top") { + _rect.setTop(child->getDoubleValue()); + } else if (name == "right") { + _rect.setRight(child->getDoubleValue()); + } else if (name == "bottom") { + _rect.setBottom(child->getDoubleValue()); + } else if (name == "width") { + _rect.setWidth(child->getDoubleValue()); + } else if (name == "height") { + _rect.setHeight(child->getDoubleValue()); } + _attributes_dirty |= RECT; + return; + } if( child->getParent() != _node ) return; @@ -893,67 +922,68 @@ return values; } - //---------------------------------------------------------------------------- + //---------------------------------------------------------------------------- + void operator+=(CoordList& base, const std::initializer_list& other) + { + base.insert(base.end(), other.begin(), other.end()); + } - void operator+=(CoordList& base, const std::initializer_list& other) - { - base.insert(base.end(), other.begin(), other.end()); + //---------------------------------------------------------------------------- + void Path::parseRectToVGPath() + { + CmdList commands; + CoordList coords; + commands.reserve(4); + coords.reserve(8); + + bool haveCorner = false; + SGVec2f topLeft = parseRectCornerRadius(_node, "left", "top", haveCorner); + if (haveCorner) { + commands.push_back(VG_MOVE_TO_ABS); + coords += {_rect.l(), _rect.t() + topLeft.y()}; + commands.push_back(VG_SCCWARC_TO_REL); + coords += {topLeft.x(), topLeft.y(), 0.0, topLeft.x(), -topLeft.y()}; + } else { + commands.push_back(VG_MOVE_TO_ABS); + coords += {_rect.l(), _rect.t()}; } - void Path::parseRectToVGPath() - { - CmdList commands; - CoordList coords; - commands.reserve(4); - coords.reserve(8); - - bool haveCorner = false; - SGVec2f topLeft = parseRectCornerRadius(_node, "left", "top", haveCorner); - if (haveCorner) { - commands.push_back(VG_MOVE_TO_ABS); - coords += {_rect.l(), _rect.t() + topLeft.y()}; - commands.push_back(VG_SCCWARC_TO_REL); - coords += {topLeft.x(), topLeft.y(), 0.0, topLeft.x(), -topLeft.y()}; - } else { - commands.push_back(VG_MOVE_TO_ABS); - coords += {_rect.l(), _rect.t()}; - } - - SGVec2f topRight = parseRectCornerRadius(_node, "right", "top", haveCorner); - if (haveCorner) { - commands.push_back(VG_HLINE_TO_ABS); - coords += {_rect.r() - topRight.x()}; - commands.push_back(VG_SCCWARC_TO_REL); - coords += {topRight.x(), topRight.y(), 0.0, topRight.x(), topRight.y()}; - } else { - commands.push_back(VG_HLINE_TO_ABS); - coords += {_rect.r()}; - } - - SGVec2f bottomRight = parseRectCornerRadius(_node, "right", "bottom", haveCorner); - if (haveCorner) { - commands.push_back(VG_VLINE_TO_ABS); - coords += {_rect.b() - bottomRight.y()}; - commands.push_back(VG_SCCWARC_TO_REL); - coords += {bottomRight.x(), bottomRight.y(), 0.0, -bottomRight.x(), bottomRight.y()}; - } else { - commands.push_back(VG_VLINE_TO_ABS); - coords += {_rect.b()}; - } - - SGVec2f bottomLeft = parseRectCornerRadius(_node, "left", "bottom", haveCorner); - if (haveCorner) { - commands.push_back(VG_HLINE_TO_ABS); - coords += {_rect.l() + bottomLeft.x()}; - commands.push_back(VG_SCCWARC_TO_REL); - coords += {bottomLeft.x(), bottomLeft.y(), 0.0, -bottomLeft.x(), -bottomLeft.y()}; - } else { - commands.push_back(VG_HLINE_TO_ABS); - coords += {_rect.l()}; - } + SGVec2f topRight = parseRectCornerRadius(_node, "right", "top", haveCorner); + if (haveCorner) { + commands.push_back(VG_HLINE_TO_ABS); + coords += {_rect.r() - topRight.x()}; + commands.push_back(VG_SCCWARC_TO_REL); + coords += {topRight.x(), topRight.y(), 0.0, topRight.x(), topRight.y()}; + } else { + commands.push_back(VG_HLINE_TO_ABS); + coords += {_rect.r()}; + } + + SGVec2f bottomRight = parseRectCornerRadius(_node, "right", "bottom", haveCorner); + if (haveCorner) { + commands.push_back(VG_VLINE_TO_ABS); + coords += {_rect.b() - bottomRight.y()}; + commands.push_back(VG_SCCWARC_TO_REL); + coords += {bottomRight.x(), bottomRight.y(), 0.0, -bottomRight.x(), bottomRight.y()}; + } else { + commands.push_back(VG_VLINE_TO_ABS); + coords += {_rect.b()}; + } - commands.push_back(VG_CLOSE_PATH); - _path->setSegments(commands, coords); + SGVec2f bottomLeft = parseRectCornerRadius(_node, "left", "bottom", haveCorner); + if (haveCorner) { + commands.push_back(VG_HLINE_TO_ABS); + coords += {_rect.l() + bottomLeft.x()}; + commands.push_back(VG_SCCWARC_TO_REL); + coords += {bottomLeft.x(), bottomLeft.y(), 0.0, -bottomLeft.x(), -bottomLeft.y()}; + } else { + commands.push_back(VG_HLINE_TO_ABS); + coords += {_rect.l()}; } + + commands.push_back(VG_CLOSE_PATH); + _path->setSegments(commands, coords); + } + } // namespace canvas } // namespace simgear diff -Nru simgear-2017.3.1+dfsg/simgear/canvas/elements/CanvasPath.hxx simgear-2018.1.1+dfsg/simgear/canvas/elements/CanvasPath.hxx --- simgear-2017.3.1+dfsg/simgear/canvas/elements/CanvasPath.hxx 2017-09-20 07:51:07.000000000 +0000 +++ simgear-2018.1.1+dfsg/simgear/canvas/elements/CanvasPath.hxx 2018-04-06 19:43:17.000000000 +0000 @@ -20,8 +20,8 @@ #define CANVAS_PATH_HXX_ #include "CanvasElement.hxx" -#include #include +#include namespace simgear { @@ -40,14 +40,10 @@ ElementWeakPtr parent = 0 ); virtual ~Path(); - virtual void update(double dt); - virtual osg::BoundingBox getTransformedBounds(const osg::Matrix& m) const; -#define BOOST_PP_ITERATION_LIMITS (0, 6) -#define BOOST_PP_FILENAME_1 \ - -#include BOOST_PP_ITERATE() + /** Add a segment with the given command and coordinates */ + Path& addSegment(uint8_t cmd, std::initializer_list coords = {}); /** Move path cursor */ Path& moveTo(float x_abs, float y_abs); @@ -70,10 +66,10 @@ void setSVGPath(const std::string& svgPath); - void setRect(const SGRect& r); - void setRoundRect(const SGRect& r, float radiusX, float radiusY = -1.0); - protected: + void setRect(const SGRectf& r); + void setRoundRect(const SGRectf& r, float radiusX, float radiusY = -1.0); + protected: enum PathAttributes { CMDS = LAST_ATTRIBUTE << 1, @@ -88,12 +84,14 @@ bool _hasSVG : 1; bool _hasRect : 1; - SGRect _rect; + SGRectf _rect; + + virtual void updateImpl(double dt); - void parseRectToVGPath(); - virtual void childRemoved(SGPropertyNode * child); virtual void childChanged(SGPropertyNode * child); + + void parseRectToVGPath(); }; } // namespace canvas diff -Nru simgear-2017.3.1+dfsg/simgear/canvas/elements/CanvasText.cxx simgear-2018.1.1+dfsg/simgear/canvas/elements/CanvasText.cxx --- simgear-2017.3.1+dfsg/simgear/canvas/elements/CanvasText.cxx 2017-09-20 07:51:07.000000000 +0000 +++ simgear-2018.1.1+dfsg/simgear/canvas/elements/CanvasText.cxx 2018-04-06 19:43:17.000000000 +0000 @@ -865,12 +865,12 @@ //---------------------------------------------------------------------------- osg::StateSet* Text::getOrCreateStateSet() { - if( !_transform.valid() ) - return 0; + if( !_scene_group.valid() ) + return nullptr; // Only check for StateSet on Transform, as the text stateset is shared // between all text instances using the same font (texture). - return _transform->getOrCreateStateSet(); + return _scene_group->getOrCreateStateSet(); } } // namespace canvas diff -Nru simgear-2017.3.1+dfsg/simgear/canvas/elements/CMakeLists.txt simgear-2018.1.1+dfsg/simgear/canvas/elements/CMakeLists.txt --- simgear-2017.3.1+dfsg/simgear/canvas/elements/CMakeLists.txt 2017-09-20 07:51:07.000000000 +0000 +++ simgear-2018.1.1+dfsg/simgear/canvas/elements/CMakeLists.txt 2018-04-06 19:43:17.000000000 +0000 @@ -9,10 +9,6 @@ CanvasText.hxx ) -set(DETAIL_HEADERS - detail/add_segment_variadic.hxx -) - set(SOURCES CanvasElement.cxx CanvasGroup.cxx @@ -23,7 +19,6 @@ ) simgear_scene_component(canvas-elements canvas/elements "${SOURCES}" "${HEADERS}") -simgear_component(canvas-elements/detail canvas/elements/detail "" "${DETAIL_HEADERS}") add_boost_test(canvas_element SOURCES canvas_element_test.cpp diff -Nru simgear-2017.3.1+dfsg/simgear/canvas/elements/detail/add_segment_variadic.hxx simgear-2018.1.1+dfsg/simgear/canvas/elements/detail/add_segment_variadic.hxx --- simgear-2017.3.1+dfsg/simgear/canvas/elements/detail/add_segment_variadic.hxx 2017-09-20 07:51:07.000000000 +0000 +++ simgear-2018.1.1+dfsg/simgear/canvas/elements/detail/add_segment_variadic.hxx 1970-01-01 00:00:00.000000000 +0000 @@ -1,21 +0,0 @@ -#ifndef CANVAS_PATH_HXX_ -# error Canvas - do not include this file! -#endif - -#define n BOOST_PP_ITERATION() - -Path& addSegment( uint8_t cmd - BOOST_PP_COMMA_IF(n) - BOOST_PP_ENUM_PARAMS(n, float coord) ) -{ - _node->addChild("cmd")->setIntValue(cmd); - -#define SG_CANVAS_PATH_SET_COORD(z, n, dummy)\ - _node->addChild("coord")->setFloatValue(coord##n); - - BOOST_PP_REPEAT(n, SG_CANVAS_PATH_SET_COORD, 0) -#undef SG_CANVAS_PATH_SET_COORD - return *this; -} - -#undef n diff -Nru simgear-2017.3.1+dfsg/simgear/canvas/elements/map/projection.hxx simgear-2018.1.1+dfsg/simgear/canvas/elements/map/projection.hxx --- simgear-2017.3.1+dfsg/simgear/canvas/elements/map/projection.hxx 2017-09-20 07:51:07.000000000 +0000 +++ simgear-2018.1.1+dfsg/simgear/canvas/elements/map/projection.hxx 2018-04-06 19:43:17.000000000 +0000 @@ -190,6 +190,32 @@ } }; + /** + * WebMercator projection, relative to the projection center. + * Required for Slippy Maps - i.e. openstreetmap + */ + class WebMercatorProjection: + public HorizontalProjection + { + protected: + + virtual ScreenPosition project(double lat, double lon) const + { + double d_lat = lat - _ref_lat, + d_lon = lon - _ref_lon; + double r = 6378137.f / 1852; // Equatorial radius divided by ? + + ScreenPosition pos; + + pos.x = r * d_lon; + pos.y = r * (log(tan(d_lat) + 1.0 / cos(d_lat))); + //pos.x = lon; + //pos.y = log(tan(lat) + 1.0 / cos(lat)); + return pos; + } + }; + + } // namespace canvas } // namespace simgear diff -Nru simgear-2017.3.1+dfsg/simgear/canvas/events/KeyboardEvent.cxx simgear-2018.1.1+dfsg/simgear/canvas/events/KeyboardEvent.cxx --- simgear-2017.3.1+dfsg/simgear/canvas/events/KeyboardEvent.cxx 2017-09-20 07:51:07.000000000 +0000 +++ simgear-2018.1.1+dfsg/simgear/canvas/events/KeyboardEvent.cxx 2018-04-06 19:43:17.000000000 +0000 @@ -19,7 +19,6 @@ #include #include "KeyboardEvent.hxx" -#include "utf8.h" #include @@ -42,6 +41,7 @@ // TODO check Win/Mac keycode for altgr/ISO Level3 Shift const uint32_t KEY_AltGraph = 0xfe03; + //---------------------------------------------------------------------------- KeyboardEvent::KeyboardEvent(): @@ -269,10 +269,24 @@ // Empty or no mapping -> convert UTF-32 key value to UTF-8 if( _name.empty() ) { - if( !utf8::internal::is_code_point_valid(_key) ) + if (( _key >= 0xd800u && _key <= 0xdfffu ) || _key > 0x10ffffu ) _name = "Unidentified"; else - utf8::unchecked::append(_key, std::back_inserter(_name)); + if ( _key <= 0x7f ) { + _name.push_back(static_cast(_key)); + } else if ( _key <= 0x7ff ) { + _name.push_back(static_cast((_key >> 6) | 0xc0)); + _name.push_back(static_cast((_key & 0x3f) | 0x80)); + } else if ( _key <= 0xffff ) { + _name.push_back(static_cast((_key >> 12) | 0xe0)); + _name.push_back(static_cast(((_key >> 6) & 0x3f) | 0x80)); + _name.push_back(static_cast((_key & 0x3f) | 0x80)); + } else { + _name.push_back(static_cast((_key >> 18) | 0xf0)); + _name.push_back(static_cast(((_key >> 12) & 0x3f) | 0x80)); + _name.push_back(static_cast(((_key >> 6) & 0x3f) | 0x80)); + _name.push_back(static_cast((_key & 0x3f) | 0x80)); + } } // Keys on the numpad with NumLock enabled are reported just like their @@ -307,11 +321,30 @@ if( key_name.empty() ) return false; - std::string::const_iterator it = key_name.begin(); - uint32_t cp = utf8::next(it, key_name.end()); + // Convert the key name to the corresponding code point by checking the + // sequence length (the first bits of the first byte) and performing the + // conversion accordingly. + uint32_t cp = key_name[0] & 0xff; + size_t len; + if (cp < 0x80) { + len = 1; + } else if ((cp >> 5) == 0x6) { + cp = ((cp << 6) & 0x7ff) + (key_name[1] & 0x3f); + len = 2; + } else if ((cp >> 4) == 0xe) { + cp = ((cp << 12) & 0xffff) + (((key_name[1] & 0xff) << 6) & 0xfff) + + (key_name[2] & 0x3f); + len = 3; + } else if ((cp >> 3) == 0x1e) { + cp = ((cp << 18) & 0x1fffff) + (((key_name[1] & 0xff) << 12) & 0x3ffff) + + (((key_name[2] & 0xff) << 6) & 0xfff) + (key_name[3] & 0x3f); + len = 4; + } else { + return false; + } // Check if _name contains exactly one (UTF-8 encoded) character. - if( it != key_name.end() ) + if (key_name.length() > len) return false; // C0 and C1 control characters are not printable. diff -Nru simgear-2017.3.1+dfsg/simgear/canvas/layout/NasalWidget.cxx simgear-2018.1.1+dfsg/simgear/canvas/layout/NasalWidget.cxx --- simgear-2017.3.1+dfsg/simgear/canvas/layout/NasalWidget.cxx 2017-09-20 07:51:07.000000000 +0000 +++ simgear-2018.1.1+dfsg/simgear/canvas/layout/NasalWidget.cxx 2018-04-06 19:43:17.000000000 +0000 @@ -45,7 +45,7 @@ //---------------------------------------------------------------------------- NasalWidget::~NasalWidget() { - onRemove(); + } //---------------------------------------------------------------------------- @@ -189,10 +189,10 @@ if( hfw.empty() ) return -1; - naContext c = naNewContext(); try { - return hfw(nasal::to_nasal(c, const_cast(this)), w); + nasal::Context ctx; + return hfw(ctx.to_me(const_cast(this)), w); } catch( std::exception const& ex ) { @@ -202,7 +202,6 @@ "NasalWidget.heightForWidth: callback error: '" << ex.what() << "'" ); } - naFreeContext(c); return -1; } @@ -262,8 +261,8 @@ try { - nasal::Context c; - _set_geometry(nasal::to_nasal(c, this), rect); + nasal::Context ctx; + _set_geometry(ctx.to_me(this), rect); _flags &= ~LAYOUT_DIRTY; } catch( std::exception const& ex ) diff -Nru simgear-2017.3.1+dfsg/simgear/CMakeLists.txt simgear-2018.1.1+dfsg/simgear/CMakeLists.txt --- simgear-2017.3.1+dfsg/simgear/CMakeLists.txt 2017-09-20 07:51:07.000000000 +0000 +++ simgear-2018.1.1+dfsg/simgear/CMakeLists.txt 2018-04-06 19:43:17.000000000 +0000 @@ -15,6 +15,7 @@ nasal/cppbind props serial + std structure threads timing diff -Nru simgear-2017.3.1+dfsg/simgear/constants.h simgear-2018.1.1+dfsg/simgear/constants.h --- simgear-2017.3.1+dfsg/simgear/constants.h 2017-09-20 07:51:07.000000000 +0000 +++ simgear-2018.1.1+dfsg/simgear/constants.h 2018-04-06 19:43:17.000000000 +0000 @@ -95,7 +95,7 @@ /** Value of earth radius from LaRCsim (ft) */ #define SG_EQUATORIAL_RADIUS_FT 20925650. -/** Value of earth radius from LaRCsim (meter) */ +/** Value of equatorial earth radius from LaRCsim (meter) */ #define SG_EQUATORIAL_RADIUS_M 6378138.12 /** Radius squared (ft) */ @@ -104,6 +104,8 @@ /** Radius squared (meter) */ #define SG_EQ_RAD_SQUARE_M 40680645877797.1344 +/** Value of WGS84 polar earth radius (meter) */ +#define SG_POLAR_RADIUS_M 6356752.3142451794975639668 // Physical Constants, SI diff -Nru simgear-2017.3.1+dfsg/simgear/embedded_resources/CMakeLists.txt simgear-2018.1.1+dfsg/simgear/embedded_resources/CMakeLists.txt --- simgear-2017.3.1+dfsg/simgear/embedded_resources/CMakeLists.txt 2017-09-20 07:51:07.000000000 +0000 +++ simgear-2018.1.1+dfsg/simgear/embedded_resources/CMakeLists.txt 2018-04-06 19:43:17.000000000 +0000 @@ -1,7 +1,11 @@ include (SimGearComponent) -set(HEADERS EmbeddedResource.hxx EmbeddedResourceManager.hxx) -set(SOURCES EmbeddedResource.cxx EmbeddedResourceManager.cxx) +set(HEADERS EmbeddedResource.hxx + EmbeddedResourceManager.hxx + EmbeddedResourceProxy.hxx) +set(SOURCES EmbeddedResource.cxx + EmbeddedResourceManager.cxx + EmbeddedResourceProxy.cxx) simgear_component(embedded_resources embedded_resources "${SOURCES}" "${HEADERS}") diff -Nru simgear-2017.3.1+dfsg/simgear/embedded_resources/EmbeddedResourceProxy.cxx simgear-2018.1.1+dfsg/simgear/embedded_resources/EmbeddedResourceProxy.cxx --- simgear-2017.3.1+dfsg/simgear/embedded_resources/EmbeddedResourceProxy.cxx 1970-01-01 00:00:00.000000000 +0000 +++ simgear-2018.1.1+dfsg/simgear/embedded_resources/EmbeddedResourceProxy.cxx 2018-04-06 19:43:17.000000000 +0000 @@ -0,0 +1,257 @@ +// -*- coding: utf-8 -*- +// +// EmbeddedResourceProxy.cxx --- Unified access to real files or embedded +// resources +// Copyright (C) 2017 Florent Rougon +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Library General Public +// License as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later version. +// +// This library 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 +// Library General Public License for more details. +// +// You should have received a copy of the GNU Library General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301 USA. + +#include + +#include // std::find() +#include // std::streamsize +#include +#include // std::numeric_limits +#include +#include +#include +#include // std::size_t +#include + +#include +#include +#include +#include +#include "EmbeddedResourceManager.hxx" +#include "EmbeddedResourceProxy.hxx" + +using std::string; +using std::vector; +using std::shared_ptr; +using std::unique_ptr; + + +namespace simgear +{ + +EmbeddedResourceProxy::EmbeddedResourceProxy(const SGPath& realRoot, + const string& virtualRoot, + bool useEmbeddedResourcesByDefault) + : _realRoot(realRoot), + _virtualRoot(normalizeVirtualRoot(virtualRoot)), + _useEmbeddedResourcesByDefault(useEmbeddedResourcesByDefault) +{ } + +SGPath +EmbeddedResourceProxy::getRealRoot() const +{ return _realRoot; } + +void +EmbeddedResourceProxy::setRealRoot(const SGPath& realRoot) +{ _realRoot = realRoot; } + +string +EmbeddedResourceProxy::getVirtualRoot() const +{ return _virtualRoot; } + +void +EmbeddedResourceProxy::setVirtualRoot(const string& virtualRoot) +{ _virtualRoot = normalizeVirtualRoot(virtualRoot); } + +bool +EmbeddedResourceProxy::getUseEmbeddedResources() const +{ return _useEmbeddedResourcesByDefault; } + +void +EmbeddedResourceProxy::setUseEmbeddedResources(bool useEmbeddedResources) +{ _useEmbeddedResourcesByDefault = useEmbeddedResources; } + +// Static method: normalize the 'virtualRoot' argument of the constructor +// +// The argument must start with a slash and mustn't contain any '.' or '..' +// component. The return value never ends with a slash. +string +EmbeddedResourceProxy::normalizeVirtualRoot(const string& path) +{ + EmbeddedResourceProxy::checkPath(__func__, path, + false /* allowStartWithColon */); + string res = path; + + // Make sure 'res' doesn't end with a '/'. + while (!res.empty() && res.back() == '/') { + res.pop_back(); // This will ease path concatenation + } + + return res; +} + +// Static method +void +EmbeddedResourceProxy::checkPath(const string& callerMethod, const string& path, + bool allowStartWithColon) +{ + if (path.empty()) { + throw sg_format_exception( + "Invalid empty path for EmbeddedResourceProxy::" + + callerMethod + "(): '" + path + "'", path); + } else if (allowStartWithColon && + !simgear::strutils::starts_with(path, ":/") && path[0] != '/') { + throw sg_format_exception( + "Invalid path for EmbeddedResourceProxy::" + callerMethod + "(): " + "it should start with either ':/' or '/'", path); + } else if (!allowStartWithColon && path[0] != '/') { + throw sg_format_exception( + "Invalid path for EmbeddedResourceProxy::" + callerMethod + "(): " + "it should start with a slash ('/')", path); + } else { + const vector components = simgear::strutils::split(path, "/"); + auto find = [&components](const string& s) -> bool { + return (std::find(components.begin(), components.end(), s) != + components.end()); + }; + + if (find(".") || find("..")) { + throw sg_format_exception( + "Invalid path for EmbeddedResourceProxy::" + callerMethod + "(): " + "'.' and '..' components are not allowed", path); + } + } +} + +unique_ptr +EmbeddedResourceProxy::getIStream(const string& path, bool fromEmbeddedResource) + const +{ + EmbeddedResourceProxy::checkPath(__func__, path, + false /* allowStartWithColon */); + assert(!path.empty() && path.front() == '/'); + + if (fromEmbeddedResource) { + const auto& embeddedResMgr = simgear::EmbeddedResourceManager::instance(); + return embeddedResMgr->getIStream( + _virtualRoot + path, + ""); // fetch the default-locale version of the resource + } else { + const SGPath sgPath = _realRoot / path.substr(std::size_t(1)); + return unique_ptr(new sg_ifstream(sgPath)); + } +} + +unique_ptr +EmbeddedResourceProxy::getIStream(const string& path) const +{ + return getIStream(path, _useEmbeddedResourcesByDefault); +} + +unique_ptr +EmbeddedResourceProxy::getIStreamDecideOnPrefix(const string& path) const +{ + EmbeddedResourceProxy::checkPath(__func__, path, + true /* allowStartWithColon */); + + // 'path' is non-empty + if (path.front() == '/') { + return getIStream(path, false /* fromEmbeddedResource */); + } else if (path.front() == ':') { + assert(path.size() >= 2 && path[1] == '/'); + // Skip the leading ':' + return getIStream(path.substr(std::size_t(1)), + true /* fromEmbeddedResource */); + } else { + // The checkPath() call should make it impossible to reach this point. + std::abort(); + } +} + +string +EmbeddedResourceProxy::getString(const string& path, bool fromEmbeddedResource) + const +{ + string result; + + EmbeddedResourceProxy::checkPath(__func__, path, + false /* allowStartWithColon */); + assert(!path.empty() && path.front() == '/'); + + if (fromEmbeddedResource) { + const auto& embeddedResMgr = simgear::EmbeddedResourceManager::instance(); + // Fetch the default-locale version of the resource + result = embeddedResMgr->getString(_virtualRoot + path, ""); + } else { + const SGPath sgPath = _realRoot / path.substr(std::size_t(1)); + result.reserve(sgPath.sizeInBytes()); + const unique_ptr streamp = getIStream(path, + fromEmbeddedResource); + std::streamsize nbCharsRead; + + // Allocate a buffer + static constexpr std::size_t bufSize = 65536; + static_assert(bufSize <= std::numeric_limits::max(), + "Type std::streamsize is unexpectedly small"); + static_assert(bufSize <= std::numeric_limits::max(), + "Type std::string::size_type is unexpectedly small"); + unique_ptr buf(new char[bufSize]); + + do { + streamp->read(buf.get(), bufSize); + nbCharsRead = streamp->gcount(); + + if (nbCharsRead > 0) { + result.append(buf.get(), nbCharsRead); + } + } while (*streamp); + + // streamp->fail() would *not* indicate an error, due to the semantics + // of std::istream::read(). + if (streamp->bad()) { + throw sg_io_exception("Error reading from file", sg_location(path)); + } + } + + return result; +} + +string +EmbeddedResourceProxy::getString(const string& path) const +{ + return getString(path, _useEmbeddedResourcesByDefault); +} + +string +EmbeddedResourceProxy::getStringDecideOnPrefix(const string& path) const +{ + string result; + + EmbeddedResourceProxy::checkPath(__func__, path, + true /* allowStartWithColon */); + + // 'path' is non-empty + if (path.front() == '/') { + result = getString(path, false /* fromEmbeddedResource */); + } else if (path.front() == ':') { + assert(path.size() >= 2 && path[1] == '/'); + // Skip the leading ':' + result = getString(path.substr(std::size_t(1)), + true /* fromEmbeddedResource */); + } else { + // The checkPath() call should make it impossible to reach this point. + std::abort(); + } + + return result; +} + +} // of namespace simgear diff -Nru simgear-2017.3.1+dfsg/simgear/embedded_resources/EmbeddedResourceProxy.hxx simgear-2018.1.1+dfsg/simgear/embedded_resources/EmbeddedResourceProxy.hxx --- simgear-2017.3.1+dfsg/simgear/embedded_resources/EmbeddedResourceProxy.hxx 1970-01-01 00:00:00.000000000 +0000 +++ simgear-2018.1.1+dfsg/simgear/embedded_resources/EmbeddedResourceProxy.hxx 2018-04-06 19:43:17.000000000 +0000 @@ -0,0 +1,155 @@ +// -*- coding: utf-8 -*- +// +// EmbeddedResourceProxy.hxx --- Unified access to real files or embedded +// resources +// Copyright (C) 2017 Florent Rougon +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Library General Public +// License as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later version. +// +// This library 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 +// Library General Public License for more details. +// +// You should have received a copy of the GNU Library General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301 USA. + +#ifndef FG_EMBEDDEDRESOURCEPROXY_HXX +#define FG_EMBEDDEDRESOURCEPROXY_HXX + +#include +#include +#include + +#include + +// The EmbeddedResourceProxy class allows one to access real files or embedded +// resources in a unified way. When using it, one can switch from one data +// source to the other with minimal code changes, possibly even at runtime (in +// which case there is obviously no code change at all). +// +// Sample usage of the EmbeddedResourceProxy class (from FlightGear): +// +// simgear::EmbeddedResourceProxy proxy(globals->get_fg_root(), "/FGData"); +// std::string s = proxy.getString("/some/path"); +// std::unique_ptr streamp = proxy.getIStream("/some/path"); +// +// The methods getString(const std::string& path) and +// getIStream(const std::string& path) of EmbeddedResourceProxy decide whether +// to use embedded resources or real files depending on the boolean value +// passed to EmbeddedResourceProxy::setUseEmbeddedResources() (also available +// as an optional parameter to the EmbeddedResourceProxy constructor, +// defaulting to true). It is often most convenient to set this boolean once +// and then don't worry about it anymore (it is stored as a data member of +// EmbeddedResourceProxy). Otherwise, if you want to fetch resources some +// times from real files, other times from embedded resources, you may use the +// following methods: +// +// // Retrieve contents using embedded resources +// std:string s = proxy.getString("/some/path", true); +// std:string s = proxy.getStringDecideOnPrefix(":/some/path"); +// +// // Retrieve contents using real files +// std:string s = proxy.getString("/some/path", false); +// std:string s = proxy.getStringDecideOnPrefix("/some/path"); +// +// You can do exactly the same with EmbeddedResourceProxy::getIStream() and +// EmbeddedResourceProxy::getIStreamDecideOnPrefix(), except they return an +// std::unique_ptr instead of an std::string. +// +// Given how the 'proxy' object was constructed above, each of these calls +// will fetch data from either the real file $FG_ROOT/some/path or the +// embedded resource whose virtual path is '/FGData/some/path' (more +// precisely: the default-locale version of this resource). +// +// The 'path' argument of EmbeddedResourceProxy's methods getString(), +// getIStream(), getStringDecideOnPrefix() and getIStreamDecideOnPrefix() +// must: +// +// - use UTF-8 encoding; +// +// - start with: +// * either '/' or ':/' for the 'DecideOnPrefix' variants; +// * only '/' for the other methods. +// +// - have its components separated by slashes; +// +// - not contain any '.' or '..' component. +// +// For the 'DecideOnPrefix' variants: +// +// - if the path starts with a slash ('/'), a real file access is done; +// +// - if, on the other hand, it starts with ':/', EmbeddedResourceProxy uses +// the embedded resource whose virtual path is the specified path without +// its leading ':' (more precisely: the default-locale version of this +// resource). +namespace simgear +{ + +class EmbeddedResourceProxy +{ +public: + // 'virtualRoot' must start with a '/', e.g: '/FGData'. Whether it ends + // with a '/' doesn't make a difference. + explicit EmbeddedResourceProxy(const SGPath& realRoot, + const std::string& virtualRoot, + bool useEmbeddedResourcesByDefault = true); + + // Getters and setters for the corresponding data members + SGPath getRealRoot() const; + void setRealRoot(const SGPath& realRoot); + + std::string getVirtualRoot() const; + void setVirtualRoot(const std::string& virtualRoot); + + bool getUseEmbeddedResources() const; + void setUseEmbeddedResources(bool useEmbeddedResources); + + // Get an std::istream to read from a file or from an embedded resource. + std::unique_ptr + getIStream(const std::string& path, bool fromEmbeddedResource) const; + + std::unique_ptr + getIStream(const std::string& path) const; + + std::unique_ptr + getIStreamDecideOnPrefix(const std::string& path) const; + + // Get a file or embedded resource contents as a string. + std::string + getString(const std::string& path, bool fromEmbeddedResource) const; + + std::string + getString(const std::string& path) const; + + std::string + getStringDecideOnPrefix(const std::string& path) const; + +private: + // Check that 'path' starts with either ':/' or '/', and doesn't contain any + // '..' component ('path' may only start with ':/' if 'allowStartWithColon' + // is true). + static void + checkPath(const std::string& callerMethod, const std::string& path, + bool allowStartWithColon); + + // Normalize the 'virtualRoot' argument of the constructor. The argument + // must start with a '/' and mustn't contain any '.' or '..' component. The + // return value never ends with a '/'. + static std::string + normalizeVirtualRoot(const std::string& path); + + SGPath _realRoot; + std::string _virtualRoot; + bool _useEmbeddedResourcesByDefault; +}; + +} // of namespace simgear + +#endif // of FG_EMBEDDEDRESOURCEPROXY_HXX diff -Nru simgear-2017.3.1+dfsg/simgear/embedded_resources/embedded_resources_test.cxx simgear-2018.1.1+dfsg/simgear/embedded_resources/embedded_resources_test.cxx --- simgear-2017.3.1+dfsg/simgear/embedded_resources/embedded_resources_test.cxx 2017-09-20 07:51:07.000000000 +0000 +++ simgear-2018.1.1+dfsg/simgear/embedded_resources/embedded_resources_test.cxx 2018-04-06 19:43:17.000000000 +0000 @@ -33,11 +33,14 @@ #include // std::size_t #include +#include #include #include +#include #include #include "EmbeddedResource.hxx" #include "EmbeddedResourceManager.hxx" +#include "EmbeddedResourceProxy.hxx" using std::cout; using std::cerr; @@ -395,6 +398,106 @@ } } +// Auxiliary function for test_EmbeddedResourceProxy() +void auxTest_EmbeddedResourceProxy_getIStream(unique_ptr iStream, + const string& contents) +{ + cout << "Testing EmbeddedResourceProxy::getIStream()" << endl; + + iStream->exceptions(std::ios_base::badbit); + static constexpr std::size_t bufSize = 65536; + unique_ptr buf(new char[bufSize]); // intermediate buffer + string result; + + do { + iStream->read(buf.get(), bufSize); + result.append(buf.get(), iStream->gcount()); + } while (*iStream); // iStream *points* to an std::istream + + // 1) If set, badbit would have caused an exception to be raised (see above). + // 2) failbit doesn't necessarily indicate an error here: it is set as soon + // as the read() call can't provide the requested number of characters. + SG_VERIFY(iStream->eof() && !iStream->bad()); + SG_CHECK_EQUAL(result, contents); +} + +void test_EmbeddedResourceProxy() +{ + cout << "Testing the EmbeddedResourceProxy class" << endl; + + // Initialize stuff we need and create two files containing the contents of + // the default-locale version of two embedded resources: those with virtual + // paths '/path/to/resource1' and '/path/to/resource2'. + const auto& resMgr = EmbeddedResourceManager::instance(); + simgear::Dir tmpDir = simgear::Dir::tempDir("FlightGear"); + tmpDir.setRemoveOnDestroy(); + + const SGPath path1 = tmpDir.path() / "resource1"; + const SGPath path2 = tmpDir.path() / "resource2"; + + sg_ofstream out1(path1); + sg_ofstream out2(path2); + const string s1 = resMgr->getString("/path/to/resource1", ""); + // To make sure in these tests that we can tell whether something came from + // a real file or from an embedded resource. + const string rs1 = s1 + " from real file"; + const string rlipsum = lipsum + " from real file"; + + out1 << rs1; + out1.close(); + if (!out1) { + throw sg_io_exception("Error writing to file", sg_location(path1)); + } + + out2 << rlipsum; + out2.close(); + if (!out2) { + throw sg_io_exception("Error writing to file", sg_location(path2)); + } + + // 'proxy' defaults to using embedded resources + const simgear::EmbeddedResourceProxy proxy(tmpDir.path(), "/path/to", + /* useEmbeddedResourcesByDefault */ + true); + simgear::EmbeddedResourceProxy rproxy(tmpDir.path(), "/path/to"); + // 'rproxy' defaults to using real files + rproxy.setUseEmbeddedResources(false); // could be done from the ctor too + + // Test EmbeddedResourceProxy::getString() + SG_CHECK_EQUAL(proxy.getStringDecideOnPrefix("/resource1"), rs1); + SG_CHECK_EQUAL(proxy.getStringDecideOnPrefix(":/resource1"), s1); + SG_CHECK_EQUAL(proxy.getString("/resource1", false), rs1); + SG_CHECK_EQUAL(proxy.getString("/resource1", true), s1); + SG_CHECK_EQUAL(proxy.getString("/resource1"), s1); + SG_CHECK_EQUAL(rproxy.getString("/resource1"), rs1); + + SG_CHECK_EQUAL(proxy.getStringDecideOnPrefix("/resource2"), rlipsum); + SG_CHECK_EQUAL(proxy.getStringDecideOnPrefix(":/resource2"), lipsum); + SG_CHECK_EQUAL(proxy.getString("/resource2", false), rlipsum); + SG_CHECK_EQUAL(proxy.getString("/resource2", true), lipsum); + SG_CHECK_EQUAL(proxy.getString("/resource2"), lipsum); + SG_CHECK_EQUAL(rproxy.getString("/resource2"), rlipsum); + + // Test EmbeddedResourceProxy::getIStream() + auxTest_EmbeddedResourceProxy_getIStream( + proxy.getIStreamDecideOnPrefix("/resource1"), + rs1); + auxTest_EmbeddedResourceProxy_getIStream( + proxy.getIStreamDecideOnPrefix(":/resource1"), + s1); + auxTest_EmbeddedResourceProxy_getIStream(proxy.getIStream("/resource1"), s1); + auxTest_EmbeddedResourceProxy_getIStream(rproxy.getIStream("/resource1"), rs1); + + auxTest_EmbeddedResourceProxy_getIStream(proxy.getIStream("/resource2", false), + rlipsum); + auxTest_EmbeddedResourceProxy_getIStream(proxy.getIStream("/resource2", true), + lipsum); + auxTest_EmbeddedResourceProxy_getIStream(proxy.getIStream("/resource2"), + lipsum); + auxTest_EmbeddedResourceProxy_getIStream(rproxy.getIStream("/resource2"), + rlipsum); +} + int main(int argc, char **argv) { // Initialize the EmbeddedResourceManager instance, add a few resources @@ -407,6 +510,7 @@ test_addAlreadyExistingResource(); test_localeDependencyOfResourceFetching(); test_getLocaleAndSelectLocale(); + test_EmbeddedResourceProxy(); return EXIT_SUCCESS; } diff -Nru simgear-2017.3.1+dfsg/simgear/environment/CMakeLists.txt simgear-2018.1.1+dfsg/simgear/environment/CMakeLists.txt --- simgear-2017.3.1+dfsg/simgear/environment/CMakeLists.txt 2017-09-20 07:51:07.000000000 +0000 +++ simgear-2018.1.1+dfsg/simgear/environment/CMakeLists.txt 2018-04-06 19:43:17.000000000 +0000 @@ -21,4 +21,9 @@ endif() add_test(metar ${EXECUTABLE_OUTPUT_PATH}/test_metar) + +add_executable(test_precipitation test_precipitation.cxx) +target_link_libraries(test_precipitation ${TEST_LIBS}) +add_test(precipitation ${EXECUTABLE_OUTPUT_PATH}/test_precipitation) + endif(ENABLE_TESTS) diff -Nru simgear-2017.3.1+dfsg/simgear/environment/precipitation.cxx simgear-2018.1.1+dfsg/simgear/environment/precipitation.cxx --- simgear-2017.3.1+dfsg/simgear/environment/precipitation.cxx 2017-09-20 07:51:07.000000000 +0000 +++ simgear-2018.1.1+dfsg/simgear/environment/precipitation.cxx 2018-04-06 19:43:17.000000000 +0000 @@ -7,7 +7,7 @@ * * @brief Precipitation effects to draw rain and snow. * - * @par Licences + * @par License * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the @@ -25,7 +25,6 @@ */ #include "precipitation.hxx" -//#include "visual_enviro.hxx" #include #include @@ -65,7 +64,7 @@ */ osg::Group* SGPrecipitation::build(void) { - osg::Group* group = new osg::Group; + osg::ref_ptr group = new osg::Group; _precipitationEffect->snow(0); _precipitationEffect->rain(0); @@ -73,8 +72,9 @@ if (_clip_distance!=0.0) { osg::ref_ptr clipNode = new osg::ClipNode; - clipNode->addClipPlane( new osg::ClipPlane( 0 ) ); - clipNode->getClipPlane(0)->setClipPlane( 0.0, 0.0, -1.0, -_clip_distance ); + osg::ref_ptr clipPlane = new osg::ClipPlane(0); + clipNode->addClipPlane(clipPlane.get()); + clipNode->getClipPlane(0)->setClipPlane(0.0, 0.0, -1.0, -_clip_distance); clipNode->setReferenceFrame(osg::ClipNode::ABSOLUTE_RF); clipNode->addChild(_precipitationEffect.get()); @@ -87,7 +87,7 @@ group->setNodeMask( ~(simgear::CASTSHADOW_BIT | simgear::MODELLIGHT_BIT) ); - return group; + return group.release(); } @@ -140,8 +140,8 @@ /** * @brief Define the illumination multiplier * - * This function permits you to define and change the rain droplet size - * which is used if external droplet size control is enabled + * This function permits you to define and change the brightness + * of the precipitation. */ void SGPrecipitation::setIllumination(float illumination) @@ -163,10 +163,9 @@ /** - * @brief Define the rain droplet size + * @brief Define the clip plane distance * - * This function permits you to define and change the rain droplet size - * which is used if external droplet size control is enabled + * This function permits you to define and change the clip plane distance. */ void SGPrecipitation::setClipDistance(float distance) @@ -224,7 +223,7 @@ * Be careful, if snow and rain intensity are greater than '0', snow effect * will be first. * - * The settings come from the osgParticule/PrecipitationEffect.cpp exemple. + * The settings come from the osgParticle/PrecipitationEffect.cpp example. */ bool SGPrecipitation::update(void) { diff -Nru simgear-2017.3.1+dfsg/simgear/environment/precipitation.hxx simgear-2018.1.1+dfsg/simgear/environment/precipitation.hxx --- simgear-2017.3.1+dfsg/simgear/environment/precipitation.hxx 2017-09-20 07:51:07.000000000 +0000 +++ simgear-2018.1.1+dfsg/simgear/environment/precipitation.hxx 2018-04-06 19:43:17.000000000 +0000 @@ -34,7 +34,7 @@ class SGPrecipitation : public osg::Referenced { -private: +protected: bool _freeze; bool _enabled; bool _droplet_external; @@ -66,7 +66,7 @@ void setIllumination(float); void setClipDistance(float); - void setEnabled( bool ); + void setEnabled(bool); bool getEnabled() const; }; diff -Nru simgear-2017.3.1+dfsg/simgear/environment/test_precipitation.cxx simgear-2018.1.1+dfsg/simgear/environment/test_precipitation.cxx --- simgear-2017.3.1+dfsg/simgear/environment/test_precipitation.cxx 1970-01-01 00:00:00.000000000 +0000 +++ simgear-2018.1.1+dfsg/simgear/environment/test_precipitation.cxx 2018-04-06 19:43:17.000000000 +0000 @@ -0,0 +1,239 @@ +/************************************************************************** + * test_precipitation.cxx -- unit-tests for SGPrecipitation class + * + * Copyright (C) 2017 Scott Giese (xDraconian) - + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $Id$ + **************************************************************************/ + +#include +#include + +#include + +#include +#include + +#include "precipitation.hxx" + +using std::cout; +using std::cerr; +using std::endl; + + +class SGPrecipitationTestFixture : public SGPrecipitation +{ +public: + osg::ref_ptr _group; + + void test_configuration() + { + _group = build(); + + SG_VERIFY(getEnabled()); + SG_VERIFY(_precipitationEffect); + SG_CHECK_EQUAL_EP(_rain_intensity, 0.0f); + SG_CHECK_EQUAL_EP(_snow_intensity, 0.0f); + SG_VERIFY(!_freeze); + } + + void test_rain() + { + int count = 500; + + do { + if (--count == 0) { + cerr << "error: method setRainIntensity took too long."; + exit(EXIT_FAILURE); + } + + float saveRainIntensity = _rain_intensity; + + setRainIntensity(0.4f); + update(); + + SG_CHECK_GE(_rain_intensity, saveRainIntensity); + } while (std::fabs(_rain_intensity - 0.4f) > 0.00001f); + + SG_CHECK_EQUAL_EP2(_rain_intensity, 0.4f, 0.00001f); + } + + void test_rain_external() + { + int count = 500; + + do { + if (--count == 0) { + cerr << "error: method setRainIntensity-external took too long."; + exit(EXIT_FAILURE); + } + + float saveRainIntensity = _rain_intensity; + + setRainIntensity(0.25f); + update(); + + SG_CHECK_LE(_rain_intensity, saveRainIntensity); + } while (std::fabs(_rain_intensity - 0.25f) > 0.00001f); + + // call once more to ensure we have intensity precisely set + setRainIntensity(0.25f); + update(); + + SG_CHECK_EQUAL_EP2(_rain_intensity, 0.25f, 0.00001f); + } + + void test_freeze() + { + float saveParticleSize = _precipitationEffect->getParticleSize(); + float saveParticleSpeed = std::fabs(_precipitationEffect->getParticleSpeed()); + + // change rain to snow + // expect particle size to increase + // expect particle speed to decrease + setFreezing(true); + update(); + + SG_CHECK_GT(_precipitationEffect->getParticleSize(), saveParticleSize); + SG_CHECK_LT(std::fabs(_precipitationEffect->getParticleSpeed()), saveParticleSpeed); + } + + void test_snow() + { + int count = 500; + + do { + if (--count == 0) { + cerr << "error: method setSnowIntensity took too long."; + exit(EXIT_FAILURE); + } + + float saveSnowIntensity = _snow_intensity; + + // not a typo - when freezing is enabled, snow intensity is keep in sync with rain intensity + setRainIntensity(0.3f); + update(); + + SG_CHECK_LE(_snow_intensity, saveSnowIntensity); + } while (std::fabs(_snow_intensity - 0.3f) > 0.00001f); + + SG_CHECK_EQUAL_EP2(_snow_intensity, 0.3f, 0.00001f); + } + + void test_snow_external() + { + setRainDropletSize(0.025f); + setSnowFlakeSize(0.04f); + setDropletExternal(true); + + int count = 600; + + do { + if (--count == 0) { + cerr << "error: method setSnowIntensity-external took too long."; + exit(EXIT_FAILURE); + } + + float saveSnowIntensity = _snow_intensity; + + setSnowIntensity(0.55f); + update(); + + SG_CHECK_GE(_snow_intensity, saveSnowIntensity); + } while (std::fabs(_snow_intensity - 0.55f) > 0.00001f); + + // call once more to ensure we have intensity precisely set + setSnowIntensity(0.55f); + update(); + + SG_CHECK_EQUAL_EP2(_snow_intensity, 0.55f, 0.00001f); + } + + void test_unfreeze() + { + float saveParticleSize = _precipitationEffect->getParticleSize(); + float saveParticleSpeed = std::fabs(_precipitationEffect->getParticleSpeed()); + + // change snow to rain + // expect particle size to decrease + // expect particle speed to increase + setFreezing(false); + update(); + + SG_CHECK_LT(_precipitationEffect->getParticleSize(), saveParticleSize); + SG_CHECK_GT(std::fabs(_precipitationEffect->getParticleSpeed()), saveParticleSpeed); + } + + void test_no_precipitation() + { + setEnabled(false); + SG_VERIFY(!getEnabled()); + + update(); + + // intensity drops to zero, so we should see this reflected in the particles + SG_CHECK_EQUAL_EP(_precipitationEffect->getParticleSize(), 0.01f); + SG_CHECK_EQUAL_EP(_precipitationEffect->getParticleSpeed(), -2.0f); + + setEnabled(true); + } + + void test_illumination() + { + setIllumination(0.87f); + SG_CHECK_EQUAL_EP(_illumination, 0.87f); + } + + void test_clipping() + { + setClipDistance(6.5); + SG_CHECK_EQUAL_EP(_clip_distance, 6.5f); + } + + void test_wind() + { + setWindProperty(87.0f, 0.7f); + auto vec = _wind_vec; + SG_CHECK_EQUAL_EP2(vec[0], 0.011166f, 0.00001f); + SG_CHECK_EQUAL_EP2(vec[1], -0.213068f, 0.00001f); + SG_CHECK_EQUAL_EP2(vec[2], 0.0f, 0.00001f); + } +}; + + +int main(int argc, char* argv[]) +{ + auto fixture = std::unique_ptr(new SGPrecipitationTestFixture); + + fixture->test_configuration(); + fixture->test_rain(); + fixture->test_freeze(); + fixture->test_snow(); + fixture->test_unfreeze(); + fixture->test_no_precipitation(); + + fixture->test_snow_external(); + fixture->test_rain_external(); + fixture->test_illumination(); + fixture->test_clipping(); + fixture->test_wind(); + fixture->test_no_precipitation(); + + cout << "all tests passed OK" << endl; + + return EXIT_SUCCESS; +} diff -Nru simgear-2017.3.1+dfsg/simgear/hla/HLAArrayDataType.cxx simgear-2018.1.1+dfsg/simgear/hla/HLAArrayDataType.cxx --- simgear-2017.3.1+dfsg/simgear/hla/HLAArrayDataType.cxx 2017-09-20 07:51:07.000000000 +0000 +++ simgear-2018.1.1+dfsg/simgear/hla/HLAArrayDataType.cxx 2018-04-06 19:43:17.000000000 +0000 @@ -19,6 +19,8 @@ # include #endif +#include + #include #include "HLAArrayDataType.hxx" diff -Nru simgear-2017.3.1+dfsg/simgear/hla/HLADataType.cxx simgear-2018.1.1+dfsg/simgear/hla/HLADataType.cxx --- simgear-2017.3.1+dfsg/simgear/hla/HLADataType.cxx 2017-09-20 07:51:07.000000000 +0000 +++ simgear-2018.1.1+dfsg/simgear/hla/HLADataType.cxx 2018-04-06 19:43:17.000000000 +0000 @@ -19,6 +19,8 @@ # include #endif +#include + #include #include "HLADataType.hxx" diff -Nru simgear-2017.3.1+dfsg/simgear/hla/HLAEnumeratedDataType.cxx simgear-2018.1.1+dfsg/simgear/hla/HLAEnumeratedDataType.cxx --- simgear-2017.3.1+dfsg/simgear/hla/HLAEnumeratedDataType.cxx 2017-09-20 07:51:07.000000000 +0000 +++ simgear-2018.1.1+dfsg/simgear/hla/HLAEnumeratedDataType.cxx 2018-04-06 19:43:17.000000000 +0000 @@ -21,11 +21,11 @@ #include -#include "HLAEnumeratedDataType.hxx" - +#include #include #include #include + #include "HLAEnumeratedDataType.hxx" #include "HLADataTypeVisitor.hxx" diff -Nru simgear-2017.3.1+dfsg/simgear/hla/HLAFixedRecordDataType.cxx simgear-2018.1.1+dfsg/simgear/hla/HLAFixedRecordDataType.cxx --- simgear-2017.3.1+dfsg/simgear/hla/HLAFixedRecordDataType.cxx 2017-09-20 07:51:07.000000000 +0000 +++ simgear-2018.1.1+dfsg/simgear/hla/HLAFixedRecordDataType.cxx 2018-04-06 19:43:17.000000000 +0000 @@ -19,10 +19,11 @@ # include #endif +#include + #include #include "HLAFixedRecordDataType.hxx" - #include "HLADataTypeVisitor.hxx" #include "HLAFixedRecordDataElement.hxx" diff -Nru simgear-2017.3.1+dfsg/simgear/hla/HLAInteractionClass.cxx simgear-2018.1.1+dfsg/simgear/hla/HLAInteractionClass.cxx --- simgear-2017.3.1+dfsg/simgear/hla/HLAInteractionClass.cxx 2017-09-20 07:51:07.000000000 +0000 +++ simgear-2018.1.1+dfsg/simgear/hla/HLAInteractionClass.cxx 2018-04-06 19:43:17.000000000 +0000 @@ -19,12 +19,12 @@ # include #endif -#include - -#include "HLAInteractionClass.hxx" +#include +#include #include +#include "HLAInteractionClass.hxx" #include "HLADataElement.hxx" #include "HLAFederate.hxx" diff -Nru simgear-2017.3.1+dfsg/simgear/hla/HLAObjectClass.cxx simgear-2018.1.1+dfsg/simgear/hla/HLAObjectClass.cxx --- simgear-2017.3.1+dfsg/simgear/hla/HLAObjectClass.cxx 2017-09-20 07:51:07.000000000 +0000 +++ simgear-2018.1.1+dfsg/simgear/hla/HLAObjectClass.cxx 2018-04-06 19:43:17.000000000 +0000 @@ -19,6 +19,8 @@ # include #endif +#include + #include #include "HLAObjectClass.hxx" diff -Nru simgear-2017.3.1+dfsg/simgear/hla/HLAVariantRecordDataType.cxx simgear-2018.1.1+dfsg/simgear/hla/HLAVariantRecordDataType.cxx --- simgear-2017.3.1+dfsg/simgear/hla/HLAVariantRecordDataType.cxx 2017-09-20 07:51:07.000000000 +0000 +++ simgear-2018.1.1+dfsg/simgear/hla/HLAVariantRecordDataType.cxx 2018-04-06 19:43:17.000000000 +0000 @@ -19,10 +19,11 @@ # include #endif +#include + #include #include "HLAVariantRecordDataType.hxx" - #include "HLADataTypeVisitor.hxx" #include "HLAVariantRecordDataElement.hxx" diff -Nru simgear-2017.3.1+dfsg/simgear/io/DNSClient.cxx simgear-2018.1.1+dfsg/simgear/io/DNSClient.cxx --- simgear-2017.3.1+dfsg/simgear/io/DNSClient.cxx 2017-09-20 07:51:07.000000000 +0000 +++ simgear-2018.1.1+dfsg/simgear/io/DNSClient.cxx 2018-04-06 19:43:17.000000000 +0000 @@ -22,9 +22,13 @@ // #include + +#include + #include "DNSClient.hxx" #include -#include +#include + #include namespace simgear { diff -Nru simgear-2017.3.1+dfsg/simgear/io/HTTPRepository.cxx simgear-2018.1.1+dfsg/simgear/io/HTTPRepository.cxx --- simgear-2017.3.1+dfsg/simgear/io/HTTPRepository.cxx 2017-09-20 07:51:07.000000000 +0000 +++ simgear-2018.1.1+dfsg/simgear/io/HTTPRepository.cxx 2018-04-06 19:43:17.000000000 +0000 @@ -142,7 +142,7 @@ void finishedRequest(const RepoRequestPtr& req); HTTPDirectory* getOrCreateDirectory(const std::string& path); - bool deleteDirectory(const std::string& path); + bool deleteDirectory(const std::string& relPath, const SGPath& absPath); typedef std::vector DirectoryVector; DirectoryVector directories; @@ -317,7 +317,8 @@ ChildInfoList::iterator c = findIndexChild(it->file()); if (c == children.end()) { SG_LOG(SG_TERRASYNC, SG_DEBUG, "is orphan '" << it->file() << "'" ); - orphans.push_back(it->file()); + + orphans.push_back(it->file()); } else if (c->hash != hash) { SG_LOG(SG_TERRASYNC, SG_DEBUG, "hash mismatch'" << it->file() ); // file exists, but hash mismatch, schedule update @@ -534,7 +535,7 @@ std::string fpath = _relativePath + "/" + name; if (p.isDir()) { - ok = _repository->deleteDirectory(fpath); + ok = _repository->deleteDirectory(fpath, p); } else { // remove the hash cache entry _repository->updatedFileContents(p, std::string()); @@ -1044,25 +1045,26 @@ return d; } - bool HTTPRepoPrivate::deleteDirectory(const std::string& path) + bool HTTPRepoPrivate::deleteDirectory(const std::string& relPath, const SGPath& absPath) { - DirectoryWithPath p(path); - DirectoryVector::iterator it = std::find_if(directories.begin(), directories.end(), p); + DirectoryWithPath p(relPath); + auto it = std::find_if(directories.begin(), directories.end(), p); if (it != directories.end()) { HTTPDirectory* d = *it; + assert(d->absolutePath() == absPath); directories.erase(it); - Dir dir(d->absolutePath()); - bool result = dir.remove(true); - - // update the hash cache too - updatedFileContents(d->absolutePath(), std::string()); - delete d; + } else { + // we encounter this code path when deleting an orphaned directory + } + + Dir dir(absPath); + bool result = dir.remove(true); - return result; - } + // update the hash cache too + updatedFileContents(absPath, std::string()); - return false; + return result; } void HTTPRepoPrivate::makeRequest(RepoRequestPtr req) diff -Nru simgear-2017.3.1+dfsg/simgear/io/iostreams/zlibstream.cxx simgear-2018.1.1+dfsg/simgear/io/iostreams/zlibstream.cxx --- simgear-2017.3.1+dfsg/simgear/io/iostreams/zlibstream.cxx 2017-09-20 07:51:07.000000000 +0000 +++ simgear-2018.1.1+dfsg/simgear/io/iostreams/zlibstream.cxx 2018-04-06 19:43:17.000000000 +0000 @@ -38,20 +38,17 @@ #include +#include #include #include #include +#include #include -#include using std::string; -using traits = std::char_traits; +using simgear::enumValue; -// Cast an enum value to its underlying type -template -static constexpr typename std::underlying_type::type enumValue(T e) { - return static_cast::type>(e); -} +using traits = std::char_traits; // Private utility function static string zlibErrorMessage(const z_stream& zstream, int errorCode) diff -Nru simgear-2017.3.1+dfsg/simgear/io/sg_netChannel.cxx simgear-2018.1.1+dfsg/simgear/io/sg_netChannel.cxx --- simgear-2017.3.1+dfsg/simgear/io/sg_netChannel.cxx 2017-09-20 07:51:07.000000000 +0000 +++ simgear-2018.1.1+dfsg/simgear/io/sg_netChannel.cxx 2018-04-06 19:43:17.000000000 +0000 @@ -32,10 +32,12 @@ #include #include "sg_netChannel.hxx" +#include #include + #include +#include #include -#include #include @@ -258,18 +260,10 @@ assert(channel); assert(channel->poller == this); channel->poller = NULL; - - // portability: MSVC throws assertion failure when empty - if (channels.empty()) { - return; - } - ChannelList::iterator it = channels.begin(); - for (; it != channels.end(); ++it) { - if (*it == channel) { - channels.erase(it); - return; - } + auto it = std::find(channels.begin(), channels.end(), channel); + if (it != channels.end()) { + channels.erase(it); } } diff -Nru simgear-2017.3.1+dfsg/simgear/io/test_HTTP.hxx simgear-2018.1.1+dfsg/simgear/io/test_HTTP.hxx --- simgear-2017.3.1+dfsg/simgear/io/test_HTTP.hxx 2017-09-20 07:51:07.000000000 +0000 +++ simgear-2018.1.1+dfsg/simgear/io/test_HTTP.hxx 2018-04-06 19:43:17.000000000 +0000 @@ -183,15 +183,6 @@ int requestContentLength; }; -class EraseIfClosed -{ -public: - bool operator()(simgear::NetChannel* chan) const - { - return chan->isClosed(); - } -}; - template class TestServer : public NetChannel { @@ -202,7 +193,6 @@ { Socket::initSockets(); - open(); bind(NULL, 2000); // localhost, any port listen(16); @@ -212,6 +202,7 @@ virtual ~TestServer() { + _poller.removeChannel(this); } virtual bool writable (void) { return false ; } @@ -231,15 +222,16 @@ { _poller.poll(); - typename std::vector::iterator it; - it = std::remove_if(_channels.begin(), _channels.end(), EraseIfClosed()); - - for (typename std::vector::iterator it2 = it; it2 != _channels.end(); ++it2) { - delete *it2; - } + auto it = std::remove_if(_channels.begin(), _channels.end(), [&](T* channel) { + if (channel->isClosed()) { + _poller.removeChannel(channel); + delete channel; + return true; + } + return false; + }); _channels.erase(it, _channels.end()); - } int connectCount() diff -Nru simgear-2017.3.1+dfsg/simgear/io/test_untar.cxx simgear-2018.1.1+dfsg/simgear/io/test_untar.cxx --- simgear-2017.3.1+dfsg/simgear/io/test_untar.cxx 2017-09-20 07:51:07.000000000 +0000 +++ simgear-2018.1.1+dfsg/simgear/io/test_untar.cxx 2018-04-06 19:43:17.000000000 +0000 @@ -33,6 +33,7 @@ SG_VERIFY(TarExtractor::isTarData(buf, bufSize)); + f.close(); } void testPlainTar() @@ -48,12 +49,13 @@ SG_VERIFY(TarExtractor::isTarData(buf, bufSize)); + f.close(); } -int main (int ac, char ** av) +int main(int ac, char ** av) { testTarGz(); testPlainTar(); - return 0; + return 0; } diff -Nru simgear-2017.3.1+dfsg/simgear/io/untar.cxx simgear-2018.1.1+dfsg/simgear/io/untar.cxx --- simgear-2017.3.1+dfsg/simgear/io/untar.cxx 2017-09-20 07:51:07.000000000 +0000 +++ simgear-2018.1.1+dfsg/simgear/io/untar.cxx 2018-04-06 19:43:17.000000000 +0000 @@ -28,6 +28,7 @@ #include +#include #include #include @@ -92,7 +93,8 @@ END_OF_ARCHIVE, ERROR_STATE, ///< states above this are error conditions BAD_ARCHIVE, - BAD_DATA + BAD_DATA, + FILTER_STOPPED } State; SGPath path; @@ -110,10 +112,13 @@ bool haveInitedZLib; bool uncompressedData; // set if reading a plain .tar (not tar.gz) uint8_t* headerPtr; - - TarExtractorPrivate() : + TarExtractor* outer; + bool skipCurrentEntry = false; + + TarExtractorPrivate(TarExtractor* o) : haveInitedZLib(false), - uncompressedData(false) + uncompressedData(false), + outer(o) { } @@ -129,7 +134,10 @@ } if (state == READING_FILE) { - currentFile->close(); + if (currentFile) { + currentFile->close(); + currentFile.reset(); + } size_t pad = currentFileSize % TAR_HEADER_BLOCK_SIZE; if (pad) { bytesRemaining = TAR_HEADER_BLOCK_SIZE - pad; @@ -177,26 +185,36 @@ return; } + skipCurrentEntry = false; std::string tarPath = std::string(header.prefix) + std::string(header.fileName); if (!isSafePath(tarPath)) { - //state = BAD_ARCHIVE; SG_LOG(SG_IO, SG_WARN, "bad tar path:" << tarPath); - //return; + skipCurrentEntry = true; } - SGPath p = path; - p.append(tarPath); - + auto result = outer->filterPath(tarPath); + if (result == TarExtractor::Stop) { + setState(FILTER_STOPPED); + return; + } else if (result == TarExtractor::Skipped) { + skipCurrentEntry = true; + } + + SGPath p = path / tarPath; if (header.typeflag == DIRTYPE) { - Dir dir(p); - dir.create(0755); + if (!skipCurrentEntry) { + Dir dir(p); + dir.create(0755); + } setState(READING_HEADER); } else if ((header.typeflag == REGTYPE) || (header.typeflag == AREGTYPE)) { currentFileSize = ::strtol(header.size, NULL, 8); bytesRemaining = currentFileSize; - currentFile.reset(new SGBinaryFile(p)); - currentFile->open(SG_IO_OUT); + if (!skipCurrentEntry) { + currentFile.reset(new SGBinaryFile(p)); + currentFile->open(SG_IO_OUT); + } setState(READING_FILE); } else { SG_LOG(SG_IO, SG_WARN, "Unsupported tar file type:" << header.typeflag); @@ -212,7 +230,9 @@ size_t curBytes = std::min(bytesRemaining, count); if (state == READING_FILE) { - currentFile->write(bytes, curBytes); + if (currentFile) { + currentFile->write(bytes, curBytes); + } bytesRemaining -= curBytes; } else if ((state == READING_HEADER) || (state == PRE_END_OF_ARCHVE) || (state == END_OF_ARCHIVE)) { memcpy(headerPtr, bytes, curBytes); @@ -264,7 +284,7 @@ }; TarExtractor::TarExtractor(const SGPath& rootPath) : - d(new TarExtractorPrivate) + d(new TarExtractorPrivate(this)) { d->path = rootPath; @@ -383,22 +403,26 @@ z.avail_in = count; if (inflateInit2(&z, ZLIB_INFLATE_WINDOW_BITS | ZLIB_DECODE_GZIP_HEADER) != Z_OK) { + inflateEnd(&z); return false; } int result = inflate(&z, Z_SYNC_FLUSH); if (result != Z_OK) { SG_LOG(SG_IO, SG_WARN, "inflate failed:" << result); + inflateEnd(&z); return false; // not tar data } size_t written = 4096 - z.avail_out; if (written < TAR_HEADER_BLOCK_SIZE) { SG_LOG(SG_IO, SG_WARN, "insufficient data for header"); + inflateEnd(&z); return false; } - + header = reinterpret_cast(zlibOutput); + inflateEnd(&z); } else { // uncompressed tar if (count < TAR_HEADER_BLOCK_SIZE) { @@ -417,4 +441,11 @@ return true; } +auto TarExtractor::filterPath(std::string& pathToExtract) + -> PathResult +{ + SG_UNUSED(pathToExtract); + return Accepted; +} + } // of simgear diff -Nru simgear-2017.3.1+dfsg/simgear/io/untar.hxx simgear-2018.1.1+dfsg/simgear/io/untar.hxx --- simgear-2017.3.1+dfsg/simgear/io/untar.hxx 2017-09-20 07:51:07.000000000 +0000 +++ simgear-2018.1.1+dfsg/simgear/io/untar.hxx 2018-04-06 19:43:17.000000000 +0000 @@ -43,7 +43,17 @@ bool hasError() const; +protected: + enum PathResult { + Accepted, + Skipped, + Modified, + Stop + }; + + virtual PathResult filterPath(std::string& pathToExtract); private: + friend class TarExtractorPrivate; std::unique_ptr d; }; diff -Nru simgear-2017.3.1+dfsg/simgear/math/SGMisc.hxx simgear-2018.1.1+dfsg/simgear/math/SGMisc.hxx --- simgear-2017.3.1+dfsg/simgear/math/SGMisc.hxx 2017-09-20 07:51:07.000000000 +0000 +++ simgear-2018.1.1+dfsg/simgear/math/SGMisc.hxx 2018-04-06 19:43:17.000000000 +0000 @@ -155,6 +155,12 @@ { return std::isnan(v); } + + static bool eq(const T& a, const T& b, const T& epsilon = SGLimits::epsilon()) + { return std::abs(a - b) < epsilon; } + + static bool neq(const T& a, const T& b, const T& epsilon = SGLimits::epsilon()) + { return !eq(a, b, epsilon); } }; #endif diff -Nru simgear-2017.3.1+dfsg/simgear/misc/path_test.cxx simgear-2018.1.1+dfsg/simgear/misc/path_test.cxx --- simgear-2017.3.1+dfsg/simgear/misc/path_test.cxx 2017-09-20 07:51:07.000000000 +0000 +++ simgear-2018.1.1+dfsg/simgear/misc/path_test.cxx 2018-04-06 19:43:17.000000000 +0000 @@ -2,7 +2,11 @@ #include +#include +#include #include +#include + #include #include @@ -286,6 +290,49 @@ SG_CHECK_EQUAL(fileInRW.canWrite(), false); } +void test_comparisons() +{ + std::cout << "Testing comparisons\n"; + + SG_CHECK_EQUAL(SGPath("/abc/def ghi"), SGPath("/abc/def ghi")); + SG_CHECK_NE(SGPath("/abc"), SGPath("abc")); + SG_CHECK_LT(SGPath(""), SGPath("/")); + SG_CHECK_LT(SGPath("A"), SGPath("a")); + SG_CHECK_LE(SGPath(""), SGPath("/")); + SG_CHECK_LE(SGPath("/"), SGPath("/")); + SG_CHECK_GT(SGPath("a"), SGPath("A")); + SG_CHECK_GE(SGPath("a"), SGPath("A")); + SG_CHECK_GE(SGPath("a"), SGPath("a")); + + std::vector origVector({ + std::string("/zer/gh/tr aze"), + std::string("/abc/def/ttt"), + std::string("/abc/def/ddd"), + std::string("/a"), + std::string("")}); + std::vector sortedVector({ + std::string(""), + std::string("/a"), + std::string("/abc/def/ddd"), + std::string("/abc/def/ttt"), + std::string("/zer/gh/tr aze")}); + + std::sort(origVector.begin(), origVector.end()); + SG_CHECK_EQUAL_NOSTREAM(origVector, sortedVector); +} + +void test_hash_function() +{ + std::cout << "Testing the std::hash specialization\n"; + + const SGPath nullPath{}; + const SGPath p{"/abc/def"}; + + SG_CHECK_EQUAL(std::hash{}(nullPath), std::hash{}(nullPath)); + SG_CHECK_EQUAL(std::hash{}(p), std::hash{}(p)); + SG_CHECK_NE(std::hash{}(p), std::hash{}(p / "foobar")); +} + int main(int argc, char* argv[]) { SGPath pa; @@ -389,12 +436,11 @@ SG_CHECK_EQUAL(pp.canWrite(), false); test_dir(); - - test_path_dir(); - + test_path_dir(); test_permissions(); - - test_update_dir(); + test_update_dir(); + test_comparisons(); + test_hash_function(); cout << "all tests passed OK" << endl; return 0; // passed diff -Nru simgear-2017.3.1+dfsg/simgear/misc/sg_dir.cxx simgear-2018.1.1+dfsg/simgear/misc/sg_dir.cxx --- simgear-2017.3.1+dfsg/simgear/misc/sg_dir.cxx 2017-09-20 07:51:07.000000000 +0000 +++ simgear-2018.1.1+dfsg/simgear/misc/sg_dir.cxx 2018-04-06 19:43:17.000000000 +0000 @@ -296,7 +296,10 @@ int n = 0; dirent* d; - while( (d = readdir(dp)) !=NULL && (n < 4) ) n++; + while (n < 3 && (d = readdir(dp)) != nullptr) { + n++; + } + closedir(dp); return (n == 2); // '.' and '..' always exist diff -Nru simgear-2017.3.1+dfsg/simgear/misc/sg_dir_test.cxx simgear-2018.1.1+dfsg/simgear/misc/sg_dir_test.cxx --- simgear-2017.3.1+dfsg/simgear/misc/sg_dir_test.cxx 2017-09-20 07:51:07.000000000 +0000 +++ simgear-2018.1.1+dfsg/simgear/misc/sg_dir_test.cxx 2018-04-06 19:43:17.000000000 +0000 @@ -2,6 +2,7 @@ #include +#include #include #include #include "sg_dir.hxx" @@ -34,11 +35,35 @@ d.remove(); } +void test_isEmpty() +{ + simgear::Dir d = simgear::Dir::tempDir("FlightGear"); + SG_VERIFY(!d.isNull() && d.exists() && d.isEmpty()); + SGPath f = d.file("some file"); + + { sg_ofstream file(f); } // create and close the file + SG_VERIFY(!d.isEmpty()); + + f.remove(); + SG_VERIFY(d.isEmpty()); + + simgear::Dir subDir{d.file("some subdir")}; + subDir.create(0777); + SG_VERIFY(!d.isEmpty()); + + subDir.remove(); + SG_VERIFY(d.isEmpty()); + + d.remove(); + SG_VERIFY(d.isEmpty()); // eek, but that's how it is +} + int main(int argc, char **argv) { test_isNull(); test_setRemoveOnDestroy(); test_tempDir(); + test_isEmpty(); return EXIT_SUCCESS; } diff -Nru simgear-2017.3.1+dfsg/simgear/misc/sg_path.cxx simgear-2018.1.1+dfsg/simgear/misc/sg_path.cxx --- simgear-2017.3.1+dfsg/simgear/misc/sg_path.cxx 2017-09-20 07:51:07.000000000 +0000 +++ simgear-2018.1.1+dfsg/simgear/misc/sg_path.cxx 2018-04-06 19:43:17.000000000 +0000 @@ -35,9 +35,16 @@ #include #include -#ifdef _WIN32 +#if !defined(SG_WINDOWS) +# include +# include +#endif + +#if defined(SG_WINDOWS) # include +# include #endif + #include "sg_path.hxx" #include @@ -52,13 +59,13 @@ static const char sgDirPathSep = '/'; static const char sgDirPathSepBad = '\\'; -#ifdef _WIN32 +#if defined(SG_WINDOWS) const char SGPath::pathListSep[] = ";"; // this is null-terminated #else const char SGPath::pathListSep[] = ":"; // ditto #endif -#ifdef _WIN32 +#if defined(SG_WINDOWS) #include // for CSIDL // TODO: replace this include file with the official header // included in the Windows 8.1 SDK @@ -240,44 +247,6 @@ return SGPath(bytes, p); } - -SGPath::SGPath(const SGPath& p) : - path(p.path), - _permission_checker(p._permission_checker), - _cached(p._cached), - _rwCached(p._rwCached), - _cacheEnabled(p._cacheEnabled), - _canRead(p._canRead), - _canWrite(p._canWrite), - _exists(p._exists), - _isDir(p._isDir), - _isFile(p._isFile), - _modTime(p._modTime), - _size(p._size) -{ -} - -SGPath& SGPath::operator=(const SGPath& p) -{ - path = p.path; - _permission_checker = p._permission_checker, - _cached = p._cached; - _rwCached = p._rwCached; - _cacheEnabled = p._cacheEnabled; - _canRead = p._canRead; - _canWrite = p._canWrite; - _exists = p._exists; - _isDir = p._isDir; - _isFile = p._isFile; - _modTime = p._modTime; - _size = p._size; - return *this; -} - -// destructor -SGPath::~SGPath() { -} - // set path void SGPath::set( const string& p ) { path = p; @@ -780,6 +749,18 @@ return (path != other.path); } +bool operator<(const SGPath& lhs, const SGPath& rhs) +{ return lhs.path < rhs.path; } + +bool operator>(const SGPath& lhs, const SGPath& rhs) +{ return operator<(rhs, lhs); } + +bool operator<=(const SGPath& lhs, const SGPath& rhs) +{ return !operator>(lhs, rhs); } + +bool operator>=(const SGPath& lhs, const SGPath& rhs) +{ return !operator<(lhs, rhs); } + //------------------------------------------------------------------------------ bool SGPath::rename(const SGPath& newName) { @@ -1074,3 +1055,40 @@ return {}; } } + +//------------------------------------------------------------------------------ +bool SGPath::touch() +{ + if (!permissionsAllowsWrite()) + { + SG_LOG(SG_IO, SG_WARN, "file touch failed: (" << *this << ")" + " reason: access denied" ); + return false; + } + + if (!exists()) { + SG_LOG(SG_IO, SG_WARN, "file touch failed: (" << *this << ")" + " reason: missing file"); + return false; + } +#if defined(SG_WINDOWS) + auto ws = wstr(); + // set this link for docs on behaviour here, about passing nullptr + // https://msdn.microsoft.com/en-us/library/aa273399(v=vs.60).aspx + if (_wutime(ws.c_str(), nullptr) != 0) { + SG_LOG(SG_IO, SG_WARN, "file touch failed: (" << *this << ")" + " reason: _wutime failed with error:" << simgear::strutils::error_string(errno)); + return false; + } +#else + if (::utime(path.c_str(), nullptr) != 0) { + SG_LOG(SG_IO, SG_WARN, "file touch failed: (" << *this << ")" + " reason: utime failed with error:" << simgear::strutils::error_string(errno)); + return false; + } +#endif + + // reset the cache flag so we re-stat() on next request + _cached = false; + return true; +} diff -Nru simgear-2017.3.1+dfsg/simgear/misc/sg_path.hxx simgear-2018.1.1+dfsg/simgear/misc/sg_path.hxx --- simgear-2017.3.1+dfsg/simgear/misc/sg_path.hxx 2017-09-20 07:51:07.000000000 +0000 +++ simgear-2018.1.1+dfsg/simgear/misc/sg_path.hxx 2018-04-06 19:43:17.000000000 +0000 @@ -28,12 +28,14 @@ #ifndef _SG_PATH_HXX #define _SG_PATH_HXX -#include - -#include +#include #include + +#include #include +#include +#include #include #ifdef _MSC_VER @@ -65,11 +67,6 @@ /** Default constructor */ explicit SGPath(PermissionChecker validator = NULL); - /** Copy contructor */ - SGPath(const SGPath& p); - - SGPath& operator=(const SGPath& p); - /** * Construct a path based on the starting path provided. * @param p initial path @@ -87,9 +84,6 @@ const std::string& r, PermissionChecker validator = NULL ); - /** Destructor */ - ~SGPath(); - /** * Set path to a new value * @param p new path @@ -99,6 +93,8 @@ bool operator==(const SGPath& other) const; bool operator!=(const SGPath& other) const; + // Other comparison operators are declared below + friend bool operator<(const SGPath& lhs, const SGPath& rhs); void setPermissionChecker(PermissionChecker validator); PermissionChecker getPermissionChecker() const; @@ -190,8 +186,8 @@ * Get the path string * @return path string */ - std::string str() const { return path; } - std::string utf8Str() const { return path; } + std::string str() const noexcept { return path; } + std::string utf8Str() const noexcept { return path; } std::string local8BitStr() const; @@ -286,6 +282,13 @@ */ std::string fileUrl() const; + /** + * Update the file modification timestamp to be 'now'. The contents will + * not be changed. (Same as POSIX 'touch' command). Will fail if the file + * does not exist or permissions do not allow writing. + */ + bool touch(); + enum StandardLocation { HOME, @@ -357,6 +360,24 @@ mutable size_t _size; }; +// Other comparison operators are in the class definition block +bool operator> (const SGPath& lhs, const SGPath& rhs); +bool operator<=(const SGPath& lhs, const SGPath& rhs); +bool operator>=(const SGPath& lhs, const SGPath& rhs); + +// Hash function for SGPath +namespace std +{ +template<> +struct hash +{ + std::size_t operator()(const SGPath& path) const noexcept + { + return std::hash{}(path.utf8Str()); + } +}; +} // of namespace std + /// Output to an ostream template inline diff -Nru simgear-2017.3.1+dfsg/simgear/misc/strutils.cxx simgear-2018.1.1+dfsg/simgear/misc/strutils.cxx --- simgear-2017.3.1+dfsg/simgear/misc/strutils.cxx 2017-09-20 07:51:07.000000000 +0000 +++ simgear-2018.1.1+dfsg/simgear/misc/strutils.cxx 2018-04-06 19:43:17.000000000 +0000 @@ -29,10 +29,7 @@ #include // strerror_r() and strerror_s() #include #include - -#if defined(HAVE_CPP11_CODECVT) - #include // new in C++11 -#endif +#include #include "strutils.hxx" @@ -44,6 +41,8 @@ #if defined(SG_WINDOWS) #include + #include + #include #endif using std::string; @@ -538,6 +537,33 @@ unsigned long long readNonNegativeInt( const std::string& s); #endif + + // parse a time string ([+/-]%f[:%f[:%f]]) into hours + double readTime(const string& time_in) + { + if (time_in.empty()) { + return 0.0; + } + + const bool negativeSign = time_in.front() == '-'; + const string_list pieces = split(time_in, ":"); + if (pieces.size() > 3) { + throw sg_format_exception("Unable to parse time string, too many pieces", time_in); + } + + const int hours = std::abs(to_int(pieces.front())); + int minutes = 0, seconds = 0; + if (pieces.size() > 1) { + minutes = to_int(pieces.at(1)); + if (pieces.size() > 2) { + seconds = to_int(pieces.at(2)); + } + } + + double result = hours + (minutes / 60.0) + (seconds / 3600.0); + return negativeSign ? -result : result; + } + int compare_versions(const string& v1, const string& v2, int maxComponents) { @@ -629,25 +655,85 @@ std::wstring convertUtf8ToWString(const std::string& a) { -#ifdef SG_WINDOWS - return convertMultiByteToWString(CP_UTF8, a); -#elif defined(HAVE_CPP11_CODECVT) +#if defined(SG_WINDOWS) std::wstring_convert, wchar_t> ucs2conv; return ucs2conv.from_bytes(a); #else - return std::wstring(); + assert(sizeof(wchar_t) == 4); + std::wstring result; + int expectedContinuationCount = 0; + wchar_t wc = 0; + + for (uint8_t utf8CodePoint : a) { + // ASCII 7-bit range + if (utf8CodePoint <= 0x7f) { + if (expectedContinuationCount != 0) { + throw sg_format_exception(); + } + + result.push_back(static_cast(utf8CodePoint)); + } else if (expectedContinuationCount > 0) { + if ((utf8CodePoint & 0xC0) != 0x80) { + throw sg_format_exception(); + } + + wc = (wc << 6) | (utf8CodePoint & 0x3F); + if (--expectedContinuationCount == 0) { + result.push_back(wc); + } + } else { + if ((utf8CodePoint & 0xE0) == 0xC0) { + expectedContinuationCount = 1; + wc = utf8CodePoint & 0x1f; + } else if ((utf8CodePoint & 0xF0) == 0xE0) { + expectedContinuationCount = 2; + wc = utf8CodePoint & 0x0f; + } else if ((utf8CodePoint & 0xF8) == 0xF0) { + expectedContinuationCount = 3; + wc =utf8CodePoint & 0x07; + } else { + // illegal UTF-8 encoding + throw sg_format_exception(); + } + } + } // of UTF-8 code point iteration + + return result; + #endif + } std::string convertWStringToUtf8(const std::wstring& w) { -#ifdef SG_WINDOWS - return convertWStringToMultiByte(CP_UTF8, w); -#elif defined(HAVE_CPP11_CODECVT) +#if defined(SG_WINDOWS) std::wstring_convert, wchar_t> ucs2conv; return ucs2conv.to_bytes(w); #else - return std::string(); + assert(sizeof(wchar_t) == 4); + std::string result; + + for (wchar_t cp : w) { + if (cp <= 0x7f) { + result.push_back(static_cast(cp)); + } else if (cp <= 0x07ff) { + result.push_back(0xC0 | ((cp >> 6) & 0x1f)); + result.push_back(0x80 | (cp & 0x3f)); + } else if (cp <= 0xffff) { + result.push_back(0xE0 | ((cp >> 12) & 0x0f)); + result.push_back(0x80 | ((cp >> 6) & 0x3f)); + result.push_back(0x80 | (cp & 0x3f)); + } else if (cp < 0x10ffff) { + result.push_back(0xF0 | ((cp >> 18) & 0x07)); + result.push_back(0x80 | ((cp >> 12) & 0x3f)); + result.push_back(0x80 | ((cp >> 6) & 0x3f)); + result.push_back(0x80 | (cp & 0x3f)); + } else { + throw sg_format_exception(); + } + } + + return result; #endif } diff -Nru simgear-2017.3.1+dfsg/simgear/misc/strutils.hxx simgear-2018.1.1+dfsg/simgear/misc/strutils.hxx --- simgear-2017.3.1+dfsg/simgear/misc/strutils.hxx 2017-09-20 07:51:07.000000000 +0000 +++ simgear-2018.1.1+dfsg/simgear/misc/strutils.hxx 2018-04-06 19:43:17.000000000 +0000 @@ -209,6 +209,16 @@ typename = typename std::enable_if::value, T>::type > T readNonNegativeInt(const std::string& s); + + /** + * Read a time value, seperated by colons, as a value in hours. + * Allowable input is ([+/-]%f[:%f[:%f]]) + * i.e 15:04:35 is parsed as 15 + (04 / 60) + (35 / 2600) + * This code is moved from flightgear's options.cxx where it was called + * parse_time(), + */ + double readTime(const std::string& s); + /** * Convert a string representing a boolean, to a bool. * Accepted values include YES, true, 0, 1, false, no, True, diff -Nru simgear-2017.3.1+dfsg/simgear/misc/strutils_test.cxx simgear-2018.1.1+dfsg/simgear/misc/strutils_test.cxx --- simgear-2017.3.1+dfsg/simgear/misc/strutils_test.cxx 2017-09-20 07:51:07.000000000 +0000 +++ simgear-2018.1.1+dfsg/simgear/misc/strutils_test.cxx 2018-04-06 19:43:17.000000000 +0000 @@ -19,6 +19,7 @@ #include #include #include +#include using std::string; using std::vector; @@ -581,6 +582,43 @@ SG_CHECK_GT(strutils::error_string(saved_errno).size(), 0); } +void test_readTime() +{ + SG_CHECK_EQUAL_EP(strutils::readTime(""), 0.0); + + SG_CHECK_EQUAL_EP(strutils::readTime("11"), 11.0); + SG_CHECK_EQUAL_EP(strutils::readTime("+11"), 11.0); + SG_CHECK_EQUAL_EP(strutils::readTime("-11"), -11.0); + + SG_CHECK_EQUAL_EP(strutils::readTime("11:30"), 11.5); + SG_CHECK_EQUAL_EP(strutils::readTime("+11:15"), 11.25); + SG_CHECK_EQUAL_EP(strutils::readTime("-11:45"), -11.75); + + const double seconds = 1 / 3600.0; + SG_CHECK_EQUAL_EP(strutils::readTime("11:30:00"), 11.5); + SG_CHECK_EQUAL_EP(strutils::readTime("+11:15:05"), 11.25 + 5 * seconds); + SG_CHECK_EQUAL_EP(strutils::readTime("-11:45:15"), -(11.75 + 15 * seconds)); + + SG_CHECK_EQUAL_EP(strutils::readTime("0:0:0"), 0); + + SG_CHECK_EQUAL_EP(strutils::readTime("0:0:28"), 28 * seconds); + SG_CHECK_EQUAL_EP(strutils::readTime("-0:0:28"), -28 * seconds); +} + +void test_utf8Convert() +{ + // F, smiley emoticon, Maths summation symbol, section sign + std::wstring a(L"\u0046\U0001F600\u2211\u00A7"); + + + std::string utf8A = strutils::convertWStringToUtf8(a); + SG_VERIFY(utf8A == std::string("F\xF0\x9F\x98\x80\xE2\x88\x91\xC2\xA7")); + + + std::wstring aRoundTrip = strutils::convertUtf8ToWString(utf8A); + SG_VERIFY(a == aRoundTrip); +} + int main(int argc, char* argv[]) { test_strip(); @@ -599,6 +637,8 @@ test_md5_hex(); test_error_string(); test_propPathMatch(); - + test_readTime(); + test_utf8Convert(); + return EXIT_SUCCESS; } diff -Nru simgear-2017.3.1+dfsg/simgear/nasal/code.c simgear-2018.1.1+dfsg/simgear/nasal/code.c --- simgear-2017.3.1+dfsg/simgear/nasal/code.c 2017-09-20 07:51:07.000000000 +0000 +++ simgear-2018.1.1+dfsg/simgear/nasal/code.c 2018-04-06 19:43:17.000000000 +0000 @@ -787,6 +787,11 @@ naHash_delete(globals->save_hash, naNum(key)); } +int naNumSaved() +{ + return naHash_size(globals->save_hash) + naVec_size(globals->save); +} + void naClearSaved() { naContext c; diff -Nru simgear-2017.3.1+dfsg/simgear/nasal/cppbind/CMakeLists.txt simgear-2018.1.1+dfsg/simgear/nasal/cppbind/CMakeLists.txt --- simgear-2017.3.1+dfsg/simgear/nasal/cppbind/CMakeLists.txt 2017-09-20 07:51:07.000000000 +0000 +++ simgear-2018.1.1+dfsg/simgear/nasal/cppbind/CMakeLists.txt 2018-04-06 19:43:17.000000000 +0000 @@ -1,10 +1,13 @@ include (SimGearComponent) set(HEADERS + cppbind_fwd.hxx Ghost.hxx NasalCallContext.hxx NasalContext.hxx NasalHash.hxx + NasalMe.hxx + NasalMethodHolder.hxx NasalObject.hxx NasalObjectHolder.hxx NasalString.hxx @@ -13,11 +16,8 @@ ) set(DETAIL_HEADERS - detail/from_nasal_function_templates.hxx detail/from_nasal_helper.hxx - detail/functor_templates.hxx detail/nasal_traits.hxx - detail/NasalObject_callMethod_templates.hxx detail/to_nasal_helper.hxx ) @@ -52,4 +52,4 @@ add_boost_test(nasal_num SOURCES test/nasal_num_test.cxx LIBRARIES ${TEST_LIBS} -) \ No newline at end of file +) diff -Nru simgear-2017.3.1+dfsg/simgear/nasal/cppbind/cppbind_fwd.hxx simgear-2018.1.1+dfsg/simgear/nasal/cppbind/cppbind_fwd.hxx --- simgear-2017.3.1+dfsg/simgear/nasal/cppbind/cppbind_fwd.hxx 1970-01-01 00:00:00.000000000 +0000 +++ simgear-2018.1.1+dfsg/simgear/nasal/cppbind/cppbind_fwd.hxx 2018-04-06 19:43:17.000000000 +0000 @@ -0,0 +1,60 @@ +///@file +/// Nasal C++ Bindings forward declarations +/// +// Copyright (C) 2018 Thomas Geymayer +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Library General Public +// License as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later version. +// +// This library 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 +// Library General Public License for more details. +// +// You should have received a copy of the GNU Library General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + +#ifndef SG_NASAL_CPPBIND_FWD_HXX_ +#define SG_NASAL_CPPBIND_FWD_HXX_ + +#include +#include +#include + +namespace nasal +{ + + class bad_nasal_cast; + + class CallContext; + class Context; + class ContextWrapper; + class Hash; + struct Me; + class Object; + class String; + + template + class Ghost; + + template + naRef to_nasal(naContext c, T arg); + + template + naRef to_nasal(naContext c, const T(&array)[N]); + + template + T from_nasal(naContext c, naRef ref); + + template + struct from_nasal_ptr; + + template + T get_member(naContext c, naRef obj, const std::string& name); + +} // namespace nasal + +#endif /* SG_NASAL_CPPBIND_FWD_HXX_ */ diff -Nru simgear-2017.3.1+dfsg/simgear/nasal/cppbind/detail/from_nasal_function_templates.hxx simgear-2018.1.1+dfsg/simgear/nasal/cppbind/detail/from_nasal_function_templates.hxx --- simgear-2017.3.1+dfsg/simgear/nasal/cppbind/detail/from_nasal_function_templates.hxx 2017-09-20 07:51:07.000000000 +0000 +++ simgear-2018.1.1+dfsg/simgear/nasal/cppbind/detail/from_nasal_function_templates.hxx 1970-01-01 00:00:00.000000000 +0000 @@ -1,131 +0,0 @@ -#ifndef SG_FROM_NASAL_HELPER_HXX_ -# error Nasal cppbind - do not include this file! -#endif - -#ifndef SG_DONT_DO_ANYTHING -#define n BOOST_PP_ITERATION() - -#ifndef SG_BOOST_FUNCTION_FROM_NASAL_FWD -# define SG_CALL_TRAITS_PARAM(z, n, dummy)\ - typename boost::call_traits::param_type a##n -# define SG_CALL_ARG(z, n, dummy)\ - to_nasal::param_type>(ctx, a##n) - - template< - class Ret - BOOST_PP_ENUM_TRAILING_PARAMS(n, class A) - > - typename boost::disable_if, Ret>::type - callNasalMethod( const ObjectHolder* holder, - Me self - BOOST_PP_ENUM_TRAILING(n, SG_CALL_TRAITS_PARAM, 0) ) - { - naContext ctx = naNewContext(); -#if n - naRef args[n] = { - BOOST_PP_ENUM(n, SG_CALL_ARG, 0) - }; -#else - naRef* args = NULL; -#endif - - naRef result = - naCallMethodCtx(ctx, holder->get_naRef(), self, n, args, naNil()); - - const char* error = naGetError(ctx); - std::string error_str(error ? error : ""); - - Ret r = Ret(); - if( !error ) - r = from_nasal_helper(ctx, result, static_cast(0)); - - naFreeContext(ctx); - - if( error ) - throw std::runtime_error(error_str); - - return r; - } - - template< - class Ret - BOOST_PP_ENUM_TRAILING_PARAMS(n, class A) - > - typename boost::enable_if, Ret>::type - callNasalMethod( const ObjectHolder* holder, - Me self - BOOST_PP_ENUM_TRAILING(n, SG_CALL_TRAITS_PARAM, 0) ) - { - callNasalMethod< - naRef // do not do any conversion and just ignore the return value - // TODO warn if something different to nil is returned? - BOOST_PP_COMMA_IF(n) - BOOST_PP_ENUM_PARAMS(n, A) - > - ( - holder, - self - BOOST_PP_ENUM_TRAILING_PARAMS(n, a) - ); - } - -# undef SG_CALL_TRAITS_PARAM -# undef SG_CALL_ARG -#endif - - template< - class Ret - BOOST_PP_ENUM_TRAILING_PARAMS(n, class A) - > - typename boost::disable_if< - // free function if first argument is not nasal::Me or no argument at all - boost::is_same, - boost::function - >::type - boostFunctionFromNasal(naRef code, Ret (*)(BOOST_PP_ENUM_PARAMS(n, A))) -#ifdef SG_BOOST_FUNCTION_FROM_NASAL_FWD - ; -#else - { - return boost::bind - ( - &callNasalMethod, - ObjectHolder::makeShared(code), - boost::bind(naNil) - BOOST_PP_COMMA_IF(n) - BOOST_PP_ENUM_SHIFTED_PARAMS(BOOST_PP_INC(n), _) - ); - } -#endif - -#if n > 0 - template< - class Ret - BOOST_PP_ENUM_TRAILING_PARAMS(n, class A) - > - typename boost::enable_if< - // method if type of first argument is nasal::Me - boost::is_same, - boost::function - >::type - boostFunctionFromNasal(naRef code, Ret (*)(BOOST_PP_ENUM_PARAMS(n, A))) -#ifdef SG_BOOST_FUNCTION_FROM_NASAL_FWD - ; -#else - { - return boost::bind - ( - &callNasalMethod< - Ret - BOOST_PP_COMMA_IF(BOOST_PP_DEC(n)) - BOOST_PP_ENUM_SHIFTED_PARAMS(n, A) - >, - ObjectHolder::makeShared(code), - BOOST_PP_ENUM_SHIFTED_PARAMS(BOOST_PP_INC(n), _) - ); - } -#endif -#endif - -#undef n -#endif // SG_DONT_DO_ANYTHING diff -Nru simgear-2017.3.1+dfsg/simgear/nasal/cppbind/detail/from_nasal_helper.cxx simgear-2018.1.1+dfsg/simgear/nasal/cppbind/detail/from_nasal_helper.cxx --- simgear-2017.3.1+dfsg/simgear/nasal/cppbind/detail/from_nasal_helper.cxx 2017-09-20 07:51:07.000000000 +0000 +++ simgear-2018.1.1+dfsg/simgear/nasal/cppbind/detail/from_nasal_helper.cxx 2018-04-06 19:43:17.000000000 +0000 @@ -39,7 +39,7 @@ } //---------------------------------------------------------------------------- - bad_nasal_cast::~bad_nasal_cast() throw() + bad_nasal_cast::~bad_nasal_cast() { } diff -Nru simgear-2017.3.1+dfsg/simgear/nasal/cppbind/detail/from_nasal_helper.hxx simgear-2018.1.1+dfsg/simgear/nasal/cppbind/detail/from_nasal_helper.hxx --- simgear-2017.3.1+dfsg/simgear/nasal/cppbind/detail/from_nasal_helper.hxx 2017-09-20 07:51:07.000000000 +0000 +++ simgear-2018.1.1+dfsg/simgear/nasal/cppbind/detail/from_nasal_helper.hxx 2018-04-06 19:43:17.000000000 +0000 @@ -24,23 +24,17 @@ #include #include -#include +#include +#include +#include #include -#include +#include #include #include -#include -#include #include -#include -#include -#include -#include -#include -#include -#include +#include #include #include @@ -48,8 +42,6 @@ namespace nasal { - class Hash; - class String; /** * Thrown when converting a type from/to Nasal has failed @@ -70,34 +62,26 @@ */ explicit bad_nasal_cast(const std::string& msg); - virtual ~bad_nasal_cast() throw(); + virtual ~bad_nasal_cast(); protected: std::string _msg; }; /** - * Wrap a naRef to indicate it references the self/me object in Nasal method - * calls. - */ - struct Me - { - naRef _ref; - - Me(naRef ref): - _ref(ref) - {} - - operator naRef() { return _ref; } - }; - - /** * Simple pass through for unified handling also of naRef. */ inline naRef from_nasal_helper(naContext, naRef ref, const naRef*) { return ref; } /** + * Ignore return value + */ + // TODO show some warning when something is returned but ignored? + inline void from_nasal_helper(naContext, naRef, const void*) + {} + + /** * Convert Nasal string to std::string */ std::string from_nasal_helper(naContext c, naRef ref, const std::string*); @@ -125,52 +109,40 @@ */ bool from_nasal_helper(naContext c, naRef ref, const bool*); - namespace detail - { -#define SG_BOOST_FUNCTION_FROM_NASAL_FWD -#define BOOST_PP_ITERATION_LIMITS (0, 9) -#define BOOST_PP_FILENAME_1 -#include BOOST_PP_ITERATE() -#undef SG_BOOST_FUNCTION_FROM_NASAL_FWD - } - /** * Convert a Nasal function to a boost::function with the given signature. * * @tparam Sig Signature of returned function (arguments and return value * are automatically converted using from_nasal/to_nasal) */ - template - boost::function - from_nasal_helper(naContext c, naRef ref, boost::function*) + template + boost::function + from_nasal_helper(naContext c, naRef ref, const boost::function*) { if( naIsNil(ref) ) - return boost::function(); + return {}; if( !naIsCode(ref) && !naIsCCode(ref) && !naIsFunc(ref) ) throw bad_nasal_cast("not a function"); - return detail::boostFunctionFromNasal(ref, static_cast(0)); + return NasalMethodHolder(ref); } - template - typename boost::enable_if< boost::is_function, - boost::function - >::type - from_nasal_helper(naContext c, naRef ref, Sig*) + template + boost::function + from_nasal_helper(naContext c, naRef ref, Ret (*const)(Args...)) { - return from_nasal_helper(c, ref, static_cast*>(0)); + return + from_nasal_helper(c, ref, static_cast*>(0)); } /** * Convert a Nasal number to a C++ numeric type */ template - typename boost::enable_if< boost::is_arithmetic, - T - >::type + std::enable_if_t::value, T> from_nasal_helper(naContext c, naRef ref, const T*) { naRef num = naNumValue(ref); @@ -200,16 +172,38 @@ } /** + * Convert a Nasal vector to a std::array + */ + template + std::array + from_nasal_helper(naContext c, naRef ref, const std::array*) + { + if( !naIsVector(ref) ) + throw bad_nasal_cast("Not a vector"); + + if( naVec_size(ref) != N ) + throw bad_nasal_cast( + "Expected vector with " + std::to_string(N) + " elements" + ); + + std::array arr; + + for(std::size_t i = 0; i < N; ++i) + arr[i] = from_nasal_helper(c, naVec_get(ref, i), static_cast(0)); + + return arr; + } + + /** * Convert a Nasal vector of 2 elements to a 2d vector */ template - typename boost::enable_if, Vec2>::type + std::enable_if_t::value, Vec2> from_nasal_helper(naContext c, naRef ref, const Vec2*) { - std::vector vec = - from_nasal_helper(c, ref, static_cast*>(0)); - if( vec.size() != 2 ) - throw bad_nasal_cast("Expected vector with two elements"); + auto vec = + from_nasal_helper(c, ref, static_cast*>(0)); + return Vec2(vec[0], vec[1]); } @@ -219,29 +213,12 @@ template SGRect from_nasal_helper(naContext c, naRef ref, const SGRect*) { - std::vector vec = - from_nasal_helper(c, ref, static_cast*>(0)); - if( vec.size() != 4 ) - throw bad_nasal_cast("Expected vector with four elements"); + auto vec = + from_nasal_helper(c, ref, static_cast*>(0)); return SGRect(vec[0], vec[1], vec[2], vec[3]); } - // Helpers for wrapping calls to Nasal functions into boost::function - namespace detail - { - // Dummy include to add a build dependency on this file for gcc/CMake/etc. -#define SG_DONT_DO_ANYTHING -# include -#undef SG_DONT_DO_ANYTHING - - // Now the actual include (we are limited to 8 arguments (+me) here because - // boost::bind has an upper limit of 9) -#define BOOST_PP_ITERATION_LIMITS (0, 8) -#define BOOST_PP_FILENAME_1 -#include BOOST_PP_ITERATE() - } - } // namespace nasal #endif /* SG_FROM_NASAL_HELPER_HXX_ */ diff -Nru simgear-2017.3.1+dfsg/simgear/nasal/cppbind/detail/functor_templates.hxx simgear-2018.1.1+dfsg/simgear/nasal/cppbind/detail/functor_templates.hxx --- simgear-2017.3.1+dfsg/simgear/nasal/cppbind/detail/functor_templates.hxx 2017-09-20 07:51:07.000000000 +0000 +++ simgear-2018.1.1+dfsg/simgear/nasal/cppbind/detail/functor_templates.hxx 1970-01-01 00:00:00.000000000 +0000 @@ -1,101 +0,0 @@ -#ifndef SG_NASAL_GHOST_HXX_ -# error Nasal cppbind - do not include this file! -#endif - -#ifndef SG_DONT_DO_ANYTHING -#define n BOOST_PP_ITERATION() - -#define SG_GHOST_FUNC_TYPE\ - boost::function - - /** - * Bind any callable entity accepting an instance of raw_type and an arbitrary - * number of arguments as method. - */ - template< - class Ret - BOOST_PP_ENUM_TRAILING_PARAMS(n, class A) - > - Ghost& method(const std::string& name, const SG_GHOST_FUNC_TYPE& func) - { -#if defined(SG_GCC_VERSION) && SG_GCC_VERSION < 40407 - // The old version of g++ used on Jenkins (16.11.2012) only compiles this - // version. -# define SG_GHOST_REQUIRE_ARG(z, n, dummy)\ - boost::bind(&arg_from_nasal, _2, n) -#else - // VS (2008, 2010, ... ?) only allow this version. -# define SG_GHOST_REQUIRE_ARG(z, n, dummy)\ - boost::bind(&Ghost::arg_from_nasal, _2, n) -#endif - - return method - ( - name, - typename boost::function - ( boost::bind( - func, - _1 - BOOST_PP_ENUM_TRAILING(n, SG_GHOST_REQUIRE_ARG, 0) - )) - ); - -#undef SG_GHOST_REQUIRE_ARG - } - -#define SG_GHOST_MEM_FN(cv)\ - /**\ - * Bind a member function with an arbitrary number of arguments as method.\ - */\ - template<\ - class Ret\ - BOOST_PP_ENUM_TRAILING_PARAMS(n, class A)\ - >\ - Ghost& method\ - (\ - const std::string& name,\ - Ret (raw_type::*fn)(BOOST_PP_ENUM_PARAMS(n,A)) cv\ - )\ - {\ - return method<\ - Ret\ - BOOST_PP_ENUM_TRAILING_PARAMS(n,A)\ - >(name, SG_GHOST_FUNC_TYPE(fn));\ - } - -// Work around MSVC warning C4003: not enough actual parameters for macro -// We really do not want to pass a parameter, even if MSVC can not believe it. -#define SG_GHOST_NO_CV - - SG_GHOST_MEM_FN(const) - SG_GHOST_MEM_FN(SG_GHOST_NO_CV) - -#undef SG_GHOST_MEM_FN -#undef SG_GHOST_NO_CV - - /** - * Bind free function accepting an instance of raw_type and an arbitrary - * number of arguments as method. - */ - template< - class Ret, - class Type - BOOST_PP_ENUM_TRAILING_PARAMS(n, class A) - > - Ghost& method - ( - const std::string& name, - Ret (*fn)(Type BOOST_PP_ENUM_TRAILING_PARAMS(n,A)) - ) - { - BOOST_STATIC_ASSERT - (( boost::is_convertible::value - //|| boost::is_convertible::value - // TODO check how to do it with pointer... - )); - return method(name, SG_GHOST_FUNC_TYPE(fn)); - } - -#undef n -#undef SG_GHOST_TYPEDEF_FUNC_TYPE -#endif // SG_DONT_DO_ANYTHING diff -Nru simgear-2017.3.1+dfsg/simgear/nasal/cppbind/detail/NasalObject_callMethod_templates.hxx simgear-2018.1.1+dfsg/simgear/nasal/cppbind/detail/NasalObject_callMethod_templates.hxx --- simgear-2017.3.1+dfsg/simgear/nasal/cppbind/detail/NasalObject_callMethod_templates.hxx 2017-09-20 07:51:07.000000000 +0000 +++ simgear-2018.1.1+dfsg/simgear/nasal/cppbind/detail/NasalObject_callMethod_templates.hxx 1970-01-01 00:00:00.000000000 +0000 @@ -1,35 +0,0 @@ -#ifndef SG_NASAL_OBJECT_HXX_ -# error Nasal cppbind - do not include this file! -#endif - -#ifndef SG_DONT_DO_ANYTHING -#define n BOOST_PP_ITERATION() - -#define SG_CALL_ARG(z, n, dummy)\ - to_nasal::param_type>(ctx, a##n) - - template< - class Ret - BOOST_PP_ENUM_TRAILING_PARAMS(n, class A) - > - Ret callMethod( const std::string& name - BOOST_PP_ENUM_TRAILING_BINARY_PARAMS(n, A, a) ) - { - if( !_nasal_impl.valid() ) - return Ret(); - - typedef boost::function - MemFunc; - - Context ctx; - MemFunc f = get_member(ctx, _nasal_impl.get_naRef(), name.c_str()); - if( f ) - return f(nasal::to_nasal(ctx, this) BOOST_PP_ENUM_TRAILING_PARAMS(n, a)); - - return Ret(); - } - -#undef SG_CALL_ARG - -#undef n -#endif // SG_DONT_DO_ANYTHING diff -Nru simgear-2017.3.1+dfsg/simgear/nasal/cppbind/detail/nasal_traits.hxx simgear-2018.1.1+dfsg/simgear/nasal/cppbind/detail/nasal_traits.hxx --- simgear-2017.3.1+dfsg/simgear/nasal/cppbind/detail/nasal_traits.hxx 2017-09-20 07:51:07.000000000 +0000 +++ simgear-2018.1.1+dfsg/simgear/nasal/cppbind/detail/nasal_traits.hxx 2018-04-06 19:43:17.000000000 +0000 @@ -20,10 +20,7 @@ #ifndef SG_NASAL_TRAITS_HXX_ #define SG_NASAL_TRAITS_HXX_ -#include -#include -#include -#include +#include // Forward declarations class SGReferenced; @@ -56,12 +53,11 @@ namespace nasal { template - struct is_vec2: public boost::integral_constant {}; + struct is_vec2: public std::false_type {}; #define SG_MAKE_TRAIT(templ,type,attr)\ template templ\ - struct attr< type >:\ - public boost::integral_constant {}; + struct attr< type >: public std::true_type {}; SG_MAKE_TRAIT(, SGVec2, is_vec2) SG_MAKE_TRAIT(<>, osg::Vec2b, is_vec2) @@ -75,42 +71,34 @@ struct shared_ptr_traits; template - struct is_strong_ref: - public boost::integral_constant - {}; + struct is_strong_ref: public std::false_type {}; template - struct is_weak_ref: - public boost::integral_constant - {}; + struct is_weak_ref: public std::false_type {}; #define SG_MAKE_SHARED_PTR_TRAIT(strong, weak, intrusive)\ template\ struct shared_ptr_traits >\ {\ - typedef strong strong_ref;\ - typedef weak weak_ref;\ - typedef T element_type;\ - typedef boost::integral_constant is_strong;\ - typedef boost::integral_constant is_intrusive;\ + using strong_ref = strong;\ + using weak_ref = weak;\ + using element_type = T;\ + using is_strong = std::true_type;\ + using is_intrusive = std::bool_constant;\ };\ template\ struct shared_ptr_traits >\ {\ - typedef strong strong_ref;\ - typedef weak weak_ref;\ - typedef T element_type;\ - typedef boost::integral_constant is_strong;\ - typedef boost::integral_constant is_intrusive;\ + using strong_ref = strong;\ + using weak_ref = weak;\ + using element_type = T;\ + using is_strong = std::false_type;\ + using is_intrusive = std::bool_constant;\ };\ template\ - struct is_strong_ref >:\ - public boost::integral_constant\ - {};\ + struct is_strong_ref >: public std::true_type {};\ template\ - struct is_weak_ref >:\ - public boost::integral_constant\ - {}; + struct is_weak_ref >: public std::true_type {}; SG_MAKE_SHARED_PTR_TRAIT(SGSharedPtr, SGWeakPtr, true) SG_MAKE_SHARED_PTR_TRAIT(osg::ref_ptr, osg::observer_ptr, true) @@ -119,25 +107,20 @@ #undef SG_MAKE_SHARED_PTR_TRAIT template - struct supports_weak_ref: - public boost::integral_constant - {}; + struct supports_weak_ref: public std::true_type {}; template struct supports_weak_ref >: - public boost::integral_constant< - bool, - boost::is_base_of::value - > + public std::bool_constant::value> {}; template struct shared_ptr_storage { - typedef T storage_type; - typedef typename T::element_type element_type; - typedef typename shared_ptr_traits::strong_ref strong_ref; - typedef typename shared_ptr_traits::weak_ref weak_ref; + using storage_type = T; + using element_type = typename T::element_type; + using strong_ref = typename shared_ptr_traits::strong_ref; + using weak_ref = typename shared_ptr_traits::weak_ref; template static storage_type* ref(U ptr) @@ -151,39 +134,30 @@ template static - typename boost::enable_if< - boost::is_same, - element_type* - >::type + std::enable_if_t::value, element_type*> get(storage_type* ptr) { return get_pointer(*ptr); } + template static - typename boost::enable_if< - boost::mpl::or_< - boost::is_same, - boost::mpl::and_< - boost::is_same, - supports_weak_ref - > - >, + std::enable_if_t< + std::is_same::value + || (std::is_same::value && supports_weak_ref::value), U - >::type + > get(storage_type* ptr) { return U(*ptr); } + template static - typename boost::enable_if< - boost::mpl::and_< - boost::is_same, - boost::mpl::not_ > - >, + std::enable_if_t< + std::is_same::value && !supports_weak_ref::value, U - >::type + > get(storage_type* ptr) { return U(); @@ -195,46 +169,37 @@ template struct intrusive_ptr_storage { - typedef typename T::element_type storage_type; - typedef typename T::element_type element_type; - typedef typename shared_ptr_traits::strong_ref strong_ref; - typedef typename shared_ptr_traits::weak_ref weak_ref; + using storage_type = typename T::element_type; + using element_type = typename T::element_type; + using strong_ref = typename shared_ptr_traits::strong_ref; + using weak_ref = typename shared_ptr_traits::weak_ref; template static - typename boost::enable_if< - boost::is_same, - element_type* - >::type + std::enable_if_t::value, element_type*> get(storage_type* ptr) { return ptr; } + template static - typename boost::enable_if< - boost::mpl::or_< - boost::is_same, - boost::mpl::and_< - boost::is_same, - supports_weak_ref - > - >, + std::enable_if_t< + std::is_same::value + || (std::is_same::value && supports_weak_ref::value), U - >::type + > get(storage_type* ptr) { return U(ptr); } + template static - typename boost::enable_if< - boost::mpl::and_< - boost::is_same, - boost::mpl::not_ > - >, + std::enable_if_t< + std::is_same::value && !supports_weak_ref::value, U - >::type + > get(storage_type* ptr) { return U(); @@ -246,8 +211,8 @@ struct shared_ptr_storage >: public internal::intrusive_ptr_storage > { - typedef T storage_type; - typedef T element_type; + using storage_type = T; + using element_type = T; static storage_type* ref(element_type* ptr) { @@ -265,8 +230,8 @@ struct shared_ptr_storage >: public internal::intrusive_ptr_storage > { - typedef T storage_type; - typedef T element_type; + using storage_type = T; + using element_type = T; static storage_type* ref(element_type* ptr) diff -Nru simgear-2017.3.1+dfsg/simgear/nasal/cppbind/detail/to_nasal_helper.hxx simgear-2018.1.1+dfsg/simgear/nasal/cppbind/detail/to_nasal_helper.hxx --- simgear-2017.3.1+dfsg/simgear/nasal/cppbind/detail/to_nasal_helper.hxx 2017-09-20 07:51:07.000000000 +0000 +++ simgear-2018.1.1+dfsg/simgear/nasal/cppbind/detail/to_nasal_helper.hxx 2018-04-06 19:43:17.000000000 +0000 @@ -24,13 +24,14 @@ #include #include -#include +#include +#include #include -#include #include -#include +#include +#include #include #include #include @@ -40,8 +41,6 @@ namespace nasal { - class CallContext; - class Hash; typedef boost::function free_function_t; @@ -77,32 +76,54 @@ naRef to_nasal_helper(naContext c, const free_function_t& func); + namespace detail + { + template + naRef array_to_nasal(naContext c, const T* arr, size_t size); + } + /** - * Convert an enum value to the according numeric value + * Convert a fixed size array to a Nasal vector */ - template - typename boost::enable_if< boost::is_enum, naRef >::type - to_nasal_helper(naContext c, T val) + template + naRef to_nasal_helper(naContext c, const T(&array)[N]) { - return naNum(val); + return detail::array_to_nasal(c, array, N); } /** - * Convert a numeric type to Nasal number + * Convert a fixed size C++ array to a Nasal vector + */ + template + naRef to_nasal_helper(naContext c, const std::array& array) + { + return detail::array_to_nasal(c, array.data(), N); + } + + /** + * Convert a std::initializer_list to a Nasal vector */ template - typename boost::enable_if< boost::is_arithmetic, naRef >::type - to_nasal_helper(naContext c, T num) + naRef to_nasal_helper(naContext c, std::initializer_list list) { - return naNum(num); + return detail::array_to_nasal(c, list.begin(), list.size()); } /** - * Convert a 2d vector to Nasal vector with 2 elements + * Convert std::vector to a Nasal vector */ - template - typename boost::enable_if, naRef>::type - to_nasal_helper(naContext c, const Vec2& vec); + template< template class Vector, + class T, + class Alloc + > + std::enable_if_t< + std::is_same, std::vector>::value, + naRef + > + to_nasal_helper(naContext c, const Vector& vec) + { + return detail::array_to_nasal(c, vec.data(), vec.size()); + } /** * Convert a std::map to a Nasal Hash @@ -111,57 +132,63 @@ naRef to_nasal_helper(naContext c, const std::map& map); /** - * Convert a fixed size array to a Nasal vector + * Convert an enum value to the according numeric value */ - template - naRef to_nasal_helper(naContext c, const T(&array)[N]); + template + std::enable_if_t::value, naRef> + to_nasal_helper(naContext c, T val) + { + return naNum(val); + } /** - * Convert std::vector to Nasal vector + * Convert a numeric type to Nasal number */ - template< template class Vector, - class T, - class Alloc - > - typename boost::enable_if< boost::is_same< Vector, - std::vector - >, - naRef - >::type - to_nasal_helper(naContext c, const Vector& vec) + template + std::enable_if_t::value, naRef> + to_nasal_helper(naContext c, T num) { - naRef ret = naNewVector(c); - naVec_setsize(c, ret, static_cast(vec.size())); - for(int i = 0; i < static_cast(vec.size()); ++i) - naVec_set(ret, i, to_nasal_helper(c, vec[i])); - return ret; + return naNum(num); } - //---------------------------------------------------------------------------- + /** + * Convert a 2d vector to Nasal vector with 2 elements + */ template - typename boost::enable_if, naRef>::type + std::enable_if_t::value, naRef> to_nasal_helper(naContext c, const Vec2& vec) { - // We take just double because in Nasal every number is represented as - // double - double nasal_vec[2] = { + return to_nasal_helper(c, { + // We take just double because in Nasal every number is represented as + // double static_cast(vec[0]), static_cast(vec[1]) - }; - return to_nasal_helper(c, nasal_vec); + }); } - //---------------------------------------------------------------------------- + /** + * Convert a SGRect to a Nasal vector with position and size of the rect + */ template naRef to_nasal_helper(naContext c, const SGRect& rect) { - std::vector vec(4); - vec[0] = rect.x(); - vec[1] = rect.y(); - vec[2] = rect.width(); - vec[3] = rect.height(); + return to_nasal_helper(c, { + static_cast(rect.x()), + static_cast(rect.y()), + static_cast(rect.width()), + static_cast(rect.height()) + }); + } - return to_nasal_helper(c, vec); + //---------------------------------------------------------------------------- + template + naRef detail::array_to_nasal(naContext c, const T* arr, size_t size) + { + naRef ret = naNewVector(c); + naVec_setsize(c, ret, static_cast(size)); + for(int i = 0; i < static_cast(size); ++i) + naVec_set(ret, i, to_nasal_helper(c, arr[i])); + return ret; } //---------------------------------------------------------------------------- @@ -184,17 +211,6 @@ return hash; } - //---------------------------------------------------------------------------- - template - naRef to_nasal_helper(naContext c, const T(&array)[N]) - { - naRef ret = naNewVector(c); - naVec_setsize(c, ret, static_cast(N)); - for(int i = 0; i < static_cast(N); ++i) - naVec_set(ret, i, to_nasal_helper(c, array[i])); - return ret; - } - } // namespace nasal #endif /* SG_TO_NASAL_HELPER_HXX_ */ diff -Nru simgear-2017.3.1+dfsg/simgear/nasal/cppbind/from_nasal.hxx simgear-2018.1.1+dfsg/simgear/nasal/cppbind/from_nasal.hxx --- simgear-2017.3.1+dfsg/simgear/nasal/cppbind/from_nasal.hxx 2017-09-20 07:51:07.000000000 +0000 +++ simgear-2018.1.1+dfsg/simgear/nasal/cppbind/from_nasal.hxx 2018-04-06 19:43:17.000000000 +0000 @@ -21,6 +21,7 @@ #define SG_FROM_NASAL_HXX_ #include +#include namespace nasal { @@ -52,8 +53,8 @@ template struct from_nasal_ptr { - typedef typename boost::remove_const - < typename boost::remove_reference::type + typedef typename std::remove_const + < typename std::remove_reference::type >::type return_type; typedef return_type(*type)(naContext, naRef); diff -Nru simgear-2017.3.1+dfsg/simgear/nasal/cppbind/Ghost.hxx simgear-2018.1.1+dfsg/simgear/nasal/cppbind/Ghost.hxx --- simgear-2017.3.1+dfsg/simgear/nasal/cppbind/Ghost.hxx 2017-09-20 07:51:07.000000000 +0000 +++ simgear-2018.1.1+dfsg/simgear/nasal/cppbind/Ghost.hxx 2018-04-06 19:43:17.000000000 +0000 @@ -24,17 +24,16 @@ #include "NasalObjectHolder.hxx" #include +#include +#include #include #include #include #include #include -#include #include -#include #include -#include #include @@ -62,10 +61,7 @@ } template -inline typename boost::enable_if< - boost::is_pointer, - T ->::type +inline std::enable_if_t::value, T> get_pointer(T ptr) { return ptr; @@ -101,8 +97,8 @@ class GhostMetadata { public: - typedef void(*Deleter)(void*); - typedef std::vector > DestroyList; + using Deleter = void(*)(void*); + using DestroyList = std::vector>; static DestroyList _destroy_list; @@ -154,19 +150,6 @@ }; BOOST_MPL_HAS_XXX_TRAIT_DEF(element_type) - - template - struct reduced_type - { - typedef typename boost::remove_cv< - typename boost::remove_reference::type - >::type type; - }; - - template - struct reduced_is_same: - public boost::is_same::type, T2> - {}; } /** @brief Destroy all ghost queued for deletion. @@ -178,8 +161,8 @@ */ void ghostProcessDestroyList(); - typedef SGSharedPtr MethodHolderPtr; - typedef SGWeakPtr MethodHolderWeakPtr; + using MethodHolderPtr = SGSharedPtr; + using MethodHolderWeakPtr = SGWeakPtr; // Dummy template to create shorter and easy to understand compile errors if // trying to wrap the wrong type as a Ghost. @@ -187,9 +170,10 @@ class Ghost { public: - BOOST_STATIC_ASSERT(("Ghost can only wrap shared pointer!" - && is_strong_ref::value - )); + static_assert( + is_strong_ref::value, + "Ghost can only wrap shared pointer!" + ); static Ghost& init(const std::string& name); static bool isInit(); @@ -209,7 +193,7 @@ * int myMember(); * void doSomethingElse(const nasal::CallContext& ctx); * } - * typedef boost::shared_ptr MyClassPtr; + * using MyClassPtr = boost::shared_ptr; * * std::string myOtherFreeMember(int num); * @@ -235,29 +219,25 @@ * @endcode */ template - class Ghost >::type>: + class Ghost::value>>: public internal::GhostMetadata { - // Shared pointer required for Ghost (no weak pointer!) - BOOST_STATIC_ASSERT((is_strong_ref::value)); - public: - typedef typename T::element_type raw_type; - typedef typename shared_ptr_traits::strong_ref strong_ref; - typedef typename shared_ptr_traits::weak_ref weak_ref; - typedef naRef (raw_type::*member_func_t)(const CallContext&); - typedef naRef (*free_func_t)(raw_type&, const CallContext&); - typedef boost::function getter_t; - typedef boost::function setter_t; - typedef boost::function method_t; - typedef boost::function fallback_getter_t; - typedef boost::function fallback_setter_t; + using raw_type = typename T::element_type; + using strong_ref = typename shared_ptr_traits::strong_ref; + using weak_ref = typename shared_ptr_traits::weak_ref; + using member_func_t = naRef (raw_type::*)(const CallContext&); + using free_func_t = naRef (*)(raw_type&, const CallContext&); + using getter_t = boost::function; + using setter_t = boost::function; + using method_t = boost::function; + using fallback_getter_t = + boost::function; + using fallback_setter_t = + boost::function; + + template + using method_variadic_t = boost::function; class MethodHolder: public internal::MethodHolder @@ -269,8 +249,8 @@ protected: - typedef SGSharedPtr SharedPtr; - typedef SGWeakPtr WeakPtr; + using SharedPtr = SGSharedPtr; + using WeakPtr = SGWeakPtr; method_t _method; @@ -369,7 +349,7 @@ MethodHolderPtr func; }; - typedef std::map MemberMap; + using MemberMap = std::map; /** * Register a new ghost type. @@ -406,19 +386,18 @@ * @endcode */ template - typename boost::enable_if - < - boost::is_base_of, - Ghost - >::type& + std::enable_if_t< + std::is_base_of::value, + Ghost& + > bases() { - BOOST_STATIC_ASSERT - (( - boost::is_base_of::value - )); + static_assert( + std::is_base_of::value, + "Not a base class!" + ); - typedef typename BaseGhost::strong_ref base_ref; + using base_ref = typename BaseGhost::strong_ref; BaseGhost* base = BaseGhost::getSingletonPtr(); base->addDerived( @@ -429,17 +408,14 @@ // Replace any getter that is not available in the current class. // TODO check if this is the correct behavior of function overriding - for( typename BaseGhost::MemberMap::const_iterator member = - base->_members.begin(); - member != base->_members.end(); - ++member ) + for(auto const& base_member: base->_members) { - if( _members.find(member->first) == _members.end() ) - _members[member->first] = member_t + if( _members.find(base_member.first) == _members.end() ) + _members[base_member.first] = member_t ( - member->second.getter, - member->second.setter, - member->second.func + base_member.second.getter, + base_member.second.setter, + base_member.second.func ); } @@ -464,17 +440,16 @@ * @endcode */ template - typename boost::disable_if - < - boost::is_base_of, - Ghost - >::type& + std::enable_if_t< + !std::is_base_of::value, + Ghost& + > bases() { - BOOST_STATIC_ASSERT - (( - boost::is_base_of::raw_type, raw_type>::value - )); + static_assert( + std::is_base_of::raw_type, raw_type>::value, + "Not a base class!" + ); return bases< Ghost >(); } @@ -770,7 +745,7 @@ /** * Register anything that accepts an object instance and a - * nasal::CallContext whith automatic conversion of the return type to + * nasal::CallContext with automatic conversion of the return type to * Nasal. * * @code{cpp} @@ -791,14 +766,73 @@ return method(name, boost::bind(method_invoker, func, _1, _2)); } - // Build dependency for CMake, gcc, etc. -#define SG_DONT_DO_ANYTHING -# include -#undef SG_DONT_DO_ANYTHING - -#define BOOST_PP_ITERATION_LIMITS (0, 9) -#define BOOST_PP_FILENAME_1 -#include BOOST_PP_ITERATE() + /** + * Bind any callable entity accepting an instance of raw_type and an + * arbitrary number of arguments as method. + * + * The std::index_sequence specifies the order of the arguments + */ + template + Ghost& method( const std::string& name, + const method_variadic_t& func, + std::index_sequence ) + { + return method + ( + name, + typename boost::function + ( boost::bind( + func, + _1, + boost::bind(&Ghost::arg_from_nasal, _2, Indices)... + )) + ); + } + + template + Ghost& method( const std::string& name, + const method_variadic_t& func ) + { + return method(name, func, std::index_sequence_for{}); + } + + /**\ + * Bind a member function with an arbitrary number of arguments as method. + */ + template + Ghost& method( const std::string& name, + Ret (raw_type::*fn)(Args...) ) + { + return method(name, method_variadic_t(fn)); + } + + template + Ghost& method( const std::string& name, + Ret (raw_type::*fn)(Args...) const ) + { + return method(name, method_variadic_t(fn)); + } + + /** + * Bind free function accepting an instance of raw_type and an arbitrary + * number of arguments as method. + */ + template + Ghost& method + ( + const std::string& name, + Ret (*fn)(Type, Args ... args) + ) + { + static_assert( + std::is_convertible::value, + //|| std::is_convertible::value + // TODO check how to do it with pointer... + "First parameter can not be converted from the Ghost raw_type!" + ); + + return method(name, method_variadic_t(fn)); + } /** * Create a shared pointer on the heap to handle the reference counting @@ -806,11 +840,11 @@ */ template static - typename boost::enable_if_c< - boost::is_same::value - || boost::is_same::value, + std::enable_if_t< + std::is_same::value + || std::is_same::value, naRef - >::type + > makeGhost(naContext c, RefType const& ref_ptr) { strong_ref ref(ref_ptr); @@ -848,10 +882,9 @@ } // otherwise try the derived classes - for( typename DerivedList::reverse_iterator - derived = getSingletonPtr()->_derived_types.rbegin(); - derived != getSingletonPtr()->_derived_types.rend(); - ++derived ) + for( auto derived = getSingletonPtr()->_derived_types.rbegin(); + derived != getSingletonPtr()->_derived_types.rend(); + ++derived ) { strong_ref ref = (derived->from_nasal)(c, me); @@ -865,13 +898,10 @@ if( !naIsVector(na_parents) ) return strong_ref(); - typedef std::vector naRefs; - naRefs parents = from_nasal(c, na_parents); - for( naRefs::const_iterator parent = parents.begin(); - parent != parents.end(); - ++parent ) + auto parents = from_nasal>(c, na_parents); + for(auto parent: parents) { - strong_ref ref = fromNasal(c, *parent); + strong_ref ref = fromNasal(c, parent); if( get_pointer(ref) ) return ref; } @@ -923,8 +953,9 @@ static naGhostType _ghost_type_strong, //!< Stored as shared pointer _ghost_type_weak; //!< Stored as weak shared pointer - typedef naRef (*to_nasal_t)(naContext, const strong_ref&, bool); - typedef strong_ref (*from_nasal_t)(naContext, naRef); + using to_nasal_t = naRef (*)(naContext, const strong_ref&, bool); + using from_nasal_t = strong_ref (*)(naContext, naRef); + struct DerivedInfo { to_nasal_t to_nasal; @@ -937,7 +968,7 @@ {} }; - typedef std::vector DerivedList; + using DerivedList = std::vector; DerivedList _derived_types; static bool isInstance(naGhostType* ghost_type, bool& is_weak) @@ -951,13 +982,10 @@ template static - typename boost::enable_if_c< - !is_weak, - RefPtr - >::type + std::enable_if_t getPtr(void* ptr) { - typedef shared_ptr_storage storage_type; + using storage_type = shared_ptr_storage; if( ptr ) return storage_type::template get( static_cast(ptr) @@ -968,13 +996,13 @@ template static - typename boost::enable_if_c< + std::enable_if_t< is_weak && supports_weak_ref::value, RefPtr - >::type + > getPtr(void* ptr) { - typedef shared_ptr_storage storage_type; + using storage_type = shared_ptr_storage; if( ptr ) return storage_type::template get( static_cast(ptr) @@ -985,10 +1013,10 @@ template static - typename boost::enable_if_c< + std::enable_if_t< is_weak && !supports_weak_ref::value, RefPtr - >::type + > getPtr(void* ptr) { return RefPtr(); @@ -1004,10 +1032,10 @@ template static - typename boost::enable_if - < boost::is_polymorphic, - naRef - >::type + std::enable_if_t< + std::is_polymorphic::value, + naRef + > toNasal( naContext c, const typename BaseGhost::strong_ref& base_ref, bool strong ) @@ -1016,10 +1044,10 @@ // Check first if passed pointer can by converted to instance of class // this ghost wraps. - if( !boost::is_same - < typename BaseGhost::raw_type, - typename Ghost::raw_type - >::value + if( !std::is_same< + typename BaseGhost::raw_type, + typename Ghost::raw_type + >::value && dynamic_cast(ptr) != ptr ) return naNil(); @@ -1038,10 +1066,9 @@ static_pointer_cast(base_ref); // Now check if we can further downcast to one of our derived classes. - for( typename DerivedList::reverse_iterator - derived = getSingletonPtr()->_derived_types.rbegin(); - derived != getSingletonPtr()->_derived_types.rend(); - ++derived ) + for( auto derived = getSingletonPtr()->_derived_types.rbegin(); + derived != getSingletonPtr()->_derived_types.rend(); + ++derived ) { naRef ghost = (derived->to_nasal)(c, ref, strong); @@ -1058,13 +1085,13 @@ template static - typename boost::disable_if - < boost::is_polymorphic, - naRef - >::type - toNasal( naContext c, - const typename BaseGhost::strong_ref& ref, - bool strong ) + std::enable_if_t< + !std::is_polymorphic::value, + naRef + > + toNasal( naContext c, + const typename BaseGhost::strong_ref& ref, + bool strong ) { // For non polymorphic classes there is no possibility to get the actual // dynamic type, therefore we can only use its static type. @@ -1087,7 +1114,7 @@ template getter_t to_getter(Ret (raw_type::*getter)() const) { - typedef typename boost::call_traits::param_type param_type; + using param_type = typename boost::call_traits::param_type; naRef(*to_nasal_)(naContext, param_type) = &to_nasal; // Getter signature: naRef(raw_type&, naContext) @@ -1140,7 +1167,7 @@ */ template static - typename boost::disable_if, naRef>::type + std::enable_if_t::value, naRef> method_invoker ( const boost::function& func, @@ -1148,7 +1175,7 @@ const CallContext& ctx ) { - return (*to_nasal_ptr::get())(ctx.c, func(obj, ctx)); + return (*to_nasal_ptr::get())(ctx.c_ctx(), func(obj, ctx)); }; /** @@ -1156,7 +1183,7 @@ */ template static - typename boost::enable_if, naRef>::type + std::enable_if_t::value, naRef> method_invoker ( const boost::function& func, @@ -1174,10 +1201,10 @@ */ template static - typename boost::disable_if< - internal::reduced_is_same, + std::enable_if_t< + !std::is_same, CallContext>::value, typename from_nasal_ptr::return_type - >::type + > arg_from_nasal(const CallContext& ctx, size_t index) { return ctx.requireArg(index); @@ -1188,19 +1215,21 @@ */ template static - typename boost::enable_if< - internal::reduced_is_same, + std::enable_if_t< + std::is_same, CallContext>::value, typename from_nasal_ptr::return_type - >::type + > arg_from_nasal(const CallContext& ctx, size_t) { // Either const CallContext& or CallContext, non-const reference // does not make sense. - BOOST_STATIC_ASSERT( (!boost::is_same::value) ); + static_assert( + !boost::is_same::value, + "Only const reference and value make sense!"); return ctx; }; - typedef std::unique_ptr GhostPtr; + using GhostPtr = std::unique_ptr; MemberMap _members; fallback_getter_t _fallback_getter; fallback_setter_t _fallback_setter; @@ -1240,13 +1269,10 @@ template static - typename boost::enable_if_c< - !is_weak, - naRef - >::type + std::enable_if_t create(naContext c, const strong_ref& ref_ptr) { - typedef shared_ptr_storage storage_type; + using storage_type = shared_ptr_storage; return naNewGhost2( c, &Ghost::_ghost_type_strong, storage_type::ref(ref_ptr) ); @@ -1254,13 +1280,13 @@ template static - typename boost::enable_if_c< + std::enable_if_t< is_weak && supports_weak_ref::value, naRef - >::type + > create(naContext c, const strong_ref& ref_ptr) { - typedef shared_ptr_storage storage_type; + using storage_type = shared_ptr_storage; return naNewGhost2( c, &Ghost::_ghost_type_weak, storage_type::ref(ref_ptr) ); @@ -1268,10 +1294,10 @@ template static - typename boost::enable_if_c< + std::enable_if_t< is_weak && !supports_weak_ref::value, naRef - >::type + > create(naContext, const strong_ref&) { return naNil(); @@ -1280,7 +1306,7 @@ template static void destroy(void *ptr) { - typedef shared_ptr_storage storage_type; + using storage_type = shared_ptr_storage; storage_type::unref( static_cast(ptr) ); @@ -1322,9 +1348,7 @@ // return ""; // } - typename MemberMap::iterator member = - getSingletonPtr()->_members.find(key_str); - + auto member = getSingletonPtr()->_members.find(key_str); if( member == getSingletonPtr()->_members.end() ) { fallback_getter_t fallback_get = getSingletonPtr()->_fallback_getter; @@ -1374,7 +1398,7 @@ const std::string key = nasal::from_nasal(c, field); const MemberMap& members = getSingletonPtr()->_members; - typename MemberMap::const_iterator member = members.find(key); + auto member = members.find(key); if( member == members.end() ) { fallback_setter_t fallback_set = getSingletonPtr()->_fallback_setter; @@ -1415,13 +1439,11 @@ template naGhostType - Ghost >::type> - ::_ghost_type_strong; + Ghost::value>>::_ghost_type_strong; template naGhostType - Ghost >::type> - ::_ghost_type_weak; + Ghost::value>>::_ghost_type_weak; } // namespace nasal @@ -1430,15 +1452,13 @@ * Convert every shared pointer to a ghost. */ template -typename boost::enable_if< - nasal::internal::has_element_type< - typename nasal::internal::reduced_type::type - >, +std::enable_if_t< + nasal::internal::has_element_type>::value, naRef ->::type +> to_nasal_helper(naContext c, T ptr) { - typedef typename nasal::shared_ptr_traits::strong_ref strong_ref; + using strong_ref = typename nasal::shared_ptr_traits::strong_ref; return nasal::Ghost::makeGhost(c, ptr); } @@ -1446,15 +1466,13 @@ * Convert nasal ghosts/hashes to shared pointer (of a ghost). */ template -typename boost::enable_if< - nasal::internal::has_element_type< - typename nasal::internal::reduced_type::type - >, +std::enable_if_t< + nasal::internal::has_element_type>::value, T ->::type +> from_nasal_helper(naContext c, naRef ref, const T*) { - typedef typename nasal::shared_ptr_traits::strong_ref strong_ref; + using strong_ref = typename nasal::shared_ptr_traits::strong_ref; return T(nasal::Ghost::fromNasalChecked(c, ref)); } @@ -1462,11 +1480,11 @@ * Convert any pointer to a SGReferenced based object to a ghost. */ template -typename boost::enable_if_c< - boost::is_base_of::value - || boost::is_base_of::value, +std::enable_if_t< + std::is_base_of::value + || std::is_base_of::value, naRef ->::type +> to_nasal_helper(naContext c, T* ptr) { return nasal::Ghost >::makeGhost(c, SGSharedPtr(ptr)); @@ -1476,20 +1494,14 @@ * Convert nasal ghosts/hashes to pointer (of a SGReferenced based ghost). */ template -typename boost::enable_if_c< - boost::is_base_of< - SGReferenced, - typename boost::remove_pointer::type - >::value - || boost::is_base_of< - SGWeakReferenced, - typename boost::remove_pointer::type - >::value, +std::enable_if_t< + std::is_base_of>::value + || std::is_base_of>::value, T ->::type +> from_nasal_helper(naContext c, naRef ref, const T*) { - typedef SGSharedPtr::type> TypeRef; + using TypeRef = SGSharedPtr>; return T(nasal::Ghost::fromNasalChecked(c, ref)); } @@ -1497,10 +1509,10 @@ * Convert any pointer to a osg::Referenced based object to a ghost. */ template -typename boost::enable_if< - boost::is_base_of, +std::enable_if_t< + std::is_base_of::value, naRef ->::type +> to_nasal_helper(naContext c, T* ptr) { return nasal::Ghost >::makeGhost(c, osg::ref_ptr(ptr)); @@ -1510,13 +1522,13 @@ * Convert nasal ghosts/hashes to pointer (of a osg::Referenced based ghost). */ template -typename boost::enable_if< - boost::is_base_of::type>, +std::enable_if_t< + std::is_base_of>::value, T ->::type +> from_nasal_helper(naContext c, naRef ref, const T*) { - typedef osg::ref_ptr::type> TypeRef; + using TypeRef = osg::ref_ptr>; return T(nasal::Ghost::fromNasalChecked(c, ref)); } diff -Nru simgear-2017.3.1+dfsg/simgear/nasal/cppbind/NasalCallContext.hxx simgear-2018.1.1+dfsg/simgear/nasal/cppbind/NasalCallContext.hxx --- simgear-2017.3.1+dfsg/simgear/nasal/cppbind/NasalCallContext.hxx 2017-09-20 07:51:07.000000000 +0000 +++ simgear-2018.1.1+dfsg/simgear/nasal/cppbind/NasalCallContext.hxx 2018-04-06 19:43:17.000000000 +0000 @@ -20,8 +20,7 @@ #ifndef SG_NASAL_CALL_CONTEXT_HXX_ #define SG_NASAL_CALL_CONTEXT_HXX_ -#include "from_nasal.hxx" -#include "to_nasal.hxx" +#include "NasalContext.hxx" namespace nasal { @@ -29,11 +28,12 @@ /** * Context passed to a function/method being called from Nasal */ - class CallContext + class CallContext: + public ContextWrapper { public: CallContext(naContext c, naRef me, size_t argc, naRef* args): - c(c), + ContextWrapper(c), me(me), argc(argc), args(args) @@ -102,28 +102,14 @@ requireArg(size_t index) const { if( index >= argc ) - naRuntimeError(c, "Missing required arg #%d", index); + runtimeError("Missing required arg #%d", index); return from_nasal(args[index]); } - template - naRef to_nasal(T arg) const - { - return nasal::to_nasal(c, arg); - } - - template - typename from_nasal_ptr::return_type - from_nasal(naRef ref) const - { - return (*from_nasal_ptr::get())(c, ref); - } - - naContext c; - naRef me; - size_t argc; - naRef *args; + naRef me; + size_t argc; + naRef *args; }; } // namespace nasal diff -Nru simgear-2017.3.1+dfsg/simgear/nasal/cppbind/NasalContext.cxx simgear-2018.1.1+dfsg/simgear/nasal/cppbind/NasalContext.cxx --- simgear-2017.3.1+dfsg/simgear/nasal/cppbind/NasalContext.cxx 2017-09-20 07:51:07.000000000 +0000 +++ simgear-2018.1.1+dfsg/simgear/nasal/cppbind/NasalContext.cxx 2018-04-06 19:43:17.000000000 +0000 @@ -17,33 +17,88 @@ // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA #include "NasalContext.hxx" +#include "NasalHash.hxx" +#include "NasalString.hxx" + +#include namespace nasal { //---------------------------------------------------------------------------- - Context::Context(): - _ctx(naNewContext()) + ContextWrapper::ContextWrapper(naContext ctx): + _ctx(ctx) { - + assert(_ctx); } //---------------------------------------------------------------------------- - Context::~Context() + ContextWrapper::operator naContext() { - naFreeContext(_ctx); + return _ctx; } //---------------------------------------------------------------------------- - Context::operator naContext() + naContext ContextWrapper::c_ctx() const { - return _ctx; + return const_cast(_ctx); } //---------------------------------------------------------------------------- - Hash Context::newHash() + Hash ContextWrapper::newHash() { return Hash(_ctx); } + //---------------------------------------------------------------------------- + String ContextWrapper::newString(const char* str) + { + return String(_ctx, str); + } + + //---------------------------------------------------------------------------- + naRef ContextWrapper::callMethod( Me me, + naRef code, + std::initializer_list args ) + { + naRef ret = naCallMethodCtx( + _ctx, + code, + me, + args.size(), + const_cast(args.begin()), + naNil() // locals + ); + + if( const char* error = naGetError(_ctx) ) + throw std::runtime_error(error); + + return ret; + } + + //---------------------------------------------------------------------------- + naRef ContextWrapper::newVector(std::initializer_list vals) + { + naRef vec = naNewVector(_ctx); + naVec_setsize(_ctx, vec, vals.size()); + int i = 0; + for(naRef val: vals) + naVec_set(vec, i++, val); + return vec; + } + + //---------------------------------------------------------------------------- + Context::Context(): + ContextWrapper(naNewContext()) + { + + } + + //---------------------------------------------------------------------------- + Context::~Context() + { + naFreeContext(_ctx); + _ctx = nullptr; + } + } // namespace nasal diff -Nru simgear-2017.3.1+dfsg/simgear/nasal/cppbind/NasalContext.hxx simgear-2018.1.1+dfsg/simgear/nasal/cppbind/NasalContext.hxx --- simgear-2017.3.1+dfsg/simgear/nasal/cppbind/NasalContext.hxx 2017-09-20 07:51:07.000000000 +0000 +++ simgear-2018.1.1+dfsg/simgear/nasal/cppbind/NasalContext.hxx 2018-04-06 19:43:17.000000000 +0000 @@ -19,28 +19,110 @@ #ifndef SG_NASAL_CONTEXT_HXX_ #define SG_NASAL_CONTEXT_HXX_ -#include "NasalHash.hxx" +#include "cppbind_fwd.hxx" +#include "NasalMe.hxx" + +#include +#include namespace nasal { /** - * Manage lifetime and encapsulate a Nasal context. + * Wraps a nasal ::naContext without taking ownership/managing its lifetime */ - class Context + class ContextWrapper { public: - Context(); - ~Context(); + explicit ContextWrapper(naContext ctx); operator naContext(); + /** Convert to non-const ::naContext for usage with C-APIs */ + naContext c_ctx() const; + Hash newHash(); + String newString(const char* str); + + /** Raise a nasal runtime error */ + template + void runtimeError(const char* fmt, Args ... args) const + { + naRuntimeError(c_ctx(), fmt, args...); + } + + template + naRef to_nasal(T arg) const + { + return nasal::to_nasal(_ctx, arg); + } + + template + naRef to_nasal(const T(&array)[N]) const + { + return nasal::to_nasal(_ctx, array); + } + + /** Create a nasal vector filled with the given values */ + template + naRef to_nasal_vec(Vals ... vals) + { + return newVector({to_nasal(vals)...}); + } + + template + Me to_me(T arg) const + { + return Me{ to_nasal(arg) }; + } + + template + typename from_nasal_ptr::return_type + from_nasal(naRef ref) const + { + return (*from_nasal_ptr::get())(_ctx, ref); + } + + naRef callMethod(Me me, naRef code, std::initializer_list args); + + template + Ret callMethod( Me me, + naRef code, + typename boost::call_traits::param_type ... args ) + { + // TODO warn if with Ret == void something different to nil is returned? + return from_nasal(callMethod( + me, + code, + { to_nasal::param_type>(args)... } + )); + } protected: naContext _ctx; + + // Not exposed to avoid confusion of intializer_list to variadic + // arguments + naRef newVector(std::initializer_list vals); + }; + + /** + * Creates and manages the lifetime of a ::naContext + */ + class Context: + public ContextWrapper + { + public: + Context(); + ~Context(); + + Context(const Context&) = delete; + Context& operator=(const Context&) = delete; }; } // namespace nasal +#include "from_nasal.hxx" +#include "to_nasal.hxx" + #endif /* SG_NASAL_CONTEXT_HXX_ */ diff -Nru simgear-2017.3.1+dfsg/simgear/nasal/cppbind/NasalMe.hxx simgear-2018.1.1+dfsg/simgear/nasal/cppbind/NasalMe.hxx --- simgear-2017.3.1+dfsg/simgear/nasal/cppbind/NasalMe.hxx 1970-01-01 00:00:00.000000000 +0000 +++ simgear-2018.1.1+dfsg/simgear/nasal/cppbind/NasalMe.hxx 2018-04-06 19:43:17.000000000 +0000 @@ -0,0 +1,44 @@ +///@file +// +// Copyright (C) 2018 Thomas Geymayer +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Library General Public +// License as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later version. +// +// This library 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 +// Library General Public License for more details. +// +// You should have received a copy of the GNU Library General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + +#ifndef SG_NASAL_ME_HXX_ +#define SG_NASAL_ME_HXX_ + +#include + +namespace nasal +{ + + /** + * Wrap a naRef to indicate it references the self/me object in Nasal method + * calls. + */ + struct Me + { + naRef _ref; + + explicit Me(naRef ref = naNil()): + _ref(ref) + {} + + operator naRef() { return _ref; } + }; + +} // namespace nasal + +#endif /* SG_NASAL_ME_HXX_ */ diff -Nru simgear-2017.3.1+dfsg/simgear/nasal/cppbind/NasalMethodHolder.hxx simgear-2018.1.1+dfsg/simgear/nasal/cppbind/NasalMethodHolder.hxx --- simgear-2017.3.1+dfsg/simgear/nasal/cppbind/NasalMethodHolder.hxx 1970-01-01 00:00:00.000000000 +0000 +++ simgear-2018.1.1+dfsg/simgear/nasal/cppbind/NasalMethodHolder.hxx 2018-04-06 19:43:17.000000000 +0000 @@ -0,0 +1,75 @@ +///@file +// +// Copyright (C) 2018 Thomas Geymayer +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Library General Public +// License as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later version. +// +// This library 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 +// Library General Public License for more details. +// +// You should have received a copy of the GNU Library General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + +#ifndef SG_NASAL_METHOD_HOLDER_HXX_ +#define SG_NASAL_METHOD_HOLDER_HXX_ + +#include "NasalContext.hxx" +#include "NasalObjectHolder.hxx" + +namespace nasal +{ + + /** + * Hold any callable function in Nasal and call it from C++ + */ + template + class NasalMethodHolder + { + using Holder = ObjectHolder; + + public: + NasalMethodHolder(naRef code): + _code(Holder::makeShared(code)) + {} + + /** + * @brief Call the function with the given arguments + * + * If the first argument is nasal::Me it will be passed as 'me' object and + * not as argument. + */ + Ret operator()(Args ... args) + { + return call(args...); + } + + private: + Holder::Ref _code; + + template + Ret call(Me self, CArgs ... args) + { + nasal::Context ctx; + return ctx.callMethod( + self, + _code->get_naRef(), + args... + ); + } + + template + Ret call(CArgs ... args) + { + return call(Me{}, args...); + } + }; + +} // namespace nasal + +#endif /* SG_NASAL_METHOD_HOLDER_HXX_ */ diff -Nru simgear-2017.3.1+dfsg/simgear/nasal/cppbind/NasalObjectHolder.hxx simgear-2018.1.1+dfsg/simgear/nasal/cppbind/NasalObjectHolder.hxx --- simgear-2017.3.1+dfsg/simgear/nasal/cppbind/NasalObjectHolder.hxx 2017-09-20 07:51:07.000000000 +0000 +++ simgear-2018.1.1+dfsg/simgear/nasal/cppbind/NasalObjectHolder.hxx 2018-04-06 19:43:17.000000000 +0000 @@ -42,6 +42,8 @@ { public: + using Ref = SGSharedPtr>; + /** * @param obj Object to save */ @@ -84,7 +86,7 @@ * * @param obj Object to save */ - static SGSharedPtr > makeShared(naRef obj); + static Ref makeShared(naRef obj); protected: naRef _ref; @@ -139,10 +141,9 @@ template ObjectHolder::ObjectHolder(naRef obj): _ref(obj), - _gc_key(0) + _gc_key(naIsNil(obj) ? 0 : naGCSave(obj)) { - if( !naIsNil(obj) ) - naGCSave(obj); + } //---------------------------------------------------------------------------- @@ -156,10 +157,10 @@ //---------------------------------------------------------------------------- template - SGSharedPtr > + typename ObjectHolder::Ref ObjectHolder::makeShared(naRef obj) { - return SGSharedPtr >( new ObjectHolder(obj) ); + return Ref( new ObjectHolder(obj) ); } } // namespace nasal diff -Nru simgear-2017.3.1+dfsg/simgear/nasal/cppbind/NasalObject.hxx simgear-2018.1.1+dfsg/simgear/nasal/cppbind/NasalObject.hxx --- simgear-2017.3.1+dfsg/simgear/nasal/cppbind/NasalObject.hxx 2017-09-20 07:51:07.000000000 +0000 +++ simgear-2018.1.1+dfsg/simgear/nasal/cppbind/NasalObject.hxx 2018-04-06 19:43:17.000000000 +0000 @@ -23,9 +23,6 @@ #include "NasalObjectHolder.hxx" #include "Ghost.hxx" -#include -#include - namespace nasal { /** @@ -48,14 +45,21 @@ bool valid() const; - // Build dependency for CMake, gcc, etc. -#define SG_DONT_DO_ANYTHING -# include -#undef SG_DONT_DO_ANYTHING - -#define BOOST_PP_ITERATION_LIMITS (0, 9) -#define BOOST_PP_FILENAME_1 -#include BOOST_PP_ITERATE() + template + Ret callMethod(const std::string& name, Args ... args) + { + if( !_nasal_impl.valid() ) + return Ret(); + + Context ctx; + auto func = get_member>( + ctx, _nasal_impl.get_naRef(), name + ); + if( func ) + return func(Me(ctx.to_nasal(this)), args...); + + return Ret(); + } bool _set(naContext c, const std::string& key, naRef val); bool _get(naContext c, const std::string& key, naRef& out); diff -Nru simgear-2017.3.1+dfsg/simgear/nasal/cppbind/NasalString.hxx simgear-2018.1.1+dfsg/simgear/nasal/cppbind/NasalString.hxx --- simgear-2017.3.1+dfsg/simgear/nasal/cppbind/NasalString.hxx 2017-09-20 07:51:07.000000000 +0000 +++ simgear-2018.1.1+dfsg/simgear/nasal/cppbind/NasalString.hxx 2018-04-06 19:43:17.000000000 +0000 @@ -47,7 +47,7 @@ * * @param str Existing Nasal String */ - String(naRef str); + explicit String(naRef str); const char* c_str() const; const char* begin() const; diff -Nru simgear-2017.3.1+dfsg/simgear/nasal/cppbind/test/cppbind_test.cxx simgear-2018.1.1+dfsg/simgear/nasal/cppbind/test/cppbind_test.cxx --- simgear-2017.3.1+dfsg/simgear/nasal/cppbind/test/cppbind_test.cxx 2017-09-20 07:51:07.000000000 +0000 +++ simgear-2018.1.1+dfsg/simgear/nasal/cppbind/test/cppbind_test.cxx 2018-04-06 19:43:17.000000000 +0000 @@ -1,6 +1,8 @@ #define BOOST_TEST_MODULE cppbind #include +#include "TestContext.hxx" + #include #include #include @@ -115,44 +117,89 @@ } naRef f_freeFunction(nasal::CallContext c) { return c.requireArg(0); } +namespace std +{ + template + ostream& operator<<(ostream& strm, const array& vec) + { + for(auto const& v: vec) + strm << "'" << v << "',"; + return strm; + } +} + +BOOST_AUTO_TEST_CASE( cppbind_arrays ) +{ + TestContext ctx; + + naRef na_vec = ctx.to_nasal_vec(1., 2., 3.42); + BOOST_REQUIRE( naIsVector(na_vec) ); + BOOST_CHECK_EQUAL(ctx.from_nasal(naVec_get(na_vec, 0)), 1); + BOOST_CHECK_EQUAL(ctx.from_nasal(naVec_get(na_vec, 1)), 2); + BOOST_CHECK_EQUAL(ctx.from_nasal(naVec_get(na_vec, 2)), 3.42); + + na_vec = ctx.to_nasal(std::initializer_list({1., 2., 3.42})); + BOOST_REQUIRE( naIsVector(na_vec) ); + BOOST_CHECK_EQUAL(ctx.from_nasal(naVec_get(na_vec, 0)), 1); + BOOST_CHECK_EQUAL(ctx.from_nasal(naVec_get(na_vec, 1)), 2); + BOOST_CHECK_EQUAL(ctx.from_nasal(naVec_get(na_vec, 2)), 3.42); + + using arr_d3_t = std::array; + arr_d3_t std_arr = {1., 2., 3.42}; + na_vec = ctx.to_nasal(std_arr); + BOOST_REQUIRE( naIsVector(na_vec) ); + BOOST_CHECK_EQUAL(ctx.from_nasal(naVec_get(na_vec, 0)), 1); + BOOST_CHECK_EQUAL(ctx.from_nasal(naVec_get(na_vec, 1)), 2); + BOOST_CHECK_EQUAL(ctx.from_nasal(naVec_get(na_vec, 2)), 3.42); + + double d_arr[] = {1., 2., 3.42}; + na_vec = ctx.to_nasal(d_arr); + BOOST_REQUIRE( naIsVector(na_vec) ); + BOOST_CHECK_EQUAL(ctx.from_nasal(naVec_get(na_vec, 0)), 1); + BOOST_CHECK_EQUAL(ctx.from_nasal(naVec_get(na_vec, 1)), 2); + BOOST_CHECK_EQUAL(ctx.from_nasal(naVec_get(na_vec, 2)), 3.42); + + BOOST_CHECK_EQUAL(std_arr, ctx.from_nasal(na_vec)); +} + BOOST_AUTO_TEST_CASE( cppbind_misc_testing ) { - naContext c = naNewContext(); + TestContext c; naRef r; using namespace nasal; - r = to_nasal(c, ENUM_ANOTHER); - BOOST_CHECK_EQUAL(from_nasal(c, r), ENUM_ANOTHER); + r = c.to_nasal(ENUM_ANOTHER); + BOOST_CHECK_EQUAL(c.from_nasal(r), ENUM_ANOTHER); - r = to_nasal(c, "Test"); + r = c.to_nasal("Test"); BOOST_CHECK( strncmp("Test", naStr_data(r), naStr_len(r)) == 0 ); - BOOST_CHECK_EQUAL(from_nasal(c, r), "Test"); + BOOST_CHECK_EQUAL(c.from_nasal(r), "Test"); - r = to_nasal(c, std::string("Test")); + r = c.to_nasal(std::string("Test")); BOOST_CHECK( strncmp("Test", naStr_data(r), naStr_len(r)) == 0 ); - BOOST_CHECK_EQUAL(from_nasal(c, r), "Test"); + BOOST_CHECK_EQUAL(c.from_nasal(r), "Test"); - r = to_nasal(c, 42); + r = c.to_nasal(42); BOOST_CHECK_EQUAL(naNumValue(r).num, 42); - BOOST_CHECK_EQUAL(from_nasal(c, r), 42); + BOOST_CHECK_EQUAL(c.from_nasal(r), 42); - r = to_nasal(c, 4.2f); + r = c.to_nasal(4.2f); BOOST_CHECK_EQUAL(naNumValue(r).num, 4.2f); - BOOST_CHECK_EQUAL(from_nasal(c, r), 4.2f); + BOOST_CHECK_EQUAL(c.from_nasal(r), 4.2f); float test_data[3] = {0, 4, 2}; - r = to_nasal(c, test_data); + r = c.to_nasal(test_data); SGVec2f vec(0,2); - r = to_nasal(c, vec); - BOOST_CHECK_EQUAL(from_nasal(c, r), vec); + r = c.to_nasal(vec); + BOOST_CHECK_EQUAL(c.from_nasal(r), vec); std::vector std_vec; - r = to_nasal(c, std_vec); + r = c.to_nasal(std_vec); - r = to_nasal(c, "string"); - BOOST_CHECK_THROW(from_nasal(c, r), bad_nasal_cast); + r = c.to_nasal("string"); + BOOST_CHECK_THROW(c.from_nasal(r), bad_nasal_cast); Hash hash(c); hash.set("vec", r); @@ -171,10 +218,10 @@ it1 = it2; it3 = it2; - r = to_nasal(c, hash); + r = c.to_nasal(hash); BOOST_REQUIRE( naIsHash(r) ); - simgear::StringMap string_map = from_nasal(c, r); + simgear::StringMap string_map = c.from_nasal(r); BOOST_CHECK_EQUAL(string_map.at("vec"), "string"); BOOST_CHECK_EQUAL(string_map.at("name"), "my-name"); BOOST_CHECK_EQUAL(string_map.at("string"), "blub"); @@ -216,7 +263,7 @@ // passed on to function typedef boost::function MeIntFunc; MeIntFunc fmeint = hash.get("func"); - BOOST_CHECK_EQUAL(fmeint(naNil(), 5), 5); + BOOST_CHECK_EQUAL(fmeint(Me{}, 5), 5); //---------------------------------------------------------------------------- // Test exposing classes to Nasal @@ -260,81 +307,81 @@ Ghost::init("SGWeakRefBasedPtr"); SGWeakRefBasedPtr weak_ptr(new SGWeakReferenceBasedClass()); - naRef nasal_ref = to_nasal(c, weak_ptr), - nasal_ptr = to_nasal(c, weak_ptr.get()); + naRef nasal_ref = c.to_nasal(weak_ptr), + nasal_ptr = c.to_nasal(weak_ptr.get()); BOOST_REQUIRE( naIsGhost(nasal_ref) ); BOOST_REQUIRE( naIsGhost(nasal_ptr) ); - SGWeakRefBasedPtr ptr1 = from_nasal(c, nasal_ref), - ptr2 = from_nasal(c, nasal_ptr); + SGWeakRefBasedPtr ptr1 = c.from_nasal(nasal_ref), + ptr2 = c.from_nasal(nasal_ptr); BOOST_CHECK_EQUAL(weak_ptr, ptr1); BOOST_CHECK_EQUAL(weak_ptr, ptr2); BOOST_REQUIRE( Ghost::isInit() ); - nasal::to_nasal(c, DoubleDerived2Ptr()); + c.to_nasal(DoubleDerived2Ptr()); BasePtr d( new Derived ); - naRef derived = to_nasal(c, d); + naRef derived = c.to_nasal(d); BOOST_REQUIRE( naIsGhost(derived) ); BOOST_CHECK_EQUAL( std::string("DerivedPtr"), naGhost_type(derived)->name ); // Get member function from ghost... naRef thisGetter = naNil(); - BOOST_CHECK( naMember_get(c, derived, to_nasal(c, "this"), &thisGetter) ); + BOOST_CHECK( naMember_get(c, derived, c.to_nasal("this"), &thisGetter) ); BOOST_CHECK( naIsFunc(thisGetter) ); // ...and check if it really gets passed the correct instance typedef boost::function MemFunc; - MemFunc fGetThis = from_nasal(c, thisGetter); + MemFunc fGetThis = c.from_nasal(thisGetter); BOOST_REQUIRE( fGetThis ); - BOOST_CHECK_EQUAL( fGetThis(derived), (unsigned long)d.get() ); + BOOST_CHECK_EQUAL( fGetThis(Me{derived}), (unsigned long)d.get() ); BasePtr d2( new DoubleDerived ); - derived = to_nasal(c, d2); + derived = c.to_nasal(d2); BOOST_CHECK( naIsGhost(derived) ); BOOST_CHECK_EQUAL( std::string("DoubleDerivedPtr"), naGhost_type(derived)->name ); BasePtr d3( new DoubleDerived2 ); - derived = to_nasal(c, d3); + derived = c.to_nasal(d3); BOOST_CHECK( naIsGhost(derived) ); BOOST_CHECK_EQUAL( std::string("DoubleDerived2Ptr"), naGhost_type(derived)->name ); SGRefBasedPtr ref_based( new SGReferenceBasedClass ); - naRef na_ref_based = to_nasal(c, ref_based.get()); + naRef na_ref_based = c.to_nasal(ref_based.get()); BOOST_CHECK( naIsGhost(na_ref_based) ); - BOOST_CHECK_EQUAL( from_nasal(c, na_ref_based), + BOOST_CHECK_EQUAL( c.from_nasal(na_ref_based), ref_based.get() ); - BOOST_CHECK_EQUAL( from_nasal(c, na_ref_based), ref_based ); + BOOST_CHECK_EQUAL( c.from_nasal(na_ref_based), ref_based ); - BOOST_CHECK_EQUAL( from_nasal(c, derived), d3 ); - BOOST_CHECK_NE( from_nasal(c, derived), d2 ); - BOOST_CHECK_EQUAL( from_nasal(c, derived), + BOOST_CHECK_EQUAL( c.from_nasal(derived), d3 ); + BOOST_CHECK_NE( c.from_nasal(derived), d2 ); + BOOST_CHECK_EQUAL( c.from_nasal(derived), boost::dynamic_pointer_cast(d3) ); - BOOST_CHECK_EQUAL( from_nasal(c, derived), + BOOST_CHECK_EQUAL( c.from_nasal(derived), boost::dynamic_pointer_cast(d3) ); - BOOST_CHECK_THROW( from_nasal(c, derived), bad_nasal_cast ); + BOOST_CHECK_THROW( c.from_nasal(derived), bad_nasal_cast ); std::map instances; - BOOST_CHECK( naIsHash(to_nasal(c, instances)) ); + BOOST_CHECK( naIsHash(c.to_nasal(instances)) ); std::map instances_d; - BOOST_CHECK( naIsHash(to_nasal(c, instances_d)) ); + BOOST_CHECK( naIsHash(c.to_nasal(instances_d)) ); std::map int_map; - BOOST_CHECK( naIsHash(to_nasal(c, int_map)) ); + BOOST_CHECK( naIsHash(c.to_nasal(int_map)) ); std::map > int_vector_map; - BOOST_CHECK( naIsHash(to_nasal(c, int_vector_map)) ); + BOOST_CHECK( naIsHash(c.to_nasal(int_vector_map)) ); simgear::StringMap dict = simgear::StringMap("hello", "value") ("key2", "value2"); - naRef na_dict = to_nasal(c, dict); + naRef na_dict = c.to_nasal(dict); BOOST_REQUIRE( naIsHash(na_dict) ); BOOST_CHECK_EQUAL( Hash(na_dict, c).get("key2"), "value2" ); @@ -346,65 +393,44 @@ Hash obj(c); obj.set("parents", parents); - BOOST_CHECK_EQUAL( from_nasal(c, obj.get_naRef()), d3 ); + BOOST_CHECK_EQUAL( c.from_nasal(obj.get_naRef()), d3 ); // Check recursive parents (aka parent-of-parent) std::vector parents2; parents2.push_back(obj.get_naRef()); Hash derived_obj(c); derived_obj.set("parents", parents2); - BOOST_CHECK_EQUAL( from_nasal(c, derived_obj.get_naRef()), d3 ); + BOOST_CHECK_EQUAL( c.from_nasal(derived_obj.get_naRef()), d3 ); std::vector nasal_objects; nasal_objects.push_back( Ghost::makeGhost(c, d) ); nasal_objects.push_back( Ghost::makeGhost(c, d2) ); nasal_objects.push_back( Ghost::makeGhost(c, d3) ); - naRef obj_vec = to_nasal(c, nasal_objects); + naRef obj_vec = c.to_nasal(nasal_objects); - std::vector objects = from_nasal >(c, obj_vec); + std::vector objects = c.from_nasal >(obj_vec); BOOST_CHECK_EQUAL( objects[0], d ); BOOST_CHECK_EQUAL( objects[1], d2 ); BOOST_CHECK_EQUAL( objects[2], d3 ); - { - // Calling fallback setter for unset values - const char* src_code = "me.test = 3;"; - int errLine = -1; - naRef code = naParseCode( c, to_nasal(c, "source.nas"), 0, - (char*)src_code, strlen(src_code), - &errLine ); - ret = naCallMethod(code, derived, 0, 0, naNil()); - - BOOST_REQUIRE( !naGetError(c) ); // TODO real error check (this seems to - // always return 0... - BOOST_CHECK_EQUAL( from_nasal(c, ret), 3 ); - } - { - // Calling generic (fallback) getter - const char* src_code = "var a = me.get_test;"; - int errLine = -1; - naRef code = naParseCode( c, to_nasal(c, "source.nas"), 0, - (char*)src_code, strlen(src_code), - &errLine ); - ret = naCallMethod(code, derived, 0, 0, naNil()); - - BOOST_REQUIRE( !naGetError(c) ); // TODO real error check (this seems to - // always return 0... - BOOST_CHECK_EQUAL( from_nasal(c, ret), "generic-get" ); - } + // Calling fallback setter for unset values + BOOST_CHECK_EQUAL( c.exec("me.test = 3;", Me{derived}), 3 ); + + // Calling generic (fallback) getter + BOOST_CHECK_EQUAL( c.exec("var a = me.get_test;", Me{derived}), + "generic-get" ); //---------------------------------------------------------------------------- // Test nasal::CallContext //---------------------------------------------------------------------------- - int int_vec[] = {1,2,3}; std::map map; naRef args[] = { - to_nasal(c, std::string("test-arg")), - to_nasal(c, 4), - to_nasal(c, int_vec), - to_nasal(c, map) + c.to_nasal(std::string("test-arg")), + c.to_nasal(4), + c.to_nasal(int_vec), + c.to_nasal(map) }; CallContext cc(c, naNil(), sizeof(args)/sizeof(args[0]), args); BOOST_CHECK_EQUAL( cc.requireArg(0), "test-arg" ); @@ -419,15 +445,15 @@ BOOST_CHECK( cc.isVector(2) ); BOOST_CHECK( cc.isHash(3) ); - naRef args_vec = nasal::to_nasal(c, args); + naRef args_vec = c.to_nasal(args); BOOST_CHECK( naIsVector(args_vec) ); //---------------------------------------------------------------------------- // Test nasal::String //---------------------------------------------------------------------------- - String string( to_nasal(c, "Test") ); - BOOST_CHECK_EQUAL( from_nasal(c, string.get_naRef()), "Test" ); + String string( c.to_nasal("Test") ); + BOOST_CHECK_EQUAL( c.from_nasal(string.get_naRef()), "Test" ); BOOST_CHECK_EQUAL( string.c_str(), std::string("Test") ); BOOST_CHECK( string.starts_with(string) ); BOOST_CHECK( string.starts_with(String(c, "T")) ); @@ -453,6 +479,16 @@ BOOST_CHECK_EQUAL( string.find_first_not_of(String(c, "Tse"), 2), 3 ); BOOST_CHECK_EQUAL( string.find_first_not_of(String(c, "abc")), 0 ); BOOST_CHECK_EQUAL( string.find_first_not_of(String(c, "abc"), 20), String::npos ); +} - naFreeContext(c); +BOOST_AUTO_TEST_CASE( cppbind_context ) +{ + nasal::Context ctx; + naRef vec = ctx.to_nasal_vec(1, 2, 3.4, "test"); + BOOST_REQUIRE( naIsVector(vec) ); + + BOOST_CHECK_EQUAL(ctx.from_nasal(naVec_get(vec, 0)), 1); + BOOST_CHECK_EQUAL(ctx.from_nasal(naVec_get(vec, 1)), 2); + BOOST_CHECK_EQUAL(ctx.from_nasal(naVec_get(vec, 2)), 3.4); + BOOST_CHECK_EQUAL(ctx.from_nasal(naVec_get(vec, 3)), "test"); } diff -Nru simgear-2017.3.1+dfsg/simgear/nasal/cppbind/test/cppbind_test_ghost.cxx simgear-2018.1.1+dfsg/simgear/nasal/cppbind/test/cppbind_test_ghost.cxx --- simgear-2017.3.1+dfsg/simgear/nasal/cppbind/test/cppbind_test_ghost.cxx 2017-09-20 07:51:07.000000000 +0000 +++ simgear-2018.1.1+dfsg/simgear/nasal/cppbind/test/cppbind_test_ghost.cxx 2018-04-06 19:43:17.000000000 +0000 @@ -1,9 +1,12 @@ #define BOOST_TEST_MODULE cppbind #include +#include "TestContext.hxx" + #include #include +#include #include #include @@ -186,3 +189,44 @@ nasal::shared_ptr_storage::unref(d_weak); } + +BOOST_AUTO_TEST_CASE( bind_methods ) +{ + struct TestClass + { + int arg1; + std::string arg2; + std::string arg3; + int arg4; + + void set(int a1, const std::string& a2, const std::string& a3, int a4) + { + arg1 = a1; + arg2 = a2; + arg3 = a3; + arg4 = a4; + } + }; + using TestClassPtr = boost::shared_ptr; + auto set_func = boost::function< + void (TestClass&, int, const std::string&, const std::string&, int) + >(&TestClass::set); + nasal::Ghost::init("TestClass") + .method("set", set_func) + .method("setReverse", set_func, std::index_sequence<3,2,1,0>{}); + + TestContext ctx; + auto test = boost::make_shared(); + + ctx.exec("me.set(1, \"s2\", \"s3\", 4);", ctx.to_me(test)); + BOOST_CHECK_EQUAL(test->arg1, 1); + BOOST_CHECK_EQUAL(test->arg2, "s2"); + BOOST_CHECK_EQUAL(test->arg3, "s3"); + BOOST_CHECK_EQUAL(test->arg4, 4); + + ctx.exec("me.setReverse(1, \"s2\", \"s3\", 4);", ctx.to_me(test)); + BOOST_CHECK_EQUAL(test->arg1, 4); + BOOST_CHECK_EQUAL(test->arg2, "s3"); + BOOST_CHECK_EQUAL(test->arg3, "s2"); + BOOST_CHECK_EQUAL(test->arg4, 1); +} diff -Nru simgear-2017.3.1+dfsg/simgear/nasal/cppbind/test/nasal_gc_test.cxx simgear-2018.1.1+dfsg/simgear/nasal/cppbind/test/nasal_gc_test.cxx --- simgear-2017.3.1+dfsg/simgear/nasal/cppbind/test/nasal_gc_test.cxx 2017-09-20 07:51:07.000000000 +0000 +++ simgear-2018.1.1+dfsg/simgear/nasal/cppbind/test/nasal_gc_test.cxx 2018-04-06 19:43:17.000000000 +0000 @@ -2,6 +2,9 @@ #include #include "TestContext.hxx" + +#include + #include #include @@ -22,7 +25,7 @@ static naRef createTestGhost(TestContext& c, intptr_t p) { active_instances.insert(p); - return naNewGhost(c.c, &ghost_type, (void*)p); + return naNewGhost(c, &ghost_type, (void*)p); } //------------------------------------------------------------------------------ @@ -91,3 +94,47 @@ BOOST_REQUIRE(active_instances.empty()); } + +//------------------------------------------------------------------------------ +BOOST_AUTO_TEST_CASE( object_holder_gc ) +{ + TestContext c; + BOOST_REQUIRE_EQUAL(naNumSaved(), 0); + BOOST_REQUIRE(active_instances.empty()); + + //----------------------------------------------- + // Put some ghosts in ObjectHolder and check if + // they are saved from gc + + naRef g1 = createTestGhost(c, 1), + g2 = createTestGhost(c, 2); + + nasal::ObjectHolder<> h1(g1); + BOOST_CHECK_EQUAL(naNumSaved(), 1); + BOOST_CHECK(naIsGhost(h1.get_naRef())); + + nasal::ObjectHolder<> h2(g2); + BOOST_CHECK_EQUAL(naNumSaved(), 2); + BOOST_CHECK(naIsGhost(h2.get_naRef())); + + c.runGC(); + + BOOST_CHECK_EQUAL(active_instances.size(), 2); + BOOST_CHECK_EQUAL(naNumSaved(), 2); + + h1.reset(naNum(1)); + h2.reset(naNum(2)); + BOOST_CHECK_EQUAL(naNumSaved(), 2); + + //----------------------------------------------- + // Check that the saved objects are released + + h1.reset(); + BOOST_CHECK_EQUAL(naNumSaved(), 1); + + h2.reset(); + BOOST_CHECK_EQUAL(naNumSaved(), 0); + + c.runGC(); + BOOST_CHECK_EQUAL(active_instances.size(), 0); +} diff -Nru simgear-2017.3.1+dfsg/simgear/nasal/cppbind/test/TestContext.hxx simgear-2018.1.1+dfsg/simgear/nasal/cppbind/test/TestContext.hxx --- simgear-2017.3.1+dfsg/simgear/nasal/cppbind/test/TestContext.hxx 2017-09-20 07:51:07.000000000 +0000 +++ simgear-2018.1.1+dfsg/simgear/nasal/cppbind/test/TestContext.hxx 2018-04-06 19:43:17.000000000 +0000 @@ -20,56 +20,55 @@ #ifndef SG_NASAL_TESTCONTEXT_HXX_ #define SG_NASAL_TESTCONTEXT_HXX_ -#include +#include class TestContext: - public nasal::CallContext + public nasal::Context { public: - TestContext(): - CallContext(naNewContext(), naNil(), 0, 0) - {} + void runGC() + { + naFreeContext(_ctx); + naGC(); + _ctx = naNewContext(); + } - ~TestContext() + template + T exec(const std::string& code, nasal::Me me) { - naFreeContext(c); + return from_nasal(execImpl(code, me)); } - void runGC() + template + T exec(const std::string& code) { - naFreeContext(c); - naGC(); - c = naNewContext(); + return from_nasal(execImpl(code, nasal::Me{})); } template - T from_str(const std::string& str) + T convert(const std::string& str) { return from_nasal(to_nasal(str)); } - naRef exec(const std::string& code_str, nasal::Me me) + protected: + naRef execImpl(const std::string& code_str, nasal::Me me) { int err_line = -1; - naRef code = naParseCode( c, to_nasal(""), 0, + naRef code = naParseCode( _ctx, to_nasal(""), 0, (char*)code_str.c_str(), code_str.length(), &err_line ); if( !naIsCode(code) ) throw std::runtime_error("Failed to parse code: " + code_str); - return naCallMethod(code, me, 0, 0, naNil()); - } + naRef ret = naCallMethod(code, me, 0, 0, naNil()); - template - T exec(const std::string& code) - { - return from_nasal(exec(code, naNil())); - } + if( char* err = naGetError(_ctx) ) + throw std::runtime_error( + "Failed to executed code: " + std::string(err) + ); - template - T convert(const std::string& str) - { - return from_nasal(to_nasal(str)); + return ret; } }; diff -Nru simgear-2017.3.1+dfsg/simgear/nasal/mathlib.c simgear-2018.1.1+dfsg/simgear/nasal/mathlib.c --- simgear-2017.3.1+dfsg/simgear/nasal/mathlib.c 2017-09-20 07:51:07.000000000 +0000 +++ simgear-2018.1.1+dfsg/simgear/nasal/mathlib.c 2018-04-06 19:43:17.000000000 +0000 @@ -109,7 +109,7 @@ naRef b = naNumValue(argc > 1 ? args[1] : naNil()); if(naIsNil(a) || naIsNil(b)) naRuntimeError(c, "non numeric arguments to fmod()"); - + a.num = fmod(a.num, b.num); return VALIDATE(a); } @@ -119,7 +119,7 @@ naRef a = naNumValue(argc > 0 ? args[0] : naNil()); naRef min = naNumValue(argc > 1 ? args[1] : naNil()); naRef max = naNumValue(argc > 2 ? args[2] : naNil()); - + if(naIsNil(a) || naIsNil(min) || naIsNil(max)) naRuntimeError(c, "non numeric arguments to clamp()"); @@ -133,10 +133,10 @@ naRef a = naNumValue(argc > 0 ? args[0] : naNil()); naRef b = naNumValue(argc > 1 ? args[1] : naNil()); naRef x = naNumValue(argc > 2 ? args[2] : naNil()); - + if(naIsNil(a) || naIsNil(b) || naIsNil(x)) naRuntimeError(c, "non numeric arguments to periodic()"); - + range = b.num - a.num; x.num = x.num - range*floor((x.num - a.num)/range); // two security checks that can only happen due to roundoff @@ -145,7 +145,7 @@ if (b.num <= x.num) x.num = b.num; return VALIDATE(x); - + // x.num = SGMiscd::normalizePeriodic(a, b, x); return VALIDATE(x); } @@ -156,7 +156,7 @@ naRef b = naNumValue(argc > 1 ? args[1] : naNil()); #ifdef _MSC_VER double x,y; -#endif +#endif if(naIsNil(a)) naRuntimeError(c, "non numeric arguments to round()"); if (naIsNil(b)) @@ -169,21 +169,31 @@ double x = round(a.num / b.num); #endif a.num = x * b.num; - + return VALIDATE(a); } static naRef f_tan(naContext c, naRef me, int argc, naRef* args) { - naRef a = naNumValue(argc > 0 ? args[0] : naNil()); + naRef a = naNumValue(argc > 0 ? args[0] : naNil()); if(naIsNil(a)) naRuntimeError(c, "non numeric arguments to tan()"); - + a.num = tan(a.num); return VALIDATE(a); } +static naRef f_atan(naContext c, naRef me, int argc, naRef* args) +{ + naRef a = naNumValue(argc > 0 ? args[0] : naNil()); + if(naIsNil(a)) + naRuntimeError(c, "non numeric arguments to tan()"); + + a.num = atan(a.num); + return VALIDATE(a); +} + static naRef f_asin(naContext c, naRef me, int argc, naRef* args) { naRef a = naNumValue(argc > 0 ? args[0] : naNil()); @@ -210,15 +220,16 @@ { "pow", f_pow }, { "sqrt", f_sqrt }, { "atan2", f_atan2 }, + { "atan", f_atan }, { "floor", f_floor }, { "ceil", f_ceil }, { "fmod", f_fmod }, { "clamp", f_clamp }, - { "periodic", f_periodic }, - { "round", f_round }, - { "tan", f_tan }, + { "periodic", f_periodic }, + { "round", f_round }, + { "tan", f_tan }, { "acos", f_acos }, - { "asin", f_asin }, + { "asin", f_asin }, { 0 } }; diff -Nru simgear-2017.3.1+dfsg/simgear/nasal/nasal.h simgear-2018.1.1+dfsg/simgear/nasal/nasal.h --- simgear-2017.3.1+dfsg/simgear/nasal/nasal.h 2017-09-20 07:51:07.000000000 +0000 +++ simgear-2018.1.1+dfsg/simgear/nasal/nasal.h 2018-04-06 19:43:17.000000000 +0000 @@ -1,3 +1,6 @@ +///@file +/// The Nasal scripting language +/// #ifndef _NASAL_H #define _NASAL_H #ifdef __cplusplus @@ -15,12 +18,14 @@ #define GCC_PURE #endif +/** Nasal context pointer */ typedef struct Context* naContext; -// The function signature for an extension function: +/** Function signature for an extension function */ typedef naRef (*naCFunction)(naContext ctx, naRef me, int argc, naRef* args); -// The function signature for an extension function with userdata passed back: +/** Function signature for an extension function with @p user_data passed back + */ typedef naRef (*naCFunctionU) (naContext ctx, naRef me, int argc, naRef* args, void* user_data); @@ -58,6 +63,10 @@ // by the garbage collector. void naGCRelease(int key); +// Get the number of currently saved and not yet again released objects +// (saved by naSave or naGCSave) +int naNumSaved(); + // Drop all saved references void naClearSaved(); @@ -254,7 +263,7 @@ void naHash_cset(naRef hash, char* key, naRef val); void naHash_delete(naRef hash, naRef key); /** - * Store the keys in ::hash into the vector at ::dst + * Store the keys in @p hash into the vector at @p dst * * @see ::naNewVector */ @@ -269,25 +278,25 @@ } naGhostType; /** - * Create a ghost for an object without any attributes. If ::t contains pointers - * to get_member or set_member function they will be ignored. + * Create a ghost for an object without any attributes. If @p t contains + * pointers to get_member or set_member function they will be ignored. */ naRef naNewGhost(naContext c, naGhostType* t, void* ghost); /** * Create a ghost for an object. This version uses the get_member and set_member - * function pointers in ::t upon trying to get or set a member respectively from - * Nasal. + * function pointers in @p t upon trying to get or set a member respectively + * from Nasal. */ naRef naNewGhost2(naContext c, naGhostType* t, void* ghost); naGhostType* naGhost_type(naRef ghost); void* naGhost_ptr(naRef ghost); /** - * Attach a nasal object to the given ghost. Binds the lifetime of @a data to - * the lifetime of the @a ghost. + * Attach a nasal object to the given ghost. Binds the lifetime of @p data to + * the lifetime of the @p ghost. */ void naGhost_setData(naRef ghost, naRef data); /** - * Retrieve the object attached to the @a ghost, previously set with + * Retrieve the object attached to the @p ghost, previously set with * naGhost_setData(). */ naRef naGhost_data(naRef ghost); diff -Nru simgear-2017.3.1+dfsg/simgear/package/Catalog.cxx simgear-2018.1.1+dfsg/simgear/package/Catalog.cxx --- simgear-2017.3.1+dfsg/simgear/package/Catalog.cxx 2017-09-20 07:51:07.000000000 +0000 +++ simgear-2018.1.1+dfsg/simgear/package/Catalog.cxx 2018-04-06 19:43:17.000000000 +0000 @@ -84,7 +84,7 @@ std::string redirectUrlForVersion(const std::string& aVersion, SGPropertyNode_ptr props) { - BOOST_FOREACH(SGPropertyNode* v, props->getChildren("alternate-version")) { + for (SGPropertyNode* v : props->getChildren("alternate-version")) { std::string s(v->getStringValue("version")); if (checkVersionString(aVersion, s)) { return v->getStringValue("url");; @@ -139,7 +139,7 @@ std::string ver(m_owner->root()->applicationVersion()); if (!checkVersion(ver, props)) { - SG_LOG(SG_GENERAL, SG_WARN, "downloaded catalog " << m_owner->url() << ", version required " << ver); + SG_LOG(SG_GENERAL, SG_WARN, "downloaded catalog " << m_owner->url() << ", but version required " << ver); // check for a version redirect entry std::string url = redirectUrlForVersion(ver, props); @@ -169,7 +169,13 @@ m_owner->writeTimestamp(); m_owner->refreshComplete(Delegate::STATUS_REFRESHED); } - + + void onFail() override + { + // network level failure + SG_LOG(SG_GENERAL, SG_WARN, "catalog network failure for:" << m_owner->url()); + m_owner->refreshComplete(Delegate::FAIL_DOWNLOAD); + } private: CatalogRef m_owner; @@ -215,7 +221,8 @@ bool versionCheckOk = checkVersion(aRoot->applicationVersion(), props); if (!versionCheckOk) { - SG_LOG(SG_GENERAL, SG_INFO, "catalog at:" << aPath << " failed version check: need" << aRoot->applicationVersion()); + SG_LOG(SG_GENERAL, SG_INFO, "catalog at:" << aPath << " failed version check: need version: " + << aRoot->applicationVersion()); // keep the catalog but mark it as needing an update } else { SG_LOG(SG_GENERAL, SG_DEBUG, "creating catalog from:" << aPath); @@ -540,6 +547,20 @@ return m_status; } +bool Catalog::isEnabled() const +{ + switch (m_status) { + case Delegate::STATUS_SUCCESS: + case Delegate::STATUS_REFRESHED: + case Delegate::STATUS_IN_PROGRESS: + // this is important so we can use Catalog aircraft in offline mode + case Delegate::FAIL_DOWNLOAD: + return true; + default: + return false; + } +} + } // of namespace pkg } // of namespace simgear diff -Nru simgear-2017.3.1+dfsg/simgear/package/Catalog.hxx simgear-2018.1.1+dfsg/simgear/package/Catalog.hxx --- simgear-2017.3.1+dfsg/simgear/package/Catalog.hxx 2017-09-20 07:51:07.000000000 +0000 +++ simgear-2018.1.1+dfsg/simgear/package/Catalog.hxx 2018-04-06 19:43:17.000000000 +0000 @@ -134,6 +134,12 @@ SGPropertyNode* properties() const; Delegate::StatusCode status() const; + + /** + * is this Catalog usable? This may be false if the catalog is currently + * failing a version check or cannot be updated + */ + bool isEnabled() const; typedef boost::function Callback; diff -Nru simgear-2017.3.1+dfsg/simgear/package/catalogTest1/catalog-v10.xml simgear-2018.1.1+dfsg/simgear/package/catalogTest1/catalog-v10.xml --- simgear-2017.3.1+dfsg/simgear/package/catalogTest1/catalog-v10.xml 1970-01-01 00:00:00.000000000 +0000 +++ simgear-2018.1.1+dfsg/simgear/package/catalogTest1/catalog-v10.xml 2018-04-06 19:43:17.000000000 +0000 @@ -0,0 +1,178 @@ + + + + org.flightgear.test.catalog1 + First test catalog + http://localhost:2000/catalogTest1/catalog-v10.xml + 4 + + 10.0.* + 10.1.* + + alpha + Alpha package + 8 + 593 + + a469c4b837f0521db48616cfe65ac1ea + http://localhost:2000/catalogTest1/alpha.zip + + alpha + + + + + c172p + Cessna 172-P + c172p + A plane made by Cessna on Jupiter + 42 + 860 + Standard author + + cessna + ga + piston + ifr + + + 3 + 4 + 5 + 4 + + + + + org.flightgear.test.catalog1.common-sounds + 10 + + + + exterior + thumb-exterior.png + http://foo.bar.com/thumb-exterior.png + + + + panel + thumb-panel.png + http://foo.bar.com/thumb-panel.png + + + + thumb-something.png + http://foo.bar.com/thumb-something.png + + + + c172p-2d-panel + C172 with 2d panel only + + + + c172p-floats + C172 with floats + A plane with floats + Floats variant author + + + exterior + thumb-exterior-floats.png + http://foo.bar.com/thumb-exterior-floats.png + + + + panel + thumb-panel.png + http://foo.bar.com/thumb-panel.png + + + http://foo.bar.com/thumb-floats.png + thumb-floats.png + + + + c172p-skis + C172 with skis + A plane with skis + + c172p + + + exterior + thumb-exterior-skis.png + http://foo.bar.com/thumb-exterior-skis.png + + + + panel + thumb-panel.png + http://foo.bar.com/thumb-panel.png + + + + + c172r + C172R + Equally good version of the C172 + _package_ + + panel + thumb-panel.png + http://foo.bar.com/thumb-panel.png + + + + + c172r-floats + C172R-floats + Equally good version of the C172 with floats + c172r + + panel + thumb-panel.png + http://foo.bar.com/thumb-panel.png + + + + ec0e2ffdf98d6a5c05c77445e5447ff5 + http://localhost:2000/catalogTest1/c172p.zip + + http://foo.bar.com/thumb-exterior.png + exterior.png + + + + b737-NG + Boeing 737 NG + b737NG + A popular twin-engined narrow body jet + 111 + 860 + + boeing + jet + ifr + + + 5 + 5 + 4 + 4 + + + a94ca5704f305b90767f40617d194ed6 + http://localhost:2000/catalogTest1/b737.tar.gz + + + + common-sounds + Common sound files for test catalog aircraft + 10 + sounds + http://localhost:2000/catalogTest1/common-sounds.zip + 360 + acf9eb89cf396eb42f8823d9cdf17584 + + diff -Nru simgear-2017.3.1+dfsg/simgear/package/catalogTest1/catalog-v2.xml simgear-2018.1.1+dfsg/simgear/package/catalogTest1/catalog-v2.xml --- simgear-2017.3.1+dfsg/simgear/package/catalogTest1/catalog-v2.xml 2017-09-20 07:51:07.000000000 +0000 +++ simgear-2018.1.1+dfsg/simgear/package/catalogTest1/catalog-v2.xml 2018-04-06 19:43:17.000000000 +0000 @@ -10,6 +10,11 @@ 8.0.0 8.2.0 + + 10.*.* + http://localhost:2000/catalogTest1/catalog-v10.xml + + alpha Alpha package diff -Nru simgear-2017.3.1+dfsg/simgear/package/catalogTest1/catalog-v9.xml simgear-2018.1.1+dfsg/simgear/package/catalogTest1/catalog-v9.xml --- simgear-2017.3.1+dfsg/simgear/package/catalogTest1/catalog-v9.xml 1970-01-01 00:00:00.000000000 +0000 +++ simgear-2018.1.1+dfsg/simgear/package/catalogTest1/catalog-v9.xml 2018-04-06 19:43:17.000000000 +0000 @@ -0,0 +1,178 @@ + + + + org.flightgear.test.catalog1 + First test catalog + http://localhost:2000/catalogTest1/catalog.xml + 4 + + 9.1.* + + + alpha + Alpha package + 8 + 593 + + a469c4b837f0521db48616cfe65ac1ea + http://localhost:2000/catalogTest1/alpha.zip + + alpha + + + + + c172p + Cessna 172-P + c172p + A plane made by Cessna on Jupiter + 42 + 860 + Standard author + + cessna + ga + piston + ifr + + + 3 + 4 + 5 + 4 + + + + + org.flightgear.test.catalog1.common-sounds + 10 + + + + exterior + thumb-exterior.png + http://foo.bar.com/thumb-exterior.png + + + + panel + thumb-panel.png + http://foo.bar.com/thumb-panel.png + + + + thumb-something.png + http://foo.bar.com/thumb-something.png + + + + c172p-2d-panel + C172 with 2d panel only + + + + c172p-floats + C172 with floats + A plane with floats + Floats variant author + + + exterior + thumb-exterior-floats.png + http://foo.bar.com/thumb-exterior-floats.png + + + + panel + thumb-panel.png + http://foo.bar.com/thumb-panel.png + + + http://foo.bar.com/thumb-floats.png + thumb-floats.png + + + + c172p-skis + C172 with skis + A plane with skis + + c172p + + + exterior + thumb-exterior-skis.png + http://foo.bar.com/thumb-exterior-skis.png + + + + panel + thumb-panel.png + http://foo.bar.com/thumb-panel.png + + + + + c172r + C172R + Equally good version of the C172 + _package_ + + panel + thumb-panel.png + http://foo.bar.com/thumb-panel.png + + + + + c172r-floats + C172R-floats + Equally good version of the C172 with floats + c172r + + panel + thumb-panel.png + http://foo.bar.com/thumb-panel.png + + + + ec0e2ffdf98d6a5c05c77445e5447ff5 + http://localhost:2000/catalogTest1/c172p.zip + + http://foo.bar.com/thumb-exterior.png + exterior.png + + + + b737-NG + Boeing 737 NG + b737NG + A popular twin-engined narrow body jet + 111 + 860 + + boeing + jet + ifr + + + 5 + 5 + 4 + 4 + + + a94ca5704f305b90767f40617d194ed6 + http://localhost:2000/catalogTest1/b737.tar.gz + + + + common-sounds + Common sound files for test catalog aircraft + 10 + sounds + http://localhost:2000/catalogTest1/common-sounds.zip + 360 + acf9eb89cf396eb42f8823d9cdf17584 + + diff -Nru simgear-2017.3.1+dfsg/simgear/package/CatalogTest.cxx simgear-2018.1.1+dfsg/simgear/package/CatalogTest.cxx --- simgear-2017.3.1+dfsg/simgear/package/CatalogTest.cxx 2017-09-20 07:51:07.000000000 +0000 +++ simgear-2018.1.1+dfsg/simgear/package/CatalogTest.cxx 2018-04-06 19:43:17.000000000 +0000 @@ -61,6 +61,7 @@ SGPath global_serverFilesRoot; unsigned int global_catalogVersion = 0; +bool global_failRequests = false; class TestPackageChannel : public TestServerChannel { @@ -71,6 +72,10 @@ state = STATE_IDLE; SGPath localPath(global_serverFilesRoot); + if (global_failRequests) { + closeWhenDone(); + return; + } if (path == "/catalogTest1/catalog.xml") { if (global_catalogVersion > 0) { @@ -540,6 +545,186 @@ SG_VERIFY(p.exists()); } +void testDisableDueToVersion(HTTP::Client* cl) +{ + global_catalogVersion = 0; + SGPath rootPath(simgear::Dir::current().path()); + rootPath.append("cat_disable_at_version"); + simgear::Dir pd(rootPath); + pd.removeChildren(); + + { + pkg::RootRef root(new pkg::Root(rootPath, "8.1.2")); + root->setHTTPClient(cl); + + pkg::CatalogRef c = pkg::Catalog::createFromUrl(root.ptr(), "http://localhost:2000/catalogTest1/catalog.xml"); + waitForUpdateComplete(cl, root); + SG_VERIFY(c->isEnabled()); + + // install a package + pkg::PackageRef p1 = root->getPackageById("org.flightgear.test.catalog1.b737-NG"); + SG_CHECK_EQUAL(p1->id(), "b737-NG"); + pkg::InstallRef ins = p1->install(); + SG_VERIFY(ins->isQueued()); + waitForUpdateComplete(cl, root); + SG_VERIFY(p1->isInstalled()); + } + + // bump version and refresh + { + pkg::RootRef root(new pkg::Root(rootPath, "9.1.2")); + pkg::CatalogRef cat = root->getCatalogById("org.flightgear.test.catalog1"); + SG_CHECK_EQUAL(root->allCatalogs().size(), 1); + SG_VERIFY(!cat->isEnabled()); + SG_CHECK_EQUAL(root->catalogs().size(), 0); + + root->setHTTPClient(cl); + root->refresh(); + waitForUpdateComplete(cl, root); + SG_CHECK_EQUAL(root->allCatalogs().size(), 1); + + + SG_CHECK_EQUAL(cat->status(), pkg::Delegate::FAIL_VERSION); + SG_VERIFY(!cat->isEnabled()); + SG_CHECK_EQUAL(cat->id(), "org.flightgear.test.catalog1"); + + auto enabledCats = root->catalogs(); + auto it = std::find(enabledCats.begin(), enabledCats.end(), cat); + SG_VERIFY(it == enabledCats.end()); + SG_CHECK_EQUAL(enabledCats.size(), 0); + + auto allCats = root->allCatalogs(); + auto j = std::find(allCats.begin(), allCats.end(), cat); + SG_VERIFY(j != allCats.end()); + + SG_CHECK_EQUAL(allCats.size(), 1); + + // ensure existing package is still installed but not directly list + + pkg::PackageRef p1 = root->getPackageById("org.flightgear.test.catalog1.b737-NG"); + SG_VERIFY(p1 != pkg::PackageRef()); + SG_CHECK_EQUAL(p1->id(), "b737-NG"); + + auto packs = root->allPackages(); + auto k = std::find(packs.begin(), packs.end(), p1); + SG_VERIFY(k == packs.end()); + } +} + +void testVersionMigrate(HTTP::Client* cl) +{ + global_catalogVersion = 2; // version which has migration info + SGPath rootPath(simgear::Dir::current().path()); + rootPath.append("cat_migrate_version"); + simgear::Dir pd(rootPath); + pd.removeChildren(); + + { + pkg::RootRef root(new pkg::Root(rootPath, "8.1.2")); + root->setHTTPClient(cl); + + pkg::CatalogRef c = pkg::Catalog::createFromUrl(root.ptr(), "http://localhost:2000/catalogTest1/catalog.xml"); + waitForUpdateComplete(cl, root); + SG_VERIFY(c->isEnabled()); + + // install a package + pkg::PackageRef p1 = root->getPackageById("org.flightgear.test.catalog1.b737-NG"); + SG_CHECK_EQUAL(p1->id(), "b737-NG"); + pkg::InstallRef ins = p1->install(); + SG_VERIFY(ins->isQueued()); + waitForUpdateComplete(cl, root); + SG_VERIFY(p1->isInstalled()); + } + + // bump version and refresh + { + pkg::RootRef root(new pkg::Root(rootPath, "10.1.2")); + root->setHTTPClient(cl); + + // this should cause auto-migration + root->refresh(true); + waitForUpdateComplete(cl, root); + + pkg::CatalogRef cat = root->getCatalogById("org.flightgear.test.catalog1"); + SG_VERIFY(cat->isEnabled()); + SG_CHECK_EQUAL(cat->status(), pkg::Delegate::STATUS_REFRESHED); + SG_CHECK_EQUAL(cat->id(), "org.flightgear.test.catalog1"); + SG_CHECK_EQUAL(cat->url(), "http://localhost:2000/catalogTest1/catalog-v10.xml"); + + auto enabledCats = root->catalogs(); + auto it = std::find(enabledCats.begin(), enabledCats.end(), cat); + SG_VERIFY(it != enabledCats.end()); + + // ensure existing package is still installed + + pkg::PackageRef p1 = root->getPackageById("org.flightgear.test.catalog1.b737-NG"); + SG_VERIFY(p1 != pkg::PackageRef()); + SG_CHECK_EQUAL(p1->id(), "b737-NG"); + + auto packs = root->allPackages(); + auto k = std::find(packs.begin(), packs.end(), p1); + SG_VERIFY(k != packs.end()); + } +} + +void testOfflineMode(HTTP::Client* cl) +{ + global_catalogVersion = 0; + SGPath rootPath(simgear::Dir::current().path()); + rootPath.append("cat_offline_mode"); + simgear::Dir pd(rootPath); + pd.removeChildren(); + + { + pkg::RootRef root(new pkg::Root(rootPath, "8.1.2")); + root->setHTTPClient(cl); + + pkg::CatalogRef c = pkg::Catalog::createFromUrl(root.ptr(), "http://localhost:2000/catalogTest1/catalog.xml"); + waitForUpdateComplete(cl, root); + SG_VERIFY(c->isEnabled()); + + // install a package + pkg::PackageRef p1 = root->getPackageById("org.flightgear.test.catalog1.b737-NG"); + SG_CHECK_EQUAL(p1->id(), "b737-NG"); + pkg::InstallRef ins = p1->install(); + SG_VERIFY(ins->isQueued()); + waitForUpdateComplete(cl, root); + SG_VERIFY(p1->isInstalled()); + } + + global_failRequests = true; + + { + pkg::RootRef root(new pkg::Root(rootPath, "8.1.2")); + SG_CHECK_EQUAL(root->catalogs().size(), 1); + + root->setHTTPClient(cl); + root->refresh(true); + waitForUpdateComplete(cl, root); + SG_CHECK_EQUAL(root->catalogs().size(), 1); + + pkg::CatalogRef cat = root->getCatalogById("org.flightgear.test.catalog1"); + SG_VERIFY(cat->isEnabled()); + SG_CHECK_EQUAL(cat->status(), pkg::Delegate::FAIL_DOWNLOAD); + SG_CHECK_EQUAL(cat->id(), "org.flightgear.test.catalog1"); + + auto enabledCats = root->catalogs(); + auto it = std::find(enabledCats.begin(), enabledCats.end(), cat); + SG_VERIFY(it != enabledCats.end()); + + // ensure existing package is still installed + pkg::PackageRef p1 = root->getPackageById("org.flightgear.test.catalog1.b737-NG"); + SG_VERIFY(p1 != pkg::PackageRef()); + SG_CHECK_EQUAL(p1->id(), "b737-NG"); + + auto packs = root->allPackages(); + auto k = std::find(packs.begin(), packs.end(), p1); + SG_VERIFY(k != packs.end()); + } + + global_failRequests = false; +} + int main(int argc, char* argv[]) { @@ -564,6 +749,12 @@ testInstallTarPackage(&cl); + testDisableDueToVersion(&cl); + + testOfflineMode(&cl); + + testVersionMigrate(&cl); + std::cout << "Successfully passed all tests!" << std::endl; return EXIT_SUCCESS; } diff -Nru simgear-2017.3.1+dfsg/simgear/package/Root.cxx simgear-2018.1.1+dfsg/simgear/package/Root.cxx --- simgear-2017.3.1+dfsg/simgear/package/Root.cxx 2017-09-20 07:51:07.000000000 +0000 +++ simgear-2018.1.1+dfsg/simgear/package/Root.cxx 2018-04-06 19:43:17.000000000 +0000 @@ -151,25 +151,36 @@ std::string u = dl->realUrl(); if (status == Delegate::STATUS_SUCCESS) { thumbnailCache[u].requestPending = false; - fireDataForThumbnail(u, reinterpret_cast(bytes.data()), bytes.size()); // if this was a network load, rather than a re-load from the disk cache, // then persist to disk now. if (strutils::starts_with(request->url(), "http")) { addToPersistentCache(u, bytes); } + + fireDataForThumbnail(u, reinterpret_cast(bytes.data()), bytes.size()); } else if (status == Delegate::FAIL_HTTP_FORBIDDEN) { // treat this as rate-limiting failure, at least from some mirrors // (eg Ibiblio) and retry up to the max count const int retries = (thumbnailCache[u].retryCount++); if (retries < 3) { - SG_LOG(SG_IO, SG_INFO, "Download failed for: " << u << ", will retry"); + SG_LOG(SG_IO, SG_DEBUG, "Download failed for: " << u << ", will retry"); thumbnailCache[u].requestPending = true; pendingThumbnails.push_back(u); } } else { // any other failure. thumbnailCache[u].requestPending = false; + + // if this was a cache refresh, let's report the cached data instead + SGPath cachePath = pathInCache(u); + if (cachePath.exists()) { + SG_LOG(SG_IO, SG_WARN, "Download failed for: " << u << ", will use old cached data"); + cachePath.touch(); // touch the file so we don't repeat this danxce + // kick a load from the cache + + queueLoadFromPersistentCache(u, cachePath); + } } downloadNextPendingThumbnail(); @@ -207,40 +218,45 @@ } } - void addToPersistentCache(const std::string& url, const std::string& imageBytes) + SGPath pathInCache(const std::string& url) const { - std::string hash = hashForUrl(url); + const auto hash = hashForUrl(url); // append the correct file suffix auto pos = url.rfind('.'); if (pos == std::string::npos) { - return; + return SGPath(); } - SGPath cachePath = path / "ThumbnailCache" / (hash + url.substr(pos)); + return path / "ThumbnailCache" / (hash + url.substr(pos)); + } + + void addToPersistentCache(const std::string& url, const std::string& imageBytes) + { + // this will over-write the existing file if we are refreshing, + // since we use 'truncatr' to open the new file + SGPath cachePath = pathInCache(url); sg_ofstream fstream(cachePath, std::ios::out | std::ios::trunc | std::ios::binary); fstream.write(imageBytes.data(), imageBytes.size()); fstream.close(); + + auto it = thumbnailCache.find(url); + assert(it != thumbnailCache.end()); + it->second.pathOnDisk = cachePath; } bool checkPersistentCache(const std::string& url) { - std::string hash = hashForUrl(url); - // append the correct file suffix - auto pos = url.rfind('.'); - if (pos == std::string::npos) { - return false; - } - - SGPath cachePath = path / "ThumbnailCache" / (hash + url.substr(pos)); + SGPath cachePath = pathInCache(url); if (!cachePath.exists()) { return false; } // check age, if it's too old, expire and download again int age = time(nullptr) - cachePath.modTime(); - if (age > SECONDS_PER_DAY * 7) { // cache for seven days - SG_LOG(SG_IO, SG_INFO, "expiring old cached thumbnail " << url); - cachePath.remove(); + const int cacheMaxAge = SECONDS_PER_DAY * 7; + if (age > cacheMaxAge) { // cache for seven days + // note we do *not* remove the file data here, since the + // cache refresh might fail return false; } @@ -258,7 +274,7 @@ entry.pathOnDisk = path; it = thumbnailCache.insert(it, std::make_pair(url, entry)); } else { - assert(it->second.pathOnDisk == path); + assert(it->second.pathOnDisk.isNull() || (it->second.pathOnDisk == path)); } if (it->second.requestPending) { @@ -392,15 +408,11 @@ } for (SGPath c : dir.children(Dir::TYPE_DIR | Dir::NO_DOT_OR_DOTDOT)) { - CatalogRef cat = Catalog::createFromPath(this, c); - if (cat) { - if (cat->status() == Delegate::STATUS_SUCCESS) { - d->catalogs[cat->id()] = cat; - } else { - // catalog has problems, such as needing an update - // keep it out of the main collection for now - d->disabledCatalogs.push_back(cat); - } + // note this will set the catalog status, which will insert into + // disabled catalogs automatically if necesary + auto cat = Catalog::createFromPath(this, c); + if (cat && cat->isEnabled()) { + d->catalogs.insert({cat->id(), cat}); } } // of child directories iteration } @@ -422,9 +434,15 @@ CatalogRef Root::getCatalogById(const std::string& aId) const { - CatalogDict::const_iterator it = d->catalogs.find(aId); + auto it = d->catalogs.find(aId); if (it == d->catalogs.end()) { - return NULL; + // check disabled catalog list + auto j = std::find_if(d->disabledCatalogs.begin(), d->disabledCatalogs.end(), + [aId](const CatalogRef& cat) { return cat->id() == aId; }); + if (j != d->disabledCatalogs.end()) { + return *j; + } + return nullptr; } return it->second; @@ -468,6 +486,13 @@ return r; } + +CatalogList Root::allCatalogs() const +{ + CatalogList r = catalogs(); + r.insert(r.end(), d->disabledCatalogs.begin(), d->disabledCatalogs.end()); + return r; +} PackageList Root::allPackages() const @@ -528,12 +553,9 @@ toRefresh.insert(toRefresh.end(), d->disabledCatalogs.begin(), d->disabledCatalogs.end()); - - - CatalogList::iterator j = toRefresh.begin(); - for (; j != toRefresh.end(); ++j) { - (*j)->refresh(); - didStartAny = true; + for (auto cat : toRefresh) { + cat->refresh(); + didStartAny = true; } if (!didStartAny) { @@ -549,12 +571,10 @@ void Root::removeDelegate(simgear::pkg::Delegate *aDelegate) { - DelegateVec::iterator it = std::find(d->delegates.begin(), - d->delegates.end(), aDelegate); - if (it == d->delegates.end()) { - throw sg_exception("unknown delegate in removeDelegate"); + auto it = std::find(d->delegates.begin(), d->delegates.end(), aDelegate); + if (it != d->delegates.end()) { + d->delegates.erase(it); } - d->delegates.erase(it); } void Root::setLocale(const std::string& aLocale) @@ -663,23 +683,20 @@ d->catalogs.insert(catIt, CatalogDict::value_type(aCat->id(), aCat)); // catalog might have been previously disabled, let's remove in that case - CatalogList::iterator j = std::find(d->disabledCatalogs.begin(), - d->disabledCatalogs.end(), - aCat); + auto j = std::find(d->disabledCatalogs.begin(), + d->disabledCatalogs.end(), + aCat); if (j != d->disabledCatalogs.end()) { SG_LOG(SG_GENERAL, SG_INFO, "re-enabling disabled catalog:" << aCat->id()); d->disabledCatalogs.erase(j); } } - if ((aReason != Delegate::STATUS_REFRESHED) && - (aReason != Delegate::STATUS_IN_PROGRESS) && - (aReason != Delegate::STATUS_SUCCESS)) - { + if (!aCat->isEnabled()) { // catalog has errors, disable it - CatalogList::iterator j = std::find(d->disabledCatalogs.begin(), - d->disabledCatalogs.end(), - aCat); + auto j = std::find(d->disabledCatalogs.begin(), + d->disabledCatalogs.end(), + aCat); if (j == d->disabledCatalogs.end()) { SG_LOG(SG_GENERAL, SG_INFO, "disabling catalog:" << aCat->id()); d->disabledCatalogs.push_back(aCat); @@ -689,7 +706,7 @@ if (catIt != d->catalogs.end()) { d->catalogs.erase(catIt); } - } // of catalog has errors case + } // of catalog is disabled if (d->refreshing.empty()) { d->fireRefreshStatus(CatalogRef(), Delegate::STATUS_REFRESHED); @@ -704,13 +721,8 @@ CatalogDict::iterator catIt = d->catalogs.find(aId); if (catIt == d->catalogs.end()) { // check the disabled list - CatalogList::iterator j = d->disabledCatalogs.begin(); - for (; j != d->disabledCatalogs.end(); ++j) { - if ((*j)->id() == aId) { - break; - } - } - + auto j = std::find_if(d->disabledCatalogs.begin(), d->disabledCatalogs.end(), + [aId](const CatalogRef& cat) { return cat->id() == aId; }); if (j == d->disabledCatalogs.end()) { SG_LOG(SG_GENERAL, SG_WARN, "removeCatalogById: no catalog with id:" << aId); return false; diff -Nru simgear-2017.3.1+dfsg/simgear/package/Root.hxx simgear-2018.1.1+dfsg/simgear/package/Root.hxx --- simgear-2017.3.1+dfsg/simgear/package/Root.hxx 2017-09-20 07:51:07.000000000 +0000 +++ simgear-2018.1.1+dfsg/simgear/package/Root.hxx 2018-04-06 19:43:17.000000000 +0000 @@ -69,7 +69,13 @@ std::string getLocale() const; CatalogList catalogs() const; - + + /** + * retrive all catalogs, including currently disabled ones + */ + CatalogList allCatalogs() const; + + void setMaxAgeSeconds(unsigned int seconds); unsigned int maxAgeSeconds() const; diff -Nru simgear-2017.3.1+dfsg/simgear/props/PropertyBasedElement.cxx simgear-2018.1.1+dfsg/simgear/props/PropertyBasedElement.cxx --- simgear-2017.3.1+dfsg/simgear/props/PropertyBasedElement.cxx 2017-09-20 07:51:07.000000000 +0000 +++ simgear-2018.1.1+dfsg/simgear/props/PropertyBasedElement.cxx 2018-04-06 19:43:17.000000000 +0000 @@ -18,7 +18,7 @@ #include #include "PropertyBasedElement.hxx" -#include +#include namespace simgear { @@ -130,7 +130,7 @@ // character that followed it by the same character converted to ASCII // uppercase. - if( !boost::starts_with(name, DATA_PREFIX) ) + if( !strutils::starts_with(name, DATA_PREFIX) ) return std::string(); std::string data_name; diff -Nru simgear-2017.3.1+dfsg/simgear/props/PropertyBasedElement.hxx simgear-2018.1.1+dfsg/simgear/props/PropertyBasedElement.hxx --- simgear-2017.3.1+dfsg/simgear/props/PropertyBasedElement.hxx 2017-09-20 07:51:07.000000000 +0000 +++ simgear-2018.1.1+dfsg/simgear/props/PropertyBasedElement.hxx 2018-04-06 19:43:17.000000000 +0000 @@ -20,6 +20,7 @@ #define SG_PROPERTY_BASED_ELEMENT_HXX_ #include +#include #include namespace simgear @@ -120,11 +121,9 @@ * @see setDataProp */ template - typename boost::disable_if< - boost::is_same, - T - >::type getDataProp( const std::string& name, - const T& def = T() ) const + std::enable_if_t::value, T> + getDataProp( const std::string& name, + const T& def = {} ) const { SGPropertyNode* node = getDataProp(name); if( node ) @@ -140,11 +139,9 @@ * @see setDataProp */ template - typename boost::enable_if< - boost::is_same, - T - >::type getDataProp( const std::string& name, - SGPropertyNode* = NULL ) const + std::enable_if_t::value, T> + getDataProp( const std::string& name, + SGPropertyNode* = nullptr ) const { const std::string& attr = dataPropToAttrName(name); if( attr.empty() ) diff -Nru simgear-2017.3.1+dfsg/simgear/props/props.cxx simgear-2018.1.1+dfsg/simgear/props/props.cxx --- simgear-2017.3.1+dfsg/simgear/props/props.cxx 2017-09-20 07:51:07.000000000 +0000 +++ simgear-2018.1.1+dfsg/simgear/props/props.cxx 2018-04-06 19:43:17.000000000 +0000 @@ -827,7 +827,7 @@ * Last used attribute * Update as needed when enum Attribute is changed */ -const int SGPropertyNode::LAST_USED_ATTRIBUTE = PRESERVE; +const int SGPropertyNode::LAST_USED_ATTRIBUTE = PROTECTED; /** * Default constructor: always creates a root node. @@ -2443,7 +2443,8 @@ { if (_listeners != 0) { for (unsigned int i = 0; i < _listeners->size(); i++) { - (*_listeners)[i]->valueChanged(node); + if ((*_listeners)[i]) + (*_listeners)[i]->valueChanged(node); } } if (_parent != 0) @@ -2581,53 +2582,77 @@ namespace simgear { #if !PROPS_STANDALONE -template<> -std::istream& readFrom(std::istream& stream, SGVec4d& result) -{ - for (int i = 0; i < 4; ++i) { - stream >> result[i]; + template<> + std::istream& readFrom(std::istream& stream, SGVec4d& result) + { + for (int i = 0; i < 4; ++i) { + stream >> result[i]; + } + return stream; } - return stream; -} #endif -namespace -{ -bool compareNodeValue(const SGPropertyNode& lhs, const SGPropertyNode& rhs) -{ - props::Type ltype = lhs.getType(); - props::Type rtype = rhs.getType(); - if (ltype != rtype) - return false; - switch (ltype) { - case props::NONE: - return true; - case props::ALIAS: - return false; // XXX Should we look in aliases? - case props::BOOL: - return lhs.getValue() == rhs.getValue(); - case props::INT: - return lhs.getValue() == rhs.getValue(); - case props::LONG: - return lhs.getValue() == rhs.getValue(); - case props::FLOAT: - return lhs.getValue() == rhs.getValue(); - case props::DOUBLE: - return lhs.getValue() == rhs.getValue(); - case props::STRING: - case props::UNSPECIFIED: - return !strcmp(lhs.getStringValue(), rhs.getStringValue()); + namespace + { + bool compareNodeValue(const SGPropertyNode& lhs, const SGPropertyNode& rhs) + { + props::Type ltype = lhs.getType(); + props::Type rtype = rhs.getType(); + if (ltype != rtype) + return false; + switch (ltype) { + case props::NONE: + return true; + case props::ALIAS: + return false; // XXX Should we look in aliases? + case props::BOOL: + return lhs.getValue() == rhs.getValue(); + case props::INT: + return lhs.getValue() == rhs.getValue(); + case props::LONG: + return lhs.getValue() == rhs.getValue(); + case props::FLOAT: + return lhs.getValue() == rhs.getValue(); + case props::DOUBLE: + return lhs.getValue() == rhs.getValue(); + case props::STRING: + case props::UNSPECIFIED: + return !strcmp(lhs.getStringValue(), rhs.getStringValue()); #if !PROPS_STANDALONE - case props::VEC3D: - return lhs.getValue() == rhs.getValue(); - case props::VEC4D: - return lhs.getValue() == rhs.getValue(); + case props::VEC3D: + return lhs.getValue() == rhs.getValue(); + case props::VEC4D: + return lhs.getValue() == rhs.getValue(); #endif - default: - return false; + default: + return false; + } + } } } -} + + +void SGPropertyNode::copy(SGPropertyNode *to) +{ + if (nChildren()) + { + for (int i = 0; i < nChildren(); i++) { + SGPropertyNode *child = getChild(i); + SGPropertyNode *to_child = to->getChild(child->getName()); + if (!to_child) + to_child = to->addChild(child->getName()); + if (child->nChildren()) + { + child->copy(to_child); + } + else + { + to_child->setValue(child->getStringValue()); + } + } + } + else + to->setValue(getStringValue()); } bool SGPropertyNode::compare(const SGPropertyNode& lhs, diff -Nru simgear-2017.3.1+dfsg/simgear/props/props.hxx simgear-2018.1.1+dfsg/simgear/props/props.hxx --- simgear-2017.3.1+dfsg/simgear/props/props.hxx 2017-09-20 07:51:07.000000000 +0000 +++ simgear-2018.1.1+dfsg/simgear/props/props.hxx 2018-04-06 19:43:17.000000000 +0000 @@ -99,9 +99,9 @@ return result; } -/** - * Property value types. - */ +// +// Property value types +// #ifdef NONE #pragma warn A sloppy coder has defined NONE as a macro! @@ -148,6 +148,7 @@ #undef STRING #endif +/// Property system which associates property names with values. namespace props { /** @@ -813,7 +814,8 @@ TRACE_READ = 16, TRACE_WRITE = 32, USERARCHIVE = 64, - PRESERVE = 128 + PRESERVE = 128, + PROTECTED = 1 << 8, // beware: if you add another attribute here, // also update value of "LAST_USED_ATTRIBUTE". }; @@ -1106,6 +1108,11 @@ SGPropertyNode * getNode (const char * relative_path, bool create = false); /** + * deep copy one node to another. + */ + void copy(SGPropertyNode *to); + + /** * Get a pointer to another node by relative path. */ SGPropertyNode * getNode (const std::string& relative_path, bool create = false) @@ -1848,7 +1855,7 @@ mutable std::string _buffer; simgear::props::Type _type; bool _tied; - int _attr; + int _attr = NO_ATTR; // The right kind of pointer... union { @@ -2139,17 +2146,18 @@ return parent->getChild(name, index, true); } -/** - * Utility function for creation of a child property node using a - * relative path. - */ namespace simgear { -template -inline SGPropertyNode* makeNode(SGPropertyNode* parent, const StringType& name) -{ - return parent->getNode(name, true); -} + /** + * Utility function for creation of a child property node using a + * relative path. + */ + template + inline SGPropertyNode* + makeNode(SGPropertyNode* parent, const StringType& name) + { + return parent->getNode(name, true); + } } // For boost::hash diff -Nru simgear-2017.3.1+dfsg/simgear/props/props_io.cxx simgear-2018.1.1+dfsg/simgear/props/props_io.cxx --- simgear-2017.3.1+dfsg/simgear/props/props_io.cxx 2017-09-20 07:51:07.000000000 +0000 +++ simgear-2018.1.1+dfsg/simgear/props/props_io.cxx 2018-04-06 19:43:17.000000000 +0000 @@ -161,8 +161,7 @@ string message = "Unrecognized flag value '"; message += flag; message += '\''; - // FIXME: add location info - throw sg_io_exception(message, location, "SimGear Property Reader"); + throw sg_io_exception(message, location, SG_ORIGIN); } } @@ -177,7 +176,7 @@ string message = "Root element name is "; message += name; message += "; expected PropertyList"; - throw sg_io_exception(message, location, "SimGear Property Reader"); + throw sg_io_exception(message, location, SG_ORIGIN); } // Check for an include. @@ -189,8 +188,7 @@ { string message ="Cannot open file "; message += attval; - throw sg_io_exception(message, location, - "SimGear Property Reader"); + throw sg_io_exception(message, location, SG_ORIGIN); } readProperties(path, _root, 0, _extended); } catch (sg_io_exception &e) { @@ -253,6 +251,9 @@ setFlag(mode, SGPropertyNode::USERARCHIVE, val, location); else if( att_name == "preserve" ) setFlag(mode, SGPropertyNode::PRESERVE, val, location); + // note we intentionally don't handle PROTECTED here, it's + // designed to be only set from compiled code, not loaded + // dynamically. // Check for an alias. else if( att_name == "alias" ) @@ -277,7 +278,7 @@ { string message ="Cannot open file "; message += val; - throw sg_io_exception(message, location, "SimGear Property Reader"); + throw sg_io_exception(message, location, SG_ORIGIN); } readProperties(path, node, 0, _extended); } @@ -352,8 +353,7 @@ string message = "Unrecognized data type '"; message += st.type; message += '\''; - // FIXME: add location information - throw sg_io_exception(message, location, "SimGear Property Reader"); + throw sg_io_exception(message, location, SG_ORIGIN); } if( !ret ) SG_LOG @@ -770,7 +770,7 @@ break; string message = "Unknown internal SGPropertyNode type"; message += in->getType(); - throw sg_error(message, "SimGear Property Reader"); + throw sg_error(message, SG_ORIGIN); } return retval; diff -Nru simgear-2017.3.1+dfsg/simgear/props/props_test.cxx simgear-2018.1.1+dfsg/simgear/props/props_test.cxx --- simgear-2017.3.1+dfsg/simgear/props/props_test.cxx 2017-09-20 07:51:07.000000000 +0000 +++ simgear-2018.1.1+dfsg/simgear/props/props_test.cxx 2018-04-06 19:43:17.000000000 +0000 @@ -9,6 +9,7 @@ #include +#include #include // std::unique_ptr #include #include diff -Nru simgear-2017.3.1+dfsg/simgear/scene/material/EffectBuilder.cxx simgear-2018.1.1+dfsg/simgear/scene/material/EffectBuilder.cxx --- simgear-2017.3.1+dfsg/simgear/scene/material/EffectBuilder.cxx 2017-09-20 07:51:07.000000000 +0000 +++ simgear-2018.1.1+dfsg/simgear/scene/material/EffectBuilder.cxx 2018-04-06 19:43:17.000000000 +0000 @@ -85,7 +85,7 @@ { } -BuilderException::~BuilderException() throw() +BuilderException::~BuilderException() { } diff -Nru simgear-2017.3.1+dfsg/simgear/scene/material/EffectBuilder.hxx simgear-2018.1.1+dfsg/simgear/scene/material/EffectBuilder.hxx --- simgear-2017.3.1+dfsg/simgear/scene/material/EffectBuilder.hxx 2017-09-20 07:51:07.000000000 +0000 +++ simgear-2018.1.1+dfsg/simgear/scene/material/EffectBuilder.hxx 2018-04-06 19:43:17.000000000 +0000 @@ -1,3 +1,6 @@ +///@file +/// Support classes for parsing effects. +/// // Copyright (C) 2009 Tim Moore timoore@redhat.com // // This library is free software; you can redistribute it and/or @@ -39,9 +42,6 @@ #include #include "Effect.hxx" -/** - * Support classes for parsing effects. - */ namespace simgear { @@ -184,7 +184,7 @@ BuilderException(); BuilderException(const char* message, const char* origin = 0); BuilderException(const std::string& message, const std::string& = ""); - virtual ~BuilderException() throw(); + virtual ~BuilderException(); }; } diff -Nru simgear-2017.3.1+dfsg/simgear/scene/model/animation.cxx simgear-2018.1.1+dfsg/simgear/scene/model/animation.cxx --- simgear-2017.3.1+dfsg/simgear/scene/model/animation.cxx 2017-09-20 07:51:07.000000000 +0000 +++ simgear-2018.1.1+dfsg/simgear/scene/model/animation.cxx 2018-04-06 19:43:17.000000000 +0000 @@ -497,6 +497,9 @@ } else if (type == "slider") { SGSliderAnimation anim(modelData); anim.apply(modelData.getNode()); + } else if (type == "touch") { + SGTouchAnimation anim(modelData); + anim.apply(modelData.getNode()); } else if (type == "range") { SGRangeAnimation anim(modelData); anim.apply(modelData); @@ -769,7 +772,7 @@ object_group->setNodeMask(0); } else - SG_LOG(SG_INPUT, SG_ALERT, "Could find a valid line segment for animation: " << axis_object_name); + SG_LOG(SG_INPUT, SG_ALERT, "Could not find a valid line segment for animation: " << axis_object_name); } else if (can_warn) SG_LOG(SG_INPUT, SG_ALERT, "Could not find at least one of the following objects for axis animation: " << axis_object_name); diff -Nru simgear-2017.3.1+dfsg/simgear/scene/model/SGPickAnimation.cxx simgear-2018.1.1+dfsg/simgear/scene/model/SGPickAnimation.cxx --- simgear-2017.3.1+dfsg/simgear/scene/model/SGPickAnimation.cxx 2017-09-20 07:51:07.000000000 +0000 +++ simgear-2018.1.1+dfsg/simgear/scene/model/SGPickAnimation.cxx 2018-04-06 19:43:17.000000000 +0000 @@ -74,8 +74,10 @@ class SGPickAnimation::PickCallback : public SGPickCallback { public: PickCallback(const SGPropertyNode* configNode, - SGPropertyNode* modelRoot) : + SGPropertyNode* modelRoot, + SGSharedPtr condition) : SGPickCallback(PriorityPanel), + _condition(condition), _repeatable(configNode->getBoolValue("repeatable", false)), _repeatInterval(configNode->getDoubleValue("interval-sec", 0.1)) { @@ -98,63 +100,76 @@ void addHoverBindings(const SGPropertyNode* hoverNode, SGPropertyNode* modelRoot) { - _hover = readBindingList(hoverNode->getChildren("binding"), modelRoot); + if (!_condition || _condition->test()) { + _hover = readBindingList(hoverNode->getChildren("binding"), modelRoot); + } } virtual bool buttonPressed( int button, const osgGA::GUIEventAdapter&, const Info& ) { - if (_buttons.find(button) == _buttons.end()) { - return false; + if (!_condition || _condition->test()) { + if (_buttons.find(button) == _buttons.end()) { + return false; + } + + if (!anyBindingEnabled(_bindingsDown)) { + return false; + } + + fireBindingList(_bindingsDown); + _repeatTime = -_repeatInterval; // anti-bobble: delay start of repeat + return true; } - - if (!anyBindingEnabled(_bindingsDown)) { - return false; - } - - fireBindingList(_bindingsDown); - _repeatTime = -_repeatInterval; // anti-bobble: delay start of repeat - return true; + return false; } virtual void buttonReleased( int keyModState, const osgGA::GUIEventAdapter&, const Info* ) { - SG_UNUSED(keyModState); - fireBindingList(_bindingsUp); + if (!_condition || _condition->test()) { + SG_UNUSED(keyModState); + fireBindingList(_bindingsUp); + } } virtual void update(double dt, int keyModState) { - SG_UNUSED(keyModState); - if (!_repeatable) - return; - - _repeatTime += dt; - while (_repeatInterval < _repeatTime) { - _repeatTime -= _repeatInterval; - fireBindingList(_bindingsDown); - } + if (!_condition || _condition->test()) { + SG_UNUSED(keyModState); + if (!_repeatable) + return; + + _repeatTime += dt; + while (_repeatInterval < _repeatTime) { + _repeatTime -= _repeatInterval; + fireBindingList(_bindingsDown); + } + } } virtual bool hover( const osg::Vec2d& windowPos, const Info& ) { - if (!anyBindingEnabled(_hover)) { - return false; + if (!_condition || _condition->test()) { + if (!anyBindingEnabled(_hover)) { + return false; + } + + SGPropertyNode_ptr params(new SGPropertyNode); + params->setDoubleValue("x", windowPos.x()); + params->setDoubleValue("y", windowPos.y()); + fireBindingList(_hover, params.ptr()); + return true; } - - SGPropertyNode_ptr params(new SGPropertyNode); - params->setDoubleValue("x", windowPos.x()); - params->setDoubleValue("y", windowPos.y()); - fireBindingList(_hover, params.ptr()); - return true; + return false; } std::string getCursor() const { return _cursorName; } private: + SGSharedPtr _condition; SGBindingList _bindingsDown; SGBindingList _bindingsUp; SGBindingList _hover; @@ -247,8 +262,10 @@ public: VncCallback(const SGPropertyNode* configNode, SGPropertyNode* modelRoot, - osg::Group *node) - : _node(node) + osg::Group *node, + SGSharedPtr condition) + : _condition(condition) + , _node(node) { SG_LOG(SG_INPUT, SG_DEBUG, "Configuring VNC callback"); const char *cornernames[3] = {"top-left", "top-right", "bottom-left"}; @@ -270,29 +287,35 @@ const osgGA::GUIEventAdapter&, const Info& info ) { - SGVec3d loc(info.local); - SG_LOG(SG_INPUT, SG_DEBUG, "VNC pressed " << button << ": " << loc); - loc -= _topLeft; - _x = dot(loc, _toRight) / _squaredRight; - _y = dot(loc, _toDown) / _squaredDown; - if (_x<0) _x = 0; else if (_x > 1) _x = 1; - if (_y<0) _y = 0; else if (_y > 1) _y = 1; - VncVisitor vv(_x, _y, 1 << button); - _node->accept(vv); - return vv.wasSuccessful(); - + if (!_condition || _condition->test()) { + SGVec3d loc(info.local); + SG_LOG(SG_INPUT, SG_DEBUG, "VNC pressed " << button << ": " << loc); + loc -= _topLeft; + _x = dot(loc, _toRight) / _squaredRight; + _y = dot(loc, _toDown) / _squaredDown; + if (_x < 0) _x = 0; else if (_x > 1) _x = 1; + if (_y < 0) _y = 0; else if (_y > 1) _y = 1; + VncVisitor vv(_x, _y, 1 << button); + _node->accept(vv); + return vv.wasSuccessful(); + } + return false; } virtual void buttonReleased( int keyModState, const osgGA::GUIEventAdapter&, const Info* ) { - SG_UNUSED(keyModState); - SG_LOG(SG_INPUT, SG_DEBUG, "VNC release"); - VncVisitor vv(_x, _y, 0); - _node->accept(vv); + if (!_condition || _condition->test()) { + SG_UNUSED(keyModState); + SG_LOG(SG_INPUT, SG_DEBUG, "VNC release"); + VncVisitor vv(_x, _y, 0); + _node->accept(vv); + } } private: + SGSharedPtr _condition; + double _x, _y; osg::ref_ptr _node; SGVec3d _topLeft, _toRight, _toDown; @@ -304,6 +327,8 @@ SGPickAnimation::SGPickAnimation(simgear::SGTransientModelData &modelData) : SGAnimation(modelData) { + _condition = getCondition(); + std::vector names = modelData.getConfigNode()->getChildren("proxy-name"); for (unsigned i = 0; i < names.size(); ++i) { _proxyNames.push_back(names[i]->getStringValue()); @@ -451,7 +476,7 @@ std::vector actions; actions = getConfig()->getChildren("action"); for (unsigned int i = 0; i < actions.size(); ++i) { - pickCb = new PickCallback(actions[i], getModelRoot()); + pickCb = new PickCallback(actions[i], getModelRoot(), _condition); ud->addPickCallback(pickCb); } @@ -459,7 +484,7 @@ if (!pickCb) { // make a trivial PickCallback to hang the hovered off of SGPropertyNode_ptr dummyNode(new SGPropertyNode); - pickCb = new PickCallback(dummyNode.ptr(), getModelRoot()); + pickCb = new PickCallback(dummyNode.ptr(), getModelRoot(), _condition); ud->addPickCallback(pickCb); } @@ -469,7 +494,7 @@ // Look for the VNC sessions that want raw mouse input actions = getConfig()->getChildren("vncaction"); for (unsigned int i = 0; i < actions.size(); ++i) { - ud->addPickCallback(new VncCallback(actions[i], getModelRoot(), parent)); + ud->addPickCallback(new VncCallback(actions[i], getModelRoot(), parent, _condition)); } } @@ -508,11 +533,13 @@ KnobSliderPickCallback(const SGPropertyNode* configNode, - SGPropertyNode* modelRoot) : + SGPropertyNode* modelRoot, + SGSharedPtr condition) : SGPickCallback(PriorityPanel), _direction(DIRECTION_NONE), _repeatInterval(configNode->getDoubleValue("interval-sec", 0.1)), - _dragDirection(DRAG_DEFAULT) + _dragDirection(DRAG_DEFAULT), + _condition(condition) { readOptionalBindingList(configNode, modelRoot, "action", _action); readOptionalBindingList(configNode, modelRoot, "increase", _bindingsIncrease); @@ -560,41 +587,49 @@ const osgGA::GUIEventAdapter& ea, const Info& ) { - // the 'be nice to Mac / laptop' users option; alt-clicking spins the + if (!_condition || _condition->test()) { + // the 'be nice to Mac / laptop' users option; alt-clicking spins the // opposite direction. Should make this configurable - if ((button == 0) && (ea.getModKeyMask() & osgGA::GUIEventAdapter::MODKEY_ALT)) { - button = 1; - } - - int increaseMouseWheel = static_knobMouseWheelAlternateDirection ? 3 : 4; - int decreaseMouseWheel = static_knobMouseWheelAlternateDirection ? 4 : 3; - - _direction = DIRECTION_NONE; - if ((button == 0) || (button == increaseMouseWheel)) { - _direction = DIRECTION_INCREASE; - } else if ((button == 1) || (button == decreaseMouseWheel)) { - _direction = DIRECTION_DECREASE; - } else { - return false; + if ((button == 0) && (ea.getModKeyMask() & osgGA::GUIEventAdapter::MODKEY_ALT)) { + button = 1; + } + + int increaseMouseWheel = static_knobMouseWheelAlternateDirection ? 3 : 4; + int decreaseMouseWheel = static_knobMouseWheelAlternateDirection ? 4 : 3; + + _direction = DIRECTION_NONE; + if ((button == 0) || (button == increaseMouseWheel)) { + _direction = DIRECTION_INCREASE; + } + else if ((button == 1) || (button == decreaseMouseWheel)) { + _direction = DIRECTION_DECREASE; + } + else { + return false; + } + + _lastFirePos = eventToWindowCoords(ea); + // delay start of repeat, makes dragging more usable + _repeatTime = -_repeatInterval; + _hasDragged = false; + return true; } - - _lastFirePos = eventToWindowCoords(ea); - // delay start of repeat, makes dragging more usable - _repeatTime = -_repeatInterval; - _hasDragged = false; - return true; + return false; } virtual void buttonReleased( int keyModState, const osgGA::GUIEventAdapter&, const Info* ) { - // for *clicks*, we only fire on button release - if (!_hasDragged) { - fire(keyModState & osgGA::GUIEventAdapter::MODKEY_SHIFT, _direction); + if (!_condition || _condition->test()) { + + // for *clicks*, we only fire on button release + if (!_hasDragged) { + fire(keyModState & osgGA::GUIEventAdapter::MODKEY_SHIFT, _direction); + } + + fireBindingList(_releaseAction); } - - fireBindingList(_releaseAction); } DragDirection effectiveDragDirection() const @@ -611,32 +646,34 @@ virtual void mouseMoved( const osgGA::GUIEventAdapter& ea, const Info* ) { - _mousePos = eventToWindowCoords(ea); - osg::Vec2d deltaMouse = _mousePos - _lastFirePos; - - if (!_hasDragged) { - - double manhattanDist = deltaMouse.x() * deltaMouse.x() + deltaMouse.y() * deltaMouse.y(); - if (manhattanDist < 5) { - // don't do anything, just input noise - return; + if (!_condition || _condition->test()) { + _mousePos = eventToWindowCoords(ea); + osg::Vec2d deltaMouse = _mousePos - _lastFirePos; + + if (!_hasDragged) { + + double manhattanDist = deltaMouse.x() * deltaMouse.x() + deltaMouse.y() * deltaMouse.y(); + if (manhattanDist < 5) { + // don't do anything, just input noise + return; + } + + // user is dragging, disable repeat behaviour + _hasDragged = true; + } + + double delta = (effectiveDragDirection() == DRAG_VERTICAL) ? deltaMouse.y() : deltaMouse.x(); + // per-animation scale factor lets the aircraft author tune for expectations, + // eg heading setting vs 5-state switch. + // then we scale by a global sensitivity, which the user can set. + delta *= static_dragSensitivity / _dragScale; + + if (fabs(delta) >= 1.0) { + // determine direction from sign of delta + Direction dir = (delta > 0.0) ? DIRECTION_INCREASE : DIRECTION_DECREASE; + fire(ea.getModKeyMask() & osgGA::GUIEventAdapter::MODKEY_SHIFT, dir); + _lastFirePos = _mousePos; } - - // user is dragging, disable repeat behaviour - _hasDragged = true; - } - - double delta = (effectiveDragDirection() == DRAG_VERTICAL) ? deltaMouse.y() : deltaMouse.x(); - // per-animation scale factor lets the aircraft author tune for expectations, - // eg heading setting vs 5-state switch. - // then we scale by a global sensitivity, which the user can set. - delta *= static_dragSensitivity / _dragScale; - - if (fabs(delta) >= 1.0) { - // determine direction from sign of delta - Direction dir = (delta > 0.0) ? DIRECTION_INCREASE : DIRECTION_DECREASE; - fire(ea.getModKeyMask() & osgGA::GUIEventAdapter::MODKEY_SHIFT, dir); - _lastFirePos = _mousePos; } } @@ -656,15 +693,19 @@ virtual bool hover( const osg::Vec2d& windowPos, const Info& ) { - if (_hover.empty()) { - return false; + if (!_condition || _condition->test()) { + + if (_hover.empty()) { + return false; + } + + SGPropertyNode_ptr params(new SGPropertyNode); + params->setDoubleValue("x", windowPos.x()); + params->setDoubleValue("y", windowPos.y()); + fireBindingList(_hover, params.ptr()); + return true; } - - SGPropertyNode_ptr params(new SGPropertyNode); - params->setDoubleValue("x", windowPos.x()); - params->setDoubleValue("y", windowPos.y()); - fireBindingList(_hover, params.ptr()); - return true; + return false; } void setCursor(const std::string& aName) @@ -678,13 +719,15 @@ private: void fire(bool isShifted, Direction dir) { - const SGBindingList& act(isShifted ? _shiftedAction : _action); - const SGBindingList& incr(isShifted ? _shiftedIncrease : _bindingsIncrease); - const SGBindingList& decr(isShifted ? _shiftedDecrease : _bindingsDecrease); - - switch (dir) { + if (!_condition || _condition->test()) { + + const SGBindingList& act(isShifted ? _shiftedAction : _action); + const SGBindingList& incr(isShifted ? _shiftedIncrease : _bindingsIncrease); + const SGBindingList& decr(isShifted ? _shiftedDecrease : _bindingsDecrease); + + switch (dir) { case DIRECTION_INCREASE: - fireBindingListWithOffset(act, 1, 1); + fireBindingListWithOffset(act, 1, 1); fireBindingList(incr); break; case DIRECTION_DECREASE: @@ -692,6 +735,7 @@ fireBindingList(decr); break; default: break; + } } } @@ -707,6 +751,8 @@ double _repeatTime; DragDirection _dragDirection; + SGSharedPtr _condition; + bool _hasDragged; ///< has the mouse been dragged since the press? osg::Vec2d _mousePos, ///< current window coords location of the mouse _lastFirePos; ///< mouse location where we last fired the bindings @@ -719,19 +765,23 @@ class SGKnobAnimation::UpdateCallback : public osg::NodeCallback { public: - UpdateCallback(SGExpressiond const* animationValue) : + UpdateCallback(SGExpressiond const* animationValue, SGSharedPtr condition) : + _condition(condition), _animationValue(animationValue) { setName("SGKnobAnimation::UpdateCallback"); } virtual void operator()(osg::Node* node, osg::NodeVisitor* nv) { - SGRotateTransform* transform = static_cast(node); - transform->setAngleDeg(_animationValue->getValue()); - traverse(node, nv); + if (!_condition || _condition->test()) { + SGRotateTransform* transform = static_cast(node); + transform->setAngleDeg(_animationValue->getValue()); + traverse(node, nv); + } } private: + SGSharedPtr _condition; SGSharedPtr _animationValue; }; @@ -739,6 +789,7 @@ SGKnobAnimation::SGKnobAnimation(simgear::SGTransientModelData &modelData) : SGPickAnimation(modelData) { + _condition = getCondition(); SGSharedPtr value = read_value(modelData.getConfigNode(), modelData.getModelRoot(), "-deg", -SGLimitsd::max(), SGLimitsd::max()); _animationValue = value->simplify(); @@ -752,7 +803,7 @@ { SGRotateTransform* transform = new SGRotateTransform(); - UpdateCallback* uc = new UpdateCallback(_animationValue); + UpdateCallback* uc = new UpdateCallback(_animationValue, _condition); transform->setUpdateCallback(uc); transform->setCenter(_center); transform->setAxis(_axis); @@ -764,7 +815,7 @@ void SGKnobAnimation::setupCallbacks(SGSceneUserData* ud, osg::Group*) { - ud->setPickCallback(new KnobSliderPickCallback(getConfig(), getModelRoot())); + ud->setPickCallback(new KnobSliderPickCallback(getConfig(), getModelRoot(), _condition)); } void SGKnobAnimation::setAlternateMouseWheelDirection(bool aToggle) @@ -830,5 +881,159 @@ void SGSliderAnimation::setupCallbacks(SGSceneUserData* ud, osg::Group*) { - ud->setPickCallback(new KnobSliderPickCallback(getConfig(), getModelRoot())); + ud->setPickCallback(new KnobSliderPickCallback(getConfig(), getModelRoot(), _condition)); +} + +/* + * touch screen is a 2d surface that will pass parameters to the callbacks indicating the + * normalized coordinates of hover or touch. Touch is defined as a button click. + * For compatibility with touchscreen operations this class does not differentiate between + * which buttons are touched, simply because this isn't how touchscreens work. + * Some touchscreens (e.g. SAW) can have a Z-axis indicating the pressure. This is not + * simulated. + */ + + +/** +* Handle picking events on object with a canvas placed onto +*/ +class TouchPickCallback : public SGPickCallback { + public: + TouchPickCallback(const SGPropertyNode* configNode, + SGPropertyNode* modelRoot) : + SGPickCallback(PriorityPanel), + _repeatable(configNode->getBoolValue("repeatable", false)), + _repeatInterval(configNode->getDoubleValue("interval-sec", 0.1)) + { + std::vector bindings; + + bindings = configNode->getChildren("touch"); + for (unsigned int i = 0; i < bindings.size(); ++i) { + _touches.insert(bindings[i]->getIntValue()); + } + + _bindingsTouched = readBindingList(configNode->getChildren("binding"), modelRoot); + readOptionalBindingList(configNode, modelRoot, "mod-up", _bindingsReleased); + + if (configNode->hasChild("cursor")) { + _cursorName = configNode->getStringValue("cursor"); + } + } + + void addHoverBindings(const SGPropertyNode* hoverNode, + SGPropertyNode* modelRoot) + { + _hover = readBindingList(hoverNode->getChildren("binding"), modelRoot); + } + + virtual bool buttonPressed(int touchIdx, + const osgGA::GUIEventAdapter& event, + const Info& info) + { + if (_touches.find(touchIdx) == _touches.end()) { + return false; + } + + if (!anyBindingEnabled(_bindingsTouched)) { + return false; + } + SGPropertyNode_ptr params(new SGPropertyNode); + params->setDoubleValue("x", info.uv[0]); + params->setDoubleValue("y", info.uv[1]); + + _repeatTime = -_repeatInterval; // anti-bobble: delay start of repeat + fireBindingList(_bindingsTouched, params.ptr()); + return true; + } + virtual void buttonReleased(int keyModState, + const osgGA::GUIEventAdapter&, + const Info* info) + { + SG_UNUSED(keyModState); + SGPropertyNode_ptr params(new SGPropertyNode); + params->setDoubleValue("x", info->uv[0]); + params->setDoubleValue("y", info->uv[1]); + fireBindingList(_bindingsReleased, params.ptr()); + } + + virtual void update(double dt, int keyModState) + { + SG_UNUSED(keyModState); + if (!_repeatable) + return; + + _repeatTime += dt; + while (_repeatInterval < _repeatTime) { + _repeatTime -= _repeatInterval; + fireBindingList(_bindingsTouched); + } + } + + virtual bool hover(const osg::Vec2d& windowPos, + const Info& info) + { + if (!anyBindingEnabled(_hover)) { + return false; + } + + SGPropertyNode_ptr params(new SGPropertyNode); + params->setDoubleValue("x", info.uv[0]); + params->setDoubleValue("y", info.uv[1]); + fireBindingList(_hover, params.ptr()); + return true; + } + + std::string getCursor() const + { + return _cursorName; + } + + virtual bool needsUV() const { return true; } + + private: + SGBindingList _bindingsTouched; + SGBindingList _bindingsReleased; + SGBindingList _hover; + std::set _touches; + std::string _cursorName; + bool _repeatable; + double _repeatInterval; + double _repeatTime; +}; + +SGTouchAnimation::SGTouchAnimation(simgear::SGTransientModelData &modelData) : + SGPickAnimation(modelData) +{ +} + +osg::Group* SGTouchAnimation::createMainGroup(osg::Group* pr) +{ + SGRotateTransform* transform = new SGRotateTransform(); + pr->addChild(transform); + return transform; +} + +void SGTouchAnimation::setupCallbacks(SGSceneUserData* ud, osg::Group*) +{ + TouchPickCallback* touchCb = NULL; + + // add actions that become macro and command invocations + std::vector actions; + actions = getConfig()->getChildren("action"); + for (unsigned int i = 0; i < actions.size(); ++i) { + touchCb = new TouchPickCallback(actions[i], getModelRoot()); + ud->addPickCallback(touchCb); + } + + if (getConfig()->hasChild("hovered")) { + if (!touchCb) { + // make a trivial PickCallback to hang the hovered off of + SGPropertyNode_ptr dummyNode(new SGPropertyNode); + touchCb = new TouchPickCallback(dummyNode.ptr(), getModelRoot()); + ud->addPickCallback(touchCb); + } + + touchCb->addHoverBindings(getConfig()->getNode("hovered"), getModelRoot()); + } +// ud->setPickCallback(new TouchPickCallback(getConfig(), getModelRoot())); } diff -Nru simgear-2017.3.1+dfsg/simgear/scene/model/SGPickAnimation.hxx simgear-2018.1.1+dfsg/simgear/scene/model/SGPickAnimation.hxx --- simgear-2017.3.1+dfsg/simgear/scene/model/SGPickAnimation.hxx 2017-09-20 07:51:07.000000000 +0000 +++ simgear-2018.1.1+dfsg/simgear/scene/model/SGPickAnimation.hxx 2018-04-06 19:43:17.000000000 +0000 @@ -45,7 +45,8 @@ virtual osg::Group* createMainGroup(osg::Group* pr); - + SGSharedPtr _condition; + virtual void setupCallbacks(SGSceneUserData* ud, osg::Group* parent); private: class PickCallback; @@ -85,7 +86,8 @@ protected: virtual osg::Group* createMainGroup(osg::Group* pr); - + SGSharedPtr _condition; + virtual void setupCallbacks(SGSceneUserData* ud, osg::Group* parent); private: @@ -114,5 +116,19 @@ SGSharedPtr _animationValue; }; +class SGTouchAnimation : public SGPickAnimation +{ +public: + SGTouchAnimation(simgear::SGTransientModelData &modelData); + + +protected: + virtual osg::Group* createMainGroup(osg::Group* pr); + + virtual void setupCallbacks(SGSceneUserData* ud, osg::Group* parent); + +private: +}; + #endif // of SG_SCENE_PICK_ANIMATION_HXX diff -Nru simgear-2017.3.1+dfsg/simgear/scene/model/SGReaderWriterXML.cxx simgear-2018.1.1+dfsg/simgear/scene/model/SGReaderWriterXML.cxx --- simgear-2017.3.1+dfsg/simgear/scene/model/SGReaderWriterXML.cxx 2017-09-20 07:51:07.000000000 +0000 +++ simgear-2018.1.1+dfsg/simgear/scene/model/SGReaderWriterXML.cxx 2018-04-06 19:43:17.000000000 +0000 @@ -226,7 +226,7 @@ { string typeString(aNode->getStringValue("type")); // exclude these so we don't show yellow outlines in preview mode - return (typeString == "pick") || (typeString == "knob") || (typeString == "slider"); + return (typeString == "pick") || (typeString == "knob") || (typeString == "slider") || (typeString == "touch"); } }; diff -Nru simgear-2017.3.1+dfsg/simgear/scene/tgdb/pt_lights.cxx simgear-2018.1.1+dfsg/simgear/scene/tgdb/pt_lights.cxx --- simgear-2017.3.1+dfsg/simgear/scene/tgdb/pt_lights.cxx 2017-09-20 07:51:07.000000000 +0000 +++ simgear-2018.1.1+dfsg/simgear/scene/tgdb/pt_lights.cxx 2018-04-06 19:43:17.000000000 +0000 @@ -275,7 +275,7 @@ //stateSet->setMode(GL_LIGHTING, osg::StateAttribute::OFF); osg::DrawArrays* drawArrays; - drawArrays = new osg::DrawArrays(osg::PrimitiveSet::TRIANGLES, + drawArrays = new osg::DrawArrays(osg::PrimitiveSet::POINTS, 0, vertices->size()); geometry->addPrimitiveSet(drawArrays); return geometry; diff -Nru simgear-2017.3.1+dfsg/simgear/scene/tgdb/TreeBin.cxx simgear-2018.1.1+dfsg/simgear/scene/tgdb/TreeBin.cxx --- simgear-2017.3.1+dfsg/simgear/scene/tgdb/TreeBin.cxx 2017-09-20 07:51:07.000000000 +0000 +++ simgear-2018.1.1+dfsg/simgear/scene/tgdb/TreeBin.cxx 2018-04-06 19:43:17.000000000 +0000 @@ -202,33 +202,41 @@ Vec3 pos = toOsg(p); Vec3 ter = toOsg(t); unsigned int numDrawables = geode->getNumDrawables(); - Geometry* geom - = static_cast(geode->getDrawable(numDrawables - 1)); + Geometry* geom = static_cast(geode->getDrawable(numDrawables - 1)); Vec3Array* posArray = static_cast(geom->getColorArray()); Vec3Array* tnormalArray = NULL; - if (use_tree_shadows || use_tree_normals) - {tnormalArray = static_cast(geom->getSecondaryColorArray());} - if (posArray->size() - >= static_cast(geom->getVertexArray())->size()) { - Vec3Array* paramsArray - = static_cast(geom->getNormalArray()); + + if (use_tree_shadows || use_tree_normals) { + tnormalArray = static_cast(geom->getSecondaryColorArray()); + } + + if (posArray->size() >= static_cast(geom->getVertexArray())->size()) { + Vec3Array* paramsArray = static_cast(geom->getNormalArray()); Vec3 params = (*paramsArray)[0]; geom = createTreeGeometry(params.x(), params.y(), params.z()); posArray = static_cast(geom->getColorArray()); - if (use_tree_shadows || use_tree_normals) - {tnormalArray = static_cast(geom->getSecondaryColorArray());} + + if (use_tree_shadows || use_tree_normals) { + tnormalArray = static_cast(geom->getSecondaryColorArray()); + } geode->addDrawable(geom); } - posArray->insert(posArray->end(), 4, pos); - if (use_tree_shadows || use_tree_normals) - {tnormalArray->insert(tnormalArray->end(),4,ter);} - size_t numVerts = posArray->size(); - int imax = 2; - if (use_tree_shadows) {imax = 3;} - for (int i = 0; i < imax; ++i) { - DrawArrays* primSet - = static_cast(geom->getPrimitiveSet(i)); - primSet->setCount(numVerts); + + if (tnormalArray && (use_tree_shadows || use_tree_normals)) + tnormalArray->insert(tnormalArray->end(), 4, ter); + + if (posArray) + { + posArray->insert(posArray->end(), 4, pos); + + size_t numVerts = posArray->size(); + int imax = 2; + if (use_tree_shadows) { imax = 3; } + for (int i = 0; i < imax; ++i) { + DrawArrays* primSet = static_cast(geom->getPrimitiveSet(i)); + if(primSet != nullptr) + primSet->setCount(numVerts); + } } } diff -Nru simgear-2017.3.1+dfsg/simgear/scene/util/parse_color_test.cxx simgear-2018.1.1+dfsg/simgear/scene/util/parse_color_test.cxx --- simgear-2017.3.1+dfsg/simgear/scene/util/parse_color_test.cxx 2017-09-20 07:51:07.000000000 +0000 +++ simgear-2018.1.1+dfsg/simgear/scene/util/parse_color_test.cxx 2018-04-06 19:43:17.000000000 +0000 @@ -7,7 +7,7 @@ #include #include - +#include #define VERIFY_COLOR(str, r, g, b, a) \ SG_VERIFY(simgear::parseColor(str, color)) \ @@ -28,7 +28,7 @@ SGPropertyNode color_node, color_arg; color_arg.setStringValue("#000000"); - simgear::PropertyInterpolator* interp = new simgear::ColorInterpolator; + auto interp = std::unique_ptr(new simgear::ColorInterpolator); interp->reset(color_arg); interp->update(color_node, 0.5); // with no color it should immediately set to the target diff -Nru simgear-2017.3.1+dfsg/simgear/sg_inlines.h simgear-2018.1.1+dfsg/simgear/sg_inlines.h --- simgear-2017.3.1+dfsg/simgear/sg_inlines.h 2017-09-20 07:51:07.000000000 +0000 +++ simgear-2018.1.1+dfsg/simgear/sg_inlines.h 2018-04-06 19:43:17.000000000 +0000 @@ -23,6 +23,8 @@ // // $Id$ +#include +#include #ifndef _SG_INLINES_H #define _SG_INLINES_H @@ -108,5 +110,31 @@ #define SG_DISABLE_COPY(Class) \ Class(const Class &); \ Class &operator=(const Class &); - + +namespace simgear { + +// A swap() that is guaranteed to be 'noexcept' as long as compilation +// succeeds. Idea and implementation from +// . +template +void noexceptSwap(T& a, T& b) noexcept +{ + using std::swap; + static_assert(noexcept(swap(a, b)), "this swap() is not 'noexcept'" ); + swap(a, b); +} + +// Cast an enum value to its underlying type (useful with scoped enumerations). +// +// Example: enum class MyEnum { first = 1, second }; +// auto e = MyEnum::second; +// std::string msg = "MyEnum::second is " + +// std::to_string(simgear::enumValue(e)); +template +constexpr typename std::underlying_type::type enumValue(T e) { + return static_cast::type>(e); +} + +} // of namespace simgear + #endif // _SG_INLINES_H diff -Nru simgear-2017.3.1+dfsg/simgear/simgear_config_cmake.h.in simgear-2018.1.1+dfsg/simgear/simgear_config_cmake.h.in --- simgear-2017.3.1+dfsg/simgear/simgear_config_cmake.h.in 2017-09-20 07:51:07.000000000 +0000 +++ simgear-2018.1.1+dfsg/simgear/simgear_config_cmake.h.in 2018-04-06 19:43:17.000000000 +0000 @@ -11,10 +11,15 @@ #cmakedefine HAVE_TIMEGM #cmakedefine HAVE_STD_ISNAN +#cmakedefine HAVE_WORKING_STD_REGEX #cmakedefine HAVE_WINDOWS_H #cmakedefine HAVE_MKDTEMP #cmakedefine HAVE_AL_EXT_H - +#cmakedefine HAVE_STD_INDEX_SEQUENCE +#cmakedefine HAVE_STD_REMOVE_CV_T +#cmakedefine HAVE_STD_REMOVE_CVREF_T +#cmakedefine HAVE_STD_ENABLE_IF_T +#cmakedefine HAVE_STD_BOOL_CONSTANT #cmakedefine GCC_ATOMIC_BUILTINS_FOUND diff -Nru simgear-2017.3.1+dfsg/simgear/sound/aeonwave_test1.cxx simgear-2018.1.1+dfsg/simgear/sound/aeonwave_test1.cxx --- simgear-2017.3.1+dfsg/simgear/sound/aeonwave_test1.cxx 2017-09-20 07:51:07.000000000 +0000 +++ simgear-2018.1.1+dfsg/simgear/sound/aeonwave_test1.cxx 2018-04-06 19:43:17.000000000 +0000 @@ -66,15 +66,15 @@ std::cout << "Vendor: " << _vendor << std::endl; std::cout << "Renderer: " << _renderer << std::endl; - aax::Matrix64 mtx64; - mtx64.translate(-5000.0, 12500.0, 1000.0); + aax::Matrix mtx; + mtx.translate(-5000.0, 12500.0, 1000.0); - aax::Matrix mtx = mtx64.toMatrix(); - emitter.matrix(mtx); + aax::Matrix64 mtx64 = mtx.toMatrix64(); + emitter.matrix(mtx64); mtx.translate(-5.0, 2.0, 1.0); - mtx.inverse(); - aax.sensor_matrix(mtx); + mtx64.inverse(); + aax.sensor_matrix(mtx64); aax.set(AAX_PLAYING); emitter.set(AAX_PLAYING); diff -Nru simgear-2017.3.1+dfsg/simgear/sound/CMakeLists.txt simgear-2018.1.1+dfsg/simgear/sound/CMakeLists.txt --- simgear-2017.3.1+dfsg/simgear/sound/CMakeLists.txt 2017-09-20 07:51:07.000000000 +0000 +++ simgear-2018.1.1+dfsg/simgear/sound/CMakeLists.txt 2018-04-06 19:43:17.000000000 +0000 @@ -4,16 +4,15 @@ sample.hxx sample_group.hxx xmlsound.hxx - readwav.hxx soundmgr.hxx + filters.hxx ) set(SOURCES sample.cxx sample_group.cxx xmlsound.cxx - readwav.cxx - soundmgr_openal_private.hxx + filters.cxx ) if (USE_AEONWAVE) @@ -21,8 +20,13 @@ soundmgr_aeonwave.cxx ) else() + set(HEADERS ${HEADERS} + readwav.hxx + ) set(SOURCES ${SOURCES} soundmgr_openal.cxx + soundmgr_openal_private.hxx + readwav.cxx ) endif() diff -Nru simgear-2017.3.1+dfsg/simgear/sound/filters.cxx simgear-2018.1.1+dfsg/simgear/sound/filters.cxx --- simgear-2017.3.1+dfsg/simgear/sound/filters.cxx 1970-01-01 00:00:00.000000000 +0000 +++ simgear-2018.1.1+dfsg/simgear/sound/filters.cxx 2018-04-06 19:43:17.000000000 +0000 @@ -0,0 +1,169 @@ +/* + * Copyright 2007-2017 by Erik Hofman. + * Copyright 2009-2017 by Adalin B.V. + * + * This file is part of SimGear + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the Lesser GNU 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 General Public License for more details. + * + * You should have received a copy of the Lesser GNU General Public License + * along with this program; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +#include +#include + +#include +#include "filters.hxx" + +namespace simgear { + +FreqFilter::FreqFilter(int order, float sample_freq, float cutoff_freq, float Qfactor) { + + Q = Qfactor; + fs = sample_freq; + no_stages = order / 2; + gain = order; + + butterworth_compute(cutoff_freq); + + for (unsigned int i = 0; i < 2*SG_FREQFILTER_MAX_STAGES; ++i) { + hist[i] = 0.0f; + } +} + +FreqFilter::~FreqFilter() { +} + +void FreqFilter::update( int16_t *data, unsigned int num) { + + if (num) { + float k = gain; + for (unsigned int stage = 0; stage < no_stages; ++stage) { + + float h0 = hist[2*stage + 0]; + float h1 = hist[2*stage + 1]; + unsigned int i = num; + do { + float nsmp, smp = data[i] * k; + smp = smp + h0 * coeff[4*stage + 0]; + nsmp = smp + h1 * coeff[4*stage + 1]; + smp = nsmp + h0 * coeff[4*stage + 2]; + smp = smp + h1 * coeff[4*stage + 3]; + + h1 = h0; + h0 = nsmp; + data[i] = smp; + } + while (--i); + + hist[2*stage + 0] = h0; + hist[2*stage + 1] = h1; + k = 1.0f; + } + } +} + +inline void FreqFilter::bilinear(float a0, float a1, float a2, + float b0, float b1, float b2, + float *k, int stage) { + a2 *= 4.0f; + b2 *= 4.0f; + a1 *= 2.0f; + b1 *= 2.0f; + + float ad = a2 + a1 + a0; + float bd = b2 + b1 + b0; + + *k *= ad/bd; + + coeff[4*stage + 0] = 2.0f*(-b2 + b0) / bd; + coeff[4*stage + 1] = (b2 - b1 + b0) / bd; + coeff[4*stage + 2] = 2.0f*(-a2 + a0) / ad; + coeff[4*stage + 3] = (a2 - a1 + a0) / ad; + + // negate to prevent this is required every time the filter is applied. + coeff[4*stage + 0] = -coeff[4*stage + 0]; + coeff[4*stage + 1] = -coeff[4*stage + 1]; +} + +// convert from S-domain to Z-domain +inline void FreqFilter::bilinear_s2z(float *a0, float *a1, float *a2, + float *b0, float *b1, float *b2, + float fc, float fs, float *k, int stage) { + // prewarp + float wp = 2.0f*tanf(SG_PI * fc/fs); + + *a2 /= wp*wp; + *b2 /= wp*wp; + *a1 /= wp; + *b1 /= wp; + + bilinear(*a0, *a1, *a2, *b0, *b1, *b2, k, stage); +} + +void FreqFilter::butterworth_compute(float fc) { + + // http://www.ti.com/lit/an/sloa049b/sloa049b.pdf + static const float _Q[SG_FREQFILTER_MAX_STAGES][SG_FREQFILTER_MAX_STAGES]= { + { 0.7071f, 1.0f, 1.0f, 1.0f }, // 2nd order + { 0.5412f, 1.3605f, 1.0f, 1.0f }, // 4th order + { 0.5177f, 0.7071f, 1.9320f, 1.0f }, // 6th roder + { 0.5098f, 0.6013f, 0.8999f, 2.5628f } // 8th order + }; + + gain = 1.0f; + + int pos = no_stages-1; + for (unsigned i = 0; i < no_stages; ++i) + { + float a2 = 0.0f; + float a1 = 0.0f; + float a0 = 1.0f; + + float b2 = 1.0f; + float b1 = 1.0f/(_Q[pos][i] * Q); + float b0 = 1.0f; + + // fill the filter coefficients + bilinear_s2z(&a0, &a1, &a2, &b0, &b1, &b2, fc, fs, &gain, i); + } +} + + + +BitCrusher::BitCrusher(float level) { + + float bits = level * 15.0f; + factor = powf(2.0f, bits); + devider = 1.0f/factor; +} + +BitCrusher::~BitCrusher() { +} + +void BitCrusher::update( int16_t *data, unsigned int num ) { + + if (num && factor < 1.0f) { + unsigned int i = num; + do { + float integral; + + modff(data[i]*devider, &integral); + data[i] = integral*factor; + } while (--i); + } +} + +}; // namespace simgear + diff -Nru simgear-2017.3.1+dfsg/simgear/sound/filters.hxx simgear-2018.1.1+dfsg/simgear/sound/filters.hxx --- simgear-2017.3.1+dfsg/simgear/sound/filters.hxx 1970-01-01 00:00:00.000000000 +0000 +++ simgear-2018.1.1+dfsg/simgear/sound/filters.hxx 2018-04-06 19:43:17.000000000 +0000 @@ -0,0 +1,77 @@ +/* + * Copyright 2007-2017 by Erik Hofman. + * Copyright 2009-2017 by Adalin B.V. + * + * This file is part of SimGear + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the Lesser GNU 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 General Public License for more details. + * + * You should have received a copy of the Lesser GNU General Public License + * along with this program; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +#ifndef _SIMGEAR_FREQUENCY_FILTER_HXX +#define _SIMGEAR_FREQUENCY_FILTER_HXX + +#include + +namespace simgear { + +// Every stage is a 2nd order filter +// Four stages therefore equals to an 8th order filter with a 48dB/oct slope. +#define SG_FREQFILTER_MAX_STAGES 4 + +class FreqFilter { + +private: + + float fs, Q, gain; + float coeff[4*SG_FREQFILTER_MAX_STAGES]; + float hist[2*SG_FREQFILTER_MAX_STAGES]; + unsigned char no_stages; + + void butterworth_compute(float fc); + void bilinear(float a0, float a1, float a2, + float b0, float b1, float b2, + float *k, int stage); + void bilinear_s2z(float *a0, float *a1, float *a2, + float *b0, float *b1, float *b2, + float fc, float fs, float *k, int stage); + +public: + + FreqFilter(int order, float fs, float cutoff, float Qfactor = 1.0f); + ~FreqFilter(); + + void update( int16_t *data, unsigned int num ); +}; + + + +class BitCrusher { + +private: + float factor, devider; + +public: + + // level ranges from 0.0f (all muted) to 1.0f (no change) + BitCrusher(float level); + ~BitCrusher(); + + void update( int16_t *data, unsigned int num ); +}; + +}; // namespace simgear + +#endif // _SIMGEAR_FREQUENCY_FILTER_HXX diff -Nru simgear-2017.3.1+dfsg/simgear/sound/sample_group.cxx simgear-2018.1.1+dfsg/simgear/sound/sample_group.cxx --- simgear-2017.3.1+dfsg/simgear/sound/sample_group.cxx 2017-09-20 07:51:07.000000000 +0000 +++ simgear-2018.1.1+dfsg/simgear/sound/sample_group.cxx 2018-04-06 19:43:17.000000000 +0000 @@ -137,10 +137,8 @@ _changed = false; } - sample_map_iterator sample_current = _samples.begin(); - sample_map_iterator sample_end = _samples.end(); - for ( ; sample_current != sample_end; ++sample_current ) { - SGSoundSample *sample = sample_current->second; + for (auto current =_samples.begin(); current !=_samples.end(); ++current) { + SGSoundSample *sample = current->second; if ( !sample->is_valid_source() && sample->is_playing() && !sample->test_out_of_range()) { start_playing_sample(sample); @@ -156,7 +154,7 @@ bool SGSampleGroup::add( SGSharedPtr sound, const std::string& refname ) { - sample_map_iterator sample_it = _samples.find( refname ); + auto sample_it = _samples.find( refname ); if ( sample_it != _samples.end() ) { // sample name already exists return false; @@ -170,7 +168,7 @@ // remove a sound effect, return true if successful bool SGSampleGroup::remove( const std::string &refname ) { - sample_map_iterator sample_it = _samples.find( refname ); + auto sample_it = _samples.find( refname ); if ( sample_it == _samples.end() ) { // sample was not found return false; @@ -187,7 +185,7 @@ // return true of the specified sound exists in the sound manager system bool SGSampleGroup::exists( const std::string &refname ) { - sample_map_iterator sample_it = _samples.find( refname ); + auto sample_it = _samples.find( refname ); if ( sample_it == _samples.end() ) { // sample was not found return false; @@ -200,7 +198,7 @@ // return a pointer to the SGSoundSample if the specified sound exists // in the sound manager system, otherwise return NULL SGSoundSample *SGSampleGroup::find( const std::string &refname ) { - sample_map_iterator sample_it = _samples.find( refname ); + auto sample_it = _samples.find( refname ); if ( sample_it == _samples.end() ) { // sample was not found return NULL; @@ -214,10 +212,8 @@ SGSampleGroup::stop () { _pause = true; - sample_map_iterator sample_current = _samples.begin(); - sample_map_iterator sample_end = _samples.end(); - for ( ; sample_current != sample_end; ++sample_current ) { - SGSoundSample *sample = sample_current->second; + for (auto current =_samples.begin(); current !=_samples.end(); ++current) { + SGSoundSample *sample = current->second; _smgr->sample_destroy( sample ); } } @@ -228,11 +224,9 @@ { if (_active && _pause == false) { _pause = true; - sample_map_iterator sample_current = _samples.begin(); - sample_map_iterator sample_end = _samples.end(); - for ( ; sample_current != sample_end; ++sample_current ) { + for (auto current =_samples.begin(); current !=_samples.end(); ++current) { #ifdef ENABLE_SOUND - SGSoundSample *sample = sample_current->second; + SGSoundSample *sample = current->second; _smgr->sample_suspend( sample ); #endif } @@ -246,10 +240,8 @@ { if (_active && _pause == true) { #ifdef ENABLE_SOUND - sample_map_iterator sample_current = _samples.begin(); - sample_map_iterator sample_end = _samples.end(); - for ( ; sample_current != sample_end; ++sample_current ) { - SGSoundSample *sample = sample_current->second; + for (auto current =_samples.begin(); current !=_samples.end(); ++current) { + SGSoundSample *sample = current->second; _smgr->sample_resume( sample ); } testForMgrError("resume"); @@ -319,10 +311,8 @@ velocity = toVec3f( hlOr.backTransform(_velocity*SG_FEET_TO_METER) ); } - sample_map_iterator sample_current = _samples.begin(); - sample_map_iterator sample_end = _samples.end(); - for ( ; sample_current != sample_end; ++sample_current ) { - SGSoundSample *sample = sample_current->second; + for (auto current =_samples.begin(); current !=_samples.end(); ++current ) { + SGSoundSample *sample = current->second; sample->set_master_volume( _volume ); sample->set_orientation( _orientation ); sample->set_rotation( ec2body ); diff -Nru simgear-2017.3.1+dfsg/simgear/sound/sample_group.hxx simgear-2018.1.1+dfsg/simgear/sound/sample_group.hxx --- simgear-2017.3.1+dfsg/simgear/sound/sample_group.hxx 2017-09-20 07:51:07.000000000 +0000 +++ simgear-2018.1.1+dfsg/simgear/sound/sample_group.hxx 2018-04-06 19:43:17.000000000 +0000 @@ -40,8 +40,6 @@ typedef std::map < std::string, SGSharedPtr > sample_map; -typedef sample_map::iterator sample_map_iterator; -typedef sample_map::const_iterator const_sample_map_iterator; class SGSoundMgr; diff -Nru simgear-2017.3.1+dfsg/simgear/sound/soundmgr_aeonwave.cxx simgear-2018.1.1+dfsg/simgear/sound/soundmgr_aeonwave.cxx --- simgear-2017.3.1+dfsg/simgear/sound/soundmgr_aeonwave.cxx 2017-09-20 07:51:07.000000000 +0000 +++ simgear-2018.1.1+dfsg/simgear/sound/soundmgr_aeonwave.cxx 2018-04-06 19:43:17.000000000 +0000 @@ -36,7 +36,6 @@ #include #include -#include #include #include "soundmgr.hxx" @@ -50,17 +49,10 @@ // We keep track of the emitters ourselves. typedef std::map < unsigned int, aax::Emitter > source_map; -typedef source_map::iterator source_map_iterator; -typedef source_map::const_iterator const_source_map_iterator; // The AeonWave class keeps track of the buffers, so use a reference instead. typedef std::map < unsigned int, aax::Buffer& > buffer_map; -typedef buffer_map::iterator buffer_map_iterator; -typedef buffer_map::const_iterator const_buffer_map_iterator; - typedef std::map < std::string, SGSharedPtr > sample_group_map; -typedef sample_group_map::iterator sample_group_map_iterator; -typedef sample_group_map::const_iterator const_sample_group_map_iterator; #ifndef NDEBUG # define TRY(a) if ((a) == 0) printf("%i: %s\n", __LINE__, d->_aax.strerror()) @@ -83,8 +75,7 @@ ~SoundManagerPrivate() { - std::vector::iterator it; - for (it = _devices.begin(); it != _devices.end(); ++it) { + for (auto it = _devices.begin(); it != _devices.end(); ++it) { free((void*)*it); } _devices.clear(); @@ -92,14 +83,14 @@ } void init() { - _mtx = aax::Matrix(); + _mtx = aax::Matrix64(); } void update_pos_and_orientation() { SGVec3d sgv_at = _orientation.backTransform(-SGVec3d::e3()); SGVec3d sgv_up = _orientation.backTransform(SGVec3d::e2()); - SGVec3f pos = SGVec3f::zeros(); + SGVec3d pos = SGVec3d::zeros(); _mtx.set(pos.data(), toVec3f(sgv_at).data(), toVec3f(sgv_up).data()); @@ -107,7 +98,7 @@ } aax::AeonWave _aax; - aax::Matrix _mtx; + aax::Matrix64 _mtx; SGVec3d _absolute_pos; SGVec3d _base_pos; @@ -117,7 +108,7 @@ buffer_map _buffers; aax::Buffer nullBuffer; aax::Buffer& get_buffer(unsigned int id) { - buffer_map_iterator buffer_it = _buffers.find(id); + auto buffer_it = _buffers.find(id); if ( buffer_it != _buffers.end() ) return buffer_it->second; SG_LOG(SG_SOUND, SG_ALERT, "unknown buffer id requested."); return nullBuffer; @@ -127,7 +118,7 @@ source_map _sources; aax::Emitter nullEmitter; aax::Emitter& get_emitter(unsigned int id) { - source_map_iterator source_it = _sources.find(id); + auto source_it = _sources.find(id); if ( source_it != _sources.end() ) return source_it->second; SG_LOG(SG_SOUND, SG_ALERT, "unknown source id requested."); return nullEmitter; @@ -149,6 +140,7 @@ _changed(true), _volume(0.0), _velocity(SGVec3d::zeros()), + _bad_doppler(false), _renderer("unknown"), _vendor("unknown") { @@ -243,10 +235,9 @@ if ( is_working() ) { _active = true; - sample_group_map_iterator sample_grp_current = d->_sample_groups.begin(); - sample_group_map_iterator sample_grp_end = d->_sample_groups.end(); - for ( ; sample_grp_current != sample_grp_end; ++sample_grp_current ) { - SGSampleGroup *sgrp = sample_grp_current->second; + for ( auto current = d->_sample_groups.begin(); + current != d->_sample_groups.end(); ++current ) { + SGSampleGroup *sgrp = current->second; sgrp->activate(); } } @@ -258,10 +249,9 @@ { #ifdef ENABLE_SOUND // first stop all sample groups - sample_group_map_iterator sample_grp_current = d->_sample_groups.begin(); - sample_group_map_iterator sample_grp_end = d->_sample_groups.end(); - for ( ; sample_grp_current != sample_grp_end; ++sample_grp_current ) { - SGSampleGroup *sgrp = sample_grp_current->second; + for ( auto current = d->_sample_groups.begin(); + current != d->_sample_groups.end(); ++ current ) { + SGSampleGroup *sgrp = current->second; sgrp->stop(); } @@ -285,10 +275,9 @@ { #ifdef ENABLE_SOUND if (is_working()) { - sample_group_map_iterator sample_grp_current = d->_sample_groups.begin(); - sample_group_map_iterator sample_grp_end = d->_sample_groups.end(); - for ( ; sample_grp_current != sample_grp_end; ++sample_grp_current ) { - SGSampleGroup *sgrp = sample_grp_current->second; + for (auto current = d->_sample_groups.begin(); + current != d->_sample_groups.end(); ++current ) { + SGSampleGroup *sgrp = current->second; sgrp->stop(); } _active = false; @@ -300,10 +289,9 @@ { #ifdef ENABLE_SOUND if (is_working()) { - sample_group_map_iterator sample_grp_current = d->_sample_groups.begin(); - sample_group_map_iterator sample_grp_end = d->_sample_groups.end(); - for ( ; sample_grp_current != sample_grp_end; ++sample_grp_current ) { - SGSampleGroup *sgrp = sample_grp_current->second; + for ( auto current = d->_sample_groups.begin(); + current != d->_sample_groups.end(); ++current ) { + SGSampleGroup *sgrp = current->second; sgrp->resume(); } _active = true; @@ -320,10 +308,9 @@ d->update_pos_and_orientation(); } - sample_group_map_iterator sample_grp_current = d->_sample_groups.begin(); - sample_group_map_iterator sample_grp_end = d->_sample_groups.end(); - for ( ; sample_grp_current != sample_grp_end; ++sample_grp_current ) { - SGSampleGroup *sgrp = sample_grp_current->second; + for ( auto current = d->_sample_groups.begin(); + current != d->_sample_groups.end(); ++current ) { + SGSampleGroup *sgrp = current->second; sgrp->update(dt); } @@ -338,7 +325,7 @@ TRY( dsp.set(AAX_SOUND_VELOCITY, 340.3f) ); TRY( d->_aax.set(dsp) ); #endif - aax::Matrix mtx = d->_mtx; + aax::Matrix64 mtx = d->_mtx; mtx.inverse(); TRY( d->_aax.sensor_matrix(mtx) ); @@ -360,7 +347,7 @@ // add a sample group, return true if successful bool SGSoundMgr::add( SGSampleGroup *sgrp, const std::string& refname ) { - sample_group_map_iterator sample_grp_it = d->_sample_groups.find( refname ); + auto sample_grp_it = d->_sample_groups.find( refname ); if ( sample_grp_it != d->_sample_groups.end() ) { // sample group already exists return false; @@ -376,7 +363,7 @@ // remove a sound effect, return true if successful bool SGSoundMgr::remove( const std::string &refname ) { - sample_group_map_iterator sample_grp_it = d->_sample_groups.find( refname ); + auto sample_grp_it = d->_sample_groups.find( refname ); if ( sample_grp_it == d->_sample_groups.end() ) { // sample group was not found. return false; @@ -390,7 +377,7 @@ // return true of the specified sound exists in the sound manager system bool SGSoundMgr::exists( const std::string &refname ) { - sample_group_map_iterator sample_grp_it = d->_sample_groups.find( refname ); + auto sample_grp_it = d->_sample_groups.find( refname ); return ( sample_grp_it != d->_sample_groups.end() ); } @@ -398,7 +385,7 @@ // return a pointer to the SGSampleGroup if the specified sound exists // in the sound manager system, otherwise return NULL SGSampleGroup *SGSoundMgr::find( const std::string &refname, bool create ) { - sample_group_map_iterator sample_grp_it = d->_sample_groups.find( refname ); + auto sample_grp_it = d->_sample_groups.find( refname ); if ( sample_grp_it == d->_sample_groups.end() ) { // sample group was not found. if (create) { @@ -432,7 +419,7 @@ // Free up a source id void SGSoundMgr::release_source( unsigned int source ) { - source_map_iterator source_it = d->_sources.find(source); + auto source_it = d->_sources.find(source); if ( source_it != d->_sources.end() ) { aax::Emitter& emitter = source_it->second; @@ -465,7 +452,7 @@ } bufid = d->_buffer_id++; - d->_buffers.insert( std::make_pair(bufid,buf) ); + d->_buffers.insert( {bufid, buf} ); if ( !sample->is_file() ) { enum aaxFormat format = AAX_FORMAT_NONE; @@ -493,7 +480,7 @@ unsigned int no_samples = sample->get_no_samples(); unsigned int no_tracks = sample->get_no_tracks(); unsigned int frequency = sample->get_frequency(); - TRY( buf.set(d->_aax, no_samples, no_tracks, format) ); + buf.set(d->_aax, no_samples, no_tracks, format); TRY( buf.set(AAX_FREQUENCY, frequency) ); TRY( buf.fill(sample->get_data()) ); @@ -527,7 +514,7 @@ if ( !sample->is_queue() ) { unsigned int buffer = sample->get_buffer(); - buffer_map_iterator buffer_it = d->_buffers.find(buffer); + auto buffer_it = d->_buffers.find(buffer); if ( buffer_it != d->_buffers.end() ) { sample->no_valid_buffer(); @@ -659,11 +646,11 @@ aax::Emitter& emitter = d->get_emitter(sample->get_source()); aax::dsp dsp; - aax::Vector pos = toVec3f(position).data(); + aax::Vector64 pos = position.data(); aax::Vector ori = orientation.data(); aax::Vector vel = velocity.data(); - aax::Matrix mtx(pos, ori); + aax::Matrix64 mtx(pos, ori); TRY( emitter.matrix(mtx) ); TRY( emitter.velocity(vel) ); @@ -763,7 +750,8 @@ SGVec3f SGSoundMgr::get_direction() const { - aaxVec3f pos, at, up; + aaxVec3f at, up; + aaxVec3d pos; d->_mtx.get(pos, at, up); return SGVec3f( at ); } diff -Nru simgear-2017.3.1+dfsg/simgear/sound/soundmgr_openal.cxx simgear-2018.1.1+dfsg/simgear/sound/soundmgr_openal.cxx --- simgear-2017.3.1+dfsg/simgear/sound/soundmgr_openal.cxx 2017-09-20 07:51:07.000000000 +0000 +++ simgear-2018.1.1+dfsg/simgear/sound/soundmgr_openal.cxx 2018-04-06 19:43:17.000000000 +0000 @@ -36,8 +36,6 @@ #include #include -#include - #include "soundmgr.hxx" #include "readwav.hxx" #include "soundmgr_openal_private.hxx" @@ -283,10 +281,9 @@ if ( is_working() ) { _active = true; - sample_group_map_iterator sample_grp_current = d->_sample_groups.begin(); - sample_group_map_iterator sample_grp_end = d->_sample_groups.end(); - for ( ; sample_grp_current != sample_grp_end; ++sample_grp_current ) { - SGSampleGroup *sgrp = sample_grp_current->second; + for ( auto current = d->_sample_groups.begin(); + current != d->_sample_groups.end(); ++current ) { + SGSampleGroup *sgrp = current->second; sgrp->activate(); } } @@ -298,25 +295,23 @@ { #ifdef ENABLE_SOUND // first stop all sample groups - sample_group_map_iterator sample_grp_current = d->_sample_groups.begin(); - sample_group_map_iterator sample_grp_end = d->_sample_groups.end(); - for ( ; sample_grp_current != sample_grp_end; ++sample_grp_current ) { - SGSampleGroup *sgrp = sample_grp_current->second; + for ( auto current = d->_sample_groups.begin(); + current != d->_sample_groups.end(); ++current ) { + SGSampleGroup *sgrp = current->second; sgrp->stop(); } // clear all OpenAL sources - BOOST_FOREACH(ALuint source, d->_free_sources) { + for(ALuint source : d->_free_sources) { alDeleteSources( 1 , &source ); testForError("SGSoundMgr::stop: delete sources"); } d->_free_sources.clear(); // clear any OpenAL buffers before shutting down - buffer_map_iterator buffers_current = d->_buffers.begin(); - buffer_map_iterator buffers_end = d->_buffers.end(); - for ( ; buffers_current != buffers_end; ++buffers_current ) { - refUint ref = buffers_current->second; + for ( auto current = d->_buffers.begin(); + current != d->_buffers.begin(); ++current ) { + refUint ref = current->second; ALuint buffer = ref.id; alDeleteBuffers(1, &buffer); testForError("SGSoundMgr::stop: delete buffers"); @@ -342,10 +337,9 @@ { #ifdef ENABLE_SOUND if (is_working()) { - sample_group_map_iterator sample_grp_current = d->_sample_groups.begin(); - sample_group_map_iterator sample_grp_end = d->_sample_groups.end(); - for ( ; sample_grp_current != sample_grp_end; ++sample_grp_current ) { - SGSampleGroup *sgrp = sample_grp_current->second; + for ( auto current = d->_sample_groups.begin(); + current != d->_sample_groups.end(); ++current ) { + SGSampleGroup *sgrp = current->second; sgrp->stop(); } _active = false; @@ -357,10 +351,9 @@ { #ifdef ENABLE_SOUND if (is_working()) { - sample_group_map_iterator sample_grp_current = d->_sample_groups.begin(); - sample_group_map_iterator sample_grp_end = d->_sample_groups.end(); - for ( ; sample_grp_current != sample_grp_end; ++sample_grp_current ) { - SGSampleGroup *sgrp = sample_grp_current->second; + for ( auto current = d->_sample_groups.begin(); + current != d->_sample_groups.end(); ++current ) { + SGSampleGroup *sgrp = current->second; sgrp->resume(); } _active = true; @@ -379,10 +372,9 @@ d->update_pos_and_orientation(); } - sample_group_map_iterator sample_grp_current = d->_sample_groups.begin(); - sample_group_map_iterator sample_grp_end = d->_sample_groups.end(); - for ( ; sample_grp_current != sample_grp_end; ++sample_grp_current ) { - SGSampleGroup *sgrp = sample_grp_current->second; + for ( auto current = d->_sample_groups.begin(); + current != d->_sample_groups.end(); ++current ) { + SGSampleGroup *sgrp = current->second; sgrp->update(dt); } @@ -420,7 +412,7 @@ // add a sample group, return true if successful bool SGSoundMgr::add( SGSampleGroup *sgrp, const std::string& refname ) { - sample_group_map_iterator sample_grp_it = d->_sample_groups.find( refname ); + auto sample_grp_it = d->_sample_groups.find( refname ); if ( sample_grp_it != d->_sample_groups.end() ) { // sample group already exists return false; @@ -436,7 +428,7 @@ // remove a sound effect, return true if successful bool SGSoundMgr::remove( const std::string &refname ) { - sample_group_map_iterator sample_grp_it = d->_sample_groups.find( refname ); + auto sample_grp_it = d->_sample_groups.find( refname ); if ( sample_grp_it == d->_sample_groups.end() ) { // sample group was not found. return false; @@ -450,7 +442,7 @@ // return true of the specified sound exists in the sound manager system bool SGSoundMgr::exists( const std::string &refname ) { - sample_group_map_iterator sample_grp_it = d->_sample_groups.find( refname ); + auto sample_grp_it = d->_sample_groups.find( refname ); return ( sample_grp_it != d->_sample_groups.end() ); } @@ -458,7 +450,7 @@ // return a pointer to the SGSampleGroup if the specified sound exists // in the sound manager system, otherwise return NULL SGSampleGroup *SGSoundMgr::find( const std::string &refname, bool create ) { - sample_group_map_iterator sample_grp_it = d->_sample_groups.find( refname ); + auto sample_grp_it = d->_sample_groups.find( refname ); if ( sample_grp_it == d->_sample_groups.end() ) { // sample group was not found. if (create) { @@ -508,9 +500,7 @@ // Free up a source id for further use void SGSoundMgr::release_source( unsigned int source ) { - vector::iterator it; - - it = std::find(d->_sources_in_use.begin(), d->_sources_in_use.end(), source); + auto it = std::find(d->_sources_in_use.begin(), d->_sources_in_use.end(), source); if ( it != d->_sources_in_use.end() ) { #ifdef ENABLE_SOUND ALint result; @@ -538,7 +528,7 @@ void *sample_data = NULL; // see if the sample name is already cached - buffer_map_iterator buffer_it = d->_buffers.find( sample_name ); + auto buffer_it = d->_buffers.find( sample_name ); if ( buffer_it != d->_buffers.end() ) { buffer_it->second.refctr++; buffer = buffer_it->second.id; @@ -632,7 +622,7 @@ if ( !sample->is_queue() ) { std::string sample_name = sample->get_sample_name(); - buffer_map_iterator buffer_it = d->_buffers.find( sample_name ); + auto buffer_it = d->_buffers.find( sample_name ); if ( buffer_it == d->_buffers.end() ) { // buffer was not found return; diff -Nru simgear-2017.3.1+dfsg/simgear/sound/soundmgr_openal_private.hxx simgear-2018.1.1+dfsg/simgear/sound/soundmgr_openal_private.hxx --- simgear-2017.3.1+dfsg/simgear/sound/soundmgr_openal_private.hxx 2017-09-20 07:51:07.000000000 +0000 +++ simgear-2018.1.1+dfsg/simgear/sound/soundmgr_openal_private.hxx 2018-04-06 19:43:17.000000000 +0000 @@ -64,12 +64,7 @@ }; typedef std::map < std::string, refUint > buffer_map; -typedef buffer_map::iterator buffer_map_iterator; -typedef buffer_map::const_iterator const_buffer_map_iterator; - typedef std::map < std::string, SGSharedPtr > sample_group_map; -typedef sample_group_map::iterator sample_group_map_iterator; -typedef sample_group_map::const_iterator const_sample_group_map_iterator; inline bool isNaN(float *v) { return (SGMisc::isNaN(v[0]) || SGMisc::isNaN(v[1]) || SGMisc::isNaN(v[2])); diff -Nru simgear-2017.3.1+dfsg/simgear/std/CMakeLists.txt simgear-2018.1.1+dfsg/simgear/std/CMakeLists.txt --- simgear-2017.3.1+dfsg/simgear/std/CMakeLists.txt 1970-01-01 00:00:00.000000000 +0000 +++ simgear-2018.1.1+dfsg/simgear/std/CMakeLists.txt 2018-04-06 19:43:17.000000000 +0000 @@ -0,0 +1,21 @@ +include (SimGearComponent) + +set(HEADERS + integer_sequence.hxx + type_traits.hxx +) + +set(SOURCES +) + +simgear_component(std std "${SOURCES}" "${HEADERS}") + +if(ENABLE_TESTS) + add_executable(test_integer_sequence integer_sequence_test.cxx) + add_test(integer_sequence ${EXECUTABLE_OUTPUT_PATH}/test_integer_sequence) + target_link_libraries(test_integer_sequence ${TEST_LIBS}) + + add_executable(test_type_traits type_traits_test.cxx) + add_test(type_traits ${EXECUTABLE_OUTPUT_PATH}/test_type_traits) + target_link_libraries(test_type_traits ${TEST_LIBS}) +endif() diff -Nru simgear-2017.3.1+dfsg/simgear/std/integer_sequence.hxx simgear-2018.1.1+dfsg/simgear/std/integer_sequence.hxx --- simgear-2017.3.1+dfsg/simgear/std/integer_sequence.hxx 1970-01-01 00:00:00.000000000 +0000 +++ simgear-2018.1.1+dfsg/simgear/std/integer_sequence.hxx 2018-04-06 19:43:17.000000000 +0000 @@ -0,0 +1,88 @@ +///@file +/// Metaprogramming Integer sequence (Is in C++14 but not C++11) +// +// Copyright (C) 2017 Thomas Geymayer +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Library General Public +// License as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later version. +// +// This library 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 +// Library General Public License for more details. +// +// You should have received a copy of the GNU Library General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + +#ifndef SIMGEAR_STD_INTEGER_SEQUENCE_HXX_ +#define SIMGEAR_STD_INTEGER_SEQUENCE_HXX_ + +#include +#include "type_traits.hxx" + +#include + +#ifndef HAVE_STD_INDEX_SEQUENCE +# include + +namespace std +{ + template + struct integer_sequence + { + static_assert( + std::is_integral::value, + "std::integer_sequence can only be instantiated with an an integral type" + ); + + typedef T value_type; + static constexpr size_t size() noexcept { return sizeof...(Ints); } + }; +} + +namespace simgear { namespace detail +{ + template + struct append; + + template + struct append, Int> + { + using type = std::integer_sequence; + }; + + template + struct sequence_gen + { + using type = + typename append::type, N - 1>::type; + }; + + template + struct sequence_gen + { + using type = std::integer_sequence; + }; +}} + +namespace std +{ + template + using index_sequence = integer_sequence; + + template + using make_integer_sequence = + typename simgear::detail::sequence_gen::type; + + template + using make_index_sequence = make_integer_sequence; + + template + using index_sequence_for = make_index_sequence; +} +#endif + +#endif /* SIMGEAR_STD_INTEGER_SEQUENCE_HXX_ */ diff -Nru simgear-2017.3.1+dfsg/simgear/std/integer_sequence_test.cxx simgear-2018.1.1+dfsg/simgear/std/integer_sequence_test.cxx --- simgear-2017.3.1+dfsg/simgear/std/integer_sequence_test.cxx 1970-01-01 00:00:00.000000000 +0000 +++ simgear-2018.1.1+dfsg/simgear/std/integer_sequence_test.cxx 2018-04-06 19:43:17.000000000 +0000 @@ -0,0 +1,46 @@ +#include +#include + +template +void print(const T& v, std::size_t i) +{ + std::cout << "arg #" << i << ": '" << v << "', "; +} + +template +void doIt_impl(Args ... args, std::index_sequence) +{ + std::initializer_list{(print(args, Is), '0')...}; +} + +template +void doIt(Args ... args) +{ + static_assert(sizeof...(Args) == std::index_sequence_for::size(), ""); + + doIt_impl(args..., std::index_sequence_for{}); +} + +int main(int argc, char* argv[]) +{ + static_assert(std::is_same::value_type, char>::value, ""); + static_assert(std::is_same::value_type, long long>::value, ""); + static_assert(std::is_same::value_type, std::size_t>::value, ""); + + static_assert(std::is_same, std::index_sequence<>>::value, ""); + static_assert(std::is_same, std::index_sequence<0>>::value, ""); + static_assert(std::is_same, std::index_sequence<0, 1>>::value, ""); + static_assert(std::is_same, std::index_sequence<0, 1, 2>>::value, ""); + static_assert(std::is_same, std::index_sequence<0, 1, 2, 3>>::value, ""); + static_assert(std::is_same, std::index_sequence<0, 1, 2, 3, 4>>::value, ""); + static_assert(std::is_same, std::index_sequence<0, 1, 2, 3, 4, 5>>::value, ""); + static_assert(std::is_same, std::index_sequence<0, 1, 2, 3, 4, 5, 6>>::value, ""); + static_assert(std::is_same, std::index_sequence<0, 1, 2, 3, 4, 5, 6, 7>>::value, ""); + + static_assert(std::make_index_sequence<5>::size() == 5, ""); + static_assert(std::index_sequence_for::size() == 3, ""); + + std::cout << std::make_integer_sequence::size() << std::endl; + doIt(1, 2, 3, "hallo", 3.4, 3.52f); + return 0; +} diff -Nru simgear-2017.3.1+dfsg/simgear/std/type_traits.hxx simgear-2018.1.1+dfsg/simgear/std/type_traits.hxx --- simgear-2017.3.1+dfsg/simgear/std/type_traits.hxx 1970-01-01 00:00:00.000000000 +0000 +++ simgear-2018.1.1+dfsg/simgear/std/type_traits.hxx 2018-04-06 19:43:17.000000000 +0000 @@ -0,0 +1,67 @@ +///@file +/// Type Traits (Provide features of later C++ standards) +// +// Copyright (C) 2017 Thomas Geymayer +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Library General Public +// License as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later version. +// +// This library 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 +// Library General Public License for more details. +// +// You should have received a copy of the GNU Library General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + +#ifndef SIMGEAR_STD_TYPE_TRAITS_HXX_ +#define SIMGEAR_STD_TYPE_TRAITS_HXX_ + +#include +#include + +namespace std +{ +#ifndef HAVE_STD_REMOVE_CV_T + template + using remove_cv_t = typename remove_cv::type; + + template + using remove_const_t = typename remove_const::type; + + template + using remove_volatile_t = typename remove_volatile::type; + + template + using remove_reference_t = typename remove_reference::type; + + template< class T > + using remove_pointer_t = typename remove_pointer::type; +#endif + +#ifndef HAVE_STD_REMOVE_CVREF_T + template + struct remove_cvref + { + using type = remove_cv_t>; + }; + + template + using remove_cvref_t = typename remove_cvref::type; +#endif + +#ifndef HAVE_STD_ENABLE_IF_T + template + using enable_if_t = typename enable_if::type; +#endif + +#ifndef HAVE_STD_BOOL_CONSTANT + template + using bool_constant = integral_constant; +#endif +} + +#endif /* SIMGEAR_STD_TYPE_TRAITS_HXX_ */ diff -Nru simgear-2017.3.1+dfsg/simgear/std/type_traits_test.cxx simgear-2018.1.1+dfsg/simgear/std/type_traits_test.cxx --- simgear-2017.3.1+dfsg/simgear/std/type_traits_test.cxx 1970-01-01 00:00:00.000000000 +0000 +++ simgear-2018.1.1+dfsg/simgear/std/type_traits_test.cxx 2018-04-06 19:43:17.000000000 +0000 @@ -0,0 +1,24 @@ +#include + +using namespace std; + +template +void assert_same() +{ + static_assert(is_same::value, ""); +} + +int main(int argc, char* argv[]) +{ + assert_same, int>(); + assert_same, int volatile>(); + assert_same, int const>(); + assert_same, int const volatile>(); + assert_same, int const volatile>(); + assert_same, int>(); + + assert_same, double>(); + assert_same, integral_constant>(); + + return 0; +} diff -Nru simgear-2017.3.1+dfsg/simgear/structure/CMakeLists.txt simgear-2018.1.1+dfsg/simgear/structure/CMakeLists.txt --- simgear-2017.3.1+dfsg/simgear/structure/CMakeLists.txt 2017-09-20 07:51:07.000000000 +0000 +++ simgear-2018.1.1+dfsg/simgear/structure/CMakeLists.txt 2018-04-06 19:43:17.000000000 +0000 @@ -28,10 +28,6 @@ StateMachine.hxx ) -set(DETAIL_HEADERS - detail/function_list_template.hxx -) - set(SOURCES SGAtomic.cxx SGBinding.cxx @@ -48,7 +44,6 @@ ) simgear_component(structure structure "${SOURCES}" "${HEADERS}") -simgear_component(structure/detail structure/detail "" "${DETAIL_HEADERS}") if(ENABLE_TESTS) @@ -60,14 +55,13 @@ target_link_libraries(test_expressions ${TEST_LIBS}) add_test(expressions ${EXECUTABLE_OUTPUT_PATH}/test_expressions) +add_executable(test_shared_ptr shared_ptr_test.cpp) +target_link_libraries(test_shared_ptr ${TEST_LIBS}) +add_test(shared_ptr ${EXECUTABLE_OUTPUT_PATH}/test_shared_ptr) + endif(ENABLE_TESTS) add_boost_test(function_list SOURCES function_list_test.cxx LIBRARIES ${TEST_LIBS} ) - -add_boost_test(shared_ptr - SOURCES shared_ptr_test.cpp - LIBRARIES ${TEST_LIBS} -) diff -Nru simgear-2017.3.1+dfsg/simgear/structure/detail/function_list_template.hxx simgear-2018.1.1+dfsg/simgear/structure/detail/function_list_template.hxx --- simgear-2017.3.1+dfsg/simgear/structure/detail/function_list_template.hxx 2017-09-20 07:51:07.000000000 +0000 +++ simgear-2018.1.1+dfsg/simgear/structure/detail/function_list_template.hxx 1970-01-01 00:00:00.000000000 +0000 @@ -1,37 +0,0 @@ -#ifndef SG_FUNCTION_LIST_HXX_ -# error function_list - do not include this file! -#endif - -#ifndef SG_DONT_DO_ANYTHING -#define n BOOST_PP_ITERATION() -#define SG_FUNC_TYPE boost::function -#define SG_LIST_TYPE std::vector - -template -class function_list: - public SG_LIST_TYPE -{ - public: - typedef SG_FUNC_TYPE function_type; - typedef typename SG_LIST_TYPE::iterator iterator; - typedef typename SG_LIST_TYPE::const_iterator const_iterator; - - Ret operator()(BOOST_PP_ENUM_BINARY_PARAMS(n, A, a)) const - { - if( this->empty() ) - return Ret(); - - const_iterator list_end = --this->end(); - for(const_iterator f = this->begin(); f != list_end; ++f) - if( *f ) - (*f)(BOOST_PP_ENUM_PARAMS(n, a)); - - return (*list_end) ? (*list_end)(BOOST_PP_ENUM_PARAMS(n, a)) : Ret(); - } -}; - -#undef n -#undef SG_FUNC_TYPE -#undef SG_LIST_TYPE - -#endif // SG_DONT_DO_ANYTHING diff -Nru simgear-2017.3.1+dfsg/simgear/structure/exception.cxx simgear-2018.1.1+dfsg/simgear/structure/exception.cxx --- simgear-2017.3.1+dfsg/simgear/structure/exception.cxx 2017-09-20 07:51:07.000000000 +0000 +++ simgear-2018.1.1+dfsg/simgear/structure/exception.cxx 2018-04-06 19:43:17.000000000 +0000 @@ -49,7 +49,7 @@ setPath(path); } -sg_location::~sg_location () throw () +sg_location::~sg_location () { } @@ -144,7 +144,7 @@ setOrigin(origin); } -sg_throwable::~sg_throwable () throw () +sg_throwable::~sg_throwable () { } @@ -185,7 +185,7 @@ } } -const char* sg_throwable::what() const throw() +const char* sg_throwable::what() const noexcept { try { return getMessage(); @@ -215,7 +215,7 @@ { } -sg_error::~sg_error () throw () +sg_error::~sg_error () { } @@ -239,7 +239,7 @@ { } -sg_exception::~sg_exception () throw () +sg_exception::~sg_exception () { } @@ -279,7 +279,7 @@ { } -sg_io_exception::~sg_io_exception () throw () +sg_io_exception::~sg_io_exception () { } @@ -335,7 +335,7 @@ setText(text.c_str()); } -sg_format_exception::~sg_format_exception () throw () +sg_format_exception::~sg_format_exception () { } @@ -379,7 +379,7 @@ { } -sg_range_exception::~sg_range_exception () throw () +sg_range_exception::~sg_range_exception () { } // end of exception.cxx diff -Nru simgear-2017.3.1+dfsg/simgear/structure/exception.hxx simgear-2018.1.1+dfsg/simgear/structure/exception.hxx --- simgear-2017.3.1+dfsg/simgear/structure/exception.hxx 2017-09-20 07:51:07.000000000 +0000 +++ simgear-2018.1.1+dfsg/simgear/structure/exception.hxx 2018-04-06 19:43:17.000000000 +0000 @@ -31,7 +31,7 @@ sg_location(const std::string& path, int line = -1, int column = -1); sg_location(const SGPath& path, int line = -1, int column = -1); explicit sg_location(const char* path, int line = -1, int column = -1); - virtual ~sg_location() throw (); + virtual ~sg_location(); virtual const char* getPath() const; virtual void setPath (const char* path); virtual int getLine () const; @@ -58,13 +58,13 @@ enum {MAX_TEXT_LEN = 1024}; sg_throwable (); sg_throwable (const char* message, const char* origin = 0); - virtual ~sg_throwable () throw (); + virtual ~sg_throwable (); virtual const char* getMessage () const; virtual const std::string getFormattedMessage () const; virtual void setMessage (const char* message); virtual const char* getOrigin () const; virtual void setOrigin (const char *origin); - virtual const char* what() const throw(); + virtual const char* what() const noexcept; private: char _message[MAX_TEXT_LEN]; char _origin[MAX_TEXT_LEN]; @@ -87,7 +87,7 @@ sg_error (); sg_error (const char* message, const char* origin = 0); sg_error (const std::string& message, const std::string& origin = ""); - virtual ~sg_error () throw (); + virtual ~sg_error (); }; @@ -111,7 +111,7 @@ sg_exception (); sg_exception (const char* message, const char* origin = 0); sg_exception (const std::string& message, const std::string& = ""); - virtual ~sg_exception () throw (); + virtual ~sg_exception (); }; @@ -136,8 +136,8 @@ sg_io_exception (const std::string &message, const std::string &origin = ""); sg_io_exception (const std::string &message, const sg_location &location, const std::string &origin = ""); - - virtual ~sg_io_exception () throw (); + + virtual ~sg_io_exception (); virtual const std::string getFormattedMessage () const; virtual const sg_location &getLocation () const; virtual void setLocation (const sg_location &location); @@ -165,7 +165,7 @@ const char* origin = 0); sg_format_exception (const std::string& message, const std::string& text, const std::string& origin = ""); - virtual ~sg_format_exception () throw (); + virtual ~sg_format_exception (); virtual const char* getText () const; virtual void setText (const char* text); private: @@ -190,7 +190,7 @@ const char* origin = 0); sg_range_exception (const std::string& message, const std::string& origin = ""); - virtual ~sg_range_exception () throw (); + virtual ~sg_range_exception (); }; #endif diff -Nru simgear-2017.3.1+dfsg/simgear/structure/expression_test.cxx simgear-2018.1.1+dfsg/simgear/structure/expression_test.cxx --- simgear-2017.3.1+dfsg/simgear/structure/expression_test.cxx 2017-09-20 07:51:07.000000000 +0000 +++ simgear-2018.1.1+dfsg/simgear/structure/expression_test.cxx 2018-04-06 19:43:17.000000000 +0000 @@ -14,6 +14,7 @@ #include #include #include +#include #include #include @@ -81,8 +82,8 @@ "" ""; - SGPropertyNode* desc = new SGPropertyNode; - readProperties(xml2, strlen(xml2), desc); + auto desc = std::unique_ptr(new SGPropertyNode); + readProperties(xml2, strlen(xml2), desc.get()); SGSharedPtr expr = SGReadDoubleExpression(propertyTree, desc->getChild(0)); @@ -107,4 +108,3 @@ cout << __FILE__ << ": All tests passed" << endl; return EXIT_SUCCESS; } - diff -Nru simgear-2017.3.1+dfsg/simgear/structure/function_list.hxx simgear-2018.1.1+dfsg/simgear/structure/function_list.hxx --- simgear-2017.3.1+dfsg/simgear/structure/function_list.hxx 2017-09-20 07:51:07.000000000 +0000 +++ simgear-2018.1.1+dfsg/simgear/structure/function_list.hxx 2018-04-06 19:43:17.000000000 +0000 @@ -20,33 +20,44 @@ #define SG_FUNCTION_LIST_HXX_ #include -#include -#include -#include -#include #include namespace simgear { template class function_list; - // Build dependency for CMake, gcc, etc. -# define SG_DONT_DO_ANYTHING -# include -# undef SG_DONT_DO_ANYTHING - -# define BOOST_PP_ITERATION_LIMITS (0, 3) -# define BOOST_PP_FILENAME_1 -# include BOOST_PP_ITERATE() - /** * Handle a list of callbacks like a single boost::function. * - * @tparam Sig Function signature. + * @tparam Ret Return type of the callbacks + * @tparam Args Parameter types of the callbacks + */ + template + class function_list: + public std::vector> + { + public: + Ret operator()(Args ... args) const + { + if( this->empty() ) + return Ret(); + + auto list_end = --this->end(); + for(auto f = this->begin(); f != list_end; ++f) + if( *f ) + (*f)(args...); + + return (*list_end) ? (*list_end)(args...) : Ret(); + } + }; + + /** + * Handle a list of callbacks with the same signature as the given + * boost::function type. */ - template - class function_list >: - public function_list + template + class function_list>: + public function_list { }; diff -Nru simgear-2017.3.1+dfsg/simgear/structure/SGBinding.cxx simgear-2018.1.1+dfsg/simgear/structure/SGBinding.cxx --- simgear-2017.3.1+dfsg/simgear/structure/SGBinding.cxx 2017-09-20 07:51:07.000000000 +0000 +++ simgear-2018.1.1+dfsg/simgear/structure/SGBinding.cxx 2018-04-06 19:43:17.000000000 +0000 @@ -41,12 +41,6 @@ read(node, root); } -SGBinding::~SGBinding() -{ - if(_arg && _arg->getParent()) - _arg->getParent()->removeChild(_arg->getName(), _arg->getIndex()); -} - void SGBinding::clear() { diff -Nru simgear-2017.3.1+dfsg/simgear/structure/SGBinding.hxx simgear-2018.1.1+dfsg/simgear/structure/SGBinding.hxx --- simgear-2017.3.1+dfsg/simgear/structure/SGBinding.hxx 2017-09-20 07:51:07.000000000 +0000 +++ simgear-2018.1.1+dfsg/simgear/structure/SGBinding.hxx 2018-04-06 19:43:17.000000000 +0000 @@ -58,13 +58,14 @@ /** * Destructor. */ - virtual ~SGBinding (); + virtual ~SGBinding () = default; /** - * clear internal state of the binding back to empty. This is useful - * if you don't want the 'remove on delete' behaviour of the - * destructor. + * Clear internal state of the binding back to empty. + * + * This was particularly useful when SGBinding's destructor had its 'remove + * on delete' behaviour, however this is not the case anymore. */ void clear(); diff -Nru simgear-2017.3.1+dfsg/simgear/structure/SGExpression.hxx simgear-2018.1.1+dfsg/simgear/structure/SGExpression.hxx --- simgear-2017.3.1+dfsg/simgear/structure/SGExpression.hxx 2017-09-20 07:51:07.000000000 +0000 +++ simgear-2018.1.1+dfsg/simgear/structure/SGExpression.hxx 2018-04-06 19:43:17.000000000 +0000 @@ -36,10 +36,9 @@ #include #include -/// Expression tree implementation. - namespace simgear { + /// Expression tree implementation. namespace expression { enum Type { diff -Nru simgear-2017.3.1+dfsg/simgear/structure/SGReferenced.hxx simgear-2018.1.1+dfsg/simgear/structure/SGReferenced.hxx --- simgear-2017.3.1+dfsg/simgear/structure/SGReferenced.hxx 2017-09-20 07:51:07.000000000 +0000 +++ simgear-2018.1.1+dfsg/simgear/structure/SGReferenced.hxx 2018-04-06 19:43:17.000000000 +0000 @@ -43,7 +43,7 @@ static unsigned get(const SGReferenced* ref) { if (ref) return ++(ref->_refcount); else return 0; } - static unsigned put(const SGReferenced* ref) + static unsigned put(const SGReferenced* ref) noexcept { if (ref) return --(ref->_refcount); else return 0; } static unsigned count(const SGReferenced* ref) { if (ref) return ref->_refcount; else return 0; } diff -Nru simgear-2017.3.1+dfsg/simgear/structure/SGSharedPtr.hxx simgear-2018.1.1+dfsg/simgear/structure/SGSharedPtr.hxx --- simgear-2017.3.1+dfsg/simgear/structure/SGSharedPtr.hxx 2017-09-20 07:51:07.000000000 +0000 +++ simgear-2018.1.1+dfsg/simgear/structure/SGSharedPtr.hxx 2018-04-06 19:43:17.000000000 +0000 @@ -20,8 +20,9 @@ #ifndef SGSharedPtr_HXX #define SGSharedPtr_HXX +#include + #include "SGReferenced.hxx" -#include template class SGWeakPtr; @@ -50,12 +51,16 @@ public: typedef T element_type; - SGSharedPtr(void) : _ptr(0) + SGSharedPtr(void) noexcept + : _ptr(0) {} SGSharedPtr(T* ptr) : _ptr(ptr) { get(_ptr); } SGSharedPtr(const SGSharedPtr& p) : _ptr(p.get()) { get(_ptr); } + SGSharedPtr(SGSharedPtr&& other) noexcept + : SGSharedPtr() + { swap(other); } template SGSharedPtr(const SGSharedPtr& p) : _ptr(p.get()) { get(_ptr); } @@ -64,9 +69,20 @@ { reset(p.lock().get()); } ~SGSharedPtr(void) { reset(); } - + SGSharedPtr& operator=(const SGSharedPtr& p) { reset(p.get()); return *this; } + + SGSharedPtr& operator=(SGSharedPtr&& p) noexcept + { // Whether self-move is to be supported at all is controversial, let's + // take the conservative approach for now + if (this != &p) { + swap(p); + p.reset(); + } + return *this; + } + template SGSharedPtr& operator=(const SGSharedPtr& p) { reset(p.get()); return *this; } @@ -86,7 +102,7 @@ { return _ptr; } T* release() { T* tmp = _ptr; _ptr = 0; T::put(tmp); return tmp; } - void reset() + void reset() noexcept { if (!T::put(_ptr)) delete _ptr; _ptr = 0; } void reset(T* p) { SGSharedPtr(p).swap(*this); } @@ -101,8 +117,8 @@ void clear() { reset(); } - void swap(SGSharedPtr& other) - { std::swap(_ptr, other._ptr); } + void swap(SGSharedPtr& other) noexcept + { simgear::noexceptSwap(_ptr, other._ptr); } private: void assignNonRef(T* p) @@ -118,6 +134,12 @@ friend class SGWeakPtr; }; +template +void swap(SGSharedPtr& a, SGSharedPtr& b) noexcept +{ + a.swap(b); +} + /** * Support for boost::mem_fn */ diff -Nru simgear-2017.3.1+dfsg/simgear/structure/shared_ptr_test.cpp simgear-2018.1.1+dfsg/simgear/structure/shared_ptr_test.cpp --- simgear-2017.3.1+dfsg/simgear/structure/shared_ptr_test.cpp 2017-09-20 07:51:07.000000000 +0000 +++ simgear-2018.1.1+dfsg/simgear/structure/shared_ptr_test.cpp 2018-04-06 19:43:17.000000000 +0000 @@ -1,6 +1,13 @@ -/// Unit tests for reference counting and smart pointer classes -#define BOOST_TEST_MODULE structure -#include +// -*- coding: utf-8 -*- +// +// Unit tests for reference counting and smart pointer classes + +#include +#include + +#include // EXIT_SUCCESS + +#include #include "SGSharedPtr.hxx" #include "SGWeakPtr.hxx" @@ -21,33 +28,152 @@ }; typedef SGSharedPtr RefPtr; -BOOST_AUTO_TEST_CASE( shared_ptr ) +void test_SGSharedPtr() { - BOOST_REQUIRE_EQUAL( ReferenceCounted::count(0), 0 ); + std::cout << "Testing SGSharedPtr and SGReferenced" << std::endl; + + SG_CHECK_EQUAL( ReferenceCounted::count(0), 0 ); RefPtr ptr( new ReferenceCounted() ); - BOOST_REQUIRE_EQUAL( instance_count, 1 ); - BOOST_REQUIRE_EQUAL( ReferenceCounted::count(ptr.get()), 1 ); - BOOST_REQUIRE_EQUAL( ptr.getNumRefs(), 1 ); + SG_CHECK_EQUAL( instance_count, 1 ); + SG_CHECK_EQUAL( ReferenceCounted::count(ptr.get()), 1 ); + SG_CHECK_EQUAL( ptr.getNumRefs(), 1 ); + // Test SGSharedPtr's copy assignment operator RefPtr ptr2 = ptr; - BOOST_REQUIRE_EQUAL( ptr.getNumRefs(), 2 ); - BOOST_REQUIRE_EQUAL( ptr2.getNumRefs(), 2 ); + SG_CHECK_EQUAL( ptr.getNumRefs(), 2 ); + SG_CHECK_EQUAL( ptr2.getNumRefs(), 2 ); - BOOST_REQUIRE_EQUAL( ptr, ptr2 ); - BOOST_REQUIRE_EQUAL( ptr.get(), ptr2.get() ); + SG_CHECK_EQUAL( ptr, ptr2 ); + SG_CHECK_EQUAL( ptr.get(), ptr2.get() ); + // Test SGSharedPtr::reset() with no argument ptr.reset(); - BOOST_REQUIRE( !ptr.get() ); - BOOST_REQUIRE_EQUAL( ptr.getNumRefs(), 0 ); - BOOST_REQUIRE_EQUAL( ReferenceCounted::count(ptr2.get()), 1 ); - BOOST_REQUIRE_EQUAL( ptr2.getNumRefs(), 1 ); + SG_CHECK_IS_NULL( ptr.get() ); + SG_CHECK_EQUAL( ptr.getNumRefs(), 0 ); + SG_CHECK_EQUAL( ReferenceCounted::count(ptr2.get()), 1 ); + SG_CHECK_EQUAL( ptr2.getNumRefs(), 1 ); ptr2.reset(); - BOOST_REQUIRE( !ptr2.get() ); - BOOST_REQUIRE_EQUAL( ptr.getNumRefs(), 0 ); - BOOST_REQUIRE_EQUAL( ptr2.getNumRefs(), 0 ); - BOOST_REQUIRE_EQUAL( instance_count, 0) ; + SG_CHECK_IS_NULL( ptr2.get() ); + SG_CHECK_EQUAL( ptr.getNumRefs(), 0 ); + SG_CHECK_EQUAL( ptr2.getNumRefs(), 0 ); + SG_CHECK_EQUAL( instance_count, 0) ; + + // Test operator==() and operator!=() for SGSharedPtr + { + RefPtr ptrA(new ReferenceCounted()); + RefPtr ptrB(ptrA); + RefPtr ptrC(new ReferenceCounted()); + RefPtr emptyPtr{}; + SG_CHECK_EQUAL( ptrA, ptrB ); + SG_CHECK_EQUAL( ptrA.get(), ptrB.get() ); // same thing by definition + SG_CHECK_NE( ptrA, ptrC ); + SG_CHECK_NE( ptrA.get(), ptrC.get() ); + SG_CHECK_NE( ptrB, ptrC ); + SG_CHECK_NE( ptrA, emptyPtr ); + SG_CHECK_EQUAL( emptyPtr, emptyPtr ); + } + + // Test SGSharedPtr::reset(T* p) and SGSharedPtr::operator T*() + { + RefPtr ptrA(new ReferenceCounted()); + SG_CHECK_EQUAL( ptrA.getNumRefs(), 1 ); + + RefPtr ptrB(new ReferenceCounted()); + SG_CHECK_NE( ptrA, ptrB ); + ptrB.reset(ptrA); + SG_CHECK_EQUAL( ptrA, ptrB ); + SG_CHECK_EQUAL( ptrA.getNumRefs(), 2 ); + SG_CHECK_EQUAL( ptrB.getNumRefs(), 2 ); + + RefPtr ptrC(new ReferenceCounted()); + SG_CHECK_NE( ptrA, ptrC ); + SG_CHECK_EQUAL( ptrC.getNumRefs(), 1 ); + // ptrA is implicit converted to ReferenceCounted* + ptrC.reset(ptrA); + SG_CHECK_EQUAL( ptrA.getNumRefs(), 3 ); + SG_CHECK_EQUAL( ptrB.getNumRefs(), 3 ); + SG_CHECK_EQUAL( ptrC.getNumRefs(), 3 ); + SG_CHECK_EQUAL( ptrA, ptrB ); + SG_CHECK_EQUAL( ptrB, ptrC ); + } + + // Test SGSharedPtr's copy constructor + { + RefPtr ptrA(new ReferenceCounted()); + SG_CHECK_EQUAL( ptrA.getNumRefs(), 1 ); + + RefPtr ptrB(ptrA); + SG_CHECK_EQUAL( ptrA.getNumRefs(), 2 ); + SG_CHECK_EQUAL( ptrB.getNumRefs(), 2 ); + SG_CHECK_EQUAL( ptrA, ptrB ); + } + + // Test SGSharedPtr's move constructor + { + RefPtr ptrA(new ReferenceCounted()); + RefPtr ptrB(ptrA); + RefPtr ptrC(std::move(ptrA)); + + SG_CHECK_EQUAL( ptrB.getNumRefs(), 2 ); + SG_CHECK_EQUAL( ptrC.getNumRefs(), 2 ); + SG_CHECK_EQUAL( ptrB, ptrC ); + // Although our implementation has these two properties, they are + // absolutely *not* guaranteed by the C++ move semantics: + SG_CHECK_EQUAL( ptrA.getNumRefs(), 0 ); + SG_CHECK_IS_NULL( ptrA.get() ); + } + + // Test SGSharedPtr's move assignment operator: self-move, supposedly + // undefined behavior but certainly safer as a no-op---which the + // copy-and-swap idiom offers for free. + { + RefPtr ptrA(new ReferenceCounted()); + RefPtr ptrB(ptrA); + + ptrA = std::move(ptrA); + SG_CHECK_EQUAL( ptrA.getNumRefs(), 2 ); + SG_CHECK_EQUAL( ptrB.getNumRefs(), 2 ); + SG_CHECK_IS_NOT_NULL( ptrA.get() ); + SG_CHECK_EQUAL( ptrA, ptrB ); + } + + // Test SGSharedPtr's move assignment operator: move to an empty SGSharedPtr + { + RefPtr ptrA; + RefPtr ptrB(new ReferenceCounted()); + + ptrA = std::move(ptrB); + SG_CHECK_EQUAL( ptrA.getNumRefs(), 1 ); + SG_CHECK_IS_NOT_NULL( ptrA.get() ); + // Implementation detail that is *not* guaranteed by the C++ move + // semantics: + SG_CHECK_EQUAL( ptrB.getNumRefs(), 0 ); + SG_CHECK_IS_NULL( ptrB.get() ); + } + + // Test SGSharedPtr's move assignment operator: move to a non-empty + // SGSharedPtr + { + RefPtr ptrA(new ReferenceCounted()); + RefPtr ptrB(ptrA); + RefPtr ptrC(new ReferenceCounted()); + + SG_CHECK_EQUAL( ptrA.getNumRefs(), 2 ); + SG_CHECK_EQUAL( ptrB.getNumRefs(), 2 ); + SG_CHECK_EQUAL( ptrC.getNumRefs(), 1 ); + SG_CHECK_EQUAL( ptrA, ptrB ); + SG_CHECK_NE( ptrA, ptrC ); + + ptrA = std::move(ptrC); + SG_CHECK_EQUAL( ptrA.getNumRefs(), 1 ); + SG_CHECK_EQUAL( ptrB.getNumRefs(), 1 ); + SG_CHECK_NE( ptrA, ptrB ); + // Implementation detail that is *not* guaranteed by the C++ move + // semantics: + SG_CHECK_IS_NULL( ptrC.get() ); + } } class Base1: @@ -63,31 +189,41 @@ public Base2 {}; -BOOST_AUTO_TEST_CASE( virtual_weak_ptr ) +void test_SGWeakPtr() { + std::cout << "Testing SGWeakPtr and SGVirtualWeakReferenced" << std::endl; + SGSharedPtr ptr( new VirtualDerived() ); SGWeakPtr weak_ptr( ptr ); - BOOST_REQUIRE_EQUAL( ptr.getNumRefs(), 1 ); + SG_CHECK_EQUAL( ptr.getNumRefs(), 1 ); SGSharedPtr ptr1( weak_ptr.lock() ); - BOOST_REQUIRE_EQUAL( ptr.getNumRefs(), 2 ); + SG_CHECK_EQUAL( ptr.getNumRefs(), 2 ); // converting constructor - BOOST_REQUIRE_EQUAL( SGSharedPtr(weak_ptr), ptr1 ); + SG_CHECK_EQUAL( SGSharedPtr(weak_ptr), ptr1 ); SGSharedPtr ptr2( weak_ptr.lock() ); - BOOST_REQUIRE_EQUAL( ptr.getNumRefs(), 3 ); + SG_CHECK_EQUAL( ptr.getNumRefs(), 3 ); - BOOST_REQUIRE( ptr != NULL ); - BOOST_REQUIRE_EQUAL( ptr.get(), ptr1.get() ); - BOOST_REQUIRE_EQUAL( ptr.get(), ptr2.get() ); + SG_CHECK_IS_NOT_NULL( ptr ); + SG_CHECK_EQUAL( ptr.get(), ptr1.get() ); + SG_CHECK_EQUAL( ptr.get(), ptr2.get() ); SGWeakPtr weak_base1( ptr ); SGWeakPtr weak_base2( ptr ); ptr1 = dynamic_cast(weak_base1.lock().get()); ptr2 = dynamic_cast(weak_base2.lock().get()); - BOOST_REQUIRE_EQUAL( ptr.get(), ptr1.get() ); - BOOST_REQUIRE_EQUAL( ptr.get(), ptr2.get() ); - BOOST_REQUIRE_EQUAL( ptr.getNumRefs(), 3 ); + SG_CHECK_EQUAL( ptr.get(), ptr1.get() ); + SG_CHECK_EQUAL( ptr.get(), ptr2.get() ); + SG_CHECK_EQUAL( ptr.getNumRefs(), 3 ); +} + +int main(int argc, char* argv[]) +{ + test_SGSharedPtr(); + test_SGWeakPtr(); + + return EXIT_SUCCESS; } diff -Nru simgear-2017.3.1+dfsg/simgear/structure/StateMachine.cxx simgear-2018.1.1+dfsg/simgear/structure/StateMachine.cxx --- simgear-2017.3.1+dfsg/simgear/structure/StateMachine.cxx 2017-09-20 07:51:07.000000000 +0000 +++ simgear-2018.1.1+dfsg/simgear/structure/StateMachine.cxx 2018-04-06 19:43:17.000000000 +0000 @@ -19,16 +19,13 @@ * */ -#ifdef HAVE_CONFIG_H -# include -#endif +#include #include "StateMachine.hxx" #include #include #include -#include #include #include @@ -44,7 +41,7 @@ static void readBindingList(SGPropertyNode* desc, const std::string& name, SGPropertyNode* root, SGBindingList& result) { - BOOST_FOREACH(SGPropertyNode* b, desc->getChildren(name)) { + for (auto b : desc->getChildren(name)) { SGBinding* bind = new SGBinding; bind->read(b, root); result.push_back(bind); @@ -85,7 +82,7 @@ void computeEligibleTransitions() { _eligible.clear(); - BOOST_FOREACH(Transition_ptr t, _transitions) { + for (Transition_ptr t : _transitions) { if (t->applicableForState(_currentState)) { _eligible.push_back(t.ptr()); } @@ -358,7 +355,7 @@ Transition_ptr trigger; - BOOST_FOREACH(Transition* trans, d->_eligible) { + for (auto trans : d->_eligible) { if (trans->evaluate()) { if (trigger != Transition_ptr()) { SG_LOG(SG_GENERAL, SG_WARN, "ambiguous transitions! " @@ -379,7 +376,7 @@ StateMachine::State_ptr StateMachine::findStateByName(const std::string& aName) const { - BOOST_FOREACH(State_ptr sp, d->_states) { + for (auto sp : d->_states) { if (sp->name() == aName) { return sp; } @@ -435,7 +432,7 @@ assert(d->_root); } - BOOST_FOREACH(SGPropertyNode* stateDesc, desc->getChildren("state")) { + for (auto stateDesc : desc->getChildren("state")) { std::string nm = stateDesc->getStringValue("name"); State_ptr st(new State(nm)); @@ -446,7 +443,7 @@ addState(st); } // of states iteration - BOOST_FOREACH(SGPropertyNode* tDesc, desc->getChildren("transition")) { + for (auto tDesc : desc->getChildren("transition")) { std::string nm = tDesc->getStringValue("name"); State_ptr target = findStateByName(tDesc->getStringValue("target")); @@ -456,7 +453,7 @@ t->setTriggerCondition(cond); t->setExcludeTarget(tDesc->getBoolValue("exclude-target", true)); - BOOST_FOREACH(SGPropertyNode* src, tDesc->getChildren("source")) { + for (auto src : tDesc->getChildren("source")) { State_ptr srcState = findStateByName(src->getStringValue()); t->addSourceState(srcState); } diff -Nru simgear-2017.3.1+dfsg/simgear/structure/state_machine_test.cxx simgear-2018.1.1+dfsg/simgear/structure/state_machine_test.cxx --- simgear-2017.3.1+dfsg/simgear/structure/state_machine_test.cxx 2017-09-20 07:51:07.000000000 +0000 +++ simgear-2018.1.1+dfsg/simgear/structure/state_machine_test.cxx 2018-04-06 19:43:17.000000000 +0000 @@ -14,6 +14,7 @@ #include #include #include +#include #include "StateMachine.hxx" @@ -212,21 +213,29 @@ void testParse() { - const char* xml = "" - "" - "" - "one" - "" - "" - "two" - "" - ""; + const char* xml = R"( + + + one + + nasal + + + + nasal + + + + + two + + )"; - SGPropertyNode* desc = new SGPropertyNode; - readProperties(xml, strlen(xml), desc); + auto desc = std::unique_ptr(new SGPropertyNode); + readProperties(xml, strlen(xml), desc.get()); SGPropertyNode_ptr root(new SGPropertyNode); - StateMachine_ptr sm = StateMachine::createFromPlist(desc, root); + StateMachine_ptr sm = StateMachine::createFromPlist(desc.get(), root); SG_VERIFY(sm->findStateByName("one") != NULL); SG_VERIFY(sm->findStateByName("two") != NULL); @@ -242,4 +251,3 @@ cout << __FILE__ << ": All tests passed" << endl; return EXIT_SUCCESS; } - diff -Nru simgear-2017.3.1+dfsg/simgear/structure/subsystem_mgr.cxx simgear-2018.1.1+dfsg/simgear/structure/subsystem_mgr.cxx --- simgear-2017.3.1+dfsg/simgear/structure/subsystem_mgr.cxx 2017-09-20 07:51:07.000000000 +0000 +++ simgear-2018.1.1+dfsg/simgear/structure/subsystem_mgr.cxx 2018-04-06 19:43:17.000000000 +0000 @@ -22,6 +22,8 @@ # include #endif +#include + #include #include diff -Nru simgear-2017.3.1+dfsg/simgear/timing/timestamp.cxx simgear-2018.1.1+dfsg/simgear/timing/timestamp.cxx --- simgear-2017.3.1+dfsg/simgear/timing/timestamp.cxx 2017-09-20 07:51:07.000000000 +0000 +++ simgear-2018.1.1+dfsg/simgear/timing/timestamp.cxx 2018-04-06 19:43:17.000000000 +0000 @@ -29,6 +29,7 @@ #include #include +#include #ifdef HAVE_UNISTD_H # include // for gettimeofday() and the _POSIX_TIMERS define @@ -98,6 +99,21 @@ #endif } +void SGTimeStamp::systemClockHoursAndMinutes() +{ + using namespace std; + using namespace std::chrono; + + typedef duration >::type> days; + + system_clock::time_point now = system_clock::now(); + system_clock::duration tp = now.time_since_epoch(); + tp -= duration_cast(tp); + + _sec = duration_cast(tp).count(); + _nsec = duration_cast(tp - seconds(_sec)).count(); +} + // sleep based timing loop. // // Calling sleep, even usleep() on linux is less accurate than diff -Nru simgear-2017.3.1+dfsg/simgear/timing/timestamp.hxx simgear-2018.1.1+dfsg/simgear/timing/timestamp.hxx --- simgear-2017.3.1+dfsg/simgear/timing/timestamp.hxx 2017-09-20 07:51:07.000000000 +0000 +++ simgear-2018.1.1+dfsg/simgear/timing/timestamp.hxx 2018-04-06 19:43:17.000000000 +0000 @@ -78,6 +78,13 @@ /** Update stored time to current time (seconds and nanoseconds) */ void stamp(); + /** Update stored time to current system clock (seconds and nanoseconds) + * non monotonic, keeping only hours, minutes, seconds and decimals, + * so restart at 0 at midnight. + * using the std::chrono libs + */ + void systemClockHoursAndMinutes(); + /** Set the time from a double value */ void setTime(const double& seconds) { diff -Nru simgear-2017.3.1+dfsg/SimGearConfig.cmake.in simgear-2018.1.1+dfsg/SimGearConfig.cmake.in --- simgear-2017.3.1+dfsg/SimGearConfig.cmake.in 2017-09-20 07:51:07.000000000 +0000 +++ simgear-2018.1.1+dfsg/SimGearConfig.cmake.in 2018-04-06 19:43:17.000000000 +0000 @@ -7,6 +7,7 @@ set(SIMGEAR_HEADLESS @SIMGEAR_HEADLESS@) set(SIMGEAR_SOUND @ENABLE_SOUND@) +set(USE_AEONWAVE @USE_AEONWAVE@) # OpenAL isn't a public dependency, so maybe not needed #if (SIMGEAR_SOUND) diff -Nru simgear-2017.3.1+dfsg/version simgear-2018.1.1+dfsg/version --- simgear-2017.3.1+dfsg/version 2017-09-20 07:51:07.000000000 +0000 +++ simgear-2018.1.1+dfsg/version 2018-04-06 19:43:17.000000000 +0000 @@ -1 +1 @@ -2017.3.1 +2018.1.1