Compare commits

..

322 Commits

Author SHA1 Message Date
Automatic Release Builder
17f86567f1 new version: 2020.1.3 2020-06-25 15:40:15 +01:00
James Turner
059bad1840 Fix zero-interval repeat on pick/knob animation
See:
https://sourceforge.net/p/flightgear/codetickets/2241/

Note did not adjust bug in knob-animation ignoring repeatable flag
(always marked repeatable) since this might break some aircraft.
2020-06-20 16:11:08 +01:00
James Turner
b7d4acc67d Fix compilation with Boost >= 1.73 2020-06-20 16:10:56 +01:00
Automatic Release Builder
af8b27032f new version: 2020.1.2 2020-05-18 11:54:22 +01:00
James Turner
f76b72e2f3 Adjust RelWithDebInfo optimisation for Clang
This should give a bit of speed boost to macOS official builds.
2020-05-14 13:50:58 +01:00
James Turner
59512aea1f Windows: ensure RelWithDebInfo is fast
Default CMake RelWithDebInfo pessimizes inlining
2020-05-10 19:46:27 +01:00
James Turner
96618a2e26 Enable old-style texture compression
Thanks to Dany for tracking this down!
2020-05-10 19:46:27 +01:00
James Turner
635f460dd5 Nasal: improve message for non-object member access
Based on some discussion on this ticket:

https://sourceforge.net/p/flightgear/codetickets/2186/

Make this message slightly clearer.
2020-05-03 12:22:23 +01:00
James Turner
36316e8859 Add Canvas::Image.dirtyPixels() 2020-05-03 12:22:11 +01:00
James Turner
81bdae283a new version: 2020.1.1 2020-04-27 09:29:54 +01:00
James Turner
00e25404c7 Add simgear::optional
This is a poor-man’s proxy for std::optional until we have it.
2020-04-25 18:05:59 +01:00
gallaert
6a157e1c08 Replace boost::tuple by std::tuple 2020-04-22 14:04:21 +01:00
gallaert
7a6f9151a6 General boost cleanup 2020-04-22 14:04:21 +01:00
gallaert
567293c14d Replace boost::function by std::function 2020-04-22 14:04:21 +01:00
gallaert
2ff6a4966a Replace boost::bind by std::bind and lambda function 2020-04-22 14:04:21 +01:00
gallaert
b13d7f9392 Replace boost::lexical_cast by std::ostringstream and std:stof/stoi/stod 2020-04-22 14:04:21 +01:00
gallaert
d21137739d Replace boost::enable_if/disable_if/enable_if_c/disable_if_c by std::enable_if 2020-04-22 14:03:47 +01:00
gallaert
1e13cf6fec Replace boost::shared_ptr/weak_ptr by std::shared_ptr/weak_ptr 2020-04-22 14:03:47 +01:00
James Turner
5c52cdc255 Fix for crash on reset:
https://sourceforge.net/p/flightgear/codetickets/2217/

Ensure that subsystem lookup during its own removal, returns nullptr.
Otherwise we cache an about-to-be dead pointer, which is Bad (TM)
2020-04-22 10:55:41 +01:00
James Turner
17d7808200 Remove a log message 2020-04-22 10:54:44 +01:00
James Turner
f144bd5a89 CanvasImage: fix updating after fillRect / setPixel 2020-04-18 21:31:16 +01:00
James Turner
b72553f6f0 Nasal: add isscalar() for Henning 2020-04-18 18:06:35 +01:00
James Turner
c82a14c252 Nasal: fix build on non-C99 mode
It seems GCC at least is strict about C99 mode.
2020-04-16 16:44:20 +01:00
James Turner
faa559e9af Nasal: add vecindex() built-in 2020-04-16 13:40:41 +01:00
James Turner
c51c0c43ed Nasal: add more isTYPE helpers from Henning 2020-04-16 11:42:41 +01:00
gallaert
2ab0122124 Replace boost::to_lower and boost::to_upper by strutils::lowercase and strutils::uppercase 2020-04-14 13:24:59 +01:00
James Turner
8626e0482d Nasal: add some type-check helpers.
These avoid building a string for the result, as typeof() does
2020-04-14 13:24:06 +01:00
gallaert
c9a4c0dac1 Replace BOOST_FOREACH by C++ 11 for range loop. 2020-04-12 14:39:12 +01:00
gallaert
825d93423d Statements following return, break, continue, goto or throw will never be executed. 2020-04-12 14:39:12 +01:00
Erik Hofman
ad24ce02e7 Remove some debugging statements 2020-04-12 14:53:16 +02:00
Erik Hofman
695616a8f2 Switch from doing the calculations ourselves in case of a bad_doppler implementation to using alDopplerFactor 2020-04-12 14:50:12 +02:00
Erik Hofman
b3bdf8636a Limit the velocity to the sound velocity after multiplying it by a foctor of 100 to compensate for bad Doppler calculations. Also add the option to set the sound velocity for the sound manager from within FlightGear (not yet used). 2020-04-11 09:48:49 +02:00
James Turner
c62c080a4b Logging: enable the WinDebug callback using an env var.
Instead of hard-coding, add an env-var SG_WINDEBUG to enable the
logging to the Windows debug stream.
2020-04-10 19:50:58 +01:00
James Turner
c90ab3df5b Packages: consider checksum failures for retry.
This means an out-of-sync mirror causes a retry on a different
mirror server.
2020-04-10 15:03:17 +01:00
James Turner
21e21a1fa8 Allow TiedPropertyList to fire valueChanged 2020-04-10 10:41:00 +01:00
James Turner
92ebdbfacc Expose compare-version helper publicly.
This helper was added for Catalog version checking, but will be used in
FlightGear now, so move it to strutils.
2020-04-09 14:39:12 +01:00
James Turner
8dbabc8b43 Texture-compression fixes
Rename a highly confusing method, and disable the older texture-
compression code since it interacts badly with the new texture-cache.
2020-04-07 12:12:52 +01:00
James Turner
c83dc79357 Revert to C++11 for now, found an alternative solution. 2020-04-06 10:44:23 +01:00
James Turner
a53c1bfe8b CMake logic to export debug symbols 2020-04-05 14:04:19 +01:00
James Turner
6185b8fd0d Fix crash with Touch animation mouse-up 2020-04-05 14:04:19 +01:00
Richard Harrison
ef002f6dfa Merge /u/fgarlin/simgear/ branch next into next
https://sourceforge.net/p/flightgear/simgear/merge-requests/73/
2020-04-04 20:43:04 +00:00
James Turner
9cdec99fa4 Quiet some warnings 2020-04-03 10:00:22 +01:00
Fernando García Liñán
9393f84972 Compositor: Fix crash when using clustered shading with OSG 3.6 2020-04-02 21:21:55 +02:00
Stuart Buchanan
ceba006c60 Only sync scenery-path-suffix with valid name
It is possible to have a property tree with
/sim/rendering/scenery-path-suffix[n]/enabled=true
/sim/rendering/scenery-path-suffix[n]/name=<blank>

Previously this would be downloaded as scenery//[tile]

Now we just ignore such invalid paths.
2020-04-01 21:47:53 +01:00
James Turner
e9e2053c64 Strutils: add hex-decoding helper
This will be used to allow entering hex bytes in XML files, for HID
descriptor support on Windows.
2020-03-31 10:09:47 +01:00
Fernando García Liñán
bf013aea54 Compositor: Improve custom depth range handling in scene passes. 2020-03-30 19:23:03 +02:00
Stuart Buchanan
454df4872a Reduce shader attribute usage by packing floats
Previously we required 6 shader attributes to pass
in all the required information for the building shader.
By using packing techniques this has been reduced to 4.

This should improve support for integrated graphics cards.
2020-03-26 22:09:04 +00:00
James Turner
ec315bf26d Fix thumbnail cache retrieval
Use direct reading of thumbnail-cache files, instead of using libCurl
2020-03-26 16:51:00 +00:00
James Turner
74c304097d Fix for older libCurl versions 2020-03-26 16:51:00 +00:00
Erik Hofman
ad1833a4d5 Move more modern C++ idioms and use std::unique_ptr for moving daat ownership around 2020-03-26 09:54:28 +01:00
James Turner
d65bb58317 HTTP: increase the Curl download buffer size
This improves download performance with large files, i.e,
most of them.
2020-03-24 20:34:49 +00:00
James Turner
8cf11f9208 Packages: fix handling of unsafe paths in zips
This was breaking the P-51D which contains a mis-named PNG file which
tripped the unsafe path detection.
2020-03-24 13:51:57 +00:00
Fernando García Liñán
73f00bd842 Compositor: Added support for logarithmic depth buffer. 2020-03-22 17:24:48 +01:00
Fernando García Liñán
1b6545fc05 Compositor: No longer append 'Compositor/' to Effects when the Compositor is
enabled. Do not remove transparent bins in shadow passes.
2020-03-22 17:24:48 +01:00
James Turner
40725a76ab Packages: randomise mirror URL selection
Also add retry on mirrors when a download fails.
2020-03-21 18:31:53 +00:00
Erik Hofman
75f40fad95 Use more modern C++ 2020-03-20 10:40:09 +01:00
Erik Hofman
b458300c02 'lin' is a valid value for the type parameter so do not show a warning. Add a 'random' tag for pitch offset 2020-03-20 09:56:21 +01:00
Erik Hofman
6c6e74e8d1 Change logging levels to developer mode and be more descriptive 2020-03-19 13:33:30 +01:00
gallaert
4bab064ad6 - When HLA is enabled in Simgear, HLA is also enabled in Flightgear
making the build between Simgear and Flightgear consistent.
- Fix an issue with lib/lib64 path between CentOS/RedHat distributions
  and Debian derived distributions.
2020-03-15 22:29:43 +00:00
gallaert
3da2880aae Merge branch 'next' of https://git.code.sf.net/p/flightgear/simgear into next 2020-03-15 21:32:42 +00:00
James Turner
c8d802bc1d More SGPath / UTF-8 fixes
Use SGPath in more public APIs, so FlightGear doesn’t have to convert
and guess the conversion itself.
2020-03-15 14:26:42 +00:00
James Turner
c3a169319c Fix UTF-8 shader path support
Work around osg::Shader::loadShaderFromSourceFile ’s lack of UTF-8
support by loading using our own istream, and then setting the text
directly.
2020-03-15 14:26:42 +00:00
Erik Hofman
72af950573 Fix an error 2020-03-15 14:57:24 +01:00
Erik Hofman
edae7a2b75 Make warnings and alerts a single message for DEV_WARN and DEV_ALERT since they will result in a popu-dialog message 2020-03-15 14:33:05 +01:00
Erik Hofman
1fa48e7edb File readind is not of the SG_INPUT class s change it to SG_IO and make all warinigs and alerts developer-warings and developer-alerts 2020-03-15 14:24:02 +01:00
Erik Hofman
d76992efe6 Show the affected file when displaying an animation error or warning 2020-03-15 10:44:29 +01:00
James Turner
87da4680c7 Tweaks to Windows string conversion
Avoid use of code-cvt, and tolerate differences in compiler handling of
string literals (tested Clanged and MSVC, hopefully GCC is kind)
2020-03-14 20:10:20 +00:00
Richard Harrison
9cc7508b07 fixes #2123 Relocation splash distorted.
Allow splash screen as an origin and do not allow splash screens to be cached and thus the poweroftwo rules don't apply.
2020-03-13 20:07:31 +01:00
James Turner
b258bc598e Load time-zones from UTF-8 paths correctly
Use _wfopen on Windows, after converting UTF-8 paths to wchar
2020-03-13 16:48:12 +00:00
James Turner
f25c0c60a8 Use UTF8 paths in nearly all places, for OSG
We already request OSG to use UTF8 paths, so ensure whenever we pass
an SGPath to OSG, we use the correct conversion function.
2020-03-13 09:50:43 +00:00
James Turner
f350bd31bd CanvasImage: fillRect / setPixel tweaks
Fix image dirtying and allocation without an underlying fike
2020-03-11 17:12:42 +00:00
gallaert
06f1699223 Merge branch 'next' of https://git.code.sf.net/p/flightgear/simgear into next 2020-03-09 22:13:16 +00:00
James Turner
3e4d528859 Additional install / download failure test
Ensure we can install again on a previously failed URL.
2020-03-09 15:00:23 +00:00
Erik Hofman
b847f57382 Fix a threads header 2020-03-09 14:56:08 +01:00
gallaert
2d7ad25031 Merge branch 'next' of https://git.code.sf.net/p/flightgear/simgear into next 2020-03-01 16:14:13 +00:00
James Turner
d670cc13ac testHTTP mods to investigate macOS failure.
Disabled one test for now, since it’s blocking macOS release process
2020-02-25 16:12:12 +00:00
James Turner
43699d039f Track package install status persistently. 2020-02-25 14:27:48 +00:00
James Turner
9192b009e1 Catalog enable/disable feature 2020-02-24 11:35:04 +00:00
Stuart Buchanan
3fe85809b2 Add support for tarballs in Terrasync 2020-02-16 20:26:20 +00:00
Stuart Buchanan
a4b79d6882 Merge /u/tbmoore/simgear/ branch tmoore/canvas-mipmap into next
https://sourceforge.net/p/flightgear/simgear/merge-requests/52/
2020-02-12 21:02:43 +00:00
gallaert
fc21e6eafa Merge branch 'next' of https://git.code.sf.net/p/flightgear/simgear into next 2020-01-31 22:51:14 +00:00
Erik Hofman
11e243a216 Clean up the threading code: remove the private classes. 2020-01-29 11:33:54 +01:00
Stuart Buchanan
9f862dcc0e Fix directional lights for AMD
Some AMD drivers do not like triangles for point sprites, which
breaks directional lights.  This works around this by allowing
users to set /rendering/triangle-directional-lights=false which
falls back to the non-directional implementation of a point.
2020-01-27 09:28:25 +00:00
Erik Hofman
faf43d2c4b Comment out Windows debugging code, for now 2020-01-24 19:30:32 +01:00
Erik Hofman
b0580b2df5 Re-order the mutex locking of the condition-mutex and the caller-mutex, add noxecept. Also add some debugging code to detect multiple locks of the same mutex. 2020-01-24 15:46:36 +01:00
Erik Hofman
308903dc91 Revert to c3ae704610 until the condition_variable problem is solved 2020-01-24 13:52:37 +01:00
Erik Hofman
1c7fd67c39 Convert condition variables to C++11 too. 2020-01-24 10:48:53 +01:00
Erik Hofman
c3ae704610 Move SGThread::current to after the system dependent code which includes the proper header files 2020-01-23 13:47:04 +01:00
Erik Hofman
9f8e6c0634 Make backwards compatible stubs so only a recompile without any modification is required. 2020-01-23 10:38:52 +01:00
Erik Hofman
aab88b411e Switch to C++11 threads, mutexes and lock_guards. Switching to C++11 condition_variables requires some more thought. 2020-01-22 13:44:47 +01:00
James Turner
867cf6b60a test_Http: Better completion checking 2020-01-19 06:30:10 -08:00
gallaert
ed87114c5e Merge branch 'next' of https://git.code.sf.net/p/flightgear/simgear into next 2020-01-11 18:52:36 +00:00
Julian Smith
213adb5f0d simgear/debug/logstream.cxx: ensure fgfs.log always includes file:line.
This is a bit of a hack - we set line = -line if m_fileLine is false, so that
the +ve value can be recovered by FileLogCallback::operator().
2019-12-31 16:50:46 +00:00
Scott Giese
c5c10a003a Rollback error until stability issues are resolved. 2019-12-31 10:29:25 -06:00
Julian Smith
e78205f071 simgear/debug/logstream.cxx: Fixed FileLogCallback to not output <filename>:-1.
logstream::LogStreamPrivate::log() sets line=-1 to turn off both filename and
line numbers in log output, if m_fileLine is false.

StderrLogCallback::operator() already omits both file and line if line == -1;
this commit does the same thing for FileLogCallback::operator().

E.g. this fixes output e.g. to ~/.fgfs/fgfs.log.
2019-12-29 19:04:47 +00:00
Richard Harrison
ba7134c2a2 Use isfinite to determine if an FP number is valid.
ref: https://forum.flightgear.org/viewtopic.php?f=30&t=36600&sid=4bdfcb69abb4a6440cd8965aa03815d5#p357164
2019-12-29 13:50:21 +01:00
Richard Harrison
1eadaa4cda Protect against null reference when effect not found 2019-12-29 13:48:40 +01:00
Richard Harrison
d93ce29b20 Exclusive thread possible fix for Linux
The deadlock is possibly caused by the thread not being joined, or because of an implementation difference with phtreads.

So this fix adds a call to release the background thread when terminating it and then also joins the thread to await for the completion of the background thread.

As before this works fine under Win32 (x64)
2019-12-29 13:48:09 +01:00
Richard Harrison
0cddb9e843 Revert "Disable NasalMainLoopRecipient"
This reverts commit 67e2860cc3.
2019-12-29 13:45:42 +01:00
Scott Giese
67e2860cc3 Disable NasalMainLoopRecipient
This is preventing FGFS shutdown on Linux OS.
Does not appear that this thread is properly joined and terminated.
2019-12-27 20:48:02 -06:00
James Turner
0cf9dd165e Add NetBeans .gitignore 2019-12-24 20:58:48 +00:00
James Turner
3aa567b672 Merge /u/fgarlin/simgear/ branch next into next
https://sourceforge.net/p/flightgear/simgear/merge-requests/70/
2019-12-24 19:47:25 +00:00
Fernando García Liñán
fa1e3cb183 Support in Effects for reading 3D textures from disk as an array of 2D textures 2019-12-24 03:37:14 +01:00
Fernando García Liñán
14845bf3f2 Compositor: Provide previous frame transformation matrices and the sunlight direction as uniforms 2019-12-24 03:34:37 +01:00
Fernando García Liñán
120328eb2b Removed 'using namespace' from header 2019-12-24 03:31:28 +01:00
gallaert
e222c0888c Merge branch 'next' of https://git.code.sf.net/p/flightgear/simgear into next 2019-12-19 11:48:09 +00:00
James Turner
ec19dfc2f2 Fix Windows build
__PRETTY_FUNCTION__ is not portable, alas.
2019-12-18 23:43:40 +00:00
James Turner
da45c26e7d Adding pixel manipulators to canvas::Image 2019-12-17 22:32:25 +00:00
James Turner
c39a5fd8c0 Fix some Clang warnings 2019-12-17 22:32:25 +00:00
Scott Giese
fb57fe9da7 Catching an exception by value 2019-12-15 03:12:54 -06:00
Scott Giese
02496da4d2 Logic error 2019-12-15 03:08:06 -06:00
Scott Giese
eb1afec22b Remove unused object creation 2019-12-15 02:39:43 -06:00
Scott Giese
1619df97f8 Potential unterminated string 2019-12-15 02:19:13 -06:00
Scott Giese
c653a82619 Eliminate unitialized warning 2019-12-15 02:00:16 -06:00
Scott Giese
2289618cea Fix conditional compile logic 2019-12-15 00:18:39 -06:00
Scott Giese
dc886118ba [CanvasText] Fix conditional compiles 2019-12-15 00:13:46 -06:00
Scott Giese
879b01004b [CanvasText] Fix conditional compiles 2019-12-14 23:31:13 -06:00
Stuart Buchanan
86f3169e4b Shader Buildings - fix roof orientation
Correct transformation to make roof ridges match orientation
in documentation.
2019-12-08 17:36:23 +00:00
Stuart Buchanan
d5957b8c5f Random Buildings - improved texture mapping
Previously the front and side faces of random/OSM buildings
had identical texture coordinates.  This resulted in the sides
of buildings texture mapping being squeezed or stretched.

This change generates a separate texture mapping for the sides
of the buildings.
2019-11-23 17:05:20 +00:00
James.Hester
595eb3efea Change order of test for is_plantation as per feedback. 2019-11-21 20:36:56 +11:00
James.Hester
81e93448e3 Merge branch 'plantation' into next 2019-11-17 09:05:47 +11:00
James.Hester
0f9fe8adef Add generation of plantation vegetation to TileDetailsCallback.
Plantations are regularly spaced vegetation. This effect is switched
on by the is_plantation material property. Vegetation is laid out
at integer spacings in x and y, with the spacing determined by the
usual coverage properties.
2019-11-16 11:16:09 +11:00
James.Hester
56933067c0 Add "is_plantation" property to materials definitions. 2019-11-16 11:02:35 +11:00
James.Hester
0919f5bf9c Improve comments on previous commit. 2019-11-12 08:00:04 +11:00
James.Hester
b91d4411b5 Added point_in_triangle function. 2019-11-12 07:55:09 +11:00
Erik Hofman
15c6131f0e Use a better way to set the listener position and orientation 2019-11-08 10:51:56 +01:00
James Turner
c2f2f25046 Merge /u/fgarlin/simgear/ branch next into next
https://sourceforge.net/p/flightgear/simgear/merge-requests/68/
2019-11-07 17:38:31 +00:00
Fernando García Liñán
94ced66f87 Compositor: Enable more culling for shadow passes 2019-11-07 15:46:05 +01:00
Erik Hofman
ece2f913a1 Get AeonWave up to par again by removing a number of errors, and fixing a unit type for the audio cone inner and outer angle. 2019-11-07 15:15:02 +01:00
Fernando García Liñán
2b2ada2037 Compositor: Added support for spotlights in clustered shading 2019-11-07 15:09:26 +01:00
Julian Smith
1a4568175c simgear/environment/metar.*: getDescription() can now also use single spaces.
If <tabstop> param is -1, all sequences of tabs are replaced by a single space.
2019-11-06 19:56:03 +00:00
Julian Smith
0c8949e723 simgear/environment/metar.cxx:SGMetar::getDescription(): minor improvements to returned text.
remove special characters from output. We used to include 0xb0 characters -
small 'o' degree symbol, but this isn't reliable and looks bad in flightgear's
weather window.

Consistently use space between number and its unit.

remove 'METAR Report' header.
2019-11-06 19:56:03 +00:00
James Turner
012966f6a2 Merge /u/fgarlin/simgear/ branch next into next
https://sourceforge.net/p/flightgear/simgear/merge-requests/67/
2019-11-05 09:32:28 +00:00
Erik Hofman
82b1cca247 Also add pressure to the mix 2019-11-04 15:18:59 +01:00
Erik Hofman
2976dc29d5 Merge branch 'next' of ssh://git.code.sf.net/p/flightgear/simgear into next 2019-11-04 14:54:30 +01:00
Erik Hofman
99d5b0dc8a Switch to in-place declarations and to the ISO9613 distance attenuation model for AeonWave which also calcualtes the proper sound velocity based on atmospheric properties 2019-11-04 14:54:06 +01:00
Fernando García Liñán
d1eb768de6 Disabled CASTSHADOW_BIT on most of the scene graph geometry to optimize performance 2019-11-03 20:53:55 +01:00
Julian Smith
5db8e42c69 simgear/environment/metar.*: added getDescription().
Returns human-readable descrition of metar. Uses code from
flightgear:src/Main/metar_main.cxx.
2019-11-01 22:55:20 +00:00
Fernando García Liñán
c7efa81efe Only use effect schemes when the Compositor is enabled 2019-10-30 17:14:38 +01:00
James Turner
7d92698644 Merge /u/fgarlin/simgear/ branch next into next
https://sourceforge.net/p/flightgear/simgear/merge-requests/64/
2019-10-29 08:40:00 +00:00
Stuart Buchanan
4b280a00db Shader Buildings - fix roofs, set small tex to 5m
Fix a bug in the roof transformation that meant the
roof ridges were not sized correctly.  Also change
the texture "unit" width for small buildings from 6m
to 5m following discussions with Rick Gruber-Riemer.
2019-10-26 16:29:37 +01:00
Fernando García Liñán
02b61b145a Compositor: Shaders are looked for in $FG_ROOT/Compositor/Shaders when the compositor is enabled.
Fixed Compositor/ always being added to the effect filename. Now it is only added when the base folder is Effects/.
2019-10-26 01:53:30 +02:00
James Turner
0a7514c47b Add new ‘listener-safe’ SGProperty attribute
Used in FG to make certain tied properties as safe to use with
listeners, and hence suppress the warning.
2019-10-20 13:03:15 +01:00
James Turner
4b1a53367f Runway lights improvements from Sascha Reißner
(Sascha Reißner <reiszner@novaplan.at>)
2019-10-20 13:02:30 +01:00
James Turner
de3625d992 Fix linkage of OpenAL tests
Now we no longer list OpenAL a s apublic dependency of SimGear, we
need to  explicitly link the tests against OpenAL, since they use it
directly, in all cases.
2019-10-19 07:42:28 -07:00
Mykola Orliuk
65331599ec BoostTestTargets.cmake: support CMake 3.15
Signed-off-by: James Turner <zakalawe@mac.com>
2019-10-17 09:41:35 +01:00
Stuart Buchanan
74cd8cf75c Shader building roof types
Shader buildings now support multiple roof types:
- flat
- gabled
- hipped
- pyramidal
2019-10-08 21:38:48 +01:00
Stuart Buchanan
07586e84ce Rotate orientation of pitched roofs by 90 degrees
Also add some additional checking of parameters.
2019-10-06 12:52:12 +01:00
James Turner
ca97b4371b Use alternative name for internal expat_config.h
Avoid clashes when system has expat installed.
2019-10-05 17:19:09 +01:00
Scott Giese
b742607c76 [ReaderWriteSTG] fix for missing header. 2019-10-03 11:50:07 -05:00
ThomasS
1399cc9482 Additional interface to ReaderWriterSTG for adding callbacks for unknown STG token handler 2019-10-03 08:03:37 +02:00
Stuart Buchanan
7464e17e22 Swap width and depth for shader defined buildings
Previously the width was set as the x-axis and
depth for the y-axis when passed through to the
shader. This was incorrect, as the x-axis is actually
the depth of the building.
2019-10-01 14:56:18 +01:00
Stuart Buchanan
053bda26a4 Shader buildings - split wall and roof tex index
Previously there was very limited texture variations
as a given texture index was used for both the wall
and ceiling.  Now these can be specified separately,
allowing for more variation in both STG defined and
random buildings.
2019-09-25 21:53:19 +01:00
James Turner
93995cd742 Nasal GC : Fix crash running FG unit-tests
When freeing a context, clear its stack so that anything dangling in
them (especially the opStack) is GCed.
2019-09-19 08:05:55 +01:00
Scott Giese
202dfc9ebe Merge branch 'next' of https://git.code.sf.net/p/flightgear/simgear into next 2019-09-17 12:51:47 -05:00
Scott Giese
23f378cd93 Support VS 2019 - courtesy of Lars Toenning 2019-09-17 12:51:05 -05:00
Scott Giese
70c564dd2c Fix compile issue on Win10 VS2019 2019-09-16 23:30:59 -05:00
Julian Smith
9bea6afb80 Revert "simgear/scene/model/ModelRegistry.cxx: avoid noisy diagnostic from OSG."
This reverts commit 7762bc2cad.

This broke Windows and Mac builds.
2019-09-05 15:00:22 +01:00
Julian Smith
20fb7a9ce0 simgear/debug/logstream.cxx: Allow runtime control of file:line in diagnostics.
New fn logstream::setFileLine() allows other code to control whether log output
includes file:line information.
2019-09-02 16:10:38 +01:00
Julian Smith
7762bc2cad simgear/scene/model/ModelRegistry.cxx: avoid noisy diagnostic from OSG. 2019-09-02 16:10:10 +01:00
Julian Smith
f407785f10 Fixes for building on OpenBSD. 2019-08-22 17:35:13 +01:00
Stuart Buchanan
138e28fcbe Instance-based random buildings
Replace existing random buildings implementation with one using
proper instancing.  This allows better control of the buildings
themselves and allows BUILDING_LIST STG verb to specify
individual building dimensions.  See README.scenery for details.
2019-08-20 17:00:17 +01:00
Richard Harrison
38f6a5a40a Remove fairly irrelevant startup log message from NasalMainLoopRecipient
This is probably the reason why the tests are failing.
2019-08-14 16:47:25 +02:00
Scott Giese
b28f030874 [CanvasText] Restore support for OSG 3.4 2019-08-04 10:43:00 -05:00
Scott Giese
d883ab278d [osgText.getActiveFont] Breaking change within OSG 2019-08-04 02:27:57 -05:00
gallaert
40ce45c8e9 Merge branch 'next' of https://git.code.sf.net/p/flightgear/simgear into next 2019-07-13 18:12:22 +01:00
Edward d'Auvergne
fa754ecaf8 SGSubsystem classes: Registration of all subsystems. 2019-07-01 14:37:49 +02:00
Edward d'Auvergne
c11557316d SGSubsystemMgr: Changed the Registrant class argument order.
The updateInterval argument is used a lot less than the dependency list, so
these have been swapped.  That simplifies the registration code in the subsystem
implementations as the updateInterval default of 0.0 no longer need to be
supplied.
2019-07-01 14:37:49 +02:00
Edward d'Auvergne
5bf28d3f90 SGSubsystemMgr: Expanded the dependency list types.
All the dependency types are now documented.
2019-07-01 14:37:49 +02:00
Edward d'Auvergne
c8625ce599 SGSubsystem classes: Addition of staticSubsystemClassId() to all subsystems. 2019-07-01 14:37:48 +02:00
Edward d'Auvergne
991fafb839 SGSubsystemGroup: Removal of the subsystem group naming.
This is a partial reversion of c1dd8faa29 and a
complete reversion of caea68007e and
846f024e91.  These changes are incompatible with
the subsystem class IDs.
2019-07-01 11:07:03 +02:00
Edward d'Auvergne
d7323f5f19 SGSubsystem classes: Renaming of the subsystem ID variables and functions. 2019-07-01 10:51:41 +02:00
Edward d'Auvergne
10662e4ca5 SGSubsystem classes: Subsystem and subsystem group API declaration standardisation.
This is a cleanup commit.
2019-06-16 14:12:38 +02:00
Edward d'Auvergne
b73dfb08ae SGSubsystem classes: Whitespace standardisation of the declarations.
This is a clean up commit prior to the subsystem API standardisation to simplify
the diffs.  It includes all SGSubsystem and SGSubsystemGroup derived classes.
2019-06-16 14:12:38 +02:00
Scott Giese
d8a46cffa5 [emesary] Fix naming conflict with some compilers 2019-06-15 17:22:53 -05:00
Scott Giese
3b3093c72e [nasal] remediate segfault in lib.c
state of vaCopy is altered during each usage, so it needs to be discarded and not reused by multiple calls.
2019-06-15 11:54:35 -05:00
Scott Giese
3d841a214c Merge branch 'next' of https://git.code.sf.net/p/flightgear/simgear into next 2019-06-14 22:12:02 -05:00
Scott Giese
2cbbb29eb5 [Emesary] Fix compile on Linux. 2019-06-14 22:11:31 -05:00
Scott Giese
c0677ad8c5 [Emesary] Fix compile on Linux. 2019-06-14 21:43:47 -05:00
James Turner
098c5b0924 Merge /u/fgarlin/simgear/ branch next into next
https://sourceforge.net/p/flightgear/simgear/merge-requests/62/
2019-06-11 21:56:28 +00:00
Richard Harrison
01ac9a71b7 Fix case sensitive filename for emesary.cxx 2019-06-11 23:40:21 +02:00
Fernando García Liñán
ab8b1d39bd Fixed undetected compiler error due to gcc accepting variable-length arrays 2019-06-11 23:05:09 +02:00
James Turner
b22a8debf2 Merge /u/fgarlin/simgear/ branch next into next
https://sourceforge.net/p/flightgear/simgear/merge-requests/61/
2019-06-11 14:59:05 +00:00
Richard Harrison
92a3c8bbd8 Added Nasal garbage collection background thread
This uses an SGThreadExclusive controlled by Emesary notifications that are received from the main loop.

When active at the end of a frame the garbage collection thread will be released; if it is already running this will do nothing. Optionally at the start of the mainloop we can wait for the previous GC to finish.

The actions of the background GC is controlled by notifications - again received from the main loop which in turn uses properties.

I initially thought that the wait at the start of the frame would be necessary; however in 100 or so hours of flight without the await for completion at the start of frame no threading problems (or any other problems) were shown; so nasal-gc-threaded-wait is defaulted to false which gives a slight boost in performance.

So what this does is to it removes the GC pause of 10-20ms every 4 seconds (test using the F-15). This change doesn't really give much extra performance per frame because normally GC is only performed when needed.
2019-06-11 13:44:52 +02:00
Richard Harrison
c71f287498 SG Threading added new class for an exclusive thread
An exclusive thread is one that is suited to being used where the thread needs to be activated at a certain point, and also that the code activating the thread may also need to wait for thread completion.

