Compare commits
396 Commits
version/20
...
version/20
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
17f86567f1 | ||
|
|
059bad1840 | ||
|
|
b7d4acc67d | ||
|
|
af8b27032f | ||
|
|
f76b72e2f3 | ||
|
|
59512aea1f | ||
|
|
96618a2e26 | ||
|
|
635f460dd5 | ||
|
|
36316e8859 | ||
|
|
81bdae283a | ||
|
|
00e25404c7 | ||
|
|
6a157e1c08 | ||
|
|
7a6f9151a6 | ||
|
|
567293c14d | ||
|
|
2ff6a4966a | ||
|
|
b13d7f9392 | ||
|
|
d21137739d | ||
|
|
1e13cf6fec | ||
|
|
5c52cdc255 | ||
|
|
17d7808200 | ||
|
|
f144bd5a89 | ||
|
|
b72553f6f0 | ||
|
|
c82a14c252 | ||
|
|
faa559e9af | ||
|
|
c51c0c43ed | ||
|
|
2ab0122124 | ||
|
|
8626e0482d | ||
|
|
c9a4c0dac1 | ||
|
|
825d93423d | ||
|
|
ad24ce02e7 | ||
|
|
695616a8f2 | ||
|
|
b3bdf8636a | ||
|
|
c62c080a4b | ||
|
|
c90ab3df5b | ||
|
|
21e21a1fa8 | ||
|
|
92ebdbfacc | ||
|
|
8dbabc8b43 | ||
|
|
c83dc79357 | ||
|
|
a53c1bfe8b | ||
|
|
6185b8fd0d | ||
|
|
ef002f6dfa | ||
|
|
9cdec99fa4 | ||
|
|
9393f84972 | ||
|
|
ceba006c60 | ||
|
|
e9e2053c64 | ||
|
|
bf013aea54 | ||
|
|
454df4872a | ||
|
|
ec315bf26d | ||
|
|
74c304097d | ||
|
|
ad1833a4d5 | ||
|
|
d65bb58317 | ||
|
|
8cf11f9208 | ||
|
|
73f00bd842 | ||
|
|
1b6545fc05 | ||
|
|
40725a76ab | ||
|
|
75f40fad95 | ||
|
|
b458300c02 | ||
|
|
6c6e74e8d1 | ||
|
|
4bab064ad6 | ||
|
|
3da2880aae | ||
|
|
c8d802bc1d | ||
|
|
c3a169319c | ||
|
|
72af950573 | ||
|
|
edae7a2b75 | ||
|
|
1fa48e7edb | ||
|
|
d76992efe6 | ||
|
|
87da4680c7 | ||
|
|
9cc7508b07 | ||
|
|
b258bc598e | ||
|
|
f25c0c60a8 | ||
|
|
f350bd31bd | ||
|
|
06f1699223 | ||
|
|
3e4d528859 | ||
|
|
b847f57382 | ||
|
|
2d7ad25031 | ||
|
|
d670cc13ac | ||
|
|
43699d039f | ||
|
|
9192b009e1 | ||
|
|
3fe85809b2 | ||
|
|
a4b79d6882 | ||
|
|
fc21e6eafa | ||
|
|
11e243a216 | ||
|
|
9f862dcc0e | ||
|
|
faf43d2c4b | ||
|
|
b0580b2df5 | ||
|
|
308903dc91 | ||
|
|
1c7fd67c39 | ||
|
|
c3ae704610 | ||
|
|
9f8e6c0634 | ||
|
|
aab88b411e | ||
|
|
867cf6b60a | ||
|
|
ed87114c5e | ||
|
|
213adb5f0d | ||
|
|
c5c10a003a | ||
|
|
e78205f071 | ||
|
|
ba7134c2a2 | ||
|
|
1eadaa4cda | ||
|
|
d93ce29b20 | ||
|
|
0cddb9e843 | ||
|
|
67e2860cc3 | ||
|
|
0cf9dd165e | ||
|
|
3aa567b672 | ||
|
|
fa1e3cb183 | ||
|
|
14845bf3f2 | ||
|
|
120328eb2b | ||
|
|
e222c0888c | ||
|
|
ec19dfc2f2 | ||
|
|
da45c26e7d | ||
|
|
c39a5fd8c0 | ||
|
|
fb57fe9da7 | ||
|
|
02496da4d2 | ||
|
|
eb1afec22b | ||
|
|
1619df97f8 | ||
|
|
c653a82619 | ||
|
|
2289618cea | ||
|
|
dc886118ba | ||
|
|
879b01004b | ||
|
|
86f3169e4b | ||
|
|
d5957b8c5f | ||
|
|
595eb3efea | ||
|
|
81e93448e3 | ||
|
|
0f9fe8adef | ||
|
|
56933067c0 | ||
|
|
0919f5bf9c | ||
|
|
b91d4411b5 | ||
|
|
15c6131f0e | ||
|
|
c2f2f25046 | ||
|
|
94ced66f87 | ||
|
|
ece2f913a1 | ||
|
|
2b2ada2037 | ||
|
|
1a4568175c | ||
|
|
0c8949e723 | ||
|
|
012966f6a2 | ||
|
|
82b1cca247 | ||
|
|
2976dc29d5 | ||
|
|
99d5b0dc8a | ||
|
|
d1eb768de6 | ||
|
|
5db8e42c69 | ||
|
|
c7efa81efe | ||
|
|
7d92698644 | ||
|
|
4b280a00db | ||
|
|
02b61b145a | ||
|
|
0a7514c47b | ||
|
|
4b1a53367f | ||
|
|
de3625d992 | ||
|
|
65331599ec | ||
|
|
74cd8cf75c | ||
|
|
07586e84ce | ||
|
|
ca97b4371b | ||
|
|
b742607c76 | ||
|
|
1399cc9482 | ||
|
|
7464e17e22 | ||
|
|
053bda26a4 | ||
|
|
93995cd742 | ||
|
|
202dfc9ebe | ||
|
|
23f378cd93 | ||
|
|
70c564dd2c | ||
|
|
9bea6afb80 | ||
|
|
20fb7a9ce0 | ||
|
|
7762bc2cad | ||
|
|
f407785f10 | ||
|
|
138e28fcbe | ||
|
|
38f6a5a40a | ||
|
|
b28f030874 | ||
|
|
d883ab278d | ||
|
|
40ce45c8e9 | ||
|
|
fa754ecaf8 | ||
|
|
c11557316d | ||
|
|
5bf28d3f90 | ||
|
|
c8625ce599 | ||
|
|
991fafb839 | ||
|
|
d7323f5f19 | ||
|
|
10662e4ca5 | ||
|
|
b73dfb08ae | ||
|
|
d8a46cffa5 | ||
|
|
3b3093c72e | ||
|
|
3d841a214c | ||
|
|
2cbbb29eb5 | ||
|
|
c0677ad8c5 | ||
|
|
098c5b0924 | ||
|
|
01ac9a71b7 | ||
|
|
ab8b1d39bd | ||
|
|
b22a8debf2 | ||
|
|
92a3c8bbd8 | ||
|
|
c71f287498 | ||
|
|
b7f8fbe7a0 | ||
|
|
1ca3f60f80 | ||
|
|
7a903361e6 | ||
|
|
4f6e72de55 | ||
|
|
a68c4e9434 | ||
|
|
798b90e0a5 | ||
|
|
cba902f22b | ||
|
|
8eb51e813f | ||
|
|
9ac3c1a394 | ||
|
|
5486ca3b4a | ||
|
|
90845974ea | ||
|
|
57fd817486 | ||
|
|
b322fa8f32 | ||
|
|
81d1e16d7b | ||
|
|
4c52d77aa5 | ||
|
|
523d5166ef | ||
|
|
cde95864b4 | ||
|
|
7354201b5d | ||
|
|
14971f88ee | ||
|
|
0b114ac5cd | ||
|
|
c06eabff24 | ||
|
|
b3ef2478f5 | ||
|
|
39eb9837e9 | ||
|
|
1a429b63c5 | ||
|
|
ae6ad20940 | ||
|
|
3e57b50066 | ||
|
|
dd38e399ca | ||
|
|
319b59500c | ||
|
|
3a79b71e80 | ||
|
|
0c0eab9247 | ||
|
|
114ddcff52 | ||
|
|
57339486d9 | ||
|
|
5f9b17d55b | ||
|
|
325b89e023 | ||
|
|
9ebc823975 | ||
|
|
5fdf6ef4f3 | ||
|
|
0792962591 | ||
|
|
f964374027 | ||
|
|
4352c0a737 | ||
|
|
a0bdcb30fd | ||
|
|
7e0a10c016 | ||
|
|
7fb6f82ab1 | ||
|
|
fdcc9c9e0e | ||
|
|
846f024e91 | ||
|
|
7e969f4e28 | ||
|
|
10a701308d | ||
|
|
e2bed86bfc | ||
|
|
487cdc85cd | ||
|
|
052bbecc3b | ||
|
|
c860b12b3d | ||
|
|
e227008331 | ||
|
|
72f3a784de | ||
|
|
31c9df665d | ||
|
|
dd240116b1 | ||
|
|
92ec139175 | ||
|
|
f076c43b90 | ||
|
|
98215e3dbd | ||
|
|
6197098541 | ||
|
|
34b3c52a28 | ||
|
|
caea68007e | ||
|
|
c1dd8faa29 | ||
|
|
95d065b3d7 | ||
|
|
2303da846b | ||
|
|
7246edcb4b | ||
|
|
dc2f24dbed | ||
|
|
4b793d5344 | ||
|
|
72dd45388f | ||
|
|
b76f78c6da | ||
|
|
30482a3599 | ||
|
|
b3f9571b2d | ||
|
|
0d5afe83a2 | ||
|
|
8b35c9a4b1 | ||
|
|
a5d8eec25f | ||
|
|
df6ec4f94c | ||
|
|
61ebee12ad | ||
|
|
105d63a697 | ||
|
|
07036ac48f | ||
|
|
4f6c8f7784 | ||
|
|
8e57a61aed | ||
|
|
8707005a97 | ||
|
|
cb024dd82d | ||
|
|
84046a6717 | ||
|
|
8bcaa50ba6 | ||
|
|
c433d29171 | ||
|
|
64d51b5290 | ||
|
|
6393b7704e | ||
|
|
0a2071b2f1 | ||
|
|
7981809f36 | ||
|
|
ed87a0b032 | ||
|
|
fc2630e66d | ||
|
|
ec14050890 | ||
|
|
4b17ea95fc | ||
|
|
1be3e5771a | ||
|
|
232cbbac5e | ||
|
|
dee204d2b2 | ||
|
|
b71e9eac51 | ||
|
|
f88636f47c | ||
|
|
8a301efe8b | ||
|
|
5bd393135f | ||
|
|
d550eac525 | ||
|
|
fea59adbbe | ||
|
|
dae0d0ff8b | ||
|
|
86d739cc85 | ||
|
|
9d99f4ce30 | ||
|
|
460a666234 | ||
|
|
ba9342cd4b | ||
|
|
c37eae3b56 | ||
|
|
61cc8d6c50 | ||
|
|
758ab5e581 | ||
|
|
f05ff37560 | ||
|
|
14354090f9 | ||
|
|
0185884ca8 | ||
|
|
5665403d66 | ||
|
|
46b271d75c | ||
|
|
854b958797 | ||
|
|
ff52b55a25 | ||
|
|
e755735ac2 | ||
|
|
69e20a3931 | ||
|
|
05da3db2ed | ||
|
|
6d89cc6c1d | ||
|
|
0494af48a3 | ||
|
|
f4cad42958 | ||
|
|
32189b7239 | ||
|
|
c192071f03 | ||
|
|
8c9fda9137 | ||
|
|
35d1bb1149 | ||
|
|
8dd0833993 | ||
|
|
306eaeaba9 | ||
|
|
7853a5329d | ||
|
|
1fa77078fc | ||
|
|
8e8ce04e18 | ||
|
|
264779e1d3 | ||
|
|
6f505f3a76 | ||
|
|
3817bdd602 | ||
|
|
2c9420d9bc | ||
|
|
2e3cace7f9 | ||
|
|
9553a604ac | ||
|
|
f1e1bf11b6 | ||
|
|
f77724a646 | ||
|
|
5921fd19fb | ||
|
|
a0c4913f84 | ||
|
|
3dfce43de2 | ||
|
|
0e9e5f77cc | ||
|
|
f2f465960b | ||
|
|
05d8ab3000 | ||
|
|
126f69434b | ||
|
|
3e081ae869 | ||
|
|
4e8df9dcc8 | ||
|
|
d7a413d5e7 | ||
|
|
609ac93c10 | ||
|
|
b1d6a41c65 | ||
|
|
a4e2fdfad2 | ||
|
|
22c2971c3c | ||
|
|
123c597e01 | ||
|
|
05e3c29ee4 | ||
|
|
9b1444deb5 | ||
|
|
99d30d5bb7 | ||
|
|
100bb3a571 | ||
|
|
7fb89e4d45 | ||
|
|
1d7c3984ca | ||
|
|
03156a6d94 | ||
|
|
08258ee4b3 | ||
|
|
3dceaf7a0b | ||
|
|
856473ca43 | ||
|
|
7dfe705717 | ||
|
|
dc2f142480 | ||
|
|
fce06105f9 | ||
|
|
94a1156a6b | ||
|
|
5ed4fbd4a3 | ||
|
|
d92a289c25 | ||
|
|
2ba5676eb0 | ||
|
|
66cfa800be | ||
|
|
9755110ec7 | ||
|
|
f5ff969cd4 | ||
|
|
a232565b3e | ||
|
|
fd4ca1c811 | ||
|
|
b6f5b40557 | ||
|
|
5710d33dbf | ||
|
|
831369f653 | ||
|
|
6db59c64aa | ||
|
|
888d7fb262 | ||
|
|
8b4ace6fb8 | ||
|
|
368120c479 | ||
|
|
b01718aae7 | ||
|
|
48b228f68f | ||
|
|
bf21c0e099 | ||
|
|
d85d85e7dc | ||
|
|
99c1dd8124 | ||
|
|
9ce026e22e | ||
|
|
2797970837 | ||
|
|
4796c8b7f9 | ||
|
|
cf03307b70 | ||
|
|
dfed2184f1 | ||
|
|
2ee5127748 | ||
|
|
340a469153 | ||
|
|
934e769513 | ||
|
|
62ae6ca35e | ||
|
|
711a4fe0c8 | ||
|
|
cba07157d5 | ||
|
|
a653d67aae | ||
|
|
249e6130fd | ||
|
|
6a142bc264 | ||
|
|
8bcdd89796 | ||
|
|
8d265a8123 | ||
|
|
e6a540660d | ||
|
|
12bed8e8ad | ||
|
|
5305095832 | ||
|
|
e4b9cbcb80 | ||
|
|
d4198659b7 | ||
|
|
ef1edc7ee8 | ||
|
|
3ccfb74028 |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -15,3 +15,5 @@ install_manifest.txt
|
||||
build*
|
||||
Build
|
||||
CMakeLists.txt.user
|
||||
3rdparty/expat_2.2.6/
|
||||
nbproject
|
||||
|
||||
2
3rdparty/expat/CMakeLists.txt
vendored
2
3rdparty/expat/CMakeLists.txt
vendored
@@ -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
|
||||
|
||||
2
3rdparty/expat/xmlparse.c
vendored
2
3rdparty/expat/xmlparse.c
vendored
@@ -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 */
|
||||
|
||||
2
3rdparty/expat/xmlrole.c
vendored
2
3rdparty/expat/xmlrole.c
vendored
@@ -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"
|
||||
|
||||
2
3rdparty/expat/xmltok.c
vendored
2
3rdparty/expat/xmltok.c
vendored
@@ -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"
|
||||
|
||||
6
3rdparty/udns/udns_init.c
vendored
6
3rdparty/udns/udns_init.c
vendored
@@ -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...
|
||||
|
||||
8
3rdparty/udns/udns_resolver.c
vendored
8
3rdparty/udns/udns_resolver.c
vendored
@@ -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)
|
||||
|
||||
2
3rdparty/udns/udns_rr_a.c
vendored
2
3rdparty/udns/udns_rr_a.c
vendored
@@ -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
|
||||
|
||||
105
CMakeLists.txt
105
CMakeLists.txt
@@ -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()
|
||||
|
||||
|
||||
@@ -32,6 +35,7 @@ include (GenerateExportHeader)
|
||||
|
||||
# only relevant for building shared libs but let's set it regardless
|
||||
set(CMAKE_OSX_RPATH 1)
|
||||
set(CMAKE_OSX_DEPLOYMENT_TARGET "10.9" CACHE STRING "Minimum OS X deployment version")
|
||||
|
||||
# let's use & require C++11 - note these are only functional with CMake 3.1
|
||||
# we do manual fallbacks for CMake 3.0 in the compilers section
|
||||
@@ -44,12 +48,7 @@ string(STRIP ${versionFile} SIMGEAR_VERSION)
|
||||
|
||||
project(SimGear VERSION ${SIMGEAR_VERSION} LANGUAGES C CXX)
|
||||
|
||||
# using 10.7 because boost requires libc++ and 10.6 doesn't include it
|
||||
# Cmake documentation says we must set this before calling project(), but
|
||||
# it only seems to be picked up setting it /after/ the call to project()
|
||||
set(CMAKE_OSX_DEPLOYMENT_TARGET "10.7")
|
||||
|
||||
# add a dependency on the versino file
|
||||
# add a dependency on the version file
|
||||
set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS version)
|
||||
|
||||
set_property(GLOBAL PROPERTY FIND_LIBRARY_USE_LIB64_PATHS TRUE)
|
||||
@@ -126,13 +125,19 @@ option(ENABLE_RTI "Set to ON to build SimGear with RTI support" OFF)
|
||||
option(ENABLE_GDAL "Set to ON to build SimGear with GDAL support" OFF)
|
||||
option(ENABLE_TESTS "Set to OFF to disable building SimGear's test applications" ON)
|
||||
option(ENABLE_SOUND "Set to OFF to disable building SimGear's sound support" ON)
|
||||
option(USE_AEONWAVE "Set to ON to use AeonWave instead of OpenAL" OFF)
|
||||
option(USE_AEONWAVE "Set to ON to use AeonWave instead of OpenAL" ON)
|
||||
option(ENABLE_PKGUTIL "Set to ON to build the sg_pkgutil application (default)" ON)
|
||||
option(ENABLE_DNS "Set to ON to use udns library and DNS service resolver" ON)
|
||||
option(ENABLE_SIMD "Enable SSE/SSE2 support for x86 compilers" ON)
|
||||
option(ENABLE_SIMD "Enable SSE/SSE2 support for compilers" ON)
|
||||
option(ENABLE_SIMD_CODE "Enable SSE/SSE2 support code for compilers" OFF)
|
||||
option(ENABLE_OPENMP "Enable OpenMP compiler support" OFF)
|
||||
|
||||
if (NOT ENABLE_SIMD AND ENABLE_SIMD_CODE)
|
||||
set(ENABLE_SIMD_CODE OFF)
|
||||
endif()
|
||||
|
||||
include (DetectArch)
|
||||
include (ExportDebugSymbols)
|
||||
|
||||
# until the fstream fix is applied and generally available in OSG,
|
||||
# keep the compatability link option as the default
|
||||
@@ -149,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)
|
||||
@@ -160,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)
|
||||
@@ -167,14 +177,24 @@ endif (MSVC)
|
||||
if (MSVC AND MSVC_3RDPARTY_ROOT)
|
||||
message(STATUS "3rdparty files located in ${MSVC_3RDPARTY_ROOT}")
|
||||
|
||||
string(SUBSTRING ${MSVC_VERSION} 0 2 MSVC_VERSION_MAJOR)
|
||||
string(SUBSTRING ${MSVC_VERSION} 2 2 MSVC_VERSION_MINOR)
|
||||
|
||||
set( OSG_MSVC "msvc" )
|
||||
if (${MSVC_VERSION} EQUAL 1900)
|
||||
if (${MSVC_VERSION_MAJOR} EQUAL "19")
|
||||
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 )
|
||||
elseif (${MSVC_VERSION} EQUAL 1800)
|
||||
set( OSG_MSVC ${OSG_MSVC}120 )
|
||||
endif ()
|
||||
elseif (${MSVC_VERSION_MAJOR} EQUAL "18")
|
||||
set( OSG_MSVC ${OSG_MSVC}120 )
|
||||
else ()
|
||||
message(FATAL_ERROR "Visual Studio 2013/2015 is required now")
|
||||
message(FATAL_ERROR "Visual Studio 2013 or higher is required")
|
||||
endif ()
|
||||
|
||||
if (CMAKE_CL_64)
|
||||
set( OSG_MSVC ${OSG_MSVC}-64 )
|
||||
set( MSVC_3RDPARTY_DIR 3rdParty.x64 )
|
||||
@@ -189,16 +209,13 @@ 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()
|
||||
|
||||
if (NOT USE_AEONWAVE)
|
||||
set (OPENAL_INCLUDE_DIR ${MSVC_3RDPARTY_ROOT}/${MSVC_3RDPARTY_DIR}/include)
|
||||
set (OPENAL_LIBRARY_DIR ${MSVC_3RDPARTY_ROOT}/${MSVC_3RDPARTY_DIR}/lib)
|
||||
message(STATUS "OPENAL_INCLUDE_DIR is ${OPENAL_INCLUDE_DIR}")
|
||||
endif()
|
||||
set (OPENAL_INCLUDE_DIR ${MSVC_3RDPARTY_ROOT}/${MSVC_3RDPARTY_DIR}/include)
|
||||
set (OPENAL_LIBRARY_DIR ${MSVC_3RDPARTY_ROOT}/${MSVC_3RDPARTY_DIR}/lib)
|
||||
endif (MSVC AND MSVC_3RDPARTY_ROOT)
|
||||
|
||||
if(APPLE)
|
||||
@@ -210,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)
|
||||
@@ -222,12 +239,19 @@ else()
|
||||
|
||||
if (ENABLE_SOUND)
|
||||
if (USE_AEONWAVE)
|
||||
find_package(AAX COMPONENTS aax REQUIRED)
|
||||
else()
|
||||
find_package(AAX)
|
||||
endif()
|
||||
|
||||
if(NOT AAX_FOUND)
|
||||
set(USE_AEONWAVE FALSE)
|
||||
find_package(OpenAL REQUIRED)
|
||||
endif()
|
||||
|
||||
message(STATUS "Sound support: ENABLED")
|
||||
if(AAX_FOUND)
|
||||
message(STATUS "Sound support: AeonWave")
|
||||
else()
|
||||
message(STATUS "Sound support: OpenAL")
|
||||
endif()
|
||||
endif(ENABLE_SOUND)
|
||||
|
||||
find_package(OpenSceneGraph 3.2.0 REQUIRED osgText osgSim osgDB osgParticle osgGA osgViewer osgUtil)
|
||||
@@ -258,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)
|
||||
@@ -279,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")
|
||||
@@ -397,10 +417,15 @@ if(CMAKE_COMPILER_IS_GNUCXX)
|
||||
message(WARNING "GCC 4.4 will be required soon, please upgrade")
|
||||
endif()
|
||||
|
||||
if(ENABLE_SIMD)
|
||||
if (CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||
set(CMAKE_C_FLAGS
|
||||
"${CMAKE_C_FLAGS} -O0 -fno-omit-frame-pointer -fno-inline")
|
||||
set(CMAKE_CXX_FLAGS
|
||||
"${CMAKE_CXX_FLAGS} -O0 -fno-omit-frame-pointer -fno-inline")
|
||||
elseif (ENABLE_SIMD)
|
||||
if (X86 OR X86_64)
|
||||
set(CMAKE_C_FLAGS_RELEASE "-O3 -msse2 -mfpmath=sse")
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "-O3 -msse2 -mfpmath=sse")
|
||||
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")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
@@ -420,10 +445,18 @@ if (CLANG)
|
||||
# fix Boost compilation :(
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++")
|
||||
|
||||
if(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")
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "-O3 -msse2 -mfpmath=sse")
|
||||
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()
|
||||
@@ -498,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
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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
|
||||
|
||||
26
CMakeModules/ExportDebugSymbols.cmake
Normal file
26
CMakeModules/ExportDebugSymbols.cmake
Normal 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()
|
||||
@@ -1,61 +1,74 @@
|
||||
# Locate AAX
|
||||
# Try to find AAX (AeonWave)
|
||||
# This module defines
|
||||
# AAX_LIBRARIES
|
||||
# AAX_FOUND, if false, do not try to link to AAX
|
||||
# AAX_INCLUDE_DIR, where to find the headers
|
||||
#
|
||||
# AAX_FOUND - if false, do not try to link to AAX
|
||||
# AAX_INCLUDE_DIR - where to find the headers
|
||||
# AAX_LIBRARIES - Link these to use AAX
|
||||
#
|
||||
# Copyright (C) 2016-2018 by Erik Hofman.
|
||||
# Copyright (C) 2016-2018 by Adalin B.V.
|
||||
#
|
||||
# $AAXDIR is an environment variable that would
|
||||
# correspond to the ./configure --prefix=$AAXDIR
|
||||
# used in building AAX.
|
||||
#
|
||||
# Created by Erik Hofman.
|
||||
# This file is Public Domain (www.unlicense.org)
|
||||
# This is free and unencumbered software released into the public domain.
|
||||
|
||||
FIND_PATH(AAX_INCLUDE_DIR aax/aax.h
|
||||
HINTS
|
||||
$ENV{AAXDIR}
|
||||
$ENV{ProgramFiles}/aax
|
||||
$ENV{ProgramFiles}/AeonWave
|
||||
$ENV{ProgramFiles}/Adalin/AeonWave
|
||||
${CMAKE_SOURCE_DIR}/aax
|
||||
PATH_SUFFIXES include
|
||||
PATHS
|
||||
~/Library/Frameworks
|
||||
/Library/Frameworks
|
||||
/usr/local
|
||||
/usr
|
||||
/opt
|
||||
)
|
||||
if (AAX_LIBRARY AND AAX_INCLUDE_DIR)
|
||||
# in cache already
|
||||
set(AAX_FOUND TRUE)
|
||||
else()
|
||||
find_path(AAX_INCLUDE_DIR aax/aax.h
|
||||
HINTS
|
||||
$ENV{AAXDIR}
|
||||
$ENV{ProgramFiles}/aax
|
||||
$ENV{ProgramFiles}/AeonWave
|
||||
$ENV{ProgramFiles}/Adalin/AeonWave
|
||||
${CMAKE_SOURCE_DIR}/aax
|
||||
PATH_SUFFIXES include
|
||||
PATHS
|
||||
~/Library/Frameworks
|
||||
/Library/Frameworks
|
||||
/usr/local
|
||||
/usr
|
||||
/opt
|
||||
)
|
||||
|
||||
FIND_LIBRARY(AAX_LIBRARY
|
||||
NAMES AAX aax AAX32
|
||||
HINTS
|
||||
$ENV{AAXDIR}
|
||||
$ENV{ProgramFiles}/AAX
|
||||
$ENV{ProgramFiles}/AeonWave
|
||||
$ENV{ProgramFiles}/Adalin/AeonWave
|
||||
${CMAKE_BUILD_DIR}/aax
|
||||
PATH_SUFFIXES bin lib lib/${CMAKE_LIBRARY_ARCHITECTURE} lib64 libs64 libs libs/Win32 libs/Win64
|
||||
PATHS
|
||||
~/Library/Frameworks
|
||||
/Library/Frameworks
|
||||
/usr/local
|
||||
/usr
|
||||
/opt
|
||||
)
|
||||
find_library(AAX_LIBRARY
|
||||
NAMES AAX aax libAAX
|
||||
HINTS
|
||||
$ENV{AAXDIR}
|
||||
$ENV{ProgramFiles}/AAX
|
||||
$ENV{ProgramFiles}/AeonWave
|
||||
$ENV{ProgramFiles}/Adalin/AeonWave
|
||||
${CMAKE_BUILD_DIR}/aax
|
||||
PATH_SUFFIXES lib64 lib lib/${CMAKE_LIBRARY_ARCHITECTURE} libs64 libs libs/Win32 libs/Win64 bin
|
||||
PATHS
|
||||
~/Library/Frameworks
|
||||
/Library/Frameworks
|
||||
/usr/local
|
||||
/usr
|
||||
/opt
|
||||
)
|
||||
|
||||
IF(AAX_LIBRARY AND AAX_INCLUDE_DIR)
|
||||
SET(AAX_FOUND "YES")
|
||||
ELSE(AAX_LIBRARY AND AAX_INCLUDE_DIR)
|
||||
IF(NOT AAX_INCLUDE_DIR)
|
||||
MESSAGE(FATAL_ERROR "Unable to find the AAX library development files.")
|
||||
SET(AAX_FOUND "NO")
|
||||
ENDIF(NOT AAX_INCLUDE_DIR)
|
||||
IF(NOT AAX_LIBRARY)
|
||||
IF(SINGLE_PACKAGE)
|
||||
SET(AAX_LIBRARY "${aax_BUILD_DIR}/aax/AAX32.dll")
|
||||
SET(AAX_FOUND "YES")
|
||||
ELSE(SINGLE_PACKAGE)
|
||||
ENDIF(SINGLE_PACKAGE)
|
||||
ENDIF(NOT AAX_LIBRARY)
|
||||
ENDIF(AAX_LIBRARY AND AAX_INCLUDE_DIR)
|
||||
set(AAX_DEFINITIONS "")
|
||||
if (AAX_LIBRARY AND AAX_INCLUDE_DIR)
|
||||
set(AAX_FOUND TRUE)
|
||||
endif()
|
||||
|
||||
if (AAX_FOUND)
|
||||
if (NOT Udns_FIND_QUIETLY)
|
||||
message(STATUS "Found AeonWave: ${AAX_LIBRARIES}")
|
||||
endif ()
|
||||
else ()
|
||||
if (Udns_FIND_REQUIRED)
|
||||
message(FATAL_ERROR "Could not find AeonWave")
|
||||
endif ()
|
||||
endif ()
|
||||
|
||||
# show the AAX_INCLUDE_DIRS and AAX_LIBRARIES variables only in the advanced view
|
||||
mark_as_advanced(AAX_INCLUDE_DIRS AAX_LIBRARIES)
|
||||
|
||||
endif()
|
||||
|
||||
|
||||
@@ -12,6 +12,11 @@ macro(simgear_component_common name includePath sourcesList sources headers)
|
||||
set_property(GLOBAL
|
||||
APPEND PROPERTY PUBLIC_HEADERS "${CMAKE_CURRENT_SOURCE_DIR}/${h}")
|
||||
set(fh${sourcesList} "${fh${sourcesList}}#${CMAKE_CURRENT_SOURCE_DIR}/${h}")
|
||||
|
||||
# also append headers to the sources list, so that IDEs find the files
|
||||
# correctly (otherwise they are not in the project)
|
||||
set_property(GLOBAL
|
||||
APPEND PROPERTY ${sourcesList} "${CMAKE_CURRENT_SOURCE_DIR}/${h}")
|
||||
endforeach()
|
||||
|
||||
set_property(GLOBAL APPEND PROPERTY FG_GROUPS_${sourcesList}_C "${fc${sourcesList}}@")
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
[This file is mirrored in both the FlightGear and SimGear packages.]
|
||||
|
||||
You *must* have the development components of OpenAL installed on your system
|
||||
to build FlightGear!" You can get a copy here:
|
||||
|
||||
http://connect.creativelabs.com/openal/default.aspx
|
||||
|
||||
Build notes:
|
||||
|
||||
You can download a versioned release of the openal library from
|
||||
http://www.openal.org/downloads.html. Download the openal source,
|
||||
release 0.0.8 (dated February 11, 2006) and run:
|
||||
tar xjvf openal-soft-1.5.304.tar.bz2
|
||||
cd openal-soft-1.5.304/
|
||||
ccmake .
|
||||
|
||||
[ While running ccmake: press 'c' to configure, press 'c' once more, and
|
||||
then press 'g' to generate and exit ]
|
||||
|
||||
|
||||
39
README.sound
Normal file
39
README.sound
Normal file
@@ -0,0 +1,39 @@
|
||||
[This file is mirrored in both the FlightGear and SimGear packages.]
|
||||
|
||||
For Sound support FlightGear requires one of the two following packages:
|
||||
- OpenAL
|
||||
- AeonWave
|
||||
|
||||
== OpenAL ===
|
||||
|
||||
You *must* have the development components of OpenAL installed on your system
|
||||
to build FlightGear!" You can get a copy here:
|
||||
|
||||
http://connect.creativelabs.com/openal/default.aspx
|
||||
|
||||
Build notes:
|
||||
|
||||
You can download a versioned release of the openal library from
|
||||
http://www.openal.org/downloads.html. Download the openal source,
|
||||
release 0.0.8 (dated February 11, 2006) and run:
|
||||
tar xjvf openal-soft-1.5.304.tar.bz2
|
||||
cd openal-soft-1.5.304/
|
||||
ccmake .
|
||||
|
||||
[ While running ccmake: press 'c' to configure, press 'c' once more, and
|
||||
then press 'g' to generate and exit ]
|
||||
|
||||
|
||||
== AeonWave ===
|
||||
|
||||
For FlightGear AeonWave has a number of advantages over OpenAL:
|
||||
* Correct Doppler effect behavior
|
||||
* Default distance attenuation frequency filtering
|
||||
* Native support for 29 types of audio formats.
|
||||
* Native support for wav, mp3, vorbis and raw file formats.
|
||||
|
||||
The source code of AeonWave can be found on GitHub:
|
||||
https://github.com/adalinbv
|
||||
|
||||
Optimized binary packages are available at:
|
||||
http://www.adalin.com/
|
||||
@@ -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@)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
// The canvas for rendering with the 2d API
|
||||
///@file
|
||||
/// The canvas for rendering with the 2d API
|
||||
//
|
||||
// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
|
||||
//
|
||||
@@ -38,6 +39,75 @@ namespace simgear
|
||||
{
|
||||
namespace canvas
|
||||
{
|
||||
static int globalinstanceid = 1;
|
||||
/**
|
||||
* Camera Callback for moving completed canvas images to subscribed listener.
|
||||
*/
|
||||
class CanvasImageCallback : public osg::Camera::DrawCallback {
|
||||
public:
|
||||
osg::Image *_rawImage;
|
||||
|
||||
CanvasImageCallback(osg::Image *rawImage)
|
||||
: _min_delta_tick(1.0 / 8.0) {
|
||||
_previousFrameTick = osg::Timer::instance()->tick();
|
||||
_rawImage = rawImage;
|
||||
SG_LOG(SG_GENERAL,SG_INFO,"CanvasImageCallback created. instance is " << instanceid);
|
||||
}
|
||||
|
||||
virtual void operator()(osg::RenderInfo& renderInfo) const {
|
||||
osg::Timer_t n = osg::Timer::instance()->tick();
|
||||
double dt = osg::Timer::instance()->delta_s(_previousFrameTick, n);
|
||||
if (dt < _min_delta_tick)
|
||||
return;
|
||||
_previousFrameTick = n;
|
||||
SG_LOG(SG_GENERAL,SG_DEBUG,"CanvasImageCallback " << instanceid << ": image available for " << _subscribers.size() << " subscribers. camera is " << renderInfo.getCurrentCamera());
|
||||
|
||||
bool hasSubscribers = false;
|
||||
{
|
||||
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_lock);
|
||||
hasSubscribers = !_subscribers.empty();
|
||||
}
|
||||
if (hasSubscribers) {
|
||||
//Make sure image can be overwritten by next frame while it is still returned to the client
|
||||
osg::Image* image = new osg::Image(*_rawImage, osg::CopyOp::DEEP_COPY_ALL);
|
||||
{
|
||||
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_lock);
|
||||
while (!_subscribers.empty()) {
|
||||
try {
|
||||
CanvasImageReadyListener *subs = _subscribers.back();
|
||||
if (subs){
|
||||
subs->imageReady(image);
|
||||
}else{
|
||||
SG_LOG(SG_GENERAL,SG_WARN,"CanvasImageCallback subscriber null");
|
||||
}
|
||||
} catch (...) { }
|
||||
_subscribers.pop_back();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void subscribe(CanvasImageReadyListener * subscriber) {
|
||||
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_lock);
|
||||
_subscribers.push_back(subscriber);
|
||||
}
|
||||
|
||||
void unsubscribe(CanvasImageReadyListener * subscriber) {
|
||||
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_lock);
|
||||
_subscribers.remove(subscriber);
|
||||
}
|
||||
|
||||
int getSubscriberCount() {
|
||||
return _subscribers.size();
|
||||
}
|
||||
|
||||
private:
|
||||
mutable list<CanvasImageReadyListener*> _subscribers;
|
||||
mutable OpenThreads::Mutex _lock;
|
||||
mutable double _previousFrameTick;
|
||||
double _min_delta_tick;
|
||||
int instanceid = globalinstanceid++;
|
||||
};
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
Canvas::CullCallback::CullCallback(const CanvasWeakPtr& canvas):
|
||||
@@ -247,6 +317,21 @@ namespace canvas
|
||||
|
||||
osg::Camera* camera = _texture.getCamera();
|
||||
|
||||
string canvasname = _node->getStringValue("name");
|
||||
int renderToImage = _node->getBoolValue("render-to-image");
|
||||
|
||||
if (renderToImage){
|
||||
CanvasImageCallback *_screenshotCallback = dynamic_cast<CanvasImageCallback*> (camera->getFinalDrawCallback());
|
||||
if (!_screenshotCallback) {
|
||||
// no draw callback yet
|
||||
osg::Image* shot = new osg::Image();
|
||||
shot->allocateImage(getSizeX(), getSizeY(), 24, GL_RGB, GL_UNSIGNED_BYTE);
|
||||
camera->attach(osg::Camera::COLOR_BUFFER, shot);
|
||||
camera->setFinalDrawCallback(new CanvasImageCallback(shot));
|
||||
SG_LOG(SG_GENERAL,SG_INFO,"CanvasImage: attached image and draw callback to camera " << camera << " for canvas " << canvasname << ". Ready for subscriber now.");
|
||||
}
|
||||
}
|
||||
|
||||
// TODO Allow custom render order? For now just keep in order with
|
||||
// property tree.
|
||||
camera->setRenderOrder(osg::Camera::PRE_RENDER, _node->getIndex());
|
||||
@@ -346,6 +431,41 @@ namespace canvas
|
||||
}
|
||||
}
|
||||
|
||||
int Canvas::subscribe(CanvasImageReadyListener * subscriber) {
|
||||
osg::Camera* camera = _texture.getCamera();
|
||||
const string canvasname = _node->getStringValue("name");
|
||||
|
||||
SG_LOG(SG_GENERAL,SG_DEBUG,"CanvasImage: subscribe to canvas " << canvasname.c_str() << ", camera ="<< camera);
|
||||
|
||||
if (!_node->getBoolValue("render-to-image")) {
|
||||
SG_LOG(SG_GENERAL,SG_INFO,"CanvasImage: Setting render-to-image");
|
||||
_node->addChild("render-to-image", 0)->setBoolValue(1);
|
||||
setStatusFlags(STATUS_DIRTY, true);
|
||||
}
|
||||
|
||||
CanvasImageCallback *_screenshotCallback = dynamic_cast<CanvasImageCallback*> (camera->getFinalDrawCallback());
|
||||
if (_screenshotCallback) {
|
||||
// Camera ready for subscriber. Otherwise, draw callback is created by canvas thread later.
|
||||
SG_LOG(SG_GENERAL,SG_DEBUG,"CanvasImage: adding subscriber to camera draw callback");
|
||||
_screenshotCallback->subscribe(subscriber);
|
||||
// TODO: check: Is this the correct way to ensure the canvas will be available?
|
||||
enableRendering(true);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Canvas::unsubscribe(CanvasImageReadyListener * subscriber) {
|
||||
osg::Camera* camera = _texture.getCamera();
|
||||
SG_LOG(SG_GENERAL,SG_DEBUG,"CanvasImage: unsubscribe");
|
||||
CanvasImageCallback *cb = dynamic_cast<CanvasImageCallback*> (camera->getFinalDrawCallback());
|
||||
if (cb) {
|
||||
SG_LOG(SG_GENERAL,SG_DEBUG,"CanvasImage: unsubscribe from camera " << camera);
|
||||
cb->unsubscribe(subscriber);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
bool Canvas::addEventListener( const std::string& type,
|
||||
const EventListener& cb )
|
||||
|
||||
@@ -44,6 +44,17 @@ namespace canvas
|
||||
class CanvasMgr;
|
||||
class MouseEvent;
|
||||
|
||||
/**
|
||||
* A listener interested in completed canvas drawing.
|
||||
*/
|
||||
class CanvasImageReadyListener {
|
||||
public:
|
||||
virtual void imageReady(osg::ref_ptr<osg::Image>) = 0;
|
||||
virtual ~CanvasImageReadyListener()
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Canvas to draw onto (to an off-screen render target).
|
||||
*/
|
||||
@@ -71,18 +82,18 @@ namespace canvas
|
||||
public osg::NodeCallback
|
||||
{
|
||||
public:
|
||||
CullCallback(const CanvasWeakPtr& canvas);
|
||||
explicit CullCallback(const CanvasWeakPtr& canvas);
|
||||
|
||||
private:
|
||||
CanvasWeakPtr _canvas;
|
||||
|
||||
virtual void operator()(osg::Node* node, osg::NodeVisitor* nv);
|
||||
void operator()(osg::Node* node, osg::NodeVisitor* nv) override;
|
||||
};
|
||||
typedef osg::ref_ptr<CullCallback> CullCallbackPtr;
|
||||
|
||||
Canvas(SGPropertyNode* node);
|
||||
explicit Canvas(SGPropertyNode* node);
|
||||
virtual ~Canvas();
|
||||
virtual void onDestroy();
|
||||
void onDestroy() override;
|
||||
|
||||
void setCanvasMgr(CanvasMgr* canvas_mgr);
|
||||
CanvasMgr* getCanvasMgr() const;
|
||||
@@ -160,7 +171,12 @@ namespace canvas
|
||||
*/
|
||||
void enableRendering(bool force = false);
|
||||
|
||||
void update(double delta_time_sec);
|
||||
void update(double delta_time_sec) override;
|
||||
|
||||
osg::Camera* getCamera();
|
||||
int subscribe(CanvasImageReadyListener * subscriber);
|
||||
int unsubscribe(CanvasImageReadyListener * subscriber);
|
||||
int getSubscriberCount();
|
||||
|
||||
bool addEventListener(const std::string& type, const EventListener& cb);
|
||||
bool dispatchEvent(const EventPtr& event);
|
||||
@@ -184,11 +200,9 @@ namespace canvas
|
||||
bool propagateEvent( EventPtr const& event,
|
||||
EventPropagationPath const& path );
|
||||
|
||||
virtual void childAdded( SGPropertyNode * parent,
|
||||
SGPropertyNode * child );
|
||||
virtual void childRemoved( SGPropertyNode * parent,
|
||||
SGPropertyNode * child );
|
||||
virtual void valueChanged (SGPropertyNode * node);
|
||||
void childAdded(SGPropertyNode* parent, SGPropertyNode* child) override;
|
||||
void childRemoved(SGPropertyNode* parent, SGPropertyNode* child) override;
|
||||
void valueChanged(SGPropertyNode * node) override;
|
||||
|
||||
osg::Texture2D* getTexture() const;
|
||||
|
||||
@@ -254,8 +268,8 @@ namespace canvas
|
||||
|
||||
static SystemAdapterPtr _system_adapter;
|
||||
|
||||
Canvas(const Canvas&); // = delete;
|
||||
Canvas& operator=(const Canvas&); // = delete;
|
||||
Canvas(const Canvas&) = delete;
|
||||
Canvas& operator=(const Canvas&) = delete;
|
||||
};
|
||||
|
||||
} // namespace canvas
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
// Canvas Event for event model similar to DOM Level 3 Event Model
|
||||
///@file
|
||||
/// Canvas Event for event model similar to DOM Level 3 Event Model
|
||||
//
|
||||
// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
|
||||
//
|
||||
@@ -125,10 +126,10 @@ namespace canvas
|
||||
//----------------------------------------------------------------------------
|
||||
std::string Event::typeToStr(int type)
|
||||
{
|
||||
TypeMap const& type_map = getTypeMap();
|
||||
auto const& map_by_id = getTypeMap().by<id>();
|
||||
|
||||
TypeMap::map_by<id>::const_iterator it = type_map.by<id>().find(type);
|
||||
if( it == type_map.by<id>().end() )
|
||||
auto it = map_by_id.find(type);
|
||||
if( it == map_by_id.end() )
|
||||
return "unknown";
|
||||
return it->second;
|
||||
}
|
||||
|
||||
@@ -65,6 +65,11 @@ namespace canvas
|
||||
// of the actual event instances.
|
||||
virtual ~Event();
|
||||
|
||||
/**
|
||||
* Clone event and set to the given type (Same type if not specified)
|
||||
*/
|
||||
virtual Event* clone(int type = 0) const = 0;
|
||||
|
||||
/**
|
||||
* Get whether this events support bubbling
|
||||
*/
|
||||
@@ -110,7 +115,14 @@ namespace canvas
|
||||
*/
|
||||
bool defaultPrevented() const;
|
||||
|
||||
/**
|
||||
* Register a new type string or get the id of an existing type string
|
||||
*
|
||||
* @param type Type string
|
||||
* @return Id of the given @a type
|
||||
*/
|
||||
static int getOrRegisterType(const std::string& type);
|
||||
|
||||
static int strToType(const std::string& type);
|
||||
static std::string typeToStr(int type);
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
// Manage event handling inside a Canvas similar to the DOM Level 3 Event Model
|
||||
///@file
|
||||
/// Manage event handling inside a Canvas similar to the DOM Level 3 Event Model
|
||||
//
|
||||
// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
|
||||
//
|
||||
@@ -17,9 +18,11 @@
|
||||
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#include <simgear_config.h>
|
||||
|
||||
#include "CanvasEventManager.hxx"
|
||||
#include <simgear/canvas/events/MouseEvent.hxx>
|
||||
#include <simgear/canvas/elements/CanvasElement.hxx>
|
||||
#include "elements/CanvasElement.hxx"
|
||||
#include "events/MouseEvent.hxx"
|
||||
|
||||
#include <cmath>
|
||||
|
||||
namespace simgear
|
||||
@@ -114,6 +117,8 @@ namespace canvas
|
||||
return handled;
|
||||
}
|
||||
case Event::DRAG:
|
||||
case Event::DRAG_START:
|
||||
case Event::DRAG_END:
|
||||
if( !_last_mouse_down.valid() )
|
||||
return false;
|
||||
else
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
// Manage event handling inside a Canvas similar to the DOM Level 3 Event Model
|
||||
///@file
|
||||
/// Manage event handling inside a Canvas similar to the DOM Level 3 Event Model
|
||||
//
|
||||
// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
|
||||
//
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
// Mapping between canvas gui Event types and their names
|
||||
///@file
|
||||
/// Mapping between canvas gui Event types and their names
|
||||
//
|
||||
// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
|
||||
//
|
||||
@@ -25,6 +26,8 @@ ENUM_MAPPING(MOUSE_UP, "mouseup", MouseEvent)
|
||||
ENUM_MAPPING(CLICK, "click", MouseEvent)
|
||||
ENUM_MAPPING(DBL_CLICK, "dblclick", MouseEvent)
|
||||
ENUM_MAPPING(DRAG, "drag", MouseEvent)
|
||||
ENUM_MAPPING(DRAG_START, "dragstart", MouseEvent)
|
||||
ENUM_MAPPING(DRAG_END, "dragend", MouseEvent)
|
||||
ENUM_MAPPING(WHEEL, "wheel", MouseEvent)
|
||||
ENUM_MAPPING(MOUSE_MOVE, "mousemove", MouseEvent)
|
||||
ENUM_MAPPING(MOUSE_OVER, "mouseover", MouseEvent)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
// Visitor for traversing a canvas element hierarchy similar to the traversal
|
||||
// of the DOM Level 3 Event Model
|
||||
///@file
|
||||
/// Visitor for traversing a canvas element hierarchy similar to the traversal
|
||||
/// of the DOM Level 3 Event Model
|
||||
//
|
||||
// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
|
||||
//
|
||||
@@ -18,9 +19,10 @@
|
||||
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#include <simgear_config.h>
|
||||
|
||||
#include "CanvasEvent.hxx"
|
||||
#include "CanvasEventVisitor.hxx"
|
||||
#include <simgear/canvas/elements/CanvasElement.hxx>
|
||||
#include "elements/CanvasElement.hxx"
|
||||
|
||||
namespace simgear
|
||||
{
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
// Visitor for traversing a canvas element hierarchy similar to the traversal
|
||||
// of the DOM Level 3 Event Model
|
||||
///@file
|
||||
/// Visitor for traversing a canvas element hierarchy similar to the traversal
|
||||
/// of the DOM Level 3 Event Model
|
||||
//
|
||||
// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
|
||||
//
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
// Canvas with 2D rendering API
|
||||
///@file
|
||||
/// Canvas with 2D rendering API
|
||||
//
|
||||
// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
|
||||
//
|
||||
@@ -17,12 +18,11 @@
|
||||
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#include <simgear_config.h>
|
||||
|
||||
#include "CanvasMgr.hxx"
|
||||
#include "Canvas.hxx"
|
||||
#include "CanvasEventManager.hxx"
|
||||
|
||||
#include <boost/bind.hpp>
|
||||
|
||||
namespace simgear
|
||||
{
|
||||
namespace canvas
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
// Canvas with 2D rendering API
|
||||
///@file
|
||||
/// Canvas with 2D rendering API
|
||||
//
|
||||
// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
|
||||
//
|
||||
@@ -27,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:
|
||||
|
||||
virtual void elementCreated(PropertyBasedElementPtr element);
|
||||
};
|
||||
protected:
|
||||
void elementCreated(PropertyBasedElementPtr element) override;
|
||||
};
|
||||
|
||||
} // namespace canvas
|
||||
} // namespace simgear
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
// Canvas placement for placing a canvas texture onto osg objects.
|
||||
///@file
|
||||
/// Canvas placement for placing a canvas texture onto osg objects
|
||||
//
|
||||
// It also provides a SGPickCallback for passing mouse events to the canvas and
|
||||
// manages emissive lighting of the placed canvas.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
///@file
|
||||
/// Placement for putting a canvas texture onto OpenSceneGraph objects.
|
||||
/// Placement for putting a canvas texture onto OpenSceneGraph objects
|
||||
///
|
||||
/// It also provides a SGPickCallback for passing mouse events to the canvas and
|
||||
/// manages emissive lighting of the placed canvas.
|
||||
@@ -60,7 +60,7 @@ namespace canvas
|
||||
*/
|
||||
void setCaptureEvents(bool enable);
|
||||
|
||||
virtual bool childChanged(SGPropertyNode* child);
|
||||
bool childChanged(SGPropertyNode* child) override;
|
||||
|
||||
protected:
|
||||
typedef SGSharedPtr<SGPickCallback> PickCallbackPtr;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
// Base class for canvas placements
|
||||
///@file
|
||||
/// Base class for canvas placements
|
||||
//
|
||||
// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
|
||||
//
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
// Base class for canvas placements
|
||||
///@file
|
||||
/// Base class for canvas placements
|
||||
//
|
||||
// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
|
||||
//
|
||||
@@ -40,9 +41,8 @@ namespace canvas
|
||||
protected:
|
||||
SGPropertyNode_ptr _node;
|
||||
|
||||
private:
|
||||
Placement(const Placement&) /* = delete */;
|
||||
Placement& operator=(const Placement&) /* = delete */;
|
||||
Placement(const Placement&) = delete;
|
||||
Placement& operator=(const Placement&) = delete;
|
||||
};
|
||||
|
||||
} // namespace canvas
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
// Adapter for using the canvas with different applications
|
||||
///@file
|
||||
/// Adapter for using the canvas with different applications
|
||||
//
|
||||
// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
|
||||
//
|
||||
@@ -29,6 +30,10 @@ namespace HTTP { class Client; }
|
||||
namespace canvas
|
||||
{
|
||||
|
||||
/**
|
||||
* Provides access to different required systems of the application to the
|
||||
* Canvas
|
||||
*/
|
||||
class SystemAdapter
|
||||
{
|
||||
public:
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
// Window for placing a Canvas onto it (for dialogs, menus, etc.)
|
||||
///@file
|
||||
/// Window for placing a Canvas onto it (for dialogs, menus, etc.)
|
||||
//
|
||||
// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
|
||||
//
|
||||
@@ -212,6 +213,19 @@ namespace canvas
|
||||
_resize_left = getRegion().l() + offset.x();
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
bool Window::handleEvent(const EventPtr& event)
|
||||
{
|
||||
if( auto mouse_event = dynamic_cast<MouseEvent*>(event.get()) )
|
||||
{
|
||||
mouse_event->local_pos =
|
||||
mouse_event->client_pos =
|
||||
mouse_event->screen_pos - toOsg(getPosition());
|
||||
}
|
||||
|
||||
return Image::handleEvent(event);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Window::parseDecorationBorder(const std::string& str)
|
||||
{
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
// Window for placing a Canvas onto it (for dialogs, menus, etc.)
|
||||
///@file
|
||||
/// Window for placing a Canvas onto it (for dialogs, menus, etc.)
|
||||
//
|
||||
// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
|
||||
//
|
||||
@@ -67,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;
|
||||
@@ -83,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.
|
||||
@@ -96,6 +97,8 @@ namespace canvas
|
||||
void handleResize( uint8_t mode,
|
||||
const osg::Vec2f& offset = osg::Vec2f() );
|
||||
|
||||
bool handleEvent(const EventPtr& event) override;
|
||||
|
||||
protected:
|
||||
|
||||
enum Attributes
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
// Owner Drawn Gauge helper class
|
||||
///@file
|
||||
/// Owner Drawn Gauge helper class
|
||||
//
|
||||
// Written by Harald JOHNSEN, started May 2005.
|
||||
//
|
||||
@@ -6,9 +7,9 @@
|
||||
//
|
||||
// Ported to OSG by Tim Moore - Jun 2007
|
||||
//
|
||||
// Heavily modified to be usable for the 2d Canvas by Thomas Geymayer - April 2012
|
||||
// Supports now multisampling/mipmapping, usage of the stencil buffer and placing
|
||||
// the texture in the scene by certain filter criteria
|
||||
// Heavily modified to be usable for the 2d Canvas by Thomas Geymayer - April
|
||||
// 2012 Supports now multisampling/mipmapping, usage of the stencil buffer and
|
||||
// placing the texture in the scene by certain filter criteria.
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Library General Public
|
||||
@@ -249,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();
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
// Owner Drawn Gauge helper class
|
||||
///@file
|
||||
/// Owner Drawn Gauge helper class
|
||||
//
|
||||
// Written by Harald JOHNSEN, started May 2005.
|
||||
//
|
||||
@@ -6,9 +7,9 @@
|
||||
//
|
||||
// Ported to OSG by Tim Moore - Jun 2007
|
||||
//
|
||||
// Heavily modified to be usable for the 2d Canvas by Thomas Geymayer - April 2012
|
||||
// Supports now multisampling/mipmapping, usage of the stencil buffer and placing
|
||||
// the texture in the scene by certain filter criteria
|
||||
// Heavily modified to be usable for the 2d Canvas by Thomas Geymayer - April
|
||||
// 2012 Supports now multisampling/mipmapping, usage of the stencil buffer and
|
||||
// placing the texture in the scene by certain filter criteria.
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Library General Public
|
||||
|
||||
@@ -31,6 +31,11 @@
|
||||
// FreeBSD
|
||||
#define VG_API_FREEBSD
|
||||
|
||||
#elif defined(__OpenBSD__)
|
||||
|
||||
// FreeBSD
|
||||
#define VG_API_OPENBSD
|
||||
|
||||
#else
|
||||
|
||||
// Unsupported system
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
// osg::Operation to initialize the OpenVG context used for path rendering
|
||||
///@file
|
||||
/// osg::Operation to initialize the OpenVG context used for path rendering
|
||||
//
|
||||
// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
|
||||
//
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
// osg::Operation to initialize the OpenVG context used for path rendering
|
||||
///@file
|
||||
/// osg::Operation to initialize the OpenVG context used for path rendering
|
||||
//
|
||||
// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
|
||||
//
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
// Canvas forward declarations
|
||||
///@file
|
||||
/// Canvas forward declarations
|
||||
//
|
||||
// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
|
||||
//
|
||||
@@ -25,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
|
||||
@@ -61,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)
|
||||
@@ -84,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
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
// Interface for 2D Canvas element
|
||||
///@file
|
||||
/// Interface for 2D Canvas element
|
||||
//
|
||||
// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
|
||||
//
|
||||
@@ -271,8 +272,7 @@ namespace canvas
|
||||
//----------------------------------------------------------------------------
|
||||
void Element::setFocus()
|
||||
{
|
||||
CanvasPtr canvas = _canvas.lock();
|
||||
if( canvas )
|
||||
if( auto canvas = _canvas.lock() )
|
||||
canvas->setFocusElement(this);
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -95,7 +92,7 @@ namespace canvas
|
||||
*
|
||||
*/
|
||||
virtual ~Element() = 0;
|
||||
virtual void onDestroy();
|
||||
void onDestroy() override;
|
||||
|
||||
ElementPtr getParent() const;
|
||||
CanvasWeakPtr getCanvas() const;
|
||||
@@ -105,7 +102,7 @@ namespace canvas
|
||||
*
|
||||
* @param dt Frame time in seconds
|
||||
*/
|
||||
virtual void update(double dt);
|
||||
void update(double dt) override;
|
||||
|
||||
bool addEventListener(const std::string& type, const EventListener& cb);
|
||||
virtual void clearEventListener();
|
||||
@@ -154,11 +151,9 @@ namespace canvas
|
||||
*/
|
||||
osg::Vec2f posToLocal(const osg::Vec2f& pos) const;
|
||||
|
||||
virtual void childAdded( SGPropertyNode * parent,
|
||||
SGPropertyNode * child );
|
||||
virtual void childRemoved( SGPropertyNode * parent,
|
||||
SGPropertyNode * child );
|
||||
virtual void valueChanged(SGPropertyNode * child);
|
||||
void childAdded(SGPropertyNode* parent, SGPropertyNode* child) override;
|
||||
void childRemoved(SGPropertyNode* parent, SGPropertyNode* child) override;
|
||||
void valueChanged(SGPropertyNode* child) override;
|
||||
|
||||
virtual bool setStyle( const SGPropertyNode* child,
|
||||
const StyleInfo* style_info = 0 );
|
||||
@@ -321,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 ];
|
||||
@@ -347,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;
|
||||
}
|
||||
|
||||
@@ -365,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);
|
||||
@@ -386,7 +378,7 @@ namespace canvas
|
||||
(
|
||||
name,
|
||||
type,
|
||||
boost::function<void (Derived&, T)>(setter),
|
||||
std::function<void (Derived&, T)>(setter),
|
||||
inheritable
|
||||
);
|
||||
}
|
||||
@@ -407,7 +399,7 @@ namespace canvas
|
||||
(
|
||||
name,
|
||||
type,
|
||||
boost::function<void (Derived&, T2)>(setter),
|
||||
std::function<void (Derived&, T2)>(setter),
|
||||
inheritable
|
||||
);
|
||||
}
|
||||
@@ -426,7 +418,7 @@ namespace canvas
|
||||
(
|
||||
name,
|
||||
type,
|
||||
boost::function<void (Derived&, const std::string&)>(setter),
|
||||
std::function<void (Derived&, const std::string&)>(setter),
|
||||
inheritable
|
||||
);
|
||||
}
|
||||
@@ -489,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 )
|
||||
{
|
||||
@@ -519,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
|
||||
);
|
||||
@@ -527,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;
|
||||
@@ -597,7 +582,7 @@ namespace canvas
|
||||
|
||||
osg::ref_ptr<osg::Drawable> _drawable;
|
||||
|
||||
Element(const Element&);// = delete
|
||||
Element(const Element&) = delete;
|
||||
|
||||
template<class Derived>
|
||||
static Derived& derived_cast(Element& el)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
// A group of 2D Canvas elements
|
||||
///@file
|
||||
/// A group of 2D Canvas elements
|
||||
//
|
||||
// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
|
||||
//
|
||||
@@ -168,6 +169,7 @@ namespace canvas
|
||||
if( !_scene_group.valid() )
|
||||
return warnSceneGroupExpired("clearEventListener");
|
||||
|
||||
// TODO should this be recursive?
|
||||
for(size_t i = 0; i < _scene_group->getNumChildren(); ++i)
|
||||
getChildByIndex(i)->clearEventListener();
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
// A group of 2D Canvas elements
|
||||
///@file
|
||||
/// A group of 2D Canvas elements
|
||||
//
|
||||
// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
|
||||
//
|
||||
@@ -86,14 +87,15 @@ namespace canvas
|
||||
*/
|
||||
ElementPtr getElementById(const std::string& id);
|
||||
|
||||
virtual void clearEventListener();
|
||||
void clearEventListener() override;
|
||||
|
||||
virtual bool traverse(EventVisitor& visitor);
|
||||
bool traverse(EventVisitor& visitor) override;
|
||||
|
||||
virtual bool setStyle( const SGPropertyNode* child,
|
||||
const StyleInfo* style_info = 0 );
|
||||
bool setStyle( const SGPropertyNode* child,
|
||||
const StyleInfo* style_info = 0 ) override;
|
||||
|
||||
virtual osg::BoundingBox getTransformedBounds(const osg::Matrix& m) const;
|
||||
osg::BoundingBox
|
||||
getTransformedBounds(const osg::Matrix& m) const override;
|
||||
|
||||
protected:
|
||||
|
||||
@@ -105,11 +107,11 @@ namespace canvas
|
||||
*/
|
||||
virtual ElementFactory getChildFactory(const std::string& type) const;
|
||||
|
||||
virtual void updateImpl(double dt);
|
||||
void updateImpl(double dt) override;
|
||||
|
||||
virtual void childAdded(SGPropertyNode * child);
|
||||
virtual void childRemoved(SGPropertyNode * child);
|
||||
virtual void childChanged(SGPropertyNode * child);
|
||||
void childAdded(SGPropertyNode * child) override;
|
||||
void childRemoved(SGPropertyNode * child) override;
|
||||
void childChanged(SGPropertyNode * child) override;
|
||||
|
||||
void handleZIndexChanged(ElementPtr child, int z_index = 0);
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
// An image on the Canvas
|
||||
///@file
|
||||
/// An image on the Canvas
|
||||
//
|
||||
// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
|
||||
//
|
||||
@@ -837,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
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
// An image on the Canvas
|
||||
///@file
|
||||
/// An image on the Canvas
|
||||
//
|
||||
// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
|
||||
//
|
||||
@@ -53,7 +54,7 @@ namespace canvas
|
||||
ElementWeakPtr parent = 0 );
|
||||
virtual ~Image();
|
||||
|
||||
virtual void valueChanged(SGPropertyNode* child);
|
||||
void valueChanged(SGPropertyNode* child) override;
|
||||
|
||||
void setSrcCanvas(CanvasPtr canvas);
|
||||
CanvasWeakPtr getSrcCanvas() const;
|
||||
@@ -93,13 +94,35 @@ namespace canvas
|
||||
|
||||
const SGRect<float>& getRegion() const;
|
||||
|
||||
bool handleEvent(const EventPtr& event);
|
||||
bool handleEvent(const EventPtr& event) override;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
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
|
||||
{
|
||||
@@ -108,9 +131,9 @@ namespace canvas
|
||||
SRC_CANVAS = DEST_SIZE << 1
|
||||
};
|
||||
|
||||
virtual void updateImpl(double dt);
|
||||
void updateImpl(double dt) override;
|
||||
|
||||
virtual void childChanged(SGPropertyNode * child);
|
||||
void childChanged(SGPropertyNode * child) override;
|
||||
|
||||
void setupDefaultDimensions();
|
||||
SGRect<int> getTextureDimensions() const;
|
||||
@@ -124,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;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
// A group of 2D Canvas elements which get automatically transformed according
|
||||
// to the map parameters.
|
||||
///@file
|
||||
/// A group of 2D Canvas elements which get automatically transformed according
|
||||
/// to the map parameters.
|
||||
//
|
||||
// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
|
||||
//
|
||||
@@ -96,17 +97,26 @@ namespace canvas
|
||||
|| (!geo_node->isDirty() && !_projection_dirty) )
|
||||
continue;
|
||||
|
||||
GeoCoord lat = parseGeoCoord(geo_node->getLat());
|
||||
if( lat.type != GeoCoord::LATITUDE )
|
||||
continue;
|
||||
|
||||
GeoCoord lon = parseGeoCoord(geo_node->getLon());
|
||||
if( lon.type != GeoCoord::LONGITUDE )
|
||||
continue;
|
||||
|
||||
Projection::ScreenPosition pos =
|
||||
_projection->worldToScreen(lat.value, lon.value);
|
||||
|
||||
double latD = -9999.0, lonD = -9999.0;
|
||||
if (geo_node->isDirty()) {
|
||||
GeoCoord lat = parseGeoCoord(geo_node->getLat());
|
||||
if( lat.type != GeoCoord::LATITUDE )
|
||||
continue;
|
||||
|
||||
GeoCoord lon = parseGeoCoord(geo_node->getLon());
|
||||
if( lon.type != GeoCoord::LONGITUDE )
|
||||
continue;
|
||||
|
||||
// save the parsed values so we can re-use them if only projection
|
||||
// is changed (very common case for moving vehicle)
|
||||
latD = lat.value;
|
||||
lonD = lon.value;
|
||||
geo_node->setCachedLatLon(std::make_pair(latD, lonD));
|
||||
} else {
|
||||
std::tie(latD, lonD) = geo_node->getCachedLatLon();
|
||||
}
|
||||
|
||||
Projection::ScreenPosition pos = _projection->worldToScreen(latD, lonD);
|
||||
geo_node->setScreenPos(pos.x, pos.y);
|
||||
|
||||
// geo_node->print();
|
||||
@@ -199,10 +209,18 @@ namespace canvas
|
||||
_projection = std::make_shared<SansonFlamsteedProjection>();
|
||||
|
||||
_projection->setWorldPosition(_node->getDoubleValue(REF_LAT),
|
||||
_node->getDoubleValue(REF_LON) );
|
||||
_projection->setOrientation(_node->getFloatValue(HDG));
|
||||
_projection->setScreenRange(_node->getDoubleValue(SCREEN_RANGE));
|
||||
_projection->setRange(_node->getDoubleValue(RANGE));
|
||||
_node->getDoubleValue(REF_LON));
|
||||
|
||||
// Only set existing properties to prevent using 0 instead of default values
|
||||
|
||||
if( auto heading = _node->getChild(HDG) )
|
||||
_projection->setOrientation(heading->getFloatValue());
|
||||
|
||||
if( auto screen_range = _node->getChild(SCREEN_RANGE) )
|
||||
_projection->setScreenRange(screen_range->getDoubleValue());
|
||||
|
||||
if( auto range = _node->getChild(RANGE) )
|
||||
_projection->setRange(range->getDoubleValue());
|
||||
|
||||
_projection_dirty = true;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
// A group of 2D Canvas elements which get automatically transformed according
|
||||
// to the map parameters.
|
||||
///@file
|
||||
/// A group of 2D Canvas elements which get automatically transformed according
|
||||
/// to the map parameters.
|
||||
//
|
||||
// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
|
||||
//
|
||||
@@ -46,16 +47,14 @@ namespace canvas
|
||||
virtual ~Map();
|
||||
|
||||
protected:
|
||||
virtual void updateImpl(double dt);
|
||||
void updateImpl(double dt) override;
|
||||
|
||||
void updateProjection(SGPropertyNode* type_node);
|
||||
|
||||
virtual void childAdded( SGPropertyNode* parent,
|
||||
SGPropertyNode* child );
|
||||
virtual void childRemoved( SGPropertyNode* parent,
|
||||
SGPropertyNode* child );
|
||||
virtual void valueChanged(SGPropertyNode* child);
|
||||
virtual void childChanged(SGPropertyNode* child);
|
||||
void childAdded(SGPropertyNode* parent, SGPropertyNode* child) override;
|
||||
void childRemoved(SGPropertyNode* parent, SGPropertyNode* child) override;
|
||||
void valueChanged(SGPropertyNode* child) override;
|
||||
void childChanged(SGPropertyNode* child) override;
|
||||
|
||||
using GeoNodes =
|
||||
std::unordered_map<SGPropertyNode*, std::shared_ptr<GeoNodePair>>;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
// An OpenVG path on the Canvas
|
||||
///@file
|
||||
/// An OpenVG path on the Canvas
|
||||
//
|
||||
// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
|
||||
//
|
||||
@@ -230,9 +231,12 @@ namespace canvas
|
||||
vgDestroyPaint(_paint_fill);
|
||||
}
|
||||
|
||||
virtual const char* className() const { return "PathDrawable"; }
|
||||
virtual osg::Object* cloneType() const { return new PathDrawable(_path_element); }
|
||||
virtual osg::Object* clone(const osg::CopyOp&) const { return new PathDrawable(_path_element); }
|
||||
const char* className() const override
|
||||
{ return "PathDrawable"; }
|
||||
osg::Object* cloneType() const override
|
||||
{ return new PathDrawable(_path_element); }
|
||||
osg::Object* clone(const osg::CopyOp&) const override
|
||||
{ return new PathDrawable(_path_element); }
|
||||
|
||||
/**
|
||||
* Replace the current path segments with the new ones
|
||||
@@ -383,7 +387,7 @@ namespace canvas
|
||||
/**
|
||||
* Draw callback
|
||||
*/
|
||||
virtual void drawImplementation(osg::RenderInfo& renderInfo) const
|
||||
void drawImplementation(osg::RenderInfo& renderInfo) const override
|
||||
{
|
||||
if( _attributes_dirty & PATH )
|
||||
return;
|
||||
@@ -555,13 +559,13 @@ namespace canvas
|
||||
/**
|
||||
* Compute the bounding box
|
||||
*/
|
||||
virtual osg::BoundingBox
|
||||
osg::BoundingBox
|
||||
#if OSG_VERSION_LESS_THAN(3,3,2)
|
||||
computeBound()
|
||||
#else
|
||||
computeBoundingBox()
|
||||
#endif
|
||||
const
|
||||
const override
|
||||
{
|
||||
if( _path == VG_INVALID_HANDLE || (_attributes_dirty & PATH) )
|
||||
return osg::BoundingBox();
|
||||
@@ -667,7 +671,7 @@ namespace canvas
|
||||
struct PathUpdateCallback:
|
||||
public osg::Drawable::UpdateCallback
|
||||
{
|
||||
virtual void update(osg::NodeVisitor*, osg::Drawable* drawable)
|
||||
void update(osg::NodeVisitor*, osg::Drawable* drawable) override
|
||||
{
|
||||
static_cast<PathDrawable*>(drawable)->update();
|
||||
}
|
||||
@@ -859,6 +863,9 @@ namespace canvas
|
||||
//----------------------------------------------------------------------------
|
||||
void Path::childChanged(SGPropertyNode* child)
|
||||
{
|
||||
if( child->getParent() != _node )
|
||||
return;
|
||||
|
||||
const std::string& name = child->getNameString();
|
||||
const std::string &prName = child->getParent()->getNameString();
|
||||
|
||||
@@ -888,16 +895,15 @@ namespace canvas
|
||||
return;
|
||||
}
|
||||
|
||||
if( child->getParent() != _node )
|
||||
return;
|
||||
|
||||
if( name == "cmd" )
|
||||
_attributes_dirty |= CMDS;
|
||||
else if( name == "coord" )
|
||||
_attributes_dirty |= COORDS;
|
||||
else if ( name == "svg")
|
||||
{
|
||||
_hasSVG = true;
|
||||
_attributes_dirty |= SVG;
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
// An OpenVG path on the Canvas
|
||||
///@file
|
||||
/// An OpenVG path on the Canvas
|
||||
//
|
||||
// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
|
||||
//
|
||||
@@ -40,7 +41,8 @@ namespace canvas
|
||||
ElementWeakPtr parent = 0 );
|
||||
virtual ~Path();
|
||||
|
||||
virtual osg::BoundingBox getTransformedBounds(const osg::Matrix& m) const;
|
||||
osg::BoundingBox
|
||||
getTransformedBounds(const osg::Matrix& m) const override;
|
||||
|
||||
/** Add a segment with the given command and coordinates */
|
||||
Path& addSegment(uint8_t cmd, std::initializer_list<float> coords = {});
|
||||
@@ -86,10 +88,10 @@ namespace canvas
|
||||
bool _hasRect : 1;
|
||||
SGRectf _rect;
|
||||
|
||||
virtual void updateImpl(double dt);
|
||||
void updateImpl(double dt) override;
|
||||
|
||||
virtual void childRemoved(SGPropertyNode * child);
|
||||
virtual void childChanged(SGPropertyNode * child);
|
||||
void childRemoved(SGPropertyNode * child) override;
|
||||
void childChanged(SGPropertyNode * child) override;
|
||||
|
||||
void parseRectToVGPath();
|
||||
};
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
// A text on the Canvas
|
||||
///@file
|
||||
/// A text on the Canvas
|
||||
//
|
||||
// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
|
||||
//
|
||||
@@ -17,6 +18,7 @@
|
||||
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#include <simgear_config.h>
|
||||
|
||||
#include "CanvasText.hxx"
|
||||
#include <simgear/canvas/Canvas.hxx>
|
||||
#include <simgear/canvas/CanvasSystemAdapter.hxx>
|
||||
@@ -51,27 +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;
|
||||
|
||||
virtual 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;
|
||||
|
||||
protected:
|
||||
|
||||
friend class TextLine;
|
||||
|
||||
canvas::Text *_text_element;
|
||||
|
||||
virtual void computePositions(unsigned int contextID) const;
|
||||
};
|
||||
#if OSG_VERSION_LESS_THAN(3,5,6)
|
||||
void computePositions(unsigned int contextID) const override;
|
||||
#else
|
||||
void computePositionsImplementation() override;
|
||||
#endif
|
||||
};
|
||||
|
||||
class TextLine
|
||||
{
|
||||
@@ -121,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);
|
||||
@@ -132,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
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
@@ -160,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;
|
||||
}
|
||||
@@ -195,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();
|
||||
@@ -224,6 +245,7 @@ namespace canvas
|
||||
}
|
||||
|
||||
return cursorPos(i - _begin);
|
||||
#endif
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
@@ -302,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);
|
||||
|
||||
@@ -326,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);
|
||||
|
||||
@@ -611,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)
|
||||
@@ -639,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 )
|
||||
@@ -709,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";
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
// A text on the Canvas
|
||||
///@file
|
||||
/// A text on the Canvas
|
||||
//
|
||||
// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
|
||||
//
|
||||
|
||||
@@ -67,7 +67,8 @@ namespace canvas
|
||||
{
|
||||
_node_lat = node;
|
||||
_status &= ~LAT_MISSING;
|
||||
|
||||
_xNode.reset();
|
||||
|
||||
if( node == _node_lon )
|
||||
{
|
||||
_node_lon = 0;
|
||||
@@ -79,7 +80,8 @@ namespace canvas
|
||||
{
|
||||
_node_lon = node;
|
||||
_status &= ~LON_MISSING;
|
||||
|
||||
_yNode.reset();
|
||||
|
||||
if( node == _node_lat )
|
||||
{
|
||||
_node_lat = 0;
|
||||
@@ -97,19 +99,34 @@ namespace canvas
|
||||
return _node_lon ? _node_lon->getStringValue() : "";
|
||||
}
|
||||
|
||||
void setCachedLatLon(const std::pair<double, double>& latLon)
|
||||
{ _cachedLatLon = latLon; }
|
||||
|
||||
std::pair<double, double> getCachedLatLon()
|
||||
{ return _cachedLatLon; }
|
||||
|
||||
void setTargetName(const std::string& name)
|
||||
{
|
||||
_target_name = name;
|
||||
_xNode.reset();
|
||||
_yNode.reset();
|
||||
}
|
||||
|
||||
void setScreenPos(float x, float y)
|
||||
{
|
||||
assert( isComplete() );
|
||||
SGPropertyNode *parent = _node_lat->getParent();
|
||||
parent->getChild(_target_name, _node_lat->getIndex(), true)
|
||||
->setDoubleValue(x);
|
||||
parent->getChild(_target_name, _node_lon->getIndex(), true)
|
||||
->setDoubleValue(y);
|
||||
if (!_xNode) {
|
||||
SGPropertyNode *parent = _node_lat->getParent();
|
||||
_xNode = parent->getChild(_target_name, _node_lat->getIndex(), true);
|
||||
}
|
||||
|
||||
if (!_yNode) {
|
||||
SGPropertyNode *parent = _node_lat->getParent();
|
||||
_yNode = parent->getChild(_target_name, _node_lon->getIndex(), true);
|
||||
}
|
||||
|
||||
_xNode->setDoubleValue(x);
|
||||
_yNode->setDoubleValue(y);
|
||||
}
|
||||
|
||||
void print()
|
||||
@@ -125,6 +142,9 @@ namespace canvas
|
||||
SGPropertyNode *_node_lat,
|
||||
*_node_lon;
|
||||
std::string _target_name;
|
||||
SGPropertyNode_ptr _xNode,
|
||||
_yNode;
|
||||
std::pair<double, double> _cachedLatLon;
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
// Geographic projections for Canvas map element
|
||||
///@file
|
||||
/// Geographic projections for Canvas map element
|
||||
//
|
||||
// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
|
||||
//
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
// Canvas user defined event
|
||||
///@file
|
||||
/// Canvas user defined event
|
||||
//
|
||||
// Copyright (C) 2014 Thomas Geymayer <tomgey@gmail.com>
|
||||
//
|
||||
@@ -46,6 +47,14 @@ namespace canvas
|
||||
// assert( type_map.find(type_id) != type_map.end() );
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
CustomEvent* CustomEvent::clone(int type) const
|
||||
{
|
||||
auto event = new CustomEvent(*this);
|
||||
event->type = type;
|
||||
return event;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void CustomEvent::setDetail(StringMap const& data)
|
||||
{
|
||||
|
||||
@@ -38,6 +38,7 @@ namespace canvas
|
||||
public:
|
||||
|
||||
/**
|
||||
* @brief Construct a user defined event from a type string
|
||||
*
|
||||
* @param type_str Event type name (if name does not exist yet it will
|
||||
* be registered as new event type)
|
||||
@@ -49,6 +50,10 @@ namespace canvas
|
||||
StringMap const& data = StringMap() );
|
||||
|
||||
/**
|
||||
* @brief Construct a user defined event from a (previously registered)
|
||||
* type id
|
||||
*
|
||||
* @see getOrRegisterType()
|
||||
*
|
||||
* @param type_id Event type id
|
||||
* @param bubbles If this event should take part in the bubbling phase
|
||||
@@ -58,6 +63,8 @@ namespace canvas
|
||||
bool bubbles = false,
|
||||
StringMap const& data = StringMap() );
|
||||
|
||||
CustomEvent* clone(int type = 0) const override;
|
||||
|
||||
/**
|
||||
* Set user data
|
||||
*/
|
||||
@@ -74,7 +81,7 @@ namespace canvas
|
||||
* @see #bubbles
|
||||
* @see CustomEvent()
|
||||
*/
|
||||
virtual bool canBubble() const { return bubbles; }
|
||||
bool canBubble() const override { return bubbles; }
|
||||
|
||||
StringMap detail; //!< User data map
|
||||
bool bubbles; //!< Whether the event supports bubbling
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
// Input device event
|
||||
///@file
|
||||
/// Input device event
|
||||
//
|
||||
// Copyright (C) 2014 Thomas Geymayer <tomgey@gmail.com>
|
||||
//
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
// Keyboard event
|
||||
///@file
|
||||
/// Keyboard event
|
||||
//
|
||||
// Copyright (C) 2014 Thomas Geymayer <tomgey@gmail.com>
|
||||
//
|
||||
@@ -69,6 +70,14 @@ namespace canvas
|
||||
// // TODO what to do with wrong event type?
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
KeyboardEvent* KeyboardEvent::clone(int type) const
|
||||
{
|
||||
auto event = new KeyboardEvent(*this);
|
||||
event->type = type;
|
||||
return event;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void KeyboardEvent::setKey(uint32_t key)
|
||||
{
|
||||
|
||||
@@ -45,6 +45,7 @@ namespace canvas
|
||||
|
||||
KeyboardEvent();
|
||||
KeyboardEvent(const osgGA::GUIEventAdapter& ea);
|
||||
KeyboardEvent* clone(int type = 0) const override;
|
||||
|
||||
void setKey(uint32_t key);
|
||||
void setUnmodifiedKey(uint32_t key);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
// Mouse event
|
||||
///@file
|
||||
/// Mouse event
|
||||
//
|
||||
// Copyright (C) 2014 Thomas Geymayer <tomgey@gmail.com>
|
||||
//
|
||||
@@ -48,6 +49,14 @@ namespace canvas
|
||||
button += 1;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
MouseEvent* MouseEvent::clone(int type) const
|
||||
{
|
||||
auto event = new MouseEvent(*this);
|
||||
event->type = type;
|
||||
return event;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
bool MouseEvent::canBubble() const
|
||||
{
|
||||
|
||||
@@ -36,8 +36,9 @@ namespace canvas
|
||||
public:
|
||||
MouseEvent();
|
||||
MouseEvent(const osgGA::GUIEventAdapter& ea);
|
||||
MouseEvent* clone(int type = 0) const override;
|
||||
|
||||
virtual bool canBubble() const;
|
||||
bool canBubble() const override;
|
||||
|
||||
osg::Vec2f getScreenPos() const { return screen_pos; }
|
||||
osg::Vec2f getClientPos() const { return client_pos; }
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
// Keyboard event demo. Press some keys and get some info...
|
||||
///@file
|
||||
/// Keyboard event demo. Press some keys and get some info...
|
||||
//
|
||||
// Copyright (C) 2014 Thomas Geymayer <tomgey@gmail.com>
|
||||
//
|
||||
|
||||
@@ -18,6 +18,9 @@
|
||||
|
||||
#include <simgear_config.h>
|
||||
#include "Layout.hxx"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include <simgear/debug/logstream.hxx>
|
||||
|
||||
namespace simgear
|
||||
|
||||
@@ -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 );
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
/**
|
||||
*
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
|
||||
include (SimGearComponent)
|
||||
|
||||
set(HEADERS debug_types.h logstream.hxx BufferedLogCallback.hxx)
|
||||
set(HEADERS debug_types.h logstream.hxx BufferedLogCallback.hxx OsgIoCapture.hxx)
|
||||
set(SOURCES logstream.cxx BufferedLogCallback.cxx)
|
||||
|
||||
simgear_component(debug debug "${SOURCES}" "${HEADERS}")
|
||||
simgear_component(debug debug "${SOURCES}" "${HEADERS}")
|
||||
|
||||
54
simgear/debug/OsgIoCapture.hxx
Normal file
54
simgear/debug/OsgIoCapture.hxx
Normal file
@@ -0,0 +1,54 @@
|
||||
#include <cstring>
|
||||
|
||||
#include <osg/Notify>
|
||||
|
||||
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.
|
||||
*/
|
||||
class NotifyLogger : public osg::NotifyHandler
|
||||
{
|
||||
public:
|
||||
// note this callback will be invoked by OSG from multiple threads.
|
||||
// fortunately our Simgear logging implementation already handles
|
||||
// that internally, so we simply pass the message on.
|
||||
virtual void notify(osg::NotifySeverity severity, const char* message) {
|
||||
// Detect whether a osg::Reference derived object is deleted with a non-zero
|
||||
// reference count. In this case trigger a segfault to get a stack trace.
|
||||
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);
|
||||
#ifndef DEBUG
|
||||
throw new std::string(message);
|
||||
//int* trigger_segfault = 0;
|
||||
//*trigger_segfault = 0;
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -34,7 +34,11 @@ typedef enum {
|
||||
SG_GUI = 0x00800000,
|
||||
SG_TERRASYNC = 0x01000000,
|
||||
SG_PARTICLES = 0x02000000,
|
||||
SG_UNDEFD = 0x04000000, // For range checking
|
||||
SG_HEADLESS = 0x04000000,
|
||||
// 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;
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
@@ -100,6 +118,8 @@ const char* LogCallback::debugClassToString(sgDebugClass c)
|
||||
case SG_GUI: return "gui";
|
||||
case SG_TERRASYNC: return "terrasync";
|
||||
case SG_PARTICLES: return "particles";
|
||||
case SG_HEADLESS: return "headless";
|
||||
case SG_OSG: return "OSG";
|
||||
default: return "unknown";
|
||||
}
|
||||
}
|
||||
@@ -111,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;
|
||||
@@ -131,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)
|
||||
@@ -147,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);
|
||||
@@ -242,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".
|
||||
*/
|
||||
|
||||
@@ -269,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
|
||||
@@ -299,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
|
||||
@@ -318,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();
|
||||
@@ -327,20 +387,23 @@ 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
|
||||
}
|
||||
|
||||
~LogStreamPrivate()
|
||||
{
|
||||
for (simgear::LogCallback* cb : m_callbacks) {
|
||||
delete cb;
|
||||
}
|
||||
removeCallbacks();
|
||||
}
|
||||
|
||||
SGMutex m_lock;
|
||||
std::mutex m_lock;
|
||||
SGBlockingQueue<LogEntry> m_entries;
|
||||
|
||||
// log entries posted during startup
|
||||
@@ -362,10 +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();
|
||||
@@ -377,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()
|
||||
@@ -387,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,
|
||||
@@ -407,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;
|
||||
@@ -443,18 +516,35 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
void removeCallbacks()
|
||||
{
|
||||
PauseThread pause(this);
|
||||
for (simgear::LogCallback* cb : m_callbacks) {
|
||||
delete cb;
|
||||
}
|
||||
m_callbacks.clear();
|
||||
m_consoleCallbacks.clear();
|
||||
}
|
||||
|
||||
void setLogLevels( sgDebugClass c, sgDebugPriority p )
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
bool would_log( sgDebugClass c, sgDebugPriority p ) const
|
||||
{
|
||||
// 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);
|
||||
@@ -464,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);
|
||||
}
|
||||
@@ -475,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;
|
||||
@@ -485,7 +579,7 @@ public:
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static std::unique_ptr<logstream> global_logstream;
|
||||
static SGMutex global_logStreamLock;
|
||||
static std::mutex global_logStreamLock;
|
||||
|
||||
logstream::logstream()
|
||||
{
|
||||
@@ -510,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)
|
||||
@@ -649,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);
|
||||
@@ -711,19 +809,26 @@ void logstream::requestConsole()
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
logstream::setTestingMode( bool testMode )
|
||||
{
|
||||
d->m_testMode = testMode;
|
||||
if (testMode) d->removeCallbacks();
|
||||
}
|
||||
|
||||
|
||||
namespace simgear
|
||||
{
|
||||
|
||||
void requestConsole()
|
||||
{
|
||||
{
|
||||
sglog().requestConsole();
|
||||
}
|
||||
|
||||
|
||||
void shutdownLogging()
|
||||
{
|
||||
SGGuard<SGMutex> g(global_logStreamLock);
|
||||
std::lock_guard<std::mutex> g(global_logStreamLock);
|
||||
global_logstream.reset();
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
*/
|
||||
@@ -158,12 +165,22 @@ public:
|
||||
|
||||
void removeCallback(simgear::LogCallback* cb);
|
||||
|
||||
void removeCallbacks();
|
||||
|
||||
/**
|
||||
* optionally record all entries and submit them to new log callbacks that
|
||||
* are added. This allows simplified logging configuration, but still including
|
||||
* early startup information in all logs.
|
||||
*/
|
||||
void setStartupLoggingEnabled(bool enabled);
|
||||
|
||||
/**
|
||||
* Set up the logstream for running in test mode. For example the callbacks
|
||||
* will be unregistered and the behaviour of the would_log() function
|
||||
* sanitized.
|
||||
*/
|
||||
void setTestingMode(bool testMode);
|
||||
|
||||
private:
|
||||
// constructor
|
||||
logstream();
|
||||
@@ -193,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
|
||||
|
||||
|
||||
33
simgear/emesary/CMakeLists.txt
Normal file
33
simgear/emesary/CMakeLists.txt
Normal 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)
|
||||
31
simgear/emesary/Emesary.cxx
Normal file
31
simgear/emesary/Emesary.cxx
Normal 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
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
48
simgear/emesary/Emesary.hxx
Normal file
48
simgear/emesary/Emesary.hxx
Normal 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
|
||||
54
simgear/emesary/INotification.hxx
Normal file
54
simgear/emesary/INotification.hxx
Normal 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
|
||||
47
simgear/emesary/IReceiver.hxx
Normal file
47
simgear/emesary/IReceiver.hxx
Normal 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
|
||||
52
simgear/emesary/ITransmitter.hxx
Normal file
52
simgear/emesary/ITransmitter.hxx
Normal 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
|
||||
54
simgear/emesary/ReceiptStatus.hxx
Normal file
54
simgear/emesary/ReceiptStatus.hxx
Normal 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
|
||||
203
simgear/emesary/Transmitter.hxx
Normal file
203
simgear/emesary/Transmitter.hxx
Normal 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
|
||||
68
simgear/emesary/notifications.hxx
Normal file
68
simgear/emesary/notifications.hxx
Normal 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
|
||||
130
simgear/emesary/test_emesary.cxx
Normal file
130
simgear/emesary/test_emesary.cxx
Normal 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;
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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 );
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
@@ -242,8 +243,13 @@ void Client::makeRequest(const Request_ptr& r)
|
||||
if( r->isComplete() )
|
||||
return;
|
||||
|
||||
if (r->url().empty()) {
|
||||
r->setFailure(EINVAL, "no URL specified on request");
|
||||
return;
|
||||
}
|
||||
|
||||
if( r->url().find("://") == std::string::npos ) {
|
||||
r->setFailure(EINVAL, "malformed URL");
|
||||
r->setFailure(EINVAL, "malformed URL: '" + r->url() + "'");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -265,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()) {
|
||||
@@ -280,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") {
|
||||
@@ -467,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;
|
||||
@@ -499,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);
|
||||
|
||||
@@ -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>
|
||||
@@ -75,6 +77,25 @@ namespace simgear
|
||||
|
||||
typedef SGSharedPtr<HTTPRepoGetRequest> RepoRequestPtr;
|
||||
|
||||
std::string innerResultCodeAsString(HTTPRepository::ResultCode code)
|
||||
{
|
||||
switch (code) {
|
||||
case HTTPRepository::REPO_NO_ERROR: return "no error";
|
||||
case HTTPRepository::REPO_ERROR_NOT_FOUND: return "not found";
|
||||
case HTTPRepository::REPO_ERROR_SOCKET: return "socket error";
|
||||
case HTTPRepository::SVN_ERROR_XML: return "malformed XML";
|
||||
case HTTPRepository::SVN_ERROR_TXDELTA: return "malformed XML";
|
||||
case HTTPRepository::REPO_ERROR_IO: return "I/O error";
|
||||
case HTTPRepository::REPO_ERROR_CHECKSUM: return "checksum verification error";
|
||||
case HTTPRepository::REPO_ERROR_FILE_NOT_FOUND: return "file not found";
|
||||
case HTTPRepository::REPO_ERROR_HTTP: return "HTTP-level error";
|
||||
case HTTPRepository::REPO_ERROR_CANCELLED: return "cancelled";
|
||||
case HTTPRepository::REPO_PARTIAL_UPDATE: return "partial update (incomplete)";
|
||||
}
|
||||
|
||||
return "Unknown response code";
|
||||
}
|
||||
|
||||
class HTTPRepoPrivate
|
||||
{
|
||||
public:
|
||||
@@ -157,23 +178,18 @@ class HTTPDirectory
|
||||
enum Type
|
||||
{
|
||||
FileType,
|
||||
DirectoryType
|
||||
DirectoryType,
|
||||
TarballType
|
||||
};
|
||||
|
||||
ChildInfo(Type ty, const std::string & nameData, const std::string & hashData) :
|
||||
type(ty),
|
||||
name(nameData),
|
||||
hash(hashData),
|
||||
sizeInBytes(0)
|
||||
hash(hashData)
|
||||
{
|
||||
}
|
||||
|
||||
ChildInfo(const ChildInfo& other) :
|
||||
type(other.type),
|
||||
name(other.name),
|
||||
hash(other.hash),
|
||||
sizeInBytes(other.sizeInBytes)
|
||||
{ }
|
||||
ChildInfo(const ChildInfo& other) = default;
|
||||
|
||||
void setSize(const std::string & sizeData)
|
||||
{
|
||||
@@ -187,7 +203,8 @@ class HTTPDirectory
|
||||
|
||||
Type type;
|
||||
std::string name, hash;
|
||||
size_t sizeInBytes;
|
||||
size_t sizeInBytes = 0;
|
||||
SGPath path; // absolute path on disk
|
||||
};
|
||||
|
||||
typedef std::vector<ChildInfo> ChildInfoList;
|
||||
@@ -254,101 +271,84 @@ public:
|
||||
return;
|
||||
}
|
||||
|
||||
string_list indexNames = indexChildren();
|
||||
const_string_list_iterator nameIt = indexNames.begin();
|
||||
for (; nameIt != indexNames.end(); ++nameIt) {
|
||||
SGPath p(absolutePath());
|
||||
p.append(*nameIt);
|
||||
if (p.exists()) {
|
||||
continue; // only copy if the file is missing entirely
|
||||
}
|
||||
char* buf = nullptr;
|
||||
size_t bufSize = 0;
|
||||
|
||||
ChildInfoList::iterator c = findIndexChild(*nameIt);
|
||||
if (c->type == ChildInfo::DirectoryType) {
|
||||
continue; // only care about files
|
||||
}
|
||||
for (const auto& child : children) {
|
||||
if (child.type != ChildInfo::FileType)
|
||||
continue;
|
||||
|
||||
SGPath cp = _repository->installedCopyPath;
|
||||
cp.append(relativePath());
|
||||
cp.append(*nameIt);
|
||||
if (!cp.exists()) {
|
||||
continue;
|
||||
}
|
||||
if (child.path.exists())
|
||||
continue;
|
||||
|
||||
SG_LOG(SG_TERRASYNC, SG_BULK, "new child, copying existing file" << cp << p);
|
||||
SGPath cp = _repository->installedCopyPath;
|
||||
cp.append(relativePath());
|
||||
cp.append(child.name);
|
||||
if (!cp.exists()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
SGBinaryFile src(cp);
|
||||
SGBinaryFile dst(p);
|
||||
src.open(SG_IO_IN);
|
||||
dst.open(SG_IO_OUT);
|
||||
SGBinaryFile src(cp);
|
||||
SGBinaryFile dst(child.path);
|
||||
src.open(SG_IO_IN);
|
||||
dst.open(SG_IO_OUT);
|
||||
|
||||
char* buf = (char*) malloc(cp.sizeInBytes());
|
||||
if (!buf) {
|
||||
continue;
|
||||
}
|
||||
if (bufSize < cp.sizeInBytes()) {
|
||||
bufSize = cp.sizeInBytes();
|
||||
free(buf);
|
||||
buf = (char*) malloc(bufSize);
|
||||
if (!buf) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
src.read(buf, cp.sizeInBytes());
|
||||
dst.write(buf, cp.sizeInBytes());
|
||||
src.close();
|
||||
dst.close();
|
||||
src.read(buf, cp.sizeInBytes());
|
||||
dst.write(buf, cp.sizeInBytes());
|
||||
src.close();
|
||||
dst.close();
|
||||
|
||||
free(buf);
|
||||
}
|
||||
}
|
||||
|
||||
free(buf);
|
||||
}
|
||||
|
||||
void updateChildrenBasedOnHash()
|
||||
{
|
||||
//SG_LOG(SG_TERRASYNC, SG_DEBUG, "updated children for:" << relativePath());
|
||||
copyInstalledChildren();
|
||||
|
||||
copyInstalledChildren();
|
||||
ChildInfoList toBeUpdated;
|
||||
|
||||
string_list indexNames = indexChildren(),
|
||||
toBeUpdated, orphans;
|
||||
simgear::Dir d(absolutePath());
|
||||
PathList fsChildren = d.children(0);
|
||||
PathList::const_iterator it = fsChildren.begin();
|
||||
simgear::Dir d(absolutePath());
|
||||
PathList fsChildren = d.children(0);
|
||||
PathList orphans = d.children(0);
|
||||
|
||||
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());
|
||||
|
||||
for (; it != fsChildren.end(); ++it) {
|
||||
ChildInfo info(it->isDir() ? ChildInfo::DirectoryType : ChildInfo::FileType,
|
||||
it->file(), "");
|
||||
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(it->file());
|
||||
if (c == children.end()) {
|
||||
SG_LOG(SG_TERRASYNC, SG_DEBUG, "is orphan '" << it->file() << "'" );
|
||||
|
||||
orphans.push_back(it->file());
|
||||
} else if (c->hash != hash) {
|
||||
SG_LOG(SG_TERRASYNC, SG_DEBUG, "hash mismatch'" << it->file() );
|
||||
// file exists, but hash mismatch, schedule update
|
||||
if (!hash.empty()) {
|
||||
SG_LOG(SG_TERRASYNC, SG_DEBUG, "file exists but hash is wrong for:" << it->file() );
|
||||
SG_LOG(SG_TERRASYNC, SG_DEBUG, "on disk:" << hash << " vs in info:" << c->hash);
|
||||
}
|
||||
|
||||
toBeUpdated.push_back(it->file() );
|
||||
} else {
|
||||
// file exists and hash is valid. If it's a directory,
|
||||
// perform a recursive check.
|
||||
SG_LOG(SG_TERRASYNC, SG_DEBUG, "file exists hash is good:" << it->file() );
|
||||
if (c->type == ChildInfo::DirectoryType) {
|
||||
HTTPDirectory* childDir = childDirectory(it->file());
|
||||
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(), it->file()), 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)
|
||||
@@ -357,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);
|
||||
}
|
||||
}
|
||||
@@ -376,22 +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;
|
||||
}
|
||||
|
||||
SG_LOG(SG_TERRASYNC,SG_DEBUG, "scheduling update for " << *it );
|
||||
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");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -411,20 +411,63 @@ public:
|
||||
void didUpdateFile(const std::string& file, const std::string& hash, size_t sz)
|
||||
{
|
||||
// check hash matches what we expected
|
||||
ChildInfoList::iterator it = findIndexChild(file);
|
||||
auto it = findIndexChild(file);
|
||||
if (it == children.end()) {
|
||||
SG_LOG(SG_TERRASYNC, SG_WARN, "updated file but not found in dir:" << _relativePath << " " << file);
|
||||
} else {
|
||||
SGPath fpath(absolutePath());
|
||||
fpath.append(file);
|
||||
|
||||
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(fpath, hash);
|
||||
_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
|
||||
}
|
||||
@@ -447,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));
|
||||
@@ -481,11 +534,13 @@ private:
|
||||
|
||||
if( typeData == "version" ) {
|
||||
if( tokens.size() < 2 ) {
|
||||
SG_LOG(SG_TERRASYNC, SG_WARN, "malformed .dirindex file: missing version number in line '" << line << "'" );
|
||||
SG_LOG(SG_TERRASYNC, SG_WARN, "malformed .dirindex file: missing version number in line '" << line << "'"
|
||||
<< "\n\tparsing:" << p.utf8Str());
|
||||
break;
|
||||
}
|
||||
if( tokens[1] != "1" ) {
|
||||
SG_LOG(SG_TERRASYNC, SG_WARN, "invalid .dirindex file: wrong version number '" << tokens[1] << "' (expected 1)" );
|
||||
SG_LOG(SG_TERRASYNC, SG_WARN, "invalid .dirindex file: wrong version number '" << tokens[1] << "' (expected 1)"
|
||||
<< "\n\tparsing:" << p.utf8Str());
|
||||
break;
|
||||
}
|
||||
continue; // version is good, continue
|
||||
@@ -496,29 +551,36 @@ private:
|
||||
}
|
||||
|
||||
if( typeData == "time" && tokens.size() > 1 ) {
|
||||
SG_LOG(SG_TERRASYNC, SG_INFO, ".dirindex at '" << p.str() << "' timestamp: " << tokens[1] );
|
||||
// SG_LOG(SG_TERRASYNC, SG_INFO, ".dirindex at '" << p.str() << "' timestamp: " << tokens[1] );
|
||||
continue;
|
||||
}
|
||||
|
||||
if( tokens.size() < 3 ) {
|
||||
SG_LOG(SG_TERRASYNC, SG_WARN, "malformed .dirindex file: not enough tokens in line '" << line << "' (ignoring line)" );
|
||||
SG_LOG(SG_TERRASYNC, SG_WARN, "malformed .dirindex file: not enough tokens in line '" << line << "' (ignoring line)"
|
||||
<< "\n\tparsing:" << p.utf8Str());
|
||||
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;
|
||||
}
|
||||
|
||||
// security: prevent writing outside the repository via ../../.. filenames
|
||||
// (valid filenames never contain / - subdirectories have their own .dirindex)
|
||||
if ((tokens[1] == "..") || (tokens[1].find_first_of("/\\") != std::string::npos)) {
|
||||
SG_LOG(SG_TERRASYNC, SG_WARN, "malformed .dirindex file: invalid filename in line '" << line << "', (ignoring line)" );
|
||||
SG_LOG(SG_TERRASYNC, SG_WARN, "malformed .dirindex file: invalid filename in line '" << line << "', (ignoring line)"
|
||||
<< "\n\tparsing:" << p.utf8Str());
|
||||
continue;
|
||||
}
|
||||
|
||||
children.push_back(ChildInfo(typeData == "f" ? ChildInfo::FileType : ChildInfo::DirectoryType, tokens[1], tokens[2]));
|
||||
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]);
|
||||
}
|
||||
@@ -527,41 +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(absolutePath());
|
||||
p.append(child.name);
|
||||
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) :
|
||||
@@ -656,6 +713,11 @@ void HTTPRepository::setInstalledCopyPath(const SGPath& copyPath)
|
||||
_d->installedCopyPath = copyPath;
|
||||
}
|
||||
|
||||
std::string HTTPRepository::resultCodeAsString(ResultCode code)
|
||||
{
|
||||
return innerResultCodeAsString(code);
|
||||
}
|
||||
|
||||
HTTPRepository::ResultCode
|
||||
HTTPRepository::failure() const
|
||||
{
|
||||
@@ -668,7 +730,7 @@ HTTPRepository::failure() const
|
||||
|
||||
void HTTPRepoGetRequest::cancel()
|
||||
{
|
||||
_directory->repository()->http->cancelRequest(this, "Reposiotry cancelled");
|
||||
_directory->repository()->http->cancelRequest(this, "Repository cancelled");
|
||||
_directory = 0;
|
||||
}
|
||||
|
||||
@@ -690,7 +752,7 @@ HTTPRepository::failure() const
|
||||
file.reset(new SGBinaryFile(pathInRepo));
|
||||
if (!file->open(SG_IO_OUT)) {
|
||||
SG_LOG(SG_TERRASYNC, SG_WARN, "unable to create file " << pathInRepo);
|
||||
_directory->repository()->http->cancelRequest(this, "Unable to create output file");
|
||||
_directory->repository()->http->cancelRequest(this, "Unable to create output file:" + pathInRepo.utf8Str());
|
||||
}
|
||||
|
||||
sha1_init(&hashContext);
|
||||
@@ -706,12 +768,12 @@ HTTPRepository::failure() const
|
||||
if (responseCode() == 200) {
|
||||
std::string hash = strutils::encodeHex(sha1_result(&hashContext), HASH_LENGTH);
|
||||
_directory->didUpdateFile(fileName, hash, contentSize());
|
||||
SG_LOG(SG_TERRASYNC, SG_DEBUG, "got file " << fileName << " in " << _directory->absolutePath());
|
||||
} else if (responseCode() == 404) {
|
||||
SG_LOG(SG_TERRASYNC, SG_WARN, "terrasync file not found on server: " << fileName << " for " << _directory->absolutePath());
|
||||
_directory->didFailToUpdateFile(fileName, HTTPRepository::REPO_ERROR_FILE_NOT_FOUND);
|
||||
} else {
|
||||
SG_LOG(SG_TERRASYNC, SG_WARN, "terrasync file download error on server: " << fileName << " for " << _directory->absolutePath() << ": " << responseCode() );
|
||||
SG_LOG(SG_TERRASYNC, SG_WARN, "terrasync file download error on server: " << fileName << " for " << _directory->absolutePath() <<
|
||||
"\n\tserver responded: " << responseCode() << "/" << responseReason());
|
||||
_directory->didFailToUpdateFile(fileName, HTTPRepository::REPO_ERROR_HTTP);
|
||||
}
|
||||
|
||||
@@ -720,13 +782,22 @@ HTTPRepository::failure() const
|
||||
|
||||
virtual void onFail()
|
||||
{
|
||||
HTTPRepository::ResultCode code = HTTPRepository::REPO_ERROR_SOCKET;
|
||||
if (responseCode() == -1) {
|
||||
code = HTTPRepository::REPO_ERROR_CANCELLED;
|
||||
}
|
||||
|
||||
if (file) {
|
||||
file->close();
|
||||
}
|
||||
|
||||
file.reset();
|
||||
if (pathInRepo.exists()) {
|
||||
pathInRepo.remove();
|
||||
}
|
||||
|
||||
if (_directory) {
|
||||
_directory->didFailToUpdateFile(fileName, HTTPRepository::REPO_ERROR_SOCKET);
|
||||
_directory->didFailToUpdateFile(fileName, code);
|
||||
_directory->repository()->finishedRequest(this);
|
||||
}
|
||||
}
|
||||
@@ -791,7 +862,7 @@ HTTPRepository::failure() const
|
||||
|
||||
// dir index data has changed, so write to disk and update
|
||||
// the hash accordingly
|
||||
sg_ofstream of(pathInRepo(), std::ios::trunc | std::ios::out);
|
||||
sg_ofstream of(pathInRepo(), std::ios::trunc | std::ios::out | std::ios::binary);
|
||||
if (!of.is_open()) {
|
||||
throw sg_io_exception("Failed to open directory index file for writing", pathInRepo());
|
||||
}
|
||||
@@ -970,11 +1041,11 @@ HTTPRepository::failure() const
|
||||
|
||||
SGPath cachePath = basePath;
|
||||
cachePath.append(".hashes");
|
||||
sg_ofstream stream(cachePath, std::ios::out | std::ios::trunc);
|
||||
sg_ofstream stream(cachePath, std::ios::out | std::ios::trunc | std::ios::binary);
|
||||
HashCache::const_iterator it;
|
||||
for (it = hashes.begin(); it != hashes.end(); ++it) {
|
||||
stream << it->filePath << ":" << it->modTime << ":"
|
||||
<< it->lengthBytes << ":" << it->hashHex << "\n";
|
||||
stream << it->filePath << "*" << it->modTime << "*"
|
||||
<< it->lengthBytes << "*" << it->hashHex << "\n";
|
||||
}
|
||||
stream.close();
|
||||
hashCacheDirty = false;
|
||||
@@ -998,7 +1069,7 @@ HTTPRepository::failure() const
|
||||
if( line.empty() || line[0] == '#' )
|
||||
continue;
|
||||
|
||||
string_list tokens = simgear::strutils::split( line, ":" );
|
||||
string_list tokens = simgear::strutils::split(line, "*");
|
||||
if( tokens.size() < 4 ) {
|
||||
SG_LOG(SG_TERRASYNC, SG_WARN, "invalid entry in '" << cachePath << "': '" << line << "' (ignoring line)");
|
||||
continue;
|
||||
@@ -1057,7 +1128,7 @@ HTTPRepository::failure() const
|
||||
} else {
|
||||
// we encounter this code path when deleting an orphaned directory
|
||||
}
|
||||
|
||||
|
||||
Dir dir(absPath);
|
||||
bool result = dir.remove(true);
|
||||
|
||||
@@ -1121,13 +1192,15 @@ HTTPRepository::failure() const
|
||||
RequestVector copyOfActive(activeRequests);
|
||||
RequestVector::iterator rq;
|
||||
for (rq = copyOfActive.begin(); rq != copyOfActive.end(); ++rq) {
|
||||
//SG_LOG(SG_TERRASYNC, SG_DEBUG, "cancelling request for:" << (*rq)->url());
|
||||
http->cancelRequest(*rq, "Repository updated failed");
|
||||
http->cancelRequest(*rq, "Repository updated failed due to checksum error");
|
||||
}
|
||||
|
||||
|
||||
SG_LOG(SG_TERRASYNC, SG_WARN, "failed to update repository:" << baseUrl
|
||||
<< ", possibly modified during sync");
|
||||
<< "\n\tchecksum failure for: " << relativePath
|
||||
<< "\n\tthis typically indicates the remote repository is corrupt or was being updated during the sync");
|
||||
} else if (fileStatus == HTTPRepository::REPO_ERROR_CANCELLED) {
|
||||
// if we were cancelled, don't report or log
|
||||
return;
|
||||
}
|
||||
|
||||
Failure f;
|
||||
@@ -1135,7 +1208,8 @@ HTTPRepository::failure() const
|
||||
f.error = fileStatus;
|
||||
failures.push_back(f);
|
||||
|
||||
SG_LOG(SG_TERRASYNC, SG_WARN, "failed to update entry:" << relativePath << " code:" << fileStatus);
|
||||
SG_LOG(SG_TERRASYNC, SG_WARN, "failed to update entry:" << relativePath << " status/code: "
|
||||
<< innerResultCodeAsString(fileStatus) << "/" << fileStatus);
|
||||
}
|
||||
|
||||
} // of namespace simgear
|
||||
|
||||
@@ -42,6 +42,7 @@ public:
|
||||
REPO_ERROR_CHECKSUM,
|
||||
REPO_ERROR_FILE_NOT_FOUND,
|
||||
REPO_ERROR_HTTP,
|
||||
REPO_ERROR_CANCELLED,
|
||||
REPO_PARTIAL_UPDATE
|
||||
};
|
||||
|
||||
@@ -70,6 +71,9 @@ public:
|
||||
* repository. When a file is missing it will be copied from this tree.
|
||||
*/
|
||||
void setInstalledCopyPath(const SGPath& copyPath);
|
||||
|
||||
static std::string resultCodeAsString(ResultCode code);
|
||||
|
||||
private:
|
||||
bool isBare() const;
|
||||
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
|
||||
BIN
simgear/io/badTar.tgz
Normal file
BIN
simgear/io/badTar.tgz
Normal file
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user