Example of this is the new background garbage collection for Nasal. The thread will be activated at the end of the frame processing and at the start of the next frame the thread can be awaited - thus allowing the thread to work in parallel with the rendering.
2019-06-11 13:30:29 +02:00
Richard Harrison
b7f8fbe7a0 Nasal lib: dosprintf
rework to measure the required length (vsnsprintf does this with a size of zero and a nullptr as the first parameters) - and then just alloc the required space. Should be more efficient than the looping version.
2019-06-11 12:38:49 +02:00
Fernando García Liñán
1ca3f60f80 Compositor: Significant rework of the clustered shading feature
- Added light definitions to the model XML files. This is not compatible with Rembrandt.
- Added depth slicing.
- Added threading support. For now it's faster to run everything on the main thread since there aren't many lights.
2019-06-10 16:02:32 +02:00
Richard Harrison
7a903361e6 Added Emesary
derived from: https://github.com/Zaretto/Emesary/tree/master/src/Emesary/EmesaryC++

This is a similar implementation to that in Nasal; currently this is only for use in C++ modules as there is no Nasal interface - however this is something that may be added later.

The basic premise of the Emesary messaging system is to allow the decoupled operation of the various components that comprise any system - usually within the same process.

The basic unit of communication is a Notification, which is passed around to any and or all objects that implement the IReceive interface (pure virtual class). Using Interfaces and ihneritance
it is possible to pass around Notifications that have special meanings to certain objects and allow them to perform the appropriate function.

Notifications are created and sent via a call to NotifyAll. Any object within the system can inherit from IReceive and register itself with a Transmitter to receive all notifications that are sent out.

The underlying concept is that one part of a system knows that something needs to be done without needing to know how to do it. The part of the system that needs something done simply creates a notification and sends it out. Once received by the part of the system that is capable of performing the requested Notification the relevant actions will be carried out and a status of OK or Finished returned.

The return code from the Receive method must follow the guidlines as follows:

If a notification is not recognised in the recipient must return ReceiptStatusNotProcessed. Normally a recipient will return ReceiptStatusOK, sometimes ReceiptStatusFail to indicate that the recipient could not properly process the notification.

The overall result of a call to NotifyAll is a composite of the results of each individual recipient's return value; and generally OK or Fail. It is acceptable to use the return code to ascertain success or failure and take appropriate action.

It is generally recommended that more detail specific to the particular purpose be included within the Notification to allow more control.

Notifications can be modified during processing to permit a multistage process; however this needs to be designed correctly.

* ReceiptStatusOK : Notification processed fine by Receive, transmitter continues with next recipient
* ReceiptStatusFail : Notification processed but failed, transmitter continues with next recipient
* ReceiptStatusAbort : Notification processed and the receive method declares a fatal error; the transmitter will stop notifying recipients
* ReceiptStatusFinished : Notification processed and the recipient declares that the processing is completed, the transmitter will stop notifying recipients
* ReceiptStatusNotProcessed : receive method does not recognise the notification

These status are for later implmentation.
* ReceiptStatusPending : (future ues); notification been sent but the return status cannot be determined as it has been queued for later processing, transmitter continues with next recipient
* ReceiptStatusPendingFinished : Message has been definitively handled but the return value cannot be determined. the transmitter will stop notifying recipients
2019-06-09 13:07:50 +02:00
Richard Harrison
4f6e72de55 UDNS Windows compatibility changes
For some reason the config file doesn't generate with WINDOWS defined on my system.
2019-06-08 15:49:15 +02:00
Richard Harrison
a68c4e9434 DDS Texture cache improvements
- change to always use the SG create mipmap function as it works better (no purple clouds)
- when enabled always generate a mipmap even if the file couldn't be compressed
- adjust level of log messages
- move lru_cache into its own header file.
2019-06-08 15:38:22 +02:00
Richard Harrison
798b90e0a5 Added Emesary 2019-06-08 10:12:22 +02:00
Richard Harrison
cba902f22b Nasal GC background threaded merge.
Merge branch 'nasal-background-thredead-gc'
2019-06-08 10:07:48 +02:00
Richard Harrison
8eb51e813f Added Emesary to SimGear Core 2019-06-03 23:32:49 +02:00
Richard Harrison
9ac3c1a394 Revert GC instrumentation committed by mistake as part of "SGTimeStamp elapsedUSec"
This reverts commit 0b114ac5cd, reversing
changes made to 39eb9837e9.
2019-05-27 18:49:33 +02:00
Tim Moore
5486ca3b4a Protect logstream startup entries with a mutex 2019-05-24 10:15:06 +01:00
Tim Moore
90845974ea nasal/lib.c: Make copy of va_list for each traversal
It's not portable to traverse a va_list more than once.
2019-05-24 10:14:50 +01:00
James Turner
57fd817486 Merge /u/fgarlin/simgear/ branch next into next
https://sourceforge.net/p/flightgear/simgear/merge-requests/58/
2019-05-22 12:57:13 +00:00
Fernando García Liñán
b322fa8f32 Use $FG_ROOT/Compositor/Effects instead of $FG_ROOT/Effects when the compositor is enabled
This allows coexistence of new compositor-compatible effects and current effects.
2019-05-20 23:40:12 +02:00
Fernando García Liñán
81d1e16d7b Compositor: Removed intersection checking
This is now responsibility of the CameraGroup.
2019-05-20 22:51:40 +02:00
Erik Hofman
4c52d77aa5 Add missing cassert incldues 2019-05-20 11:00:51 +02:00
James Turner
523d5166ef Merge /u/dancliff/simgear/ branch fix_sound_on_model_reload into next
https://sourceforge.net/p/flightgear/simgear/merge-requests/57/
2019-05-14 20:33:12 +00:00
Dan Wickstrom
cde95864b4 Aircraft model reinit deletes sound effect samples, but leaves them defined in the sample group, so a reload doesn't re-add them. 2019-05-14 12:51:35 -04:00
Richard Harrison
7354201b5d Added background (threaded) garbage collector 2019-05-13 15:21:08 +02:00
Scott Giese
14971f88ee [soundmgr_openal] Pause/Resume Sound.
The following changes fixes a case for me where I hear the sound change levels up and down for each pause un-pause cycle.
Patch provided by daniel.c.wickstrom@gmail.com.
2019-05-09 20:52:20 -05:00
Richard Harrison
0b114ac5cd SGTimeStamp elapsedUSec 2019-05-07 05:17:28 +02:00
Richard Harrison
c06eabff24 Instrumented Nasal GC 2019-05-07 05:17:27 +02:00
Richard Harrison
b3ef2478f5 Instrumented Nasal GC 2019-05-07 05:13:55 +02:00
Richard Harrison
39eb9837e9 Merge /u/fgarlin/simgear/ branch next into next
https://sourceforge.net/p/flightgear/simgear/merge-requests/55/
2019-05-05 13:50:27 +00:00
Scott Giese
1a429b63c5 [SGImageUtils] eliminate unused variable. 2019-05-04 15:45:53 -05:00
Scott Giese
ae6ad20940 [SGDemSession] fix uninitialized members. 2019-05-04 15:26:25 -05:00
Fernando García Liñán
3e57b50066 Added support for Effect schemes
Effect schemes are a way of rendering an object in different ways depending on the Compositor pipeline. A new <scheme> tag in the Compositor pipeline definition allows the user to choose which Effect scheme is going to be used for that pass. Every Effect will then be rendered using the technique that has a matching scheme name. If no valid technique is found, it won't be rendered. Since it would be a pain to define a valid technique for every scheme and for every Effect, the file '$FG_ROOT/Effects/schemes.xml' is introduced:

<scheme>
  <name>test</name>
  <fallback>Effects/test</fallback>
</scheme>

If an Effect doesn't have a valid technique for the 'test' scheme, the 'Effects/test.eff' Effect will be merged with it. This process is done at initialization when techniques are being realized.
2019-05-04 19:15:22 +02:00
Florent Rougon
dd38e399ca Add readwav.hxx to HEADERS and readwav.cxx to SOURCES in all cases
Previously, these two files were only added when SG was built with
-DUSE_AEONWAVE:BOOL=OFF, because the SG code in use when USE_AEONWAVE is
set to ON doesn't need readwav.hxx nor readwav.cxx. However, readwav.hxx
and readwav.cxx are not only used in SG, but also in FG (at least by
flightgear/src/Sound/VoiceSynthesizer.cxx), and this was causing a build
failure when SG's USE_AEONWAVE and FG's ENABLE_FLITE flags were both set
to ON:

  https://forum.flightgear.org/viewtopic.php?f=45&t=35672#p346633

and

  https://sourceforge.net/p/flightgear/mailman/message/36645567/

This commit should fix this build failure.
2019-04-27 11:23:14 +02:00
Florent Rougon
319b59500c Convert obsolete uses of get_filename_component(<var> <FileName> PATH)
In the context of get_filename_component(), PATH is a legacy alias for
DIRECTORY. Replace it with DIRECTORY, which is recommended[1] for
CMake versions > 2.8.11.

[1] https://cmake.org/cmake/help/latest/command/get_filename_component.html
2019-04-25 19:19:19 +02:00
Fernando García Liñán
3a79b71e80 Compositor: Effects used by a compositor now receive a proper SGReaderWriterOptions and other misc fixes 2019-04-23 03:35:10 +02:00
gallaert
0c0eab9247 Merge branch 'next' of https://git.code.sf.net/p/flightgear/simgear into next 2019-04-22 19:08:39 +01:00
James Turner
114ddcff52 Tweak code for older GCC 2019-04-11 21:39:41 +01:00
James Turner
57339486d9 Flip the default meaning of alternate-mouse-wheel-dir 2019-04-11 20:37:47 +01:00
Richard Harrison
5f9b17d55b Terrasync fixes;
- remove the old svn properties; a scan of FGAddon and FGdata showed these are not in use except for the dialog
- new property "active" - which is true when the worker thread is running.
- changes to enabled now take effect immediately; whereas before a reinit had to be issued. This could lead to confusing GUI state as the enabled property would be true but without closing the dialog (e.g. cancelling it) terrasync could be in a different state.
- use property nodes (performance optimisation)
2019-04-08 14:08:58 +02:00
Tim Moore
325b89e023 Fix automatic mipmap generation for Canvas textures
When using glTexStorage2D, OSG doesn't allocate storage for the levels
of the mipmap above the base unless an image is assigned to the
texture.
2019-04-04 12:07:34 +00:00
Tim Moore
9ebc823975 Optimize parsing of property node names
Add hand-coded versions of isalpha() and isdigit(), which were a
hotspot in profiling.
2019-04-04 12:00:18 +00:00
James Turner
5fdf6ef4f3 Simgear parts of queued command execution
Extend the command API to allow posting commands from arbitrary threads,
for deferred execution on the main thread. Additional pieces on the FG
side to follow shortly.
2019-03-24 14:13:42 +00:00
Richard Harrison
0792962591 add simgear::strutils::replace 2019-03-17 18:20:27 +01:00
Torsten Dreyer
f964374027 new version: 2019.2.0 2019-03-13 21:20:49 +01:00
Torsten Dreyer
4352c0a737 new version: 2019.1.1 2019-03-13 21:20:49 +01:00
Stuart Buchanan
a0bdcb30fd Correct log messages. 2019-03-12 21:53:22 +00:00
Stuart Buchanan
7e0a10c016 Set radius for PagedLOD
Speculative fix to address scenery chunks being unloaded
unexpectedly under high load.
2019-03-12 21:45:34 +00:00
Richard Harrison
7fb6f82ab1 Merge /u/fgarlin/simgear/ branch next into next
https://sourceforge.net/p/flightgear/simgear/merge-requests/49/
2019-03-07 15:21:35 +00:00
Scott Giese
fdcc9c9e0e Fix compile issue with OSG versions below 3.4 2019-03-05 23:56:47 -06:00
Richard Harrison
846f024e91 SGSubsytem name compatibility for other compilers;
changed to use const char* rather than const std::string & as this doesn't work on GCC; and besides std::string converts nicely to const char* and most of the times this is used it is actually a string literal.
2019-03-01 19:00:43 +01:00
James Turner
7e969f4e28 Updated star rendering phases/parameters
As part of this, adjust the normalisation so the active cutoff value
corresponds to 0.0 normalised intensity.
2019-02-25 16:12:41 +00:00
James Turner
10a701308d Make star cutoff controllable at runtime.
When a new property /environment/star-magnitude-cutoff is defined,
use this to set an upper bound on the magnitude of stars displayed.
2019-02-25 16:12:41 +00:00
Fernando García Liñán
e2bed86bfc Compositor: Enable small feature and view frustum culling by default for every pass. Added option to inherit the view master camera cull mask. 2019-02-23 23:26:27 +01:00
Richard Harrison
487cdc85cd DDS-TC; retain original filename and fix previous commit
Set the filename back to what it was after loading - seems sensible and as yet no side effects.

Also the previous commit that included the handling of sgio_exceptions also broke the DDS-TC pretty much completely.
2019-02-21 19:07:37 +01:00
Richard Harrison
052bbecc3b OSG 3.4.0 compilation fixes and anisotropy
Set anisotropy on model textures after loading.
2019-02-20 19:10:58 +01:00
Richard Harrison
c860b12b3d OSG 3.4.0 compilation fixes 2019-02-20 19:10:58 +01:00
Richard Harrison
e227008331 DDS-TC handle sgio_exceptions during hash creation.
ref: https://sourceforge.net/p/flightgear/codetickets/2113/
2019-02-20 19:10:58 +01:00
Fernando García Liñán
72f3a784de Compositor: Added support for static branching and <property> tags to be able to configure the Compositor at startup. 2019-02-20 03:05:55 +01:00
Richard Harrison
31c9df665d Fix compile error related to missing osg version include (< 3.6)
For some reason this compiles on 3.6.
2019-02-19 15:11:04 +01:00
Richard Harrison
dd240116b1 Compositor compilation fixes for OSG 3.6 and Win32 2019-02-19 08:59:54 +01:00
Richard Harrison
92ec139175 Merge /u/fgarlin/simgear/ branch next into next
https://sourceforge.net/p/flightgear/simgear/merge-requests/47/
2019-02-19 07:28:58 +00:00
Stuart Buchanan
f076c43b90 Improve parsing of state machines
State machines will now throw exceptions if errors are encountered
when loading the XML file.

This includes:
- Unknown <source> or <target> states referenced by transitions
- States and transitions without <name> elements
- Transitions without <target> or <condition> elements
- State machines with fewer than two states

Previously the state machine would load and then crash when hitting
such malformed states or transitions.  Now it will refuse to load,
while providing useful error messages as to the problem.
2019-02-14 22:15:38 +00:00
Stuart Buchanan
98215e3dbd Improve error messages for Nasal prop references. 2019-02-14 22:13:54 +00:00
James Turner
6197098541 Guard against a potential race init-ing libCurl 2019-02-13 12:49:21 +00:00
James Turner
34b3c52a28 Improve HTTP redirect handling, and add test.
Ensure we get the final status code for the request after redirecting.
2019-02-13 12:34:17 +00:00
Richard Harrison
caea68007e Fix subsystem tests 2019-02-11 08:49:40 +01:00
Richard Harrison
c1dd8faa29 Improved subsystem performance monitoring and overrun detection
This is to help diagnose where performance is being used and augments the current performance monitor.

The easiest way to active this is to open the performance monitor; however setting the enable property will enable less intrusive logging and overrun detection.

Using the GUI performance monitor itself affects the performance when it updates.

Properties

/sim/performance-monitor/enabled - need to be true to be active (unchanged)
/sim/performance-monitor/dump-stats - hierarchical dump to console of all current timing stats for all subsysems
/sim/performance-monitor/interval-s - reporting interval (unchanged)
/sim/performance-monitor/max-time-per-frame-ms - any subsystem that takes more than this amount of will be logged to the console
2019-02-10 17:48:21 +01:00
James Turner
95d065b3d7 Add strutils::iequals helper
Replaces another boost method
2019-02-06 12:35:08 +00:00
Scott Giese
2303da846b Fix missing header for OSG macros.
Patch provided by Stewart Andreason.
2019-02-05 21:46:02 -06:00
Scott Giese
7246edcb4b [boost::enable_if] Support Boost versions < 1.56 2019-02-05 12:20:06 -06:00
Fernando García Liñán
dc2f24dbed Compositor framework initial commit. 2019-02-04 18:02:44 +01:00
Scott Giese
4b793d5344 Remove deprecated boost/utility.
This is enable compatibility with boost 1.69.
2019-02-02 19:46:04 -06:00
gallaert
72dd45388f Merge branch 'next' of https://git.code.sf.net/p/flightgear/simgear into next 2019-02-01 12:34:40 +00:00
Richard Harrison
b76f78c6da OSG < 3.4.x fixes
This will use the inherently unsafe versions of the load methods which can result in deleting an object (from the cache) that has just been loaded in the database thread.

Symptom OSG WARN deleting still referenced object.
2019-01-26 17:48:31 +01:00
Richard Harrison
30482a3599 Fix errant spaces in DDS cache log file 2019-01-26 17:48:31 +01:00
Erik Hofman
b3f9571b2d Merge branch 'next' of ssh://git.code.sf.net/p/flightgear/simgear into next 2019-01-26 09:13:17 +01:00
Richard Harrison
0d5afe83a2 No need to set precipitation (rain/snow) each frame when it is turned off. 2019-01-24 18:07:05 +01:00
Richard Harrison
8b35c9a4b1 Improvements to the DDS texture cache
- use a file contents hash instead of filepath.
- add a local lru cache for filepath->hash (for performance improvements)
- calculate and resize to nearest power of two
- handle normal maps and images from effects differently.
- when cache is active any image that can't be converted to a dds will have a mipmap generated (which still helps the loading process); although this may be responsible for introducing purple into transparent images..
2019-01-24 18:07:05 +01:00
Richard Harrison
a5d8eec25f Fix for deleting referenced object from model registry
This should have been in the previous commit - However I managed to mess up the merging of this module due to other changes related to the DDS texture cache.
2019-01-24 18:07:05 +01:00
James Turner
df6ec4f94c Property alias loop protection
Patch by Henning Stahlke
2019-01-22 17:24:21 +01:00
James Turner
61ebee12ad Set curl VERBOSE based on current SG log settings 2019-01-22 17:24:21 +01:00
Scott Giese
105d63a697 Simgear: LARGE_INTEGER is defined in Windows.h
Fixes build issues on Linux.
2019-01-20 20:35:08 -06:00
Richard Harrison
07036ac48f Add load origin hint to options
This is to allow the DDS cache to handle the image load differently depending on the origin.
2019-01-20 18:28:37 +01:00
Richard Harrison
4f6c8f7784 Better error handling on load failure 2019-01-20 18:28:37 +01:00
Richard Harrison
8e57a61aed Fix null ref during load.
This happened a few times
2019-01-20 18:28:37 +01:00
Richard Harrison
8707005a97 Fix particles active even when disabled during load.
Possibly this could be fixed better by using the plugin string data - but there is nothing that currently set this; and it seems easier to use the particle callback enabled flag.
2019-01-20 18:28:37 +01:00
Richard Harrison
cb024dd82d Fix for deleting still referenced object
ref https://sourceforge.net/p/flightgear/codetickets/2105/

Use the thread safe versions (getRef) of the objectcache methods
2019-01-20 18:28:37 +01:00
Richard Harrison
84046a6717 Add missing method 2019-01-20 18:28:37 +01:00
Richard Harrison
8bcaa50ba6 OSG3.7 changes 2019-01-20 18:28:37 +01:00
Richard Harrison
c433d29171 Win32: use high resolution timers (QueryPerformanceCounter) if available 2019-01-20 18:28:36 +01:00
Richard Harrison
64d51b5290 sg_file: Add compute hash method 2019-01-20 18:28:36 +01:00
Erik Hofman
6393b7704e Do not attempt to deregister the same emitter more than once 2019-01-16 14:55:37 +01:00
Erik Hofman
0a2071b2f1 Use AAX_PROCESSED since AAX_STOPPED is only a request to stop but the library decides when it is actually stopped. And AeonWave has become more picky about destroying emitters which aren't completely processed yet since MIDI support was added. 2019-01-15 11:38:38 +01:00
Erik Hofman
7981809f36 Split up SIMD support in ENABLE_SIMD which enables sse2 support for the compiler and ENABLE_SIMD_CODE which enables the hand crafted SIMD math functions which defaults to OFF now since compilers have catched up on generating optimized vectorized SIMD code. 2019-01-15 10:36:23 +01:00
Scott Giese
ed87a0b032 Eliminate compiler warnings 2018-12-23 22:16:05 -06:00
James Turner
fc2630e66d Revert "Logging API changes"
This is causing perf impact, so reverting until we figure out
a lower-impact solution.

This reverts commit 1be3e5771a.
2018-12-23 09:17:15 +00:00
Stuart Buchanan
ec14050890 General tiles with only BUILDING_LIST
Bug found during code-read.  If tile just contained BUILDING_LIST
entries (i.e. no OBJECT_STATIC or OBJECT_SIGN entries), then it would
not generate.
2018-12-22 16:52:05 +00:00
James Turner
4b17ea95fc Random buildings: reduce log output in a common case 2018-12-20 16:10:36 +00:00
James Turner
1be3e5771a Logging API changes
Remove would_log() : callbacks receive all log messages so they can
do custom filtering individually.

Also move popup message handling into the private (d-ptr) of sglog(),
to keep the header as minimal impossible (no include of <vector>)
2018-12-20 16:10:36 +00:00
Stuart Buchanan
232cbbac5e DEBUG logging of model animation counts. 2018-12-19 20:05:06 +00:00
gallaert
dee204d2b2 Merge branch 'next' of https://git.code.sf.net/p/flightgear/simgear into next 2018-12-13 21:59:16 +00:00
Stuart Buchanan
b71e9eac51 Fix 2092 DDS cache: segmentation fault when reloading scenery.
https://sourceforge.net/p/flightgear/codetickets/2092/

DDS cache: segmentation fault when reloading scenery.
2018-12-10 21:21:44 +00:00
gallaert
f88636f47c Merge branch 'next' of https://git.code.sf.net/p/flightgear/simgear into next 2018-12-06 12:35:21 +00:00
Edward d'Auvergne
8a301efe8b PropertyObject: create() function support for SGPropertyNode_ptr. 2018-12-04 12:38:14 +01:00
Edward d'Auvergne
5bd393135f PropertyObject: Unit test for declaration followed by definition. 2018-12-04 12:38:14 +01:00
Edward d'Auvergne
d550eac525 PropertyObject<std::string>: Added a default ctor. 2018-12-04 12:38:14 +01:00
Edward d'Auvergne
fea59adbbe PropertyObject: Unit test for declarations.
This shows a failure for the declaration of std::string type PropertyObjects.
2018-12-04 12:38:14 +01:00
Richard Harrison
dae0d0ff8b Logging changes.
1. Anything that comes in from OSG will always be logged; it seems
somewhat redundant to have to set the FG log level to be identical to
the OSG log level to see these messages.

2. Console output will now have delta time (since start), priority and class.

3. Log file output wil also have delta time (since start), priority and class.

Times are displayed as delta decimal seconds since the logger was created

e.g.
  1294.06 [INFO]:terrain    Some log message
2018-11-29 19:29:14 +01:00
Richard Harrison
86d739cc85 DDS TextureCache: remove unused code to support non-persistent images
- this didn't compile for Stuart on OSG3.2 and it's not used.
2018-11-25 23:48:58 +01:00
Richard Harrison
9d99f4ce30 Added DDS Texture Cache.
This is a performance improvement that reduces the amount of frame pauses which are related to mipmap creation when the geometry (osg::Texture) is added to the scene graph within osg::Texture::applyTexImage2D_load

The texture cache is configured from FG as follows
- /sim/rendering/texture-cache/cache-enabled
- /sim/rendering/texture-cache/compress-transparent
- /sim/rendering/texture-cache/compress-solid
- /sim/rendering/texture-cache/compress

These properties are set via the SGSceneFeatures singleton.

When the texture cache is enabled it will auto convert files from any supported osg::Image format that can be read and store the resulting (compressed or raw) file in the texture cache.

The texture cache uses osg_nvtt to perform texture compression (and mipmap generation) if available. When not available simgear::effect::computeMipmap is used to make mimaps but compression isn't available.

The texture cache filename ends with .TIME.cache.dds where TIME is the hex modtime of the original file. As yet there isn't a clean way to maintain the texture cache to ensure that stale files are removed; and in fact this is quite difficult to do because of the dynamic nature of the cache.

The texture cache will be stored in download_dir/texture-cache unless --texture-cache is passed on the command line.

The UI has a single checkbox to turn the texture cache on or off.
2018-11-24 20:02:33 +01:00
Stuart Buchanan
460a666234 STG-defined buildings using Random Building shader
Support BUILDING_LIST STG verb which references a file
containing the cartesian coordinates of individual buildings,
to be generated using the random-building shader approach.

BUILDING_LIST buildings.txt OSM_Building -2.72943543 56.00080606 36.1 0

buildings.txt:

0 0 0 0 0
0 100 0 0 0
0 200 0 0 0
0 300 0 0 1
0 400 0 0 1
0 500 0 0 2
100 0 0 0 2
200 0 0 0 2
300 0 0 0 2
400 0 0 15 2
2018-11-18 21:08:06 +00:00
Stuart Buchanan
ba9342cd4b Fix effects for MP models - ticket 2076
https://sourceforge.net/p/flightgear/codetickets/2076/

Effects were being instantiated by the loader for
all models, rather than just simple .ac/.obj models.
2018-11-06 17:01:01 +00:00
gallaert
c37eae3b56 Merge branch 'next' of https://git.code.sf.net/p/flightgear/simgear into next 2018-11-04 15:52:05 +00:00
Richard Harrison
61cc8d6c50 Fix exception when number of primitive sets not what assumed.
This was something that happened when random vegetation was off, but tree shadows was on.

Adding random vegetation would then reliably cause an exception.
2018-10-30 20:13:32 +01:00
Richard Harrison
758ab5e581 LOD ranges
The scenery ranges for bare and rough are now deltas, to avoid overlapping values by user error.
2018-10-30 20:12:12 +01:00
James Turner
f05ff37560 Fix for assert with empty systems
Empty subsystem groups didn’t set their init state correctly, leading
to an assert on post-init. Fix this and add a test for it.

https://sourceforge.net/p/flightgear/codetickets/2043/
2018-10-23 15:29:56 +01:00
Stuart Buchanan
14354090f9 Don't check for cloud movement every frame
Under Basic Weather, the cloudfield is finite size and clouds
are shifted as the viewpoint changes.  Previously each cloud
was checked every frame to determine if it should be shifted.
Not this only occurs if the viewpoint has moved a non-trivial
distance.

Note that this is separate from the clouds moving due to the wind.
2018-10-18 22:28:39 +01:00
James Turner
0185884ca8 Catalogs: allow migration to alternate IDs 2018-10-16 09:56:56 +01:00
gallaert
5665403d66 Merge branch 'next' of https://git.code.sf.net/p/flightgear/simgear into next 2018-10-15 22:29:01 +01:00
James Turner
46b271d75c Packages: improve localised string support 2018-10-12 10:49:14 +01:00
James Turner
854b958797 Packages: check for existing update when scheduling
This is fixing an issue identified in the launcher in a secondary way,
to ensure if another user of the API tries to schedule an already
scheduled package, we ignore the second request.
2018-10-05 10:40:35 +01:00
James Turner
ff52b55a25 Mac: Set CMake OS-X deployment target correctly
Also raises the OS-X min version to 10.9 for libc++ compat
2018-10-01 22:28:56 +01:00
gallaert
e755735ac2 Merge branch 'next' of https://git.code.sf.net/p/flightgear/simgear into next 2018-09-30 16:25:33 +01:00
Scott Giese
69e20a3931 Bug Fix #1922 Wrong retun type 2018-09-29 18:41:00 -05:00
James Turner
05da3db2ed Fix a debug message left in the terrasync code 2018-09-24 14:54:20 +01:00
Torsten Dreyer
6d89cc6c1d new version: 2018.4.0 2018-09-21 17:18:46 +02:00
gallaert
35d1bb1149 Merge branch 'next' of https://git.code.sf.net/p/flightgear/simgear into next 2018-08-29 21:01:36 +01:00
gallaert
5921fd19fb Merge branch 'next' of https://git.code.sf.net/p/flightgear/simgear into next 2018-07-18 20:48:01 +01:00
gallaert
fce06105f9 Merge branch 'next' of https://git.code.sf.net/p/flightgear/simgear into next 2018-05-26 19:05:11 +01:00
gallaert
b01718aae7 Merge branch 'next' of https://git.code.sf.net/p/flightgear/simgear into next 2018-04-25 21:08:46 +01:00
gallaert
9ce026e22e Merge branch 'next' of https://git.code.sf.net/p/flightgear/simgear into next 2018-04-12 18:56:37 +01:00
gallaert
4796c8b7f9 Merge branch 'next' of https://git.code.sf.net/p/flightgear/simgear into next 2018-04-07 20:15:42 +01:00
gallaert
2ee5127748 Merge branch 'next' of https://git.code.sf.net/p/flightgear/simgear into next 2018-03-25 18:27:57 +01:00
gallaert
934e769513 Merge branch 'next' of https://git.code.sf.net/p/flightgear/simgear into next 2018-03-04 19:47:48 +00:00
gallaert
249e6130fd Merge branch 'next' of ssh://git.code.sf.net/p/flightgear/simgear into next 2018-02-22 22:33:47 +00:00
gallaert
8d265a8123 Merge branch 'next' of ssh://git.code.sf.net/p/flightgear/simgear into next 2018-01-21 20:06:46 +00:00
gallaert
e6a540660d Merge branch 'next' of ssh://git.code.sf.net/p/flightgear/simgear into next 2017-12-18 19:14:12 +00:00
gallaert
12bed8e8ad Merge branch 'next' of ssh://git.code.sf.net/p/flightgear/simgear into next 2017-11-17 18:46:21 +00:00
gallaert
5305095832 Merge branch 'next' of ssh://git.code.sf.net/p/flightgear/simgear into next 2017-10-09 22:04:36 +01:00
gallaert
e4b9cbcb80 Merge branch 'next' of ssh://git.code.sf.net/p/flightgear/simgear into next 2017-10-01 21:25:16 +01:00
gallaert
d4198659b7 Merge branch 'next' of ssh://git.code.sf.net/p/flightgear/simgear into next 2017-08-31 22:38:56 +01:00
gallaert
ef1edc7ee8 Merge branch 'next' of ssh://git.code.sf.net/p/flightgear/simgear into next 2017-08-25 20:47:36 +01:00
gallaert
3ccfb74028 Fix candidate for 'lib' vs 'lib64'. 2017-08-21 23:07:05 +01:00
263 changed files with 14226 additions and 4589 deletions

2
.gitignore vendored
View File

@@ -15,3 +15,5 @@ install_manifest.txt
build*
Build
CMakeLists.txt.user
3rdparty/expat_2.2.6/
nbproject

View File

@@ -1,7 +1,7 @@
configure_file (
"${PROJECT_SOURCE_DIR}/3rdparty/expat/expat_config_cmake.in"
"${PROJECT_BINARY_DIR}/3rdparty/expat/expat_config.h"
"${PROJECT_BINARY_DIR}/3rdparty/expat/simgear_expat_config.h"
)
set(expat_sources

View File

@@ -18,6 +18,8 @@
#include "amigaconfig.h"
#elif defined(__WATCOMC__)
#include "watcomconfig.h"
#elif defined(HAVE_SIMGEAR_EXPAT_CONFIG_H)
#include "simgear_expat_config.h"
#elif defined(HAVE_EXPAT_CONFIG_H)
#include "expat_config.h"
#endif /* ndef COMPILED_FROM_DSP */

View File

@@ -12,6 +12,8 @@
#include "amigaconfig.h"
#elif defined(__WATCOMC__)
#include "watcomconfig.h"
#elif defined(HAVE_SIMGEAR_EXPAT_CONFIG_H)
#include "simgear_expat_config.h"
#else
#ifdef HAVE_EXPAT_CONFIG_H
#include "expat_config.h"

View File

@@ -12,6 +12,8 @@
#include "amigaconfig.h"
#elif defined(__WATCOMC__)
#include "watcomconfig.h"
#elif defined(HAVE_SIMGEAR_EXPAT_CONFIG_H)
#include "simgear_expat_config.h"
#else
#ifdef HAVE_EXPAT_CONFIG_H
#include "expat_config.h"

View File

@@ -24,7 +24,7 @@
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#ifdef WINDOWS
#if defined(_WINDOWS) || defined(WINDOWS)
# include <winsock2.h> /* includes <windows.h> */
# include <iphlpapi.h> /* for dns server addresses etc */
#else
@@ -53,7 +53,7 @@ static void dns_set_srch_internal(struct dns_ctx *ctx, char *srch) {
dns_add_srch(ctx, srch);
}
#ifdef WINDOWS
#if defined(_WINDOWS) || defined(WINDOWS)
#ifndef NO_IPHLPAPI
/* Apparently, some systems does not have proper headers for IPHLPAIP to work.
@@ -217,7 +217,7 @@ int dns_init(struct dns_ctx *ctx, int do_open) {
ctx = &dns_defctx;
dns_reset(ctx);
#ifdef WINDOWS
#if defined(_WINDOWS) || defined(WINDOWS)
if (dns_initns_iphlpapi(ctx) != 0)
dns_initns_registry(ctx);
/*XXX WINDOWS: probably good to get default domain and search list too...

View File

@@ -24,7 +24,7 @@
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#ifdef WINDOWS
#if defined(_WINDOWS) || defined(WINDOWS)
# include <winsock2.h> /* includes <windows.h> */
# include <ws2tcpip.h> /* needed for struct in6_addr */
#else
@@ -392,7 +392,7 @@ dns_set_tmcbck(struct dns_ctx *ctx, dns_utm_fn *fn, void *data) {
}
static unsigned dns_nonrandom_32(void) {
#ifdef WINDOWS
#if defined(_WINDOWS) || defined(WINDOWS)
FILETIME ft;
GetSystemTimeAsFileTime(&ft);
return ft.dwLowDateTime;
@@ -551,7 +551,7 @@ int dns_open(struct dns_ctx *ctx) {
ctx->dnsc_qstatus = DNS_E_TEMPFAIL;
return -1;
}
#ifdef WINDOWS
#if defined(_WINDOWS) || defined(WINDOWS)
{ unsigned long on = 1;
if (ioctlsocket(sock, FIONBIO, &on) == SOCKET_ERROR) {
closesocket(sock);
@@ -991,7 +991,7 @@ again: /* receive the reply */
* or remote. On local errors, we should stop, while
* remote errors should be ignored (for now anyway).
*/
#ifdef WINDOWS
#if defined(_WINDOWS) || defined(WINDOWS)
if (WSAGetLastError() == WSAEWOULDBLOCK)
#else
if (errno == EAGAIN)

View File

@@ -27,7 +27,7 @@
#include <string.h>
#include <stdlib.h>
#include <assert.h>
#ifndef WINDOWS
#if !defined(_WINDOWS) && !defined(WINDOWS)
# include <sys/types.h>
# include <netinet/in.h>
#endif

View File

@@ -10,6 +10,9 @@ if(COMMAND cmake_policy)
if(POLICY CMP0067)
cmake_policy(SET CMP0067 NEW)
endif()
if(POLICY CMP0093)
cmake_policy(SET CMP0093 NEW)
endif()
endif()
@@ -134,6 +137,7 @@ if (NOT ENABLE_SIMD AND ENABLE_SIMD_CODE)
endif()
include (DetectArch)
include (ExportDebugSymbols)
# until the fstream fix is applied and generally available in OSG,
# keep the compatability link option as the default
@@ -150,7 +154,7 @@ endif()
if (MSVC)
GET_FILENAME_COMPONENT(PARENT_DIR ${PROJECT_BINARY_DIR} PATH)
GET_FILENAME_COMPONENT(PARENT_DIR ${PROJECT_BINARY_DIR} DIRECTORY)
if (CMAKE_CL_64)
SET(TEST_3RDPARTY_DIR "${PARENT_DIR}/3rdparty.x64")
else (CMAKE_CL_64)
@@ -161,6 +165,11 @@ if (MSVC)
else (EXISTS ${TEST_3RDPARTY_DIR})
set(MSVC_3RDPARTY_ROOT NOT_FOUND CACHE PATH "Location where the third-party dependencies are extracted")
endif (EXISTS ${TEST_3RDPARTY_DIR})
# override CMake default RelWithDebInfo flags. This is important to ensure
# good performance
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "/Zi /O2 /Ob2 /D NDEBUG")
set(CMAKE_C_FLAGS_RELWITHDEBINFO "/Zi /O2 /Ob2 /D NDEBUG")
else (MSVC)
set(MSVC_3RDPARTY_ROOT NOT_FOUND CACHE PATH "Location where the third-party dependencies are extracted")
endif (MSVC)
@@ -173,15 +182,17 @@ if (MSVC AND MSVC_3RDPARTY_ROOT)
set( OSG_MSVC "msvc" )
if (${MSVC_VERSION_MAJOR} EQUAL "19")
if (${MSVC_VERSION_MINOR} EQUAL "00")
set( OSG_MSVC ${OSG_MSVC}140 )
else ()
if (${MSVC_VERSION_MINOR} GREATER_EQUAL "20")
set( OSG_MSVC ${OSG_MSVC}142 )
elseif (${MSVC_VERSION_MINOR} GREATER_EQUAL "10")
set( OSG_MSVC ${OSG_MSVC}141 )
else ()
set( OSG_MSVC ${OSG_MSVC}140 )
endif ()
elseif (${MSVC_VERSION_MAJOR} EQUAL "18")
set( OSG_MSVC ${OSG_MSVC}120 )
else ()
message(FATAL_ERROR "Visual Studio 2013/15/17 is required")
message(FATAL_ERROR "Visual Studio 2013 or higher is required")
endif ()
if (CMAKE_CL_64)
@@ -198,7 +209,7 @@ if (MSVC AND MSVC_3RDPARTY_ROOT)
# if this variable was not set by the user, set it to 3rdparty root's
# parent dir, which is the normal location for people using our
# windows-3rd-party repo
GET_FILENAME_COMPONENT(MSVC_ROOT_PARENT_DIR ${MSVC_3RDPARTY_ROOT} PATH)
get_filename_component(MSVC_ROOT_PARENT_DIR ${MSVC_3RDPARTY_ROOT} DIRECTORY)
set(BOOST_INCLUDEDIR ${MSVC_ROOT_PARENT_DIR})
message(STATUS "BOOST_INCLUDEDIR is ${BOOST_INCLUDEDIR}")
endif()
@@ -216,7 +227,7 @@ if(${CMAKE_SYSTEM_NAME} MATCHES "Linux" OR ${CMAKE_SYSTEM_NAME} MATCHES "FreeBSD
endif()
find_package(Boost REQUIRED)
set (BOOST_CXX_FLAGS "-DBOOST_BIMAP_DISABLE_SERIALIZATION")
set (BOOST_CXX_FLAGS "-DBOOST_BIMAP_DISABLE_SERIALIZATION -DBOOST_NO_STDLIB_CONFIG")
include(BoostTestTargets)
if(SIMGEAR_HEADLESS)
@@ -271,9 +282,6 @@ if (SYSTEM_EXPAT)
else()
message(STATUS "Using built-in expat code")
# XML_STATIC is important to avoid sg_expat_external.h
# declaring symbols as declspec(import)
add_definitions(-DHAVE_EXPAT_CONFIG_H -DXML_STATIC)
set(EXPAT_INCLUDE_DIRS
${PROJECT_SOURCE_DIR}/3rdparty/expat
${PROJECT_BINARY_DIR}/3rdparty/expat)
@@ -292,11 +300,10 @@ endif()
if(ENABLE_RTI)
find_package(PkgConfig)
if(PKG_CONFIG_FOUND)
SET(ENV{PKG_CONFIG_PATH} "${CMAKE_INSTALL_PREFIX}/lib/pkgconfig:$ENV{PKG_CONFIG_PATH}")
SET(ENV{PKG_CONFIG_PATH} "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}/pkgconfig:$ENV{PKG_CONFIG_PATH}")
pkg_check_modules(RTI hla-rti13)
endif(PKG_CONFIG_FOUND)
if(RTI_FOUND)
SET(RTI_INCLUDE_DIR "${RTI_INCLUDE_DIRS}")
message(STATUS "RTI: ENABLED")
else()
message(STATUS "RTI: DISABLED")
@@ -438,15 +445,18 @@ if (CLANG)
# fix Boost compilation :(
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++")
if (CMAKE_BUILD_TYPE STREQUAL "Debug")
set(CMAKE_C_FLAGS
"${CMAKE_C_FLAGS} -O0 -fno-omit-frame-pointer -fno-inline-functions")
set(CMAKE_CXX_FLAGS
"${CMAKE_CXX_FLAGS} -O0 -fno-omit-frame-pointer -fno-inline-functions")
elseif (ENABLE_SIMD)
# override CMake default RelWithDebInfo flags.
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-O3 -g -DNDEBUG")
set(CMAKE_C_FLAGS_RELWITHDEBINFO "-O3 -g -DNDEBUG")
if (ENABLE_SIMD)
if (X86 OR X86_64)
set(CMAKE_C_FLAGS_RELEASE "-O3 -msse2 -mfpmath=sse -ftree-vectorize -ftree-slp-vectorize")
set(CMAKE_CXX_FLAGS_RELEASE "-O3 -msse2 -mfpmath=sse -ftree-vectorize -ftree-slp-vectorize")
# propogate to the RelWithDebInfo flags
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELEASE} -g -DNDEBUG")
set(CMAKE_C_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS_RELEASE} -g -DNDEBUG")
endif()
endif()
endif()
@@ -521,6 +531,10 @@ include(CheckCXXFeatures)
# ahead of system-installed libs
include_directories(BEFORE ${PROJECT_BINARY_DIR}/simgear)
if(${CMAKE_SYSTEM_NAME} MATCHES "OpenBSD")
include_directories("/usr/X11R6/include")
endif()
add_definitions(-DHAVE_CONFIG_H)
# configure a header file to pass some of the CMake settings

View File

@@ -46,11 +46,17 @@ set(BOOST_TEST_TARGET_PREFIX "test")
if(NOT Boost_FOUND)
find_package(Boost 1.34.0 QUIET)
endif()
if("${Boost_VERSION}0" LESS "1034000")
if (NOT Boost_VERSION_MACRO)
# Compatibility with pre CMP0093 (CMake 3.15)
set(Boost_VERSION_MACRO ${Boost_VERSION})
endif()
if("${Boost_VERSION_MACRO}0" LESS "1034000")
set(_shared_msg
"NOTE: boost::test-based targets and tests cannot "
"be added: boost >= 1.34.0 required but not found. "
"(found: '${Boost_VERSION}'; want >=103400) ")
"(found: '${Boost_VERSION_MACRO}'; want >=103400) ")
if(ENABLE_TESTS)
message(FATAL_ERROR
${_shared_msg}
@@ -66,7 +72,7 @@ endif()
include(GetForceIncludeDefinitions)
include(CopyResourcesToBuildTree)
if(Boost_FOUND AND NOT "${Boost_VERSION}0" LESS "1034000")
if(Boost_FOUND AND NOT "${Boost_VERSION_MACRO}0" LESS "1034000")
set(_boosttesttargets_libs)
set(_boostConfig "BoostTestTargetsIncluded.h")
if(NOT Boost_UNIT_TEST_FRAMEWORK_LIBRARY)
@@ -80,7 +86,7 @@ if(Boost_FOUND AND NOT "${Boost_VERSION}0" LESS "1034000")
set(_boostConfig "BoostTestTargetsDynamic.h")
endif()
endif()
get_filename_component(_moddir ${CMAKE_CURRENT_LIST_FILE} PATH)
get_filename_component(_moddir ${CMAKE_CURRENT_LIST_FILE} DIRECTORY)
configure_file("${_moddir}/${_boostConfig}"
"${CMAKE_CURRENT_BINARY_DIR}/BoostTestTargetConfig.h"
COPYONLY)
@@ -129,7 +135,7 @@ function(add_boost_test _name)
"Syntax error in use of add_boost_test: at least one source file required!")
endif()
if(Boost_FOUND AND NOT "${Boost_VERSION}0" LESS "1034000")
if(Boost_FOUND AND NOT "${Boost_VERSION_MACRO}0" LESS "1034000")
include_directories(${Boost_INCLUDE_DIRS})
@@ -221,7 +227,7 @@ function(add_boost_test _name)
set(_test_command ${_target_name})
endif()
if(TESTS AND ( "${Boost_VERSION}" VERSION_GREATER "103799" ))
if(TESTS AND ( "${Boost_VERSION_MACRO}" VERSION_GREATER "103799" ))
foreach(_test ${TESTS})
add_test(
${_name}-${_test}

View File

@@ -30,12 +30,12 @@ function(copy_resources_to_build_tree _target)
endif()
get_target_property(_path ${_target} LOCATION)
get_filename_component(_path "${_path}" PATH)
get_filename_component(_path "${_path}" DIRECTORY)
if(NOT MSVC AND NOT "${CMAKE_GENERATOR}" MATCHES "Makefiles")
foreach(_config ${CMAKE_CONFIGURATION_TYPES})
get_target_property(_path${_config} ${_target} LOCATION_${_config})
get_filename_component(_path${_config} "${_path${_config}}" PATH)
get_filename_component(_path${_config} "${_path${_config}}" DIRECTORY)
add_custom_command(TARGET ${_target}
POST_BUILD
COMMAND

View File

@@ -0,0 +1,26 @@
# placehodler target for other ones to depend upon
add_custom_target(
debug_symbols
)
function(export_debug_symbols target)
if (NOT SIMGEAR_SHARED)
return()
endif()
if (APPLE)
add_custom_target(${target}.dSYM
COMMENT "Generating dSYM files for ${target}"
COMMAND dsymutil --out=${target}.dSYM $<TARGET_FILE:${target}>
DEPENDS $<TARGET_FILE:${target}>
)
install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/${target}.dSYM DESTINATION symbols OPTIONAL)
add_dependencies(debug_symbols ${target}.dSYM)
endif()
endfunction()

568
Doxyfile

File diff suppressed because it is too large Load Diff

View File

@@ -18,6 +18,16 @@ set(USE_AEONWAVE @USE_AEONWAVE@)
set(ENABLE_SIMD @ENABLE_SIMD@)
# OpenRTI support
set(ENABLE_RTI @ENABLE_RTI@)
if(ENABLE_RTI)
set(RTI_FOUND @RTI_FOUND@)
if(RTI_FOUND)
set(RTI_INCLUDE_DIRS @RTI_INCLUDE_DIRS@)
set(RTI_LDFLAGS @RTI_LDFLAGS@)
endif(RTI_FOUND)
endif(ENABLE_RTI)
# Alternative terrain engine based on pagedLOD
set(ENABLE_GDAL @ENABLE_GDAL@)

View File

@@ -6,6 +6,7 @@ foreach( mylibfolder
bvh
debug
embedded_resources
emesary
ephemeris
io
magvar
@@ -64,6 +65,9 @@ if(SIMGEAR_SHARED)
set_property(TARGET SimGearScene PROPERTY VERSION ${SIMGEAR_VERSION})
set_property(TARGET SimGearScene PROPERTY SOVERSION ${SIMGEAR_SOVERSION})
endif()
export_debug_symbols(SimGearCore)
export_debug_symbols(SimGearScene)
else()
message(STATUS "Library building mode: STATIC LIBRARIES")
@@ -114,11 +118,22 @@ target_include_directories(SimGearCore BEFORE PUBLIC
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}>
$<INSTALL_INTERFACE:include>)
# so simgear/simgear_config.h is found
target_include_directories(SimGearCore BEFORE PUBLIC
$<BUILD_INTERFACE:${PROJECT_BINARY_DIR}>
$<INSTALL_INTERFACE:include>)
target_include_directories(SimGearCore PUBLIC
${Boost_INCLUDE_DIRS} ${ZLIB_INCLUDE_DIR})
target_include_directories(SimGearCore PRIVATE
${EXPAT_INCLUDE_DIRS} ${CURL_INCLUDE_DIRS})
if (NOT SYSTEM_EXPAT)
# XML_STATIC is important to avoid sg_expat_external.h
# declaring symbols as declspec(import)
target_compile_definitions(SimGearCore PRIVATE HAVE_SIMGEAR_EXPAT_CONFIG_H XML_STATIC)
endif()
install(TARGETS SimGearCore
EXPORT SimGearTargets
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
@@ -143,9 +158,10 @@ if (NOT SIMGEAR_HEADLESS)
endif()
endif()
# we expose ZLib in some of our headers
target_link_libraries(SimGearCore PUBLIC ${ZLIB_LIBRARY})
target_link_libraries(SimGearCore
${ZLIB_LIBRARY}
target_link_libraries(SimGearCore PRIVATE
${RT_LIBRARY}
${DL_LIBRARY}
${CMAKE_THREAD_LIBS_INIT}
@@ -154,29 +170,29 @@ target_link_libraries(SimGearCore
${WINSOCK_LIBRARY})
if(SYSTEM_EXPAT)
target_link_libraries(SimGearCore
${EXPAT_LIBRARIES})
target_link_libraries(SimGearCore PRIVATE ${EXPAT_LIBRARIES})
endif()
if(ENABLE_DNS AND SYSTEM_UDNS)
target_link_libraries(SimGearCore
${UDNS_LIBRARIES})
target_link_libraries(SimGearCore PRIVATE ${UDNS_LIBRARIES})
endif()
if(NOT SIMGEAR_HEADLESS)
target_include_directories(SimGearScene PRIVATE ${PROJECT_SOURCE_DIR}/simgear/canvas/ShivaVG/include)
target_link_libraries(SimGearScene
target_link_libraries(SimGearScene PUBLIC
SimGearCore
${ZLIB_LIBRARY}
${OPENSCENEGRAPH_LIBRARIES}
)
target_link_libraries(SimGearScene PRIVATE
${ZLIB_LIBRARY}
${OPENAL_LIBRARY}
${OPENGL_LIBRARY}
${JPEG_LIBRARY})
if(ENABLE_GDAL)
target_link_libraries(SimGearScene
${GDAL_LIBRARIES})
target_link_libraries(SimGearScene PRIVATE ${GDAL_LIBRARIES})
endif()
# only actually needed by canvas/KeyboardEvent.cxx
@@ -188,5 +204,5 @@ if(ENABLE_RTI)
set_property(SOURCE hla/RTI13InteractionClass.cxx hla/RTI13ObjectClass.cxx
hla/RTI13ObjectInstance.cxx hla/RTI13Federate.cxx
hla/RTI13FederateFactory.cxx
APPEND PROPERTY COMPILE_FLAGS "-I${RTI_INCLUDE_DIR}")
APPEND PROPERTY COMPILE_FLAGS "-I${RTI_INCLUDE_DIRS}")
endif(ENABLE_RTI)

View File

@@ -20,9 +20,9 @@
#include "BVHPager.hxx"
#include <list>
#include <mutex>
#include <simgear/threads/SGThread.hxx>
#include <simgear/threads/SGGuard.hxx>
#include "BVHPageNode.hxx"
#include "BVHPageRequest.hxx"
@@ -37,12 +37,12 @@ struct BVHPager::_PrivateData : protected SGThread {
struct _LockedQueue {
void _push(const _Request& request)
{
SGGuard<SGMutex> scopeLock(_mutex);
std::lock_guard<std::mutex> scopeLock(_mutex);
_requestList.push_back(request);
}
_Request _pop()
{
SGGuard<SGMutex> scopeLock(_mutex);
std::lock_guard<std::mutex> scopeLock(_mutex);
if (_requestList.empty())
return _Request();
_Request request;
@@ -51,7 +51,7 @@ struct BVHPager::_PrivateData : protected SGThread {
return request;
}
private:
SGMutex _mutex;
std::mutex _mutex;
_RequestList _requestList;
};
@@ -62,7 +62,7 @@ struct BVHPager::_PrivateData : protected SGThread {
}
void _push(const _Request& request)
{
SGGuard<SGMutex> scopeLock(_mutex);
std::lock_guard<std::mutex> scopeLock(_mutex);
bool needSignal = _requestList.empty();
_requestList.push_back(request);
if (needSignal)
@@ -70,7 +70,7 @@ struct BVHPager::_PrivateData : protected SGThread {
}
_Request _pop()
{
SGGuard<SGMutex> scopeLock(_mutex);
std::lock_guard<std::mutex> scopeLock(_mutex);
while (_requestList.empty())
_waitCondition.wait(_mutex);
_Request request;
@@ -79,7 +79,7 @@ struct BVHPager::_PrivateData : protected SGThread {
return request;
}
private:
SGMutex _mutex;
std::mutex _mutex;
SGWaitCondition _waitCondition;
_RequestList _requestList;
};

View File

@@ -28,42 +28,39 @@ namespace simgear
namespace canvas
{
class CanvasMgr:
public PropertyBasedMgr
{
public:
class CanvasMgr : public PropertyBasedMgr
{
public:
/**
* @param node Root node of branch used to control canvasses
*/
CanvasMgr(SGPropertyNode_ptr node);
/**
* @param node Root node of branch used to control canvasses
*/
CanvasMgr(SGPropertyNode_ptr node);
/**
* Create a new canvas
*
* @param name Name of the new canvas
*/
CanvasPtr createCanvas(const std::string& name = "");
/**
* Create a new canvas
*
* @param name Name of the new canvas
*/
CanvasPtr createCanvas(const std::string& name = "");
/**
* Get ::Canvas by index
*
* @param index Index of texture node in /canvas/by-index/
*/
CanvasPtr getCanvas(size_t index) const;
/**
* Get ::Canvas by index
*
* @param index Index of texture node in /canvas/by-index/
*/
CanvasPtr getCanvas(size_t index) const;
/**
* Get ::Canvas by name
*
* @param name Value of child node "name" in
* /canvas/by-index/texture[i]/name
*/
CanvasPtr getCanvas(const std::string& name) const;
/**
* Get ::Canvas by name
*
* @param name Value of child node "name" in
* /canvas/by-index/texture[i]/name
*/
CanvasPtr getCanvas(const std::string& name) const;
protected:
void elementCreated(PropertyBasedElementPtr element) override;
};
protected:
void elementCreated(PropertyBasedElementPtr element) override;
};
} // namespace canvas
} // namespace simgear

View File

@@ -68,8 +68,8 @@ namespace canvas
Element* parent = 0 );
virtual ~Window();
virtual void update(double delta_time_sec);
virtual void valueChanged(SGPropertyNode* node);
void update(double delta_time_sec) override;
void valueChanged(SGPropertyNode* node) override;
const SGVec2<float> getPosition() const;
const SGRect<float> getScreenRegion() const;
@@ -84,8 +84,8 @@ namespace canvas
bool isResizable() const;
bool isCapturingEvents() const;
virtual void setVisible(bool visible);
virtual bool isVisible() const;
void setVisible(bool visible) override;
bool isVisible() const override;
/**
* Moves window on top of all other windows with the same z-index.

View File

@@ -250,10 +250,16 @@ namespace canvas
if( !texture )
{
// It shouldn't be necessary to allocate an image for the
// texture that is the target of dynamic rendering, but
// otherwise OSG won't construct all the mipmaps for the texture
// and dynamic mipmap generation doesn't work.
osg::Image* image = new osg::Image;
image->allocateImage(_size_x, _size_y, 1, GL_RGBA, GL_UNSIGNED_BYTE);
texture = new osg::Texture2D;
texture->setResizeNonPowerOfTwoHint(false);
texture->setTextureSize(_size_x, _size_y);
texture->setInternalFormat(GL_RGBA);
texture->setImage(image);
texture->setUnRefImageDataAfterApply(true);
}
updateSampling();

View File

@@ -31,6 +31,11 @@
// FreeBSD
#define VG_API_FREEBSD
#elif defined(__OpenBSD__)
// FreeBSD
#define VG_API_OPENBSD
#else
// Unsupported system

View File

@@ -34,7 +34,7 @@
#include <math.h>
#include <float.h>
#if !defined(VG_API_MACOSX) && !defined(__FreeBSD__)
#if !defined(VG_API_MACOSX) && !defined(__FreeBSD__) && !defined(__OpenBSD__)
# include <malloc.h>
#endif
@@ -161,7 +161,7 @@ SHfloat getMaxFloat();
/* OpenGL headers */
#if defined(VG_API_LINUX) || defined(VG_API_FREEBSD)
#if defined(VG_API_LINUX) || defined(VG_API_FREEBSD) || defined(VG_API_OPENBSD)
#include <GL/gl.h>
#include <GL/glx.h>
#elif defined(VG_API_MACOSX)

View File

@@ -1193,7 +1193,7 @@ VG_API_CALL VGboolean vgInterpolatePath(VGPath dstPath, VGPath startPath,
SHfloat *procData1, *procData2;
SHint procSegCount1=0, procSegCount2=0;
SHint procDataCount1=0, procDataCount2=0;
SHuint8 *newSegs, *newData;
SHuint8 *newSegs, *newData=0;
void *userData[4];
SHint segment1, segment2;
SHint segindex, s,d,i;

View File

@@ -26,11 +26,9 @@
#include <osg/ref_ptr>
#include <osgText/Font>
#include <boost/function.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/weak_ptr.hpp>
#include <functional>
#include <map>
#include <memory>
#include <vector>
namespace simgear
@@ -62,8 +60,8 @@ namespace canvas
#define SG_FWD_DECL(name)\
class name;\
typedef boost::shared_ptr<name> name##Ptr;\
typedef boost::weak_ptr<name> name##WeakPtr;
typedef std::shared_ptr<name> name##Ptr;\
typedef std::weak_ptr<name> name##WeakPtr;
SG_FWD_DECL(Placement)
SG_FWD_DECL(SystemAdapter)
@@ -85,10 +83,10 @@ namespace canvas
typedef osg::ref_ptr<osgText::Font> FontPtr;
typedef std::vector<PlacementPtr> Placements;
typedef boost::function<Placements( SGPropertyNode*,
CanvasPtr )> PlacementFactory;
typedef std::function<Placements( SGPropertyNode*,
CanvasPtr )> PlacementFactory;
typedef boost::function<void(const EventPtr&)> EventListener;
typedef std::function<void(const EventPtr&)> EventListener;
} // namespace canvas
} // namespace simgear

View File

@@ -29,9 +29,6 @@
#include <osg/BoundingBox>
#include <osg/MatrixTransform>
#include <boost/bind.hpp>
#include <boost/function.hpp>
namespace osg
{
class Drawable;
@@ -62,9 +59,9 @@ namespace canvas
OSGUserData(ElementPtr element);
};
typedef boost::function<bool(Element&, const SGPropertyNode*)>
typedef std::function<bool(Element&, const SGPropertyNode*)>
StyleSetterFunc;
typedef boost::function<void(Element&, const SGPropertyNode*)>
typedef std::function<void(Element&, const SGPropertyNode*)>
StyleSetterFuncUnchecked;
struct StyleSetter:
public SGReferenced
@@ -319,7 +316,7 @@ namespace canvas
StyleSetter
addStyle( const std::string& name,
const std::string& type,
const boost::function<void (Derived&, T2)>& setter,
const std::function<void (Derived&, T2)>& setter,
bool inheritable = true )
{
StyleInfo& style_info = _style_setters[ name ];
@@ -345,13 +342,10 @@ namespace canvas
if( style->func )
style = style->next = new StyleSetter;
style->func = boost::bind
(
&type_match<Derived>::call,
_1,
_2,
bindStyleSetter<T1>(name, setter)
);
style->func = std::bind(&type_match<Derived>::call,
std::placeholders::_1,
std::placeholders::_2,
bindStyleSetter<T1>(name, setter));
return *style;
}
@@ -363,7 +357,7 @@ namespace canvas
StyleSetter
addStyle( const std::string& name,
const std::string& type,
const boost::function<void (Derived&, T)>& setter,
const std::function<void (Derived&, T)>& setter,
bool inheritable = true )
{
return addStyle<T, T>(name, type, setter, inheritable);
@@ -384,7 +378,7 @@ namespace canvas
(
name,
type,
boost::function<void (Derived&, T)>(setter),
std::function<void (Derived&, T)>(setter),
inheritable
);
}
@@ -405,7 +399,7 @@ namespace canvas
(
name,
type,
boost::function<void (Derived&, T2)>(setter),
std::function<void (Derived&, T2)>(setter),
inheritable
);
}
@@ -424,7 +418,7 @@ namespace canvas
(
name,
type,
boost::function<void (Derived&, const std::string&)>(setter),
std::function<void (Derived&, const std::string&)>(setter),
inheritable
);
}
@@ -487,7 +481,7 @@ namespace canvas
StyleSetter
addStyle( const std::string& name,
const std::string& type,
const boost::function<void (Other&, T2)>& setter,
const std::function<void (Other&, T2)>& setter,
OtherRef Derived::*instance_ref,
bool inheritable = true )
{
@@ -517,7 +511,7 @@ namespace canvas
(
name,
type,
boost::function<void (Other&, const std::string&)>(setter),
std::function<void (Other&, const std::string&)>(setter),
instance_ref,
inheritable
);
@@ -525,44 +519,37 @@ namespace canvas
template<typename T, class Derived, class Other, class OtherRef>
static
boost::function<void (Derived&, T)>
std::function<void (Derived&, T)>
bindOther( void (Other::*setter)(T), OtherRef Derived::*instance_ref )
{
return boost::bind(setter, boost::bind(instance_ref, _1), _2);
return std::bind(setter,
std::bind(instance_ref, std::placeholders::_1),
std::placeholders::_2);
}
template<typename T, class Derived, class Other, class OtherRef>
static
boost::function<void (Derived&, T)>
bindOther( const boost::function<void (Other&, T)>& setter,
std::function<void (Derived&, T)>
bindOther( const std::function<void (Other&, T)>& setter,
OtherRef Derived::*instance_ref )
{
return boost::bind
(
setter,
boost::bind
(
&reference_from_pointer<Other, OtherRef>,
boost::bind(instance_ref, _1)
),
_2
);
return std::bind(setter,
std::bind(&reference_from_pointer<Other, OtherRef>,
std::bind(instance_ref, std::placeholders::_1)),
std::placeholders::_2);
}
template<typename T1, typename T2, class Derived>
static
StyleSetterFuncUnchecked
bindStyleSetter( const std::string& name,
const boost::function<void (Derived&, T2)>& setter )
const std::function<void (Derived&, T2)>& setter )
{
return boost::bind
(
setter,
// We will only call setters with Derived instances, so we can safely
// cast here.
boost::bind(&derived_cast<Derived>, _1),
boost::bind(&getValue<T1>, _2)
);
return std::bind(setter,
// We will only call setters with Derived instances, so we can safely
// cast here.
std::bind(&derived_cast<Derived>, std::placeholders::_1),
std::bind(&getValue<T1>, std::placeholders::_2));
}
bool isStyleEmpty(const SGPropertyNode* child) const;

View File

@@ -838,5 +838,143 @@ namespace canvas
return false;
}
void Image::fillRect(const SGRect<int>& rect, const std::string& c)
{
osg::Vec4 color(1,1,1,1);
if(!c.empty() && !parseColor(c, color))
return;
fillRect(rect, color);
}
void fillRow(GLubyte* row, GLuint pixel, GLuint width, GLuint pixelBytes)
{
GLubyte* dst = row;
for (int x = 0; x < width; ++x) {
memcpy(dst, &pixel, pixelBytes);
dst += pixelBytes;
}
}
SGRect<int> intersectRect(const SGRect<int>& a, const SGRect<int>& b)
{
SGVec2<int> m1 = max(a.getMin(), b.getMin());
SGVec2<int> m2 = min(a.getMax(), b.getMax());
return SGRect<int>(m1, m2);
}
void Image::fillRect(const SGRect<int>& rect, const osg::Vec4& color)
{
osg::ref_ptr<osg::Image> image = _texture->getImage();
if (!image) {
allocateImage();
image = _texture->getImage();
}
if (image->getDataVariance() != osg::Object::DYNAMIC) {
image->setDataVariance(osg::Object::DYNAMIC);
}
const auto format = image->getInternalTextureFormat();
auto clippedRect = intersectRect(rect, SGRect<int>(0, 0, image->s(), image->t()));
if ((clippedRect.width() == 0) || (clippedRect.height() == 0)) {
return;
}
GLubyte* rowData = nullptr;
size_t rowByteSize = 0;
GLuint pixelWidth = clippedRect.width();
GLuint pixel = 0;
GLuint pixelBytes = 0;
switch (format) {
case GL_RGBA8:
case GL_RGBA:
rowByteSize = pixelWidth * 4;
rowData = static_cast<GLubyte*>(alloca(rowByteSize));
// assume litte-endian, so read out backwards, hence when we memcpy
// the data, it ends up in RGBA order
pixel = color.asABGR();
pixelBytes = 4;
fillRow(rowData, pixel, pixelWidth, pixelBytes);
break;
case GL_RGB8:
case GL_RGB:
rowByteSize = pixelWidth * 3;
rowData = static_cast<GLubyte*>(alloca(rowByteSize));
pixel = color.asABGR();
pixelBytes = 3;
fillRow(rowData, pixel, pixelWidth, pixelBytes);
break;
default:
SG_LOG(SG_IO, SG_WARN, "Image::fillRect: unsupported internal image format:" << format);
return;
}
for (int row=clippedRect.t(); row < clippedRect.b(); ++row) {
GLubyte* imageData = image->data(clippedRect.l(), row);
memcpy(imageData, rowData, rowByteSize);
}
image->dirty();
auto c = getCanvas().lock();
c->enableRendering(true); // force a repaint
}
void Image::setPixel(int x, int y, const std::string& c)
{
osg::Vec4 color(1,1,1,1);
if(!c.empty() && !parseColor(c, color))
return;
setPixel(x, y, color);
}
void Image::setPixel(int x, int y, const osg::Vec4& color)
{
osg::ref_ptr<osg::Image> image = _texture->getImage();
if (!image) {
allocateImage();
image = _texture->getImage();
}
if (image->getDataVariance() != osg::Object::DYNAMIC) {
image->setDataVariance(osg::Object::DYNAMIC);
}
image->setColor(color, x, y);
}
void Image::dirtyPixels()
{
osg::ref_ptr<osg::Image> image = _texture->getImage();
if (!image)
return;
image->dirty();
auto c = getCanvas().lock();
c->enableRendering(true); // force a repaint
}
void Image::allocateImage()
{
osg::Image* image = new osg::Image;
// default to RGBA
image->allocateImage(_node->getIntValue("size[0]"), _node->getIntValue("size[1]"), 1, GL_RGBA, GL_UNSIGNED_BYTE);
image->setInternalTextureFormat(GL_RGBA);
_texture->setImage(image);
}
osg::ref_ptr<osg::Image> Image::getImage() const
{
if (!_texture)
return {};
return _texture->getImage();
}
} // namespace canvas
} // namespace simgear

View File

@@ -101,6 +101,28 @@ namespace canvas
*/
void setSourceRect(const SGRect<float>& sourceRect);
/**
* fill the specified rectangle of the image, with an RGB value
*/
void fillRect(const SGRect<int>& rect, const std::string& color);
/**
* fill the specified rectangle of the image, with an RGB value
*/
void fillRect(const SGRect<int>& rect, const osg::Vec4& color);
void setPixel(int x, int y, const std::string& color);
void setPixel(int x, int y, const osg::Vec4& color);
/**
* mark the image pixels as modified, so the canvas is re-painted
*/
void dirtyPixels();
osg::ref_ptr<osg::Image> getImage() const;
// void setRow(int row, int offset, )
protected:
enum ImageAttributes
{
@@ -125,6 +147,8 @@ namespace canvas
HTTP::Request& request,
const std::string& type );
void allocateImage();
osg::ref_ptr<osg::Texture2D> _texture;
// TODO optionally forward events to canvas
CanvasWeakPtr _src_canvas;

View File

@@ -53,26 +53,31 @@ namespace canvas
TextLine lineAt(size_t i) const;
/// Get nearest line to given y-coordinate
#if OSG_VERSION_LESS_THAN(3,6,5)
TextLine nearestLine(float pos_y) const;
SGVec2i sizeForWidth(int w) const;
osg::BoundingBox
#if OSG_VERSION_LESS_THAN(3,3,2)
computeBound()
#else
computeBoundingBox()
TextLine nearestLine(float pos_y);
SGVec2i sizeForWidth(int w);
#endif
#if OSG_VERSION_LESS_THAN(3,3,2)
osg::BoundingBox computeBound() const override;
#else
osg::BoundingBox computeBoundingBox() const override;
#endif
const override;
protected:
friend class TextLine;
canvas::Text *_text_element;
void computePositions(unsigned int contextID) const override;
};
#if OSG_VERSION_LESS_THAN(3,5,6)
void computePositions(unsigned int contextID) const override;
#else
void computePositionsImplementation() override;
#endif
};
class TextLine
{
@@ -122,6 +127,7 @@ namespace canvas
_quads = &text->_textureGlyphQuadMap.begin()->second;
#if OSG_VERSION_LESS_THAN(3,5,6)
GlyphQuads::LineNumbers const& line_numbers = _quads->_lineNumbers;
GlyphQuads::LineNumbers::const_iterator begin_it =
std::lower_bound(line_numbers.begin(), line_numbers.end(), _line);
@@ -133,6 +139,9 @@ namespace canvas
_begin = begin_it - line_numbers.begin();
_end = std::upper_bound(begin_it, line_numbers.end(), _line)
- line_numbers.begin();
#else
// TODO: Need 3.5.6 version of this
#endif
}
//----------------------------------------------------------------------------
@@ -161,34 +170,40 @@ namespace canvas
if( empty() )
return pos;
#if OSG_VERSION_LESS_THAN(3,3,5)
GlyphQuads::Coords2 const& coords = _quads->_coords;
#if OSG_VERSION_GREATER_OR_EQUAL(3,5,6)
// TODO: need 3.5.6 version of this.
#else
GlyphQuads::Coords2 refCoords = _quads->_coords;
GlyphQuads::Coords2::element_type &coords = *refCoords.get();
#if OSG_VERSION_LESS_THAN(3,3,5)
GlyphQuads::Coords2 const& coords = _quads->_coords;
#else
GlyphQuads::Coords2 refCoords = _quads->_coords;
GlyphQuads::Coords2::element_type &coords = *refCoords.get();
#endif
size_t global_i = _begin + i;
if( global_i == _begin )
// before first character of line
pos.x() = coords[_begin * 4].x();
else if( global_i == _end )
// After Last character of line
pos.x() = coords[(_end - 1) * 4 + 2].x();
else
{
float prev_l = coords[(global_i - 1) * 4].x(),
prev_r = coords[(global_i - 1) * 4 + 2].x(),
cur_l = coords[global_i * 4].x();
size_t global_i = _begin + i;
if( prev_l == prev_r )
// If previous character width is zero set to begin of next character
// (Happens eg. with spaces)
pos.x() = cur_l;
if (global_i == _begin)
// before first character of line
pos.x() = coords[_begin * 4].x();
else if (global_i == _end)
// After Last character of line
pos.x() = coords[(_end - 1) * 4 + 2].x();
else
// position at center between characters
pos.x() = 0.5 * (prev_r + cur_l);
}
{
float prev_l = coords[(global_i - 1) * 4].x(),
prev_r = coords[(global_i - 1) * 4 + 2].x(),
cur_l = coords[global_i * 4].x();
if (prev_l == prev_r)
// If previous character width is zero set to begin of next character
// (Happens eg. with spaces)
pos.x() = cur_l;
else
// position at center between characters
pos.x() = 0.5 * (prev_r + cur_l);
}
#endif
return pos;
}
@@ -196,17 +211,22 @@ namespace canvas
//----------------------------------------------------------------------------
osg::Vec2 TextLine::nearestCursor(float x) const
{
if( empty() )
if (empty())
return cursorPos(0);
GlyphQuads::Glyphs const& glyphs = _quads->_glyphs;
#if OSG_VERSION_LESS_THAN(3,3,5)
#if OSG_VERSION_GREATER_OR_EQUAL(3,5,6)
// TODO: need 3.5.7 version of this.
return cursorPos(0);
#else
#if OSG_VERSION_LESS_THAN(3,3,5)
GlyphQuads::Coords2 const& coords = _quads->_coords;
#else
GlyphQuads::Coords2 refCoords = _quads->_coords;
GlyphQuads::Coords2::element_type &coords = *refCoords.get();
#endif
GlyphQuads::Glyphs const& glyphs = _quads->_glyphs;
float const HIT_FRACTION = 0.6;
float const character_width = _text->getCharacterHeight()
* _text->getCharacterAspectRatio();
@@ -225,6 +245,7 @@ namespace canvas
}
return cursorPos(i - _begin);
#endif
}
//----------------------------------------------------------------------------
@@ -303,9 +324,16 @@ namespace canvas
}
//----------------------------------------------------------------------------
#if OSG_VERSION_LESS_THAN(3,6,5)
TextLine Text::TextOSG::nearestLine(float pos_y) const
{
osgText::Font const* font = getActiveFont();
#else
TextLine Text::TextOSG::nearestLine(float pos_y)
{
auto font = getActiveFont();
#endif
if( !font || lineCount() <= 0 )
return TextLine(0, this);
@@ -327,12 +355,21 @@ namespace canvas
// simplified version of osgText::Text::computeGlyphRepresentation() to
// just calculate the size for a given weight. Glpyh calculations/creating
// is not necessary for this...
#if OSG_VERSION_LESS_THAN(3,6,5)
SGVec2i Text::TextOSG::sizeForWidth(int w) const
#else
SGVec2i Text::TextOSG::sizeForWidth(int w)
#endif
{
if( _text.empty() )
return SGVec2i(0, 0);
#if OSG_VERSION_LESS_THAN(3,6,5)
osgText::Font* activefont = const_cast<osgText::Font*>(getActiveFont());
#else
auto activefont = getActiveFont();
#endif
if( !activefont )
return SGVec2i(-1, -1);
@@ -612,19 +649,16 @@ namespace canvas
}
//----------------------------------------------------------------------------
osg::BoundingBox
#if OSG_VERSION_LESS_THAN(3,3,2)
Text::TextOSG::computeBound()
osg::BoundingBox Text::TextOSG::computeBound() const
#else
Text::TextOSG::computeBoundingBox()
osg::BoundingBox Text::TextOSG::computeBoundingBox() const
#endif
const
{
osg::BoundingBox bb =
#if OSG_VERSION_LESS_THAN(3,3,2)
osgText::Text::computeBound();
osg::BoundingBox bb = osgText::Text::computeBound();
#else
osgText::Text::computeBoundingBox();
osg::BoundingBox bb = osgText::Text::computeBoundingBox();
#endif
#if OSG_VERSION_LESS_THAN(3,1,0)
@@ -640,7 +674,7 @@ namespace canvas
return bb;
}
//----------------------------------------------------------------------------
#if OSG_VERSION_LESS_THAN(3,5,6)
void Text::TextOSG::computePositions(unsigned int contextID) const
{
if( _textureGlyphQuadMap.empty() || _layout == VERTICAL )
@@ -710,6 +744,14 @@ namespace canvas
return osgText::Text::computePositions(contextID);
}
#else
void Text::TextOSG::computePositionsImplementation()
{
TextBase::computePositionsImplementation();
}
#endif
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
const std::string Text::TYPE_NAME = "text";

View File

@@ -18,6 +18,9 @@
#include <simgear_config.h>
#include "Layout.hxx"
#include <algorithm>
#include <simgear/debug/logstream.hxx>
namespace simgear

View File

@@ -150,7 +150,7 @@ namespace canvas
//----------------------------------------------------------------------------
bool NasalWidget::hasHeightForWidth() const
{
return !_height_for_width.empty() || !_min_height_for_width.empty();
return _height_for_width || _min_height_for_width;
}
//----------------------------------------------------------------------------
@@ -186,7 +186,7 @@ namespace canvas
int NasalWidget::callHeightForWidthFunc( const HeightForWidthFunc& hfw,
int w ) const
{
if( hfw.empty() )
if( !hfw )
return -1;
try
@@ -239,17 +239,17 @@ namespace canvas
//----------------------------------------------------------------------------
int NasalWidget::heightForWidthImpl(int w) const
{
return callHeightForWidthFunc( _height_for_width.empty()
? _min_height_for_width
: _height_for_width, w );
return callHeightForWidthFunc( _height_for_width
? _height_for_width
: _min_height_for_width, w );
}
//----------------------------------------------------------------------------
int NasalWidget::minimumHeightForWidthImpl(int w) const
{
return callHeightForWidthFunc( _min_height_for_width.empty()
? _height_for_width
: _min_height_for_width, w );
return callHeightForWidthFunc( _min_height_for_width
? _min_height_for_width
: _height_for_width, w );
}

View File

@@ -40,8 +40,8 @@ namespace canvas
{
public:
typedef boost::function<void (nasal::Me, const SGRecti&)> SetGeometryFunc;
typedef boost::function<int (nasal::Me, int)> HeightForWidthFunc;
typedef std::function<void (nasal::Me, const SGRecti&)> SetGeometryFunc;
typedef std::function<int (nasal::Me, int)> HeightForWidthFunc;
/**
*

View File

@@ -18,30 +18,29 @@
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
#include <simgear_config.h>
#include <simgear/debug/BufferedLogCallback.hxx>
#include <boost/foreach.hpp>
#include <simgear/sg_inlines.h>
#include <simgear/threads/SGThread.hxx>
#include <simgear/threads/SGGuard.hxx>
#include <cstdlib> // for malloc
#include <cstring>
#include <mutex>
namespace simgear
{
class BufferedLogCallback::BufferedLogCallbackPrivate
{
public:
SGMutex m_mutex;
std::mutex m_mutex;
vector_cstring m_buffer;
unsigned int m_stamp;
unsigned int m_maxLength;
};
BufferedLogCallback::BufferedLogCallback(sgDebugClass c, sgDebugPriority p) :
simgear::LogCallback(c,p),
d(new BufferedLogCallbackPrivate)
@@ -52,19 +51,19 @@ BufferedLogCallback::BufferedLogCallback(sgDebugClass c, sgDebugPriority p) :
BufferedLogCallback::~BufferedLogCallback()
{
BOOST_FOREACH(unsigned char* msg, d->m_buffer) {
for (auto msg : d->m_buffer) {
free(msg);
}
}
void BufferedLogCallback::operator()(sgDebugClass c, sgDebugPriority p,
void BufferedLogCallback::operator()(sgDebugClass c, sgDebugPriority p,
const char* file, int line, const std::string& aMessage)
{
SG_UNUSED(file);
SG_UNUSED(line);
if (!shouldLog(c, p)) return;
vector_cstring::value_type msg;
if (aMessage.size() >= d->m_maxLength) {
msg = (vector_cstring::value_type) malloc(d->m_maxLength);
@@ -73,29 +72,29 @@ void BufferedLogCallback::operator()(sgDebugClass c, sgDebugPriority p,
} else {
msg = (vector_cstring::value_type) strdup(aMessage.c_str());
}
SGGuard<SGMutex> g(d->m_mutex);
std::lock_guard<std::mutex> g(d->m_mutex);
d->m_buffer.push_back(msg);
d->m_stamp++;
}
unsigned int BufferedLogCallback::stamp() const
{
return d->m_stamp;
}
unsigned int BufferedLogCallback::threadsafeCopy(vector_cstring& aOutput)
{
SGGuard<SGMutex> g(d->m_mutex);
std::lock_guard<std::mutex> g(d->m_mutex);
size_t sz = d->m_buffer.size();
aOutput.resize(sz);
memcpy(aOutput.data(), d->m_buffer.data(), sz * sizeof(vector_cstring::value_type));
return d->m_stamp;
}
}
void BufferedLogCallback::truncateAt(unsigned int t)
{
d->m_maxLength = t;
}
} // of namespace simgear

View File

@@ -6,10 +6,10 @@ using namespace osg;
/**
* merge OSG output into our logging system, so it gets recorded to file,
* and so we can display a GUI console with renderer issues, especially
* shader compilation warnings and errors.
*/
* merge OSG output into our logging system, so it gets recorded to file,
* and so we can display a GUI console with renderer issues, especially
* shader compilation warnings and errors.
*/
class NotifyLogger : public osg::NotifyHandler
{
public:
@@ -22,24 +22,33 @@ public:
if (strstr(message, "the final reference count was")) {
// as this is going to segfault ignore the translation of severity and always output the message.
SG_LOG(SG_GL, SG_ALERT, message);
int* trigger_segfault = 0;
*trigger_segfault = 0;
#ifndef DEBUG
throw new std::string(message);
//int* trigger_segfault = 0;
//*trigger_segfault = 0;
#endif
return;
}
SG_LOG(SG_GL, translateSeverity(severity), message);
char*tmessage = strdup(message);
char*lf = strrchr(tmessage, '\n');
if (lf)
*lf = 0;
SG_LOG(SG_OSG, translateSeverity(severity), tmessage);
free(tmessage);
}
private:
sgDebugPriority translateSeverity(osg::NotifySeverity severity) {
switch (severity) {
case osg::ALWAYS:
case osg::FATAL: return SG_ALERT;
case osg::WARN: return SG_WARN;
case osg::NOTICE:
case osg::INFO: return SG_INFO;
case osg::DEBUG_FP:
case osg::DEBUG_INFO: return SG_DEBUG;
default: return SG_ALERT;
case osg::ALWAYS:
case osg::FATAL: return SG_ALERT;
case osg::WARN: return SG_WARN;
case osg::NOTICE:
case osg::INFO: return SG_INFO;
case osg::DEBUG_FP:
case osg::DEBUG_INFO: return SG_DEBUG;
default: return SG_ALERT;
}
}
};

View File

@@ -35,7 +35,10 @@ typedef enum {
SG_TERRASYNC = 0x01000000,
SG_PARTICLES = 0x02000000,
SG_HEADLESS = 0x04000000,
SG_UNDEFD = 0x08000000, // For range checking
// SG_OSG (OSG notify) - will always be displayed regardless of FG log settings as OSG log level is configured
// separately and thus it makes more sense to allow these message through.
SG_OSG = 0x08000000,
SG_UNDEFD = 0x10000000, // For range checking
SG_ALL = 0xFFFFFFFF
} sgDebugClass;

View File

@@ -24,20 +24,21 @@
#include "logstream.hxx"
#include <cstring>
#include <iostream>
#include <fstream>
#include <sstream>
#include <algorithm>
#include <boost/foreach.hpp>
#include <mutex>
#include <simgear/sg_inlines.h>
#include <simgear/threads/SGThread.hxx>
#include <simgear/threads/SGQueue.hxx>
#include <simgear/threads/SGGuard.hxx>
#include <simgear/io/iostreams/sgstream.hxx>
#include <simgear/misc/sg_path.hxx>
#include <simgear/misc/strutils.hxx>
#include <simgear/timing/timestamp.hxx>
#if defined (SG_WINDOWS)
// for AllocConsole, OutputDebugString
@@ -61,7 +62,12 @@ LogCallback::LogCallback(sgDebugClass c, sgDebugPriority p) :
bool LogCallback::shouldLog(sgDebugClass c, sgDebugPriority p) const
{
return ((c & m_class) != 0 && p >= m_priority);
if ((c & m_class) != 0 && p >= m_priority)
return true;
if (c == SG_OSG) // always have OSG logging as it OSG logging is configured separately.
return true;
return false;
}
void LogCallback::setLogLevels( sgDebugClass c, sgDebugPriority p )
@@ -69,6 +75,18 @@ void LogCallback::setLogLevels( sgDebugClass c, sgDebugPriority p )
m_priority = p;
m_class = c;
}
const char* LogCallback::debugPriorityToString(sgDebugPriority p)
{
switch (p) {
case SG_ALERT: return "ALRT";
case SG_BULK: return "BULK";
case SG_DEBUG: return "DBUG";
case SG_INFO: return "INFO";
case SG_POPUP: return "POPU";
case SG_WARN: return "WARN";
default: return "UNKN";
}
}
const char* LogCallback::debugClassToString(sgDebugClass c)
{
@@ -101,6 +119,7 @@ const char* LogCallback::debugClassToString(sgDebugClass c)
case SG_TERRASYNC: return "terrasync";
case SG_PARTICLES: return "particles";
case SG_HEADLESS: return "headless";
case SG_OSG: return "OSG";
default: return "unknown";
}
}
@@ -112,18 +131,48 @@ const char* LogCallback::debugClassToString(sgDebugClass c)
class FileLogCallback : public simgear::LogCallback
{
public:
SGTimeStamp logTimer;
FileLogCallback(const SGPath& aPath, sgDebugClass c, sgDebugPriority p) :
simgear::LogCallback(c, p)
{
m_file.open(aPath, std::ios_base::out | std::ios_base::trunc);
logTimer.stamp();
}
virtual void operator()(sgDebugClass c, sgDebugPriority p,
const char* file, int line, const std::string& message)
{
if (!shouldLog(c, p)) return;
m_file << debugClassToString(c) << ":" << (int) p
<< ":" << file << ":" << line << ":" << message << std::endl;
// fprintf(stderr, "%7.2f [%.8s]:%-10s %s\n", logTimer.elapsedMSec() / 1000.0, debugPriorityToString(p), debugClassToString(c), aMessage.c_str());
m_file
<< std::fixed
<< std::setprecision(2)
<< std::setw(8)
<< std::right
<< (logTimer.elapsedMSec() / 1000.0)
<< std::setw(8)
<< std::left
<< " ["+std::string(debugPriorityToString(p))+"]:"
<< std::setw(10)
<< std::left
<< debugClassToString(c)
;
if (file) {
/* <line> can be -ve to indicate that m_fileLine was false, but we
want to show file:line information regardless of m_fileLine. */
m_file
<< file
<< ":"
<< abs(line)
<< ": "
;
}
m_file
<< message << std::endl;
//m_file << debugClassToString(c) << ":" << (int)p
// << ":" << file << ":" << line << ":" << message << std::endl;
}
private:
sg_ofstream m_file;
@@ -132,9 +181,12 @@ private:
class StderrLogCallback : public simgear::LogCallback
{
public:
SGTimeStamp logTimer;
StderrLogCallback(sgDebugClass c, sgDebugPriority p) :
simgear::LogCallback(c, p)
{
logTimer.stamp();
}
#if defined (SG_WINDOWS)
@@ -148,8 +200,15 @@ public:
const char* file, int line, const std::string& aMessage)
{
if (!shouldLog(c, p)) return;
//fprintf(stderr, "%s\n", aMessage.c_str());
fprintf(stderr, "%s\n", aMessage.c_str());
if (file && line > 0) {
fprintf(stderr, "%8.2f %s:%i: [%.8s]:%-10s %s\n", logTimer.elapsedMSec()/1000.0, file, line, debugPriorityToString(p), debugClassToString(c), aMessage.c_str());
}
else {
fprintf(stderr, "%8.2f [%.8s]:%-10s %s\n", logTimer.elapsedMSec()/1000.0, debugPriorityToString(p), debugClassToString(c), aMessage.c_str());
}
// file, line, aMessage.c_str());
//fprintf(stderr, "%s:%d:%s:%d:%s\n", debugClassToString(c), p,
// file, line, aMessage.c_str());
fflush(stderr);
@@ -243,10 +302,10 @@ public:
* window; stdout/stderr will not appear (except in logfiles as they do now)
* 4) When started from the Console (with --console) open a new console window
* 5) Ensure that IO redirection still works when started from the console
*
*
* Notes:
* 1) fgfs needs to be a GUI subsystem app - which it already is
* 2) What can't be done is to make the cmd prompt run fgfs synchronously;
* 2) What can't be done is to make the cmd prompt run fgfs synchronously;
* this is only something that can be done via "start /wait fgfs".
*/
@@ -270,7 +329,7 @@ public:
}
} else {
/*
* Attempt to attach to the console process of the parent process; when launched from cmd.exe this should be the console,
* Attempt to attach to the console process of the parent process; when launched from cmd.exe this should be the console,
* when launched via the RUN menu explorer, or another GUI app that wasn't started from the console this will fail.
* When it fails we will redirect to the NUL device. This is to ensure that we have valid streams.
* Later on in the initialisation sequence the --console option will be processed and this will cause the requestConsole() to
@@ -300,7 +359,7 @@ public:
if (!stdout_isNull){
if (!m_stdout_isRedirectedAlready)
freopen("conout$", "w", stdout);
else
else
/*
* for already redirected streams we need to attach the stream to the OS handle that is open.
* - this comes from part of the answer http://stackoverflow.com/a/13841522
@@ -319,7 +378,7 @@ public:
}
}
//http://stackoverflow.com/a/25927081
//Clear the error state for each of the C++ standard stream objects.
//Clear the error state for each of the C++ standard stream objects.
std::wcout.clear();
std::cout.clear();
std::wcerr.clear();
@@ -328,9 +387,14 @@ public:
m_callbacks.push_back(new StderrLogCallback(m_logClass, m_logPriority));
m_consoleCallbacks.push_back(m_callbacks.back());
#if defined (SG_WINDOWS) && !defined(NDEBUG)
m_callbacks.push_back(new WinDebugLogCallback(m_logClass, m_logPriority));
m_consoleCallbacks.push_back(m_callbacks.back());
#if defined (SG_WINDOWS)
const char* winDebugEnv = ::getenv("SG_WINDEBUG");
const bool b = winDebugEnv ? simgear::strutils::to_bool(std::string{winDebugEnv}) : false;
if (b) {
m_callbacks.push_back(new WinDebugLogCallback(m_logClass, m_logPriority));
m_consoleCallbacks.push_back(m_callbacks.back());
}
#endif
}
@@ -339,7 +403,7 @@ public:
removeCallbacks();
}
SGMutex m_lock;
std::mutex m_lock;
SGBlockingQueue<LogEntry> m_entries;
// log entries posted during startup
@@ -361,13 +425,14 @@ public:
bool m_stdout_isRedirectedAlready = false;
#endif
bool m_developerMode = false;
bool m_fileLine = false;
// test suite mode.
bool m_testMode = false;
void startLog()
{
SGGuard<SGMutex> g(m_lock);
std::lock_guard<std::mutex> g(m_lock);
if (m_isRunning) return;
m_isRunning = true;
start();
@@ -379,8 +444,11 @@ public:
return;
}
m_startupLogging = on;
m_startupEntries.clear();
{
std::lock_guard<std::mutex> g(m_lock);
m_startupLogging = on;
m_startupEntries.clear();
}
}
virtual void run()
@@ -389,16 +457,17 @@ public:
LogEntry entry(m_entries.pop());
// special marker entry detected, terminate the thread since we are
// making a configuration change or quitting the app
if ((entry.debugClass == SG_NONE) && !strcmp(entry.file, "done")) {
if ((entry.debugClass == SG_NONE) && entry.file && !strcmp(entry.file, "done")) {
return;
}
if (m_startupLogging) {
// save to the startup list for not-yet-added callbacks to
// pull down on startup
m_startupEntries.push_back(entry);
{
std::lock_guard<std::mutex> g(m_lock);
if (m_startupLogging) {
// save to the startup list for not-yet-added callbacks to
// pull down on startup
m_startupEntries.push_back(entry);
}
}
// submit to each installed callback in turn
for (simgear::LogCallback* cb : m_callbacks) {
(*cb)(entry.debugClass, entry.debugPriority,
@@ -409,14 +478,16 @@ public:
bool stop()
{
SGGuard<SGMutex> g(m_lock);
if (!m_isRunning) {
return false;
}
{
std::lock_guard<std::mutex> g(m_lock);
if (!m_isRunning) {
return false;
}
// log a special marker value, which will cause the thread to wakeup,
// and then exit
log(SG_NONE, SG_ALERT, "done", -1, "");
// log a special marker value, which will cause the thread to wakeup,
// and then exit
log(SG_NONE, SG_ALERT, "done", -1, "");
}
join();
m_isRunning = false;
@@ -460,7 +531,7 @@ public:
PauseThread pause(this);
m_logPriority = p;
m_logClass = c;
BOOST_FOREACH(simgear::LogCallback* cb, m_consoleCallbacks) {
for (auto cb : m_consoleCallbacks) {
cb->setLogLevels(c, p);
}
}
@@ -470,6 +541,10 @@ public:
// Testing mode, so always log.
if (m_testMode) return true;
// SG_OSG (OSG notify) - will always be displayed regardless of FG log settings as OSG log level is configured
// separately and thus it makes more sense to allow these message through.
if (static_cast<unsigned>(p) == static_cast<unsigned>(SG_OSG)) return true;
p = translatePriority(p);
if (p >= SG_INFO) return true;
return ((c & m_logClass) != 0 && p >= m_logPriority);
@@ -479,6 +554,10 @@ public:
const char* fileName, int line, const std::string& msg)
{
p = translatePriority(p);
if (!m_fileLine) {
/* This prevents output of file:line in StderrLogCallback. */
line = -line;
}
LogEntry entry(c, p, fileName, line, msg);
m_entries.push(entry);
}
@@ -490,7 +569,7 @@ public:
}
if (in == SG_DEV_ALERT) {
return m_developerMode ? SG_POPUP : SG_WARN;
return m_developerMode ? SG_ALERT : SG_WARN;
}
return in;
@@ -500,7 +579,7 @@ public:
/////////////////////////////////////////////////////////////////////////////
static std::unique_ptr<logstream> global_logstream;
static SGMutex global_logStreamLock;
static std::mutex global_logStreamLock;
logstream::logstream()
{
@@ -525,6 +604,10 @@ void logstream::setDeveloperMode(bool devMode)
d->m_developerMode = devMode;
}
void logstream::setFileLine(bool fileLine)
{
d->m_fileLine = fileLine;
}
void
logstream::addCallback(simgear::LogCallback* cb)
@@ -664,7 +747,7 @@ sglog()
// http://www.aristeia.com/Papers/DDJ_Jul_Aug_2004_revised.pdf
// in the absence of portable memory barrier ops in Simgear,
// let's keep this correct & safe
SGGuard<SGMutex> g(global_logStreamLock);
std::lock_guard<std::mutex> g(global_logStreamLock);
if( !global_logstream )
global_logstream.reset(new logstream);
@@ -738,14 +821,14 @@ namespace simgear
{
void requestConsole()
{
{
sglog().requestConsole();
}
void shutdownLogging()
{
SGGuard<SGMutex> g(global_logStreamLock);
std::lock_guard<std::mutex> g(global_logStreamLock);
global_logstream.reset();
}

View File

@@ -52,6 +52,7 @@ protected:
bool shouldLog(sgDebugClass c, sgDebugPriority p) const;
static const char* debugClassToString(sgDebugClass c);
static const char* debugPriorityToString(sgDebugPriority p);
private:
sgDebugClass m_class;
sgDebugPriority m_priority;
@@ -111,6 +112,12 @@ public:
*/
void setDeveloperMode(bool devMode);
/**
* set output of file:line mode on/off. If on, all log messages are
* prefixed by the file:line of the caller of SG_LOG().
*/
void setFileLine(bool fileLine);
/**
* the core logging method
*/
@@ -203,9 +210,11 @@ logstream& sglog();
} } while(0)
#ifdef FG_NDEBUG
# define SG_LOG(C,P,M) do { if((P) == SG_POPUP) SG_LOGX(C,P,M) } while(0)
# define SG_LOG_NAN(C,P,M) SG_LOG(C,P,M)
# define SG_HEXDUMP(C,P,MEM,LEN)
#else
# define SG_LOG(C,P,M) SG_LOGX(C,P,M)
# define SG_LOG_NAN(C,P,M) do { SG_LOGX(C,P,M); throw std::overflow_error(M); } while(0)
# define SG_LOG_HEXDUMP(C,P,MEM,LEN) if(sglog().would_log(C,P)) sglog().hexdump(C, P, __FILE__, __LINE__, MEM, LEN)
#endif

View File

@@ -0,0 +1,33 @@
include (SimGearComponent)
set(HEADERS
Emesary.hxx
INotification.hxx
IReceiver.hxx
ITransmitter.hxx
ReceiptStatus.hxx
Transmitter.hxx
notifications.hxx
)
set(SOURCES
Emesary.cxx
)
simgear_component(emesary emesary "${SOURCES}" "${HEADERS}")
if(ENABLE_TESTS)
add_executable(test_emesary test_emesary.cxx)
set_target_properties(test_emesary PROPERTIES
COMPILE_DEFINITIONS "SRC_DIR=\"${CMAKE_CURRENT_SOURCE_DIR}\"" )
target_link_libraries(test_emesary ${TEST_LIBS})
add_test(emesary ${EXECUTABLE_OUTPUT_PATH}/test_emesary)
endif(ENABLE_TESTS)

View File

@@ -0,0 +1,31 @@
/*---------------------------------------------------------------------------
*
* Title : Emesary - class based inter-object communication
*
* File Type : Implementation File
*
* Description : Emesary main.
* : This only needs to instance the GlobalTransmitter as all of the
* : logic is in the header files (by design)
*
* References : http://www.chateau-logic.com/content/class-based-inter-object-communication
*
* Author : Richard Harrison (richard@zaretto.com)
*
* Creation Date : 18 March 2002
*
* Version : $Header: $
*
* Copyright © 2002 Richard Harrison All Rights Reserved.
*
*---------------------------------------------------------------------------*/
#include "simgear/emesary/Emesary.hxx"
namespace simgear
{
namespace Emesary
{
}
}

View File

@@ -0,0 +1,48 @@
#ifndef EMESARY_hxx
#define EMESARY_hxx
/*---------------------------------------------------------------------------
*
* Title : Emesary - class based inter-object communication
*
* File Type : Implementation File
*
* Description : Provides generic inter-object communication. For an object to receive a message it
* : must first register with a Transmitter, such as GlobalTransmitter, and implement the
* : IReceiver interface. That's it.
* : To send a message use a Transmitter with an object. That's all there is to it.
*
* References : http://www.chateau-logic.com/content/class-based-inter-object-communication
*
* Author : Richard Harrison (richard@zaretto.com)
*
* Creation Date : 18 March 2002, rewrite 2017, simgear version 2019
*
* Version : $Header: $
*
* Copyright (C)2019 Richard Harrison Licenced under GPL2 or later.
*
*---------------------------------------------------------------------------*/
#include <typeinfo>
#include "ReceiptStatus.hxx"
#include "INotification.hxx"
#include "IReceiver.hxx"
#include "ITransmitter.hxx"
#include "Transmitter.hxx"
#include <simgear/structure/Singleton.hxx>
namespace simgear
{
namespace Emesary
{
class GlobalTransmitter : public simgear::Singleton<Transmitter>
{
public:
GlobalTransmitter()
{
}
virtual ~GlobalTransmitter() {}
};
}
}
#endif

View File

@@ -0,0 +1,54 @@
#ifndef INOTIFICATION_hxx
#define INOTIFICATION_hxx
/*---------------------------------------------------------------------------
*
* Title : Emesary - Notification base class
*
* File Type : Implementation File
*
* Description : Base class (interface) for all Notifications.
* : This is also compatible with the usual implementation of how we
* : implement queued notifications.
*
* References : http://www.chateau-logic.com/content/class-based-inter-object-communication
*
* Author : Richard Harrison (richard@zaretto.com)
*
* Creation Date : 18 March 2002, rewrite 2017, simgear version 2019
*
* Version : $Header: $
*
* Copyright (C)2019 Richard Harrison Licenced under GPL2 or later.
*
*---------------------------------------------------------------------------*/
namespace simgear
{
namespace Emesary
{
/// Interface (base class) for all notifications.
class INotification
{
public:
// text representation of notification type. must be unique across all notifications
virtual const char *GetType() = 0;
/// Used to control the sending of notifications. If this returns false then the Transmitter
/// should not send this notification.
virtual bool IsReadyToSend() { return true; }
/// Used to control the timeout. If this notification has timed out - then the processor is entitled
/// to true.
virtual bool IsTimedOut() { return false; }
/// when this notification has completed the processing recipient must set this to true.
/// the processing recipient is responsible for follow on notifications.
/// a notification can remain as complete until the transmit queue decides to remove it from the queue.
/// there is no requirement that elements are removed immediately upon completion merely that once complete
/// the transmitter should not notify any more elements.
/// The current notification loop may be completed - following the usual convention unless Completed or Abort
/// is returned as the status.
virtual bool IsComplete() { return true; }
};
}
}
#endif

View File

@@ -0,0 +1,47 @@
#ifndef IRECEIVER_hxx
#define IRECEIVER_hxx
/*---------------------------------------------------------------------------
*
* Title : Emesary - Receiver base class
*
* File Type : Implementation File
*
* Description : Base class for all recipients.
*
* References : http://www.chateau-logic.com/content/class-based-inter-object-communication
*
* Author : Richard Harrison (richard@zaretto.com)
*
* Creation Date : 18 March 2002, rewrite 2017, simgear version 2019
*
* Version : $Header: $
*
* Copyright (C)2019 Richard Harrison Licenced under GPL2 or later.
*
*---------------------------------------------------------------------------*/
namespace simgear
{
namespace Emesary
{
/// Interface (base class) for a recipeint.
class IReceiver
{
public:
/// Receive notification - must be implemented
virtual ReceiptStatus Receive(INotification& message) = 0;
/// Called when registered at a transmitter
virtual void OnRegisteredAtTransmitter(class Transmitter *p)
{
}
/// Called when de-registered at a transmitter
virtual void OnDeRegisteredAtTransmitter(class Transmitter *p)
{
}
};
}
}
#endif

View File

@@ -0,0 +1,52 @@
#ifndef ITRANSMITTER_hxx
#define ITRANSMITTER_hxx
/*---------------------------------------------------------------------------
*
* Title : Emesary - Transmitter base class
*
* File Type : Implementation File
*
* Description : Base class for all transmitters.
*
* References : http://www.chateau-logic.com/content/class-based-inter-object-communication
*
* Author : Richard Harrison (richard@zaretto.com)
*
* Creation Date : 18 March 2002, rewrite 2017, simgear version 2019
*
* Version : $Header: $
*
* Copyright (C)2019 Richard Harrison Licenced under GPL2 or later.
*
*---------------------------------------------------------------------------*/
namespace simgear
{
namespace Emesary
{
/// Interface (base clasee) for a transmitter.
/// Transmits Message derived objects. Each instance of this class provides a
/// event/databus to which any number of receivers can attach to.
class ITransmitter
{
public:
// Registers a recipient to receive message from this transmitter
virtual void Register(IReceiver& R) = 0;
// Removes a recipient from from this transmitter
virtual void DeRegister(IReceiver& R) = 0;
//Notify all registered recipients. Stop when receipt status of abort or finished are received.
//The receipt status from this method will be
// - OK > message handled
// - Fail > message not handled. A status of Abort from a recipient will result in our status
// being fail as Abort means that the message was not and cannot be handled, and
// allows for usages such as access controls.
virtual ReceiptStatus NotifyAll(INotification& M) = 0;
/// number of recipients
virtual int Count() = 0;
};
}
}
#endif

View File

@@ -0,0 +1,54 @@
#ifndef RECEIPTSTATUS_hxx
#define RECEIPTSTATUS_hxx
/*---------------------------------------------------------------------------
*
* Title : Emesary - Transmitter base class
*
* File Type : Implementation File
*
* Description : Defines the receipt status that can be returned from
* : a receive method.
*
* References : http://www.chateau-logic.com/content/class-based-inter-object-communication
*
* Author : Richard Harrison (richard@zaretto.com)
*
* Creation Date : 18 March 2002, rewrite 2017, simgear version 2019
*
* Version : $Header: $
*
* Copyright (C)2019 Richard Harrison Licenced under GPL2 or later.
*
*---------------------------------------------------------------------------*/
namespace simgear
{
namespace Emesary
{
enum ReceiptStatus
{
/// Processing completed successfully
ReceiptStatusOK = 0,
/// Individual item failure
ReceiptStatusFail = 1,
/// Fatal error; stop processing any further recipieints of this message. Implicitly fail
ReceiptStatusAbort = 2,
/// Definitive completion - do not send message to any further recipieints
ReceiptStatusFinished = 3,
/// Return value when method doesn't process a message.
ReceiptStatusNotProcessed = 4,
/// Message has been sent but the return status cannot be determined as it has not been processed by the recipient.
/// e.g. a queue or outgoing bridge
ReceiptStatusPending = 5,
/// Message has been definitively handled but the return value cannot be determined. The message will not be sent any further
/// e.g. a point to point forwarding bridge
ReceiptStatusPendingFinished = 6,
};
}
}
#endif

View File

@@ -0,0 +1,203 @@
#ifndef TRANSMITTER_hxx
#define TRANSMITTER_hxx
/*---------------------------------------------------------------------------
*
* Title : Emesary - Transmitter base class
*
* File Type : Implementation File
*
* Description : Defines the receipt status that can be returned from
* : a receive method.
*
* References : http://www.chateau-logic.com/content/class-based-inter-object-communication
*
* Author : Richard Harrison (richard@zaretto.com)
*
* Creation Date : 18 March 2002, rewrite 2017, simgear version 2019
*
* Version : $Header: $
*
* Copyright (C)2019 Richard Harrison Licenced under GPL2 or later.
*
*---------------------------------------------------------------------------*/
#include <algorithm>
#include <string>
#include <list>
#include <set>
#include <vector>
#include <atomic>
#include <mutex>
namespace simgear
{
namespace Emesary
{
// Implementation of a ITransmitter
class Transmitter : public ITransmitter
{
protected:
typedef std::list<IReceiver *> RecipientList;
RecipientList recipient_list;
RecipientList deleted_recipients;
int CurrentRecipientIndex = 0;
std::mutex _lock;
std::atomic<int> receiveDepth;
std::atomic<int> sentMessageCount;
void UnlockList()
{
_lock.unlock();
}
void LockList()
{
_lock.lock();
}
public:
Transmitter() : receiveDepth(0), sentMessageCount(0)
{
}
virtual ~Transmitter()
{
}
// Registers an object to receive messsages from this transmitter.
// This object is added to the top of the list of objects to be notified. This is deliberate as
// the sequence of registration and message receipt can influence the way messages are processing
// when ReceiptStatus of Abort or Finished are encountered. So it was a deliberate decision that the
// most recently registered recipients should process the messages/events first.
virtual void Register(IReceiver& r)
{
LockList();
recipient_list.push_back(&r);
r.OnRegisteredAtTransmitter(this);
if (std::find(deleted_recipients.begin(), deleted_recipients.end(), &r) != deleted_recipients.end())
deleted_recipients.remove(&r);
UnlockList();
}
// Removes an object from receving message from this transmitter
virtual void DeRegister(IReceiver& R)
{
LockList();
//printf("Remove %x\n", &R);
if (recipient_list.size())
{
if (std::find(recipient_list.begin(), recipient_list.end(), &R) != recipient_list.end())
{
recipient_list.remove(&R);
R.OnDeRegisteredAtTransmitter(this);
if (std::find(deleted_recipients.begin(), deleted_recipients.end(), &R) == deleted_recipients.end())
deleted_recipients.push_back(&R);
}
}
UnlockList();
}
// Notify all registered recipients. Stop when receipt status of abort or finished are received.
// The receipt status from this method will be
// - OK > message handled
// - Fail > message not handled. A status of Abort from a recipient will result in our status
// being fail as Abort means that the message was not and cannot be handled, and
// allows for usages such as access controls.
virtual ReceiptStatus NotifyAll(INotification& M)
{
ReceiptStatus return_status = ReceiptStatusNotProcessed;
sentMessageCount++;
try
{
LockList();
if (receiveDepth == 0)
deleted_recipients.clear();
receiveDepth++;
std::vector<IReceiver*> temp(recipient_list.size());
int idx = 0;
for (RecipientList::iterator i = recipient_list.begin(); i != recipient_list.end(); i++)
{
temp[idx++] = *i;
}
UnlockList();
int tempSize = temp.size();
for (int index = 0; index < tempSize; index++)
{
IReceiver* R = temp[index];
LockList();
if (deleted_recipients.size())
{
if (std::find(deleted_recipients.begin(), deleted_recipients.end(), R) != deleted_recipients.end())
{
UnlockList();
continue;
}
}
UnlockList();
if (R)
{
ReceiptStatus rstat = R->Receive(M);
switch (rstat)
{
case ReceiptStatusFail:
return_status = ReceiptStatusFail;
break;
case ReceiptStatusPending:
return_status = ReceiptStatusPending;
break;
case ReceiptStatusPendingFinished:
return rstat;
case ReceiptStatusNotProcessed:
break;
case ReceiptStatusOK:
if (return_status == ReceiptStatusNotProcessed)
return_status = rstat;
break;
case ReceiptStatusAbort:
return ReceiptStatusAbort;
case ReceiptStatusFinished:
return ReceiptStatusOK;
}
}
}
}
catch (...)
{
throw;
// return_status = ReceiptStatusAbort;
}
receiveDepth--;
return return_status;
}
// number of currently registered recipients
virtual int Count()
{
LockList();
return recipient_list.size();
UnlockList();
}
// number of sent messages.
int SentMessageCount()
{
return sentMessageCount;
}
// ascertain if a receipt status can be interpreted as failure.
static bool Failed(ReceiptStatus receiptStatus)
{
//
// failed is either Fail or Abort.
// NotProcessed isn't a failure because it hasn't been processed.
return receiptStatus == ReceiptStatusFail
|| receiptStatus == ReceiptStatusAbort;
}
};
}
}
#endif

View File

@@ -0,0 +1,68 @@
#ifndef NOTIFICATIONS_hxx
#define NOTIFICATIONS_hxx
/*---------------------------------------------------------------------------
*
* Title : Emesary - class based inter-object communication
*
* File Type : Implementation File
*
* Description : simgear notifications
*
* References : http://www.chateau-logic.com/content/class-based-inter-object-communication
*
* Author : Richard Harrison (richard@zaretto.com)
*
* Creation Date : 18 March 2002, rewrite 2017
*
* Version : $Header: $
*
* Copyright <20> 2002 - 2017 Richard Harrison All Rights Reserved.
*
*---------------------------------------------------------------------------*/
#include "INotification.hxx"
namespace simgear
{
namespace Notifications
{
class MainLoopNotification : public simgear::Emesary::INotification
{
public:
enum Type { Started, Stopped, Begin, End };
MainLoopNotification(Type v) : _type(v) {}
virtual Type GetValue() { return _type; }
virtual const char *GetType() { return "MainLoop"; }
protected:
Type _type;
};
class NasalGarbageCollectionConfigurationNotification : public simgear::Emesary::INotification
{
public:
NasalGarbageCollectionConfigurationNotification(bool canWait, bool active) : CanWait(canWait), Active(active) {}
virtual bool GetCanWait() { return CanWait; }
virtual bool GetActive() { return Active; }
virtual const char *GetType() { return "NasalGarbageCollectionConfiguration"; }
virtual bool SetWait(bool wait) {
if (wait == CanWait)
return false;
CanWait = wait;
return true;
}
virtual bool SetActive(bool active) {
if (active == Active)
return false;
Active = active;
return true;
}
public:
bool CanWait;
bool Active;
};
}
}
#endif

View File

@@ -0,0 +1,130 @@
////////////////////////////////////////////////////////////////////////
// Test harness for Emesary.
////////////////////////////////////////////////////////////////////////
#include <simgear_config.h>
#include <simgear/compiler.h>
#include <iostream>
#include <simgear/threads/SGThread.hxx>
#include <simgear/emesary/Emesary.hxx>
using std::cout;
using std::cerr;
using std::endl;
std::atomic<int> nthread {0};
std::atomic<int> noperations {0};
const int MaxIterations = 9999;
class TestThreadNotification : public simgear::Emesary::INotification
{
protected:
const char *baseValue;
public:
TestThreadNotification(const char *v) : baseValue(v) {}
virtual const char* GetType () { return baseValue; }
};
class TestThreadRecipient : public simgear::Emesary::IReceiver
{
public:
TestThreadRecipient() : receiveCount(0)
{
}
std::atomic<int> receiveCount;
virtual simgear::Emesary::ReceiptStatus Receive(simgear::Emesary::INotification &n)
{
if (n.GetType() == (const char*)this)
{
// Unused: TestThreadNotification *tn = dynamic_cast<TestThreadNotification *>(&n);
receiveCount++;
TestThreadNotification onwardNotification("AL");
simgear::Emesary::GlobalTransmitter::instance()->NotifyAll(onwardNotification);
return simgear::Emesary::ReceiptStatusOK;
}
return simgear::Emesary::ReceiptStatusOK;
}
};
class EmesaryTestThread : public SGThread
{
protected:
virtual void run() {
int threadId = nthread.fetch_add(1);
//System.Threading.Interlocked.Increment(ref nthread);
//var rng = new Random();
TestThreadRecipient r;
char temp[100];
sprintf(temp, "Notif %d", threadId);
printf("starting thread %s\n", temp);
TestThreadNotification tn((const char*)&r);
for (int i = 0; i < MaxIterations; i++)
{
simgear::Emesary::GlobalTransmitter::instance()->Register(r);
simgear::Emesary::GlobalTransmitter::instance()->NotifyAll(tn);
simgear::Emesary::GlobalTransmitter::instance()->DeRegister(r);
//System.Threading.Thread.Sleep(rng.Next(MaxSleep));
noperations++;
}
printf("%s invocations %d\n", temp, (int)r.receiveCount);
printf("finish thread %s\n", temp);
}
};
class EmesaryTest
{
public:
void Emesary_MultiThreadTransmitterTest()
{
int num_threads = 12;
std::list<EmesaryTestThread*> threads;
for (int i = 0; i < num_threads; i++)
{
EmesaryTestThread *thread = new EmesaryTestThread();
threads.push_back(thread);
thread->start();
}
for (std::list<EmesaryTestThread*>::iterator i = threads.begin(); i != threads.end(); i++)
{
(*i)->join();
}
}
};
void testEmesaryThreaded()
{
TestThreadRecipient r;
TestThreadNotification tn((const char*)&r);
simgear::Emesary::GlobalTransmitter::instance()->Register(r);
for (int i = 0; i < MaxIterations*MaxIterations; i++)
{
simgear::Emesary::GlobalTransmitter::instance()->NotifyAll(tn);
//System.Threading.Thread.Sleep(rng.Next(MaxSleep));
noperations++;
}
simgear::Emesary::GlobalTransmitter::instance()->DeRegister(r);
printf("invocations %d\n", simgear::Emesary::GlobalTransmitter::instance()->SentMessageCount());
EmesaryTest t;
t.Emesary_MultiThreadTransmitterTest();
}
int main(int ac, char ** av)
{
testEmesaryThreaded();
std::cout << "all tests passed" << std::endl;
return 0;
}

View File

@@ -38,9 +38,12 @@
# include <simgear_config.h>
#endif
#include <iomanip>
#include <string>
#include <time.h>
#include <cstring>
#include <ostream>
#include <sstream>
#include <simgear/debug/logstream.hxx>
#include <simgear/structure/exception.hxx>
@@ -155,6 +158,320 @@ SGMetar::~SGMetar()
}
static const char *azimuthName(double d)
{
const char *dir[] = {
"N", "NNE", "NE", "ENE",
"E", "ESE", "SE", "SSE",
"S", "SSW", "SW", "WSW",
"W", "WNW", "NW", "NNW"
};
d += 11.25;
while (d < 0)
d += 360;
while (d >= 360)
d -= 360;
return dir[int(d / 22.5)];
}
// round double to 10^g
static double rnd(double r, int g = 0)
{
double f = pow(10.0, g);
return f * floor(r / f + 0.5);
}
/* A manipulator that can use spaces to emulate tab characters. */
struct Tab
{
/* If <stops> is 0, we simply insert tab characters. Otherwise we insert
spaces to align with the next column at multiple of <stops>. */
explicit Tab(int stops)
:
_stops(stops)
{}
int _stops;
};
std::ostream& operator << (std::ostream& out, const Tab& t)
{
if (t._stops == 0) {
return out << '\t';
}
std::ostringstream& out2 = *(std::ostringstream*) &out;
std::string s = out2.str();
if (t._stops < 0) {
if (!s.size() || s[s.size()-1] != ' ') {
out << ' ';
}
return out;
}
auto nl = s.rfind('\n');
if (nl < 0) nl = 0;
int column = 0;
for (auto i = nl+1; i != s.size(); ++i) {
if (s[i] == '\t')
column = (column + t._stops) / t._stops * t._stops;
else
column += 1;
}
int column2 = (column + t._stops) / t._stops * t._stops;
for (int i=column; i<column2; ++i) {
out << ' ';
}
return out;
}
/* Manipulator for SGMetarVisibility using a Tab. */
struct SGMetarVisibilityManip
{
explicit SGMetarVisibilityManip(const SGMetarVisibility& v, const Tab& tab)
:
_v(v),
_tab(tab)
{}
const SGMetarVisibility& _v;
const Tab& _tab;
};
std::ostream& operator << (std::ostream& out, const SGMetarVisibilityManip& v)
{
int m = v._v.getModifier();
const char *mod;
if (m == SGMetarVisibility::GREATER_THAN)
mod = ">=";
else if (m == SGMetarVisibility::LESS_THAN)
mod = "<";
else
mod = "";
out << mod;
double dist = rnd(v._v.getVisibility_m(), 1);
if (dist < 1000.0)
out << rnd(dist, 1) << " m";
else
out << rnd(dist / 1000.0, -1) << " km";
const char *dir = "";
int i;
if ((i = v._v.getDirection()) != -1) {
dir = azimuthName(i);
out << " " << dir;
}
out << v._tab << v._tab << v._tab << v._tab << v._tab;
out << mod << rnd(v._v.getVisibility_sm(), -1) << " US-miles " << dir;
return out;
}
std::string SGMetar::getDescription(int tabstops) const
{
std::ostringstream out;
const char *s;
char buf[256];
double d;
int i, lineno;
Tab tab(tabstops);
if ((i = getReportType()) == SGMetar::AUTO)
out << "(METAR automatically generated)\n";
else if (i == SGMetar::COR)
out << "(METAR manually corrected)\n";
else if (i == SGMetar::RTD)
out << "(METAR routine delayed)\n";
out << "Airport-Id:" << tab << tab << getId() << "\n";
// date/time
int year = getYear();
int month = getMonth();
out << "Report time:" << tab << tab << year << '/' << month << '/' << getDay();
out << ' ' << getHour() << ':';
out << std::setw(2) << std::setfill('0') << getMinute() << " UTC\n";
// visibility
SGMetarVisibility minvis = getMinVisibility();
SGMetarVisibility maxvis = getMaxVisibility();
double min = minvis.getVisibility_m();
double max = maxvis.getVisibility_m();
if (min != NaN) {
if (max != NaN) {
out << "min. Visibility:" << tab << SGMetarVisibilityManip(minvis, tab) << "\n";
out << "max. Visibility:" << tab << SGMetarVisibilityManip(maxvis, tab) << "\n";
} else {
out << "Visibility:" << tab << tab << SGMetarVisibilityManip(minvis, tab) << "\n";
}
}
// directed visibility
const SGMetarVisibility *dirvis = getDirVisibility();
for (i = 0; i < 8; i++, dirvis++)
if (dirvis->getVisibility_m() != NaN)
out << tab << tab << tab << SGMetarVisibilityManip(*dirvis, tab) << "\n";
// vertical visibility
SGMetarVisibility vertvis = getVertVisibility();
if ((d = vertvis.getVisibility_ft()) != NaN)
out << "Vert. visibility:" << tab << SGMetarVisibilityManip(vertvis, tab) << "\n";
else if (vertvis.getModifier() == SGMetarVisibility::NOGO)
out << "Vert. visibility:" << tab << "impossible to determine" << "\n";
// wind
d = getWindSpeed_kmh();
out << "Wind:" << tab << tab << tab;
if (d < .1)
out << "none" << "\n";
else {
if ((i = getWindDir()) == -1)
out << "from variable directions";
else
out << "from the " << azimuthName(i) << " (" << i << " deg)";
out << " at " << rnd(d, -1) << " km/h";
out << tab << tab << rnd(getWindSpeed_kt(), -1) << " kt";
out << " = " << rnd(getWindSpeed_mph(), -1) << " mph";
out << " = " << rnd(getWindSpeed_mps(), -1) << " m/s";
out << "\n";
d = getGustSpeed_kmh();
if (d != NaN && d != 0) {
out << tab << tab << tab << "with gusts at " << rnd(d, -1) << " km/h";
out << tab << tab << tab << rnd(getGustSpeed_kt(), -1) << " kt";
out << " = " << rnd(getGustSpeed_mph(), -1) << " mph";
out << " = " << rnd(getGustSpeed_mps(), -1) << " m/s";
out << "\n";
}
int from = getWindRangeFrom();
int to = getWindRangeTo();
if (from != to) {
out << tab << tab << tab << "variable from " << azimuthName(from);
out << " to " << azimuthName(to);
out << " (" << from << "deg --" << to << " deg)" << "\n";
}
}
// temperature/humidity/air pressure
if ((d = getTemperature_C()) != NaN) {
out << "Temperature:" << tab << tab << d << " C" << tab << tab << tab << tab << tab;
out << rnd(getTemperature_F(), -1) << " F" << "\n";
if ((d = getDewpoint_C()) != NaN) {
out << "Dewpoint:" << tab << tab << d << " C" << tab << tab << tab << tab << tab;
out << rnd(getDewpoint_F(), -1) << " F" << "\n";
out << "Rel. Humidity: " << tab << tab << rnd(getRelHumidity()) << " %" << "\n";
}
}
if ((d = getPressure_hPa()) != NaN) {
out << "Pressure:" << tab << tab << rnd(d) << " hPa" << tab << tab << tab << tab;
out << rnd(getPressure_inHg(), -2) << " in. Hg" << "\n";
}
// weather phenomena
vector<string> wv = getWeather();
vector<string>::iterator weather;
for (i = 0, weather = wv.begin(); weather != wv.end(); weather++, i++) {
out << (i ? ", " : "Weather:") << tab << tab << weather->c_str();
}
if (i)
out << "\n";
// cloud layers
const char *coverage_string[5] = {
"clear skies", "few clouds", "scattered clouds", "broken clouds", "sky overcast"
};
vector<SGMetarCloud> cv = getClouds();
vector<SGMetarCloud>::iterator cloud;
for (lineno = 0, cloud = cv.begin(); cloud != cv.end(); cloud++, lineno++) {
if (lineno) out << tab << tab << tab;
else out << "Sky condition:" << tab << tab;
if ((i = cloud->getCoverage()) != -1)
out << coverage_string[i];
if ((d = cloud->getAltitude_ft()) != NaN)
out << " at " << rnd(d, 1) << " ft";
if ((s = cloud->getTypeLongString()))
out << " (" << s << ')';
if (d != NaN)
out << tab << tab << tab << rnd(cloud->getAltitude_m(), 1) << " m";
out << "\n";
}
// runways
map<string, SGMetarRunway> rm = getRunways();
map<string, SGMetarRunway>::iterator runway;
for (runway = rm.begin(); runway != rm.end(); runway++) {
lineno = 0;
if (!strcmp(runway->first.c_str(), "ALL"))
out << "All runways:" << tab << tab;
else
out << "Runway " << runway->first << ":" << tab << tab;
SGMetarRunway rwy = runway->second;
// assemble surface string
vector<string> surface;
if ((s = rwy.getDepositString()) && strlen(s))
surface.push_back(s);
if ((s = rwy.getExtentString()) && strlen(s))
surface.push_back(s);
if ((d = rwy.getDepth()) != NaN) {
sprintf(buf, "%.1lf mm", d * 1000.0);
surface.push_back(buf);
}
if ((s = rwy.getFrictionString()) && strlen(s))
surface.push_back(s);
if ((d = rwy.getFriction()) != NaN) {
sprintf(buf, "friction: %.2lf", d);
surface.push_back(buf);
}
if (! surface.empty()) {
vector<string>::iterator rwysurf = surface.begin();
for (i = 0; rwysurf != surface.end(); rwysurf++, i++) {
if (i)
out << ", ";
out << *rwysurf;
}
lineno++;
}
// assemble visibility string
SGMetarVisibility minvis = rwy.getMinVisibility();
SGMetarVisibility maxvis = rwy.getMaxVisibility();
if ((d = minvis.getVisibility_m()) != NaN) {
if (lineno++)
out << "\n" << tab << tab << tab;
out << SGMetarVisibilityManip(minvis, tab);
}
if (maxvis.getVisibility_m() != d) {
out << "\n" << tab << tab << tab << SGMetarVisibilityManip(maxvis, tab) << "\n";
lineno++;
}
if (rwy.getWindShear()) {
if (lineno++)
out << "\n" << tab << tab << tab;
out << "critical wind shear" << "\n";
}
out << "\n";
}
out << "\n";
return out.str();
}
void SGMetar::useCurrentDate()
{
struct tm now;

View File

@@ -235,6 +235,11 @@ public:
inline const std::vector<std::string>& getWeather() const { return _weather; }
inline const std::vector<struct Weather> getWeather2() const { return _weather2; }
/* Returns human-readable description. If tabtops is 0, we use tab
characters. If +ve we use spaces to pad to multiple of <tabstops>. If
-1 all sequences of tabs are represented by a single space. */
std::string getDescription(int tabstops) const;
protected:
std::string _url;
int _grpcount;

View File

@@ -44,6 +44,10 @@ SGPrecipitation::SGPrecipitation() :
void SGPrecipitation::setEnabled( bool value )
{
_enabled = value;
if (!_enabled) {
_precipitationEffect->snow(0);
_precipitationEffect->rain(0);
}
}
void SGPrecipitation::setDropletExternal( bool value )
@@ -64,6 +68,9 @@ bool SGPrecipitation::getEnabled() const
*/
osg::Group* SGPrecipitation::build(void)
{
if (!_enabled)
return nullptr;
osg::ref_ptr<osg::Group> group = new osg::Group;
_precipitationEffect->snow(0);
@@ -227,6 +234,9 @@ void SGPrecipitation::setWindProperty(double heading, double speed)
*/
bool SGPrecipitation::update(void)
{
if (!_enabled)
return false;
if (this->_freeze) {
if (this->_rain_intensity > 0) {
this->_snow_intensity = this->_rain_intensity;

View File

@@ -191,52 +191,11 @@ SGEnviro::~SGEnviro(void) {
// OSGFIXME
return;
list_of_lightning::iterator iLightning;
for( iLightning = lightnings.begin() ; iLightning != lightnings.end() ; ++iLightning ) {
delete (*iLightning);
}
lightnings.clear();
}
void SGEnviro::startOfFrame( SGVec3f p, SGVec3f up, double lon, double lat, double alt, double delta_time) {
// OSGFIXME
return;
view_in_cloud = false;
// ask the impostor cache to do some cleanup
last_cloud_turbulence = cloud_turbulence;
cloud_turbulence = 0.0;
elapsed_time += delta_time;
min_time_before_lt -= delta_time;
dt = delta_time;
#if 0
sgMat4 T1, LON, LAT;
sgVec3 axis;
sgMakeTransMat4( T1, p );
sgSetVec3( axis, 0.0, 0.0, 1.0 );
sgMakeRotMat4( LON, lon, axis );
sgSetVec3( axis, 0.0, 1.0, 0.0 );
sgMakeRotMat4( LAT, 90.0 - lat, axis );
sgMat4 TRANSFORM;
sgCopyMat4( TRANSFORM, T1 );
sgPreMultMat4( TRANSFORM, LON );
sgPreMultMat4( TRANSFORM, LAT );
sgCoord pos;
sgSetCoord( &pos, TRANSFORM );
sgMakeCoordMat4( transform, &pos );
#endif
last_lon = lon;
last_lat = lat;
last_alt = alt;
radarEcho.clear();
precipitation_max_alt = 400.0;
}
void SGEnviro::endOfFrame(void) {
@@ -291,10 +250,6 @@ void SGEnviro::set_lightning_enable_state(bool enable) {
void SGEnviro::setLight(SGVec4f adj_fog_color) {
// OSGFIXME
return;
fog_color = adj_fog_color;
if( false ) {
// ssgGetLight( 0 ) -> setColour( GL_DIFFUSE, l->scene_diffuse() );
}
}
#if 0
void SGEnviro::callback_cloud(float heading, float alt, float radius, int family, float dist, int cloudId) {
@@ -431,9 +386,6 @@ void SGEnviro::set_sampleGroup(SGSampleGroup *sgr) {
void SGEnviro::drawPrecipitation(double rain_norm, double snow_norm, double hail_norm, double pitch, double roll, double heading, double hspeed) {
// OSGFIXME
return;
if( precipitation_enable_state && rain_norm > 0.0)
if( precipitation_max_alt >= last_alt )
drawRain(pitch, roll, heading, hspeed, rain_norm);
}
@@ -471,10 +423,6 @@ void SGLightning::lt_Render(void) {
void SGEnviro::addLightning(double lon, double lat, double alt) {
// OSGFIXME
return;
if( lightnings.size() > 10)
return;
SGLightning *lt= new SGLightning(lon, lat, alt);
lightnings.push_back(lt);
}
void SGEnviro::drawLightning(void) {

View File

@@ -31,7 +31,7 @@
// Constructor
SGEphemeris::SGEphemeris( const std::string &path ) {
SGEphemeris::SGEphemeris( const SGPath& path ) {
our_sun = new Star;
moon = new MoonPos;
mercury = new Mercury;
@@ -44,7 +44,7 @@ SGEphemeris::SGEphemeris( const std::string &path ) {
nplanets = 7;
for ( int i = 0; i < nplanets; ++i )
planets[i] = SGVec3d::zeros();
stars = new SGStarData( SGPath(path) );
stars = new SGStarData(path);
}

View File

@@ -97,7 +97,7 @@ public:
* calling the constructor you need to provide a path pointing to
* your star database file.
* @param path path to your star database */
SGEphemeris( const std::string &path );
SGEphemeris( const SGPath &path );
/** Destructor */
~SGEphemeris( void );

View File

@@ -19,11 +19,13 @@
# include <simgear_config.h>
#endif
#include <mutex>
#include <simgear/compiler.h>
#include "RTIFederateFactoryRegistry.hxx"
#include "simgear/threads/SGGuard.hxx"
#include "simgear/threads/SGThread.hxx"
#include "RTIFederate.hxx"
namespace simgear {
@@ -39,7 +41,7 @@ RTIFederateFactoryRegistry::~RTIFederateFactoryRegistry()
SGSharedPtr<RTIFederate>
RTIFederateFactoryRegistry::create(const std::string& name, const std::list<std::string>& stringList) const
{
SGGuard<SGMutex> guard(_mutex);
std::lock_guard<std::mutex> guard(_mutex);
for (FederateFactoryList::const_iterator i = _federateFactoryList.begin(); i != _federateFactoryList.end(); ++i) {
SGSharedPtr<RTIFederate> federate = (*i)->create(name, stringList);
if (!federate.valid())
@@ -52,7 +54,7 @@ RTIFederateFactoryRegistry::create(const std::string& name, const std::list<std:
void
RTIFederateFactoryRegistry::registerFactory(RTIFederateFactory* factory)
{
SGGuard<SGMutex> guard(_mutex);
std::lock_guard<std::mutex> guard(_mutex);
_federateFactoryList.push_back(factory);
}

View File

@@ -33,9 +33,7 @@
#include <errno.h>
#include <map>
#include <stdexcept>
#include <boost/foreach.hpp>
#include <boost/algorithm/string/case_conv.hpp>
#include <mutex>
#include <simgear/simgear_config.h>
@@ -123,6 +121,9 @@ Client::Client() :
setUserAgent("SimGear-" SG_STRINGIZE(SIMGEAR_VERSION));
static bool didInitCurlGlobal = false;
static std::mutex initMutex;
std::lock_guard<std::mutex> g(initMutex);
if (!didInitCurlGlobal) {
curl_global_init(CURL_GLOBAL_ALL);
didInitCurlGlobal = true;
@@ -179,15 +180,15 @@ void Client::update(int waitTimeout)
&curlWriteFDs,
&curlErrorFDs,
&maxFD);
struct timeval timeout;
long t;
curl_multi_timeout(d->curlMulti, &t);
if ((t < 0) || (t > waitTimeout)) {
t = waitTimeout;
}
timeout.tv_sec = t / 1000;
timeout.tv_usec = (t % 1000) * 1000;
::select(maxFD, &curlReadFDs, &curlWriteFDs, &curlErrorFDs, &timeout);
@@ -270,9 +271,18 @@ void Client::makeRequest(const Request_ptr& r)
curl_easy_setopt(curlRequest, CURLOPT_HEADERFUNCTION, requestHeaderCallback);
curl_easy_setopt(curlRequest, CURLOPT_HEADERDATA, r.get());
#if !defined(CURL_MAX_READ_SIZE)
const int CURL_MAX_READ_SIZE = 512 * 1024;
#endif
curl_easy_setopt(curlRequest, CURLOPT_BUFFERSIZE, CURL_MAX_READ_SIZE);
curl_easy_setopt(curlRequest, CURLOPT_USERAGENT, d->userAgent.c_str());
curl_easy_setopt(curlRequest, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
if (sglog().would_log(SG_TERRASYNC, SG_DEBUG)) {
curl_easy_setopt(curlRequest, CURLOPT_VERBOSE, 1);
}
curl_easy_setopt(curlRequest, CURLOPT_FOLLOWLOCATION, 1);
if (!d->proxy.empty()) {
@@ -285,7 +295,7 @@ void Client::makeRequest(const Request_ptr& r)
}
}
std::string method = boost::to_lower_copy(r->method());
const std::string method = strutils::lowercase (r->method());
if (method == "get") {
curl_easy_setopt(curlRequest, CURLOPT_HTTPGET, 1);
} else if (method == "put") {
@@ -472,12 +482,26 @@ size_t Client::requestReadCallback(char *ptr, size_t size, size_t nmemb, void *u
return actualBytes;
}
bool isRedirectStatus(int code)
{
return ((code >= 300) && (code < 400));
}
size_t Client::requestHeaderCallback(char *rawBuffer, size_t size, size_t nitems, void *userdata)
{
size_t byteSize = size * nitems;
Request* req = static_cast<Request*>(userdata);
std::string h = strutils::simplify(std::string(rawBuffer, byteSize));
if (req->readyState() >= HTTP::Request::HEADERS_RECEIVED) {
// this can happen with chunked transfers (secondary chunks)
// or redirects
if (isRedirectStatus(req->responseCode())) {
req->responseStart(h);
return byteSize;
}
}
if (req->readyState() == HTTP::Request::OPENED) {
req->responseStart(h);
return byteSize;
@@ -504,8 +528,8 @@ size_t Client::requestHeaderCallback(char *rawBuffer, size_t size, size_t nitems
return byteSize;
}
std::string key = strutils::simplify(h.substr(0, colonPos));
std::string lkey = boost::to_lower_copy(key);
const std::string key = strutils::simplify(h.substr(0, colonPos));
const std::string lkey = strutils::lowercase (key);
std::string value = strutils::strip(h.substr(colonPos + 1));
req->responseHeader(lkey, value);

View File

@@ -34,9 +34,11 @@
#include "simgear/debug/logstream.hxx"
#include "simgear/misc/strutils.hxx"
#include <simgear/misc/sg_dir.hxx>
#include <simgear/io/HTTPClient.hxx>
#include <simgear/io/sg_file.hxx>
#include <simgear/io/untar.hxx>
#include <simgear/io/iostreams/sgstream.hxx>
#include <simgear/structure/exception.hxx>
#include <simgear/timing/timestamp.hxx>
@@ -176,7 +178,8 @@ class HTTPDirectory
enum Type
{
FileType,
DirectoryType
DirectoryType,
TarballType
};
ChildInfo(Type ty, const std::string & nameData, const std::string & hashData) :
@@ -186,7 +189,7 @@ class HTTPDirectory
{
}
ChildInfo(const ChildInfo& other) = default;
ChildInfo(const ChildInfo& other) = default;
void setSize(const std::string & sizeData)
{
@@ -311,59 +314,41 @@ public:
void updateChildrenBasedOnHash()
{
//SG_LOG(SG_TERRASYNC, SG_DEBUG, "updated children for:" << relativePath());
copyInstalledChildren();
copyInstalledChildren();
ChildInfoList toBeUpdated;
string_list toBeUpdated, orphans,
indexNames = indexChildren();
simgear::Dir d(absolutePath());
PathList fsChildren = d.children(0);
simgear::Dir d(absolutePath());
PathList fsChildren = d.children(0);
PathList orphans = d.children(0);
for (const auto& child : fsChildren) {
const auto& fileName = child.file();
if ((fileName == ".dirindex") || (fileName == ".hashes")) {
continue;
}
ChildInfoList::const_iterator it;
for (it=children.begin(); it != children.end(); ++it) {
// Check if the file exists
PathList::const_iterator p = std::find_if(fsChildren.begin(), fsChildren.end(), LocalFileMatcher(*it));
if (p == fsChildren.end()) {
// File or directory does not exist on local disk, so needs to be updated.
toBeUpdated.push_back(ChildInfo(*it));
} else if (hashForChild(*it) != it->hash) {
// File/directory exists, but hash doesn't match.
toBeUpdated.push_back(ChildInfo(*it));
orphans.erase(std::remove(orphans.begin(), orphans.end(), *p), orphans.end());
} else {
// File/Directory exists and hash is valid.
orphans.erase(std::remove(orphans.begin(), orphans.end(), *p), orphans.end());
ChildInfo info(child.isDir() ? ChildInfo::DirectoryType : ChildInfo::FileType,
fileName, "");
info.path = child;
std::string hash = hashForChild(info);
if (it->type == ChildInfo::DirectoryType) {
// If it's a directory,perform a recursive check.
HTTPDirectory* childDir = childDirectory(it->name);
childDir->updateChildrenBasedOnHash();
}
}
}
ChildInfoList::iterator c = findIndexChild(fileName);
if (c == children.end()) {
orphans.push_back(fileName);
} else if (c->hash != hash) {
#if 0
SG_LOG(SG_TERRASYNC, SG_DEBUG, "hash mismatch'" << fileName);
// file exists, but hash mismatch, schedule update
if (!hash.empty()) {
SG_LOG(SG_TERRASYNC, SG_DEBUG, "file exists but hash is wrong for:" << fileName);
SG_LOG(SG_TERRASYNC, SG_DEBUG, "on disk:" << hash << " vs in info:" << c->hash);
}
#endif
toBeUpdated.push_back(fileName);
} else {
// file exists and hash is valid. If it's a directory,
// perform a recursive check.
if (c->type == ChildInfo::DirectoryType) {
HTTPDirectory* childDir = childDirectory(fileName);
childDir->updateChildrenBasedOnHash();
}
}
// remove existing file system children from the index list,
// so we can detect new children
// https://en.wikibooks.org/wiki/More_C%2B%2B_Idioms/Erase-Remove
indexNames.erase(std::remove(indexNames.begin(), indexNames.end(), fileName), indexNames.end());
} // of real children iteration
// all remaining names in indexChilden are new children
toBeUpdated.insert(toBeUpdated.end(), indexNames.begin(), indexNames.end());
removeOrphans(orphans);
scheduleUpdates(toBeUpdated);
// We now have a list of entries that need to be updated, and a list
// of orphan files that should be removed.
removeOrphans(orphans);
scheduleUpdates(toBeUpdated);
}
HTTPDirectory* childDirectory(const std::string& name)
@@ -372,10 +357,12 @@ public:
return _repository->getOrCreateDirectory(childPath);
}
void removeOrphans(const string_list& orphans)
void removeOrphans(const PathList orphans)
{
string_list::const_iterator it;
PathList::const_iterator it;
for (it = orphans.begin(); it != orphans.end(); ++it) {
if (it->file() == ".dirindex") continue;
if (it->file() == ".hash") continue;
removeChild(*it);
}
}
@@ -391,21 +378,20 @@ public:
return r;
}
void scheduleUpdates(const string_list& names)
void scheduleUpdates(const ChildInfoList names)
{
string_list::const_iterator it;
ChildInfoList::const_iterator it;
for (it = names.begin(); it != names.end(); ++it) {
ChildInfoList::iterator cit = findIndexChild(*it);
if (cit == children.end()) {
SG_LOG(SG_TERRASYNC, SG_WARN, "scheduleUpdate, unknown child:" << *it);
continue;
}
if (cit->type == ChildInfo::FileType) {
_repository->updateFile(this, *it, cit->sizeInBytes);
if (it->type == ChildInfo::FileType) {
_repository->updateFile(this, it->name, it->sizeInBytes);
} else if (it->type == ChildInfo::DirectoryType){
HTTPDirectory* childDir = childDirectory(it->name);
_repository->updateDir(childDir, it->hash, it->sizeInBytes);
} else if (it->type == ChildInfo::TarballType) {
// Download a tarball just as a file.
_repository->updateFile(this, it->name, it->sizeInBytes);
} else {
HTTPDirectory* childDir = childDirectory(*it);
_repository->updateDir(childDir, cit->hash, cit->sizeInBytes);
SG_LOG(SG_TERRASYNC, SG_ALERT, "Coding error! Unknown Child type to schedule update");
}
}
}
@@ -430,12 +416,58 @@ public:
SG_LOG(SG_TERRASYNC, SG_WARN, "updated file but not found in dir:" << _relativePath << " " << file);
} else {
if (it->hash != hash) {
// we don't erase the file on a hash mismatch, becuase if we're syncing during the
SG_LOG(SG_TERRASYNC, SG_INFO, "Checksum error for " << absolutePath() << "/" << file << " " << it->hash << " " << hash);
// we don't erase the file on a hash mismatch, because if we're syncing during the
// middle of a server-side update, the downloaded file may actually become valid.
_repository->failedToUpdateChild(_relativePath, HTTPRepository::REPO_ERROR_CHECKSUM);
} else {
_repository->updatedFileContents(it->path, hash);
_repository->totalDownloaded += sz;
SGPath p = SGPath(absolutePath(), file);
if ((p.extension() == "tgz") || (p.extension() == "zip")) {
// We require that any compressed files have the same filename as the file or directory
// they expand to, so we can remove the old file/directory before extracting the new
// data.
SGPath removePath = SGPath(p.base());
bool pathAvailable = true;
if (removePath.exists()) {
if (removePath.isDir()) {
simgear::Dir pd(removePath);
pathAvailable = pd.removeChildren();
} else {
pathAvailable = removePath.remove();
}
}
if (pathAvailable) {
// If this is a tarball, then extract it.
SGBinaryFile f(p);
if (! f.open(SG_IO_IN)) SG_LOG(SG_TERRASYNC, SG_ALERT, "Unable to open " << p << " to extract");
SG_LOG(SG_TERRASYNC, SG_INFO, "Extracting " << absolutePath() << "/" << file << " to " << p.dir());
SGPath extractDir = p.dir();
ArchiveExtractor ex(extractDir);
uint8_t* buf = (uint8_t*) alloca(128);
while (!f.eof()) {
size_t bufSize = f.read((char*) buf, 128);
ex.extractBytes(buf, bufSize);
}
ex.flush();
if (! ex.isAtEndOfArchive()) {
SG_LOG(SG_TERRASYNC, SG_ALERT, "Corrupt tarball " << p);
}
if (ex.hasError()) {
SG_LOG(SG_TERRASYNC, SG_ALERT, "Error extracting " << p);
}
} else {
SG_LOG(SG_TERRASYNC, SG_ALERT, "Unable to remove old file/directory " << removePath);
} // of pathAvailable
} // of handling tgz files
} // of hash matches
} // of found in child list
}
@@ -458,6 +490,16 @@ private:
{ return info.name == name; }
};
struct LocalFileMatcher
{
LocalFileMatcher(const ChildInfo ci) : childInfo(ci) {}
ChildInfo childInfo;
bool operator()(const SGPath path) const {
return path.file() == childInfo.name;
}
};
ChildInfoList::iterator findIndexChild(const std::string& name)
{
return std::find_if(children.begin(), children.end(), ChildWithName(name));
@@ -519,8 +561,8 @@ private:
continue;
}
if (typeData != "f" && typeData != "d" ) {
SG_LOG(SG_TERRASYNC, SG_WARN, "malformed .dirindex file: invalid type in line '" << line << "', expected 'd' or 'f', (ignoring line)"
if (typeData != "f" && typeData != "d" && typeData != "t" ) {
SG_LOG(SG_TERRASYNC, SG_WARN, "malformed .dirindex file: invalid type in line '" << line << "', expected 't', 'd' or 'f', (ignoring line)"
<< "\n\tparsing:" << p.utf8Str());
continue;
}
@@ -533,8 +575,12 @@ private:
continue;
}
children.emplace_back(ChildInfo(typeData == "f" ? ChildInfo::FileType : ChildInfo::DirectoryType, tokens[1], tokens[2]));
children.back().path = absolutePath() / tokens[1];
ChildInfo ci = ChildInfo(ChildInfo::FileType, tokens[1], tokens[2]);
if (typeData == "d") ci.type = ChildInfo::DirectoryType;
if (typeData == "t") ci.type = ChildInfo::TarballType;
children.emplace_back(ci);
children.back().path = absolutePath() / tokens[1];
if (tokens.size() > 3) {
children.back().setSize(tokens[3]);
}
@@ -543,40 +589,36 @@ private:
return true;
}
void removeChild(const std::string& name)
void removeChild(SGPath path)
{
SGPath p(absolutePath());
p.append(name);
bool ok;
SG_LOG(SG_TERRASYNC, SG_WARN, "Removing:" << path);
std::string fpath = _relativePath + "/" + name;
if (p.isDir()) {
ok = _repository->deleteDirectory(fpath, p);
std::string fpath = _relativePath + "/" + path.file();
if (path.isDir()) {
ok = _repository->deleteDirectory(fpath, path);
} else {
// remove the hash cache entry
_repository->updatedFileContents(p, std::string());
ok = p.remove();
_repository->updatedFileContents(path, std::string());
ok = path.remove();
}
if (!ok) {
SG_LOG(SG_TERRASYNC, SG_WARN, "removal failed for:" << p);
throw sg_io_exception("Failed to remove existing file/dir:", p);
SG_LOG(SG_TERRASYNC, SG_WARN, "removal failed for:" << path);
throw sg_io_exception("Failed to remove existing file/dir:", path);
}
}
std::string hashForChild(const ChildInfo& child) const
{
SGPath p(child.path);
if (child.type == ChildInfo::DirectoryType) {
p.append(".dirindex");
}
SGPath p(child.path);
if (child.type == ChildInfo::DirectoryType) p.append(".dirindex");
if (child.type == ChildInfo::TarballType) p.concat(".tgz"); // For tarballs the hash is against the tarball file itself
return _repository->hashForPath(p);
}
HTTPRepoPrivate* _repository;
std::string _relativePath; // in URL and file-system space
};
HTTPRepository::HTTPRepository(const SGPath& base, HTTP::Client *cl) :

View File

@@ -18,6 +18,9 @@
#include <simgear_config.h>
#include "HTTPRequest.hxx"
#include <cstring>
#include <algorithm> // for std::min
#include <simgear/compiler.h>
#include <simgear/debug/logstream.hxx>
#include <simgear/misc/strutils.hxx>
@@ -328,6 +331,16 @@ unsigned int Request::responseLength() const
return _responseLength;
}
//------------------------------------------------------------------------------
void Request::setSuccess(int code)
{
_responseStatus = code;
_responseReason.clear();
if( !isComplete() ) {
setReadyState(DONE);
}
}
//------------------------------------------------------------------------------
void Request::setFailure(int code, const std::string& reason)
{

View File

@@ -28,8 +28,6 @@
#include <simgear/structure/SGSharedPtr.hxx>
#include <simgear/math/sg_types.hxx>
#include <boost/bind.hpp>
class SGPropertyNode;
namespace simgear
@@ -46,7 +44,7 @@ class Request:
public SGReferenced
{
public:
typedef boost::function<void(Request*)> Callback;
typedef std::function<void(Request*)> Callback;
enum ReadyState
{
@@ -82,7 +80,7 @@ public:
template<class C>
Request* done(C* instance, void (C::*mem_func)(Request*))
{
return done(boost::bind(mem_func, instance, _1));
return done(std::bind(mem_func, instance, std::placeholders::_1));
}
/**
@@ -97,7 +95,7 @@ public:
template<class C>
Request* fail(C* instance, void (C::*mem_func)(Request*))
{
return fail(boost::bind(mem_func, instance, _1));
return fail(std::bind(mem_func, instance, std::placeholders::_1));
}
/**
@@ -112,7 +110,7 @@ public:
template<class C>
Request* always(C* instance, void (C::*mem_func)(Request*))
{
return always(boost::bind(mem_func, instance, _1));
return always(std::bind(mem_func, instance, std::placeholders::_1));
}
/**
@@ -224,7 +222,7 @@ protected:
virtual void onAlways();
void setFailure(int code, const std::string& reason);
void setSuccess(int code);
private:
friend class Client;
friend class Connection;

View File

@@ -5,8 +5,6 @@
#include <signal.h>
#include <iostream>
#include <boost/foreach.hpp>
#include <simgear/io/sg_file.hxx>
#include <simgear/io/HTTPClient.hxx>
@@ -29,17 +27,17 @@ public:
_complete(false),
_file(NULL)
{
}
void setFile(SGFile* f)
{
_file = f;
}
bool complete() const
{ return _complete; }
void addHeader(const string& h)
{
int colonPos = h.find(':');
@@ -47,22 +45,22 @@ public:
cerr << "malformed header: " << h << endl;
return;
}
string key = h.substr(0, colonPos);
requestHeader(key) = h.substr(colonPos + 1);
}
protected:
virtual void onDone()
{
_complete = true;
}
}
virtual void gotBodyData(const char* s, int n)
{
_file->write(s, n);
}
private:
private:
bool _complete;
SGFile* _file;
};
@@ -74,7 +72,7 @@ int main(int argc, char* argv[])
string proxy, proxyAuth;
string_list headers;
string url;
for (int a=0; a<argc;++a) {
if (argv[a][0] == '-') {
if (!strcmp(argv[a], "--user-agent")) {
@@ -105,7 +103,7 @@ int main(int argc, char* argv[])
proxyHost = proxy.substr(0, colonPos);
proxyPort = strutils::to_int(proxy.substr(colonPos + 1));
}
cl.setProxy(proxyHost, proxyPort, proxyAuth);
}
@@ -123,23 +121,23 @@ int main(int argc, char* argv[])
}
ARequest* req = new ARequest(url);
BOOST_FOREACH(string h, headers) {
for (const auto& h : headers) {
req->addHeader(h);
}
req->setFile(outFile);
cl.makeRequest(req);
while (!req->complete()) {
cl.update();
SGTimeStamp::sleepForMSec(100);
}
if (req->responseCode() != 200) {
cerr << "got response:" << req->responseCode() << endl;
cerr << "\treason:" << req->responseReason() << endl;
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}

View File

@@ -71,10 +71,10 @@ static const uint32_t EndianMagic = 0x11223344;
* gzContainerWriter
**************************************************************************/
gzContainerWriter::gzContainerWriter(const std::string& name,
gzContainerWriter::gzContainerWriter(const SGPath& name,
const std::string& fileMagic) :
sg_gzofstream(name, ios_out | ios_binary),
filename(name)
filename(name.utf8Str())
{
/* write byte-order marker **************************************/
write((char*)&EndianMagic, sizeof(EndianMagic));
@@ -138,10 +138,10 @@ gzContainerWriter::writeContainer(ContainerType Type, SGPropertyNode* root)
* gzContainerReader
**************************************************************************/
gzContainerReader::gzContainerReader(const std::string& name,
gzContainerReader::gzContainerReader(const SGPath& name,
const std::string& fileMagic) :
sg_gzifstream(SGPath(name), ios_in | ios_binary),
filename(name)
filename(name.utf8Str())
{
bool ok = (good() && !eof());

View File

@@ -35,7 +35,7 @@ typedef int ContainerType;
class gzContainerReader : public sg_gzifstream
{
public:
gzContainerReader( const std::string& name,
gzContainerReader( const SGPath& name,
const std::string& fileMagic);
bool readContainerHeader(ContainerType* pType, size_t* pSize);
@@ -48,7 +48,7 @@ private:
class gzContainerWriter : public sg_gzofstream
{
public:
gzContainerWriter( const std::string& name,
gzContainerWriter( const SGPath& name,
const std::string& fileMagic);
bool writeContainerHeader(ContainerType Type, size_t Size);

22
simgear/io/iostreams/sgstream.cxx Normal file → Executable file
View File

@@ -212,7 +212,7 @@ sg_ifstream::sg_ifstream(const SGPath& path, ios_openmode io_mode)
#if defined(SG_WINDOWS)
std::wstring ps = path.wstr();
#else
std::string ps = path.local8BitStr();
std::string ps = path.utf8Str();
#endif
std::ifstream::open(ps.c_str(), io_mode);
}
@@ -222,17 +222,31 @@ void sg_ifstream::open( const SGPath& name, ios_openmode io_mode )
#if defined(SG_WINDOWS)
std::wstring ps = name.wstr();
#else
std::string ps = name.local8BitStr();
std::string ps = name.utf8Str();
#endif
std::ifstream::open(ps.c_str(), io_mode);
}
std::string sg_ifstream::read_all()
{
this->seekg(0, std::ios::end); // seek to end
const auto pos = this->tellg();
std::string result;
result.resize(pos);
this->seekg(0, std::ios::beg);
this->read(const_cast<char*>(result.data()), pos);
return result;
}
sg_ofstream::sg_ofstream(const SGPath& path, ios_openmode io_mode)
{
#if defined(SG_WINDOWS)
std::wstring ps = path.wstr();
#else
std::string ps = path.local8BitStr();
std::string ps = path.utf8Str();
#endif
std::ofstream::open(ps.c_str(), io_mode);
}
@@ -242,7 +256,7 @@ void sg_ofstream::open( const SGPath& name, ios_openmode io_mode )
#if defined(SG_WINDOWS)
std::wstring ps = name.wstr();
#else
std::string ps = name.local8BitStr();
std::string ps = name.utf8Str();
#endif
std::ofstream::open(ps.c_str(), io_mode);
}

View File

@@ -196,6 +196,10 @@ public:
void open( const SGPath& name,
ios_openmode io_mode = ios_in|ios_binary );
/// read the entire stream into a buffer. Use on files, etc - not recommended on streams
/// which never EOF, will bvlock forever.
std::string read_all();
};
class sg_ofstream : public std::ofstream

View File

@@ -431,7 +431,7 @@ void test_ZlibDecompressorIStream_readPutbackEtc()
try {
// 'Z' is not the last character read from the stream
decompressor.putback('Z');
} catch (std::ios_base::failure) {
} catch (const std::ios_base::failure&) {
gotException = true;
} catch (const std::exception& e) {
// gcc fails to catch std::ios_base::failure due to an inconsistent C++11

View File

@@ -36,6 +36,7 @@
#include <cstring>
#include <cassert>
#include <cstdio> // for snprintf
#include <mutex>
#include <errno.h>
#if defined(WINSOCK)
@@ -213,7 +214,7 @@ private:
return ok;
}
SGMutex _lock;
std::mutex _lock;
SGWaitCondition _wait;
typedef std::map<string, simgear::IPAddress*> AddressCache;

View File

@@ -160,7 +160,7 @@ public:
}
}
float readInt()
int32_t readInt()
{
unsigned int* p = reinterpret_cast<unsigned int*>(ptr + offset);
if ( sgIsBigEndian() ) {

View File

@@ -34,18 +34,15 @@
#include <simgear/math/sg_types.hxx>
#include <simgear/math/SGMath.hxx>
#include <vector>
#include <array>
#include <string>
#include <boost/array.hpp>
#include <vector>
#define MAX_TC_SETS (4)
#define MAX_VAS (8)
// I really want to pass around fixed length arrays, as the size
// has to be hardcoded
// but it's a C++0x feature use boost in its absence
typedef boost::array<int_list, MAX_TC_SETS> tci_list;
typedef boost::array<int_list, MAX_VAS> vai_list;
typedef std::array<int_list, MAX_TC_SETS> tci_list;
typedef std::array<int_list, MAX_VAS> vai_list;
/** STL Structure used to store (integer index) object information */
typedef std::vector < int_list > group_list;

View File

@@ -64,6 +64,31 @@ SGFile::SGFile( int existingFd ) :
SGFile::~SGFile() {
}
#include <simgear/misc/sg_hash.hxx>
#include <simgear/structure/exception.hxx>
#include "simgear/misc/strutils.hxx"
std::string SGFile::computeHash()
{
if (!file_name.exists())
return std::string();
simgear::sha1nfo info;
sha1_init(&info);
char* buf = static_cast<char*>(malloc(1024 * 1024));
size_t readLen;
SGBinaryFile f(file_name);
if (!f.open(SG_IO_IN)) {
throw sg_io_exception("Couldn't open file for compute hash", file_name);
}
while ((readLen = f.read(buf, 1024 * 1024)) > 0) {
sha1_write(&info, buf, readLen);
}
f.close();
free(buf);
std::string hashBytes((char*)sha1_result(&info), HASH_LENGTH);
return simgear::strutils::encodeHex(hashBytes);
}
// open the file based on specified direction
bool SGFile::open( const SGProtocolDir d ) {

View File

@@ -87,6 +87,9 @@ public:
/** @return true of eof conditions exists */
virtual bool eof() const { return eof_flag; };
std::string computeHash();
};
class SGBinaryFile : public SGFile {

View File

@@ -7,8 +7,6 @@
#include <thread>
#include <atomic>
#include <boost/algorithm/string/case_conv.hpp>
#include <simgear/simgear_config.h>
#include "DNSClient.hxx"

View File

@@ -1,12 +1,10 @@
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <map>
#include <sstream>
#include <cerrno>
#include <boost/algorithm/string/case_conv.hpp>
#include <simgear/simgear_config.h>
#include "HTTPClient.hxx"
@@ -61,23 +59,23 @@ public:
std::map<string, string> headers;
protected:
virtual void onDone()
void onDone() override
{
complete = true;
}
virtual void onFail()
void onFail() override
{
failed = true;
}
virtual void gotBodyData(const char* s, int n)
void gotBodyData(const char* s, int n) override
{
//std::cout << "got body data:'" << string(s, n) << "'" <<std::endl;
// std::cout << "got body data:'" << string(s, n) << "'" <<std::endl;
bodyData += string(s, n);
}
virtual void responseHeader(const string& header, const string& value)
void responseHeader(const string& header, const string& value) override
{
Request::responseHeader(header, value);
headers[header] = value;
@@ -273,7 +271,23 @@ public:
d << "\r\n"; // final CRLF to terminate the headers
d << contentStr;
push(d.str().c_str());
} else if (path == "/test_redirect") {
string contentStr("<html>See <a href=\"wibble\">Here</a></html>");
stringstream d;
d << "HTTP/1.1 " << 302 << " " << "Found" << "\r\n";
d << "Location:" << " http://localhost:2000/was_redirected" << "\r\n";
d << "Content-Length:" << contentStr.size() << "\r\n";
d << "\r\n"; // final CRLF to terminate the headers
d << contentStr;
push(d.str().c_str());
} else if (path == "/was_redirected") {
string contentStr(BODY1);
stringstream d;
d << "HTTP/1.1 " << 200 << " " << reasonForCode(200) << "\r\n";
d << "Content-Length:" << contentStr.size() << "\r\n";
d << "\r\n"; // final CRLF to terminate the headers
d << contentStr;
push(d.str().c_str());
} else {
TestServerChannel::processRequestHeaders();
}
@@ -365,6 +379,26 @@ void waitForFailed(HTTP::Client* cl, TestRequest* tr)
cerr << "timed out waiting for failure" << endl;
}
using CompletionCheck = std::function<bool()>;
bool waitFor(HTTP::Client* cl, CompletionCheck ccheck)
{
SGTimeStamp start(SGTimeStamp::now());
while (start.elapsedMSec() < 10000) {
cl->update();
testServer.poll();
if (ccheck()) {
return true;
}
SGTimeStamp::sleepForMSec(15);
}
cerr << "timed out" << endl;
return false;
}
int main(int argc, char* argv[])
{
sglog().setLogLevels( SG_ALL, SG_INFO );
@@ -440,6 +474,8 @@ int main(int argc, char* argv[])
// larger get request
for (unsigned int i=0; i<body2Size; ++i) {
// this contains embeded 0s on purpose, i.e it's
// not text data but binary
body2[i] = (i << 4) | (i >> 2);
}
@@ -584,9 +620,10 @@ cout << "testing proxy close" << endl;
HTTP::Request_ptr own3(tr3);
cl.makeRequest(tr3);
waitForComplete(&cl, tr3);
SG_VERIFY(tr->complete);
SG_VERIFY(tr2->complete);
SG_VERIFY(waitFor(&cl, [tr, tr2, tr3]() {
return tr->complete && tr2->complete &&tr3->complete;
}));
SG_CHECK_EQUAL(tr->bodyData, string(BODY1));
SG_CHECK_EQUAL(tr2->responseLength(), strlen(BODY3));
@@ -615,9 +652,9 @@ cout << "testing proxy close" << endl;
HTTP::Request_ptr own3(tr3);
cl.makeRequest(tr3);
waitForComplete(&cl, tr3);
SG_VERIFY(tr->complete);
SG_VERIFY(tr2->complete);
SG_VERIFY(waitFor(&cl, [tr, tr2, tr3]() {
return tr->complete && tr2->complete &&tr3->complete;
}));
SG_CHECK_EQUAL(tr->responseLength(), strlen(BODY1));
SG_CHECK_EQUAL(tr->responseBytesReceived(), strlen(BODY1));
@@ -744,8 +781,15 @@ cout << "testing proxy close" << endl;
SG_CHECK_EQUAL(tr3->bodyData, string(BODY1));
}
// disabling this test for now, since it seems to have changed depending
// on the libCurl version. (Or some other configuration which is currently
// not apparent).
// old behaviour: Curl sends the second request soon after makeRequest
// new behaviour: Curl waits for the first request to complete, before
// sending the second request (i.e acts as if HTTP pipelining is disabled)
#if 0
{
cout << "get-during-response-send" << endl;
cout << "get-during-response-send\n\n" << endl;
cl.clearAllConnections();
//test_get_during_send
@@ -765,7 +809,10 @@ cout << "testing proxy close" << endl;
HTTP::Request_ptr own2(tr2);
cl.makeRequest(tr2);
waitForComplete(&cl, tr2);
SG_VERIFY(waitFor(&cl, [tr, tr2]() {
return tr->isComplete() && tr2->isComplete();
}));
SG_CHECK_EQUAL(tr->responseCode(), 200);
SG_CHECK_EQUAL(tr->bodyData, string(BODY3));
SG_CHECK_EQUAL(tr->responseBytesReceived(), strlen(BODY3));
@@ -773,6 +820,25 @@ cout << "testing proxy close" << endl;
SG_CHECK_EQUAL(tr2->bodyData, string(BODY1));
SG_CHECK_EQUAL(tr2->responseBytesReceived(), strlen(BODY1));
}
#endif
{
cout << "redirect test" << endl;
// redirect test
testServer.disconnectAll();
cl.clearAllConnections();
TestRequest* tr = new TestRequest("http://localhost:2000/test_redirect");
HTTP::Request_ptr own(tr);
cl.makeRequest(tr);
waitForComplete(&cl, tr);
SG_CHECK_EQUAL(tr->responseCode(), 200);
SG_CHECK_EQUAL(tr->responseReason(), string("OK"));
SG_CHECK_EQUAL(tr->responseLength(), strlen(BODY1));
SG_CHECK_EQUAL(tr->responseBytesReceived(), strlen(BODY1));
SG_CHECK_EQUAL(tr->bodyData, string(BODY1));
}
cout << "all tests passed ok" << endl;
return EXIT_SUCCESS;

View File

@@ -1,6 +1,7 @@
#ifndef SIMGEAR_IO_TEST_HTTP_HXX
#define SIMGEAR_IO_TEST_HTTP_HXX
#include <algorithm>
#include <sstream>
#include <vector>
@@ -30,7 +31,6 @@ public:
virtual ~TestServerChannel()
{
std::cerr << "dtor test server channel" << std::endl;
}
virtual void collectIncomingData(const char* s, int n)
@@ -139,8 +139,8 @@ public:
void sendErrorResponse(int code, bool close, std::string content)
{
std::cerr << "sending error " << code << " for " << path << std::endl;
std::cerr << "\tcontent:" << content << std::endl;
// std::cerr << "sending error " << code << " for " << path << std::endl;
// std::cerr << "\tcontent:" << content << std::endl;
std::stringstream headerData;
headerData << "HTTP/1.1 " << code << " " << reasonForCode(code) << "\r\n";
@@ -168,7 +168,6 @@ public:
virtual void handleClose (void)
{
std::cerr << "channel close" << std::endl;
NetBufferChannel::handleClose();
}

View File

@@ -1,3 +1,4 @@
#include <cassert>
#include <cstdlib>
#include <iostream>
#include <map>
@@ -5,8 +6,6 @@
#include <errno.h>
#include <fcntl.h>
#include <boost/algorithm/string/case_conv.hpp>
#include <simgear/simgear_config.h>
#include "test_HTTP.hxx"

View File

@@ -5,8 +5,6 @@
#include <sstream>
#include <errno.h>
#include <boost/algorithm/string/case_conv.hpp>
#include <simgear/simgear_config.h>
#include "HTTPClient.hxx"

View File

@@ -48,6 +48,8 @@ namespace simgear
assert(outer);
}
virtual ~ArchiveExtractorPrivate() = default;
typedef enum {
INVALID = 0,
READING_HEADER,
@@ -338,7 +340,7 @@ public:
}
if (!isSafePath(tarPath)) {
SG_LOG(SG_IO, SG_WARN, "bad tar path:" << tarPath);
SG_LOG(SG_IO, SG_WARN, "unsafe tar path, skipping::" << tarPath);
skipCurrentEntry = true;
}
@@ -504,7 +506,7 @@ public:
#endif
unzFile zip = unzOpen2(bufferName, &memoryAccessFuncs);
const size_t BUFFER_SIZE = 32 * 1024;
const size_t BUFFER_SIZE = 1024 * 1024;
void* buf = malloc(BUFFER_SIZE);
try {
@@ -530,24 +532,28 @@ public:
state = END_OF_ARCHIVE;
}
catch (sg_exception&) {
state = BAD_ARCHIVE;
}
free(buf);
unzClose(zip);
}
void extractCurrentFile(unzFile zip, char* buffer, size_t bufferSize)
{
unz_file_info fileInfo;
unzGetCurrentFileInfo(zip, &fileInfo,
buffer, bufferSize,
NULL, 0, /* extra field */
NULL, 0 /* comment field */);
state = BAD_ARCHIVE;
}
free(buf);
unzClose(zip);
}
void extractCurrentFile(unzFile zip, char* buffer, size_t bufferSize)
{
unz_file_info fileInfo;
int result = unzGetCurrentFileInfo(zip, &fileInfo,
buffer, bufferSize,
NULL, 0, /* extra field */
NULL, 0 /* comment field */);
if (result != Z_OK) {
throw sg_io_exception("Failed to get zip current file info");
}
std::string name(buffer);
if (!isSafePath(name)) {
throw sg_format_exception("Bad zip path", name);
SG_LOG(SG_IO, SG_WARN, "unsafe zip path, skipping::" << name);
return;
}
auto filterResult = filterPath(name);
@@ -566,7 +572,7 @@ public:
return;
}
int result = unzOpenCurrentFile(zip);
result = unzOpenCurrentFile(zip);
if (result != UNZ_OK) {
throw sg_io_exception("opening current zip file failed", sg_location(name));
}
@@ -614,10 +620,7 @@ ArchiveExtractor::ArchiveExtractor(const SGPath& rootPath) :
{
}
ArchiveExtractor::~ArchiveExtractor()
{
}
ArchiveExtractor::~ArchiveExtractor() = default;
void ArchiveExtractor::extractBytes(const uint8_t* bytes, size_t count)
{
@@ -635,7 +638,7 @@ void ArchiveExtractor::extractBytes(const uint8_t* bytes, size_t count)
d.reset(new ZipExtractorPrivate(this));
}
else {
SG_LOG(SG_IO, SG_ALERT, "Invcalid archive type");
SG_LOG(SG_IO, SG_WARN, "Invalid archive type");
_invalidDataType = true;
return;
}
@@ -725,8 +728,10 @@ ArchiveExtractor::DetermineResult ArchiveExtractor::isTarData(const uint8_t* byt
}
int result = inflate(&z, Z_SYNC_FLUSH);
if (result != Z_OK) {
SG_LOG(SG_IO, SG_WARN, "inflate failed:" << result);
if ((result == Z_OK) || (result == Z_STREAM_END)) {
// all good
} else {
SG_LOG(SG_IO, SG_WARN, "isTarData: Zlib inflate failed:" << result);
inflateEnd(&z);
return Invalid; // not tar data
}

View File

@@ -34,7 +34,7 @@ class ArchiveExtractor
{
public:
ArchiveExtractor(const SGPath& rootPath);
~ArchiveExtractor();
virtual ~ArchiveExtractor();
enum DetermineResult
{

View File

@@ -193,8 +193,8 @@ min(S s, SGVec2<T> v)
template<typename T>
inline
SGVec2<T>
max(const SGVec2<T>& v1, const SGVec2<T>& v2)
{ v1 = simd4::max(v1.simd2(), v2.simd2()); return v1; }
max(SGVec2<T> v1, const SGVec2<T>& v2)
{ v1.simd2() = simd4::max(v1.simd2(), v2.simd2()); return v1; }
template<typename S, typename T>
inline
SGVec2<T>
@@ -375,6 +375,29 @@ interpolate(T tau, const SGVec2<T>& v1, const SGVec2<T>& v2)
return r;
}
// Helper function for point_in_triangle
template <typename T>
inline
T
pt_determine(const SGVec2<T>& pt1, const SGVec2<T>& pt2, const SGVec2<T>& pt3)
{
return (pt1.x()-pt3.x()) * (pt2.y()-pt3.y()) - (pt2.x() - pt3.x()) * (pt1.y() - pt3.y());
}
// Is testpt inside the triangle formed by the other three points?
template <typename T>
inline
bool
point_in_triangle(const SGVec2<T>& testpt, const SGVec2<T>& pt1, const SGVec2<T>& pt2, const SGVec2<T>& pt3)
{
T d1 = pt_determine(testpt,pt1,pt2);
T d2 = pt_determine(testpt,pt2,pt3);
T d3 = pt_determine(testpt,pt3,pt1);
bool has_neg = (d1 < 0) || (d2 < 0) || (d3 < 0);
bool has_pos = (d1 > 0) || (d2 > 0) || (d3 > 0);
return !(has_neg && has_pos);
}
#ifndef NDEBUG
template<typename T>
inline

View File

@@ -56,8 +56,6 @@ SGInterpTable::SGInterpTable(const SGPropertyNode* interpolation)
// file
SGInterpTable::SGInterpTable( const std::string& file )
{
SG_LOG( SG_MATH, SG_INFO, "Initializing Interpolator for " << file );
sg_gzifstream in( SGPath::fromUtf8(file) );
if ( !in.is_open() ) {
SG_LOG( SG_GENERAL, SG_ALERT, "Cannot open file: " << file );
@@ -78,8 +76,6 @@ SGInterpTable::SGInterpTable( const std::string& file )
// file
SGInterpTable::SGInterpTable( const SGPath& file )
{
SG_LOG( SG_MATH, SG_INFO, "Initializing Interpolator for " << file );
sg_gzifstream in( file );
if ( !in.is_open() ) {
SG_LOG( SG_GENERAL, SG_ALERT, "Cannot open file: " << file );

View File

@@ -19,6 +19,8 @@ set(HEADERS
tabbed_values.hxx
texcoord.hxx
test_macros.hxx
lru_cache.hxx
simgear_optional.hxx
)
set(SOURCES

View File

@@ -18,8 +18,6 @@
#include "CSSBorder.hxx"
#include <boost/lexical_cast.hpp>
#include <boost/range.hpp>
#include <boost/tokenizer.hpp>
namespace simgear
@@ -114,9 +112,9 @@ namespace simgear
std::max
(
0.f,
boost::lexical_cast<float>
std::stof
(
rel ? boost::make_iterator_range(tok->begin(), tok->end() - 1)
rel ? std::string(tok->begin(), tok->end() - 1)
: *tok
)
/

View File

@@ -21,7 +21,6 @@
#define SG_LISTDIFF_HXX_
#include <vector>
#include <boost/function.hpp>
namespace simgear
{
@@ -30,7 +29,7 @@ namespace simgear
struct ListDiff
{
typedef std::vector<T> List;
typedef boost::function<void (T)> Callback;
typedef std::function<void (T)> Callback;
/**
* Perform list diff in-place (modifies both lists) and call cb_add for

View File

@@ -30,11 +30,16 @@
// code can register another one immediately without worrying about
// timer aliasing.
class SGInterpolator : public SGSubsystem {
class SGInterpolator : public SGSubsystem
{
public:
SGInterpolator() { _list = 0; }
virtual void init() {}
virtual void update(double delta_time_sec);
// Subsystem API.
void update(double delta_time_sec) override;
// Subsystem identification.
static const char* staticSubsystemClassId() { return "interpolator"; }
// Simple method that interpolates a double property value from
// its current value (default of zero) to the specified target

173
simgear/misc/lru_cache.hxx Normal file
View File

@@ -0,0 +1,173 @@
///@file
/// Compare lists and get differences
//---------------------------------------------------------------------------//
// Copyright (c) 2013 Kyle Lutz <kyle.r.lutz@gmail.com>
//
// Distributed under the Boost Software License, Version 1.0
// See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt
//
// See http://boostorg.github.com/compute for more information.
//---------------------------------------------------------------------------//
///
// Changes Copyright (C) 2019 Richard Harrison (rjh@zaretto.com)
//
// As the boost licence is lax and permissive see
// (https://www.gnu.org/licenses/license-list.en.html#boost)
// any changes to this module are covered under the GPL
//
// 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 LRU_CACHE_HXX_
#define LRU_CACHE_HXX_
#include <map>
#include <list>
#include <utility>
#include <mutex>
#include <boost/optional.hpp>
#include <simgear/threads/SGThread.hxx>
namespace simgear
{
// a cache which evicts the least recently used item when it is full
template<class Key, class Value>
class lru_cache
{
public:
std::mutex _mutex;
typedef Key key_type;
typedef Value value_type;
typedef std::list<key_type> list_type;
typedef std::map<
key_type,
std::pair<value_type, typename list_type::iterator>
> map_type;
lru_cache(size_t capacity)
: m_capacity(capacity)
{
}
~lru_cache()
{
}
size_t size() const
{
return m_map.size();
}
size_t capacity() const
{
return m_capacity;
}
bool empty() const
{
return m_map.empty();
}
bool contains(const key_type &key)
{
std::lock_guard<std::mutex> scopeLock(_mutex);
return m_map.find(key) != m_map.end();
}
void insert(const key_type &key, const value_type &value)
{
std::lock_guard<std::mutex> scopeLock(_mutex);
typename map_type::iterator i = m_map.find(key);
if (i == m_map.end()) {
// insert item into the cache, but first check if it is full
if (size() >= m_capacity) {
// cache is full, evict the least recently used item
evict();
}
// insert the new item
m_list.push_front(key);
m_map[key] = std::make_pair(value, m_list.begin());
}
}
boost::optional<key_type> findValue(const std::string &requiredValue)
{
std::lock_guard<std::mutex> scopeLock(_mutex);
for (typename map_type::iterator it = m_map.begin(); it != m_map.end(); ++it)
if (it->second.first == requiredValue)
return it->first;
return boost::none;
}
boost::optional<value_type> get(const key_type &key)
{
std::lock_guard<std::mutex> scopeLock(_mutex);
// lookup value in the cache
typename map_type::iterator i = m_map.find(key);
if (i == m_map.end()) {
// value not in cache
return boost::none;
}
// return the value, but first update its place in the most
// recently used list
typename list_type::iterator j = i->second.second;
if (j != m_list.begin()) {
// move item to the front of the most recently used list
m_list.erase(j);
m_list.push_front(key);
// update iterator in map
j = m_list.begin();
const value_type &value = i->second.first;
m_map[key] = std::make_pair(value, j);
// return the value
return value;
}
else {
// the item is already at the front of the most recently
// used list so just return it
return i->second.first;
}
}
void clear()
{
std::lock_guard<std::mutex> scopeLock(_mutex);
m_map.clear();
m_list.clear();
}
private:
void evict()
{
std::lock_guard<std::mutex> scopeLock(_mutex);
// evict item from the end of most recently used list
typename list_type::iterator i = --m_list.end();
m_map.erase(*i);
m_list.erase(i);
}
private:
map_type m_map;
list_type m_list;
size_t m_capacity;
};
} // namespace simgear
#endif /* SG_LISTDIFF_HXX_ */

7
simgear/misc/path_test.cxx Normal file → Executable file
View File

@@ -333,6 +333,12 @@ void test_hash_function()
SG_CHECK_NE(std::hash<SGPath>{}(p), std::hash<SGPath>{}(p / "foobar"));
}
void test_null_path()
{
const SGPath nullPath;
SG_VERIFY(!nullPath.exists());
}
int main(int argc, char* argv[])
{
SGPath pa;
@@ -441,6 +447,7 @@ int main(int argc, char* argv[])
test_update_dir();
test_comparisons();
test_hash_function();
test_null_path();
cout << "all tests passed OK" << endl;
return 0; // passed

View File

@@ -41,7 +41,6 @@
#include <simgear/misc/strutils.hxx>
#include <simgear/debug/logstream.hxx>
#include <boost/foreach.hpp>
#include <cstring>
#include <cstdlib>
@@ -115,14 +114,14 @@ Dir Dir::tempDir(const std::string& templ)
// Mac OS-X / BSD manual says any number of 'X's, but GLibc manual
// says exactly six, so that's what I'm going with
p.concat("-XXXXXX");
std::string s = p.local8BitStr();
std::string s = p.utf8Str();
::snprintf(buf, 1024, "%s", s.c_str());
if (!mkdtemp(buf)) {
SG_LOG(SG_IO, SG_WARN,
"mkdtemp failed: " << simgear::strutils::error_string(errno));
return Dir();
}
return Dir(SGPath(buf));
#else
#if defined(SG_WINDOWS)
@@ -138,7 +137,7 @@ Dir Dir::tempDir(const std::string& templ)
SG_LOG(SG_IO, SG_WARN, "failed to create temporary directory at " << p);
return Dir();
}
return t;
#endif
}
@@ -154,7 +153,7 @@ PathList Dir::children(int types, const std::string& nameFilter) const
if (types == 0) {
types = TYPE_FILE | TYPE_DIR | NO_DOT_OR_DOTDOT;
}
#if defined(SG_WINDOWS)
std::wstring search(_path.wstr());
if (nameFilter.empty()) {
@@ -162,18 +161,18 @@ PathList Dir::children(int types, const std::string& nameFilter) const
} else {
search += simgear::strutils::convertUtf8ToWString("\\*" + nameFilter);
}
WIN32_FIND_DATAW fData;
HANDLE find = FindFirstFileW(search.c_str(), &fData);
if (find == INVALID_HANDLE_VALUE) {
int err = GetLastError();
if (err != ERROR_FILE_NOT_FOUND) {
SG_LOG(SG_GENERAL, SG_WARN, "Dir::children: FindFirstFile failed:" <<
SG_LOG(SG_GENERAL, SG_WARN, "Dir::children: FindFirstFile failed:" <<
_path << " with error:" << err);
}
return result;
}
bool done = false;
for (bool done = false; !done; done = (FindNextFileW(find, &fData) == 0)) {
if (fData.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) {
@@ -181,7 +180,7 @@ PathList Dir::children(int types, const std::string& nameFilter) const
continue;
}
}
std::string utf8File = simgear::strutils::convertWStringToUtf8(fData.cFileName);
if (fData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
if (types & NO_DOT_OR_DOTDOT) {
@@ -206,38 +205,38 @@ PathList Dir::children(int types, const std::string& nameFilter) const
FindClose(find);
#else
std::string ps = _path.local8BitStr();
std::string ps = _path.utf8Str();
DIR* dp = opendir(ps.c_str());
if (!dp) {
SG_LOG(SG_GENERAL, SG_WARN, "Dir::children: opendir failed:" << _path);
return result;
}
int filterLen = nameFilter.size();
while (true) {
struct dirent* entry = readdir(dp);
if (!entry) {
break; // done iteration
}
// skip hidden files (names beginning with '.') unless requested
if (!(types & INCLUDE_HIDDEN) && (entry->d_name[0] == '.') &&
strcmp(entry->d_name, ".") && strcmp(entry->d_name, "..")) {
continue;
}
SGPath f = file(entry->d_name);
if (!f.exists()) {
continue; // stat() failed
}
if (f.isDir()) {
// directory handling
if (!(types & TYPE_DIR)) {
continue;
}
if (types & NO_DOT_OR_DOTDOT) {
if (!strcmp(entry->d_name, ".") || !strcmp(entry->d_name, "..")) {
continue;
@@ -258,17 +257,17 @@ PathList Dir::children(int types, const std::string& nameFilter) const
if (nameLen < filterLen) {
continue; // name is shorter than the filter
}
char* nameSuffix = entry->d_name + (nameLen - filterLen);
if (strcmp(nameSuffix, nameFilter.c_str())) {
continue;
}
}
// passed all criteria, add to our result vector
result.push_back(file(entry->d_name));
}
closedir(dp);
#endif
@@ -290,7 +289,7 @@ bool Dir::isEmpty() const
std::wstring ps = _path.wstr();
return PathIsDirectoryEmptyW( ps.c_str() );
#else
std::string ps = _path.local8BitStr();
std::string ps = _path.utf8Str();
DIR* dp = opendir( ps.c_str() );
if (!dp) return true;
@@ -316,7 +315,7 @@ SGPath Dir::file(const std::string& name) const
SGPath childPath = _path;
childPath.set_cached(true);
childPath.append(name);
return childPath;
return childPath;
}
bool Dir::create(mode_t mode)
@@ -324,7 +323,7 @@ bool Dir::create(mode_t mode)
if (exists()) {
return false; // already exists
}
// recursively create parent directories
Dir pr(parent());
if (!pr.path().isNull() && !pr.exists()) {
@@ -333,13 +332,13 @@ bool Dir::create(mode_t mode)
return false;
}
}
// finally, create ourselves
#if defined(SG_WINDOWS)
std::wstring ps = _path.wstr();
int err = _wmkdir(ps.c_str());
#else
std::string ps = _path.local8BitStr();
std::string ps = _path.utf8Str();
int err = mkdir(ps.c_str(), mode);
#endif
if (err) {
@@ -347,7 +346,7 @@ bool Dir::create(mode_t mode)
"directory creation failed for '" << _path.utf8Str() << "': " <<
simgear::strutils::error_string(errno));
}
return (err == 0);
}
@@ -359,20 +358,20 @@ bool Dir::removeChildren() const
bool ok;
PathList cs = children(NO_DOT_OR_DOTDOT | INCLUDE_HIDDEN | TYPE_FILE | TYPE_DIR);
BOOST_FOREACH(SGPath path, cs) {
for (auto path : cs) {
if (path.isDir()) {
Dir childDir(path);
ok = childDir.remove(true);
} else {
ok = path.remove();
}
if (!ok) {
SG_LOG(SG_IO, SG_WARN, "failed to remove:" << path);
return false;
}
} // of child iteration
return true;
}
@@ -382,7 +381,7 @@ bool Dir::remove(bool recursive)
SG_LOG(SG_IO, SG_WARN, "attempt to remove non-existant dir:" << _path);
return false;
}
if (recursive) {
if (!removeChildren()) {
SG_LOG(SG_IO, SG_WARN, "Dir at:" << _path << " failed to remove children");
@@ -394,7 +393,7 @@ bool Dir::remove(bool recursive)
std::wstring ps = _path.wstr();
int err = _wrmdir(ps.c_str());
#else
std::string ps = _path.local8BitStr();
std::string ps = _path.utf8Str();
int err = rmdir(ps.c_str());
#endif
if (err) {

View File

@@ -29,6 +29,7 @@
#include <simgear/misc/strutils.hxx>
#include <simgear/io/iostreams/sgstream.hxx>
#include <cstring>
#include <stdio.h>
#include <sys/stat.h>
#include <errno.h>
@@ -47,8 +48,6 @@
#include "sg_path.hxx"
#include <boost/algorithm/string/case_conv.hpp>
using std::string;
using simgear::strutils::starts_with;
@@ -398,7 +397,7 @@ string SGPath::extension() const {
}
string SGPath::lower_extension() const {
return boost::to_lower_copy(extension());
return simgear::strutils::lowercase (extension());
}
string SGPath::complete_lower_extension() const
@@ -412,7 +411,7 @@ string SGPath::complete_lower_extension() const
string::size_type firstDot = path.find(".", index);
if ((firstDot != string::npos) && (path.find(sgDirPathSep, firstDot) == string::npos)) {
return boost::to_lower_copy(path.substr(firstDot + 1));
return simgear::strutils::lowercase (path.substr(firstDot + 1));
} else {
return "";
}
@@ -786,8 +785,8 @@ bool SGPath::rename(const SGPath& newName)
std::wstring np = newName.wstr();
if (_wrename(p.c_str(), np.c_str()) != 0)
#else
std::string p = local8BitStr();
std::string np = newName.local8BitStr();
std::string p = utf8Str();
std::string np = newName.utf8Str();
if( ::rename(p.c_str(), np.c_str()) != 0 )
#endif

View File

@@ -0,0 +1,95 @@
// -*- coding: utf-8 -*-
//
// simgear_optional.hxx --- Mimic std::optional until we can use C++14
// Copyright (C) 2020 James Turner <james@flightgear.org>
//
// 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.
#pragma once
#include <simgear/structure/exception.hxx>
namespace simgear
{
/**
* Inefficient version of std::optional. It requires a default-constructable,
* copyable T type, unlike the real version.
*/
template <class T>
class optional
{
public:
using value_type = T;
optional() = default;
optional(const T& v) :
_value(v),
_haveValue(true)
{}
optional(const optional<T>& other) :
_value(other._value),
_haveValue(other._haveValue)
{}
optional<T>& operator=(const optional<T>& other)
{
_haveValue = other._haveValue;
_value = other._value;
return *this;
}
explicit operator bool() const
{
return _haveValue;
}
bool has_value() const
{
return _haveValue;
}
const T& value() const
{
if (!_haveValue) {
throw sg_exception("No value in optional");
}
return _value;
}
T& value()
{
if (!_haveValue) {
throw sg_exception("No value in optional");
}
return _value;
}
void reset()
{
_haveValue = false;
_value = {};
}
private:
T _value = {};
bool _haveValue = false;
};
} // of namespace simgear

View File

@@ -587,6 +587,40 @@ namespace simgear {
// reached end - longer wins
return v1parts.size() - v2parts.size();
}
bool compareVersionToWildcard(const std::string& aVersion, const std::string& aCandidate)
{
if (aCandidate == aVersion) {
return true;
}
// examine each dot-seperated component in turn, supporting a wildcard
// in the versions from the catalog.
string_list parts = split(aVersion, ".");
string_list candidateParts = split(aCandidate, ".");
const size_t partCount = parts.size();
const size_t candidatePartCount = candidateParts.size();
bool previousCandidatePartWasWildcard = false;
for (unsigned int p=0; p < partCount; ++p) {
// candidate string is too short, can match if it ended with wildcard
// part. This allows candidate '2016.*' to match '2016.1.2' and so on
if (candidatePartCount <= p) {
return previousCandidatePartWasWildcard;
}
if (candidateParts.at(p) == "*") {
// always passes
previousCandidatePartWasWildcard = true;
} else if (parts.at(p) != candidateParts.at(p)) {
return false;
}
}
return true;
}
string join(const string_list& l, const string& joinWith)
{
@@ -623,6 +657,22 @@ namespace simgear {
*p = tolower(*p);
}
}
bool iequals(const std::string& a, const std::string& b)
{
const auto lenA = a.length();
const auto lenB = b.length();
if (lenA != lenB) return false;
const char* aPtr = a.data();
const char* bPtr = b.data();
for (size_t i = 0; i < lenA; ++i) {
if (tolower(*aPtr++) != tolower(*bPtr++)) return false;
}
return true;
}
#if defined(SG_WINDOWS)
static std::wstring convertMultiByteToWString(DWORD encoding, const std::string& a)
@@ -656,8 +706,7 @@ static std::string convertWStringToMultiByte(DWORD encoding, const std::wstring&
std::wstring convertUtf8ToWString(const std::string& a)
{
#if defined(SG_WINDOWS)
std::wstring_convert<std::codecvt_utf8<wchar_t>, wchar_t> ucs2conv;
return ucs2conv.from_bytes(a);
return convertMultiByteToWString(CP_UTF8, a);
#else
assert(sizeof(wchar_t) == 4);
std::wstring result;
@@ -707,8 +756,7 @@ std::wstring convertUtf8ToWString(const std::string& a)
std::string convertWStringToUtf8(const std::wstring& w)
{
#if defined(SG_WINDOWS)
std::wstring_convert<std::codecvt_utf8<wchar_t>, wchar_t> ucs2conv;
return ucs2conv.to_bytes(w);
return convertWStringToMultiByte(CP_UTF8, w);
#else
assert(sizeof(wchar_t) == 4);
std::string result;
@@ -883,6 +931,69 @@ std::string encodeHex(const unsigned char* rawBytes, unsigned int length)
return hex;
}
std::vector<uint8_t> decodeHex(const std::string& input)
{
std::vector<uint8_t> result;
char* ptr = const_cast<char*>(input.data());
const char* end = ptr + input.length();
bool highNibble = true;
uint8_t b = 0;
while (ptr != end) {
const char c = *ptr;
char val = 0;
if (c == '0') {
val = 0;
if ((ptr + 1) < end) {
const auto peek = *(ptr + 1);
if (peek == 'x') {
// tolerate 0x prefixing
highNibble = true;
ptr += 2; // skip both bytes
continue;
}
}
} else if (isdigit(c)) {
val = c - '0';
} else if ((c >= 'A') && (c <= 'F')) {
val = c - 'A' + 10;
} else if ((c >= 'a') && (c <= 'f')) {
val = c - 'a' + 10;
} else {
// any other input: newline, space, tab, comma...
if (!highNibble) {
// allow a single digit to work, if we have spacing
highNibble = true;
result.push_back(b >> 4);
}
++ptr;
continue;
}
if (highNibble) {
highNibble = false;
b = val << 4;
} else {
highNibble = true;
b |= val;
result.push_back(b);
}
++ptr;
}
// watch for trailing single digit
// this is reqquired so a stirng ending in 0x3 is decoded.
if (!highNibble) {
result.push_back(b >> 4);
}
return result;
}
// Write an octal backslash-escaped respresentation of 'val' to 'buf'.
//
// At least 4 write positions must be available at 'buf'. The result is *not*
@@ -991,6 +1102,16 @@ std::string unescape(const char* s)
}
return r;
}
std::string replace(std::string source, const std::string search, const std::string replacement, std::size_t start_pos)
{
if (start_pos < source.length()) {
while ((start_pos = source.find(search, start_pos)) != std::string::npos) {
source.replace(start_pos, search.length(), replacement);
start_pos += replacement.length();
}
}
return source;
}
string sanitizePrintfFormat(const string& input)
{
@@ -1017,7 +1138,7 @@ std::string error_string(int errnum)
retcode = strerror_s(buf, sizeof(buf), errnum);
#elif defined(_GNU_SOURCE)
return std::string(strerror_r(errnum, buf, sizeof(buf)));
#elif (_POSIX_C_SOURCE >= 200112L) || defined(SG_MAC) || defined(__FreeBSD__)
#elif (_POSIX_C_SOURCE >= 200112L) || defined(SG_MAC) || defined(__FreeBSD__) || defined(__OpenBSD__)
int retcode;
// POSIX.1-2001 and POSIX.1-2008
retcode = strerror_r(errnum, buf, sizeof(buf));

View File

@@ -33,6 +33,7 @@
#include <vector>
#include <type_traits>
#include <cstdlib>
#include <cstdint>
typedef std::vector < std::string > string_list;
@@ -247,6 +248,13 @@ namespace simgear {
const std::string& v2,
int maxComponents = 0 );
/**
@brief COmpare a version string to a template version string (which can contain wildcards)
@param aVersion : a regular version such as 2017.6 or 2020.1.2
@param aCandidate : a version specifier, eg 2020.* or 21.5.*
*/
bool compareVersionToWildcard(const std::string& aVersion, const std::string& aCandidate);
/**
* Convert a string to upper case.
* @return upper case string
@@ -264,6 +272,11 @@ namespace simgear {
*/
void lowercase(std::string &s);
/**
* case-insensitive string comparisom
*/
bool iequals(const std::string& a, const std::string& b);
/**
* convert a string in the local Windows 8-bit encoding to UTF-8
* (no-op on other platforms)
@@ -299,6 +312,9 @@ namespace simgear {
std::string encodeHex(const unsigned char* rawBytes, unsigned int length);
std::vector<uint8_t> decodeHex(const std::string& input);
/**
* Backslash-escape a string for C/C++ string literal syntax.
*
@@ -328,7 +344,18 @@ namespace simgear {
inline std::string unescape(const std::string& str)
{ return unescape(str.c_str()); }
/**
/**
* Replace matching elements of string.
*
* @param source source string
* @param search search string
* @param replace replacement string
* @param start_pos starting position for replacement in source. Checked to ensure less than length of source.
* @return string with all occurrences of search changed to replace
*/
std::string replace(std::string source, const std::string search, const std::string replacement, std::size_t start_pos = 0);
/**
* Check a printf-style format string for dangerous (buffer-overflowing,
* memory re-writing) format tokens. If a problematic token is
* found, logs an error (SG_WARN) and returns an empty format string.

View File

@@ -99,6 +99,16 @@ void test_to_int()
SG_CHECK_EQUAL(strutils::to_int("-10000"), -10000);
}
void test_iequals()
{
SG_VERIFY(strutils::iequals("abcdef", "AbCDeF"));
SG_VERIFY(strutils::iequals("", ""));
SG_VERIFY(!strutils::iequals("abcdE", "ABCD"));
SG_VERIFY(strutils::iequals("%$abcdef12", "%$AbCDeF12"));
SG_VERIFY(strutils::iequals("VOR-DME", "vor-dme"));
SG_VERIFY(!strutils::iequals("VOR-DME", "vor_dme"));
}
// Auxiliary function for test_readNonNegativeInt()
void aux_readNonNegativeInt_setUpOStringStream(std::ostringstream& oss, int base)
{
@@ -448,7 +458,7 @@ void test_escape()
" ab\\nc \\\\def\\t\\r \\\\ ghi\\\\");
// U+0152 is LATIN CAPITAL LIGATURE OE. The last word is Egg translated in
// French and encoded in UTF-8 ('Œuf' if you can read UTF-8).
SG_CHECK_EQUAL(strutils::escape("Un \"Bel\" '\u0152uf'"),
SG_CHECK_EQUAL(strutils::escape(u8"Un \"Bel\" '\u0152uf'"),
"Un \\\"Bel\\\" '\\305\\222uf'");
SG_CHECK_EQUAL(strutils::escape("\a\b\f\n\r\t\v"),
"\\a\\b\\f\\n\\r\\t\\v");
@@ -618,6 +628,11 @@ void test_utf8Convert()
std::wstring aRoundTrip = strutils::convertUtf8ToWString(utf8A);
SG_VERIFY(a == aRoundTrip);
const auto wide2(L"\U0001f6eb\u2708\ufe0f\u2764\ufe0f");
std::string utf8_2 = strutils::convertWStringToUtf8(wide2);
SG_VERIFY(utf8_2 == std::string("\xf0\x9f\x9b\xab\xe2\x9c\x88\xef\xb8\x8f\xe2\x9d\xa4\xef\xb8\x8f"));
}
void test_parseGeod()
@@ -715,6 +730,13 @@ void test_formatGeod()
}
void testDecodeHex()
{
const auto decoded = simgear::strutils::decodeHex("01 0xff,0xcd \n\t99 0xcD abcdef\n\r0x1 0x2 0x3");
vector<uint8_t> data1 = {0x1, 0xff, 0xcd, 0x99, 0xcd, 0xAB, 0xCD,0xEF, 1, 2, 3};
SG_VERIFY(decoded == data1);
}
int main(int argc, char* argv[])
{
test_strip();
@@ -737,6 +759,8 @@ int main(int argc, char* argv[])
test_utf8Convert();
test_parseGeod();
test_formatGeod();
test_iequals();
testDecodeHex();
return EXIT_SUCCESS;
}

View File

@@ -157,7 +157,7 @@ static void initContext(naContext c)
c->error[0] = 0;
c->userData = 0;
}
#define BASE_SIZE 256000
static void initGlobals()
{
int i;
@@ -168,10 +168,10 @@ static void initGlobals()
globals->sem = naNewSem();
globals->lock = naNewLock();
globals->allocCount = 256; // reasonable starting value
globals->allocCount = BASE_SIZE; // reasonable starting value
for(i=0; i<NUM_NASAL_TYPES; i++)
naGC_init(&(globals->pools[i]), i);
globals->deadsz = 256;
globals->deadsz = BASE_SIZE;
globals->ndead = 0;
globals->deadBlocks = naAlloc(sizeof(void*) * globals->deadsz);
@@ -235,6 +235,21 @@ void naFreeContext(naContext c)
if(c->callChild) naFreeContext(c->callChild);
if(c->callParent) c->callParent->callChild = 0;
LOCK();
// 2019-09-21
// James adding this to ensure stray stuff in freed contexts gets GCed
// this shows up when doing a reset / shutdown of all Nasal - we drop
// all our contexts and saved refs, and run a GC pass. We expect *everything*
// to be freed but actually the freed contexts often have a ref in their
// opStack.
//
// The underlying cause is likely some operation which leaves a value on
// the opstack accidently, but tracing that down requires more Nasal-fu
// than I have right now. So instead I'm clearing the stack tops here, so
// a freed context looks the same as a new one returned by initContext.
c->fTop = c->opTop = c->markTop = 0;
c->nextFree = globals->freeContexts;
globals->freeContexts = c;
UNLOCK();
@@ -488,7 +503,7 @@ static void setMember(naContext ctx, naRef obj, naRef fld, naRef value)
return;
}
if(!IS_HASH(obj)) ERR(ctx, "non-objects have no members");
if(!IS_HASH(obj)) naRuntimeError(ctx, "non-object does not have member: %s", naStr_data(fld));
naHash_set(obj, fld, value);
ctx->opTop -= 2;
}
@@ -833,9 +848,13 @@ naRef naGetSourceFile(naContext ctx, int frame)
{
naRef f;
frame = findFrame(ctx, &ctx, frame);
f = ctx->fStack[frame].func;
f = PTR(f).func->code;
return PTR(f).code->srcFile;
if (frame >= 0) {
f = ctx->fStack[frame].func;
f = PTR(f).func->code;
if (!IS_NIL(f) && PTR(f).code)
return PTR(f).code->srcFile;
}
return naNil();
}
char* naGetError(naContext ctx)

View File

@@ -5,6 +5,7 @@ set(HEADERS
Ghost.hxx
NasalCallContext.hxx
NasalContext.hxx
NasalEmesaryInterface.hxx
NasalHash.hxx
NasalMe.hxx
NasalMethodHolder.hxx

View File

@@ -29,20 +29,24 @@
#include <simgear/structure/SGWeakReferenced.hxx>
#include <simgear/structure/SGWeakPtr.hxx>
#include <boost/bind.hpp>
#include <boost/call_traits.hpp>
#include <boost/function.hpp>
#include <boost/mpl/has_xxx.hpp>
#include <boost/shared_ptr.hpp>
#include <map>
#include <memory>
template<class T>
inline T* get_pointer(boost::weak_ptr<T> const& p)
inline T* get_pointer(std::weak_ptr<T> const& p)
{
return p.lock().get();
}
template<class T>
inline T* get_pointer(std::shared_ptr<T> const& p)
{
return p.get();
}
template<class T>
inline T* get_pointer(SGWeakPtr<T> const& p)
{
@@ -77,7 +81,7 @@ get_pointer(T ptr)
// With old g++ on Jenkins (21.07.2014), ADL for static_pointer_cast does not
// work.
using boost::static_pointer_cast;
using std::static_pointer_cast;
using osg::static_pointer_cast;
#else
// VS (2008, 2010, ... ?) only allow this version.
@@ -193,7 +197,7 @@ namespace nasal
* int myMember();
* void doSomethingElse(const nasal::CallContext& ctx);
* }
* using MyClassPtr = boost::shared_ptr<MyClass>;
* using MyClassPtr = std::shared_ptr<MyClass>;
*
* std::string myOtherFreeMember(int num);
*
@@ -201,7 +205,7 @@ namespace nasal
* {
* // Register a nasal ghost type for MyClass. This needs to be done only
* // once before creating the first ghost instance. The exposed class needs
* // to be wrapped inside a shared pointer, eg. boost::shared_ptr.
* // to be wrapped inside a shared pointer, eg. std::shared_ptr.
* Ghost<MyClassPtr>::init("MyClass")
* // Members can be exposed by getters and setters
* .member("x", &MyClass::getX, &MyClass::setX)
@@ -211,7 +215,7 @@ namespace nasal
* .member("x_writeonly", &MyClass::setX)
* // Methods can be nearly anything callable and accepting a reference
* // to an instance of the class type. (member functions, free functions
* // and anything else bindable using boost::function and boost::bind)
* // and anything else bindable using std::function and std::bind)
* .method("myMember", &MyClass::myMember)
* .method("doSomething", &MyClass::doSomethingElse)
* .method("other", &myOtherFreeMember);
@@ -228,16 +232,16 @@ namespace nasal
using weak_ref = typename shared_ptr_traits<T>::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<naRef(raw_type&, naContext)>;
using setter_t = boost::function<void( raw_type&, naContext, naRef)>;
using method_t = boost::function<naRef(raw_type&, const CallContext&)>;
using getter_t = std::function<naRef(raw_type&, naContext)>;
using setter_t = std::function<void( raw_type&, naContext, naRef)>;
using method_t = std::function<naRef(raw_type&, const CallContext&)>;
using fallback_getter_t =
boost::function<bool(raw_type&, naContext, const std::string&, naRef&)>;
std::function<bool(raw_type&, naContext, const std::string&, naRef&)>;
using fallback_setter_t =
boost::function<bool(raw_type&, naContext, const std::string&, naRef)>;
std::function<bool(raw_type&, naContext, const std::string&, naRef)>;
template<class Ret, class... Args>
using method_variadic_t = boost::function<Ret (raw_type&, Args...)>;
using method_variadic_t = std::function<Ret (raw_type&, Args...)>;
class MethodHolder:
public internal::MethodHolder
@@ -559,7 +563,7 @@ namespace nasal
const getter_t& getter,
const setter_t& setter = setter_t() )
{
if( !getter.empty() || !setter.empty() )
if( getter || setter )
_members[field] = member_t(getter, setter);
else
SG_LOG
@@ -586,13 +590,16 @@ namespace nasal
* of this ghost, and convert it to the given @a Param type.
*/
template<class Param>
Ghost& _get( const boost::function<bool ( raw_type&,
const std::string&,
Param& )>& getter )
Ghost& _get( const std::function<bool ( raw_type&,
const std::string&,
Param& )>& getter )
{
return _get(boost::bind(
convert_param_invoker<Param>, getter, _1, _2, _3, _4
));
return _get(std::bind(convert_param_invoker<Param>,
getter,
std::placeholders::_1,
std::placeholders::_2,
std::placeholders::_3,
std::placeholders::_4));
}
/**
@@ -615,7 +622,7 @@ namespace nasal
Ghost& _get(bool (raw_type::*getter)(const std::string&, Param&) const)
{
return _get(
boost::function<bool (raw_type&, const std::string&, Param&)>(getter)
std::function<bool (raw_type&, const std::string&, Param&)>(getter)
);
}
@@ -658,20 +665,20 @@ namespace nasal
* this ghost, and convert it to the given @a Param type.
*/
template<class Param>
Ghost& _set(const boost::function<bool ( raw_type&,
const std::string&,
Param )>& setter)
Ghost& _set(const std::function<bool ( raw_type&,
const std::string&,
Param )>& setter)
{
// Setter signature: bool( raw_type&,
// naContext,
// const std::string&,
// naRef )
return _set(boost::bind(
setter,
_1,
_3,
boost::bind(from_nasal_ptr<Param>::get(), _2, _4)
));
return _set(std::bind(setter,
std::placeholders::_1,
std::placeholders::_3,
std::bind(from_nasal_ptr<Param>::get(),
std::placeholders::_2,
std::placeholders::_4)));
}
/**
@@ -694,7 +701,7 @@ namespace nasal
Ghost& _set(bool (raw_type::*setter)(const std::string&, Param))
{
return _set(
boost::function<bool (raw_type&, const std::string&, Param)>(setter)
std::function<bool (raw_type&, const std::string&, Param)>(setter)
);
}
@@ -760,10 +767,13 @@ namespace nasal
Ghost& method
(
const std::string& name,
const boost::function<Ret (raw_type&, const CallContext&)>& func
const std::function<Ret (raw_type&, const CallContext&)>& func
)
{
return method(name, boost::bind(method_invoker<Ret>, func, _1, _2));
return method(name, std::bind(method_invoker<Ret>,
func,
std::placeholders::_1,
std::placeholders::_2));
}
/**
@@ -777,16 +787,13 @@ namespace nasal
const method_variadic_t<Ret, Args...>& func,
std::index_sequence<Indices...> )
{
return method<Ret>
(
name,
typename boost::function<Ret (raw_type&, const CallContext&)>
( boost::bind(
func,
_1,
boost::bind(&Ghost::arg_from_nasal<Args>, _2, Indices)...
))
);
return method<Ret>(name,
typename std::function<Ret (raw_type&, const CallContext&)>
(std::bind(func,
std::placeholders::_1,
std::bind(&Ghost::arg_from_nasal<Args>,
std::placeholders::_2,
Indices)...)));
}
template<class Ret, class... Args>
@@ -1118,24 +1125,20 @@ namespace nasal
naRef(*to_nasal_)(naContext, param_type) = &to_nasal;
// Getter signature: naRef(raw_type&, naContext)
return boost::bind
(
to_nasal_,
_2,
boost::bind(getter, _1)
);
return std::bind(to_nasal_,
std::placeholders::_2,
std::bind(getter, std::placeholders::_1));
}
template<class Param>
setter_t to_setter(void (raw_type::*setter)(Param))
{
// Setter signature: void(raw_type&, naContext, naRef)
return boost::bind
(
setter,
_1,
boost::bind(from_nasal_ptr<Param>::get(), _2, _3)
);
return std::bind(setter,
std::placeholders::_1,
std::bind(from_nasal_ptr<Param>::get(),
std::placeholders::_2,
std::placeholders::_3));
}
/**
@@ -1145,9 +1148,9 @@ namespace nasal
static
bool convert_param_invoker
(
const boost::function<bool ( raw_type&,
const std::string&,
Param& )>& func,
const std::function<bool ( raw_type&,
const std::string&,
Param& )>& func,
raw_type& obj,
naContext c,
const std::string& key,
@@ -1170,7 +1173,7 @@ namespace nasal
std::enable_if_t<!std::is_void<Ret>::value, naRef>
method_invoker
(
const boost::function<Ret (raw_type&, const CallContext&)>& func,
const std::function<Ret (raw_type&, const CallContext&)>& func,
raw_type& obj,
const CallContext& ctx
)
@@ -1186,7 +1189,7 @@ namespace nasal
std::enable_if_t<std::is_void<Ret>::value, naRef>
method_invoker
(
const boost::function<Ret (raw_type&, const CallContext&)>& func,
const std::function<Ret (raw_type&, const CallContext&)>& func,
raw_type& obj,
const CallContext& ctx
)
@@ -1224,7 +1227,7 @@ namespace nasal
// Either const CallContext& or CallContext, non-const reference
// does not make sense.
static_assert(
!boost::is_same<Arg, CallContext&>::value,
!std::is_same<Arg, CallContext&>::value,
"Only const reference and value make sense!");
return ctx;
};
@@ -1358,7 +1361,7 @@ namespace nasal
}
else if( member->second.func )
*out = member->second.func->get_naRef(c);
else if( !member->second.getter.empty() )
else if( member->second.getter )
*out = member->second.getter(obj, c);
else
return "Read-protected member";
@@ -1407,7 +1410,7 @@ namespace nasal
else if( !fallback_set(obj, c, key, val) )
naRuntimeError(c, "ghost: Failed to write (_set: %s)", key.c_str());
}
else if( member->second.setter.empty() )
else if( !member->second.setter )
naRuntimeError(c, "ghost: Write protected member: %s", key.c_str());
else if( member->second.func )
naRuntimeError(c, "ghost: Write to function: %s", key.c_str());

View File

@@ -0,0 +1,120 @@
#ifndef NASALEMESARYINTERFACE_INCLUDED
#define NASALEMESARYINTERFACE_INCLUDED 1
// Nasal Emesary receipient interface.
//
// Copyright (C) 2019 Richard Harrison rjh@zaretto.com
//
// 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
#include <simgear/nasal/cppbind/NasalHash.hxx>
#include <simgear/nasal/cppbind/Ghost.hxx>
#include <simgear/math/SGMath.hxx>
#include <simgear/misc/sg_path.hxx>
#include <simgear/emesary/Emesary.hxx>
#include <simgear/emesary/notifications.hxx>
#include <simgear/debug/logstream.hxx>
#include <simgear/threads/SGThread.hxx>
#include <mutex>
#include <condition_variable>
#include <atomic>
namespace nasal
{
extern"C" {
extern int GCglobalAlloc();
extern int naGarbageCollect();
// these are used by the detailed debug in the Nasal GC.
SGTimeStamp global_timestamp;
void global_stamp() {
global_timestamp.stamp();
}
extern int global_elapsedUSec()
{
return global_timestamp.elapsedUSec();
}
}
class ThreadedGarbageCollector : public SGExclusiveThread {
public:
ThreadedGarbageCollector() : SGExclusiveThread() {}
virtual ~ThreadedGarbageCollector() {}
virtual int process(){
return naGarbageCollect();
}
};
class NasalMainLoopRecipient : public simgear::Emesary::IReceiver {
public:
NasalMainLoopRecipient() : receiveCount(0) {
simgear::Emesary::GlobalTransmitter::instance()->Register(*this);
}
virtual ~NasalMainLoopRecipient() {
simgear::Emesary::GlobalTransmitter::instance()->DeRegister(*this);
}
std::atomic<int> receiveCount;
virtual simgear::Emesary::ReceiptStatus Receive(simgear::Emesary::INotification &n)
{
simgear::Notifications::MainLoopNotification *mln = dynamic_cast<simgear::Notifications::MainLoopNotification *>(&n);
if (mln) {
switch (mln->GetValue()) {
case simgear::Notifications::MainLoopNotification::Type::Begin:
if (gct.is_running()) {
if (Active && CanWait)
gct.awaitCompletion();
else
gct.clearAwaitCompletionTime();
}
break;
case simgear::Notifications::MainLoopNotification::Type::End:
if (Active) {
if (gct.is_running())
gct.release();
}
break;
case simgear::Notifications::MainLoopNotification::Type::Started:
gct.ensure_running();
break;
case simgear::Notifications::MainLoopNotification::Type::Stopped:
gct.terminate();
break;
}
return simgear::Emesary::ReceiptStatusOK;
}
auto *gccn = dynamic_cast<simgear::Notifications::NasalGarbageCollectionConfigurationNotification *>(&n);
if (gccn) {
CanWait = gccn->GetCanWait();
Active = gccn->GetActive();
return simgear::Emesary::ReceiptStatusOK;
}
return simgear::Emesary::ReceiptStatusNotProcessed;
}
protected:
bool CanWait;
bool Active;
ThreadedGarbageCollector gct;
};
} // namespace nasal
#endif

View File

@@ -120,9 +120,9 @@ namespace nasal
* @param name Member name
*/
template<class Sig, class Key>
typename boost::enable_if< boost::is_function<Sig>,
boost::function<Sig>
>::type
typename std::enable_if< std::is_function<Sig>::value,
std::function<Sig>
>::type
get(const Key& name) const
{
BOOST_STATIC_ASSERT(( boost::is_convertible<Key, naRef>::value
@@ -237,9 +237,9 @@ namespace nasal
*/
template<bool is_other_const>
Iterator( Iterator<is_other_const> const& other,
typename boost::enable_if_c< is_const || !is_other_const,
void*
>::type = NULL ):
typename std::enable_if< is_const || !is_other_const,
void*
>::type = NULL ):
_hash(other._hash),
_index(other._index)
{}

View File

@@ -52,7 +52,7 @@ namespace nasal
return Ret();
Context ctx;
auto func = get_member<boost::function<Ret (Me, Args...)>>(
auto func = get_member<std::function<Ret (Me, Args...)>>(
ctx, _nasal_impl.get_naRef(), name
);
if( func )

View File

@@ -32,9 +32,8 @@
#include <simgear/structure/exception.hxx>
#include <simgear/structure/SGSharedPtr.hxx>
#include <boost/function.hpp>
#include <array>
#include <functional>
#include <string>
#include <vector>
@@ -110,14 +109,14 @@ namespace nasal
bool from_nasal_helper(naContext c, naRef ref, const bool*);
/**
* Convert a Nasal function to a boost::function with the given signature.
* Convert a Nasal function to a std::function with the given signature.
*
* @tparam Sig Signature of returned function (arguments and return value
* are automatically converted using from_nasal/to_nasal)
*/
template<class Ret, class... Args>
boost::function<Ret (Args...)>
from_nasal_helper(naContext c, naRef ref, const boost::function<Ret (Args...)>*)
std::function<Ret (Args...)>
from_nasal_helper(naContext c, naRef ref, const std::function<Ret (Args...)>*)
{
if( naIsNil(ref) )
return {};
@@ -131,11 +130,11 @@ namespace nasal
}
template<class Ret, class... Args>
boost::function<Ret (Args...)>
std::function<Ret (Args...)>
from_nasal_helper(naContext c, naRef ref, Ret (*const)(Args...))
{
return
from_nasal_helper(c, ref, static_cast<boost::function<Ret (Args...)>*>(0));
from_nasal_helper(c, ref, static_cast<std::function<Ret (Args...)>*>(0));
}
/**
@@ -207,6 +206,15 @@ namespace nasal
return Vec2(vec[0], vec[1]);
}
template<class Vec4>
std::enable_if_t<is_vec4<Vec4>::value, Vec4>
from_nasal_helper(naContext c, naRef ref, const Vec4*)
{
auto vec = from_nasal_helper(c, ref, static_cast<std::array<double, 4>*>(0));
return Vec4(vec[0], vec[1], vec[2], vec[3]);
}
/**
* Convert a Nasal vector with 4 elements ([x, y, w, h]) to an SGRect
*/

View File

@@ -20,6 +20,7 @@
#ifndef SG_NASAL_TRAITS_HXX_
#define SG_NASAL_TRAITS_HXX_
#include <memory>
#include <simgear/std/type_traits.hxx>
// Forward declarations
@@ -28,12 +29,8 @@ class SGWeakReferenced;
template<class T> class SGSharedPtr;
template<class T> class SGWeakPtr;
template<class T> class SGVec2;
template<class T> class SGVec4;
namespace boost
{
template<class T> class shared_ptr;
template<class T> class weak_ptr;
}
namespace osg
{
class Referenced;
@@ -47,6 +44,9 @@ namespace osg
class Vec2d;
class Vec2f;
class Vec2s;
class Vec4f;
class Vec4d;
}
// The actual traits...
@@ -55,6 +55,10 @@ namespace nasal
template<class T>
struct is_vec2: public std::false_type {};
template<class T>
struct is_vec4: public std::false_type {};
#define SG_MAKE_TRAIT(templ,type,attr)\
template templ\
struct attr< type >: public std::true_type {};
@@ -65,6 +69,10 @@ SG_MAKE_TRAIT(<>, osg::Vec2d, is_vec2)
SG_MAKE_TRAIT(<>, osg::Vec2f, is_vec2)
SG_MAKE_TRAIT(<>, osg::Vec2s, is_vec2)
SG_MAKE_TRAIT(<class T>, SGVec4<T>, is_vec4)
SG_MAKE_TRAIT(<>, osg::Vec4d, is_vec4)
SG_MAKE_TRAIT(<>, osg::Vec4f, is_vec4)
#undef SG_MAKE_TRAIT
template<class T>
@@ -102,7 +110,7 @@ SG_MAKE_TRAIT(<>, osg::Vec2s, is_vec2)
SG_MAKE_SHARED_PTR_TRAIT(SGSharedPtr, SGWeakPtr, true)
SG_MAKE_SHARED_PTR_TRAIT(osg::ref_ptr, osg::observer_ptr, true)
SG_MAKE_SHARED_PTR_TRAIT(boost::shared_ptr, boost::weak_ptr, false)
SG_MAKE_SHARED_PTR_TRAIT(std::shared_ptr, std::weak_ptr, false)
#undef SG_MAKE_SHARED_PTR_TRAIT

View File

@@ -19,14 +19,26 @@
#include "to_nasal_helper.hxx"
#include <simgear/nasal/cppbind/NasalHash.hxx>
#include <simgear/nasal/cppbind/Ghost.hxx>
#include <simgear/nasal/cppbind/NasalEmesaryInterface.hxx>
#include <simgear/math/SGMath.hxx>
#include <simgear/misc/sg_path.hxx>
#include <boost/function.hpp>
namespace nasal
{
// create single instance of the main loop recipient for Nasal - this will self register at the
// global transmitter - and that's all that is needed to link up the background GC to the main
// loop in FG that will send out the MainLoop notifications.
//class NasalMainLoopRecipientSingleton : public simgear::Singleton<NasalMainLoopRecipient>
//{
//public:
// NasalMainLoopRecipientSingleton()
// {
// }
// virtual ~NasalMainLoopRecipientSingleton() {}
//};
NasalMainLoopRecipient mrl;
//----------------------------------------------------------------------------
naRef to_nasal_helper(naContext c, const std::string& str)
{

View File

@@ -27,10 +27,10 @@
#include <simgear/nasal/cppbind/cppbind_fwd.hxx>
#include <simgear/std/type_traits.hxx>
#include <boost/function/function_fwd.hpp>
#include <boost/call_traits.hpp>
#include <array>
#include <functional>
#include <initializer_list>
#include <map>
#include <string>
@@ -42,7 +42,7 @@ class SGPath;
namespace nasal
{
typedef boost::function<naRef (CallContext)> free_function_t;
typedef std::function<naRef (CallContext)> free_function_t;
/**
* Convert std::string to Nasal string

View File

@@ -9,9 +9,6 @@
#include <simgear/math/SGMath.hxx>
#include <simgear/structure/map.hxx>
#include <boost/shared_ptr.hpp>
#include <boost/weak_ptr.hpp>
#include <cstring>
enum MyEnum
@@ -79,7 +76,7 @@ struct DoubleDerived:
};
typedef boost::shared_ptr<Base> BasePtr;
typedef std::shared_ptr<Base> BasePtr;
typedef std::vector<BasePtr> BaseVec;
struct DoubleDerived2:
@@ -102,13 +99,13 @@ class SGWeakReferenceBasedClass:
};
typedef boost::shared_ptr<Derived> DerivedPtr;
typedef boost::shared_ptr<DoubleDerived> DoubleDerivedPtr;
typedef boost::shared_ptr<DoubleDerived2> DoubleDerived2Ptr;
typedef std::shared_ptr<Derived> DerivedPtr;
typedef std::shared_ptr<DoubleDerived> DoubleDerivedPtr;
typedef std::shared_ptr<DoubleDerived2> DoubleDerived2Ptr;
typedef SGSharedPtr<SGReferenceBasedClass> SGRefBasedPtr;
typedef SGSharedPtr<SGWeakReferenceBasedClass> SGWeakRefBasedPtr;
typedef boost::weak_ptr<Derived> DerivedWeakPtr;
typedef std::weak_ptr<Derived> DerivedWeakPtr;
naRef derivedFreeMember(Derived&, const nasal::CallContext&) { return naNil(); }
naRef f_derivedGetX(const Derived& d, naContext c)
@@ -234,25 +231,25 @@ BOOST_AUTO_TEST_CASE( cppbind_misc_testing )
// 'func' is a C++ function registered to Nasal and now converted back to C++
boost::function<int (int)> f = hash.get<int (int)>("func");
std::function<int (int)> f = hash.get<int (int)>("func");
BOOST_REQUIRE( f );
BOOST_CHECK_EQUAL(f(3), 3);
boost::function<std::string (int)> fs = hash.get<std::string (int)>("func");
std::function<std::string (int)> fs = hash.get<std::string (int)>("func");
BOOST_REQUIRE( fs );
BOOST_CHECK_EQUAL(fs(14), "14");
typedef boost::function<void (int)> FuncVoidInt;
typedef std::function<void (int)> FuncVoidInt;
FuncVoidInt fvi = hash.get<FuncVoidInt>("func");
BOOST_REQUIRE( fvi );
fvi(123);
typedef boost::function<std::string (const std::string&, int, float)> FuncMultiArg;
typedef std::function<std::string (const std::string&, int, float)> FuncMultiArg;
FuncMultiArg fma = hash.get<FuncMultiArg>("func");
BOOST_REQUIRE( fma );
BOOST_CHECK_EQUAL(fma("test", 3, .5), "test");
typedef boost::function<naRef (naRef)> naRefMemFunc;
typedef std::function<naRef (naRef)> naRefMemFunc;
naRefMemFunc fmem = hash.get<naRefMemFunc>("func");
BOOST_REQUIRE( fmem );
naRef ret = fmem(hash.get_naRef()),
@@ -261,7 +258,7 @@ BOOST_AUTO_TEST_CASE( cppbind_misc_testing )
// Check if nasal::Me gets passed as self/me and remaining arguments are
// passed on to function
typedef boost::function<int (Me, int)> MeIntFunc;
typedef std::function<int (Me, int)> MeIntFunc;
MeIntFunc fmeint = hash.get<MeIntFunc>("func");
BOOST_CHECK_EQUAL(fmeint(Me{}, 5), 5);
@@ -334,7 +331,7 @@ BOOST_AUTO_TEST_CASE( cppbind_misc_testing )
BOOST_CHECK( naIsFunc(thisGetter) );
// ...and check if it really gets passed the correct instance
typedef boost::function<unsigned long (Me)> MemFunc;
typedef std::function<unsigned long (Me)> MemFunc;
MemFunc fGetThis = c.from_nasal<MemFunc>(thisGetter);
BOOST_REQUIRE( fGetThis );
BOOST_CHECK_EQUAL( fGetThis(Me{derived}), (unsigned long)d.get() );
@@ -361,9 +358,9 @@ BOOST_AUTO_TEST_CASE( cppbind_misc_testing )
BOOST_CHECK_EQUAL( c.from_nasal<BasePtr>(derived), d3 );
BOOST_CHECK_NE( c.from_nasal<BasePtr>(derived), d2 );
BOOST_CHECK_EQUAL( c.from_nasal<DerivedPtr>(derived),
boost::dynamic_pointer_cast<Derived>(d3) );
std::dynamic_pointer_cast<Derived>(d3) );
BOOST_CHECK_EQUAL( c.from_nasal<DoubleDerived2Ptr>(derived),
boost::dynamic_pointer_cast<DoubleDerived2>(d3) );
std::dynamic_pointer_cast<DoubleDerived2>(d3) );
BOOST_CHECK_THROW( c.from_nasal<DoubleDerivedPtr>(derived), bad_nasal_cast );
std::map<std::string, BasePtr> instances;

View File

@@ -6,10 +6,6 @@
#include <simgear/nasal/cppbind/Ghost.hxx>
#include <simgear/nasal/cppbind/NasalContext.hxx>
#include <boost/make_shared.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/weak_ptr.hpp>
class Base1:
public virtual SGVirtualWeakReferenced
{};
@@ -43,7 +39,7 @@ typedef SGSharedPtr<SGReferenced> SGReferencedPtr;
CHECK_PTR_TRAIT_TYPE(weak, weak_ref, weak)\
CHECK_PTR_TRAIT(DerivedPtr, DerivedWeakPtr)
CHECK_PTR_TRAIT(boost::shared_ptr<Base1>, boost::weak_ptr<Base1>)
CHECK_PTR_TRAIT(std::shared_ptr<Base1>, std::weak_ptr<Base1>)
#undef CHECK_PTR_TRAIT
#undef CHECK_PTR_TRAIT_TYPE
@@ -150,11 +146,11 @@ BOOST_AUTO_TEST_CASE( ghost_casting_storage )
CHECK_PTR_STORAGE_TRAIT_TYPE(DerivedPtr, Derived)
CHECK_PTR_STORAGE_TRAIT_TYPE(DerivedWeakPtr, DerivedWeakPtr)
typedef boost::shared_ptr<Derived> BoostDerivedPtr;
CHECK_PTR_STORAGE_TRAIT_TYPE(BoostDerivedPtr, BoostDerivedPtr)
typedef std::shared_ptr<Derived> StdDerivedPtr;
CHECK_PTR_STORAGE_TRAIT_TYPE(StdDerivedPtr, StdDerivedPtr)
typedef boost::weak_ptr<Derived> BoostDerivedWeakPtr;
CHECK_PTR_STORAGE_TRAIT_TYPE(BoostDerivedWeakPtr, BoostDerivedWeakPtr)
typedef std::weak_ptr<Derived> StdDerivedWeakPtr;
CHECK_PTR_STORAGE_TRAIT_TYPE(StdDerivedWeakPtr, StdDerivedWeakPtr)
#undef CHECK_PTR_STORAGE_TRAIT_TYPE
@@ -164,8 +160,8 @@ BOOST_STATIC_ASSERT(( nasal::shared_ptr_traits<DerivedPtr>::is_intrusive::value)
BOOST_STATIC_ASSERT(( nasal::shared_ptr_traits<DerivedWeakPtr>::is_intrusive::value));
BOOST_STATIC_ASSERT(( nasal::shared_ptr_traits<SGReferencedPtr>::is_intrusive::value));
BOOST_STATIC_ASSERT((!nasal::shared_ptr_traits<boost::shared_ptr<Derived> >::is_intrusive::value));
BOOST_STATIC_ASSERT((!nasal::shared_ptr_traits<boost::weak_ptr<Derived> >::is_intrusive::value));
BOOST_STATIC_ASSERT((!nasal::shared_ptr_traits<std::shared_ptr<Derived> >::is_intrusive::value));
BOOST_STATIC_ASSERT((!nasal::shared_ptr_traits<std::weak_ptr<Derived> >::is_intrusive::value));
BOOST_AUTO_TEST_CASE( storage_traits )
{
@@ -207,8 +203,8 @@ BOOST_AUTO_TEST_CASE( bind_methods )
arg4 = a4;
}
};
using TestClassPtr = boost::shared_ptr<TestClass>;
auto set_func = boost::function<
using TestClassPtr = std::shared_ptr<TestClass>;
auto set_func = std::function<
void (TestClass&, int, const std::string&, const std::string&, int)
>(&TestClass::set);
nasal::Ghost<TestClassPtr>::init("TestClass")
@@ -216,7 +212,7 @@ BOOST_AUTO_TEST_CASE( bind_methods )
.method("setReverse", set_func, std::index_sequence<3,2,1,0>{});
TestContext ctx;
auto test = boost::make_shared<TestClass>();
auto test = std::make_shared<TestClass>();
ctx.exec("me.set(1, \"s2\", \"s3\", 4);", ctx.to_me(test));
BOOST_CHECK_EQUAL(test->arg1, 1);

Some files were not shown because too many files have changed in this diff Show More