Compare commits
202
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7fbdd9ad2f | ||
|
|
24539968fd | ||
|
|
f13b356531 | ||
|
|
477594d7bc | ||
|
|
e955a18126 | ||
|
|
adec1c4507 | ||
|
|
172b8a032c | ||
|
|
d6c8008978 | ||
|
|
488ca0fcc5 | ||
|
|
dfd15cadab | ||
|
|
1613257bdc | ||
|
|
1061580118 | ||
|
|
7184828a58 | ||
|
|
0428adff03 | ||
|
|
04246f0a63 | ||
|
|
61df58d651 | ||
|
|
1bd9a440e8 | ||
|
|
d82c8eb945 | ||
|
|
becbad7ea4 | ||
|
|
2335796a28 | ||
|
|
c6330b64f6 | ||
|
|
df57a23512 | ||
|
|
aea71cf0af | ||
|
|
fb54386f8d | ||
|
|
4d931b6109 | ||
|
|
407c7a10f5 | ||
|
|
16fa4d1d17 | ||
|
|
1ecccd43d4 | ||
|
|
2edbb1cf8b | ||
|
|
8dccbe8029 | ||
|
|
031c833e4d | ||
|
|
890cc46825 | ||
|
|
6b0f7e8512 | ||
|
|
7a220921f0 | ||
|
|
7ef9acda5d | ||
|
|
2a6d62e6f7 | ||
|
|
42b50bd7f7 | ||
|
|
2c3f44ae11 | ||
|
|
b7662f16e5 | ||
|
|
5bc535a8d2 | ||
|
|
3ec9c7ae6e | ||
|
|
e326d0c756 | ||
|
|
7c5434a99f | ||
|
|
26d9256ee9 | ||
|
|
053e761d8f | ||
|
|
5861b52cff | ||
|
|
66193d7283 | ||
|
|
dfb07f359e | ||
|
|
6fe1433497 | ||
|
|
01ace109ba | ||
|
|
8e01acaa12 | ||
|
|
7bbec8dbcb | ||
|
|
10375086ed | ||
|
|
dd367daac9 | ||
|
|
27063d0c32 | ||
|
|
80ff0282c3 | ||
|
|
e973e406a3 | ||
|
|
c64776029e | ||
|
|
5d7d0a922d | ||
|
|
c2c96c4219 | ||
|
|
3e9ed08c53 | ||
|
|
b7df9026c0 | ||
|
|
6527c6357b | ||
|
|
fad333256c | ||
|
|
8753811fe1 | ||
|
|
237752ff62 | ||
|
|
743244a69c | ||
|
|
9f89737986 | ||
|
|
508a5c5f66 | ||
|
|
d84394efa0 | ||
|
|
dd613d48bc | ||
|
|
2e8e500d4f | ||
|
|
48f866dcc6 | ||
|
|
ec4d3e4f9b | ||
|
|
38ddfab1e0 | ||
|
|
353b7e4438 | ||
|
|
7076c9a0ff | ||
|
|
2f42a8714c | ||
|
|
fdf6fc32ff | ||
|
|
f2188b33c6 | ||
|
|
f93fead8f2 | ||
|
|
050f3791cc | ||
|
|
94326fd964 | ||
|
|
f26f3b606d | ||
|
|
7cbfa76be4 | ||
|
|
d896e71ae9 | ||
|
|
ca79d99ec4 | ||
|
|
95f77ef81d | ||
|
|
c4e7d26b70 | ||
|
|
ea49a1a07b | ||
|
|
784223c67f | ||
|
|
77aa2c9a9d | ||
|
|
48145fb234 | ||
|
|
49730cadee | ||
|
|
0b197501e1 | ||
|
|
8b0c246a7b | ||
|
|
9c4face1ae | ||
|
|
952e2e6631 | ||
|
|
4766910413 | ||
|
|
3c2f97a8d2 | ||
|
|
f75730f165 | ||
|
|
b596e62a61 | ||
|
|
1349d48339 | ||
|
|
d69ea5fda6 | ||
|
|
9b68062ba7 | ||
|
|
b217019723 | ||
|
|
a37a254a68 | ||
|
|
5258699f20 | ||
|
|
483bebb003 | ||
|
|
8ca8052a8d | ||
|
|
d68b1144b8 | ||
|
|
42c39b6be3 | ||
|
|
bb82b9d168 | ||
|
|
b1f865d461 | ||
|
|
8e75c6be50 | ||
|
|
06a5f9188d | ||
|
|
370a991208 | ||
|
|
6deb77dd4d | ||
|
|
9e3172cb04 | ||
|
|
426c6b9a72 | ||
|
|
d148bdedcd | ||
|
|
23140e3bf7 | ||
|
|
2f023803e7 | ||
|
|
d658b5fc38 | ||
|
|
68cd84c330 | ||
|
|
0bd82a43d3 | ||
|
|
7fdf42b699 | ||
|
|
aeb0e9aac3 | ||
|
|
1099a3bdf0 | ||
|
|
aa3458f69c | ||
|
|
0186cbb7b7 | ||
|
|
fa36e94c4b | ||
|
|
ad83e70cf5 | ||
|
|
4a0377c0a1 | ||
|
|
83a3241830 | ||
|
|
f299b351e3 | ||
|
|
3880e4ef47 | ||
|
|
f205e918d9 | ||
|
|
42b0b9306b | ||
|
|
483659c319 | ||
|
|
add14dd27c | ||
|
|
61516a5e97 | ||
|
|
17418039e1 | ||
|
|
25cae61211 | ||
|
|
db98c7440e | ||
|
|
3783ae2234 | ||
|
|
3e8732b230 | ||
|
|
b39bca9458 | ||
|
|
d263334030 | ||
|
|
b2cea62189 | ||
|
|
d6b886c69b | ||
|
|
38fb9ea41e | ||
|
|
a922aaa68e | ||
|
|
4f2e36ca46 | ||
|
|
f367627cac | ||
|
|
7a7fcf10ad | ||
|
|
b9bd2734eb | ||
|
|
9c7bd4f5d5 | ||
|
|
362d47f91f | ||
|
|
a18792c397 | ||
|
|
0e2ddb2f16 | ||
|
|
bcfadd6657 | ||
|
|
021cf1a2b4 | ||
|
|
acf86be516 | ||
|
|
dd93eb88f2 | ||
|
|
e06f9e35b3 | ||
|
|
c0424aba7e | ||
|
|
b468254d73 | ||
|
|
15e3e92ec2 | ||
|
|
ee403fd83a | ||
|
|
05f266ac10 | ||
|
|
f52c0026dd | ||
|
|
a284107249 | ||
|
|
a2e25e7940 | ||
|
|
d45a0161cf | ||
|
|
c7a5d15e7a | ||
|
|
3cb2241b1d | ||
|
|
b236821661 | ||
|
|
fc75b0bd21 | ||
|
|
a9fc84d568 | ||
|
|
b15c6d887d | ||
|
|
c2f89978ff | ||
|
|
a004153223 | ||
|
|
dde2bbcbe7 | ||
|
|
7bd572f3ec | ||
|
|
486011dceb | ||
|
|
01c45633b7 | ||
|
|
5320d0ecaa | ||
|
|
757ba03918 | ||
|
|
948db69cc9 | ||
|
|
92f363926e | ||
|
|
d0ff144753 | ||
|
|
943efbb3ed | ||
|
|
6962de4b1f | ||
|
|
6f7c0c23d1 | ||
|
|
14eccc70da | ||
|
|
d4b48cec5d | ||
|
|
2d23c5351f | ||
|
|
b5dfaf170a | ||
|
|
c372591f36 | ||
|
|
36d1308aa6 | ||
|
|
b0d6ed3844 |
@@ -1,4 +1,5 @@
|
|||||||
.*
|
.*
|
||||||
|
*~
|
||||||
Makefile
|
Makefile
|
||||||
*.o
|
*.o
|
||||||
lib*.a
|
lib*.a
|
||||||
|
|||||||
Vendored
+4
@@ -0,0 +1,4 @@
|
|||||||
|
if (NOT SYSTEM_EXPAT)
|
||||||
|
add_subdirectory(expat)
|
||||||
|
endif()
|
||||||
|
|
||||||
Vendored
+33
@@ -0,0 +1,33 @@
|
|||||||
|
|
||||||
|
configure_file (
|
||||||
|
"${PROJECT_SOURCE_DIR}/3rdparty/expat/expat_config_cmake.in"
|
||||||
|
"${PROJECT_BINARY_DIR}/3rdparty/expat/expat_config.h"
|
||||||
|
)
|
||||||
|
|
||||||
|
set(expat_sources
|
||||||
|
asciitab.h
|
||||||
|
hashtable.h
|
||||||
|
iasciitab.h
|
||||||
|
latin1tab.h
|
||||||
|
nametab.h
|
||||||
|
utf8tab.h
|
||||||
|
xmldef.h
|
||||||
|
xmlparse.h
|
||||||
|
xmlrole.h
|
||||||
|
xmltok.h
|
||||||
|
xmltok_impl.h
|
||||||
|
hashtable.c
|
||||||
|
xmlparse.c
|
||||||
|
xmlrole.c
|
||||||
|
xmltok.c
|
||||||
|
internal.h
|
||||||
|
ascii.h
|
||||||
|
sg_expat.h
|
||||||
|
sg_expat_external.h
|
||||||
|
)
|
||||||
|
|
||||||
|
foreach(s ${expat_sources})
|
||||||
|
set_property(GLOBAL
|
||||||
|
APPEND PROPERTY LOCAL_EXPAT_SOURCES
|
||||||
|
"${CMAKE_CURRENT_SOURCE_DIR}/${s}")
|
||||||
|
endforeach()
|
||||||
+1
-1
@@ -15,7 +15,7 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include "expat_external.h"
|
#include "sg_expat_external.h"
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
+1
-1
@@ -23,7 +23,7 @@
|
|||||||
#endif /* ndef COMPILED_FROM_DSP */
|
#endif /* ndef COMPILED_FROM_DSP */
|
||||||
|
|
||||||
#include "ascii.h"
|
#include "ascii.h"
|
||||||
#include "expat.h"
|
#include "sg_expat.h"
|
||||||
|
|
||||||
#ifdef XML_UNICODE
|
#ifdef XML_UNICODE
|
||||||
#define XML_ENCODE_MAX XML_UTF16_ENCODE_MAX
|
#define XML_ENCODE_MAX XML_UTF16_ENCODE_MAX
|
||||||
+1
-1
@@ -18,7 +18,7 @@
|
|||||||
#endif
|
#endif
|
||||||
#endif /* ndef COMPILED_FROM_DSP */
|
#endif /* ndef COMPILED_FROM_DSP */
|
||||||
|
|
||||||
#include "expat_external.h"
|
#include "sg_expat_external.h"
|
||||||
#include "internal.h"
|
#include "internal.h"
|
||||||
#include "xmlrole.h"
|
#include "xmlrole.h"
|
||||||
#include "ascii.h"
|
#include "ascii.h"
|
||||||
+1
-1
@@ -18,7 +18,7 @@
|
|||||||
#endif
|
#endif
|
||||||
#endif /* ndef COMPILED_FROM_DSP */
|
#endif /* ndef COMPILED_FROM_DSP */
|
||||||
|
|
||||||
#include "expat_external.h"
|
#include "sg_expat_external.h"
|
||||||
#include "internal.h"
|
#include "internal.h"
|
||||||
#include "xmltok.h"
|
#include "xmltok.h"
|
||||||
#include "nametab.h"
|
#include "nametab.h"
|
||||||
+64
-78
@@ -2,7 +2,7 @@ cmake_minimum_required (VERSION 2.6.4)
|
|||||||
include (CheckFunctionExists)
|
include (CheckFunctionExists)
|
||||||
include (CheckIncludeFile)
|
include (CheckIncludeFile)
|
||||||
include (CheckCXXSourceCompiles)
|
include (CheckCXXSourceCompiles)
|
||||||
|
include (CheckCXXCompilerFlag)
|
||||||
|
|
||||||
project(SimGear)
|
project(SimGear)
|
||||||
|
|
||||||
@@ -10,6 +10,8 @@ project(SimGear)
|
|||||||
file(READ version versionFile)
|
file(READ version versionFile)
|
||||||
string(STRIP ${versionFile} SIMGEAR_VERSION)
|
string(STRIP ${versionFile} SIMGEAR_VERSION)
|
||||||
|
|
||||||
|
set(FIND_LIBRARY_USE_LIB64_PATHS ON)
|
||||||
|
|
||||||
# use simgear version also as the SO version (if building SOs)
|
# use simgear version also as the SO version (if building SOs)
|
||||||
SET(SIMGEAR_SOVERSION ${SIMGEAR_VERSION})
|
SET(SIMGEAR_SOVERSION ${SIMGEAR_VERSION})
|
||||||
|
|
||||||
@@ -21,33 +23,31 @@ if(InSourceBuild)
|
|||||||
message(WARNING " mkdir ../sgbuild && cd ../sgbuild && cmake ${CMAKE_SOURCE_DIR}")
|
message(WARNING " mkdir ../sgbuild && cd ../sgbuild && cmake ${CMAKE_SOURCE_DIR}")
|
||||||
endif(InSourceBuild)
|
endif(InSourceBuild)
|
||||||
|
|
||||||
if (NOT EMBEDDED_SIMGEAR)
|
#packaging
|
||||||
#packaging
|
SET(CPACK_RESOURCE_FILE_LICENSE "${PROJECT_SOURCE_DIR}/COPYING")
|
||||||
SET(CPACK_RESOURCE_FILE_LICENSE "${PROJECT_SOURCE_DIR}/COPYING")
|
SET(CPACK_RESOURCE_FILE_README "${PROJECT_SOURCE_DIR}/README")
|
||||||
SET(CPACK_RESOURCE_FILE_README "${PROJECT_SOURCE_DIR}/README")
|
SET(CPACK_PACKAGE_DESCRIPTION_SUMMARY "Simulation support libraries for FlightGear and related projects")
|
||||||
SET(CPACK_PACKAGE_DESCRIPTION_SUMMARY "Simulation support libraries for FlightGear and related projects")
|
SET(CPACK_PACKAGE_VENDOR "The FlightGear project")
|
||||||
SET(CPACK_PACKAGE_VENDOR "The FlightGear project")
|
SET(CPACK_GENERATOR "TBZ2")
|
||||||
SET(CPACK_GENERATOR "TBZ2")
|
SET(CPACK_INSTALL_CMAKE_PROJECTS ${CMAKE_CURRENT_BINARY_DIR};SimGear;ALL;/)
|
||||||
SET(CPACK_INSTALL_CMAKE_PROJECTS ${CMAKE_CURRENT_BINARY_DIR};SimGear;ALL;/)
|
|
||||||
|
|
||||||
|
|
||||||
# split version string into components, note CMAKE_MATCH_0 is the entire regexp match
|
# split version string into components, note CMAKE_MATCH_0 is the entire regexp match
|
||||||
string(REGEX MATCH "([0-9]+)\\.([0-9]+)\\.([0-9]+)" CPACK_PACKAGE_VERSION ${SIMGEAR_VERSION} )
|
string(REGEX MATCH "([0-9]+)\\.([0-9]+)\\.([0-9]+)" CPACK_PACKAGE_VERSION ${SIMGEAR_VERSION} )
|
||||||
set(CPACK_PACKAGE_VERSION_MAJOR ${CMAKE_MATCH_1})
|
set(CPACK_PACKAGE_VERSION_MAJOR ${CMAKE_MATCH_1})
|
||||||
set(CPACK_PACKAGE_VERSION_MINOR ${CMAKE_MATCH_2})
|
set(CPACK_PACKAGE_VERSION_MINOR ${CMAKE_MATCH_2})
|
||||||
set(CPACK_PACKAGE_VERSION_PATCH ${CMAKE_MATCH_3})
|
set(CPACK_PACKAGE_VERSION_PATCH ${CMAKE_MATCH_3})
|
||||||
|
|
||||||
message(STATUS "version is ${CPACK_PACKAGE_VERSION_MAJOR} dot ${CPACK_PACKAGE_VERSION_MINOR} dot ${CPACK_PACKAGE_VERSION_PATCH}")
|
message(STATUS "version is ${CPACK_PACKAGE_VERSION_MAJOR} dot ${CPACK_PACKAGE_VERSION_MINOR} dot ${CPACK_PACKAGE_VERSION_PATCH}")
|
||||||
|
|
||||||
set(CPACK_SOURCE_GENERATOR TBZ2)
|
set(CPACK_SOURCE_GENERATOR TBZ2)
|
||||||
set(CPACK_SOURCE_PACKAGE_FILE_NAME "simgear-${SIMGEAR_VERSION}" CACHE INTERNAL "tarball basename")
|
set(CPACK_SOURCE_PACKAGE_FILE_NAME "simgear-${SIMGEAR_VERSION}" CACHE INTERNAL "tarball basename")
|
||||||
set(CPACK_SOURCE_IGNORE_FILES
|
set(CPACK_SOURCE_IGNORE_FILES
|
||||||
"^${PROJECT_SOURCE_DIR}/.git;\\\\.gitignore;Makefile.am;~$;${CPACK_SOURCE_IGNORE_FILES}")
|
"^${PROJECT_SOURCE_DIR}/.git;\\\\.gitignore;Makefile.am;~$;${CPACK_SOURCE_IGNORE_FILES}")
|
||||||
|
|
||||||
message(STATUS "ignoring: ${CPACK_SOURCE_IGNORE_FILES}")
|
message(STATUS "ignoring: ${CPACK_SOURCE_IGNORE_FILES}")
|
||||||
|
|
||||||
include (CPack)
|
include (CPack)
|
||||||
endif()
|
|
||||||
|
|
||||||
# We have some custom .cmake scripts not in the official distribution.
|
# We have some custom .cmake scripts not in the official distribution.
|
||||||
set(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/CMakeModules;${CMAKE_MODULE_PATH}")
|
set(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/CMakeModules;${CMAKE_MODULE_PATH}")
|
||||||
@@ -87,13 +87,6 @@ if(NOT "${CMAKE_LIBRARY_ARCHITECTURE}" STREQUAL "")
|
|||||||
message(STATUS "additional library directories: ${ADDITIONAL_LIBRARY_PATHS}")
|
message(STATUS "additional library directories: ${ADDITIONAL_LIBRARY_PATHS}")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(NOT MSVC)
|
|
||||||
# TBD: are these really necessary? Aren't they considered by cmake automatically?
|
|
||||||
list(APPEND ADDITIONAL_LIBRARY_PATHS
|
|
||||||
/opt/local
|
|
||||||
/usr/local
|
|
||||||
/usr)
|
|
||||||
endif()
|
|
||||||
#####################################################################################
|
#####################################################################################
|
||||||
|
|
||||||
if (NOT MSVC)
|
if (NOT MSVC)
|
||||||
@@ -108,8 +101,6 @@ endif()
|
|||||||
|
|
||||||
option(SIMGEAR_HEADLESS "Set to ON to build SimGear without GUI/graphics support" OFF)
|
option(SIMGEAR_HEADLESS "Set to ON to build SimGear without GUI/graphics support" OFF)
|
||||||
option(JPEG_FACTORY "Enable JPEG-factory support" OFF)
|
option(JPEG_FACTORY "Enable JPEG-factory support" OFF)
|
||||||
option(SG_SVN_CLIENT "Set to ON to build SimGear with built-in SVN support" OFF)
|
|
||||||
option(ENABLE_LIBSVN "Set to ON to build SimGear with libsvnclient support" ON)
|
|
||||||
option(ENABLE_RTI "Set to ON to build SimGear with RTI support" OFF)
|
option(ENABLE_RTI "Set to ON to build SimGear with RTI support" OFF)
|
||||||
option(ENABLE_TESTS "Set to OFF to disable building SimGear's test applications" ON)
|
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(ENABLE_SOUND "Set to OFF to disable building SimGear's sound support" ON)
|
||||||
@@ -199,25 +190,6 @@ else()
|
|||||||
message(STATUS "JPEG-factory: DISABLED")
|
message(STATUS "JPEG-factory: DISABLED")
|
||||||
endif(JPEG_FACTORY)
|
endif(JPEG_FACTORY)
|
||||||
|
|
||||||
if (SG_SVN_CLIENT)
|
|
||||||
message(STATUS "Using built-in subversion client code")
|
|
||||||
elseif(ENABLE_LIBSVN)
|
|
||||||
find_package(SvnClient)
|
|
||||||
|
|
||||||
if(LIBSVN_FOUND)
|
|
||||||
message(STATUS "Subversion client support: ENABLED")
|
|
||||||
set(HAVE_SVN_CLIENT_H 1)
|
|
||||||
set(HAVE_LIBSVN_CLIENT_1 1)
|
|
||||||
else()
|
|
||||||
# Oops. ENABLE_LIBSVN is ON, but svn is still missing.
|
|
||||||
# Provide clearly visible warning/hint, so builders know what else they should install (or disable).
|
|
||||||
message(WARNING "Failed to enable subversion client support. Unable to find required subversion client library. Some features may not be available (scenery download).")
|
|
||||||
message(WARNING "Install 'libsvn' library/DLL (libsvn-devel/libsvnclient/...). Otherwise disable subversion support (set 'ENABLE_LIBSVN' to 'OFF').")
|
|
||||||
endif(LIBSVN_FOUND)
|
|
||||||
else()
|
|
||||||
message(STATUS "Subversion client support: DISABLED")
|
|
||||||
endif(SG_SVN_CLIENT)
|
|
||||||
|
|
||||||
find_package(ZLIB REQUIRED)
|
find_package(ZLIB REQUIRED)
|
||||||
find_package(Threads REQUIRED)
|
find_package(Threads REQUIRED)
|
||||||
|
|
||||||
@@ -225,12 +197,19 @@ if (SYSTEM_EXPAT)
|
|||||||
message(STATUS "Requested to use system Expat library, forcing SIMGEAR_SHARED to true")
|
message(STATUS "Requested to use system Expat library, forcing SIMGEAR_SHARED to true")
|
||||||
set(SIMGEAR_SHARED ON)
|
set(SIMGEAR_SHARED ON)
|
||||||
find_package(EXPAT REQUIRED)
|
find_package(EXPAT REQUIRED)
|
||||||
include_directories(${EXPAT_INCLUDE_DIRS})
|
|
||||||
else()
|
else()
|
||||||
message(STATUS "Using built-in expat code")
|
message(STATUS "Using built-in expat code")
|
||||||
add_definitions(-DHAVE_EXPAT_CONFIG_H)
|
# 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)
|
||||||
endif(SYSTEM_EXPAT)
|
endif(SYSTEM_EXPAT)
|
||||||
|
|
||||||
|
include_directories(${EXPAT_INCLUDE_DIRS})
|
||||||
|
|
||||||
check_include_file(inttypes.h HAVE_INTTYPES_H)
|
check_include_file(inttypes.h HAVE_INTTYPES_H)
|
||||||
check_include_file(sys/time.h HAVE_SYS_TIME_H)
|
check_include_file(sys/time.h HAVE_SYS_TIME_H)
|
||||||
check_include_file(sys/timeb.h HAVE_SYS_TIMEB_H)
|
check_include_file(sys/timeb.h HAVE_SYS_TIMEB_H)
|
||||||
@@ -279,6 +258,20 @@ if(HAVE_CLOCK_GETTIME)
|
|||||||
endif(HAVE_RT)
|
endif(HAVE_RT)
|
||||||
endif(HAVE_CLOCK_GETTIME)
|
endif(HAVE_CLOCK_GETTIME)
|
||||||
|
|
||||||
|
set(DL_LIBRARY "")
|
||||||
|
check_cxx_source_compiles(
|
||||||
|
"#include <dlfcn.h>
|
||||||
|
int main(void) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
"
|
||||||
|
HAVE_DLFCN_H)
|
||||||
|
|
||||||
|
if(HAVE_DLFCN_H)
|
||||||
|
check_library_exists(dl dlerror "" HAVE_DL)
|
||||||
|
set(DL_LIBRARY "dl")
|
||||||
|
endif()
|
||||||
|
|
||||||
SET(CMAKE_DEBUG_POSTFIX "d" CACHE STRING "add a postfix, usually 'd' on windows")
|
SET(CMAKE_DEBUG_POSTFIX "d" CACHE STRING "add a postfix, usually 'd' on windows")
|
||||||
SET(CMAKE_RELEASE_POSTFIX "" CACHE STRING "add a postfix, usually empty on windows")
|
SET(CMAKE_RELEASE_POSTFIX "" CACHE STRING "add a postfix, usually empty on windows")
|
||||||
SET(CMAKE_RELWITHDEBINFO_POSTFIX "" CACHE STRING "add a postfix, usually empty on windows")
|
SET(CMAKE_RELWITHDEBINFO_POSTFIX "" CACHE STRING "add a postfix, usually empty on windows")
|
||||||
@@ -312,6 +305,14 @@ if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
|
|||||||
set(WARNING_FLAGS_C "-Wall")
|
set(WARNING_FLAGS_C "-Wall")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
|
||||||
|
# boost goes haywire wrt static asserts
|
||||||
|
check_cxx_compiler_flag(-Wno-unused-local-typedefs HAS_NOWARN_UNUSED_TYPEDEFS)
|
||||||
|
if(HAS_NOWARN_UNUSED_TYPEDEFS)
|
||||||
|
set(WARNING_FLAGS_CXX " ${WARNING_FLAGS_CXX} -Wno-unused-local-typedefs")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
if(WIN32)
|
if(WIN32)
|
||||||
|
|
||||||
if(MINGW)
|
if(MINGW)
|
||||||
@@ -344,13 +345,11 @@ set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${MSVC_LD_FLAGS}")
|
|||||||
include_directories(${PROJECT_SOURCE_DIR})
|
include_directories(${PROJECT_SOURCE_DIR})
|
||||||
include_directories(${PROJECT_SOURCE_DIR}/simgear/canvas/ShivaVG/include)
|
include_directories(${PROJECT_SOURCE_DIR}/simgear/canvas/ShivaVG/include)
|
||||||
include_directories(${PROJECT_BINARY_DIR}/simgear)
|
include_directories(${PROJECT_BINARY_DIR}/simgear)
|
||||||
include_directories(${PROJECT_BINARY_DIR}/simgear/xml)
|
|
||||||
|
|
||||||
include_directories(${OPENSCENEGRAPH_INCLUDE_DIRS}
|
include_directories(${OPENSCENEGRAPH_INCLUDE_DIRS}
|
||||||
${Boost_INCLUDE_DIRS}
|
${Boost_INCLUDE_DIRS}
|
||||||
${ZLIB_INCLUDE_DIR}
|
${ZLIB_INCLUDE_DIR}
|
||||||
${OPENAL_INCLUDE_DIR}
|
${OPENAL_INCLUDE_DIR}
|
||||||
${LibArchive_INCLUDE_DIRS}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
add_definitions(-DHAVE_CONFIG_H)
|
add_definitions(-DHAVE_CONFIG_H)
|
||||||
@@ -362,11 +361,6 @@ configure_file (
|
|||||||
"${PROJECT_BINARY_DIR}/simgear/simgear_config.h"
|
"${PROJECT_BINARY_DIR}/simgear/simgear_config.h"
|
||||||
)
|
)
|
||||||
|
|
||||||
configure_file (
|
|
||||||
"${PROJECT_SOURCE_DIR}/simgear/xml/expat_config_cmake.in"
|
|
||||||
"${PROJECT_BINARY_DIR}/simgear/xml/expat_config.h"
|
|
||||||
)
|
|
||||||
|
|
||||||
if(ENABLE_TESTS)
|
if(ENABLE_TESTS)
|
||||||
# enable CTest / make test target
|
# enable CTest / make test target
|
||||||
message(STATUS "Tests: ENABLED")
|
message(STATUS "Tests: ENABLED")
|
||||||
@@ -378,31 +372,24 @@ else()
|
|||||||
endif(ENABLE_TESTS)
|
endif(ENABLE_TESTS)
|
||||||
|
|
||||||
# always set TEST_LIBS as it is also used by other tools/applications
|
# always set TEST_LIBS as it is also used by other tools/applications
|
||||||
# TODO maybe better rename?
|
set(TEST_LIBS_INTERNAL_CORE
|
||||||
if(SIMGEAR_SHARED)
|
${CMAKE_THREAD_LIBS_INIT}
|
||||||
set( TEST_LIBS
|
${ZLIB_LIBRARY}
|
||||||
SimGearCore)
|
${WINSOCK_LIBRARY}
|
||||||
else()
|
${RT_LIBRARY}
|
||||||
set( TEST_LIBS
|
${DL_LIBRARY}
|
||||||
SimGearCore
|
${CORE_SERVICES_LIBRARY})
|
||||||
${CMAKE_THREAD_LIBS_INIT}
|
set(TEST_LIBS SimGearCore ${TEST_LIBS_INTERNAL_CORE})
|
||||||
${ZLIB_LIBRARY}
|
|
||||||
${WINSOCK_LIBRARY}
|
|
||||||
${RT_LIBRARY}
|
|
||||||
${CORE_SERVICES_LIBRARY})
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(NOT SIMGEAR_HEADLESS)
|
if(NOT SIMGEAR_HEADLESS)
|
||||||
set( TEST_LIBS
|
set(TEST_LIBS SimGearScene ${OPENGL_LIBRARIES} ${TEST_LIBS})
|
||||||
SimGearScene
|
|
||||||
${TEST_LIBS}
|
|
||||||
${OPENGL_LIBRARIES})
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
install (FILES ${PROJECT_BINARY_DIR}/simgear/simgear_config.h DESTINATION include/simgear/)
|
install (FILES ${PROJECT_BINARY_DIR}/simgear/simgear_config.h DESTINATION include/simgear/)
|
||||||
|
|
||||||
|
add_subdirectory(3rdparty)
|
||||||
add_subdirectory(simgear)
|
add_subdirectory(simgear)
|
||||||
|
|
||||||
if (NOT EMBEDDED_SIMGEAR)
|
|
||||||
#-----------------------------------------------------------------------------
|
#-----------------------------------------------------------------------------
|
||||||
### uninstall target
|
### uninstall target
|
||||||
#-----------------------------------------------------------------------------
|
#-----------------------------------------------------------------------------
|
||||||
@@ -413,5 +400,4 @@ CONFIGURE_FILE(
|
|||||||
ADD_CUSTOM_TARGET(uninstall
|
ADD_CUSTOM_TARGET(uninstall
|
||||||
"${CMAKE_COMMAND}" -P "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake")
|
"${CMAKE_COMMAND}" -P "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake")
|
||||||
|
|
||||||
endif()
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,83 +0,0 @@
|
|||||||
# Find Subversion client libraries, and dependencies
|
|
||||||
# including APR (Apache Portable Runtime)
|
|
||||||
|
|
||||||
include (CheckFunctionExists)
|
|
||||||
include (CheckIncludeFile)
|
|
||||||
include (CheckLibraryExists)
|
|
||||||
|
|
||||||
macro(find_static_component comp libs)
|
|
||||||
# account for alternative Windows svn distribution naming
|
|
||||||
if(MSVC)
|
|
||||||
set(compLib "lib${comp}")
|
|
||||||
else(MSVC)
|
|
||||||
set(compLib "${comp}")
|
|
||||||
endif(MSVC)
|
|
||||||
|
|
||||||
string(TOUPPER "${comp}" compLibBase)
|
|
||||||
set( compLibName ${compLibBase}_LIBRARY )
|
|
||||||
|
|
||||||
# NO_DEFAULT_PATH is important on Mac - we need to ensure subversion
|
|
||||||
# libraires in dist/ or Macports are picked over the Apple version
|
|
||||||
# in /usr, since that's what we will ship.
|
|
||||||
# On other platforms we do need default paths though, i.e. since Linux
|
|
||||||
# distros may use architecture-specific directories (like
|
|
||||||
# /usr/lib/x86_64-linux-gnu) which we cannot hardcode/guess here.
|
|
||||||
FIND_LIBRARY(${compLibName}
|
|
||||||
if(APPLE)
|
|
||||||
NO_DEFAULT_PATH
|
|
||||||
endif(APPLE)
|
|
||||||
NAMES ${compLib}
|
|
||||||
HINTS $ENV{LIBSVN_DIR} ${CMAKE_INSTALL_PREFIX} ${MSVC_3RDPARTY_ROOT}/${MSVC_3RDPARTY_DIR}/lib
|
|
||||||
PATH_SUFFIXES lib64 lib libs64 libs libs/Win32 libs/Win64
|
|
||||||
PATHS ${ADDITIONAL_LIBRARY_PATHS}
|
|
||||||
)
|
|
||||||
|
|
||||||
list(APPEND ${libs} ${${compLibName}})
|
|
||||||
endmacro()
|
|
||||||
|
|
||||||
find_program(HAVE_APR_CONFIG apr-1-config)
|
|
||||||
if(HAVE_APR_CONFIG)
|
|
||||||
|
|
||||||
execute_process(COMMAND apr-1-config --cppflags --includes
|
|
||||||
OUTPUT_VARIABLE APR_CFLAGS
|
|
||||||
OUTPUT_STRIP_TRAILING_WHITESPACE)
|
|
||||||
|
|
||||||
execute_process(COMMAND apr-1-config --link-ld
|
|
||||||
OUTPUT_VARIABLE RAW_APR_LIBS
|
|
||||||
OUTPUT_STRIP_TRAILING_WHITESPACE)
|
|
||||||
|
|
||||||
# clean up some vars, or other CMake pieces complain
|
|
||||||
string(STRIP "${RAW_APR_LIBS}" APR_LIBS)
|
|
||||||
|
|
||||||
else(HAVE_APR_CONFIG)
|
|
||||||
message(STATUS "apr-1-config not found, implement manual search for APR")
|
|
||||||
endif(HAVE_APR_CONFIG)
|
|
||||||
|
|
||||||
if(HAVE_APR_CONFIG OR MSVC)
|
|
||||||
find_path(LIBSVN_INCLUDE_DIR svn_client.h
|
|
||||||
NO_DEFAULT_PATH
|
|
||||||
HINTS
|
|
||||||
$ENV{LIBSVN_DIR} ${CMAKE_INSTALL_PREFIX} ${MSVC_3RDPARTY_ROOT}/${MSVC_3RDPARTY_DIR}/include
|
|
||||||
PATH_SUFFIXES include/subversion-1
|
|
||||||
PATHS
|
|
||||||
/opt/local
|
|
||||||
/usr/local
|
|
||||||
/usr
|
|
||||||
)
|
|
||||||
|
|
||||||
set(LIBSVN_LIBRARIES "")
|
|
||||||
if (MSVC)
|
|
||||||
find_static_component("apr-1" LIBSVN_LIBRARIES)
|
|
||||||
else (MSVC)
|
|
||||||
list(APPEND LIBSVN_LIBRARIES ${APR_LIBS})
|
|
||||||
endif (MSVC)
|
|
||||||
find_static_component("svn_client-1" LIBSVN_LIBRARIES)
|
|
||||||
find_static_component("svn_subr-1" LIBSVN_LIBRARIES)
|
|
||||||
find_static_component("svn_ra-1" LIBSVN_LIBRARIES)
|
|
||||||
|
|
||||||
include(FindPackageHandleStandardArgs)
|
|
||||||
FIND_PACKAGE_HANDLE_STANDARD_ARGS(LIBSVN DEFAULT_MSG LIBSVN_LIBRARIES LIBSVN_INCLUDE_DIR)
|
|
||||||
if(NOT LIBSVN_FOUND)
|
|
||||||
set(LIBSVN_LIBRARIES "")
|
|
||||||
endif(NOT LIBSVN_FOUND)
|
|
||||||
endif(HAVE_APR_CONFIG OR MSVC)
|
|
||||||
+26
-33
@@ -45,54 +45,29 @@ install (FILES ${HEADERS} DESTINATION include/simgear/)
|
|||||||
get_property(coreSources GLOBAL PROPERTY CORE_SOURCES)
|
get_property(coreSources GLOBAL PROPERTY CORE_SOURCES)
|
||||||
get_property(sceneSources GLOBAL PROPERTY SCENE_SOURCES)
|
get_property(sceneSources GLOBAL PROPERTY SCENE_SOURCES)
|
||||||
get_property(publicHeaders GLOBAL PROPERTY PUBLIC_HEADERS)
|
get_property(publicHeaders GLOBAL PROPERTY PUBLIC_HEADERS)
|
||||||
|
get_property(localExpatSources GLOBAL PROPERTY LOCAL_EXPAT_SOURCES)
|
||||||
if(LIBSVN_FOUND)
|
|
||||||
add_definitions(${APR_CFLAGS})
|
|
||||||
include_directories(${LIBSVN_INCLUDE_DIR})
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(SIMGEAR_SHARED)
|
if(SIMGEAR_SHARED)
|
||||||
message(STATUS "Library building mode: SHARED LIBRARIES")
|
message(STATUS "Library building mode: SHARED LIBRARIES")
|
||||||
add_library(SimGearCore SHARED ${coreSources})
|
add_library(SimGearCore SHARED ${coreSources} ${localExpatSources})
|
||||||
|
|
||||||
# set_property(TARGET SimGearCore PROPERTY FRAMEWORK 1)
|
|
||||||
# message(STATUS "public header: ${publicHeaders}")
|
|
||||||
# set_property(TARGET SimGearCore PROPERTY PUBLIC_HEADER "${publicHeaders}")
|
|
||||||
set_property(TARGET SimGearCore PROPERTY LINKER_LANGUAGE CXX)
|
set_property(TARGET SimGearCore PROPERTY LINKER_LANGUAGE CXX)
|
||||||
|
|
||||||
set_property(TARGET SimGearCore PROPERTY VERSION ${SIMGEAR_VERSION})
|
set_property(TARGET SimGearCore PROPERTY VERSION ${SIMGEAR_VERSION})
|
||||||
set_property(TARGET SimGearCore PROPERTY SOVERSION ${SIMGEAR_SOVERSION})
|
set_property(TARGET SimGearCore PROPERTY SOVERSION ${SIMGEAR_SOVERSION})
|
||||||
|
install(TARGETS SimGearCore EXPORT SimGearCoreConfig LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR})
|
||||||
target_link_libraries(SimGearCore ${ZLIB_LIBRARY} ${RT_LIBRARY}
|
install(EXPORT SimGearCoreConfig DESTINATION share/SimGearCore)
|
||||||
${LibArchive_LIBRARIES}
|
|
||||||
${EXPAT_LIBRARIES}
|
|
||||||
${CMAKE_THREAD_LIBS_INIT}
|
|
||||||
${CORE_SERVICES_LIBRARY})
|
|
||||||
|
|
||||||
if(LIBSVN_FOUND)
|
|
||||||
target_link_libraries(SimGearCore ${LIBSVN_LIBRARIES})
|
|
||||||
endif(LIBSVN_FOUND)
|
|
||||||
|
|
||||||
if(NOT SIMGEAR_HEADLESS)
|
if(NOT SIMGEAR_HEADLESS)
|
||||||
add_library(SimGearScene SHARED ${sceneSources})
|
add_library(SimGearScene SHARED ${sceneSources})
|
||||||
# set_property(TARGET SimGearScene PROPERTY FRAMEWORK 1)
|
|
||||||
# set_property(TARGET SimGearScene PROPERTY PUBLIC_HEADER "${publicHeaders}")
|
|
||||||
set_property(TARGET SimGearScene PROPERTY LINKER_LANGUAGE CXX)
|
set_property(TARGET SimGearScene PROPERTY LINKER_LANGUAGE CXX)
|
||||||
set_property(TARGET SimGearScene PROPERTY VERSION ${SIMGEAR_VERSION})
|
set_property(TARGET SimGearScene PROPERTY VERSION ${SIMGEAR_VERSION})
|
||||||
set_property(TARGET SimGearScene PROPERTY SOVERSION ${SIMGEAR_SOVERSION})
|
set_property(TARGET SimGearScene PROPERTY SOVERSION ${SIMGEAR_SOVERSION})
|
||||||
|
|
||||||
target_link_libraries(SimGearScene
|
# EXPORT SimGearSceneConfig
|
||||||
SimGearCore
|
install(TARGETS SimGearScene LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} )
|
||||||
${ZLIB_LIBRARY}
|
# install(EXPORT SimGearSceneConfig DESTINATION share/SimGearScene)
|
||||||
${OPENSCENEGRAPH_LIBRARIES}
|
|
||||||
${OPENAL_LIBRARY}
|
|
||||||
${OPENGL_LIBRARY}
|
|
||||||
${JPEG_LIBRARY})
|
|
||||||
|
|
||||||
install(TARGETS SimGearScene LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR})
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
install(TARGETS SimGearCore LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR})
|
|
||||||
else()
|
else()
|
||||||
message(STATUS "Library building mode: STATIC LIBRARIES")
|
message(STATUS "Library building mode: STATIC LIBRARIES")
|
||||||
|
|
||||||
@@ -114,7 +89,7 @@ else()
|
|||||||
source_group("${name}\\Headers" FILES ${g2})
|
source_group("${name}\\Headers" FILES ${g2})
|
||||||
endforeach()
|
endforeach()
|
||||||
|
|
||||||
add_library(SimGearCore STATIC ${coreSources})
|
add_library(SimGearCore STATIC ${coreSources} ${localExpatSources})
|
||||||
install(TARGETS SimGearCore ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR})
|
install(TARGETS SimGearCore ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR})
|
||||||
|
|
||||||
if(NOT SIMGEAR_HEADLESS)
|
if(NOT SIMGEAR_HEADLESS)
|
||||||
@@ -141,6 +116,24 @@ else()
|
|||||||
endif(NOT SIMGEAR_HEADLESS)
|
endif(NOT SIMGEAR_HEADLESS)
|
||||||
endif(SIMGEAR_SHARED)
|
endif(SIMGEAR_SHARED)
|
||||||
|
|
||||||
|
target_link_libraries(SimGearCore
|
||||||
|
${ZLIB_LIBRARY}
|
||||||
|
${RT_LIBRARY}
|
||||||
|
${DL_LIBRARY}
|
||||||
|
${EXPAT_LIBRARIES}
|
||||||
|
${CMAKE_THREAD_LIBS_INIT}
|
||||||
|
${CORE_SERVICES_LIBRARY})
|
||||||
|
|
||||||
|
if(NOT SIMGEAR_HEADLESS)
|
||||||
|
target_link_libraries(SimGearScene
|
||||||
|
SimGearCore
|
||||||
|
${ZLIB_LIBRARY}
|
||||||
|
${OPENSCENEGRAPH_LIBRARIES}
|
||||||
|
${OPENAL_LIBRARY}
|
||||||
|
${OPENGL_LIBRARY}
|
||||||
|
${JPEG_LIBRARY})
|
||||||
|
endif()
|
||||||
|
|
||||||
if(ENABLE_RTI)
|
if(ENABLE_RTI)
|
||||||
# Ugly first aid to make hla compile agian
|
# Ugly first aid to make hla compile agian
|
||||||
set_property(SOURCE hla/RTI13InteractionClass.cxx hla/RTI13ObjectClass.cxx
|
set_property(SOURCE hla/RTI13InteractionClass.cxx hla/RTI13ObjectClass.cxx
|
||||||
|
|||||||
@@ -212,7 +212,7 @@ std::string SGBucket::gen_base_path() const {
|
|||||||
main_lat *= -1;
|
main_lat *= -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
sprintf(raw_path, "%c%03d%c%02d/%c%03d%c%02d",
|
snprintf(raw_path, 256, "%c%03d%c%02d/%c%03d%c%02d",
|
||||||
hem, top_lon, pole, top_lat,
|
hem, top_lon, pole, top_lat,
|
||||||
hem, main_lon, pole, main_lat);
|
hem, main_lon, pole, main_lat);
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ set(HEADERS
|
|||||||
canvas_fwd.hxx
|
canvas_fwd.hxx
|
||||||
Canvas.hxx
|
Canvas.hxx
|
||||||
CanvasEvent.hxx
|
CanvasEvent.hxx
|
||||||
CanvasEventListener.hxx
|
|
||||||
CanvasEventManager.hxx
|
CanvasEventManager.hxx
|
||||||
CanvasEventTypes.hxx
|
CanvasEventTypes.hxx
|
||||||
CanvasEventVisitor.hxx
|
CanvasEventVisitor.hxx
|
||||||
@@ -20,7 +19,6 @@ set(HEADERS
|
|||||||
set(SOURCES
|
set(SOURCES
|
||||||
Canvas.cxx
|
Canvas.cxx
|
||||||
CanvasEvent.cxx
|
CanvasEvent.cxx
|
||||||
CanvasEventListener.cxx
|
|
||||||
CanvasEventManager.cxx
|
CanvasEventManager.cxx
|
||||||
CanvasEventVisitor.cxx
|
CanvasEventVisitor.cxx
|
||||||
CanvasMgr.cxx
|
CanvasMgr.cxx
|
||||||
|
|||||||
+46
-21
@@ -74,6 +74,12 @@ namespace canvas
|
|||||||
setStatusFlags(MISSING_SIZE_X | MISSING_SIZE_Y);
|
setStatusFlags(MISSING_SIZE_X | MISSING_SIZE_Y);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
Canvas::~Canvas()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
void Canvas::onDestroy()
|
void Canvas::onDestroy()
|
||||||
{
|
{
|
||||||
@@ -84,19 +90,6 @@ namespace canvas
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
|
||||||
void Canvas::setSystemAdapter(const SystemAdapterPtr& system_adapter)
|
|
||||||
{
|
|
||||||
_system_adapter = system_adapter;
|
|
||||||
_texture.setSystemAdapter(system_adapter);
|
|
||||||
}
|
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
|
||||||
SystemAdapterPtr Canvas::getSystemAdapter() const
|
|
||||||
{
|
|
||||||
return _system_adapter;
|
|
||||||
}
|
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
void Canvas::setCanvasMgr(CanvasMgr* canvas_mgr)
|
void Canvas::setCanvasMgr(CanvasMgr* canvas_mgr)
|
||||||
{
|
{
|
||||||
@@ -221,15 +214,16 @@ namespace canvas
|
|||||||
|
|
||||||
osg::Camera* camera = _texture.getCamera();
|
osg::Camera* camera = _texture.getCamera();
|
||||||
|
|
||||||
|
// TODO Allow custom render order? For now just keep in order with
|
||||||
|
// property tree.
|
||||||
|
camera->setRenderOrder(osg::Camera::PRE_RENDER, _node->getIndex());
|
||||||
|
|
||||||
osg::Vec4 clear_color(0.0f, 0.0f, 0.0f , 1.0f);
|
osg::Vec4 clear_color(0.0f, 0.0f, 0.0f , 1.0f);
|
||||||
parseColor(_node->getStringValue("background"), clear_color);
|
parseColor(_node->getStringValue("background"), clear_color);
|
||||||
camera->setClearColor(clear_color);
|
camera->setClearColor(clear_color);
|
||||||
|
|
||||||
camera->addChild(_root_group->getMatrixTransform());
|
camera->addChild(_root_group->getMatrixTransform());
|
||||||
|
|
||||||
// Ensure objects are drawn in order of traversal
|
|
||||||
camera->getOrCreateStateSet()->setBinName("TraversalOrderBin");
|
|
||||||
|
|
||||||
if( _texture.serviceable() )
|
if( _texture.serviceable() )
|
||||||
{
|
{
|
||||||
setStatusFlags(STATUS_OK);
|
setStatusFlags(STATUS_OK);
|
||||||
@@ -319,12 +313,13 @@ namespace canvas
|
|||||||
}
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
naRef Canvas::addEventListener(const nasal::CallContext& ctx)
|
bool Canvas::addEventListener( const std::string& type,
|
||||||
|
const EventListener& cb )
|
||||||
{
|
{
|
||||||
if( !_root_group.get() )
|
if( !_root_group.get() )
|
||||||
naRuntimeError(ctx.c, "Canvas: No root group!");
|
throw std::runtime_error("Canvas::AddEventListener: no root group!");
|
||||||
|
|
||||||
return _root_group->addEventListener(ctx);
|
return _root_group->addEventListener(type, cb);
|
||||||
}
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
@@ -419,7 +414,8 @@ namespace canvas
|
|||||||
|
|
||||||
EventVisitor visitor( EventVisitor::TRAVERSE_DOWN,
|
EventVisitor visitor( EventVisitor::TRAVERSE_DOWN,
|
||||||
event->getClientPos(),
|
event->getClientPos(),
|
||||||
event->getDelta() );
|
event->getDelta(),
|
||||||
|
_root_group );
|
||||||
if( !_root_group->accept(visitor) )
|
if( !_root_group->accept(visitor) )
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
@@ -585,12 +581,40 @@ namespace canvas
|
|||||||
(
|
(
|
||||||
SG_GENERAL,
|
SG_GENERAL,
|
||||||
SG_WARN,
|
SG_WARN,
|
||||||
"Canvas::addPlacementFactory: replace existing factor for type " << type
|
"Canvas::addPlacementFactory: replace existing factory '" << type << "'"
|
||||||
);
|
);
|
||||||
|
|
||||||
_placement_factories[type] = factory;
|
_placement_factories[type] = factory;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
void Canvas::removePlacementFactory(const std::string& type)
|
||||||
|
{
|
||||||
|
PlacementFactoryMap::iterator it = _placement_factories.find(type);
|
||||||
|
if( it == _placement_factories.end() )
|
||||||
|
SG_LOG
|
||||||
|
(
|
||||||
|
SG_GENERAL,
|
||||||
|
SG_WARN,
|
||||||
|
"Canvas::removePlacementFactory: no such factory '" << type << "'"
|
||||||
|
);
|
||||||
|
else
|
||||||
|
_placement_factories.erase(it);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
void Canvas::setSystemAdapter(const SystemAdapterPtr& system_adapter)
|
||||||
|
{
|
||||||
|
_system_adapter = system_adapter;
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
SystemAdapterPtr Canvas::getSystemAdapter()
|
||||||
|
{
|
||||||
|
return _system_adapter;
|
||||||
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
void Canvas::setSelf(const PropertyBasedElementPtr& self)
|
void Canvas::setSelf(const PropertyBasedElementPtr& self)
|
||||||
{
|
{
|
||||||
@@ -632,6 +656,7 @@ namespace canvas
|
|||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
Canvas::PlacementFactoryMap Canvas::_placement_factories;
|
Canvas::PlacementFactoryMap Canvas::_placement_factories;
|
||||||
|
SystemAdapterPtr Canvas::_system_adapter;
|
||||||
|
|
||||||
} // namespace canvas
|
} // namespace canvas
|
||||||
} // namespace simgear
|
} // namespace simgear
|
||||||
|
|||||||
@@ -29,7 +29,7 @@
|
|||||||
#include <osg/NodeCallback>
|
#include <osg/NodeCallback>
|
||||||
#include <osg/observer_ptr>
|
#include <osg/observer_ptr>
|
||||||
|
|
||||||
#include <memory>
|
#include <boost/scoped_ptr.hpp>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
namespace simgear
|
namespace simgear
|
||||||
@@ -71,11 +71,9 @@ namespace canvas
|
|||||||
typedef osg::ref_ptr<CullCallback> CullCallbackPtr;
|
typedef osg::ref_ptr<CullCallback> CullCallbackPtr;
|
||||||
|
|
||||||
Canvas(SGPropertyNode* node);
|
Canvas(SGPropertyNode* node);
|
||||||
|
virtual ~Canvas();
|
||||||
virtual void onDestroy();
|
virtual void onDestroy();
|
||||||
|
|
||||||
void setSystemAdapter(const SystemAdapterPtr& system_adapter);
|
|
||||||
SystemAdapterPtr getSystemAdapter() const;
|
|
||||||
|
|
||||||
void setCanvasMgr(CanvasMgr* canvas_mgr);
|
void setCanvasMgr(CanvasMgr* canvas_mgr);
|
||||||
CanvasMgr* getCanvasMgr() const;
|
CanvasMgr* getCanvasMgr() const;
|
||||||
|
|
||||||
@@ -132,7 +130,7 @@ namespace canvas
|
|||||||
|
|
||||||
void update(double delta_time_sec);
|
void update(double delta_time_sec);
|
||||||
|
|
||||||
naRef addEventListener(const nasal::CallContext& ctx);
|
bool addEventListener(const std::string& type, const EventListener& cb);
|
||||||
|
|
||||||
void setSizeX(int sx);
|
void setSizeX(int sx);
|
||||||
void setSizeY(int sy);
|
void setSizeY(int sy);
|
||||||
@@ -162,13 +160,23 @@ namespace canvas
|
|||||||
void reloadPlacements( const std::string& type = std::string() );
|
void reloadPlacements( const std::string& type = std::string() );
|
||||||
static void addPlacementFactory( const std::string& type,
|
static void addPlacementFactory( const std::string& type,
|
||||||
PlacementFactory factory );
|
PlacementFactory factory );
|
||||||
|
static void removePlacementFactory(const std::string& type);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set global SystemAdapter for all Canvas/ODGauge instances.
|
||||||
|
*
|
||||||
|
* The SystemAdapter is responsible for application specific operations
|
||||||
|
* like loading images/fonts and adding/removing cameras to the scene
|
||||||
|
* graph.
|
||||||
|
*/
|
||||||
|
static void setSystemAdapter(const SystemAdapterPtr& system_adapter);
|
||||||
|
static SystemAdapterPtr getSystemAdapter();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
SystemAdapterPtr _system_adapter;
|
|
||||||
CanvasMgr *_canvas_mgr;
|
CanvasMgr *_canvas_mgr;
|
||||||
|
|
||||||
std::auto_ptr<EventManager> _event_manager;
|
boost::scoped_ptr<EventManager> _event_manager;
|
||||||
|
|
||||||
int _size_x,
|
int _size_x,
|
||||||
_size_y,
|
_size_y,
|
||||||
@@ -202,6 +210,8 @@ namespace canvas
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
|
static SystemAdapterPtr _system_adapter;
|
||||||
|
|
||||||
Canvas(const Canvas&); // = delete;
|
Canvas(const Canvas&); // = delete;
|
||||||
Canvas& operator=(const Canvas&); // = delete;
|
Canvas& operator=(const Canvas&); // = delete;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -62,6 +62,12 @@ namespace canvas
|
|||||||
return target;
|
return target;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
ElementWeakPtr Event::getCurrentTarget() const
|
||||||
|
{
|
||||||
|
return current_target;
|
||||||
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
double Event::getTime() const
|
double Event::getTime() const
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -41,7 +41,8 @@ namespace canvas
|
|||||||
};
|
};
|
||||||
|
|
||||||
Type type;
|
Type type;
|
||||||
ElementWeakPtr target;
|
ElementWeakPtr target,
|
||||||
|
current_target;
|
||||||
double time;
|
double time;
|
||||||
bool propagation_stopped;
|
bool propagation_stopped;
|
||||||
|
|
||||||
@@ -55,6 +56,7 @@ namespace canvas
|
|||||||
std::string getTypeString() const;
|
std::string getTypeString() const;
|
||||||
|
|
||||||
ElementWeakPtr getTarget() const;
|
ElementWeakPtr getTarget() const;
|
||||||
|
ElementWeakPtr getCurrentTarget() const;
|
||||||
|
|
||||||
double getTime() const;
|
double getTime() const;
|
||||||
|
|
||||||
|
|||||||
@@ -1,70 +0,0 @@
|
|||||||
// Listener for canvas (GUI) events being passed to a Nasal function/code
|
|
||||||
//
|
|
||||||
// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
|
|
||||||
//
|
|
||||||
// This library is free software; you can redistribute it and/or
|
|
||||||
// modify it under the terms of the GNU Library General Public
|
|
||||||
// License as published by the Free Software Foundation; either
|
|
||||||
// version 2 of the License, or (at your option) any later version.
|
|
||||||
//
|
|
||||||
// This library is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
// Library General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Library General Public
|
|
||||||
// License along with this library; if not, write to the Free Software
|
|
||||||
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
|
||||||
|
|
||||||
#include "CanvasEvent.hxx"
|
|
||||||
#include "CanvasEventListener.hxx"
|
|
||||||
#include "CanvasSystemAdapter.hxx"
|
|
||||||
|
|
||||||
#include <simgear/nasal/cppbind/Ghost.hxx>
|
|
||||||
|
|
||||||
namespace simgear
|
|
||||||
{
|
|
||||||
namespace canvas
|
|
||||||
{
|
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
|
||||||
EventListener::EventListener(naRef code, const SystemAdapterPtr& sys_adapter):
|
|
||||||
_code(code),
|
|
||||||
_gc_key(-1),
|
|
||||||
_sys(sys_adapter)
|
|
||||||
{
|
|
||||||
assert( sys_adapter );
|
|
||||||
if( !naIsCode(code)
|
|
||||||
&& !naIsCCode(code)
|
|
||||||
&& !naIsFunc(code) )
|
|
||||||
throw std::runtime_error
|
|
||||||
(
|
|
||||||
"canvas::EventListener: invalid function argument"
|
|
||||||
);
|
|
||||||
|
|
||||||
_gc_key = sys_adapter->gcSave(_code);
|
|
||||||
}
|
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
|
||||||
EventListener::~EventListener()
|
|
||||||
{
|
|
||||||
assert( !_sys.expired() );
|
|
||||||
_sys.lock()->gcRelease(_gc_key);
|
|
||||||
}
|
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
|
||||||
void EventListener::call(const canvas::EventPtr& event)
|
|
||||||
{
|
|
||||||
SystemAdapterPtr sys = _sys.lock();
|
|
||||||
|
|
||||||
naRef args[] = {
|
|
||||||
nasal::Ghost<EventPtr>::create(sys->getNasalContext(), event)
|
|
||||||
};
|
|
||||||
const int num_args = sizeof(args)/sizeof(args[0]);
|
|
||||||
|
|
||||||
sys->callMethod(_code, naNil(), num_args, args, naNil());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
} // namespace canvas
|
|
||||||
} // namespace simgear
|
|
||||||
@@ -19,6 +19,7 @@
|
|||||||
#include "CanvasEventManager.hxx"
|
#include "CanvasEventManager.hxx"
|
||||||
#include "MouseEvent.hxx"
|
#include "MouseEvent.hxx"
|
||||||
#include <simgear/canvas/elements/CanvasElement.hxx>
|
#include <simgear/canvas/elements/CanvasElement.hxx>
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
namespace simgear
|
namespace simgear
|
||||||
{
|
{
|
||||||
@@ -71,6 +72,7 @@ namespace canvas
|
|||||||
bool EventManager::handleEvent( const MouseEventPtr& event,
|
bool EventManager::handleEvent( const MouseEventPtr& event,
|
||||||
const EventPropagationPath& path )
|
const EventPropagationPath& path )
|
||||||
{
|
{
|
||||||
|
bool handled = false;
|
||||||
switch( event->type )
|
switch( event->type )
|
||||||
{
|
{
|
||||||
case Event::MOUSE_DOWN:
|
case Event::MOUSE_DOWN:
|
||||||
@@ -78,20 +80,27 @@ namespace canvas
|
|||||||
break;
|
break;
|
||||||
case Event::MOUSE_UP:
|
case Event::MOUSE_UP:
|
||||||
{
|
{
|
||||||
if( _last_mouse_down.path.empty() )
|
// If the mouse has moved while a button was down (aka. dragging) we
|
||||||
// Ignore mouse up without any previous mouse down
|
// need to notify the original element that the mouse has left it, and
|
||||||
return false;
|
// the new element that it has been entered
|
||||||
|
if( _last_mouse_down.path != path )
|
||||||
|
handled |= handleMove(event, path);
|
||||||
|
|
||||||
// normal mouseup
|
// normal mouseup
|
||||||
propagateEvent(event, path);
|
handled |= propagateEvent(event, path);
|
||||||
|
|
||||||
|
if( _last_mouse_down.path.empty() )
|
||||||
|
// Ignore mouse up without any previous mouse down
|
||||||
|
return handled;
|
||||||
|
|
||||||
// now handle click/dblclick
|
// now handle click/dblclick
|
||||||
if( checkClickDistance(path, _last_mouse_down.path) )
|
if( checkClickDistance(path, _last_mouse_down.path) )
|
||||||
handleClick(event, getCommonAncestor(_last_mouse_down.path, path));
|
handled |=
|
||||||
|
handleClick(event, getCommonAncestor(_last_mouse_down.path, path));
|
||||||
|
|
||||||
_last_mouse_down.clear();
|
_last_mouse_down.clear();
|
||||||
|
|
||||||
return true;
|
return handled;
|
||||||
}
|
}
|
||||||
case Event::DRAG:
|
case Event::DRAG:
|
||||||
if( !_last_mouse_down.valid() )
|
if( !_last_mouse_down.valid() )
|
||||||
@@ -99,11 +108,18 @@ namespace canvas
|
|||||||
else
|
else
|
||||||
return propagateEvent(event, _last_mouse_down.path);
|
return propagateEvent(event, _last_mouse_down.path);
|
||||||
case Event::MOUSE_MOVE:
|
case Event::MOUSE_MOVE:
|
||||||
handleMove(event, path);
|
handled |= handleMove(event, path);
|
||||||
break;
|
break;
|
||||||
case Event::MOUSE_LEAVE:
|
case Event::MOUSE_LEAVE:
|
||||||
// Mouse leaves window and therefore also current mouseover element
|
// Mouse leaves window and therefore also current mouseover element
|
||||||
handleMove(event, EventPropagationPath());
|
handleMove(event, EventPropagationPath());
|
||||||
|
|
||||||
|
// Event is only send if mouse is moved outside the window or dragging
|
||||||
|
// has ended somewhere outside the window. In both cases a mouse button
|
||||||
|
// has been released, so no more mouse down or click...
|
||||||
|
_last_mouse_down.clear();
|
||||||
|
_last_click.clear();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
case Event::WHEEL:
|
case Event::WHEEL:
|
||||||
break;
|
break;
|
||||||
@@ -111,11 +127,11 @@ namespace canvas
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return propagateEvent(event, path);
|
return handled | propagateEvent(event, path);
|
||||||
}
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
void EventManager::handleClick( const MouseEventPtr& event,
|
bool EventManager::handleClick( const MouseEventPtr& event,
|
||||||
const EventPropagationPath& path )
|
const EventPropagationPath& path )
|
||||||
{
|
{
|
||||||
MouseEventPtr click(new MouseEvent(*event));
|
MouseEventPtr click(new MouseEvent(*event));
|
||||||
@@ -145,28 +161,33 @@ namespace canvas
|
|||||||
dbl_click->type = Event::DBL_CLICK;
|
dbl_click->type = Event::DBL_CLICK;
|
||||||
}
|
}
|
||||||
|
|
||||||
propagateEvent(click, path);
|
bool handled = propagateEvent(click, path);
|
||||||
|
|
||||||
if( dbl_click )
|
if( dbl_click )
|
||||||
propagateEvent(dbl_click, getCommonAncestor(_last_click.path, path));
|
handled |= propagateEvent( dbl_click,
|
||||||
|
getCommonAncestor(_last_click.path, path) );
|
||||||
|
|
||||||
_last_click = StampedPropagationPath(path, event->getTime());
|
_last_click = StampedPropagationPath(path, event->getTime());
|
||||||
|
|
||||||
|
return handled;
|
||||||
}
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
void EventManager::handleMove( const MouseEventPtr& event,
|
bool EventManager::handleMove( const MouseEventPtr& event,
|
||||||
const EventPropagationPath& path )
|
const EventPropagationPath& path )
|
||||||
{
|
{
|
||||||
EventPropagationPath& last_path = _last_mouse_over.path;
|
EventPropagationPath& last_path = _last_mouse_over.path;
|
||||||
if( last_path == path )
|
if( last_path == path )
|
||||||
return;
|
return false;
|
||||||
|
|
||||||
|
bool handled = false;
|
||||||
|
|
||||||
// Leave old element
|
// Leave old element
|
||||||
if( !last_path.empty() )
|
if( !last_path.empty() )
|
||||||
{
|
{
|
||||||
MouseEventPtr mouseout(new MouseEvent(*event));
|
MouseEventPtr mouseout(new MouseEvent(*event));
|
||||||
mouseout->type = Event::MOUSE_OUT;
|
mouseout->type = Event::MOUSE_OUT;
|
||||||
propagateEvent(mouseout, last_path);
|
handled |= propagateEvent(mouseout, last_path);
|
||||||
|
|
||||||
// Send a mouseleave event to all ancestors of the currently left element
|
// Send a mouseleave event to all ancestors of the currently left element
|
||||||
// which are not ancestor of the new element currently entered
|
// which are not ancestor of the new element currently entered
|
||||||
@@ -178,7 +199,7 @@ namespace canvas
|
|||||||
|
|
||||||
MouseEventPtr mouseleave(new MouseEvent(*event));
|
MouseEventPtr mouseleave(new MouseEvent(*event));
|
||||||
mouseleave->type = Event::MOUSE_LEAVE;
|
mouseleave->type = Event::MOUSE_LEAVE;
|
||||||
propagateEvent(mouseleave, path_leave);
|
handled |= propagateEvent(mouseleave, path_leave);
|
||||||
|
|
||||||
path_leave.pop_back();
|
path_leave.pop_back();
|
||||||
}
|
}
|
||||||
@@ -189,7 +210,7 @@ namespace canvas
|
|||||||
{
|
{
|
||||||
MouseEventPtr mouseover(new MouseEvent(*event));
|
MouseEventPtr mouseover(new MouseEvent(*event));
|
||||||
mouseover->type = Event::MOUSE_OVER;
|
mouseover->type = Event::MOUSE_OVER;
|
||||||
propagateEvent(mouseover, path);
|
handled |= propagateEvent(mouseover, path);
|
||||||
|
|
||||||
// Send a mouseenter event to all ancestors of the currently entered
|
// Send a mouseenter event to all ancestors of the currently entered
|
||||||
// element which are not ancestor of the old element currently being
|
// element which are not ancestor of the old element currently being
|
||||||
@@ -204,11 +225,12 @@ namespace canvas
|
|||||||
|
|
||||||
MouseEventPtr mouseenter(new MouseEvent(*event));
|
MouseEventPtr mouseenter(new MouseEvent(*event));
|
||||||
mouseenter->type = Event::MOUSE_ENTER;
|
mouseenter->type = Event::MOUSE_ENTER;
|
||||||
propagateEvent(mouseenter, path_enter);
|
handled |= propagateEvent(mouseenter, path_enter);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_last_mouse_over.path = path;
|
_last_mouse_over.path = path;
|
||||||
|
return handled;
|
||||||
}
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
@@ -230,6 +252,21 @@ namespace canvas
|
|||||||
// std::cout << it->element.lock()->getProps()->getPath() << std::endl;
|
// std::cout << it->element.lock()->getProps()->getPath() << std::endl;
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
// Check if event supports bubbling
|
||||||
|
const Event::Type types_no_bubbling[] = {
|
||||||
|
Event::MOUSE_ENTER,
|
||||||
|
Event::MOUSE_LEAVE,
|
||||||
|
};
|
||||||
|
const size_t num_types_no_bubbling = sizeof(types_no_bubbling)
|
||||||
|
/ sizeof(types_no_bubbling[0]);
|
||||||
|
bool do_bubble = true;
|
||||||
|
for( size_t i = 0; i < num_types_no_bubbling; ++i )
|
||||||
|
if( event->type == types_no_bubbling[i] )
|
||||||
|
{
|
||||||
|
do_bubble = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
// Bubbling phase
|
// Bubbling phase
|
||||||
for( EventPropagationPath::const_reverse_iterator
|
for( EventPropagationPath::const_reverse_iterator
|
||||||
it = path.rbegin();
|
it = path.rbegin();
|
||||||
@@ -239,9 +276,14 @@ namespace canvas
|
|||||||
ElementPtr el = it->element.lock();
|
ElementPtr el = it->element.lock();
|
||||||
|
|
||||||
if( !el )
|
if( !el )
|
||||||
|
{
|
||||||
// Ignore element if it has been destroyed while traversing the event
|
// Ignore element if it has been destroyed while traversing the event
|
||||||
// (eg. removed by another event handler)
|
// (eg. removed by another event handler)
|
||||||
continue;
|
if( do_bubble )
|
||||||
|
continue;
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
// TODO provide functions to convert delta to local coordinates on demand.
|
// TODO provide functions to convert delta to local coordinates on demand.
|
||||||
// Maybe also provide a clone method for events as local coordinates
|
// Maybe also provide a clone method for events as local coordinates
|
||||||
@@ -258,9 +300,10 @@ namespace canvas
|
|||||||
//mouse_event->delta = it->local_delta;
|
//mouse_event->delta = it->local_delta;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
event->current_target = el;
|
||||||
el->handleEvent(event);
|
el->handleEvent(event);
|
||||||
|
|
||||||
if( event->propagation_stopped )
|
if( event->propagation_stopped || !do_bubble )
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -273,8 +316,8 @@ namespace canvas
|
|||||||
const EventPropagationPath& path2 ) const
|
const EventPropagationPath& path2 ) const
|
||||||
{
|
{
|
||||||
osg::Vec2 delta = path1.front().local_pos - path2.front().local_pos;
|
osg::Vec2 delta = path1.front().local_pos - path2.front().local_pos;
|
||||||
return delta.x() < drag_threshold
|
return std::fabs(delta.x()) < drag_threshold
|
||||||
&& delta.y() < drag_threshold;
|
&& std::fabs(delta.y()) < drag_threshold;
|
||||||
}
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
|
|||||||
@@ -70,13 +70,13 @@ namespace canvas
|
|||||||
/**
|
/**
|
||||||
* Propagate click event and handle multi-click (eg. create dblclick)
|
* Propagate click event and handle multi-click (eg. create dblclick)
|
||||||
*/
|
*/
|
||||||
void handleClick( const MouseEventPtr& event,
|
bool handleClick( const MouseEventPtr& event,
|
||||||
const EventPropagationPath& path );
|
const EventPropagationPath& path );
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle mouseover/enter/out/leave
|
* Handle mouseover/enter/out/leave
|
||||||
*/
|
*/
|
||||||
void handleMove( const MouseEventPtr& event,
|
bool handleMove( const MouseEventPtr& event,
|
||||||
const EventPropagationPath& path );
|
const EventPropagationPath& path );
|
||||||
|
|
||||||
bool propagateEvent( const EventPtr& event,
|
bool propagateEvent( const EventPtr& event,
|
||||||
|
|||||||
@@ -30,8 +30,10 @@ namespace canvas
|
|||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
EventVisitor::EventVisitor( TraverseMode mode,
|
EventVisitor::EventVisitor( TraverseMode mode,
|
||||||
const osg::Vec2f& pos,
|
const osg::Vec2f& pos,
|
||||||
const osg::Vec2f& delta ):
|
const osg::Vec2f& delta,
|
||||||
_traverse_mode( mode )
|
const ElementPtr& root ):
|
||||||
|
_traverse_mode( mode ),
|
||||||
|
_root(root)
|
||||||
{
|
{
|
||||||
if( mode == TRAVERSE_DOWN )
|
if( mode == TRAVERSE_DOWN )
|
||||||
{
|
{
|
||||||
@@ -70,10 +72,11 @@ namespace canvas
|
|||||||
m(0, 1) * pos[0] + m(1, 1) * pos[1] + m(3, 1)
|
m(0, 1) * pos[0] + m(1, 1) * pos[1] + m(3, 1)
|
||||||
);
|
);
|
||||||
|
|
||||||
// Don't check collision with root element (2nd element in _target_path)
|
// Don't check specified root element for collision, as its purpose is to
|
||||||
// do event listeners attached to the canvas itself (its root group)
|
// catch all events which have no target. This allows for example calling
|
||||||
// always get called even if no element has been hit.
|
// event listeners attached to the canvas itself (its root group) even if
|
||||||
if( _target_path.size() > 1 && !el.hitBound(pos, local_pos) )
|
// no element has been hit.
|
||||||
|
if( _root.get() != &el && !el.hitBound(pos, local_pos) )
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
const osg::Vec2f& delta = _target_path.back().local_delta;
|
const osg::Vec2f& delta = _target_path.back().local_delta;
|
||||||
@@ -86,7 +89,7 @@ namespace canvas
|
|||||||
EventTarget target = {el.getWeakPtr(), local_pos, local_delta};
|
EventTarget target = {el.getWeakPtr(), local_pos, local_delta};
|
||||||
_target_path.push_back(target);
|
_target_path.push_back(target);
|
||||||
|
|
||||||
if( el.traverse(*this) || _target_path.size() <= 2 )
|
if( el.traverse(*this) || &el == _root.get() )
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
_target_path.pop_back();
|
_target_path.pop_back();
|
||||||
|
|||||||
@@ -38,9 +38,17 @@ namespace canvas
|
|||||||
TRAVERSE_DOWN
|
TRAVERSE_DOWN
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param mode
|
||||||
|
* @param pos Mouse position
|
||||||
|
* @param delta Mouse movement since last mouse move event
|
||||||
|
* @param root Element to dispatch events to if no element is hit
|
||||||
|
*/
|
||||||
EventVisitor( TraverseMode mode,
|
EventVisitor( TraverseMode mode,
|
||||||
const osg::Vec2f& pos,
|
const osg::Vec2f& pos,
|
||||||
const osg::Vec2f& delta );
|
const osg::Vec2f& delta,
|
||||||
|
const ElementPtr& root = ElementPtr() );
|
||||||
virtual ~EventVisitor();
|
virtual ~EventVisitor();
|
||||||
virtual bool traverse(Element& el);
|
virtual bool traverse(Element& el);
|
||||||
virtual bool apply(Element& el);
|
virtual bool apply(Element& el);
|
||||||
@@ -51,6 +59,7 @@ namespace canvas
|
|||||||
|
|
||||||
TraverseMode _traverse_mode;
|
TraverseMode _traverse_mode;
|
||||||
EventPropagationPath _target_path;
|
EventPropagationPath _target_path;
|
||||||
|
ElementPtr _root;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -18,6 +18,7 @@
|
|||||||
|
|
||||||
#include "CanvasMgr.hxx"
|
#include "CanvasMgr.hxx"
|
||||||
#include "Canvas.hxx"
|
#include "Canvas.hxx"
|
||||||
|
#include "CanvasEventManager.hxx"
|
||||||
|
|
||||||
#include <boost/bind.hpp>
|
#include <boost/bind.hpp>
|
||||||
|
|
||||||
@@ -35,10 +36,8 @@ namespace canvas
|
|||||||
}
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
CanvasMgr::CanvasMgr( SGPropertyNode_ptr node,
|
CanvasMgr::CanvasMgr(SGPropertyNode_ptr node):
|
||||||
SystemAdapterPtr system_adapter ):
|
PropertyBasedMgr(node, "texture", &canvasFactory)
|
||||||
PropertyBasedMgr(node, "texture", &canvasFactory),
|
|
||||||
_system_adapter(system_adapter)
|
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -65,7 +64,6 @@ namespace canvas
|
|||||||
void CanvasMgr::elementCreated(PropertyBasedElementPtr element)
|
void CanvasMgr::elementCreated(PropertyBasedElementPtr element)
|
||||||
{
|
{
|
||||||
CanvasPtr canvas = boost::static_pointer_cast<Canvas>(element);
|
CanvasPtr canvas = boost::static_pointer_cast<Canvas>(element);
|
||||||
canvas->setSystemAdapter(_system_adapter);
|
|
||||||
canvas->setCanvasMgr(this);
|
canvas->setCanvasMgr(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -34,12 +34,8 @@ namespace canvas
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @param node Root node of branch used to control canvasses
|
* @param node Root node of branch used to control canvasses
|
||||||
* @param system_adapter Adapter for connecting between canvas and
|
|
||||||
* application framework
|
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
CanvasMgr( SGPropertyNode_ptr node,
|
CanvasMgr(SGPropertyNode_ptr node);
|
||||||
SystemAdapterPtr system_adapter );
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new canvas
|
* Create a new canvas
|
||||||
@@ -65,8 +61,6 @@ namespace canvas
|
|||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
SystemAdapterPtr _system_adapter;
|
|
||||||
|
|
||||||
virtual void elementCreated(PropertyBasedElementPtr element);
|
virtual void elementCreated(PropertyBasedElementPtr element);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -20,7 +20,6 @@
|
|||||||
#define SG_CANVAS_SYSTEM_ADAPTER_HXX_
|
#define SG_CANVAS_SYSTEM_ADAPTER_HXX_
|
||||||
|
|
||||||
#include "canvas_fwd.hxx"
|
#include "canvas_fwd.hxx"
|
||||||
#include <simgear/nasal/nasal.h>
|
|
||||||
|
|
||||||
namespace simgear
|
namespace simgear
|
||||||
{
|
{
|
||||||
@@ -36,29 +35,6 @@ namespace canvas
|
|||||||
virtual void addCamera(osg::Camera* camera) const = 0;
|
virtual void addCamera(osg::Camera* camera) const = 0;
|
||||||
virtual void removeCamera(osg::Camera* camera) const = 0;
|
virtual void removeCamera(osg::Camera* camera) const = 0;
|
||||||
virtual osg::Image* getImage(const std::string& path) const = 0;
|
virtual osg::Image* getImage(const std::string& path) const = 0;
|
||||||
|
|
||||||
virtual naContext getNasalContext() const = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Save passed reference to Nasal object from being deleted by the
|
|
||||||
* garbage collector.
|
|
||||||
*/
|
|
||||||
virtual int gcSave(naRef r) = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Release an object previously passed to ::gcSave to allow it being
|
|
||||||
* cleaned up by the garbage collector.
|
|
||||||
*/
|
|
||||||
virtual void gcRelease(int key) = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Call a Nasal function with the given environment and arguments.
|
|
||||||
*/
|
|
||||||
virtual naRef callMethod( naRef code,
|
|
||||||
naRef self,
|
|
||||||
int argc,
|
|
||||||
naRef* args,
|
|
||||||
naRef locals ) = 0;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace canvas
|
} // namespace canvas
|
||||||
|
|||||||
+58
-10
@@ -29,6 +29,7 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "ODGauge.hxx"
|
#include "ODGauge.hxx"
|
||||||
|
#include "Canvas.hxx"
|
||||||
#include "CanvasSystemAdapter.hxx"
|
#include "CanvasSystemAdapter.hxx"
|
||||||
|
|
||||||
#include <simgear/debug/logstream.hxx>
|
#include <simgear/debug/logstream.hxx>
|
||||||
@@ -43,6 +44,7 @@
|
|||||||
#include <osg/ShadeModel>
|
#include <osg/ShadeModel>
|
||||||
#include <osg/StateSet>
|
#include <osg/StateSet>
|
||||||
#include <osg/FrameBufferObject> // for GL_DEPTH_STENCIL_EXT on Windows
|
#include <osg/FrameBufferObject> // for GL_DEPTH_STENCIL_EXT on Windows
|
||||||
|
#include <osgUtil/RenderBin>
|
||||||
|
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
|
|
||||||
@@ -51,6 +53,58 @@ namespace simgear
|
|||||||
namespace canvas
|
namespace canvas
|
||||||
{
|
{
|
||||||
|
|
||||||
|
class PreOrderBin:
|
||||||
|
public osgUtil::RenderBin
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
PreOrderBin()
|
||||||
|
{}
|
||||||
|
PreOrderBin( const RenderBin& rhs,
|
||||||
|
const osg::CopyOp& copyop=osg::CopyOp::SHALLOW_COPY ):
|
||||||
|
RenderBin(rhs, copyop)
|
||||||
|
{}
|
||||||
|
|
||||||
|
virtual osg::Object* cloneType() const
|
||||||
|
{
|
||||||
|
return new PreOrderBin();
|
||||||
|
}
|
||||||
|
virtual osg::Object* clone(const osg::CopyOp& copyop) const
|
||||||
|
{
|
||||||
|
return new PreOrderBin(*this,copyop);
|
||||||
|
}
|
||||||
|
virtual bool isSameKindAs(const osg::Object* obj) const
|
||||||
|
{
|
||||||
|
return dynamic_cast<const PreOrderBin*>(obj) != 0L;
|
||||||
|
}
|
||||||
|
virtual const char* className() const
|
||||||
|
{
|
||||||
|
return "PreOrderBin";
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void sort()
|
||||||
|
{
|
||||||
|
// Do not sort to keep traversal order...
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifndef OSG_INIT_SINGLETON_PROXY
|
||||||
|
/**
|
||||||
|
* http://svn.openscenegraph.org/osg/OpenSceneGraph/trunk/include/osg/Object
|
||||||
|
*
|
||||||
|
* Helper macro that creates a static proxy object to call singleton function
|
||||||
|
* on it's construction, ensuring that the singleton gets initialized at
|
||||||
|
* startup.
|
||||||
|
*/
|
||||||
|
# define OSG_INIT_SINGLETON_PROXY(ProxyName, Func)\
|
||||||
|
static struct ProxyName{ ProxyName() { Func; } } s_##ProxyName;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
OSG_INIT_SINGLETON_PROXY(
|
||||||
|
PreOrderBinProxy,
|
||||||
|
(osgUtil::RenderBin::addRenderBinPrototype("PreOrderBin", new PreOrderBin))
|
||||||
|
);
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
ODGauge::ODGauge():
|
ODGauge::ODGauge():
|
||||||
_size_x( -1 ),
|
_size_x( -1 ),
|
||||||
@@ -70,12 +124,6 @@ namespace canvas
|
|||||||
clear();
|
clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
|
||||||
void ODGauge::setSystemAdapter(const SystemAdapterPtr& system_adapter)
|
|
||||||
{
|
|
||||||
_system_adapter = system_adapter;
|
|
||||||
}
|
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
void ODGauge::setSize(int size_x, int size_y)
|
void ODGauge::setSize(int size_x, int size_y)
|
||||||
{
|
{
|
||||||
@@ -209,8 +257,8 @@ namespace canvas
|
|||||||
updateSampling();
|
updateSampling();
|
||||||
updateBlendMode();
|
updateBlendMode();
|
||||||
|
|
||||||
if( _system_adapter )
|
if( Canvas::getSystemAdapter() )
|
||||||
_system_adapter->addCamera(camera.get());
|
Canvas::getSystemAdapter()->addCamera(camera.get());
|
||||||
|
|
||||||
_flags |= AVAILABLE;
|
_flags |= AVAILABLE;
|
||||||
}
|
}
|
||||||
@@ -226,8 +274,8 @@ namespace canvas
|
|||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
void ODGauge::clear()
|
void ODGauge::clear()
|
||||||
{
|
{
|
||||||
if( camera.valid() && _system_adapter )
|
if( camera.valid() && Canvas::getSystemAdapter() )
|
||||||
_system_adapter->removeCamera(camera.get());
|
Canvas::getSystemAdapter()->removeCamera(camera.get());
|
||||||
camera.release();
|
camera.release();
|
||||||
texture.release();
|
texture.release();
|
||||||
|
|
||||||
|
|||||||
@@ -53,8 +53,6 @@ namespace canvas
|
|||||||
ODGauge();
|
ODGauge();
|
||||||
virtual ~ODGauge();
|
virtual ~ODGauge();
|
||||||
|
|
||||||
void setSystemAdapter(const SystemAdapterPtr& system_adapter);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the size of the render target.
|
* Set the size of the render target.
|
||||||
*
|
*
|
||||||
@@ -136,8 +134,6 @@ namespace canvas
|
|||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
SystemAdapterPtr _system_adapter;
|
|
||||||
|
|
||||||
int _size_x,
|
int _size_x,
|
||||||
_size_y,
|
_size_y,
|
||||||
_view_width,
|
_view_width,
|
||||||
|
|||||||
@@ -51,7 +51,6 @@ namespace canvas
|
|||||||
SG_FWD_DECL(Text)
|
SG_FWD_DECL(Text)
|
||||||
|
|
||||||
SG_FWD_DECL(Event)
|
SG_FWD_DECL(Event)
|
||||||
SG_FWD_DECL(EventListener)
|
|
||||||
SG_FWD_DECL(MouseEvent)
|
SG_FWD_DECL(MouseEvent)
|
||||||
SG_FWD_DECL(Placement)
|
SG_FWD_DECL(Placement)
|
||||||
SG_FWD_DECL(SystemAdapter)
|
SG_FWD_DECL(SystemAdapter)
|
||||||
@@ -73,6 +72,8 @@ namespace canvas
|
|||||||
typedef boost::function<Placements( SGPropertyNode*,
|
typedef boost::function<Placements( SGPropertyNode*,
|
||||||
CanvasPtr )> PlacementFactory;
|
CanvasPtr )> PlacementFactory;
|
||||||
|
|
||||||
|
typedef boost::function<void(const EventPtr&)> EventListener;
|
||||||
|
|
||||||
} // namespace canvas
|
} // namespace canvas
|
||||||
} // namespace simgear
|
} // namespace simgear
|
||||||
|
|
||||||
|
|||||||
@@ -17,10 +17,10 @@
|
|||||||
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||||
|
|
||||||
#include "CanvasElement.hxx"
|
#include "CanvasElement.hxx"
|
||||||
#include <simgear/canvas/Canvas.hxx>
|
|
||||||
#include <simgear/canvas/CanvasEventListener.hxx>
|
|
||||||
#include <simgear/canvas/CanvasEventVisitor.hxx>
|
#include <simgear/canvas/CanvasEventVisitor.hxx>
|
||||||
#include <simgear/canvas/MouseEvent.hxx>
|
#include <simgear/canvas/MouseEvent.hxx>
|
||||||
|
#include <simgear/math/SGMisc.hxx>
|
||||||
|
#include <simgear/misc/strutils.hxx>
|
||||||
#include <simgear/scene/material/parseBlendFunc.hxx>
|
#include <simgear/scene/material/parseBlendFunc.hxx>
|
||||||
|
|
||||||
#include <osg/Drawable>
|
#include <osg/Drawable>
|
||||||
@@ -29,11 +29,10 @@
|
|||||||
|
|
||||||
#include <boost/algorithm/string/predicate.hpp>
|
#include <boost/algorithm/string/predicate.hpp>
|
||||||
#include <boost/foreach.hpp>
|
#include <boost/foreach.hpp>
|
||||||
#include <boost/lexical_cast.hpp>
|
|
||||||
#include <boost/make_shared.hpp>
|
#include <boost/make_shared.hpp>
|
||||||
#include <boost/tokenizer.hpp>
|
|
||||||
|
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
|
#include <cmath>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
|
||||||
namespace simgear
|
namespace simgear
|
||||||
@@ -42,6 +41,63 @@ namespace canvas
|
|||||||
{
|
{
|
||||||
const std::string NAME_TRANSFORM = "tf";
|
const std::string NAME_TRANSFORM = "tf";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* glScissor with coordinates relative to different reference frames.
|
||||||
|
*/
|
||||||
|
class Element::RelativeScissor:
|
||||||
|
public osg::Scissor
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
ReferenceFrame _coord_reference;
|
||||||
|
osg::Matrix _parent_inverse;
|
||||||
|
|
||||||
|
RelativeScissor():
|
||||||
|
_coord_reference(GLOBAL)
|
||||||
|
{}
|
||||||
|
|
||||||
|
virtual void apply(osg::State& state) const
|
||||||
|
{
|
||||||
|
const osg::Viewport* vp = state.getCurrentViewport();
|
||||||
|
float w2 = 0.5 * vp->width(),
|
||||||
|
h2 = 0.5 * vp->height();
|
||||||
|
|
||||||
|
osg::Matrix model_view
|
||||||
|
(
|
||||||
|
w2, 0, 0, 0,
|
||||||
|
0, h2, 0, 0,
|
||||||
|
0, 0, 1, 0,
|
||||||
|
w2, h2, 0, 1
|
||||||
|
);
|
||||||
|
model_view.preMult(state.getProjectionMatrix());
|
||||||
|
|
||||||
|
if( _coord_reference != GLOBAL )
|
||||||
|
{
|
||||||
|
model_view.preMult(state.getModelViewMatrix());
|
||||||
|
|
||||||
|
if( _coord_reference == PARENT )
|
||||||
|
model_view.preMult(_parent_inverse);
|
||||||
|
}
|
||||||
|
|
||||||
|
const osg::Vec2 scale( model_view(0,0), model_view(1,1)),
|
||||||
|
offset(model_view(3,0), model_view(3,1));
|
||||||
|
|
||||||
|
// TODO check/warn for rotation?
|
||||||
|
|
||||||
|
GLint x = SGMiscf::roundToInt(scale.x() * _x + offset.x()),
|
||||||
|
y = SGMiscf::roundToInt(scale.y() * _y + offset.y()),
|
||||||
|
w = SGMiscf::roundToInt(std::fabs(scale.x()) * _width),
|
||||||
|
h = SGMiscf::roundToInt(std::fabs(scale.y()) * _height);
|
||||||
|
|
||||||
|
if( scale.x() < 0 )
|
||||||
|
x -= w;
|
||||||
|
if( scale.y() < 0 )
|
||||||
|
y -= h;
|
||||||
|
|
||||||
|
glScissor(x, y, w, h);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
Element::OSGUserData::OSGUserData(ElementPtr element):
|
Element::OSGUserData::OSGUserData(ElementPtr element):
|
||||||
element(element)
|
element(element)
|
||||||
@@ -52,7 +108,19 @@ namespace canvas
|
|||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
Element::~Element()
|
Element::~Element()
|
||||||
{
|
{
|
||||||
|
if( !_transform.valid() )
|
||||||
|
return;
|
||||||
|
|
||||||
|
for(unsigned int i = 0; i < _transform->getNumChildren(); ++i)
|
||||||
|
{
|
||||||
|
OSGUserData* ud =
|
||||||
|
static_cast<OSGUserData*>(_transform->getChild(i)->getUserData());
|
||||||
|
|
||||||
|
if( ud )
|
||||||
|
// Ensure parent is cleared to prevent accessing released memory if an
|
||||||
|
// element somehow survives longer than his parent.
|
||||||
|
ud->element->_parent = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
@@ -86,6 +154,12 @@ namespace canvas
|
|||||||
return boost::static_pointer_cast<Element>(_self.lock());
|
return boost::static_pointer_cast<Element>(_self.lock());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
ElementPtr Element::getParent()
|
||||||
|
{
|
||||||
|
return _parent ? _parent->getWeakPtr().lock() : ElementPtr();
|
||||||
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
void Element::update(double dt)
|
void Element::update(double dt)
|
||||||
{
|
{
|
||||||
@@ -93,64 +167,15 @@ namespace canvas
|
|||||||
// Don't do anything if element is hidden
|
// Don't do anything if element is hidden
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if( _transform_dirty )
|
// Trigger matrix update
|
||||||
|
getMatrix();
|
||||||
|
|
||||||
|
if( _attributes_dirty & SCISSOR_COORDS )
|
||||||
{
|
{
|
||||||
osg::Matrix m;
|
if( _scissor && _scissor->_coord_reference != GLOBAL )
|
||||||
for( size_t i = 0; i < _transform_types.size(); ++i )
|
_scissor->_parent_inverse = _transform->getInverseMatrix();
|
||||||
{
|
|
||||||
// Skip unused indizes...
|
|
||||||
if( _transform_types[i] == TT_NONE )
|
|
||||||
continue;
|
|
||||||
|
|
||||||
SGPropertyNode* tf_node = _node->getChild("tf", i, true);
|
_attributes_dirty &= ~SCISSOR_COORDS;
|
||||||
|
|
||||||
// Build up the matrix representation of the current transform node
|
|
||||||
osg::Matrix tf;
|
|
||||||
switch( _transform_types[i] )
|
|
||||||
{
|
|
||||||
case TT_MATRIX:
|
|
||||||
tf = osg::Matrix( tf_node->getDoubleValue("m[0]", 1),
|
|
||||||
tf_node->getDoubleValue("m[1]", 0),
|
|
||||||
0,
|
|
||||||
tf_node->getDoubleValue("m[6]", 0),
|
|
||||||
|
|
||||||
tf_node->getDoubleValue("m[2]", 0),
|
|
||||||
tf_node->getDoubleValue("m[3]", 1),
|
|
||||||
0,
|
|
||||||
tf_node->getDoubleValue("m[7]", 0),
|
|
||||||
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
1,
|
|
||||||
0,
|
|
||||||
|
|
||||||
tf_node->getDoubleValue("m[4]", 0),
|
|
||||||
tf_node->getDoubleValue("m[5]", 0),
|
|
||||||
0,
|
|
||||||
tf_node->getDoubleValue("m[8]", 1) );
|
|
||||||
break;
|
|
||||||
case TT_TRANSLATE:
|
|
||||||
tf.makeTranslate( osg::Vec3f( tf_node->getDoubleValue("t[0]", 0),
|
|
||||||
tf_node->getDoubleValue("t[1]", 0),
|
|
||||||
0 ) );
|
|
||||||
break;
|
|
||||||
case TT_ROTATE:
|
|
||||||
tf.makeRotate( tf_node->getDoubleValue("rot", 0), 0, 0, 1 );
|
|
||||||
break;
|
|
||||||
case TT_SCALE:
|
|
||||||
{
|
|
||||||
float sx = tf_node->getDoubleValue("s[0]", 1);
|
|
||||||
// sy defaults to sx...
|
|
||||||
tf.makeScale( sx, tf_node->getDoubleValue("s[1]", sx), 1 );
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
m.postMult( tf );
|
|
||||||
}
|
|
||||||
_transform->setMatrix(m);
|
|
||||||
_transform_dirty = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update bounding box on manual update (manual updates pass zero dt)
|
// Update bounding box on manual update (manual updates pass zero dt)
|
||||||
@@ -173,31 +198,28 @@ namespace canvas
|
|||||||
}
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
naRef Element::addEventListener(const nasal::CallContext& ctx)
|
bool Element::addEventListener( const std::string& type_str,
|
||||||
|
const EventListener& cb )
|
||||||
{
|
{
|
||||||
const std::string type_str = ctx.requireArg<std::string>(0);
|
|
||||||
naRef code = ctx.requireArg<naRef>(1);
|
|
||||||
|
|
||||||
SG_LOG
|
SG_LOG
|
||||||
(
|
(
|
||||||
SG_NASAL,
|
SG_GENERAL,
|
||||||
SG_INFO,
|
SG_INFO,
|
||||||
"addEventListener(" << _node->getPath() << ", " << type_str << ")"
|
"addEventListener(" << _node->getPath() << ", " << type_str << ")"
|
||||||
);
|
);
|
||||||
|
|
||||||
Event::Type type = Event::strToType(type_str);
|
Event::Type type = Event::strToType(type_str);
|
||||||
if( type == Event::UNKNOWN )
|
if( type == Event::UNKNOWN )
|
||||||
naRuntimeError( ctx.c,
|
{
|
||||||
"addEventListener: Unknown event type %s",
|
SG_LOG( SG_GENERAL,
|
||||||
type_str.c_str() );
|
SG_WARN,
|
||||||
|
"addEventListener: Unknown event type " << type_str );
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
_listener[ type ].push_back
|
_listener[ type ].push_back(cb);
|
||||||
(
|
|
||||||
boost::make_shared<EventListener>( code,
|
|
||||||
_canvas.lock()->getSystemAdapter() )
|
|
||||||
);
|
|
||||||
|
|
||||||
return naNil();
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
@@ -209,7 +231,7 @@ namespace canvas
|
|||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
bool Element::accept(EventVisitor& visitor)
|
bool Element::accept(EventVisitor& visitor)
|
||||||
{
|
{
|
||||||
if( !_transform.valid() )
|
if( !isVisible() )
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return visitor.apply(*this);
|
return visitor.apply(*this);
|
||||||
@@ -236,8 +258,8 @@ namespace canvas
|
|||||||
if( listeners == _listener.end() )
|
if( listeners == _listener.end() )
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
BOOST_FOREACH(EventListenerPtr listener, listeners->second)
|
BOOST_FOREACH(EventListener const& listener, listeners->second)
|
||||||
listener->call(event);
|
listener(event);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -259,7 +281,7 @@ namespace canvas
|
|||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
bool Element::isVisible() const
|
bool Element::isVisible() const
|
||||||
{
|
{
|
||||||
return _transform->getNodeMask() != 0;
|
return _transform.valid() && _transform->getNodeMask() != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
@@ -284,7 +306,7 @@ namespace canvas
|
|||||||
_transform_types.resize( child->getIndex() + 1 );
|
_transform_types.resize( child->getIndex() + 1 );
|
||||||
|
|
||||||
_transform_types[ child->getIndex() ] = TT_NONE;
|
_transform_types[ child->getIndex() ] = TT_NONE;
|
||||||
_transform_dirty = true;
|
_attributes_dirty |= TRANSFORM;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
else if( parent->getParent() == _node
|
else if( parent->getParent() == _node
|
||||||
@@ -305,7 +327,7 @@ namespace canvas
|
|||||||
else if( name == "s" )
|
else if( name == "s" )
|
||||||
type = TT_SCALE;
|
type = TT_SCALE;
|
||||||
|
|
||||||
_transform_dirty = true;
|
_attributes_dirty |= TRANSFORM;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -315,29 +337,37 @@ namespace canvas
|
|||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
void Element::childRemoved(SGPropertyNode* parent, SGPropertyNode* child)
|
void Element::childRemoved(SGPropertyNode* parent, SGPropertyNode* child)
|
||||||
{
|
{
|
||||||
if( parent == _node && child->getNameString() == NAME_TRANSFORM )
|
if( parent == _node )
|
||||||
{
|
{
|
||||||
if( !_transform.valid() )
|
if( child->getNameString() == NAME_TRANSFORM )
|
||||||
return;
|
|
||||||
|
|
||||||
if( child->getIndex() >= static_cast<int>(_transform_types.size()) )
|
|
||||||
{
|
{
|
||||||
SG_LOG
|
if( !_transform.valid() )
|
||||||
(
|
return;
|
||||||
SG_GENERAL,
|
|
||||||
SG_WARN,
|
if( child->getIndex() >= static_cast<int>(_transform_types.size()) )
|
||||||
"Element::childRemoved: unknown transform: " << child->getPath()
|
{
|
||||||
);
|
SG_LOG
|
||||||
|
(
|
||||||
|
SG_GENERAL,
|
||||||
|
SG_WARN,
|
||||||
|
"Element::childRemoved: unknown transform: " << child->getPath()
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_transform_types[ child->getIndex() ] = TT_NONE;
|
||||||
|
|
||||||
|
while( !_transform_types.empty() && _transform_types.back() == TT_NONE )
|
||||||
|
_transform_types.pop_back();
|
||||||
|
|
||||||
|
_attributes_dirty |= TRANSFORM;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
else if( StyleInfo const* style = getStyleInfo(child->getNameString()) )
|
||||||
_transform_types[ child->getIndex() ] = TT_NONE;
|
{
|
||||||
|
if( setStyle(getParentStyle(child), style) )
|
||||||
while( !_transform_types.empty() && _transform_types.back() == TT_NONE )
|
return;
|
||||||
_transform_types.pop_back();
|
}
|
||||||
|
|
||||||
_transform_dirty = true;
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
childRemoved(child);
|
childRemoved(child);
|
||||||
@@ -350,8 +380,17 @@ namespace canvas
|
|||||||
if( parent == _node )
|
if( parent == _node )
|
||||||
{
|
{
|
||||||
const std::string& name = child->getNameString();
|
const std::string& name = child->getNameString();
|
||||||
if( setStyle(child) )
|
if( StyleInfo const* style_info = getStyleInfo(name) )
|
||||||
|
{
|
||||||
|
SGPropertyNode const* style = child;
|
||||||
|
if( isStyleEmpty(child) )
|
||||||
|
{
|
||||||
|
child->clearValue();
|
||||||
|
style = getParentStyle(child);
|
||||||
|
}
|
||||||
|
setStyle(style, style_info);
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
else if( name == "update" )
|
else if( name == "update" )
|
||||||
return update(0);
|
return update(0);
|
||||||
else if( name == "visible" )
|
else if( name == "visible" )
|
||||||
@@ -363,7 +402,7 @@ namespace canvas
|
|||||||
else if( parent->getParent() == _node
|
else if( parent->getParent() == _node
|
||||||
&& parent->getNameString() == NAME_TRANSFORM )
|
&& parent->getNameString() == NAME_TRANSFORM )
|
||||||
{
|
{
|
||||||
_transform_dirty = true;
|
_attributes_dirty |= TRANSFORM;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -371,21 +410,10 @@ namespace canvas
|
|||||||
}
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
bool Element::setStyle(const SGPropertyNode* child)
|
bool Element::setStyle( const SGPropertyNode* child,
|
||||||
|
const StyleInfo* style_info )
|
||||||
{
|
{
|
||||||
StyleSetters::const_iterator setter =
|
return canApplyStyle(child) && setStyleImpl(child, style_info);
|
||||||
_style_setters.find(child->getNameString());
|
|
||||||
if( setter == _style_setters.end() )
|
|
||||||
return false;
|
|
||||||
|
|
||||||
const StyleSetter* style_setter = &setter->second.setter;
|
|
||||||
while( style_setter )
|
|
||||||
{
|
|
||||||
if( style_setter->func(*this, child) )
|
|
||||||
return true;
|
|
||||||
style_setter = style_setter->next;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
@@ -394,6 +422,7 @@ namespace canvas
|
|||||||
if( clip.empty() || clip == "auto" )
|
if( clip.empty() || clip == "auto" )
|
||||||
{
|
{
|
||||||
getOrCreateStateSet()->removeAttribute(osg::StateAttribute::SCISSOR);
|
getOrCreateStateSet()->removeAttribute(osg::StateAttribute::SCISSOR);
|
||||||
|
_scissor = 0;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -406,17 +435,22 @@ namespace canvas
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
|
const std::string sep(", \t\npx");
|
||||||
const boost::char_separator<char> del(", \t\npx");
|
|
||||||
|
|
||||||
tokenizer tokens(clip.begin() + RECT.size(), clip.end() - 1, del);
|
|
||||||
int comp = 0;
|
int comp = 0;
|
||||||
int values[4];
|
float values[4];
|
||||||
for( tokenizer::const_iterator tok = tokens.begin();
|
|
||||||
tok != tokens.end() && comp < 4;
|
for(size_t pos = RECT.size(); comp < 4; ++comp)
|
||||||
++tok, ++comp )
|
|
||||||
{
|
{
|
||||||
values[comp] = boost::lexical_cast<int>(*tok);
|
pos = clip.find_first_not_of(sep, pos);
|
||||||
|
if( pos == std::string::npos || pos == clip.size() - 1 )
|
||||||
|
break;
|
||||||
|
|
||||||
|
char *end = 0;
|
||||||
|
values[comp] = strtod(&clip[pos], &end);
|
||||||
|
if( end == &clip[pos] || !end )
|
||||||
|
break;
|
||||||
|
|
||||||
|
pos = end - &clip[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
if( comp < 4 )
|
if( comp < 4 )
|
||||||
@@ -425,32 +459,37 @@ namespace canvas
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
float scale_x = 1,
|
float width = values[1] - values[3],
|
||||||
scale_y = 1;
|
height = values[2] - values[0];
|
||||||
|
|
||||||
CanvasPtr canvas = _canvas.lock();
|
if( width < 0 || height < 0 )
|
||||||
if( canvas )
|
|
||||||
{
|
{
|
||||||
// The scissor rectangle isn't affected by any transformation, so we need
|
SG_LOG(SG_GENERAL, SG_WARN, "Canvas: negative clip size: " << clip);
|
||||||
// to convert to image/canvas coordinates on our selves.
|
return;
|
||||||
scale_x = canvas->getSizeX()
|
|
||||||
/ static_cast<float>(canvas->getViewWidth());
|
|
||||||
scale_y = canvas->getSizeY()
|
|
||||||
/ static_cast<float>(canvas->getViewHeight());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
osg::Scissor* scissor = new osg::Scissor();
|
_scissor = new RelativeScissor();
|
||||||
// <top>, <right>, <bottom>, <left>
|
// <top>, <right>, <bottom>, <left>
|
||||||
scissor->x() = scale_x * values[3];
|
_scissor->x() = SGMiscf::roundToInt(values[3]);
|
||||||
scissor->y() = scale_y * values[0];
|
_scissor->y() = SGMiscf::roundToInt(values[0]);
|
||||||
scissor->width() = scale_x * (values[1] - values[3]);
|
_scissor->width() = SGMiscf::roundToInt(width);
|
||||||
scissor->height() = scale_y * (values[2] - values[0]);
|
_scissor->height() = SGMiscf::roundToInt(height);
|
||||||
|
|
||||||
if( canvas )
|
getOrCreateStateSet()->setAttributeAndModes(_scissor);
|
||||||
// Canvas has y axis upside down
|
|
||||||
scissor->y() = canvas->getSizeY() - scissor->y() - scissor->height();
|
|
||||||
|
|
||||||
getOrCreateStateSet()->setAttributeAndModes(scissor);
|
SGPropertyNode* clip_frame = _node->getChild("clip-frame", 0);
|
||||||
|
if( clip_frame )
|
||||||
|
valueChanged(clip_frame);
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
void Element::setClipFrame(ReferenceFrame rf)
|
||||||
|
{
|
||||||
|
if( _scissor )
|
||||||
|
{
|
||||||
|
_scissor->_coord_reference = rf;
|
||||||
|
_attributes_dirty |= SCISSOR_COORDS;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
@@ -481,11 +520,78 @@ namespace canvas
|
|||||||
osg::BoundingBox transformed;
|
osg::BoundingBox transformed;
|
||||||
const osg::BoundingBox& bb = _drawable->getBound();
|
const osg::BoundingBox& bb = _drawable->getBound();
|
||||||
for(int i = 0; i < 4; ++i)
|
for(int i = 0; i < 4; ++i)
|
||||||
transformed.expandBy( m * bb.corner(i) );
|
transformed.expandBy( bb.corner(i) * m );
|
||||||
|
|
||||||
return transformed;
|
return transformed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
osg::Matrix Element::getMatrix() const
|
||||||
|
{
|
||||||
|
if( !(_attributes_dirty & TRANSFORM) )
|
||||||
|
return _transform->getMatrix();
|
||||||
|
|
||||||
|
osg::Matrix m;
|
||||||
|
for( size_t i = 0; i < _transform_types.size(); ++i )
|
||||||
|
{
|
||||||
|
// Skip unused indizes...
|
||||||
|
if( _transform_types[i] == TT_NONE )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
SGPropertyNode* tf_node = _node->getChild("tf", i, true);
|
||||||
|
|
||||||
|
// Build up the matrix representation of the current transform node
|
||||||
|
osg::Matrix tf;
|
||||||
|
switch( _transform_types[i] )
|
||||||
|
{
|
||||||
|
case TT_MATRIX:
|
||||||
|
tf = osg::Matrix( tf_node->getDoubleValue("m[0]", 1),
|
||||||
|
tf_node->getDoubleValue("m[1]", 0),
|
||||||
|
0,
|
||||||
|
tf_node->getDoubleValue("m[6]", 0),
|
||||||
|
|
||||||
|
tf_node->getDoubleValue("m[2]", 0),
|
||||||
|
tf_node->getDoubleValue("m[3]", 1),
|
||||||
|
0,
|
||||||
|
tf_node->getDoubleValue("m[7]", 0),
|
||||||
|
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
|
||||||
|
tf_node->getDoubleValue("m[4]", 0),
|
||||||
|
tf_node->getDoubleValue("m[5]", 0),
|
||||||
|
0,
|
||||||
|
tf_node->getDoubleValue("m[8]", 1) );
|
||||||
|
break;
|
||||||
|
case TT_TRANSLATE:
|
||||||
|
tf.makeTranslate( osg::Vec3f( tf_node->getDoubleValue("t[0]", 0),
|
||||||
|
tf_node->getDoubleValue("t[1]", 0),
|
||||||
|
0 ) );
|
||||||
|
break;
|
||||||
|
case TT_ROTATE:
|
||||||
|
tf.makeRotate( tf_node->getDoubleValue("rot", 0), 0, 0, 1 );
|
||||||
|
break;
|
||||||
|
case TT_SCALE:
|
||||||
|
{
|
||||||
|
float sx = tf_node->getDoubleValue("s[0]", 1);
|
||||||
|
// sy defaults to sx...
|
||||||
|
tf.makeScale( sx, tf_node->getDoubleValue("s[1]", sx), 1 );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
m.postMult( tf );
|
||||||
|
}
|
||||||
|
_transform->setMatrix(m);
|
||||||
|
_attributes_dirty &= ~TRANSFORM;
|
||||||
|
_attributes_dirty |= SCISSOR_COORDS;
|
||||||
|
|
||||||
|
return m;
|
||||||
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
Element::StyleSetters Element::_style_setters;
|
Element::StyleSetters Element::_style_setters;
|
||||||
|
|
||||||
@@ -498,11 +604,13 @@ namespace canvas
|
|||||||
_canvas( canvas ),
|
_canvas( canvas ),
|
||||||
_parent( parent ),
|
_parent( parent ),
|
||||||
_attributes_dirty( 0 ),
|
_attributes_dirty( 0 ),
|
||||||
_transform_dirty( false ),
|
|
||||||
_transform( new osg::MatrixTransform ),
|
_transform( new osg::MatrixTransform ),
|
||||||
_style( parent_style ),
|
_style( parent_style ),
|
||||||
|
_scissor( 0 ),
|
||||||
_drawable( 0 )
|
_drawable( 0 )
|
||||||
{
|
{
|
||||||
|
staticInit();
|
||||||
|
|
||||||
SG_LOG
|
SG_LOG
|
||||||
(
|
(
|
||||||
SG_GL,
|
SG_GL,
|
||||||
@@ -510,10 +618,93 @@ namespace canvas
|
|||||||
"New canvas element " << node->getPath()
|
"New canvas element " << node->getPath()
|
||||||
);
|
);
|
||||||
|
|
||||||
if( !isInit<Element>() )
|
// Ensure elements are drawn in order they appear in the element tree
|
||||||
|
_transform->getOrCreateStateSet()
|
||||||
|
->setRenderBinDetails
|
||||||
|
(
|
||||||
|
0,
|
||||||
|
"PreOrderBin",
|
||||||
|
osg::StateSet::OVERRIDE_RENDERBIN_DETAILS
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
void Element::staticInit()
|
||||||
|
{
|
||||||
|
if( isInit<Element>() )
|
||||||
|
return;
|
||||||
|
|
||||||
|
addStyle("clip", "", &Element::setClip, false);
|
||||||
|
addStyle("clip-frame", "", &Element::setClipFrame, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
bool Element::isStyleEmpty(const SGPropertyNode* child) const
|
||||||
|
{
|
||||||
|
return !child
|
||||||
|
|| simgear::strutils::strip(child->getStringValue()).empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
bool Element::canApplyStyle(const SGPropertyNode* child) const
|
||||||
|
{
|
||||||
|
if( _node == child->getParent() )
|
||||||
|
return true;
|
||||||
|
|
||||||
|
// Parent values do not override if element has own value
|
||||||
|
return isStyleEmpty( _node->getChild(child->getName()) );
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
bool Element::setStyleImpl( const SGPropertyNode* child,
|
||||||
|
const StyleInfo* style_info )
|
||||||
|
{
|
||||||
|
const StyleSetter* style_setter = style_info
|
||||||
|
? &style_info->setter
|
||||||
|
: getStyleSetter(child->getNameString());
|
||||||
|
while( style_setter )
|
||||||
{
|
{
|
||||||
addStyle("clip", "", &Element::setClip);
|
if( style_setter->func(*this, child) )
|
||||||
|
return true;
|
||||||
|
style_setter = style_setter->next;
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
const Element::StyleInfo*
|
||||||
|
Element::getStyleInfo(const std::string& name) const
|
||||||
|
{
|
||||||
|
StyleSetters::const_iterator setter = _style_setters.find(name);
|
||||||
|
if( setter == _style_setters.end() )
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return &setter->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
const Element::StyleSetter*
|
||||||
|
Element::getStyleSetter(const std::string& name) const
|
||||||
|
{
|
||||||
|
const StyleInfo* info = getStyleInfo(name);
|
||||||
|
return info ? &info->setter : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
const SGPropertyNode*
|
||||||
|
Element::getParentStyle(const SGPropertyNode* child) const
|
||||||
|
{
|
||||||
|
// Try to get value from parent...
|
||||||
|
if( _parent )
|
||||||
|
{
|
||||||
|
Style::const_iterator style =
|
||||||
|
_parent->_style.find(child->getNameString());
|
||||||
|
if( style != _parent->_style.end() )
|
||||||
|
return style->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ...or reset to default if none is available
|
||||||
|
return child; // TODO somehow get default value for each style?
|
||||||
}
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
|
|||||||
@@ -23,13 +23,13 @@
|
|||||||
#include <simgear/canvas/CanvasEvent.hxx>
|
#include <simgear/canvas/CanvasEvent.hxx>
|
||||||
#include <simgear/props/PropertyBasedElement.hxx>
|
#include <simgear/props/PropertyBasedElement.hxx>
|
||||||
#include <simgear/misc/stdint.hxx> // for uint32_t
|
#include <simgear/misc/stdint.hxx> // for uint32_t
|
||||||
#include <simgear/nasal/cppbind/Ghost.hxx>
|
|
||||||
|
|
||||||
#include <osg/BoundingBox>
|
#include <osg/BoundingBox>
|
||||||
#include <osg/MatrixTransform>
|
#include <osg/MatrixTransform>
|
||||||
|
|
||||||
#include <boost/bind.hpp>
|
#include <boost/bind.hpp>
|
||||||
#include <boost/function.hpp>
|
#include <boost/function.hpp>
|
||||||
|
#include <boost/type_traits/is_base_of.hpp>
|
||||||
|
|
||||||
namespace osg
|
namespace osg
|
||||||
{
|
{
|
||||||
@@ -71,6 +71,19 @@ namespace canvas
|
|||||||
{
|
{
|
||||||
StyleSetter setter; ///!< Function(s) for setting this style
|
StyleSetter setter; ///!< Function(s) for setting this style
|
||||||
std::string type; ///!< Interpolation type
|
std::string type; ///!< Interpolation type
|
||||||
|
bool inheritable; ///!< Whether children can inherit this style from
|
||||||
|
/// their parents
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Coordinate reference frame (eg. "clip" property)
|
||||||
|
*/
|
||||||
|
enum ReferenceFrame
|
||||||
|
{
|
||||||
|
GLOBAL, ///!< Global coordinates
|
||||||
|
PARENT, ///!< Coordinates relative to parent coordinate frame
|
||||||
|
LOCAL ///!< Coordinates relative to local coordinates (parent
|
||||||
|
/// coordinates with local transformations applied)
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -82,6 +95,7 @@ namespace canvas
|
|||||||
virtual void onDestroy();
|
virtual void onDestroy();
|
||||||
|
|
||||||
ElementWeakPtr getWeakPtr() const;
|
ElementWeakPtr getWeakPtr() const;
|
||||||
|
ElementPtr getParent();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called every frame to update internal state
|
* Called every frame to update internal state
|
||||||
@@ -90,7 +104,7 @@ namespace canvas
|
|||||||
*/
|
*/
|
||||||
virtual void update(double dt);
|
virtual void update(double dt);
|
||||||
|
|
||||||
naRef addEventListener(const nasal::CallContext& ctx);
|
bool addEventListener(const std::string& type, const EventListener& cb);
|
||||||
virtual void clearEventListener();
|
virtual void clearEventListener();
|
||||||
|
|
||||||
virtual bool accept(EventVisitor& visitor);
|
virtual bool accept(EventVisitor& visitor);
|
||||||
@@ -117,7 +131,8 @@ namespace canvas
|
|||||||
SGPropertyNode * child );
|
SGPropertyNode * child );
|
||||||
virtual void valueChanged(SGPropertyNode * child);
|
virtual void valueChanged(SGPropertyNode * child);
|
||||||
|
|
||||||
virtual bool setStyle(const SGPropertyNode* child);
|
virtual bool setStyle( const SGPropertyNode* child,
|
||||||
|
const StyleInfo* style_info = 0 );
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set clipping shape
|
* Set clipping shape
|
||||||
@@ -127,6 +142,11 @@ namespace canvas
|
|||||||
*/
|
*/
|
||||||
void setClip(const std::string& clip);
|
void setClip(const std::string& clip);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clipping coordinates reference frame
|
||||||
|
*/
|
||||||
|
void setClipFrame(ReferenceFrame rf);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Write the given bounding box to the property tree
|
* Write the given bounding box to the property tree
|
||||||
*/
|
*/
|
||||||
@@ -137,6 +157,11 @@ namespace canvas
|
|||||||
*/
|
*/
|
||||||
virtual osg::BoundingBox getTransformedBounds(const osg::Matrix& m) const;
|
virtual osg::BoundingBox getTransformedBounds(const osg::Matrix& m) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the transformation matrix (product of all transforms)
|
||||||
|
*/
|
||||||
|
osg::Matrix getMatrix() const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create an canvas Element
|
* Create an canvas Element
|
||||||
*
|
*
|
||||||
@@ -161,8 +186,10 @@ namespace canvas
|
|||||||
|
|
||||||
enum Attributes
|
enum Attributes
|
||||||
{
|
{
|
||||||
BLEND_FUNC = 0x0001,
|
TRANSFORM = 1,
|
||||||
LAST_ATTRIBUTE = BLEND_FUNC << 1
|
BLEND_FUNC = TRANSFORM << 1,
|
||||||
|
SCISSOR_COORDS = BLEND_FUNC << 1,
|
||||||
|
LAST_ATTRIBUTE = SCISSOR_COORDS << 1
|
||||||
};
|
};
|
||||||
|
|
||||||
enum TransformType
|
enum TransformType
|
||||||
@@ -174,19 +201,21 @@ namespace canvas
|
|||||||
TT_SCALE
|
TT_SCALE
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class RelativeScissor;
|
||||||
|
|
||||||
CanvasWeakPtr _canvas;
|
CanvasWeakPtr _canvas;
|
||||||
Element *_parent;
|
Element *_parent;
|
||||||
|
|
||||||
uint32_t _attributes_dirty;
|
mutable uint32_t _attributes_dirty;
|
||||||
|
|
||||||
bool _transform_dirty;
|
|
||||||
osg::observer_ptr<osg::MatrixTransform> _transform;
|
osg::observer_ptr<osg::MatrixTransform> _transform;
|
||||||
std::vector<TransformType> _transform_types;
|
std::vector<TransformType> _transform_types;
|
||||||
|
|
||||||
Style _style;
|
Style _style;
|
||||||
std::vector<SGPropertyNode_ptr> _bounding_box;
|
std::vector<SGPropertyNode_ptr> _bounding_box;
|
||||||
|
RelativeScissor *_scissor;
|
||||||
|
|
||||||
typedef std::vector<EventListenerPtr> Listener;
|
typedef std::vector<EventListener> Listener;
|
||||||
typedef std::map<Event::Type, Listener> ListenerMap;
|
typedef std::map<Event::Type, Listener> ListenerMap;
|
||||||
|
|
||||||
ListenerMap _listener;
|
ListenerMap _listener;
|
||||||
@@ -194,6 +223,8 @@ namespace canvas
|
|||||||
typedef std::map<std::string, StyleInfo> StyleSetters;
|
typedef std::map<std::string, StyleInfo> StyleSetters;
|
||||||
static StyleSetters _style_setters;
|
static StyleSetters _style_setters;
|
||||||
|
|
||||||
|
static void staticInit();
|
||||||
|
|
||||||
Element( const CanvasWeakPtr& canvas,
|
Element( const CanvasWeakPtr& canvas,
|
||||||
const SGPropertyNode_ptr& node,
|
const SGPropertyNode_ptr& node,
|
||||||
const Style& parent_style,
|
const Style& parent_style,
|
||||||
@@ -207,7 +238,7 @@ namespace canvas
|
|||||||
* @tparam Derived (Derived) class type
|
* @tparam Derived (Derived) class type
|
||||||
*/
|
*/
|
||||||
template<class Derived>
|
template<class Derived>
|
||||||
bool isInit() const
|
static bool isInit()
|
||||||
{
|
{
|
||||||
static bool is_init = false;
|
static bool is_init = false;
|
||||||
if( is_init )
|
if( is_init )
|
||||||
@@ -236,10 +267,12 @@ namespace canvas
|
|||||||
typename T2,
|
typename T2,
|
||||||
class Derived
|
class Derived
|
||||||
>
|
>
|
||||||
|
static
|
||||||
StyleSetter
|
StyleSetter
|
||||||
addStyle( const std::string& name,
|
addStyle( const std::string& name,
|
||||||
const std::string& type,
|
const std::string& type,
|
||||||
const boost::function<void (Derived&, T2)>& setter )
|
const boost::function<void (Derived&, T2)>& setter,
|
||||||
|
bool inheritable = true )
|
||||||
{
|
{
|
||||||
StyleInfo& style_info = _style_setters[ name ];
|
StyleInfo& style_info = _style_setters[ name ];
|
||||||
if( !type.empty() )
|
if( !type.empty() )
|
||||||
@@ -255,6 +288,8 @@ namespace canvas
|
|||||||
|
|
||||||
style_info.type = type;
|
style_info.type = type;
|
||||||
}
|
}
|
||||||
|
// TODO check if changed?
|
||||||
|
style_info.inheritable = inheritable;
|
||||||
|
|
||||||
StyleSetter* style = &style_info.setter;
|
StyleSetter* style = &style_info.setter;
|
||||||
while( style->next )
|
while( style->next )
|
||||||
@@ -276,28 +311,33 @@ namespace canvas
|
|||||||
typename T,
|
typename T,
|
||||||
class Derived
|
class Derived
|
||||||
>
|
>
|
||||||
|
static
|
||||||
StyleSetter
|
StyleSetter
|
||||||
addStyle( const std::string& name,
|
addStyle( const std::string& name,
|
||||||
const std::string& type,
|
const std::string& type,
|
||||||
const boost::function<void (Derived&, T)>& setter )
|
const boost::function<void (Derived&, T)>& setter,
|
||||||
|
bool inheritable = true )
|
||||||
{
|
{
|
||||||
return addStyle<T, T>(name, type, setter);
|
return addStyle<T, T>(name, type, setter, inheritable);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<
|
template<
|
||||||
typename T,
|
typename T,
|
||||||
class Derived
|
class Derived
|
||||||
>
|
>
|
||||||
|
static
|
||||||
StyleSetter
|
StyleSetter
|
||||||
addStyle( const std::string& name,
|
addStyle( const std::string& name,
|
||||||
const std::string& type,
|
const std::string& type,
|
||||||
void (Derived::*setter)(T) )
|
void (Derived::*setter)(T),
|
||||||
|
bool inheritable = true )
|
||||||
{
|
{
|
||||||
return addStyle<T, T>
|
return addStyle<T, T>
|
||||||
(
|
(
|
||||||
name,
|
name,
|
||||||
type,
|
type,
|
||||||
boost::function<void (Derived&, T)>(setter)
|
boost::function<void (Derived&, T)>(setter),
|
||||||
|
inheritable
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -306,32 +346,38 @@ namespace canvas
|
|||||||
typename T2,
|
typename T2,
|
||||||
class Derived
|
class Derived
|
||||||
>
|
>
|
||||||
|
static
|
||||||
StyleSetterFunc
|
StyleSetterFunc
|
||||||
addStyle( const std::string& name,
|
addStyle( const std::string& name,
|
||||||
const std::string& type,
|
const std::string& type,
|
||||||
void (Derived::*setter)(T2) )
|
void (Derived::*setter)(T2),
|
||||||
|
bool inheritable = true )
|
||||||
{
|
{
|
||||||
return addStyle<T1>
|
return addStyle<T1>
|
||||||
(
|
(
|
||||||
name,
|
name,
|
||||||
type,
|
type,
|
||||||
boost::function<void (Derived&, T2)>(setter)
|
boost::function<void (Derived&, T2)>(setter),
|
||||||
|
inheritable
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<
|
template<
|
||||||
class Derived
|
class Derived
|
||||||
>
|
>
|
||||||
|
static
|
||||||
StyleSetter
|
StyleSetter
|
||||||
addStyle( const std::string& name,
|
addStyle( const std::string& name,
|
||||||
const std::string& type,
|
const std::string& type,
|
||||||
void (Derived::*setter)(const std::string&) )
|
void (Derived::*setter)(const std::string&),
|
||||||
|
bool inheritable = true )
|
||||||
{
|
{
|
||||||
return addStyle<const char*, const std::string&>
|
return addStyle<const char*, const std::string&>
|
||||||
(
|
(
|
||||||
name,
|
name,
|
||||||
type,
|
type,
|
||||||
boost::function<void (Derived&, const std::string&)>(setter)
|
boost::function<void (Derived&, const std::string&)>(setter),
|
||||||
|
inheritable
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -341,13 +387,21 @@ namespace canvas
|
|||||||
class Other,
|
class Other,
|
||||||
class OtherRef
|
class OtherRef
|
||||||
>
|
>
|
||||||
|
static
|
||||||
StyleSetter
|
StyleSetter
|
||||||
addStyle( const std::string& name,
|
addStyle( const std::string& name,
|
||||||
const std::string& type,
|
const std::string& type,
|
||||||
void (Other::*setter)(T),
|
void (Other::*setter)(T),
|
||||||
OtherRef Derived::*instance_ref )
|
OtherRef Derived::*instance_ref,
|
||||||
|
bool inheritable = true )
|
||||||
{
|
{
|
||||||
return addStyle<T, T>(name, type, bindOther(setter, instance_ref));
|
return addStyle<T, T>
|
||||||
|
(
|
||||||
|
name,
|
||||||
|
type,
|
||||||
|
bindOther(setter, instance_ref),
|
||||||
|
inheritable
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<
|
template<
|
||||||
@@ -357,13 +411,21 @@ namespace canvas
|
|||||||
class Other,
|
class Other,
|
||||||
class OtherRef
|
class OtherRef
|
||||||
>
|
>
|
||||||
|
static
|
||||||
StyleSetter
|
StyleSetter
|
||||||
addStyle( const std::string& name,
|
addStyle( const std::string& name,
|
||||||
const std::string& type,
|
const std::string& type,
|
||||||
void (Other::*setter)(T2),
|
void (Other::*setter)(T2),
|
||||||
OtherRef Derived::*instance_ref )
|
OtherRef Derived::*instance_ref,
|
||||||
|
bool inheritable = true )
|
||||||
{
|
{
|
||||||
return addStyle<T1>(name, type, bindOther(setter, instance_ref));
|
return addStyle<T1>
|
||||||
|
(
|
||||||
|
name,
|
||||||
|
type,
|
||||||
|
bindOther(setter, instance_ref),
|
||||||
|
inheritable
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<
|
template<
|
||||||
@@ -373,13 +435,21 @@ namespace canvas
|
|||||||
class Other,
|
class Other,
|
||||||
class OtherRef
|
class OtherRef
|
||||||
>
|
>
|
||||||
|
static
|
||||||
StyleSetter
|
StyleSetter
|
||||||
addStyle( const std::string& name,
|
addStyle( const std::string& name,
|
||||||
const std::string& type,
|
const std::string& type,
|
||||||
const boost::function<void (Other&, T2)>& setter,
|
const boost::function<void (Other&, T2)>& setter,
|
||||||
OtherRef Derived::*instance_ref )
|
OtherRef Derived::*instance_ref,
|
||||||
|
bool inheritable = true )
|
||||||
{
|
{
|
||||||
return addStyle<T1>(name, type, bindOther(setter, instance_ref));
|
return addStyle<T1>
|
||||||
|
(
|
||||||
|
name,
|
||||||
|
type,
|
||||||
|
bindOther(setter, instance_ref),
|
||||||
|
inheritable
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<
|
template<
|
||||||
@@ -387,22 +457,26 @@ namespace canvas
|
|||||||
class Other,
|
class Other,
|
||||||
class OtherRef
|
class OtherRef
|
||||||
>
|
>
|
||||||
|
static
|
||||||
StyleSetter
|
StyleSetter
|
||||||
addStyle( const std::string& name,
|
addStyle( const std::string& name,
|
||||||
const std::string& type,
|
const std::string& type,
|
||||||
void (Other::*setter)(const std::string&),
|
void (Other::*setter)(const std::string&),
|
||||||
OtherRef Derived::*instance_ref )
|
OtherRef Derived::*instance_ref,
|
||||||
|
bool inheritable = true )
|
||||||
{
|
{
|
||||||
return addStyle<const char*, const std::string&>
|
return addStyle<const char*, const std::string&>
|
||||||
(
|
(
|
||||||
name,
|
name,
|
||||||
type,
|
type,
|
||||||
boost::function<void (Other&, const std::string&)>(setter),
|
boost::function<void (Other&, const std::string&)>(setter),
|
||||||
instance_ref
|
instance_ref,
|
||||||
|
inheritable
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T, class Derived, class Other, class OtherRef>
|
template<typename T, class Derived, class Other, class OtherRef>
|
||||||
|
static
|
||||||
boost::function<void (Derived&, T)>
|
boost::function<void (Derived&, T)>
|
||||||
bindOther( void (Other::*setter)(T), OtherRef Derived::*instance_ref )
|
bindOther( void (Other::*setter)(T), OtherRef Derived::*instance_ref )
|
||||||
{
|
{
|
||||||
@@ -410,6 +484,7 @@ namespace canvas
|
|||||||
}
|
}
|
||||||
|
|
||||||
template<typename T, class Derived, class Other, class OtherRef>
|
template<typename T, class Derived, class Other, class OtherRef>
|
||||||
|
static
|
||||||
boost::function<void (Derived&, T)>
|
boost::function<void (Derived&, T)>
|
||||||
bindOther( const boost::function<void (Other&, T)>& setter,
|
bindOther( const boost::function<void (Other&, T)>& setter,
|
||||||
OtherRef Derived::*instance_ref )
|
OtherRef Derived::*instance_ref )
|
||||||
@@ -427,6 +502,7 @@ namespace canvas
|
|||||||
}
|
}
|
||||||
|
|
||||||
template<typename T1, typename T2, class Derived>
|
template<typename T1, typename T2, class Derived>
|
||||||
|
static
|
||||||
StyleSetterFuncUnchecked
|
StyleSetterFuncUnchecked
|
||||||
bindStyleSetter( const std::string& name,
|
bindStyleSetter( const std::string& name,
|
||||||
const boost::function<void (Derived&, T2)>& setter )
|
const boost::function<void (Derived&, T2)>& setter )
|
||||||
@@ -441,6 +517,15 @@ namespace canvas
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool isStyleEmpty(const SGPropertyNode* child) const;
|
||||||
|
bool canApplyStyle(const SGPropertyNode* child) const;
|
||||||
|
bool setStyleImpl( const SGPropertyNode* child,
|
||||||
|
const StyleInfo* style_info = 0 );
|
||||||
|
|
||||||
|
const StyleInfo* getStyleInfo(const std::string& name) const;
|
||||||
|
const StyleSetter* getStyleSetter(const std::string& name) const;
|
||||||
|
const SGPropertyNode* getParentStyle(const SGPropertyNode* child) const;
|
||||||
|
|
||||||
virtual void childAdded(SGPropertyNode * child) {}
|
virtual void childAdded(SGPropertyNode * child) {}
|
||||||
virtual void childRemoved(SGPropertyNode * child){}
|
virtual void childRemoved(SGPropertyNode * child){}
|
||||||
virtual void childChanged(SGPropertyNode * child){}
|
virtual void childChanged(SGPropertyNode * child){}
|
||||||
@@ -495,6 +580,27 @@ namespace canvas
|
|||||||
};
|
};
|
||||||
|
|
||||||
} // namespace canvas
|
} // namespace canvas
|
||||||
|
|
||||||
|
template<>
|
||||||
|
struct enum_traits<canvas::Element::ReferenceFrame>
|
||||||
|
{
|
||||||
|
static const char* name()
|
||||||
|
{
|
||||||
|
return "canvas::Element::ReferenceFrame";
|
||||||
|
}
|
||||||
|
|
||||||
|
static canvas::Element::ReferenceFrame defVal()
|
||||||
|
{
|
||||||
|
return canvas::Element::GLOBAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool validate(int frame)
|
||||||
|
{
|
||||||
|
return frame >= canvas::Element::GLOBAL
|
||||||
|
&& frame <= canvas::Element::LOCAL;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
} // namespace simgear
|
} // namespace simgear
|
||||||
|
|
||||||
#endif /* CANVAS_ELEMENT_HXX_ */
|
#endif /* CANVAS_ELEMENT_HXX_ */
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ namespace canvas
|
|||||||
template<typename ElementType>
|
template<typename ElementType>
|
||||||
void add(ElementFactories& factories)
|
void add(ElementFactories& factories)
|
||||||
{
|
{
|
||||||
|
ElementType::staticInit();
|
||||||
factories[ElementType::TYPE_NAME] = &Element::create<ElementType>;
|
factories[ElementType::TYPE_NAME] = &Element::create<ElementType>;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -45,6 +46,26 @@ namespace canvas
|
|||||||
ElementFactories Group::_child_factories;
|
ElementFactories Group::_child_factories;
|
||||||
const std::string Group::TYPE_NAME = "group";
|
const std::string Group::TYPE_NAME = "group";
|
||||||
|
|
||||||
|
void warnTransformExpired(const char* member_name)
|
||||||
|
{
|
||||||
|
SG_LOG( SG_GENERAL,
|
||||||
|
SG_WARN,
|
||||||
|
"canvas::Group::" << member_name << ": Group has expired." );
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
void Group::staticInit()
|
||||||
|
{
|
||||||
|
if( isInit<Group>() )
|
||||||
|
return;
|
||||||
|
|
||||||
|
add<Group>(_child_factories);
|
||||||
|
add<Image>(_child_factories);
|
||||||
|
add<Map >(_child_factories);
|
||||||
|
add<Path >(_child_factories);
|
||||||
|
add<Text >(_child_factories);
|
||||||
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
Group::Group( const CanvasWeakPtr& canvas,
|
Group::Group( const CanvasWeakPtr& canvas,
|
||||||
const SGPropertyNode_ptr& node,
|
const SGPropertyNode_ptr& node,
|
||||||
@@ -52,14 +73,7 @@ namespace canvas
|
|||||||
Element* parent ):
|
Element* parent ):
|
||||||
Element(canvas, node, parent_style, parent)
|
Element(canvas, node, parent_style, parent)
|
||||||
{
|
{
|
||||||
if( !isInit<Group>() )
|
staticInit();
|
||||||
{
|
|
||||||
add<Group>(_child_factories);
|
|
||||||
add<Image>(_child_factories);
|
|
||||||
add<Map >(_child_factories);
|
|
||||||
add<Path >(_child_factories);
|
|
||||||
add<Text >(_child_factories);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
@@ -119,8 +133,13 @@ namespace canvas
|
|||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
ElementPtr Group::getElementById(const std::string& id)
|
ElementPtr Group::getElementById(const std::string& id)
|
||||||
{
|
{
|
||||||
std::vector<GroupPtr> groups;
|
if( !_transform.valid() )
|
||||||
|
{
|
||||||
|
warnTransformExpired("getElementById");
|
||||||
|
return ElementPtr();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<GroupPtr> groups;
|
||||||
for(size_t i = 0; i < _transform->getNumChildren(); ++i)
|
for(size_t i = 0; i < _transform->getNumChildren(); ++i)
|
||||||
{
|
{
|
||||||
const ElementPtr& el = getChildByIndex(i);
|
const ElementPtr& el = getChildByIndex(i);
|
||||||
@@ -145,6 +164,8 @@ namespace canvas
|
|||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
void Group::clearEventListener()
|
void Group::clearEventListener()
|
||||||
{
|
{
|
||||||
|
if( !_transform.valid() )
|
||||||
|
return warnTransformExpired("clearEventListener");
|
||||||
|
|
||||||
for(size_t i = 0; i < _transform->getNumChildren(); ++i)
|
for(size_t i = 0; i < _transform->getNumChildren(); ++i)
|
||||||
getChildByIndex(i)->clearEventListener();
|
getChildByIndex(i)->clearEventListener();
|
||||||
@@ -174,21 +195,23 @@ namespace canvas
|
|||||||
}
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
bool Group::setStyle(const SGPropertyNode* style)
|
bool Group::setStyle( const SGPropertyNode* style,
|
||||||
|
const StyleInfo* style_info )
|
||||||
{
|
{
|
||||||
// Don't propagate styles directly applicable to this group
|
if( !canApplyStyle(style) )
|
||||||
if( Element::setStyle(style) )
|
|
||||||
return true;
|
|
||||||
|
|
||||||
if( style->getParent() != _node
|
|
||||||
&& _style.find(style->getNameString()) != _style.end() )
|
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
bool handled = false;
|
bool handled = setStyleImpl(style, style_info);
|
||||||
for(size_t i = 0; i < _transform->getNumChildren(); ++i)
|
if( style_info->inheritable )
|
||||||
{
|
{
|
||||||
if( getChildByIndex(i)->setStyle(style) )
|
if( !_transform.valid() )
|
||||||
handled = true;
|
{
|
||||||
|
warnTransformExpired("setStyle");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(size_t i = 0; i < _transform->getNumChildren(); ++i)
|
||||||
|
handled |= getChildByIndex(i)->setStyle(style, style_info);
|
||||||
}
|
}
|
||||||
|
|
||||||
return handled;
|
return handled;
|
||||||
@@ -198,6 +221,11 @@ namespace canvas
|
|||||||
osg::BoundingBox Group::getTransformedBounds(const osg::Matrix& m) const
|
osg::BoundingBox Group::getTransformedBounds(const osg::Matrix& m) const
|
||||||
{
|
{
|
||||||
osg::BoundingBox bb;
|
osg::BoundingBox bb;
|
||||||
|
if( !_transform.valid() )
|
||||||
|
{
|
||||||
|
warnTransformExpired("getTransformedBounds");
|
||||||
|
return bb;
|
||||||
|
}
|
||||||
|
|
||||||
for(size_t i = 0; i < _transform->getNumChildren(); ++i)
|
for(size_t i = 0; i < _transform->getNumChildren(); ++i)
|
||||||
{
|
{
|
||||||
@@ -236,6 +264,9 @@ namespace canvas
|
|||||||
ElementFactory child_factory = getChildFactory( child->getNameString() );
|
ElementFactory child_factory = getChildFactory( child->getNameString() );
|
||||||
if( child_factory )
|
if( child_factory )
|
||||||
{
|
{
|
||||||
|
if( !_transform.valid() )
|
||||||
|
return warnTransformExpired("childAdded");
|
||||||
|
|
||||||
ElementPtr element = child_factory(_canvas, child, _style, this);
|
ElementPtr element = child_factory(_canvas, child, _style, this);
|
||||||
|
|
||||||
// Add to osg scene graph...
|
// Add to osg scene graph...
|
||||||
@@ -247,12 +278,9 @@ namespace canvas
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if( !Element::setStyle(child) )
|
StyleInfo const* style = getStyleInfo(child->getNameString());
|
||||||
{
|
if( style && style->inheritable )
|
||||||
// Only add style if not applicable to group itself
|
|
||||||
_style[ child->getNameString() ] = child;
|
_style[ child->getNameString() ] = child;
|
||||||
setStyle(child);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
@@ -285,9 +313,7 @@ namespace canvas
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Style::iterator style = _style.find(node->getNameString());
|
_style.erase( node->getNameString() );
|
||||||
if( style != _style.end() )
|
|
||||||
_style.erase(style);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -303,7 +329,7 @@ namespace canvas
|
|||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
void Group::handleZIndexChanged(ElementPtr child, int z_index)
|
void Group::handleZIndexChanged(ElementPtr child, int z_index)
|
||||||
{
|
{
|
||||||
if( !child )
|
if( !child || !_transform.valid() )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
osg::ref_ptr<osg::MatrixTransform> tf = child->getMatrixTransform();
|
osg::ref_ptr<osg::MatrixTransform> tf = child->getMatrixTransform();
|
||||||
@@ -364,6 +390,12 @@ namespace canvas
|
|||||||
ElementPtr Group::findChild( const SGPropertyNode* node,
|
ElementPtr Group::findChild( const SGPropertyNode* node,
|
||||||
const std::string& id ) const
|
const std::string& id ) const
|
||||||
{
|
{
|
||||||
|
if( !_transform.valid() )
|
||||||
|
{
|
||||||
|
warnTransformExpired("findChild");
|
||||||
|
return ElementPtr();
|
||||||
|
}
|
||||||
|
|
||||||
for(size_t i = 0; i < _transform->getNumChildren(); ++i)
|
for(size_t i = 0; i < _transform->getNumChildren(); ++i)
|
||||||
{
|
{
|
||||||
ElementPtr el = getChildByIndex(i);
|
ElementPtr el = getChildByIndex(i);
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ namespace canvas
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
static const std::string TYPE_NAME;
|
static const std::string TYPE_NAME;
|
||||||
|
static void staticInit();
|
||||||
|
|
||||||
typedef std::list< std::pair< const SGPropertyNode*,
|
typedef std::list< std::pair< const SGPropertyNode*,
|
||||||
ElementPtr
|
ElementPtr
|
||||||
@@ -92,7 +93,8 @@ namespace canvas
|
|||||||
|
|
||||||
virtual bool traverse(EventVisitor& visitor);
|
virtual bool traverse(EventVisitor& visitor);
|
||||||
|
|
||||||
virtual bool setStyle(const SGPropertyNode* child);
|
virtual bool setStyle( const SGPropertyNode* child,
|
||||||
|
const StyleInfo* style_info = 0 );
|
||||||
|
|
||||||
virtual osg::BoundingBox getTransformedBounds(const osg::Matrix& m) const;
|
virtual osg::BoundingBox getTransformedBounds(const osg::Matrix& m) const;
|
||||||
|
|
||||||
|
|||||||
@@ -89,6 +89,18 @@ namespace canvas
|
|||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
const std::string Image::TYPE_NAME = "image";
|
const std::string Image::TYPE_NAME = "image";
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
void Image::staticInit()
|
||||||
|
{
|
||||||
|
if( isInit<Image>() )
|
||||||
|
return;
|
||||||
|
|
||||||
|
addStyle("fill", "color", &Image::setFill);
|
||||||
|
addStyle("slice", "", &Image::setSlice);
|
||||||
|
addStyle("slice-width", "", &Image::setSliceWidth);
|
||||||
|
addStyle("outset", "", &Image::setOutset);
|
||||||
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
Image::Image( const CanvasWeakPtr& canvas,
|
Image::Image( const CanvasWeakPtr& canvas,
|
||||||
const SGPropertyNode_ptr& node,
|
const SGPropertyNode_ptr& node,
|
||||||
@@ -100,6 +112,8 @@ namespace canvas
|
|||||||
_src_rect(0,0),
|
_src_rect(0,0),
|
||||||
_region(0,0)
|
_region(0,0)
|
||||||
{
|
{
|
||||||
|
staticInit();
|
||||||
|
|
||||||
_geom = new osg::Geometry;
|
_geom = new osg::Geometry;
|
||||||
_geom->setUseDisplayList(false);
|
_geom->setUseDisplayList(false);
|
||||||
|
|
||||||
@@ -128,16 +142,7 @@ namespace canvas
|
|||||||
|
|
||||||
setDrawable(_geom);
|
setDrawable(_geom);
|
||||||
|
|
||||||
if( !isInit<Image>() )
|
|
||||||
{
|
|
||||||
addStyle("fill", "color", &Image::setFill);
|
|
||||||
addStyle("slice", "", &Image::setSlice);
|
|
||||||
addStyle("slice-width", "", &Image::setSliceWidth);
|
|
||||||
addStyle("outset", "", &Image::setOutset);
|
|
||||||
}
|
|
||||||
|
|
||||||
setFill("#ffffff"); // TODO how should we handle default values?
|
setFill("#ffffff"); // TODO how should we handle default values?
|
||||||
|
|
||||||
setupStyle();
|
setupStyle();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -400,8 +405,9 @@ namespace canvas
|
|||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
void Image::setFill(const std::string& fill)
|
void Image::setFill(const std::string& fill)
|
||||||
{
|
{
|
||||||
osg::Vec4 color;
|
osg::Vec4 color(1,1,1,1);
|
||||||
if( !parseColor(fill, color) )
|
if( !fill.empty() // If no color is given default to white
|
||||||
|
&& !parseColor(fill, color) )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
_colors->front() = color;
|
_colors->front() = color;
|
||||||
@@ -559,7 +565,7 @@ namespace canvas
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
setImage( canvas->getSystemAdapter()->getImage(path) );
|
setImage( Canvas::getSystemAdapter()->getImage(path) );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ namespace canvas
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
static const std::string TYPE_NAME;
|
static const std::string TYPE_NAME;
|
||||||
|
static void staticInit();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param node Property node containing settings for this image:
|
* @param node Property node containing settings for this image:
|
||||||
|
|||||||
@@ -46,6 +46,17 @@ namespace canvas
|
|||||||
const std::string GEO = "-geo";
|
const std::string GEO = "-geo";
|
||||||
const std::string Map::TYPE_NAME = "map";
|
const std::string Map::TYPE_NAME = "map";
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
void Map::staticInit()
|
||||||
|
{
|
||||||
|
Group::staticInit();
|
||||||
|
|
||||||
|
if( isInit<Map>() )
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Do some initialization if needed...
|
||||||
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
Map::Map( const CanvasWeakPtr& canvas,
|
Map::Map( const CanvasWeakPtr& canvas,
|
||||||
const SGPropertyNode_ptr& node,
|
const SGPropertyNode_ptr& node,
|
||||||
@@ -56,7 +67,7 @@ namespace canvas
|
|||||||
_projection(new SansonFlamsteedProjection),
|
_projection(new SansonFlamsteedProjection),
|
||||||
_projection_dirty(true)
|
_projection_dirty(true)
|
||||||
{
|
{
|
||||||
|
staticInit();
|
||||||
}
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
@@ -194,6 +205,8 @@ namespace canvas
|
|||||||
_projection->setOrientation(child->getFloatValue());
|
_projection->setOrientation(child->getFloatValue());
|
||||||
else if( child->getNameString() == "range" )
|
else if( child->getNameString() == "range" )
|
||||||
_projection->setRange(child->getDoubleValue());
|
_projection->setRange(child->getDoubleValue());
|
||||||
|
else if( child->getNameString() == "screen-range" )
|
||||||
|
_projection->setScreenRange(child->getDoubleValue());
|
||||||
else
|
else
|
||||||
return Group::childChanged(child);
|
return Group::childChanged(child);
|
||||||
|
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ namespace canvas
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
static const std::string TYPE_NAME;
|
static const std::string TYPE_NAME;
|
||||||
|
static void staticInit();
|
||||||
|
|
||||||
Map( const CanvasWeakPtr& canvas,
|
Map( const CanvasWeakPtr& canvas,
|
||||||
const SGPropertyNode_ptr& node,
|
const SGPropertyNode_ptr& node,
|
||||||
|
|||||||
@@ -110,7 +110,7 @@ namespace canvas
|
|||||||
*/
|
*/
|
||||||
void setFill(const std::string& fill)
|
void setFill(const std::string& fill)
|
||||||
{
|
{
|
||||||
if( fill == "none" )
|
if( fill.empty() || fill == "none" )
|
||||||
{
|
{
|
||||||
_mode &= ~VG_FILL_PATH;
|
_mode &= ~VG_FILL_PATH;
|
||||||
}
|
}
|
||||||
@@ -150,7 +150,7 @@ namespace canvas
|
|||||||
*/
|
*/
|
||||||
void setStroke(const std::string& stroke)
|
void setStroke(const std::string& stroke)
|
||||||
{
|
{
|
||||||
if( stroke == "none" )
|
if( stroke.empty() || stroke == "none" )
|
||||||
{
|
{
|
||||||
_mode &= ~VG_STROKE_PATH;
|
_mode &= ~VG_STROKE_PATH;
|
||||||
}
|
}
|
||||||
@@ -277,7 +277,8 @@ namespace canvas
|
|||||||
{
|
{
|
||||||
osg::BoundingBox bb;
|
osg::BoundingBox bb;
|
||||||
|
|
||||||
osg::Vec2f cur; // VG "Current point" (in local coordinates)
|
osg::Vec2f cur(0, 0), // VG "Current point" (in local coordinates)
|
||||||
|
sub(0, 0); // beginning of current sub path
|
||||||
VGubyte cmd_index = 0;
|
VGubyte cmd_index = 0;
|
||||||
for( size_t i = 0, ci = 0;
|
for( size_t i = 0, ci = 0;
|
||||||
i < _cmds.size() && ci < _coords.size();
|
i < _cmds.size() && ci < _coords.size();
|
||||||
@@ -297,6 +298,7 @@ namespace canvas
|
|||||||
switch( cmd )
|
switch( cmd )
|
||||||
{
|
{
|
||||||
case VG_CLOSE_PATH:
|
case VG_CLOSE_PATH:
|
||||||
|
cur = sub;
|
||||||
break;
|
break;
|
||||||
case VG_MOVE_TO:
|
case VG_MOVE_TO:
|
||||||
case VG_LINE_TO:
|
case VG_LINE_TO:
|
||||||
@@ -352,7 +354,12 @@ namespace canvas
|
|||||||
}
|
}
|
||||||
|
|
||||||
if( num_coords > 0 )
|
if( num_coords > 0 )
|
||||||
|
{
|
||||||
cur = points[ num_coords - 1 ];
|
cur = points[ num_coords - 1 ];
|
||||||
|
|
||||||
|
if( cmd == VG_MOVE_TO )
|
||||||
|
sub = cur;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return bb;
|
return bb;
|
||||||
@@ -472,6 +479,22 @@ namespace canvas
|
|||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
const std::string Path::TYPE_NAME = "path";
|
const std::string Path::TYPE_NAME = "path";
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
void Path::staticInit()
|
||||||
|
{
|
||||||
|
if( isInit<Path>() )
|
||||||
|
return;
|
||||||
|
|
||||||
|
PathDrawableRef Path::*path = &Path::_path;
|
||||||
|
|
||||||
|
addStyle("fill", "color", &PathDrawable::setFill, path);
|
||||||
|
addStyle("fill-rule", "", &PathDrawable::setFillRule, path);
|
||||||
|
addStyle("stroke", "color", &PathDrawable::setStroke, path);
|
||||||
|
addStyle("stroke-width", "numeric", &PathDrawable::setStrokeWidth, path);
|
||||||
|
addStyle("stroke-dasharray", "", &PathDrawable::setStrokeDashArray, path);
|
||||||
|
addStyle("stroke-linecap", "", &PathDrawable::setStrokeLinecap, path);
|
||||||
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
Path::Path( const CanvasWeakPtr& canvas,
|
Path::Path( const CanvasWeakPtr& canvas,
|
||||||
const SGPropertyNode_ptr& node,
|
const SGPropertyNode_ptr& node,
|
||||||
@@ -480,20 +503,9 @@ namespace canvas
|
|||||||
Element(canvas, node, parent_style, parent),
|
Element(canvas, node, parent_style, parent),
|
||||||
_path( new PathDrawable(this) )
|
_path( new PathDrawable(this) )
|
||||||
{
|
{
|
||||||
|
staticInit();
|
||||||
|
|
||||||
setDrawable(_path);
|
setDrawable(_path);
|
||||||
|
|
||||||
if( !isInit<Path>() )
|
|
||||||
{
|
|
||||||
PathDrawableRef Path::*path = &Path::_path;
|
|
||||||
|
|
||||||
addStyle("fill", "color", &PathDrawable::setFill, path);
|
|
||||||
addStyle("fill-rule", "", &PathDrawable::setFillRule, path);
|
|
||||||
addStyle("stroke", "color", &PathDrawable::setStroke, path);
|
|
||||||
addStyle("stroke-width", "numeric", &PathDrawable::setStrokeWidth, path);
|
|
||||||
addStyle("stroke-dasharray", "", &PathDrawable::setStrokeDashArray, path);
|
|
||||||
addStyle("stroke-linecap", "", &PathDrawable::setStrokeLinecap, path);
|
|
||||||
}
|
|
||||||
|
|
||||||
setupStyle();
|
setupStyle();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ namespace canvas
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
static const std::string TYPE_NAME;
|
static const std::string TYPE_NAME;
|
||||||
|
static void staticInit();
|
||||||
|
|
||||||
Path( const CanvasWeakPtr& canvas,
|
Path( const CanvasWeakPtr& canvas,
|
||||||
const SGPropertyNode_ptr& node,
|
const SGPropertyNode_ptr& node,
|
||||||
|
|||||||
@@ -20,7 +20,7 @@
|
|||||||
#include <simgear/canvas/Canvas.hxx>
|
#include <simgear/canvas/Canvas.hxx>
|
||||||
#include <simgear/canvas/CanvasSystemAdapter.hxx>
|
#include <simgear/canvas/CanvasSystemAdapter.hxx>
|
||||||
#include <simgear/scene/util/parse_color.hxx>
|
#include <simgear/scene/util/parse_color.hxx>
|
||||||
#include <simgear/structure/OSGVersion.hxx>
|
#include <osg/Version>
|
||||||
#include <osgText/Text>
|
#include <osgText/Text>
|
||||||
|
|
||||||
namespace simgear
|
namespace simgear
|
||||||
@@ -36,6 +36,7 @@ namespace canvas
|
|||||||
|
|
||||||
void setFontResolution(int res);
|
void setFontResolution(int res);
|
||||||
void setCharacterAspect(float aspect);
|
void setCharacterAspect(float aspect);
|
||||||
|
void setLineHeight(float factor);
|
||||||
void setFill(const std::string& fill);
|
void setFill(const std::string& fill);
|
||||||
void setBackgroundColor(const std::string& fill);
|
void setBackgroundColor(const std::string& fill);
|
||||||
|
|
||||||
@@ -54,7 +55,7 @@ namespace canvas
|
|||||||
Text::TextOSG::TextOSG(canvas::Text* text):
|
Text::TextOSG::TextOSG(canvas::Text* text):
|
||||||
_text_element(text)
|
_text_element(text)
|
||||||
{
|
{
|
||||||
|
setBackdropImplementation(NO_DEPTH_BUFFER);
|
||||||
}
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
@@ -69,6 +70,12 @@ namespace canvas
|
|||||||
setCharacterSize(getCharacterHeight(), aspect);
|
setCharacterSize(getCharacterHeight(), aspect);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
void Text::TextOSG::setLineHeight(float factor)
|
||||||
|
{
|
||||||
|
setLineSpacing(factor - 1);
|
||||||
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
void Text::TextOSG::setFill(const std::string& fill)
|
void Text::TextOSG::setFill(const std::string& fill)
|
||||||
{
|
{
|
||||||
@@ -175,7 +182,7 @@ namespace canvas
|
|||||||
if( !bb.valid() )
|
if( !bb.valid() )
|
||||||
return bb;
|
return bb;
|
||||||
|
|
||||||
#if SG_OSG_VERSION_LESS_THAN(3,1,0)
|
#if OSG_VERSION_LESS_THAN(3,1,0)
|
||||||
// TODO bounding box still doesn't seem always right (eg. with center
|
// TODO bounding box still doesn't seem always right (eg. with center
|
||||||
// horizontal alignment not completely accurate)
|
// horizontal alignment not completely accurate)
|
||||||
bb._min.y() += _offset.y();
|
bb._min.y() += _offset.y();
|
||||||
@@ -254,6 +261,39 @@ namespace canvas
|
|||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
const std::string Text::TYPE_NAME = "text";
|
const std::string Text::TYPE_NAME = "text";
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
void Text::staticInit()
|
||||||
|
{
|
||||||
|
if( isInit<Text>() )
|
||||||
|
return;
|
||||||
|
|
||||||
|
osg::ref_ptr<TextOSG> Text::*text = &Text::_text;
|
||||||
|
|
||||||
|
addStyle("fill", "color", &TextOSG::setFill, text);
|
||||||
|
addStyle("background", "color", &TextOSG::setBackgroundColor, text);
|
||||||
|
addStyle("character-size",
|
||||||
|
"numeric",
|
||||||
|
static_cast<
|
||||||
|
void (TextOSG::*)(float)
|
||||||
|
> (&TextOSG::setCharacterSize),
|
||||||
|
text);
|
||||||
|
addStyle("character-aspect-ratio",
|
||||||
|
"numeric",
|
||||||
|
&TextOSG::setCharacterAspect, text);
|
||||||
|
addStyle("line-height", "numeric", &TextOSG::setLineHeight, text);
|
||||||
|
addStyle("font-resolution", "numeric", &TextOSG::setFontResolution, text);
|
||||||
|
addStyle("padding", "numeric", &TextOSG::setBoundingBoxMargin, text);
|
||||||
|
// TEXT = 1 default
|
||||||
|
// BOUNDINGBOX = 2
|
||||||
|
// FILLEDBOUNDINGBOX = 4
|
||||||
|
// ALIGNMENT = 8
|
||||||
|
addStyle<int>("draw-mode", "", &TextOSG::setDrawMode, text);
|
||||||
|
addStyle("max-width", "numeric", &TextOSG::setMaximumWidth, text);
|
||||||
|
addStyle("font", "", &Text::setFont);
|
||||||
|
addStyle("alignment", "", &Text::setAlignment);
|
||||||
|
addStyle("text", "", &Text::setText, false);
|
||||||
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
Text::Text( const CanvasWeakPtr& canvas,
|
Text::Text( const CanvasWeakPtr& canvas,
|
||||||
const SGPropertyNode_ptr& node,
|
const SGPropertyNode_ptr& node,
|
||||||
@@ -262,39 +302,13 @@ namespace canvas
|
|||||||
Element(canvas, node, parent_style, parent),
|
Element(canvas, node, parent_style, parent),
|
||||||
_text( new Text::TextOSG(this) )
|
_text( new Text::TextOSG(this) )
|
||||||
{
|
{
|
||||||
|
staticInit();
|
||||||
|
|
||||||
setDrawable(_text);
|
setDrawable(_text);
|
||||||
_text->setCharacterSizeMode(osgText::Text::OBJECT_COORDS);
|
_text->setCharacterSizeMode(osgText::Text::OBJECT_COORDS);
|
||||||
_text->setAxisAlignment(osgText::Text::USER_DEFINED_ROTATION);
|
_text->setAxisAlignment(osgText::Text::USER_DEFINED_ROTATION);
|
||||||
_text->setRotation(osg::Quat(osg::PI, osg::X_AXIS));
|
_text->setRotation(osg::Quat(osg::PI, osg::X_AXIS));
|
||||||
|
|
||||||
if( !isInit<Text>() )
|
|
||||||
{
|
|
||||||
osg::ref_ptr<TextOSG> Text::*text = &Text::_text;
|
|
||||||
|
|
||||||
addStyle("fill", "color", &TextOSG::setFill, text);
|
|
||||||
addStyle("background", "color", &TextOSG::setBackgroundColor, text);
|
|
||||||
addStyle("character-size",
|
|
||||||
"numeric",
|
|
||||||
static_cast<
|
|
||||||
void (TextOSG::*)(float)
|
|
||||||
> (&TextOSG::setCharacterSize),
|
|
||||||
text);
|
|
||||||
addStyle("character-aspect-ratio",
|
|
||||||
"numeric",
|
|
||||||
&TextOSG::setCharacterAspect, text);
|
|
||||||
addStyle("font-resolution", "numeric", &TextOSG::setFontResolution, text);
|
|
||||||
addStyle("padding", "numeric", &TextOSG::setBoundingBoxMargin, text);
|
|
||||||
// TEXT = 1 default
|
|
||||||
// BOUNDINGBOX = 2
|
|
||||||
// FILLEDBOUNDINGBOX = 4
|
|
||||||
// ALIGNMENT = 8
|
|
||||||
addStyle<int>("draw-mode", "", &TextOSG::setDrawMode, text);
|
|
||||||
addStyle("max-width", "numeric", &TextOSG::setMaximumWidth, text);
|
|
||||||
addStyle("font", "", &Text::setFont);
|
|
||||||
addStyle("alignment", "", &Text::setAlignment);
|
|
||||||
addStyle("text", "", &Text::setText);
|
|
||||||
}
|
|
||||||
|
|
||||||
setupStyle();
|
setupStyle();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -313,7 +327,7 @@ namespace canvas
|
|||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
void Text::setFont(const char* name)
|
void Text::setFont(const char* name)
|
||||||
{
|
{
|
||||||
_text->setFont( _canvas.lock()->getSystemAdapter()->getFont(name) );
|
_text->setFont( Canvas::getSystemAdapter()->getFont(name) );
|
||||||
}
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ namespace canvas
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
static const std::string TYPE_NAME;
|
static const std::string TYPE_NAME;
|
||||||
|
static void staticInit();
|
||||||
|
|
||||||
Text( const CanvasWeakPtr& canvas,
|
Text( const CanvasWeakPtr& canvas,
|
||||||
const SGPropertyNode_ptr& node,
|
const SGPropertyNode_ptr& node,
|
||||||
|
|||||||
+1
-1
@@ -46,7 +46,7 @@
|
|||||||
# warning GCC compilers prior to 3.4 are suspect
|
# warning GCC compilers prior to 3.4 are suspect
|
||||||
# endif
|
# endif
|
||||||
|
|
||||||
# define GCC_VERSION (__GNUC__ * 10000 \
|
# define SG_GCC_VERSION (__GNUC__ * 10000 \
|
||||||
+ __GNUC_MINOR__ * 100 \
|
+ __GNUC_MINOR__ * 100 \
|
||||||
+ __GNUC_PATCHLEVEL__)
|
+ __GNUC_PATCHLEVEL__)
|
||||||
# define SG_COMPILER_STR "GNU C++ version " SG_STRINGIZE(__GNUC__) "." SG_STRINGIZE(__GNUC_MINOR__)
|
# define SG_COMPILER_STR "GNU C++ version " SG_STRINGIZE(__GNUC__) "." SG_STRINGIZE(__GNUC_MINOR__)
|
||||||
|
|||||||
@@ -34,18 +34,15 @@ class BufferedLogCallback::BufferedLogCallbackPrivate
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
SGMutex m_mutex;
|
SGMutex m_mutex;
|
||||||
sgDebugClass m_class;
|
|
||||||
sgDebugPriority m_priority;
|
|
||||||
vector_cstring m_buffer;
|
vector_cstring m_buffer;
|
||||||
unsigned int m_stamp;
|
unsigned int m_stamp;
|
||||||
unsigned int m_maxLength;
|
unsigned int m_maxLength;
|
||||||
};
|
};
|
||||||
|
|
||||||
BufferedLogCallback::BufferedLogCallback(sgDebugClass c, sgDebugPriority p) :
|
BufferedLogCallback::BufferedLogCallback(sgDebugClass c, sgDebugPriority p) :
|
||||||
|
simgear::LogCallback(c,p),
|
||||||
d(new BufferedLogCallbackPrivate)
|
d(new BufferedLogCallbackPrivate)
|
||||||
{
|
{
|
||||||
d->m_class = c;
|
|
||||||
d->m_priority = p;
|
|
||||||
d->m_stamp = 0;
|
d->m_stamp = 0;
|
||||||
d->m_maxLength = 0xffff;
|
d->m_maxLength = 0xffff;
|
||||||
}
|
}
|
||||||
@@ -63,7 +60,7 @@ void BufferedLogCallback::operator()(sgDebugClass c, sgDebugPriority p,
|
|||||||
SG_UNUSED(file);
|
SG_UNUSED(file);
|
||||||
SG_UNUSED(line);
|
SG_UNUSED(line);
|
||||||
|
|
||||||
if ((c & d->m_class) == 0 || p < d->m_priority) return;
|
if (!shouldLog(c, p)) return;
|
||||||
|
|
||||||
vector_cstring::value_type msg;
|
vector_cstring::value_type msg;
|
||||||
if (aMessage.size() >= d->m_maxLength) {
|
if (aMessage.size() >= d->m_maxLength) {
|
||||||
|
|||||||
+108
-28
@@ -20,10 +20,13 @@
|
|||||||
//
|
//
|
||||||
// $Id$
|
// $Id$
|
||||||
|
|
||||||
|
#include <simgear_config.h>
|
||||||
|
|
||||||
#include "logstream.hxx"
|
#include "logstream.hxx"
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
|
#include <sstream>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
#include <boost/foreach.hpp>
|
#include <boost/foreach.hpp>
|
||||||
@@ -35,8 +38,8 @@
|
|||||||
|
|
||||||
#include <simgear/misc/sg_path.hxx>
|
#include <simgear/misc/sg_path.hxx>
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef SG_WINDOWS
|
||||||
// for AllocConsole
|
// for AllocConsole, OutputDebugString
|
||||||
#include "windows.h"
|
#include "windows.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -73,37 +76,57 @@ const char* debugClassToString(sgDebugClass c)
|
|||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
namespace simgear
|
||||||
|
{
|
||||||
|
|
||||||
|
LogCallback::LogCallback(sgDebugClass c, sgDebugPriority p) :
|
||||||
|
m_class(c),
|
||||||
|
m_priority(p)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LogCallback::shouldLog(sgDebugClass c, sgDebugPriority p) const
|
||||||
|
{
|
||||||
|
return ((c & m_class) != 0 && p >= m_priority);
|
||||||
|
}
|
||||||
|
|
||||||
|
void LogCallback::setLogLevels( sgDebugClass c, sgDebugPriority p )
|
||||||
|
{
|
||||||
|
m_priority = p;
|
||||||
|
m_class = c;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // of namespace simgear
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
class FileLogCallback : public simgear::LogCallback
|
class FileLogCallback : public simgear::LogCallback
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
FileLogCallback(const std::string& aPath, sgDebugClass c, sgDebugPriority p) :
|
FileLogCallback(const std::string& aPath, sgDebugClass c, sgDebugPriority p) :
|
||||||
m_file(aPath.c_str(), std::ios_base::out | std::ios_base::trunc),
|
simgear::LogCallback(c, p),
|
||||||
m_class(c),
|
m_file(aPath.c_str(), std::ios_base::out | std::ios_base::trunc)
|
||||||
m_priority(p)
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void operator()(sgDebugClass c, sgDebugPriority p,
|
virtual void operator()(sgDebugClass c, sgDebugPriority p,
|
||||||
const char* file, int line, const std::string& message)
|
const char* file, int line, const std::string& message)
|
||||||
{
|
{
|
||||||
if ((c & m_class) == 0 || p < m_priority) return;
|
if (!shouldLog(c, p)) return;
|
||||||
m_file << debugClassToString(c) << ":" << (int) p
|
m_file << debugClassToString(c) << ":" << (int) p
|
||||||
<< ":" << file << ":" << line << ":" << message << std::endl;
|
<< ":" << file << ":" << line << ":" << message << std::endl;
|
||||||
}
|
}
|
||||||
private:
|
private:
|
||||||
std::ofstream m_file;
|
std::ofstream m_file;
|
||||||
sgDebugClass m_class;
|
|
||||||
sgDebugPriority m_priority;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class StderrLogCallback : public simgear::LogCallback
|
class StderrLogCallback : public simgear::LogCallback
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
StderrLogCallback(sgDebugClass c, sgDebugPriority p) :
|
StderrLogCallback(sgDebugClass c, sgDebugPriority p) :
|
||||||
m_class(c),
|
simgear::LogCallback(c, p)
|
||||||
m_priority(p)
|
|
||||||
{
|
{
|
||||||
#ifdef _WIN32
|
#ifdef SG_WINDOWS
|
||||||
AllocConsole(); // but only if we want a console
|
AllocConsole(); // but only if we want a console
|
||||||
freopen("conin$", "r", stdin);
|
freopen("conin$", "r", stdin);
|
||||||
freopen("conout$", "w", stdout);
|
freopen("conout$", "w", stdout);
|
||||||
@@ -111,29 +134,42 @@ public:
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void setLogLevels( sgDebugClass c, sgDebugPriority p )
|
|
||||||
{
|
|
||||||
m_priority = p;
|
|
||||||
m_class = c;
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void operator()(sgDebugClass c, sgDebugPriority p,
|
virtual void operator()(sgDebugClass c, sgDebugPriority p,
|
||||||
const char* file, int line, const std::string& aMessage)
|
const char* file, int line, const std::string& aMessage)
|
||||||
{
|
{
|
||||||
if ((c & m_class) == 0 || p < m_priority) return;
|
if (!shouldLog(c, p)) return;
|
||||||
|
|
||||||
// if running under MSVC, we could use OutputDebugString here
|
|
||||||
|
|
||||||
fprintf(stderr, "%s\n", aMessage.c_str());
|
fprintf(stderr, "%s\n", aMessage.c_str());
|
||||||
//fprintf(stderr, "%s:%d:%s:%d:%s\n", debugClassToString(c), p,
|
//fprintf(stderr, "%s:%d:%s:%d:%s\n", debugClassToString(c), p,
|
||||||
// file, line, aMessage.c_str());
|
// file, line, aMessage.c_str());
|
||||||
fflush(stderr);
|
fflush(stderr);
|
||||||
}
|
}
|
||||||
private:
|
|
||||||
sgDebugClass m_class;
|
|
||||||
sgDebugPriority m_priority;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef SG_WINDOWS
|
||||||
|
|
||||||
|
class WinDebugLogCallback : public simgear::LogCallback
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
WinDebugLogCallback(sgDebugClass c, sgDebugPriority p) :
|
||||||
|
simgear::LogCallback(c, p)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void operator()(sgDebugClass c, sgDebugPriority p,
|
||||||
|
const char* file, int line, const std::string& aMessage)
|
||||||
|
{
|
||||||
|
if (!shouldLog(c, p)) return;
|
||||||
|
|
||||||
|
std::ostringstream os;
|
||||||
|
os << debugClassToString(c) << ":" << aMessage << std::endl;
|
||||||
|
OutputDebugStringA(os.str().c_str());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
class LogStreamPrivate : public SGThread
|
class LogStreamPrivate : public SGThread
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
@@ -181,10 +217,20 @@ public:
|
|||||||
LogStreamPrivate() :
|
LogStreamPrivate() :
|
||||||
m_logClass(SG_ALL),
|
m_logClass(SG_ALL),
|
||||||
m_logPriority(SG_ALERT),
|
m_logPriority(SG_ALERT),
|
||||||
m_isRunning(false)
|
m_isRunning(false),
|
||||||
|
m_consoleRequested(false)
|
||||||
{
|
{
|
||||||
m_stderrCallback = new StderrLogCallback(m_logClass, m_logPriority);
|
|
||||||
addCallback(m_stderrCallback);
|
#if !defined(SG_WINDOWS)
|
||||||
|
m_callbacks.push_back(new StderrLogCallback(m_logClass, m_logPriority));
|
||||||
|
m_consoleCallbacks.push_back(m_callbacks.back());
|
||||||
|
m_consoleRequested = true;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined (SG_WINDOWS) && !defined(NDEBUG)
|
||||||
|
m_callbacks.push_back(new WinDebugLogCallback(m_logClass, m_logPriority));
|
||||||
|
m_consoleCallbacks.push_back(m_callbacks.back());
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
SGMutex m_lock;
|
SGMutex m_lock;
|
||||||
@@ -192,11 +238,14 @@ public:
|
|||||||
|
|
||||||
typedef std::vector<simgear::LogCallback*> CallbackVec;
|
typedef std::vector<simgear::LogCallback*> CallbackVec;
|
||||||
CallbackVec m_callbacks;
|
CallbackVec m_callbacks;
|
||||||
|
/// subset of callbacks which correspond to stdout / console,
|
||||||
|
/// and hence should dynamically reflect console logging settings
|
||||||
|
CallbackVec m_consoleCallbacks;
|
||||||
|
|
||||||
sgDebugClass m_logClass;
|
sgDebugClass m_logClass;
|
||||||
sgDebugPriority m_logPriority;
|
sgDebugPriority m_logPriority;
|
||||||
bool m_isRunning;
|
bool m_isRunning;
|
||||||
StderrLogCallback* m_stderrCallback;
|
bool m_consoleRequested;
|
||||||
|
|
||||||
void startLog()
|
void startLog()
|
||||||
{
|
{
|
||||||
@@ -260,7 +309,9 @@ public:
|
|||||||
PauseThread pause(this);
|
PauseThread pause(this);
|
||||||
m_logPriority = p;
|
m_logPriority = p;
|
||||||
m_logClass = c;
|
m_logClass = c;
|
||||||
m_stderrCallback->setLogLevels(c, p);
|
BOOST_FOREACH(simgear::LogCallback* cb, m_consoleCallbacks) {
|
||||||
|
cb->setLogLevels(c, p);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool would_log( sgDebugClass c, sgDebugPriority p ) const
|
bool would_log( sgDebugClass c, sgDebugPriority p ) const
|
||||||
@@ -275,6 +326,18 @@ public:
|
|||||||
LogEntry entry(c, p, fileName, line, msg);
|
LogEntry entry(c, p, fileName, line, msg);
|
||||||
m_entries.push(entry);
|
m_entries.push(entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void requestConsole()
|
||||||
|
{
|
||||||
|
PauseThread pause(this);
|
||||||
|
if (m_consoleRequested) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_consoleRequested = true;
|
||||||
|
m_callbacks.push_back(new StderrLogCallback(m_logClass, m_logPriority));
|
||||||
|
m_consoleCallbacks.push_back(m_callbacks.back());
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
@@ -348,6 +411,13 @@ sglog()
|
|||||||
{
|
{
|
||||||
// Force initialization of cerr.
|
// Force initialization of cerr.
|
||||||
static std::ios_base::Init initializer;
|
static std::ios_base::Init initializer;
|
||||||
|
|
||||||
|
// 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
|
||||||
|
static SGMutex m;
|
||||||
|
SGGuard<SGMutex> g(m);
|
||||||
|
|
||||||
if( !global_logstream )
|
if( !global_logstream )
|
||||||
global_logstream = new logstream();
|
global_logstream = new logstream();
|
||||||
return *global_logstream;
|
return *global_logstream;
|
||||||
@@ -359,3 +429,13 @@ logstream::logToFile( const SGPath& aPath, sgDebugClass c, sgDebugPriority p )
|
|||||||
global_privateLogstream->addCallback(new FileLogCallback(aPath.str(), c, p));
|
global_privateLogstream->addCallback(new FileLogCallback(aPath.str(), c, p));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace simgear
|
||||||
|
{
|
||||||
|
|
||||||
|
void requestConsole()
|
||||||
|
{
|
||||||
|
sglog(); // force creation
|
||||||
|
global_privateLogstream->requestConsole();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // of namespace simgear
|
||||||
@@ -42,8 +42,24 @@ public:
|
|||||||
virtual ~LogCallback() {}
|
virtual ~LogCallback() {}
|
||||||
virtual void operator()(sgDebugClass c, sgDebugPriority p,
|
virtual void operator()(sgDebugClass c, sgDebugPriority p,
|
||||||
const char* file, int line, const std::string& aMessage) = 0;
|
const char* file, int line, const std::string& aMessage) = 0;
|
||||||
|
|
||||||
|
void setLogLevels(sgDebugClass c, sgDebugPriority p);
|
||||||
|
protected:
|
||||||
|
LogCallback(sgDebugClass c, sgDebugPriority p);
|
||||||
|
|
||||||
|
bool shouldLog(sgDebugClass c, sgDebugPriority p) const;
|
||||||
|
private:
|
||||||
|
sgDebugClass m_class;
|
||||||
|
sgDebugPriority m_priority;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper force a console on platforms where it might optional, when
|
||||||
|
* we need to show a console. This basically means Windows at the
|
||||||
|
* moment - on other plaforms it's a no-op
|
||||||
|
*/
|
||||||
|
void requestConsole();
|
||||||
|
|
||||||
} // of namespace simgear
|
} // of namespace simgear
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -103,6 +119,8 @@ private:
|
|||||||
|
|
||||||
logstream& sglog();
|
logstream& sglog();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/** \def SG_LOG(C,P,M)
|
/** \def SG_LOG(C,P,M)
|
||||||
* Log a message.
|
* Log a message.
|
||||||
* @param C debug class
|
* @param C debug class
|
||||||
|
|||||||
@@ -629,7 +629,7 @@ bool SGMetar::scanWeather()
|
|||||||
weather = pre + weather + post;
|
weather = pre + weather + post;
|
||||||
weather.erase(weather.length() - 1);
|
weather.erase(weather.length() - 1);
|
||||||
_weather.push_back(weather);
|
_weather.push_back(weather);
|
||||||
if( w.phenomena.size() > 0 )
|
if( ! w.phenomena.empty() )
|
||||||
_weather2.push_back( w );
|
_weather2.push_back( w );
|
||||||
_grpcount++;
|
_grpcount++;
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -42,7 +42,6 @@ private:
|
|||||||
float _rain_intensity;
|
float _rain_intensity;
|
||||||
float _clip_distance;
|
float _clip_distance;
|
||||||
|
|
||||||
int _wind_dir;
|
|
||||||
osg::Vec3 _wind_vec;
|
osg::Vec3 _wind_vec;
|
||||||
|
|
||||||
osg::ref_ptr<osgParticle::PrecipitationEffect> _precipitationEffect;
|
osg::ref_ptr<osgParticle::PrecipitationEffect> _precipitationEffect;
|
||||||
|
|||||||
@@ -15,7 +15,10 @@ set(HEADERS
|
|||||||
sg_socket.hxx
|
sg_socket.hxx
|
||||||
sg_socket_udp.hxx
|
sg_socket_udp.hxx
|
||||||
HTTPClient.hxx
|
HTTPClient.hxx
|
||||||
|
HTTPFileRequest.hxx
|
||||||
|
HTTPMemoryRequest.hxx
|
||||||
HTTPRequest.hxx
|
HTTPRequest.hxx
|
||||||
|
HTTPContentDecode.hxx
|
||||||
DAVMultiStatus.hxx
|
DAVMultiStatus.hxx
|
||||||
SVNRepository.hxx
|
SVNRepository.hxx
|
||||||
SVNDirectory.hxx
|
SVNDirectory.hxx
|
||||||
@@ -35,7 +38,10 @@ set(SOURCES
|
|||||||
sg_socket.cxx
|
sg_socket.cxx
|
||||||
sg_socket_udp.cxx
|
sg_socket_udp.cxx
|
||||||
HTTPClient.cxx
|
HTTPClient.cxx
|
||||||
|
HTTPFileRequest.cxx
|
||||||
|
HTTPMemoryRequest.cxx
|
||||||
HTTPRequest.cxx
|
HTTPRequest.cxx
|
||||||
|
HTTPContentDecode.cxx
|
||||||
DAVMultiStatus.cxx
|
DAVMultiStatus.cxx
|
||||||
SVNRepository.cxx
|
SVNRepository.cxx
|
||||||
SVNDirectory.cxx
|
SVNDirectory.cxx
|
||||||
|
|||||||
@@ -16,6 +16,10 @@
|
|||||||
// along with this program; if not, write to the Free Software
|
// along with this program; if not, write to the Free Software
|
||||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
# include <simgear_config.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include "DAVMultiStatus.hxx"
|
#include "DAVMultiStatus.hxx"
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
@@ -27,10 +31,15 @@
|
|||||||
#include <boost/foreach.hpp>
|
#include <boost/foreach.hpp>
|
||||||
|
|
||||||
#include "simgear/debug/logstream.hxx"
|
#include "simgear/debug/logstream.hxx"
|
||||||
#include "simgear/xml/xmlparse.h"
|
|
||||||
#include "simgear/misc/strutils.hxx"
|
#include "simgear/misc/strutils.hxx"
|
||||||
#include "simgear/structure/exception.hxx"
|
#include "simgear/structure/exception.hxx"
|
||||||
|
|
||||||
|
#ifdef SYSTEM_EXPAT
|
||||||
|
# include <expat.h>
|
||||||
|
#else
|
||||||
|
# include "sg_expat.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
using std::string;
|
using std::string;
|
||||||
|
|
||||||
using namespace simgear;
|
using namespace simgear;
|
||||||
@@ -59,6 +68,9 @@ DAVResource::DAVResource(const string& href) :
|
|||||||
_container(NULL)
|
_container(NULL)
|
||||||
{
|
{
|
||||||
assert(!href.empty());
|
assert(!href.empty());
|
||||||
|
if (strutils::ends_with(href, "/")) {
|
||||||
|
_url = href.substr(0, _url.size() - 1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void DAVResource::setVersionName(const std::string& aVersion)
|
void DAVResource::setVersionName(const std::string& aVersion)
|
||||||
@@ -171,7 +183,8 @@ class DAVMultiStatus::DAVMultiStatusPrivate
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
DAVMultiStatusPrivate() :
|
DAVMultiStatusPrivate() :
|
||||||
parserInited(false)
|
parserInited(false),
|
||||||
|
valid(false)
|
||||||
{
|
{
|
||||||
rootResource = NULL;
|
rootResource = NULL;
|
||||||
}
|
}
|
||||||
@@ -275,6 +288,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool parserInited;
|
bool parserInited;
|
||||||
|
bool valid;
|
||||||
XML_Parser xmlParser;
|
XML_Parser xmlParser;
|
||||||
DAVResource* rootResource;
|
DAVResource* rootResource;
|
||||||
|
|
||||||
@@ -354,13 +368,21 @@ void DAVMultiStatus::parseXML(const char* data, int size)
|
|||||||
|
|
||||||
XML_ParserFree(_d->xmlParser);
|
XML_ParserFree(_d->xmlParser);
|
||||||
_d->parserInited = false;
|
_d->parserInited = false;
|
||||||
|
_d->valid = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void DAVMultiStatus::finishParse()
|
void DAVMultiStatus::finishParse()
|
||||||
{
|
{
|
||||||
if (_d->parserInited) {
|
if (_d->parserInited) {
|
||||||
XML_Parse(_d->xmlParser, NULL, 0, true);
|
if (!XML_Parse(_d->xmlParser, NULL, 0, true)) {
|
||||||
|
SG_LOG(SG_IO, SG_WARN, "DAV parse error:" << XML_ErrorString(XML_GetErrorCode(_d->xmlParser))
|
||||||
|
<< " at line:" << XML_GetCurrentLineNumber(_d->xmlParser)
|
||||||
|
<< " column " << XML_GetCurrentColumnNumber(_d->xmlParser));
|
||||||
|
_d->valid = false;
|
||||||
|
} else {
|
||||||
|
_d->valid = true;
|
||||||
|
}
|
||||||
XML_ParserFree(_d->xmlParser);
|
XML_ParserFree(_d->xmlParser);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -372,4 +394,9 @@ DAVResource* DAVMultiStatus::resource()
|
|||||||
return _d->rootResource;
|
return _d->rootResource;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool DAVMultiStatus::isValid() const
|
||||||
|
{
|
||||||
|
return _d->valid;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -129,6 +129,8 @@ public:
|
|||||||
|
|
||||||
void finishParse();
|
void finishParse();
|
||||||
|
|
||||||
|
bool isValid() const;
|
||||||
|
|
||||||
DAVResource* resource();
|
DAVResource* resource();
|
||||||
|
|
||||||
class DAVMultiStatusPrivate;
|
class DAVMultiStatusPrivate;
|
||||||
|
|||||||
+389
-287
@@ -1,22 +1,47 @@
|
|||||||
|
/**
|
||||||
|
* \file HTTPClient.cxx - simple HTTP client engine for SimHear
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Written by James Turner
|
||||||
|
//
|
||||||
|
// Copyright (C) 2013 James Turner <zakalawe@mac.com>
|
||||||
|
//
|
||||||
|
// This library is free software; you can redistribute it and/or
|
||||||
|
// modify it under the terms of the GNU Library General Public
|
||||||
|
// License as published by the Free Software Foundation; either
|
||||||
|
// version 2 of the License, or (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This library is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
// Library General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with this program; if not, write to the Free Software
|
||||||
|
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
//
|
||||||
|
|
||||||
#include "HTTPClient.hxx"
|
#include "HTTPClient.hxx"
|
||||||
|
#include "HTTPFileRequest.hxx"
|
||||||
|
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
|
#include <cstdlib> // rand()
|
||||||
#include <list>
|
#include <list>
|
||||||
#include <iostream>
|
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
#include <map>
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
#include <boost/foreach.hpp>
|
#include <boost/foreach.hpp>
|
||||||
#include <boost/algorithm/string/case_conv.hpp>
|
#include <boost/algorithm/string/case_conv.hpp>
|
||||||
|
|
||||||
#include <zlib.h>
|
|
||||||
|
|
||||||
#include <simgear/io/sg_netChat.hxx>
|
#include <simgear/io/sg_netChat.hxx>
|
||||||
#include <simgear/io/lowlevel.hxx>
|
#include <simgear/io/HTTPContentDecode.hxx>
|
||||||
#include <simgear/misc/strutils.hxx>
|
#include <simgear/misc/strutils.hxx>
|
||||||
#include <simgear/compiler.h>
|
#include <simgear/compiler.h>
|
||||||
#include <simgear/debug/logstream.hxx>
|
#include <simgear/debug/logstream.hxx>
|
||||||
#include <simgear/timing/timestamp.hxx>
|
#include <simgear/timing/timestamp.hxx>
|
||||||
|
#include <simgear/structure/exception.hxx>
|
||||||
|
|
||||||
#if defined( HAVE_VERSION_H ) && HAVE_VERSION_H
|
#if defined( HAVE_VERSION_H ) && HAVE_VERSION_H
|
||||||
#include "version.h"
|
#include "version.h"
|
||||||
@@ -26,10 +51,6 @@
|
|||||||
# endif
|
# endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
using std::string;
|
|
||||||
using std::stringstream;
|
|
||||||
using std::vector;
|
|
||||||
|
|
||||||
namespace simgear
|
namespace simgear
|
||||||
{
|
{
|
||||||
|
|
||||||
@@ -39,48 +60,60 @@ namespace HTTP
|
|||||||
extern const int DEFAULT_HTTP_PORT = 80;
|
extern const int DEFAULT_HTTP_PORT = 80;
|
||||||
const char* CONTENT_TYPE_URL_ENCODED = "application/x-www-form-urlencoded";
|
const char* CONTENT_TYPE_URL_ENCODED = "application/x-www-form-urlencoded";
|
||||||
const unsigned int MAX_INFLIGHT_REQUESTS = 32;
|
const unsigned int MAX_INFLIGHT_REQUESTS = 32;
|
||||||
const int ZLIB_DECOMPRESS_BUFFER_SIZE = 32 * 1024;
|
|
||||||
const int ZLIB_INFLATE_WINDOW_BITS = -MAX_WBITS;
|
|
||||||
|
|
||||||
// see http://www.ietf.org/rfc/rfc1952.txt for these values and
|
|
||||||
// detailed description of the logic
|
|
||||||
const int GZIP_HEADER_ID1 = 31;
|
|
||||||
const int GZIP_HEADER_ID2 = 139;
|
|
||||||
const int GZIP_HEADER_METHOD_DEFLATE = 8;
|
|
||||||
const unsigned int GZIP_HEADER_SIZE = 10;
|
|
||||||
const int GZIP_HEADER_FEXTRA = 1 << 2;
|
|
||||||
const int GZIP_HEADER_FNAME = 1 << 3;
|
|
||||||
const int GZIP_HEADER_COMMENT = 1 << 4;
|
|
||||||
const int GZIP_HEADER_CRC = 1 << 1;
|
|
||||||
|
|
||||||
|
class Connection;
|
||||||
|
typedef std::multimap<std::string, Connection*> ConnectionDict;
|
||||||
typedef std::list<Request_ptr> RequestList;
|
typedef std::list<Request_ptr> RequestList;
|
||||||
|
|
||||||
|
class Client::ClientPrivate
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
std::string userAgent;
|
||||||
|
std::string proxy;
|
||||||
|
int proxyPort;
|
||||||
|
std::string proxyAuth;
|
||||||
|
NetChannelPoller poller;
|
||||||
|
unsigned int maxConnections;
|
||||||
|
|
||||||
|
RequestList pendingRequests;
|
||||||
|
|
||||||
|
// connections by host (potentially more than one)
|
||||||
|
ConnectionDict connections;
|
||||||
|
|
||||||
|
SGTimeStamp timeTransferSample;
|
||||||
|
unsigned int bytesTransferred;
|
||||||
|
unsigned int lastTransferRate;
|
||||||
|
uint64_t totalBytesDownloaded;
|
||||||
|
};
|
||||||
|
|
||||||
class Connection : public NetChat
|
class Connection : public NetChat
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Connection(Client* pr) :
|
Connection(Client* pr) :
|
||||||
client(pr),
|
client(pr),
|
||||||
state(STATE_CLOSED),
|
state(STATE_CLOSED),
|
||||||
port(DEFAULT_HTTP_PORT),
|
port(DEFAULT_HTTP_PORT)
|
||||||
zlibInflateBuffer(NULL),
|
|
||||||
zlibInflateBufferSize(0),
|
|
||||||
zlibOutputBuffer(NULL)
|
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual ~Connection()
|
virtual ~Connection()
|
||||||
{
|
{
|
||||||
if (zlibInflateBuffer) {
|
|
||||||
free(zlibInflateBuffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (zlibOutputBuffer) {
|
|
||||||
free(zlibOutputBuffer);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void setServer(const string& h, short p)
|
virtual void handleBufferRead (NetBuffer& buffer)
|
||||||
|
{
|
||||||
|
if( !activeRequest || !activeRequest->isComplete() )
|
||||||
|
return NetChat::handleBufferRead(buffer);
|
||||||
|
|
||||||
|
// Request should be aborted (signaled by setting its state to complete).
|
||||||
|
|
||||||
|
// force the state to GETTING_BODY, to simplify logic in
|
||||||
|
// responseComplete and handleClose
|
||||||
|
state = STATE_GETTING_BODY;
|
||||||
|
responseComplete();
|
||||||
|
}
|
||||||
|
|
||||||
|
void setServer(const std::string& h, short p)
|
||||||
{
|
{
|
||||||
host = h;
|
host = h;
|
||||||
port = p;
|
port = p;
|
||||||
@@ -111,6 +144,7 @@ public:
|
|||||||
SG_LOG(SG_IO, SG_INFO, "HTTP socket error");
|
SG_LOG(SG_IO, SG_INFO, "HTTP socket error");
|
||||||
activeRequest->setFailure(error, "socket error");
|
activeRequest->setFailure(error, "socket error");
|
||||||
activeRequest = NULL;
|
activeRequest = NULL;
|
||||||
|
_contentDecoder.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
state = STATE_SOCKET_ERROR;
|
state = STATE_SOCKET_ERROR;
|
||||||
@@ -138,6 +172,7 @@ public:
|
|||||||
sentRequests.erase(it);
|
sentRequests.erase(it);
|
||||||
}
|
}
|
||||||
activeRequest = NULL;
|
activeRequest = NULL;
|
||||||
|
_contentDecoder.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
state = STATE_CLOSED;
|
state = STATE_CLOSED;
|
||||||
@@ -154,18 +189,37 @@ public:
|
|||||||
sentRequests.clear();
|
sentRequests.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void handleTimeout()
|
||||||
|
{
|
||||||
|
NetChat::handleError(ETIMEDOUT);
|
||||||
|
if (activeRequest) {
|
||||||
|
SG_LOG(SG_IO, SG_DEBUG, "HTTP socket timeout");
|
||||||
|
activeRequest->setFailure(ETIMEDOUT, "socket timeout");
|
||||||
|
activeRequest = NULL;
|
||||||
|
_contentDecoder.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
state = STATE_SOCKET_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
void queueRequest(const Request_ptr& r)
|
void queueRequest(const Request_ptr& r)
|
||||||
{
|
{
|
||||||
queuedRequests.push_back(r);
|
queuedRequests.push_back(r);
|
||||||
tryStartNextRequest();
|
tryStartNextRequest();
|
||||||
}
|
}
|
||||||
|
|
||||||
void beginResponse()
|
void beginResponse()
|
||||||
{
|
{
|
||||||
assert(!sentRequests.empty());
|
assert(!sentRequests.empty());
|
||||||
|
assert(state == STATE_WAITING_FOR_RESPONSE);
|
||||||
|
|
||||||
|
activeRequest = sentRequests.front();
|
||||||
|
try {
|
||||||
|
activeRequest->responseStart(buffer);
|
||||||
|
} catch (sg_exception& e) {
|
||||||
|
handleError(EIO);
|
||||||
|
}
|
||||||
|
|
||||||
activeRequest = sentRequests.front();
|
|
||||||
activeRequest->responseStart(buffer);
|
|
||||||
state = STATE_GETTING_HEADERS;
|
state = STATE_GETTING_HEADERS;
|
||||||
buffer.clear();
|
buffer.clear();
|
||||||
if (activeRequest->responseCode() == 204) {
|
if (activeRequest->responseCode() == 204) {
|
||||||
@@ -178,11 +232,15 @@ public:
|
|||||||
|
|
||||||
bodyTransferSize = -1;
|
bodyTransferSize = -1;
|
||||||
chunkedTransfer = false;
|
chunkedTransfer = false;
|
||||||
contentGZip = contentDeflate = false;
|
_contentDecoder.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
void tryStartNextRequest()
|
void tryStartNextRequest()
|
||||||
{
|
{
|
||||||
|
while( !queuedRequests.empty()
|
||||||
|
&& queuedRequests.front()->isComplete() )
|
||||||
|
queuedRequests.pop_front();
|
||||||
|
|
||||||
if (queuedRequests.empty()) {
|
if (queuedRequests.empty()) {
|
||||||
idleTime.stamp();
|
idleTime.stamp();
|
||||||
return;
|
return;
|
||||||
@@ -203,28 +261,28 @@ public:
|
|||||||
|
|
||||||
Request_ptr r = queuedRequests.front();
|
Request_ptr r = queuedRequests.front();
|
||||||
r->requestStart();
|
r->requestStart();
|
||||||
requestBodyBytesToSend = r->requestBodyLength();
|
|
||||||
|
|
||||||
stringstream headerData;
|
std::stringstream headerData;
|
||||||
string path = r->path();
|
std::string path = r->path();
|
||||||
assert(!path.empty());
|
assert(!path.empty());
|
||||||
string query = r->query();
|
std::string query = r->query();
|
||||||
string bodyData;
|
std::string bodyData;
|
||||||
|
|
||||||
if (!client->proxyHost().empty()) {
|
if (!client->proxyHost().empty()) {
|
||||||
path = r->scheme() + "://" + r->host() + r->path();
|
path = r->scheme() + "://" + r->host() + r->path();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (r->requestBodyType() == CONTENT_TYPE_URL_ENCODED) {
|
if (r->bodyType() == CONTENT_TYPE_URL_ENCODED) {
|
||||||
headerData << r->method() << " " << path << " HTTP/1.1\r\n";
|
headerData << r->method() << " " << path << " HTTP/1.1\r\n";
|
||||||
bodyData = query.substr(1); // URL-encode, drop the leading '?'
|
bodyData = query.substr(1); // URL-encode, drop the leading '?'
|
||||||
headerData << "Content-Type:" << CONTENT_TYPE_URL_ENCODED << "\r\n";
|
headerData << "Content-Type:" << CONTENT_TYPE_URL_ENCODED << "\r\n";
|
||||||
headerData << "Content-Length:" << bodyData.size() << "\r\n";
|
headerData << "Content-Length:" << bodyData.size() << "\r\n";
|
||||||
} else {
|
} else {
|
||||||
headerData << r->method() << " " << path << query << " HTTP/1.1\r\n";
|
headerData << r->method() << " " << path << query << " HTTP/1.1\r\n";
|
||||||
if (requestBodyBytesToSend >= 0) {
|
if( r->hasBodyData() )
|
||||||
headerData << "Content-Length:" << requestBodyBytesToSend << "\r\n";
|
{
|
||||||
headerData << "Content-Type:" << r->requestBodyType() << "\r\n";
|
headerData << "Content-Length:" << r->bodyLength() << "\r\n";
|
||||||
|
headerData << "Content-Type:" << r->bodyType() << "\r\n";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -235,8 +293,8 @@ public:
|
|||||||
headerData << "Proxy-Authorization: " << client->proxyAuth() << "\r\n";
|
headerData << "Proxy-Authorization: " << client->proxyAuth() << "\r\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOST_FOREACH(string h, r->requestHeaders()) {
|
BOOST_FOREACH(const StringMap::value_type& h, r->requestHeaders()) {
|
||||||
headerData << h << ": " << r->header(h) << "\r\n";
|
headerData << h.first << ": " << h.second << "\r\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
headerData << "\r\n"; // final CRLF to terminate the headers
|
headerData << "\r\n"; // final CRLF to terminate the headers
|
||||||
@@ -252,169 +310,61 @@ public:
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
while (requestBodyBytesToSend > 0) {
|
if( r->hasBodyData() )
|
||||||
char buf[4096];
|
for(size_t body_bytes_sent = 0; body_bytes_sent < r->bodyLength();)
|
||||||
int len = r->getBodyData(buf, 4096);
|
{
|
||||||
if (len > 0) {
|
char buf[4096];
|
||||||
requestBodyBytesToSend -= len;
|
size_t len = r->getBodyData(buf, body_bytes_sent, 4096);
|
||||||
if (!bufferSend(buf, len)) {
|
if( len )
|
||||||
SG_LOG(SG_IO, SG_WARN, "overflow the HTTP::Connection output buffer");
|
{
|
||||||
state = STATE_SOCKET_ERROR;
|
if( !bufferSend(buf, len) )
|
||||||
return;
|
{
|
||||||
|
SG_LOG(SG_IO,
|
||||||
|
SG_WARN,
|
||||||
|
"overflow the HTTP::Connection output buffer");
|
||||||
|
state = STATE_SOCKET_ERROR;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
body_bytes_sent += len;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
SG_LOG(SG_IO,
|
||||||
|
SG_WARN,
|
||||||
|
"HTTP asynchronous request body generation is unsupported");
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
// SG_LOG(SG_IO, SG_INFO, "sent body:\n" << string(buf, len) << "\n%%%%%%%%%");
|
|
||||||
} else {
|
|
||||||
SG_LOG(SG_IO, SG_WARN, "HTTP asynchronous request body generation is unsupported");
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
SG_LOG(SG_IO, SG_DEBUG, "did start request:" << r->url() <<
|
// SG_LOG(SG_IO, SG_INFO, "did start request:" << r->url() <<
|
||||||
"\n\t @ " << reinterpret_cast<void*>(r.ptr()) <<
|
// "\n\t @ " << reinterpret_cast<void*>(r.ptr()) <<
|
||||||
"\n\t on connection " << this);
|
// "\n\t on connection " << this);
|
||||||
// successfully sent, remove from queue, and maybe send the next
|
// successfully sent, remove from queue, and maybe send the next
|
||||||
queuedRequests.pop_front();
|
queuedRequests.pop_front();
|
||||||
sentRequests.push_back(r);
|
sentRequests.push_back(r);
|
||||||
|
state = STATE_WAITING_FOR_RESPONSE;
|
||||||
|
|
||||||
// pipelining, let's maybe send the next request right away
|
// pipelining, let's maybe send the next request right away
|
||||||
tryStartNextRequest();
|
tryStartNextRequest();
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void collectIncomingData(const char* s, int n)
|
virtual void collectIncomingData(const char* s, int n)
|
||||||
{
|
{
|
||||||
idleTime.stamp();
|
idleTime.stamp();
|
||||||
if ((state == STATE_GETTING_BODY) || (state == STATE_GETTING_CHUNKED_BYTES)) {
|
client->receivedBytes(static_cast<unsigned int>(n));
|
||||||
if (contentGZip || contentDeflate) {
|
|
||||||
expandCompressedData(s, n);
|
|
||||||
} else {
|
|
||||||
activeRequest->processBodyBytes(s, n);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
buffer += string(s, n);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
if( (state == STATE_GETTING_BODY)
|
||||||
void expandCompressedData(const char* s, int n)
|
|| (state == STATE_GETTING_CHUNKED_BYTES) )
|
||||||
{
|
_contentDecoder.receivedBytes(s, n);
|
||||||
int reqSize = n + zlib.avail_in;
|
else
|
||||||
if (reqSize > zlibInflateBufferSize) {
|
buffer.append(s, n);
|
||||||
// reallocate
|
|
||||||
unsigned char* newBuf = (unsigned char*) malloc(reqSize);
|
|
||||||
memcpy(newBuf, zlib.next_in, zlib.avail_in);
|
|
||||||
memcpy(newBuf + zlib.avail_in, s, n);
|
|
||||||
free(zlibInflateBuffer);
|
|
||||||
zlibInflateBuffer = newBuf;
|
|
||||||
zlibInflateBufferSize = reqSize;
|
|
||||||
} else {
|
|
||||||
// important to use memmove here, since it's very likely
|
|
||||||
// the source and destination ranges overlap
|
|
||||||
memmove(zlibInflateBuffer, zlib.next_in, zlib.avail_in);
|
|
||||||
memcpy(zlibInflateBuffer + zlib.avail_in, s, n);
|
|
||||||
}
|
|
||||||
|
|
||||||
zlib.next_in = (unsigned char*) zlibInflateBuffer;
|
|
||||||
zlib.avail_in = reqSize;
|
|
||||||
zlib.next_out = zlibOutputBuffer;
|
|
||||||
zlib.avail_out = ZLIB_DECOMPRESS_BUFFER_SIZE;
|
|
||||||
|
|
||||||
if (contentGZip && !handleGZipHeader()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
int writtenSize = 0;
|
|
||||||
do {
|
|
||||||
int result = inflate(&zlib, Z_NO_FLUSH);
|
|
||||||
if (result == Z_OK || result == Z_STREAM_END) {
|
|
||||||
// nothing to do
|
|
||||||
} else {
|
|
||||||
SG_LOG(SG_IO, SG_WARN, "HTTP: got Zlib error:" << result);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
writtenSize = ZLIB_DECOMPRESS_BUFFER_SIZE - zlib.avail_out;
|
|
||||||
if (result == Z_STREAM_END) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} while ((writtenSize == 0) && (zlib.avail_in > 0));
|
|
||||||
|
|
||||||
if (writtenSize > 0) {
|
|
||||||
activeRequest->processBodyBytes((const char*) zlibOutputBuffer, writtenSize);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool handleGZipHeader()
|
|
||||||
{
|
|
||||||
// we clear this down to contentDeflate once the GZip header has been seen
|
|
||||||
if (zlib.avail_in < GZIP_HEADER_SIZE) {
|
|
||||||
return false; // need more header bytes
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((zlibInflateBuffer[0] != GZIP_HEADER_ID1) ||
|
|
||||||
(zlibInflateBuffer[1] != GZIP_HEADER_ID2) ||
|
|
||||||
(zlibInflateBuffer[2] != GZIP_HEADER_METHOD_DEFLATE))
|
|
||||||
{
|
|
||||||
return false; // invalid GZip header
|
|
||||||
}
|
|
||||||
|
|
||||||
char flags = zlibInflateBuffer[3];
|
|
||||||
unsigned int gzipHeaderSize = GZIP_HEADER_SIZE;
|
|
||||||
if (flags & GZIP_HEADER_FEXTRA) {
|
|
||||||
gzipHeaderSize += 2;
|
|
||||||
if (zlib.avail_in < gzipHeaderSize) {
|
|
||||||
return false; // need more header bytes
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned short extraHeaderBytes = *(reinterpret_cast<unsigned short*>(zlibInflateBuffer + GZIP_HEADER_FEXTRA));
|
|
||||||
if ( sgIsBigEndian() ) {
|
|
||||||
sgEndianSwap( &extraHeaderBytes );
|
|
||||||
}
|
|
||||||
|
|
||||||
gzipHeaderSize += extraHeaderBytes;
|
|
||||||
if (zlib.avail_in < gzipHeaderSize) {
|
|
||||||
return false; // need more header bytes
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (flags & GZIP_HEADER_FNAME) {
|
|
||||||
gzipHeaderSize++;
|
|
||||||
while (gzipHeaderSize <= zlib.avail_in) {
|
|
||||||
if (zlibInflateBuffer[gzipHeaderSize-1] == 0) {
|
|
||||||
break; // found terminating NULL character
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (flags & GZIP_HEADER_COMMENT) {
|
|
||||||
gzipHeaderSize++;
|
|
||||||
while (gzipHeaderSize <= zlib.avail_in) {
|
|
||||||
if (zlibInflateBuffer[gzipHeaderSize-1] == 0) {
|
|
||||||
break; // found terminating NULL character
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (flags & GZIP_HEADER_CRC) {
|
|
||||||
gzipHeaderSize += 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (zlib.avail_in < gzipHeaderSize) {
|
|
||||||
return false; // need more header bytes
|
|
||||||
}
|
|
||||||
|
|
||||||
zlib.next_in += gzipHeaderSize;
|
|
||||||
zlib.avail_in -= gzipHeaderSize;
|
|
||||||
// now we've processed the GZip header, can decode as deflate
|
|
||||||
contentGZip = false;
|
|
||||||
contentDeflate = true;
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void foundTerminator(void)
|
virtual void foundTerminator(void)
|
||||||
{
|
{
|
||||||
idleTime.stamp();
|
idleTime.stamp();
|
||||||
switch (state) {
|
switch (state) {
|
||||||
case STATE_IDLE:
|
case STATE_WAITING_FOR_RESPONSE:
|
||||||
beginResponse();
|
beginResponse();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -443,6 +393,9 @@ public:
|
|||||||
buffer.clear();
|
buffer.clear();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case STATE_IDLE:
|
||||||
|
SG_LOG(SG_IO, SG_WARN, "HTTP got data in IDLE state, bad server?");
|
||||||
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -454,6 +407,7 @@ public:
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
assert(sentRequests.empty());
|
||||||
return idleTime.elapsedMSec() > 1000 * 10; // ten seconds
|
return idleTime.elapsedMSec() > 1000 * 10; // ten seconds
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -500,37 +454,9 @@ private:
|
|||||||
|
|
||||||
void processHeader()
|
void processHeader()
|
||||||
{
|
{
|
||||||
string h = strutils::simplify(buffer);
|
std::string h = strutils::simplify(buffer);
|
||||||
if (h.empty()) { // blank line terminates headers
|
if (h.empty()) { // blank line terminates headers
|
||||||
headersComplete();
|
headersComplete();
|
||||||
|
|
||||||
if (contentGZip || contentDeflate) {
|
|
||||||
memset(&zlib, 0, sizeof(z_stream));
|
|
||||||
if (!zlibOutputBuffer) {
|
|
||||||
zlibOutputBuffer = (unsigned char*) malloc(ZLIB_DECOMPRESS_BUFFER_SIZE);
|
|
||||||
}
|
|
||||||
|
|
||||||
// NULLs means we'll get default alloc+free methods
|
|
||||||
// which is absolutely fine
|
|
||||||
zlib.avail_out = ZLIB_DECOMPRESS_BUFFER_SIZE;
|
|
||||||
zlib.next_out = zlibOutputBuffer;
|
|
||||||
if (inflateInit2(&zlib, ZLIB_INFLATE_WINDOW_BITS) != Z_OK) {
|
|
||||||
SG_LOG(SG_IO, SG_WARN, "inflateInit2 failed");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (chunkedTransfer) {
|
|
||||||
state = STATE_GETTING_CHUNKED;
|
|
||||||
} else if (noMessageBody || (bodyTransferSize == 0)) {
|
|
||||||
// force the state to GETTING_BODY, to simplify logic in
|
|
||||||
// responseComplete and handleClose
|
|
||||||
state = STATE_GETTING_BODY;
|
|
||||||
responseComplete();
|
|
||||||
} else {
|
|
||||||
setByteCount(bodyTransferSize); // may be -1, that's fine
|
|
||||||
state = STATE_GETTING_BODY;
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -540,9 +466,9 @@ private:
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
string key = strutils::simplify(buffer.substr(0, colonPos));
|
std::string key = strutils::simplify(buffer.substr(0, colonPos));
|
||||||
string lkey = boost::to_lower_copy(key);
|
std::string lkey = boost::to_lower_copy(key);
|
||||||
string value = strutils::strip(buffer.substr(colonPos + 1));
|
std::string value = strutils::strip(buffer.substr(colonPos + 1));
|
||||||
|
|
||||||
// only consider these if getting headers (as opposed to trailers
|
// only consider these if getting headers (as opposed to trailers
|
||||||
// of a chunked transfer)
|
// of a chunked transfer)
|
||||||
@@ -559,20 +485,14 @@ private:
|
|||||||
} else if (lkey == "transfer-encoding") {
|
} else if (lkey == "transfer-encoding") {
|
||||||
processTransferEncoding(value);
|
processTransferEncoding(value);
|
||||||
} else if (lkey == "content-encoding") {
|
} else if (lkey == "content-encoding") {
|
||||||
if (value == "gzip") {
|
_contentDecoder.setEncoding(value);
|
||||||
contentGZip = true;
|
|
||||||
} else if (value == "deflate") {
|
|
||||||
contentDeflate = true;
|
|
||||||
} else if (value != "identity") {
|
|
||||||
SG_LOG(SG_IO, SG_WARN, "unsupported content encoding:" << value);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
activeRequest->responseHeader(lkey, value);
|
activeRequest->responseHeader(lkey, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
void processTransferEncoding(const string& te)
|
void processTransferEncoding(const std::string& te)
|
||||||
{
|
{
|
||||||
if (te == "chunked") {
|
if (te == "chunked") {
|
||||||
chunkedTransfer = true;
|
chunkedTransfer = true;
|
||||||
@@ -623,17 +543,25 @@ private:
|
|||||||
void headersComplete()
|
void headersComplete()
|
||||||
{
|
{
|
||||||
activeRequest->responseHeadersComplete();
|
activeRequest->responseHeadersComplete();
|
||||||
|
_contentDecoder.initWithRequest(activeRequest);
|
||||||
|
|
||||||
|
if (chunkedTransfer) {
|
||||||
|
state = STATE_GETTING_CHUNKED;
|
||||||
|
} else if (noMessageBody || (bodyTransferSize == 0)) {
|
||||||
|
// force the state to GETTING_BODY, to simplify logic in
|
||||||
|
// responseComplete and handleClose
|
||||||
|
state = STATE_GETTING_BODY;
|
||||||
|
responseComplete();
|
||||||
|
} else {
|
||||||
|
setByteCount(bodyTransferSize); // may be -1, that's fine
|
||||||
|
state = STATE_GETTING_BODY;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void responseComplete()
|
void responseComplete()
|
||||||
{
|
{
|
||||||
// SG_LOG(SG_IO, SG_INFO, "*** responseComplete:" << activeRequest->url());
|
Request_ptr completedRequest = activeRequest;
|
||||||
activeRequest->responseComplete();
|
_contentDecoder.finish();
|
||||||
client->requestFinished(this);
|
|
||||||
|
|
||||||
if (contentDeflate) {
|
|
||||||
inflateEnd(&zlib);
|
|
||||||
}
|
|
||||||
|
|
||||||
assert(sentRequests.front() == activeRequest);
|
assert(sentRequests.front() == activeRequest);
|
||||||
sentRequests.pop_front();
|
sentRequests.pop_front();
|
||||||
@@ -644,21 +572,29 @@ private:
|
|||||||
if (doClose) {
|
if (doClose) {
|
||||||
// this will bring us into handleClose() above, which updates
|
// this will bring us into handleClose() above, which updates
|
||||||
// state to STATE_CLOSED
|
// state to STATE_CLOSED
|
||||||
close();
|
close();
|
||||||
|
|
||||||
// if we have additional requests waiting, try to start them now
|
// if we have additional requests waiting, try to start them now
|
||||||
tryStartNextRequest();
|
tryStartNextRequest();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (state != STATE_CLOSED) {
|
if (state != STATE_CLOSED) {
|
||||||
state = STATE_IDLE;
|
state = sentRequests.empty() ? STATE_IDLE : STATE_WAITING_FOR_RESPONSE;
|
||||||
}
|
}
|
||||||
setTerminator("\r\n");
|
|
||||||
|
// notify request after we change state, so this connection is idle
|
||||||
|
// if completion triggers other requests (which is likely)
|
||||||
|
// SG_LOG(SG_IO, SG_INFO, "*** responseComplete:" << activeRequest->url());
|
||||||
|
completedRequest->responseComplete();
|
||||||
|
client->requestFinished(this);
|
||||||
|
|
||||||
|
setTerminator("\r\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
enum ConnectionState {
|
enum ConnectionState {
|
||||||
STATE_IDLE = 0,
|
STATE_IDLE = 0,
|
||||||
|
STATE_WAITING_FOR_RESPONSE,
|
||||||
STATE_GETTING_HEADERS,
|
STATE_GETTING_HEADERS,
|
||||||
STATE_GETTING_BODY,
|
STATE_GETTING_BODY,
|
||||||
STATE_GETTING_CHUNKED,
|
STATE_GETTING_CHUNKED,
|
||||||
@@ -671,75 +607,187 @@ private:
|
|||||||
Client* client;
|
Client* client;
|
||||||
Request_ptr activeRequest;
|
Request_ptr activeRequest;
|
||||||
ConnectionState state;
|
ConnectionState state;
|
||||||
string host;
|
std::string host;
|
||||||
short port;
|
short port;
|
||||||
std::string buffer;
|
std::string buffer;
|
||||||
int bodyTransferSize;
|
int bodyTransferSize;
|
||||||
SGTimeStamp idleTime;
|
SGTimeStamp idleTime;
|
||||||
bool chunkedTransfer;
|
bool chunkedTransfer;
|
||||||
bool noMessageBody;
|
bool noMessageBody;
|
||||||
int requestBodyBytesToSend;
|
|
||||||
|
|
||||||
z_stream zlib;
|
|
||||||
unsigned char* zlibInflateBuffer;
|
|
||||||
int zlibInflateBufferSize;
|
|
||||||
unsigned char* zlibOutputBuffer;
|
|
||||||
bool contentGZip, contentDeflate;
|
|
||||||
|
|
||||||
RequestList queuedRequests;
|
RequestList queuedRequests;
|
||||||
RequestList sentRequests;
|
RequestList sentRequests;
|
||||||
|
|
||||||
|
ContentDecoder _contentDecoder;
|
||||||
};
|
};
|
||||||
|
|
||||||
Client::Client()
|
Client::Client() :
|
||||||
|
d(new ClientPrivate)
|
||||||
{
|
{
|
||||||
|
d->proxyPort = 0;
|
||||||
|
d->maxConnections = 4;
|
||||||
|
d->bytesTransferred = 0;
|
||||||
|
d->lastTransferRate = 0;
|
||||||
|
d->timeTransferSample.stamp();
|
||||||
|
d->totalBytesDownloaded = 0;
|
||||||
|
|
||||||
setUserAgent("SimGear-" SG_STRINGIZE(SIMGEAR_VERSION));
|
setUserAgent("SimGear-" SG_STRINGIZE(SIMGEAR_VERSION));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Client::~Client()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void Client::setMaxConnections(unsigned int maxCon)
|
||||||
|
{
|
||||||
|
if (maxCon < 1) {
|
||||||
|
throw sg_range_exception("illegal HTTP::Client::setMaxConnections value");
|
||||||
|
}
|
||||||
|
|
||||||
|
d->maxConnections = maxCon;
|
||||||
|
}
|
||||||
|
|
||||||
void Client::update(int waitTimeout)
|
void Client::update(int waitTimeout)
|
||||||
{
|
{
|
||||||
_poller.poll(waitTimeout);
|
if (!d->poller.hasChannels() && (waitTimeout > 0)) {
|
||||||
|
SGTimeStamp::sleepForMSec(waitTimeout);
|
||||||
|
} else {
|
||||||
|
d->poller.poll(waitTimeout);
|
||||||
|
}
|
||||||
|
|
||||||
ConnectionDict::iterator it = _connections.begin();
|
bool waitingRequests = !d->pendingRequests.empty();
|
||||||
for (; it != _connections.end(); ) {
|
ConnectionDict::iterator it = d->connections.begin();
|
||||||
if (it->second->hasIdleTimeout() || it->second->hasError() ||
|
for (; it != d->connections.end(); ) {
|
||||||
it->second->hasErrorTimeout())
|
Connection* con = it->second;
|
||||||
|
if (con->hasIdleTimeout() ||
|
||||||
|
con->hasError() ||
|
||||||
|
con->hasErrorTimeout() ||
|
||||||
|
(!con->isActive() && waitingRequests))
|
||||||
{
|
{
|
||||||
|
if (con->hasErrorTimeout()) {
|
||||||
|
// tell the connection we're timing it out
|
||||||
|
con->handleTimeout();
|
||||||
|
}
|
||||||
|
|
||||||
// connection has been idle for a while, clean it up
|
// connection has been idle for a while, clean it up
|
||||||
// (or has an error condition, again clean it up)
|
// (or if we have requests waiting for a different host,
|
||||||
|
// or an error condition
|
||||||
ConnectionDict::iterator del = it++;
|
ConnectionDict::iterator del = it++;
|
||||||
delete del->second;
|
delete del->second;
|
||||||
_connections.erase(del);
|
d->connections.erase(del);
|
||||||
} else {
|
} else {
|
||||||
if (it->second->shouldStartNext()) {
|
if (it->second->shouldStartNext()) {
|
||||||
it->second->tryStartNextRequest();
|
it->second->tryStartNextRequest();
|
||||||
}
|
}
|
||||||
|
|
||||||
++it;
|
++it;
|
||||||
}
|
}
|
||||||
} // of connecion iteration
|
} // of connection iteration
|
||||||
|
|
||||||
|
if (waitingRequests && (d->connections.size() < d->maxConnections)) {
|
||||||
|
RequestList waiting(d->pendingRequests);
|
||||||
|
d->pendingRequests.clear();
|
||||||
|
|
||||||
|
// re-submit all waiting requests in order; this takes care of
|
||||||
|
// finding multiple pending items targetted to the same (new)
|
||||||
|
// connection
|
||||||
|
BOOST_FOREACH(Request_ptr req, waiting) {
|
||||||
|
makeRequest(req);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Client::makeRequest(const Request_ptr& r)
|
void Client::makeRequest(const Request_ptr& r)
|
||||||
{
|
{
|
||||||
string host = r->host();
|
if( r->isComplete() )
|
||||||
|
return;
|
||||||
|
|
||||||
|
if( r->url().find("://") == std::string::npos ) {
|
||||||
|
r->setFailure(EINVAL, "malformed URL");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( r->url().find("http://") != 0 ) {
|
||||||
|
r->setFailure(EINVAL, "only HTTP protocol is supported");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string host = r->host();
|
||||||
int port = r->port();
|
int port = r->port();
|
||||||
if (!_proxy.empty()) {
|
if (!d->proxy.empty()) {
|
||||||
host = _proxy;
|
host = d->proxy;
|
||||||
port = _proxyPort;
|
port = d->proxyPort;
|
||||||
}
|
}
|
||||||
|
|
||||||
stringstream ss;
|
Connection* con = NULL;
|
||||||
|
std::stringstream ss;
|
||||||
ss << host << "-" << port;
|
ss << host << "-" << port;
|
||||||
string connectionId = ss.str();
|
std::string connectionId = ss.str();
|
||||||
|
bool havePending = !d->pendingRequests.empty();
|
||||||
|
bool atConnectionsLimit = d->connections.size() >= d->maxConnections;
|
||||||
|
ConnectionDict::iterator consEnd = d->connections.end();
|
||||||
|
|
||||||
if (_connections.find(connectionId) == _connections.end()) {
|
// assign request to an existing Connection.
|
||||||
Connection* con = new Connection(this);
|
// various options exist here, examined in order
|
||||||
con->setServer(host, port);
|
ConnectionDict::iterator it = d->connections.find(connectionId);
|
||||||
_poller.addChannel(con);
|
if (atConnectionsLimit && (it == consEnd)) {
|
||||||
_connections[connectionId] = con;
|
// maximum number of connections active, queue this request
|
||||||
|
// when a connection goes inactive, we'll start this one
|
||||||
|
d->pendingRequests.push_back(r);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_connections[connectionId]->queueRequest(r);
|
// scan for an idle Connection to the same host (likely if we're
|
||||||
|
// retrieving multiple resources from the same host in quick succession)
|
||||||
|
// if we have pending requests (waiting for a free Connection), then
|
||||||
|
// force new requests on this id to always use the first Connection
|
||||||
|
// (instead of the random selection below). This ensures that when
|
||||||
|
// there's pressure on the number of connections to keep alive, one
|
||||||
|
// host can't DoS every other.
|
||||||
|
int count = 0;
|
||||||
|
for (; (it != consEnd) && (it->first == connectionId); ++it, ++count) {
|
||||||
|
if (havePending || !it->second->isActive()) {
|
||||||
|
con = it->second;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!con && atConnectionsLimit) {
|
||||||
|
// all current connections are busy (active), and we don't
|
||||||
|
// have free connections to allocate, so let's assign to
|
||||||
|
// an existing one randomly. Ideally we'd used whichever one will
|
||||||
|
// complete first but we don't have that info.
|
||||||
|
int index = rand() % count;
|
||||||
|
for (it = d->connections.find(connectionId); index > 0; --index) { ; }
|
||||||
|
con = it->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
// allocate a new connection object
|
||||||
|
if (!con) {
|
||||||
|
con = new Connection(this);
|
||||||
|
con->setServer(host, port);
|
||||||
|
d->poller.addChannel(con);
|
||||||
|
d->connections.insert(d->connections.end(),
|
||||||
|
ConnectionDict::value_type(connectionId, con));
|
||||||
|
}
|
||||||
|
|
||||||
|
con->queueRequest(r);
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
FileRequestRef Client::save( const std::string& url,
|
||||||
|
const std::string& filename )
|
||||||
|
{
|
||||||
|
FileRequestRef req = new FileRequest(url, filename);
|
||||||
|
makeRequest(req);
|
||||||
|
return req;
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
MemoryRequestRef Client::load(const std::string& url)
|
||||||
|
{
|
||||||
|
MemoryRequestRef req = new MemoryRequest(url);
|
||||||
|
makeRequest(req);
|
||||||
|
return req;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Client::requestFinished(Connection* con)
|
void Client::requestFinished(Connection* con)
|
||||||
@@ -747,28 +795,82 @@ void Client::requestFinished(Connection* con)
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Client::setUserAgent(const string& ua)
|
void Client::setUserAgent(const std::string& ua)
|
||||||
{
|
{
|
||||||
_userAgent = ua;
|
d->userAgent = ua;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Client::setProxy(const string& proxy, int port, const string& auth)
|
const std::string& Client::userAgent() const
|
||||||
{
|
{
|
||||||
_proxy = proxy;
|
return d->userAgent;
|
||||||
_proxyPort = port;
|
}
|
||||||
_proxyAuth = auth;
|
|
||||||
|
const std::string& Client::proxyHost() const
|
||||||
|
{
|
||||||
|
return d->proxy;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string& Client::proxyAuth() const
|
||||||
|
{
|
||||||
|
return d->proxyAuth;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Client::setProxy( const std::string& proxy,
|
||||||
|
int port,
|
||||||
|
const std::string& auth )
|
||||||
|
{
|
||||||
|
d->proxy = proxy;
|
||||||
|
d->proxyPort = port;
|
||||||
|
d->proxyAuth = auth;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Client::hasActiveRequests() const
|
bool Client::hasActiveRequests() const
|
||||||
{
|
{
|
||||||
ConnectionDict::const_iterator it = _connections.begin();
|
ConnectionDict::const_iterator it = d->connections.begin();
|
||||||
for (; it != _connections.end(); ++it) {
|
for (; it != d->connections.end(); ++it) {
|
||||||
if (it->second->isActive()) return true;
|
if (it->second->isActive()) return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Client::receivedBytes(unsigned int count)
|
||||||
|
{
|
||||||
|
d->bytesTransferred += count;
|
||||||
|
d->totalBytesDownloaded += count;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int Client::transferRateBytesPerSec() const
|
||||||
|
{
|
||||||
|
unsigned int e = d->timeTransferSample.elapsedMSec();
|
||||||
|
if (e > 400) {
|
||||||
|
// too long a window, ignore
|
||||||
|
d->timeTransferSample.stamp();
|
||||||
|
d->bytesTransferred = 0;
|
||||||
|
d->lastTransferRate = 0;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (e < 100) { // avoid really narrow windows
|
||||||
|
return d->lastTransferRate;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int ratio = (d->bytesTransferred * 1000) / e;
|
||||||
|
// run a low-pass filter
|
||||||
|
unsigned int smoothed = ((400 - e) * d->lastTransferRate) + (e * ratio);
|
||||||
|
smoothed /= 400;
|
||||||
|
|
||||||
|
d->timeTransferSample.stamp();
|
||||||
|
d->bytesTransferred = 0;
|
||||||
|
d->lastTransferRate = smoothed;
|
||||||
|
return smoothed;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t Client::totalBytesDownloaded() const
|
||||||
|
{
|
||||||
|
return d->totalBytesDownloaded;
|
||||||
|
}
|
||||||
|
|
||||||
} // of namespace HTTP
|
} // of namespace HTTP
|
||||||
|
|
||||||
} // of namespace simgear
|
} // of namespace simgear
|
||||||
|
|||||||
+71
-18
@@ -1,10 +1,34 @@
|
|||||||
|
/**
|
||||||
|
* \file HTTPClient.hxx - simple HTTP client engine for SimHear
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Written by James Turner
|
||||||
|
//
|
||||||
|
// Copyright (C) 2013 James Turner <zakalawe@mac.com>
|
||||||
|
//
|
||||||
|
// This library is free software; you can redistribute it and/or
|
||||||
|
// modify it under the terms of the GNU Library General Public
|
||||||
|
// License as published by the Free Software Foundation; either
|
||||||
|
// version 2 of the License, or (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This library is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
// Library General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with this program; if not, write to the Free Software
|
||||||
|
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
//
|
||||||
|
|
||||||
#ifndef SG_HTTP_CLIENT_HXX
|
#ifndef SG_HTTP_CLIENT_HXX
|
||||||
#define SG_HTTP_CLIENT_HXX
|
#define SG_HTTP_CLIENT_HXX
|
||||||
|
|
||||||
#include <map>
|
#include <memory> // for std::auto_ptr
|
||||||
|
#include <stdint.h> // for uint_64t
|
||||||
|
|
||||||
#include <simgear/io/HTTPRequest.hxx>
|
#include <simgear/io/HTTPFileRequest.hxx>
|
||||||
#include <simgear/io/sg_netChannel.hxx>
|
#include <simgear/io/HTTPMemoryRequest.hxx>
|
||||||
|
|
||||||
namespace simgear
|
namespace simgear
|
||||||
{
|
{
|
||||||
@@ -12,49 +36,78 @@ namespace simgear
|
|||||||
namespace HTTP
|
namespace HTTP
|
||||||
{
|
{
|
||||||
|
|
||||||
|
// forward decls
|
||||||
class Connection;
|
class Connection;
|
||||||
|
|
||||||
class Client
|
class Client
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Client();
|
Client();
|
||||||
|
~Client();
|
||||||
|
|
||||||
void update(int waitTimeout = 0);
|
void update(int waitTimeout = 0);
|
||||||
|
|
||||||
void makeRequest(const Request_ptr& r);
|
void makeRequest(const Request_ptr& r);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Download a resource and save it to a file.
|
||||||
|
*
|
||||||
|
* @param url The resource to download
|
||||||
|
* @param filename Path to the target file
|
||||||
|
* @param data Data for POST request
|
||||||
|
*/
|
||||||
|
FileRequestRef save( const std::string& url,
|
||||||
|
const std::string& filename );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Request a resource and keep it in memory.
|
||||||
|
*
|
||||||
|
* @param url The resource to download
|
||||||
|
*/
|
||||||
|
MemoryRequestRef load(const std::string& url);
|
||||||
|
|
||||||
void setUserAgent(const std::string& ua);
|
void setUserAgent(const std::string& ua);
|
||||||
void setProxy(const std::string& proxy, int port, const std::string& auth = "");
|
void setProxy(const std::string& proxy, int port, const std::string& auth = "");
|
||||||
|
|
||||||
const std::string& userAgent() const
|
/**
|
||||||
{ return _userAgent; }
|
* Specify the maximum permitted simultaneous connections
|
||||||
|
* (default value is 1)
|
||||||
|
*/
|
||||||
|
void setMaxConnections(unsigned int maxCons);
|
||||||
|
|
||||||
const std::string& proxyHost() const
|
const std::string& userAgent() const;
|
||||||
{ return _proxy; }
|
|
||||||
|
|
||||||
const std::string& proxyAuth() const
|
const std::string& proxyHost() const;
|
||||||
{ return _proxyAuth; }
|
|
||||||
|
const std::string& proxyAuth() const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* predicate, check if at least one connection is active, with at
|
* predicate, check if at least one connection is active, with at
|
||||||
* least one request active or queued.
|
* least one request active or queued.
|
||||||
*/
|
*/
|
||||||
bool hasActiveRequests() const;
|
bool hasActiveRequests() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* crude tracking of bytes-per-second transferred over the socket.
|
||||||
|
* suitable for user feedback and rough profiling, nothing more.
|
||||||
|
*/
|
||||||
|
unsigned int transferRateBytesPerSec() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* total bytes downloaded by this HTTP client, for bandwidth usage
|
||||||
|
* monitoring
|
||||||
|
*/
|
||||||
|
uint64_t totalBytesDownloaded() const;
|
||||||
private:
|
private:
|
||||||
void requestFinished(Connection* con);
|
void requestFinished(Connection* con);
|
||||||
|
|
||||||
|
void receivedBytes(unsigned int count);
|
||||||
|
|
||||||
friend class Connection;
|
friend class Connection;
|
||||||
friend class Request;
|
friend class Request;
|
||||||
|
|
||||||
std::string _userAgent;
|
class ClientPrivate;
|
||||||
std::string _proxy;
|
std::auto_ptr<ClientPrivate> d;
|
||||||
int _proxyPort;
|
|
||||||
std::string _proxyAuth;
|
|
||||||
NetChannelPoller _poller;
|
|
||||||
|
|
||||||
// connections by host
|
|
||||||
typedef std::map<std::string, Connection*> ConnectionDict;
|
|
||||||
ConnectionDict _connections;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // of namespace HTTP
|
} // of namespace HTTP
|
||||||
|
|||||||
@@ -0,0 +1,269 @@
|
|||||||
|
// Written by James Turner
|
||||||
|
//
|
||||||
|
// Copyright (C) 2013 James Turner <zakalawe@mac.com>
|
||||||
|
//
|
||||||
|
// This library is free software; you can redistribute it and/or
|
||||||
|
// modify it under the terms of the GNU Library General Public
|
||||||
|
// License as published by the Free Software Foundation; either
|
||||||
|
// version 2 of the License, or (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This library is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
// Library General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with this program; if not, write to the Free Software
|
||||||
|
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "HTTPContentDecode.hxx"
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
|
#include <cstdlib> // rand()
|
||||||
|
#include <cstring> // for memset, memcpy
|
||||||
|
|
||||||
|
#include <simgear/debug/logstream.hxx>
|
||||||
|
#include <simgear/structure/exception.hxx>
|
||||||
|
#include <simgear/io/lowlevel.hxx> // for sgEndian stuff
|
||||||
|
|
||||||
|
namespace simgear
|
||||||
|
{
|
||||||
|
|
||||||
|
namespace HTTP
|
||||||
|
{
|
||||||
|
|
||||||
|
const int ZLIB_DECOMPRESS_BUFFER_SIZE = 32 * 1024;
|
||||||
|
const int ZLIB_INFLATE_WINDOW_BITS = -MAX_WBITS;
|
||||||
|
|
||||||
|
// see http://www.ietf.org/rfc/rfc1952.txt for these values and
|
||||||
|
// detailed description of the logic
|
||||||
|
const int GZIP_HEADER_ID1 = 31;
|
||||||
|
const int GZIP_HEADER_ID2 = 139;
|
||||||
|
const int GZIP_HEADER_METHOD_DEFLATE = 8;
|
||||||
|
const unsigned int GZIP_HEADER_SIZE = 10;
|
||||||
|
const int GZIP_HEADER_FEXTRA = 1 << 2;
|
||||||
|
const int GZIP_HEADER_FNAME = 1 << 3;
|
||||||
|
const int GZIP_HEADER_COMMENT = 1 << 4;
|
||||||
|
const int GZIP_HEADER_CRC = 1 << 1;
|
||||||
|
|
||||||
|
ContentDecoder::ContentDecoder() :
|
||||||
|
_output(NULL),
|
||||||
|
_zlib(NULL),
|
||||||
|
_input(NULL),
|
||||||
|
_inputAllocated(0),
|
||||||
|
_inputSize(0)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
ContentDecoder::~ContentDecoder()
|
||||||
|
{
|
||||||
|
free(_output);
|
||||||
|
free(_input);
|
||||||
|
free(_zlib);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ContentDecoder::setEncoding(const std::string& encoding)
|
||||||
|
{
|
||||||
|
if (encoding == "gzip") {
|
||||||
|
_contentDeflate = true;
|
||||||
|
_needGZipHeader = true;
|
||||||
|
} else if (encoding == "deflate") {
|
||||||
|
_contentDeflate = true;
|
||||||
|
_needGZipHeader = false;
|
||||||
|
} else if (encoding != "identity") {
|
||||||
|
SG_LOG(SG_IO, SG_WARN, "unsupported content encoding:" << encoding);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ContentDecoder::reset()
|
||||||
|
{
|
||||||
|
_request = NULL;
|
||||||
|
_contentDeflate = false;
|
||||||
|
_needGZipHeader = false;
|
||||||
|
_inputSize = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ContentDecoder::initWithRequest(Request_ptr req)
|
||||||
|
{
|
||||||
|
_request = req;
|
||||||
|
if (!_contentDeflate) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_zlib) {
|
||||||
|
_zlib = (z_stream*) malloc(sizeof(z_stream));
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(_zlib, 0, sizeof(z_stream));
|
||||||
|
if (!_output) {
|
||||||
|
_output = (unsigned char*) malloc(ZLIB_DECOMPRESS_BUFFER_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
_inputSize = 0;
|
||||||
|
// NULLs means we'll get default alloc+free methods
|
||||||
|
// which is absolutely fine
|
||||||
|
_zlib->avail_out = ZLIB_DECOMPRESS_BUFFER_SIZE;
|
||||||
|
_zlib->next_out = _output;
|
||||||
|
if (inflateInit2(_zlib, ZLIB_INFLATE_WINDOW_BITS) != Z_OK) {
|
||||||
|
SG_LOG(SG_IO, SG_WARN, "inflateInit2 failed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ContentDecoder::finish()
|
||||||
|
{
|
||||||
|
if (_contentDeflate) {
|
||||||
|
runDecoder();
|
||||||
|
inflateEnd(_zlib);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ContentDecoder::receivedBytes(const char* n, size_t s)
|
||||||
|
{
|
||||||
|
if (!_contentDeflate) {
|
||||||
|
_request->processBodyBytes(n, s);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// allocate more space if needed (this will only happen rarely once the
|
||||||
|
// buffer has hit something proportionate to the server's compression
|
||||||
|
// window size)
|
||||||
|
size_t requiredSize = _inputSize + s;
|
||||||
|
if (requiredSize > _inputAllocated) {
|
||||||
|
reallocateInputBuffer(requiredSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
// copy newly recieved bytes into the buffer
|
||||||
|
memcpy(_input + _inputSize, n, s);
|
||||||
|
_inputSize += s;
|
||||||
|
|
||||||
|
if (_needGZipHeader && !consumeGZipHeader()) {
|
||||||
|
// still waiting on the full GZIP header, so done
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
runDecoder();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ContentDecoder::consumeBytes(size_t consumed)
|
||||||
|
{
|
||||||
|
assert(_inputSize >= consumed);
|
||||||
|
// move existing (consumed) bytes down
|
||||||
|
if (consumed > 0) {
|
||||||
|
size_t newSize = _inputSize - consumed;
|
||||||
|
memmove(_input, _input + consumed, newSize);
|
||||||
|
_inputSize = newSize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ContentDecoder::reallocateInputBuffer(size_t newSize)
|
||||||
|
{
|
||||||
|
_input = (unsigned char*) realloc(_input, newSize);
|
||||||
|
_inputAllocated = newSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ContentDecoder::runDecoder()
|
||||||
|
{
|
||||||
|
_zlib->next_in = (unsigned char*) _input;
|
||||||
|
_zlib->avail_in = _inputSize;
|
||||||
|
int writtenSize;
|
||||||
|
|
||||||
|
// loop, running zlib() inflate and sending output bytes to
|
||||||
|
// our request body handler. Keep calling inflate until no bytes are
|
||||||
|
// written, and ZLIB has consumed all available input
|
||||||
|
do {
|
||||||
|
_zlib->next_out = _output;
|
||||||
|
_zlib->avail_out = ZLIB_DECOMPRESS_BUFFER_SIZE;
|
||||||
|
int result = inflate(_zlib, Z_NO_FLUSH);
|
||||||
|
if (result == Z_OK || result == Z_STREAM_END) {
|
||||||
|
// nothing to do
|
||||||
|
} else if (result == Z_BUF_ERROR) {
|
||||||
|
// transient error, fall through
|
||||||
|
} else {
|
||||||
|
// _error = result;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
writtenSize = ZLIB_DECOMPRESS_BUFFER_SIZE - _zlib->avail_out;
|
||||||
|
if (writtenSize > 0) {
|
||||||
|
_request->processBodyBytes((char*) _output, writtenSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result == Z_STREAM_END) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} while ((_zlib->avail_in > 0) || (writtenSize > 0));
|
||||||
|
|
||||||
|
// update input buffers based on what we consumed
|
||||||
|
consumeBytes(_inputSize - _zlib->avail_in);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ContentDecoder::consumeGZipHeader()
|
||||||
|
{
|
||||||
|
size_t avail = _inputSize;
|
||||||
|
if (avail < GZIP_HEADER_SIZE) {
|
||||||
|
return false; // need more header bytes
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((_input[0] != GZIP_HEADER_ID1) ||
|
||||||
|
(_input[1] != GZIP_HEADER_ID2) ||
|
||||||
|
(_input[2] != GZIP_HEADER_METHOD_DEFLATE))
|
||||||
|
{
|
||||||
|
return false; // invalid GZip header
|
||||||
|
}
|
||||||
|
|
||||||
|
char flags = _input[3];
|
||||||
|
unsigned int gzipHeaderSize = GZIP_HEADER_SIZE;
|
||||||
|
if (flags & GZIP_HEADER_FEXTRA) {
|
||||||
|
gzipHeaderSize += 2;
|
||||||
|
if (avail < gzipHeaderSize) {
|
||||||
|
return false; // need more header bytes
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned short extraHeaderBytes = *(reinterpret_cast<unsigned short*>(_input + GZIP_HEADER_FEXTRA));
|
||||||
|
if ( sgIsBigEndian() ) {
|
||||||
|
sgEndianSwap( &extraHeaderBytes );
|
||||||
|
}
|
||||||
|
|
||||||
|
gzipHeaderSize += extraHeaderBytes;
|
||||||
|
if (avail < gzipHeaderSize) {
|
||||||
|
return false; // need more header bytes
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
if (flags & GZIP_HEADER_FNAME) {
|
||||||
|
gzipHeaderSize++;
|
||||||
|
while (gzipHeaderSize <= avail) {
|
||||||
|
if (_input[gzipHeaderSize-1] == 0) {
|
||||||
|
break; // found terminating NULL character
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (flags & GZIP_HEADER_COMMENT) {
|
||||||
|
gzipHeaderSize++;
|
||||||
|
while (gzipHeaderSize <= avail) {
|
||||||
|
if (_input[gzipHeaderSize-1] == 0) {
|
||||||
|
break; // found terminating NULL character
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (flags & GZIP_HEADER_CRC) {
|
||||||
|
gzipHeaderSize += 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (avail < gzipHeaderSize) {
|
||||||
|
return false; // need more header bytes
|
||||||
|
}
|
||||||
|
|
||||||
|
consumeBytes(gzipHeaderSize);
|
||||||
|
_needGZipHeader = false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // of namespace HTTP
|
||||||
|
|
||||||
|
} // of namespace simgear
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
// Written by James Turner
|
||||||
|
//
|
||||||
|
// Copyright (C) 2013 James Turner <zakalawe@mac.com>
|
||||||
|
//
|
||||||
|
// This library is free software; you can redistribute it and/or
|
||||||
|
// modify it under the terms of the GNU Library General Public
|
||||||
|
// License as published by the Free Software Foundation; either
|
||||||
|
// version 2 of the License, or (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This library is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
// Library General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with this program; if not, write to the Free Software
|
||||||
|
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef SG_HTTP_CONTENT_DECODER_HXX
|
||||||
|
#define SG_HTTP_CONTENT_DECODER_HXX
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include <zlib.h>
|
||||||
|
|
||||||
|
#include <simgear/io/HTTPRequest.hxx>
|
||||||
|
|
||||||
|
|
||||||
|
namespace simgear
|
||||||
|
{
|
||||||
|
|
||||||
|
namespace HTTP
|
||||||
|
{
|
||||||
|
|
||||||
|
class ContentDecoder
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ContentDecoder();
|
||||||
|
~ContentDecoder();
|
||||||
|
|
||||||
|
void reset();
|
||||||
|
|
||||||
|
void initWithRequest(Request_ptr req);
|
||||||
|
|
||||||
|
void finish();
|
||||||
|
|
||||||
|
void setEncoding(const std::string& encoding);
|
||||||
|
|
||||||
|
void receivedBytes(const char* n, size_t s);
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool consumeGZipHeader();
|
||||||
|
void runDecoder();
|
||||||
|
|
||||||
|
void consumeBytes(size_t consumed);
|
||||||
|
void reallocateInputBuffer(size_t newSize);
|
||||||
|
|
||||||
|
Request_ptr _request;
|
||||||
|
unsigned char* _output;
|
||||||
|
|
||||||
|
z_stream* _zlib;
|
||||||
|
unsigned char* _input;
|
||||||
|
size_t _inputAllocated, _inputSize;
|
||||||
|
bool _contentDeflate, _needGZipHeader;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // of namespace HTTP
|
||||||
|
|
||||||
|
} // of namespace simgear
|
||||||
|
|
||||||
|
#endif // of SG_HTTP_CONTENT_DECODER_HXX
|
||||||
@@ -0,0 +1,91 @@
|
|||||||
|
// HTTP request writing response to a file.
|
||||||
|
//
|
||||||
|
// Copyright (C) 2013 Thomas Geymayer <tomgey@gmail.com>
|
||||||
|
//
|
||||||
|
// This library is free software; you can redistribute it and/or
|
||||||
|
// modify it under the terms of the GNU Library General Public
|
||||||
|
// License as published by the Free Software Foundation; either
|
||||||
|
// version 2 of the License, or (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This library is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
// Library General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Library General Public
|
||||||
|
// License along with this library; if not, write to the Free Software
|
||||||
|
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||||
|
|
||||||
|
#include "HTTPFileRequest.hxx"
|
||||||
|
#include <simgear/debug/logstream.hxx>
|
||||||
|
#include <simgear/misc/sg_path.hxx>
|
||||||
|
|
||||||
|
namespace simgear
|
||||||
|
{
|
||||||
|
namespace HTTP
|
||||||
|
{
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
FileRequest::FileRequest(const std::string& url, const std::string& path):
|
||||||
|
Request(url, "GET"),
|
||||||
|
_filename(path)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
void FileRequest::responseHeadersComplete()
|
||||||
|
{
|
||||||
|
Request::responseHeadersComplete();
|
||||||
|
|
||||||
|
if( responseCode() != 200 )
|
||||||
|
return setFailure(responseCode(), responseReason());
|
||||||
|
|
||||||
|
if( !_filename.empty() )
|
||||||
|
{
|
||||||
|
// TODO validate path? (would require to expose fgValidatePath somehow to
|
||||||
|
// simgear)
|
||||||
|
SGPath path(_filename);
|
||||||
|
path.create_dir(0755);
|
||||||
|
|
||||||
|
_file.open(_filename.c_str(), std::ios::binary | std::ios::trunc);
|
||||||
|
}
|
||||||
|
|
||||||
|
if( !_file )
|
||||||
|
{
|
||||||
|
SG_LOG
|
||||||
|
(
|
||||||
|
SG_IO,
|
||||||
|
SG_WARN,
|
||||||
|
"HTTP::FileRequest: failed to open file '" << _filename << "'"
|
||||||
|
);
|
||||||
|
|
||||||
|
abort("Failed to open file.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
void FileRequest::gotBodyData(const char* s, int n)
|
||||||
|
{
|
||||||
|
if( !_file )
|
||||||
|
{
|
||||||
|
SG_LOG
|
||||||
|
(
|
||||||
|
SG_IO,
|
||||||
|
SG_DEBUG,
|
||||||
|
"HTTP::FileRequest: received data for closed file '" << _filename << "'"
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_file.write(s, n);
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
void FileRequest::onAlways()
|
||||||
|
{
|
||||||
|
_file.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace HTTP
|
||||||
|
} // namespace simgear
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
///@file HTTP request writing response to a file.
|
||||||
|
//
|
||||||
|
// Copyright (C) 2013 Thomas Geymayer <tomgey@gmail.com>
|
||||||
|
//
|
||||||
|
// This library is free software; you can redistribute it and/or
|
||||||
|
// modify it under the terms of the GNU Library General Public
|
||||||
|
// License as published by the Free Software Foundation; either
|
||||||
|
// version 2 of the License, or (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This library is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
// Library General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Library General Public
|
||||||
|
// License along with this library; if not, write to the Free Software
|
||||||
|
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||||
|
|
||||||
|
#ifndef SG_HTTP_FILEREQUEST_HXX_
|
||||||
|
#define SG_HTTP_FILEREQUEST_HXX_
|
||||||
|
|
||||||
|
#include "HTTPRequest.hxx"
|
||||||
|
#include <fstream>
|
||||||
|
|
||||||
|
namespace simgear
|
||||||
|
{
|
||||||
|
namespace HTTP
|
||||||
|
{
|
||||||
|
|
||||||
|
class FileRequest:
|
||||||
|
public Request
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param url Adress to download from
|
||||||
|
* @param path Path to file for saving response
|
||||||
|
*/
|
||||||
|
FileRequest(const std::string& url, const std::string& path);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
std::string _filename;
|
||||||
|
std::ofstream _file;
|
||||||
|
|
||||||
|
virtual void responseHeadersComplete();
|
||||||
|
virtual void gotBodyData(const char* s, int n);
|
||||||
|
virtual void onAlways();
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef SGSharedPtr<FileRequest> FileRequestRef;
|
||||||
|
|
||||||
|
} // namespace HTTP
|
||||||
|
} // namespace simgear
|
||||||
|
|
||||||
|
#endif /* SG_HTTP_FILEREQUEST_HXX_ */
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
// HTTP request keeping response in memory.
|
||||||
|
//
|
||||||
|
// Copyright (C) 2013 Thomas Geymayer <tomgey@gmail.com>
|
||||||
|
//
|
||||||
|
// This library is free software; you can redistribute it and/or
|
||||||
|
// modify it under the terms of the GNU Library General Public
|
||||||
|
// License as published by the Free Software Foundation; either
|
||||||
|
// version 2 of the License, or (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This library is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
// Library General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Library General Public
|
||||||
|
// License along with this library; if not, write to the Free Software
|
||||||
|
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||||
|
|
||||||
|
#include "HTTPMemoryRequest.hxx"
|
||||||
|
|
||||||
|
namespace simgear
|
||||||
|
{
|
||||||
|
namespace HTTP
|
||||||
|
{
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
MemoryRequest::MemoryRequest(const std::string& url):
|
||||||
|
Request(url, "GET")
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
const std::string& MemoryRequest::responseBody() const
|
||||||
|
{
|
||||||
|
return _response;
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
void MemoryRequest::responseHeadersComplete()
|
||||||
|
{
|
||||||
|
Request::responseHeadersComplete();
|
||||||
|
|
||||||
|
if( responseLength() )
|
||||||
|
_response.reserve( responseLength() );
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
void MemoryRequest::gotBodyData(const char* s, int n)
|
||||||
|
{
|
||||||
|
_response.append(s, n);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace HTTP
|
||||||
|
} // namespace simgear
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
///@file HTTP request keeping response in memory.
|
||||||
|
//
|
||||||
|
// Copyright (C) 2013 Thomas Geymayer <tomgey@gmail.com>
|
||||||
|
//
|
||||||
|
// This library is free software; you can redistribute it and/or
|
||||||
|
// modify it under the terms of the GNU Library General Public
|
||||||
|
// License as published by the Free Software Foundation; either
|
||||||
|
// version 2 of the License, or (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This library is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
// Library General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Library General Public
|
||||||
|
// License along with this library; if not, write to the Free Software
|
||||||
|
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||||
|
|
||||||
|
#ifndef SG_HTTP_MEMORYREQUEST_HXX_
|
||||||
|
#define SG_HTTP_MEMORYREQUEST_HXX_
|
||||||
|
|
||||||
|
#include "HTTPRequest.hxx"
|
||||||
|
#include <fstream>
|
||||||
|
|
||||||
|
namespace simgear
|
||||||
|
{
|
||||||
|
namespace HTTP
|
||||||
|
{
|
||||||
|
|
||||||
|
class MemoryRequest:
|
||||||
|
public Request
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param url Adress to download from
|
||||||
|
*/
|
||||||
|
MemoryRequest(const std::string& url);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Body contents of server response.
|
||||||
|
*/
|
||||||
|
const std::string& responseBody() const;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
std::string _response;
|
||||||
|
|
||||||
|
virtual void responseHeadersComplete();
|
||||||
|
virtual void gotBodyData(const char* s, int n);
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef SGSharedPtr<MemoryRequest> MemoryRequestRef;
|
||||||
|
|
||||||
|
} // namespace HTTP
|
||||||
|
} // namespace simgear
|
||||||
|
|
||||||
|
#endif /* SG_HTTP_MEMORYREQUEST_HXX_ */
|
||||||
+246
-104
@@ -1,104 +1,190 @@
|
|||||||
#include "HTTPRequest.hxx"
|
#include "HTTPRequest.hxx"
|
||||||
|
|
||||||
#include <simgear/misc/strutils.hxx>
|
|
||||||
#include <simgear/compiler.h>
|
#include <simgear/compiler.h>
|
||||||
#include <simgear/debug/logstream.hxx>
|
#include <simgear/debug/logstream.hxx>
|
||||||
|
#include <simgear/misc/strutils.hxx>
|
||||||
using std::string;
|
#include <simgear/props/props_io.hxx>
|
||||||
using std::map;
|
#include <simgear/structure/exception.hxx>
|
||||||
|
|
||||||
namespace simgear
|
namespace simgear
|
||||||
{
|
{
|
||||||
|
|
||||||
namespace HTTP
|
namespace HTTP
|
||||||
{
|
{
|
||||||
|
|
||||||
extern const int DEFAULT_HTTP_PORT;
|
extern const int DEFAULT_HTTP_PORT;
|
||||||
|
|
||||||
Request::Request(const string& url, const string method) :
|
//------------------------------------------------------------------------------
|
||||||
_method(method),
|
Request::Request(const std::string& url, const std::string method):
|
||||||
_url(url),
|
_method(method),
|
||||||
_responseVersion(HTTP_VERSION_UNKNOWN),
|
_url(url),
|
||||||
_responseStatus(0),
|
_responseVersion(HTTP_VERSION_UNKNOWN),
|
||||||
_responseLength(0),
|
_responseStatus(0),
|
||||||
_receivedBodyBytes(0),
|
_responseLength(0),
|
||||||
_willClose(false)
|
_receivedBodyBytes(0),
|
||||||
|
_ready_state(UNSENT),
|
||||||
|
_willClose(false)
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
Request::~Request()
|
Request::~Request()
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Request::setUrl(const string& url)
|
//------------------------------------------------------------------------------
|
||||||
|
Request* Request::done(const Callback& cb)
|
||||||
{
|
{
|
||||||
_url = url;
|
if( _ready_state == DONE )
|
||||||
|
cb(this);
|
||||||
|
else
|
||||||
|
_cb_done = cb;
|
||||||
|
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
string_list Request::requestHeaders() const
|
//------------------------------------------------------------------------------
|
||||||
|
Request* Request::fail(const Callback& cb)
|
||||||
{
|
{
|
||||||
string_list r;
|
if( _ready_state == FAILED )
|
||||||
return r;
|
cb(this);
|
||||||
|
else
|
||||||
|
_cb_fail = cb;
|
||||||
|
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
string Request::header(const std::string& name) const
|
//------------------------------------------------------------------------------
|
||||||
|
Request* Request::always(const Callback& cb)
|
||||||
{
|
{
|
||||||
return string();
|
if( isComplete() )
|
||||||
|
cb(this);
|
||||||
|
else
|
||||||
|
_cb_always = cb;
|
||||||
|
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
void Request::setBodyData( const std::string& data,
|
||||||
|
const std::string& type )
|
||||||
|
{
|
||||||
|
_request_data = data;
|
||||||
|
_request_media_type = type;
|
||||||
|
|
||||||
|
if( !data.empty() && _method == "GET" )
|
||||||
|
_method = "POST";
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
void Request::setBodyData(const SGPropertyNode* data)
|
||||||
|
{
|
||||||
|
if( !data )
|
||||||
|
setBodyData("");
|
||||||
|
|
||||||
|
std::stringstream buf;
|
||||||
|
writeProperties(buf, data, true);
|
||||||
|
|
||||||
|
setBodyData(buf.str(), "application/xml");
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
void Request::setUrl(const std::string& url)
|
||||||
|
{
|
||||||
|
_url = url;
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
void Request::requestStart()
|
void Request::requestStart()
|
||||||
{
|
{
|
||||||
|
setReadyState(OPENED);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Request::responseStart(const string& r)
|
//------------------------------------------------------------------------------
|
||||||
|
Request::HTTPVersion decodeHTTPVersion(const std::string& v)
|
||||||
|
{
|
||||||
|
if( v == "HTTP/1.1" ) return Request::HTTP_1_1;
|
||||||
|
if( v == "HTTP/1.0" ) return Request::HTTP_1_0;
|
||||||
|
if( strutils::starts_with(v, "HTTP/0.") ) return Request::HTTP_0_x;
|
||||||
|
return Request::HTTP_VERSION_UNKNOWN;
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
void Request::responseStart(const std::string& r)
|
||||||
{
|
{
|
||||||
const int maxSplit = 2; // HTTP/1.1 nnn reason-string
|
const int maxSplit = 2; // HTTP/1.1 nnn reason-string
|
||||||
string_list parts = strutils::split(r, NULL, maxSplit);
|
string_list parts = strutils::split(r, NULL, maxSplit);
|
||||||
if (parts.size() != 3) {
|
if (parts.size() != 3) {
|
||||||
SG_LOG(SG_IO, SG_WARN, "HTTP::Request: malformed response start:" << r);
|
throw sg_io_exception("bad HTTP response");
|
||||||
setFailure(400, "malformed HTTP response header");
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_responseVersion = decodeVersion(parts[0]);
|
_responseVersion = decodeHTTPVersion(parts[0]);
|
||||||
_responseStatus = strutils::to_int(parts[1]);
|
_responseStatus = strutils::to_int(parts[1]);
|
||||||
_responseReason = parts[2];
|
_responseReason = parts[2];
|
||||||
}
|
}
|
||||||
|
|
||||||
void Request::responseHeader(const string& key, const string& value)
|
//------------------------------------------------------------------------------
|
||||||
|
void Request::responseHeader(const std::string& key, const std::string& value)
|
||||||
{
|
{
|
||||||
if (key == "connection") {
|
if( key == "connection" )
|
||||||
_willClose = (value.find("close") != string::npos);
|
_willClose = (value.find("close") != std::string::npos);
|
||||||
}
|
|
||||||
|
|
||||||
_responseHeaders[key] = value;
|
_responseHeaders[key] = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
void Request::responseHeadersComplete()
|
void Request::responseHeadersComplete()
|
||||||
{
|
{
|
||||||
// no op
|
setReadyState(HEADERS_RECEIVED);
|
||||||
}
|
|
||||||
|
|
||||||
void Request::processBodyBytes(const char* s, int n)
|
|
||||||
{
|
|
||||||
_receivedBodyBytes += n;
|
|
||||||
gotBodyData(s, n);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Request::gotBodyData(const char* s, int n)
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
void Request::responseComplete()
|
void Request::responseComplete()
|
||||||
|
{
|
||||||
|
if( !isComplete() )
|
||||||
|
setReadyState(DONE);
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
void Request::gotBodyData(const char* s, int n)
|
||||||
|
{
|
||||||
|
setReadyState(LOADING);
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
void Request::onDone()
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
string Request::scheme() const
|
//------------------------------------------------------------------------------
|
||||||
|
void Request::onFail()
|
||||||
|
{
|
||||||
|
SG_LOG
|
||||||
|
(
|
||||||
|
SG_IO,
|
||||||
|
SG_INFO,
|
||||||
|
"request failed:" << url() << " : "
|
||||||
|
<< responseCode() << "/" << responseReason()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
void Request::onAlways()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
void Request::processBodyBytes(const char* s, int n)
|
||||||
|
{
|
||||||
|
_receivedBodyBytes += n;
|
||||||
|
gotBodyData(s, n);
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
std::string Request::scheme() const
|
||||||
{
|
{
|
||||||
int firstColon = url().find(":");
|
int firstColon = url().find(":");
|
||||||
if (firstColon > 0) {
|
if (firstColon > 0) {
|
||||||
@@ -108,9 +194,10 @@ string Request::scheme() const
|
|||||||
return ""; // couldn't parse scheme
|
return ""; // couldn't parse scheme
|
||||||
}
|
}
|
||||||
|
|
||||||
string Request::path() const
|
//------------------------------------------------------------------------------
|
||||||
|
std::string Request::path() const
|
||||||
{
|
{
|
||||||
string u(url());
|
std::string u(url());
|
||||||
int schemeEnd = u.find("://");
|
int schemeEnd = u.find("://");
|
||||||
if (schemeEnd < 0) {
|
if (schemeEnd < 0) {
|
||||||
return ""; // couldn't parse scheme
|
return ""; // couldn't parse scheme
|
||||||
@@ -132,10 +219,10 @@ string Request::path() const
|
|||||||
return u.substr(hostEnd, query - hostEnd);
|
return u.substr(hostEnd, query - hostEnd);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
string Request::query() const
|
std::string Request::query() const
|
||||||
{
|
{
|
||||||
string u(url());
|
std::string u(url());
|
||||||
int query = u.find('?');
|
int query = u.find('?');
|
||||||
if (query < 0) {
|
if (query < 0) {
|
||||||
return ""; //no query string found
|
return ""; //no query string found
|
||||||
@@ -144,104 +231,159 @@ string Request::query() const
|
|||||||
return u.substr(query); //includes question mark
|
return u.substr(query); //includes question mark
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
std::string Request::host() const
|
||||||
string Request::host() const
|
|
||||||
{
|
{
|
||||||
string hp(hostAndPort());
|
std::string hp(hostAndPort());
|
||||||
int colonPos = hp.find(':');
|
int colonPos = hp.find(':');
|
||||||
if (colonPos >= 0) {
|
if (colonPos >= 0) {
|
||||||
return hp.substr(0, colonPos); // trim off the colon and port
|
return hp.substr(0, colonPos); // trim off the colon and port
|
||||||
} else {
|
} else {
|
||||||
return hp; // no port specifier
|
return hp; // no port specifier
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
unsigned short Request::port() const
|
unsigned short Request::port() const
|
||||||
{
|
{
|
||||||
string hp(hostAndPort());
|
std::string hp(hostAndPort());
|
||||||
int colonPos = hp.find(':');
|
int colonPos = hp.find(':');
|
||||||
if (colonPos >= 0) {
|
if (colonPos >= 0) {
|
||||||
return (unsigned short) strutils::to_int(hp.substr(colonPos + 1));
|
return (unsigned short) strutils::to_int(hp.substr(colonPos + 1));
|
||||||
} else {
|
} else {
|
||||||
return DEFAULT_HTTP_PORT;
|
return DEFAULT_HTTP_PORT;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
string Request::hostAndPort() const
|
//------------------------------------------------------------------------------
|
||||||
|
std::string Request::hostAndPort() const
|
||||||
{
|
{
|
||||||
string u(url());
|
std::string u(url());
|
||||||
int schemeEnd = u.find("://");
|
int schemeEnd = u.find("://");
|
||||||
if (schemeEnd < 0) {
|
if (schemeEnd < 0) {
|
||||||
return ""; // couldn't parse scheme
|
return ""; // couldn't parse scheme
|
||||||
}
|
}
|
||||||
|
|
||||||
int hostEnd = u.find('/', schemeEnd + 3);
|
int hostEnd = u.find('/', schemeEnd + 3);
|
||||||
if (hostEnd < 0) { // all remainder of URL is host
|
if (hostEnd < 0) { // all remainder of URL is host
|
||||||
return u.substr(schemeEnd + 3);
|
return u.substr(schemeEnd + 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
return u.substr(schemeEnd + 3, hostEnd - (schemeEnd + 3));
|
return u.substr(schemeEnd + 3, hostEnd - (schemeEnd + 3));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
void Request::setResponseLength(unsigned int l)
|
void Request::setResponseLength(unsigned int l)
|
||||||
{
|
{
|
||||||
_responseLength = l;
|
_responseLength = l;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
unsigned int Request::responseLength() const
|
unsigned int Request::responseLength() const
|
||||||
{
|
{
|
||||||
// if the server didn't supply a content length, use the number
|
// if the server didn't supply a content length, use the number
|
||||||
// of bytes we actually received (so far)
|
// of bytes we actually received (so far)
|
||||||
if ((_responseLength == 0) && (_receivedBodyBytes > 0)) {
|
if( (_responseLength == 0) && (_receivedBodyBytes > 0) )
|
||||||
return _receivedBodyBytes;
|
return _receivedBodyBytes;
|
||||||
}
|
|
||||||
|
|
||||||
return _responseLength;
|
return _responseLength;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
void Request::setFailure(int code, const std::string& reason)
|
void Request::setFailure(int code, const std::string& reason)
|
||||||
{
|
{
|
||||||
_responseStatus = code;
|
_responseStatus = code;
|
||||||
_responseReason = reason;
|
_responseReason = reason;
|
||||||
failed();
|
setReadyState(FAILED);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Request::failed()
|
//------------------------------------------------------------------------------
|
||||||
|
void Request::setReadyState(ReadyState state)
|
||||||
{
|
{
|
||||||
// no-op in base class
|
_ready_state = state;
|
||||||
SG_LOG(SG_IO, SG_INFO, "request failed:" << url());
|
if( state == DONE )
|
||||||
|
{
|
||||||
|
// Finish C++ part of request to ensure everything is finished (for example
|
||||||
|
// files and streams are closed) before calling any callback (possibly using
|
||||||
|
// such files)
|
||||||
|
onDone();
|
||||||
|
onAlways();
|
||||||
|
|
||||||
|
if( _cb_done )
|
||||||
|
_cb_done(this);
|
||||||
|
}
|
||||||
|
else if( state == FAILED )
|
||||||
|
{
|
||||||
|
onFail();
|
||||||
|
onAlways();
|
||||||
|
|
||||||
|
if( _cb_fail )
|
||||||
|
_cb_fail(this);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return;
|
||||||
|
|
||||||
|
if( _cb_always )
|
||||||
|
_cb_always(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
Request::HTTPVersion Request::decodeVersion(const string& v)
|
//------------------------------------------------------------------------------
|
||||||
|
void Request::abort()
|
||||||
{
|
{
|
||||||
if (v == "HTTP/1.1") return HTTP_1_1;
|
abort("Request aborted.");
|
||||||
if (v == "HTTP/1.0") return HTTP_1_0;
|
|
||||||
if (strutils::starts_with(v, "HTTP/0.")) return HTTP_0_x;
|
|
||||||
return HTTP_VERSION_UNKNOWN;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
void Request::abort(const std::string& reason)
|
||||||
|
{
|
||||||
|
if( isComplete() )
|
||||||
|
return;
|
||||||
|
|
||||||
|
setFailure(-1, reason);
|
||||||
|
_willClose = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
bool Request::closeAfterComplete() const
|
bool Request::closeAfterComplete() const
|
||||||
{
|
{
|
||||||
// for non HTTP/1.1 connections, assume server closes
|
// for non HTTP/1.1 connections, assume server closes
|
||||||
return _willClose || (_responseVersion != HTTP_1_1);
|
return _willClose || (_responseVersion != HTTP_1_1);
|
||||||
}
|
}
|
||||||
|
|
||||||
int Request::requestBodyLength() const
|
//------------------------------------------------------------------------------
|
||||||
|
bool Request::isComplete() const
|
||||||
{
|
{
|
||||||
return -1;
|
return _ready_state == DONE || _ready_state == FAILED;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string Request::requestBodyType() const
|
//------------------------------------------------------------------------------
|
||||||
|
bool Request::hasBodyData() const
|
||||||
{
|
{
|
||||||
return "text/plain";
|
return !_request_media_type.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
int Request::getBodyData(char*, int maxCount) const
|
//------------------------------------------------------------------------------
|
||||||
|
std::string Request::bodyType() const
|
||||||
{
|
{
|
||||||
return 0;
|
return _request_media_type;
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
size_t Request::bodyLength() const
|
||||||
|
{
|
||||||
|
return _request_data.length();
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
size_t Request::getBodyData(char* s, size_t offset, size_t max_count) const
|
||||||
|
{
|
||||||
|
size_t bytes_available = _request_data.size() - offset;
|
||||||
|
size_t bytes_to_read = std::min(bytes_available, max_count);
|
||||||
|
|
||||||
|
memcpy(s, _request_data.data() + offset, bytes_to_read);
|
||||||
|
|
||||||
|
return bytes_to_read;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // of namespace HTTP
|
} // of namespace HTTP
|
||||||
|
|
||||||
} // of namespace simgear
|
} // of namespace simgear
|
||||||
|
|||||||
+124
-28
@@ -3,21 +3,85 @@
|
|||||||
|
|
||||||
#include <map>
|
#include <map>
|
||||||
|
|
||||||
|
#include <simgear/structure/map.hxx>
|
||||||
#include <simgear/structure/SGReferenced.hxx>
|
#include <simgear/structure/SGReferenced.hxx>
|
||||||
#include <simgear/structure/SGSharedPtr.hxx>
|
#include <simgear/structure/SGSharedPtr.hxx>
|
||||||
#include <simgear/math/sg_types.hxx>
|
#include <simgear/math/sg_types.hxx>
|
||||||
|
|
||||||
|
#include <boost/function.hpp>
|
||||||
|
|
||||||
|
class SGPropertyNode;
|
||||||
|
|
||||||
namespace simgear
|
namespace simgear
|
||||||
{
|
{
|
||||||
|
|
||||||
namespace HTTP
|
namespace HTTP
|
||||||
{
|
{
|
||||||
|
|
||||||
class Request : public SGReferenced
|
class Request:
|
||||||
|
public SGReferenced
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
typedef boost::function<void(Request*)> Callback;
|
||||||
|
|
||||||
|
enum ReadyState
|
||||||
|
{
|
||||||
|
UNSENT = 0,
|
||||||
|
OPENED,
|
||||||
|
HEADERS_RECEIVED,
|
||||||
|
LOADING,
|
||||||
|
DONE,
|
||||||
|
FAILED
|
||||||
|
};
|
||||||
|
|
||||||
virtual ~Request();
|
virtual ~Request();
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
StringMap& requestHeaders() { return _request_headers; }
|
||||||
|
const StringMap& requestHeaders() const { return _request_headers; }
|
||||||
|
std::string& requestHeader(const std::string& key)
|
||||||
|
{ return _request_headers[key]; }
|
||||||
|
const std::string requestHeader(const std::string& key) const
|
||||||
|
{ return _request_headers.get(key); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the handler to be called when the request successfully completes.
|
||||||
|
*
|
||||||
|
* @note If the request is already complete, the handler is called
|
||||||
|
* immediately.
|
||||||
|
*/
|
||||||
|
Request* done(const Callback& cb);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the handler to be called when the request completes or aborts with an
|
||||||
|
* error.
|
||||||
|
*
|
||||||
|
* @note If the request has already failed, the handler is called
|
||||||
|
* immediately.
|
||||||
|
*/
|
||||||
|
Request* fail(const Callback& cb);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the handler to be called when the request either successfully
|
||||||
|
* completes or fails.
|
||||||
|
*
|
||||||
|
* @note If the request is already complete or has already failed, the
|
||||||
|
* handler is called immediately.
|
||||||
|
*/
|
||||||
|
Request* always(const Callback& cb);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the data for the body of the request. The request is automatically
|
||||||
|
* send using the POST method.
|
||||||
|
*
|
||||||
|
* @param data Body data
|
||||||
|
* @param type Media Type (aka MIME) of the body data
|
||||||
|
*/
|
||||||
|
void setBodyData( const std::string& data,
|
||||||
|
const std::string& type = "text/plain" );
|
||||||
|
void setBodyData( const SGPropertyNode* data );
|
||||||
|
|
||||||
virtual void setUrl(const std::string& url);
|
virtual void setUrl(const std::string& url);
|
||||||
|
|
||||||
virtual std::string method() const
|
virtual std::string method() const
|
||||||
@@ -32,8 +96,8 @@ public:
|
|||||||
virtual unsigned short port() const;
|
virtual unsigned short port() const;
|
||||||
virtual std::string query() const;
|
virtual std::string query() const;
|
||||||
|
|
||||||
virtual string_list requestHeaders() const;
|
StringMap const& responseHeaders() const
|
||||||
virtual std::string header(const std::string& name) const;
|
{ return _responseHeaders; }
|
||||||
|
|
||||||
virtual int responseCode() const
|
virtual int responseCode() const
|
||||||
{ return _responseStatus; }
|
{ return _responseStatus; }
|
||||||
@@ -45,22 +109,26 @@ public:
|
|||||||
virtual unsigned int responseLength() const;
|
virtual unsigned int responseLength() const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Query the size of the request body. -1 (the default value) means no
|
* Check if request contains body data.
|
||||||
* request body
|
|
||||||
*/
|
*/
|
||||||
virtual int requestBodyLength() const;
|
virtual bool hasBodyData() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the request body content type.
|
||||||
|
*/
|
||||||
|
virtual std::string bodyType() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the size of the request body.
|
||||||
|
*/
|
||||||
|
virtual size_t bodyLength() const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve the body data bytes. Will be passed the maximum body bytes
|
* Retrieve the body data bytes. Will be passed the maximum body bytes
|
||||||
* to return in the buffer, and must return the actual number
|
* to return in the buffer, and must return the actual number
|
||||||
* of bytes written.
|
* of bytes written.
|
||||||
*/
|
*/
|
||||||
virtual int getBodyData(char* s, int count) const;
|
virtual size_t getBodyData(char* s, size_t offset, size_t max_count) const;
|
||||||
|
|
||||||
/**
|
|
||||||
* retrieve the request body content type. Default is text/plain
|
|
||||||
*/
|
|
||||||
virtual std::string requestBodyType() const;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* running total of body bytes received so far. Can be used
|
* running total of body bytes received so far. Can be used
|
||||||
@@ -80,9 +148,21 @@ public:
|
|||||||
HTTPVersion responseVersion() const
|
HTTPVersion responseVersion() const
|
||||||
{ return _responseVersion; }
|
{ return _responseVersion; }
|
||||||
|
|
||||||
static HTTPVersion decodeVersion(const std::string& v);
|
ReadyState readyState() const { return _ready_state; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Request aborting this request.
|
||||||
|
*/
|
||||||
|
void abort();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Request aborting this request and specify the reported reaseon for it.
|
||||||
|
*/
|
||||||
|
void abort(const std::string& reason);
|
||||||
|
|
||||||
bool closeAfterComplete() const;
|
bool closeAfterComplete() const;
|
||||||
|
bool isComplete() const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
Request(const std::string& url, const std::string method = "GET");
|
Request(const std::string& url, const std::string method = "GET");
|
||||||
|
|
||||||
@@ -91,33 +171,49 @@ protected:
|
|||||||
virtual void responseHeader(const std::string& key, const std::string& value);
|
virtual void responseHeader(const std::string& key, const std::string& value);
|
||||||
virtual void responseHeadersComplete();
|
virtual void responseHeadersComplete();
|
||||||
virtual void responseComplete();
|
virtual void responseComplete();
|
||||||
virtual void failed();
|
|
||||||
virtual void gotBodyData(const char* s, int n);
|
virtual void gotBodyData(const char* s, int n);
|
||||||
|
|
||||||
|
virtual void onDone();
|
||||||
|
virtual void onFail();
|
||||||
|
virtual void onAlways();
|
||||||
|
|
||||||
|
void setFailure(int code, const std::string& reason);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
friend class Client;
|
friend class Client;
|
||||||
friend class Connection;
|
friend class Connection;
|
||||||
|
friend class ContentDecoder;
|
||||||
|
|
||||||
|
Request(const Request&); // = delete;
|
||||||
|
Request& operator=(const Request&); // = delete;
|
||||||
|
|
||||||
void processBodyBytes(const char* s, int n);
|
void processBodyBytes(const char* s, int n);
|
||||||
void setFailure(int code, const std::string& reason);
|
void setReadyState(ReadyState state);
|
||||||
|
|
||||||
std::string _method;
|
std::string _method;
|
||||||
std::string _url;
|
std::string _url;
|
||||||
HTTPVersion _responseVersion;
|
StringMap _request_headers;
|
||||||
int _responseStatus;
|
std::string _request_data;
|
||||||
std::string _responseReason;
|
std::string _request_media_type;
|
||||||
unsigned int _responseLength;
|
|
||||||
unsigned int _receivedBodyBytes;
|
|
||||||
bool _willClose;
|
|
||||||
|
|
||||||
typedef std::map<std::string, std::string> HeaderDict;
|
HTTPVersion _responseVersion;
|
||||||
HeaderDict _responseHeaders;
|
int _responseStatus;
|
||||||
|
std::string _responseReason;
|
||||||
|
StringMap _responseHeaders;
|
||||||
|
unsigned int _responseLength;
|
||||||
|
unsigned int _receivedBodyBytes;
|
||||||
|
|
||||||
|
Callback _cb_done,
|
||||||
|
_cb_fail,
|
||||||
|
_cb_always;
|
||||||
|
|
||||||
|
ReadyState _ready_state;
|
||||||
|
bool _willClose;
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef SGSharedPtr<Request> Request_ptr;
|
typedef SGSharedPtr<Request> Request_ptr;
|
||||||
|
|
||||||
} // of namespace HTTP
|
} // of namespace HTTP
|
||||||
|
|
||||||
} // of namespace simgear
|
} // of namespace simgear
|
||||||
|
|
||||||
#endif // of SG_HTTP_REQUEST_HXX
|
#endif // of SG_HTTP_REQUEST_HXX
|
||||||
|
|
||||||
|
|||||||
+28
-13
@@ -28,7 +28,12 @@ typedef std::map<std::string, DAVResource*> DAVResourceMap;
|
|||||||
|
|
||||||
const char* DAV_CACHE_NAME = ".terrasync_cache";
|
const char* DAV_CACHE_NAME = ".terrasync_cache";
|
||||||
const char* CACHE_VERSION_4_TOKEN = "terrasync-cache-4";
|
const char* CACHE_VERSION_4_TOKEN = "terrasync-cache-4";
|
||||||
const unsigned int MAX_UPDATE_REPORT_DEPTH = 3;
|
|
||||||
|
// important: with the Google servers, setting this higher than '1' causes
|
||||||
|
// server internal errors (500, the connection is closed). In other words we
|
||||||
|
// can only specify update report items one level deep at most and no more.
|
||||||
|
// (the root and its direct children, not NOT grand-children)
|
||||||
|
const unsigned int MAX_UPDATE_REPORT_DEPTH = 1;
|
||||||
|
|
||||||
enum LineState
|
enum LineState
|
||||||
{
|
{
|
||||||
@@ -90,11 +95,15 @@ void SVNDirectory::parseCache()
|
|||||||
char versionName[128];
|
char versionName[128];
|
||||||
LineState lineState = LINESTATE_HREF;
|
LineState lineState = LINESTATE_HREF;
|
||||||
std::ifstream file(p.c_str());
|
std::ifstream file(p.c_str());
|
||||||
|
if (!file.is_open()) {
|
||||||
|
SG_LOG(SG_IO, SG_WARN, "unable to open cache file for reading:" << p);
|
||||||
|
return;
|
||||||
|
}
|
||||||
bool doneSelf = false;
|
bool doneSelf = false;
|
||||||
|
|
||||||
file.getline(href, 1024);
|
file.getline(href, 1024);
|
||||||
if (strcmp(CACHE_VERSION_4_TOKEN, href)) {
|
if (strcmp(CACHE_VERSION_4_TOKEN, href)) {
|
||||||
SG_LOG(SG_IO, SG_WARN, "invalid cache file:" << p.str());
|
SG_LOG(SG_IO, SG_WARN, "invalid cache file [missing header token]:" << p << " '" << href << "'");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -128,8 +137,11 @@ void SVNDirectory::parseCache()
|
|||||||
doneSelf = true;
|
doneSelf = true;
|
||||||
} else {
|
} else {
|
||||||
DAVResource* child = addChildDirectory(hrefPtr)->collection();
|
DAVResource* child = addChildDirectory(hrefPtr)->collection();
|
||||||
child->setVersionName(versionName);
|
string s = strutils::strip(versionName);
|
||||||
}
|
if (!s.empty()) {
|
||||||
|
child->setVersionName(versionName);
|
||||||
|
}
|
||||||
|
} // of done self test
|
||||||
} // of line-state switching
|
} // of line-state switching
|
||||||
} // of file get-line loop
|
} // of file get-line loop
|
||||||
}
|
}
|
||||||
@@ -142,7 +154,7 @@ void SVNDirectory::writeCache()
|
|||||||
d.create(0755);
|
d.create(0755);
|
||||||
}
|
}
|
||||||
|
|
||||||
p.append(DAV_CACHE_NAME);
|
p.append(string(DAV_CACHE_NAME) + ".new");
|
||||||
|
|
||||||
std::ofstream file(p.c_str(), std::ios::trunc);
|
std::ofstream file(p.c_str(), std::ios::trunc);
|
||||||
// first, cache file version header
|
// first, cache file version header
|
||||||
@@ -159,6 +171,13 @@ void SVNDirectory::writeCache()
|
|||||||
file << child->name() << '\n' << child->versionName() << "\n";
|
file << child->name() << '\n' << child->versionName() << "\n";
|
||||||
}
|
}
|
||||||
} // of child iteration
|
} // of child iteration
|
||||||
|
|
||||||
|
file.close();
|
||||||
|
|
||||||
|
// approximately atomic delete + rename operation
|
||||||
|
SGPath cacheName(localPath);
|
||||||
|
cacheName.append(DAV_CACHE_NAME);
|
||||||
|
p.rename(cacheName);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SVNDirectory::setBaseUrl(const string& url)
|
void SVNDirectory::setBaseUrl(const string& url)
|
||||||
@@ -210,6 +229,7 @@ SVNDirectory::addChildDirectory(const std::string& dirName)
|
|||||||
|
|
||||||
DAVCollection* childCol = dav->createChildCollection(dirName);
|
DAVCollection* childCol = dav->createChildCollection(dirName);
|
||||||
SVNDirectory* child = new SVNDirectory(this, childCol);
|
SVNDirectory* child = new SVNDirectory(this, childCol);
|
||||||
|
childCol->setVersionName(child->cachedRevision());
|
||||||
_children.push_back(child);
|
_children.push_back(child);
|
||||||
writeCache();
|
writeCache();
|
||||||
return child;
|
return child;
|
||||||
@@ -224,8 +244,6 @@ void SVNDirectory::deleteChildByName(const std::string& nm)
|
|||||||
}
|
}
|
||||||
|
|
||||||
SGPath path = fsDir().file(nm);
|
SGPath path = fsDir().file(nm);
|
||||||
dav->removeChild(child);
|
|
||||||
delete child;
|
|
||||||
|
|
||||||
if (child->isCollection()) {
|
if (child->isCollection()) {
|
||||||
Dir d(path);
|
Dir d(path);
|
||||||
@@ -242,12 +260,10 @@ void SVNDirectory::deleteChildByName(const std::string& nm)
|
|||||||
path.remove();
|
path.remove();
|
||||||
}
|
}
|
||||||
|
|
||||||
writeCache();
|
dav->removeChild(child);
|
||||||
}
|
delete child;
|
||||||
|
|
||||||
void SVNDirectory::requestFailed(HTTP::Request *req)
|
writeCache();
|
||||||
{
|
|
||||||
SG_LOG(SG_IO, SG_WARN, "Request failed for:" << req->url());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SVNDirectory::isDoingSync() const
|
bool SVNDirectory::isDoingSync() const
|
||||||
@@ -303,7 +319,6 @@ void SVNDirectory::mergeUpdateReportDetails(unsigned int depth,
|
|||||||
|
|
||||||
Dir d(localPath);
|
Dir d(localPath);
|
||||||
if (depth >= MAX_UPDATE_REPORT_DEPTH) {
|
if (depth >= MAX_UPDATE_REPORT_DEPTH) {
|
||||||
std::cerr << localPath << "exceeded MAX_UPDATE_REPORT_DEPTH, cleaning" << std::endl;
|
|
||||||
d.removeChildren();
|
d.removeChildren();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -52,10 +52,6 @@ public:
|
|||||||
// init from a collection
|
// init from a collection
|
||||||
SVNDirectory(SVNDirectory* pr, DAVCollection* col);
|
SVNDirectory(SVNDirectory* pr, DAVCollection* col);
|
||||||
|
|
||||||
// void update();
|
|
||||||
// void gotResource(HTTP::Request* get, const std::string& etag);
|
|
||||||
void requestFailed(HTTP::Request* req);
|
|
||||||
|
|
||||||
void beginUpdateReport();
|
void beginUpdateReport();
|
||||||
void updateReportComplete();
|
void updateReportComplete();
|
||||||
|
|
||||||
|
|||||||
@@ -16,6 +16,10 @@
|
|||||||
// along with this program; if not, write to the Free Software
|
// along with this program; if not, write to the Free Software
|
||||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
# include <simgear_config.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include "SVNReportParser.hxx"
|
#include "SVNReportParser.hxx"
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
@@ -30,11 +34,16 @@
|
|||||||
#include "simgear/misc/sg_path.hxx"
|
#include "simgear/misc/sg_path.hxx"
|
||||||
#include "simgear/misc/sg_dir.hxx"
|
#include "simgear/misc/sg_dir.hxx"
|
||||||
#include "simgear/debug/logstream.hxx"
|
#include "simgear/debug/logstream.hxx"
|
||||||
#include "simgear/xml/xmlparse.h"
|
|
||||||
#include "simgear/xml/easyxml.hxx"
|
#include "simgear/xml/easyxml.hxx"
|
||||||
#include "simgear/misc/strutils.hxx"
|
#include "simgear/misc/strutils.hxx"
|
||||||
#include "simgear/package/md5.h"
|
#include "simgear/package/md5.h"
|
||||||
|
|
||||||
|
#ifdef SYSTEM_EXPAT
|
||||||
|
# include <expat.h>
|
||||||
|
#else
|
||||||
|
# include "sg_expat.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
#include "SVNDirectory.hxx"
|
#include "SVNDirectory.hxx"
|
||||||
#include "SVNRepository.hxx"
|
#include "SVNRepository.hxx"
|
||||||
#include "DAVMultiStatus.hxx"
|
#include "DAVMultiStatus.hxx"
|
||||||
@@ -97,12 +106,13 @@ namespace {
|
|||||||
const char* SVN_ADD_FILE_TAG = SVN_NS "add-file";
|
const char* SVN_ADD_FILE_TAG = SVN_NS "add-file";
|
||||||
const char* SVN_TXDELTA_TAG = SVN_NS "txdelta";
|
const char* SVN_TXDELTA_TAG = SVN_NS "txdelta";
|
||||||
const char* SVN_SET_PROP_TAG = SVN_NS "set-prop";
|
const char* SVN_SET_PROP_TAG = SVN_NS "set-prop";
|
||||||
|
const char* SVN_PROP_TAG = SVN_NS "prop";
|
||||||
const char* SVN_DELETE_ENTRY_TAG = SVN_NS "delete-entry";
|
const char* SVN_DELETE_ENTRY_TAG = SVN_NS "delete-entry";
|
||||||
|
|
||||||
const char* SVN_DAV_MD5_CHECKSUM = SUBVERSION_DAV_NS ":md5-checksum";
|
const char* SVN_DAV_MD5_CHECKSUM = SUBVERSION_DAV_NS ":md5-checksum";
|
||||||
|
|
||||||
const char* DAV_HREF_TAG = DAV_NS "href";
|
const char* DAV_HREF_TAG = DAV_NS "href";
|
||||||
const char* DAV_CHECKED_IN_TAG = SVN_NS "checked-in";
|
const char* DAV_CHECKED_IN_TAG = DAV_NS "checked-in";
|
||||||
|
|
||||||
|
|
||||||
const int svn_txdelta_source = 0;
|
const int svn_txdelta_source = 0;
|
||||||
@@ -156,7 +166,7 @@ namespace {
|
|||||||
_ptr = p;
|
_ptr = p;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool apply(std::vector<char>& output, std::istream& source)
|
bool apply(std::vector<unsigned char>& output, std::istream& source)
|
||||||
{
|
{
|
||||||
unsigned char* pEnd = _ptr + instructionLength;
|
unsigned char* pEnd = _ptr + instructionLength;
|
||||||
unsigned char* newData = pEnd;
|
unsigned char* newData = pEnd;
|
||||||
@@ -164,7 +174,7 @@ namespace {
|
|||||||
while (_ptr < pEnd) {
|
while (_ptr < pEnd) {
|
||||||
int op = ((*_ptr >> 6) & 0x3);
|
int op = ((*_ptr >> 6) & 0x3);
|
||||||
if (op >= 3) {
|
if (op >= 3) {
|
||||||
SG_LOG(SG_IO, SG_INFO, "SVNDeltaWindow: bad opcode:" << op);
|
SG_LOG(SG_IO, SG_INFO, "SVNDeltaWindow: bad opcode:" << op);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -186,12 +196,14 @@ namespace {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (op == svn_txdelta_target) {
|
if (op == svn_txdelta_target) {
|
||||||
while (length > 0) {
|
// this is inefficent, but ranges can overlap.
|
||||||
output.push_back(output[offset++]);
|
while (length > 0) {
|
||||||
--length;
|
output.push_back(output[offset++]);
|
||||||
}
|
--length;
|
||||||
|
}
|
||||||
} else if (op == svn_txdelta_new) {
|
} else if (op == svn_txdelta_new) {
|
||||||
output.insert(output.end(), newData, newData + length);
|
output.insert(output.end(), newData, newData + length);
|
||||||
|
newData += length;
|
||||||
} else if (op == svn_txdelta_source) {
|
} else if (op == svn_txdelta_source) {
|
||||||
source.seekg(offset);
|
source.seekg(offset);
|
||||||
char* sourceBuf = (char*) malloc(length);
|
char* sourceBuf = (char*) malloc(length);
|
||||||
@@ -199,6 +211,9 @@ namespace {
|
|||||||
source.read(sourceBuf, length);
|
source.read(sourceBuf, length);
|
||||||
output.insert(output.end(), sourceBuf, sourceBuf + length);
|
output.insert(output.end(), sourceBuf, sourceBuf + length);
|
||||||
free(sourceBuf);
|
free(sourceBuf);
|
||||||
|
} else {
|
||||||
|
SG_LOG(SG_IO, SG_WARN, "bad opcode logic");
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
} // of instruction loop
|
} // of instruction loop
|
||||||
|
|
||||||
@@ -261,9 +276,9 @@ public:
|
|||||||
SGPath filePath(Dir(currentPath).file(fileName));
|
SGPath filePath(Dir(currentPath).file(fileName));
|
||||||
currentPath = filePath;
|
currentPath = filePath;
|
||||||
|
|
||||||
DAVResource* res = currentDir->collection()->childWithName(fileName);
|
if (!filePath.exists()) {
|
||||||
if (!res || !filePath.exists()) {
|
fail(SVNRepository::SVN_ERROR_FILE_NOT_FOUND);
|
||||||
// set error condition
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
inFile = true;
|
inFile = true;
|
||||||
@@ -293,10 +308,12 @@ public:
|
|||||||
} else if (!strcmp(name, SVN_DELETE_ENTRY_TAG)) {
|
} else if (!strcmp(name, SVN_DELETE_ENTRY_TAG)) {
|
||||||
string entryName(attrs.getValue("name"));
|
string entryName(attrs.getValue("name"));
|
||||||
deleteEntry(entryName);
|
deleteEntry(entryName);
|
||||||
} else if (!strcmp(name, DAV_CHECKED_IN_TAG) || !strcmp(name, DAV_HREF_TAG)) {
|
} else if (!strcmp(name, DAV_CHECKED_IN_TAG) ||
|
||||||
|
!strcmp(name, DAV_HREF_TAG) ||
|
||||||
|
!strcmp(name, SVN_PROP_TAG)) {
|
||||||
// don't warn on these ones
|
// don't warn on these ones
|
||||||
} else {
|
} else {
|
||||||
//std::cerr << "unhandled element:" << name << std::endl;
|
//SG_LOG(SG_IO, SG_WARN, "SVNReportParser: unhandled tag:" << name);
|
||||||
}
|
}
|
||||||
} // of startElement
|
} // of startElement
|
||||||
|
|
||||||
@@ -322,11 +339,12 @@ public:
|
|||||||
|
|
||||||
bool decodeTextDelta(const SGPath& outputPath)
|
bool decodeTextDelta(const SGPath& outputPath)
|
||||||
{
|
{
|
||||||
string decoded = strutils::decodeBase64(txDeltaData);
|
std::vector<unsigned char> output, decoded;
|
||||||
|
strutils::decodeBase64(txDeltaData, decoded);
|
||||||
size_t bytesToDecode = decoded.size();
|
size_t bytesToDecode = decoded.size();
|
||||||
std::vector<char> output;
|
|
||||||
unsigned char* p = (unsigned char*) decoded.data();
|
unsigned char* p = decoded.data();
|
||||||
if (memcmp(decoded.data(), "SVN\0", DELTA_HEADER_SIZE) != 0) {
|
if (memcmp(p, "SVN\0", DELTA_HEADER_SIZE) != 0) {
|
||||||
return false; // bad header
|
return false; // bad header
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -336,6 +354,11 @@ public:
|
|||||||
source.open(outputPath.c_str(), std::ios::in | std::ios::binary);
|
source.open(outputPath.c_str(), std::ios::in | std::ios::binary);
|
||||||
|
|
||||||
while (bytesToDecode > 0) {
|
while (bytesToDecode > 0) {
|
||||||
|
if (!SVNDeltaWindow::isWindowComplete(p, bytesToDecode)) {
|
||||||
|
SG_LOG(SG_IO, SG_WARN, "SVN txdelta broken window");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
SVNDeltaWindow window(p);
|
SVNDeltaWindow window(p);
|
||||||
assert(bytesToDecode >= window.size());
|
assert(bytesToDecode >= window.size());
|
||||||
window.apply(output, source);
|
window.apply(output, source);
|
||||||
@@ -344,18 +367,20 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
source.close();
|
source.close();
|
||||||
|
|
||||||
std::ofstream f;
|
std::ofstream f;
|
||||||
f.open(outputPath.c_str(),
|
f.open(outputPath.c_str(),
|
||||||
std::ios::out | std::ios::trunc | std::ios::binary);
|
std::ios::out | std::ios::trunc | std::ios::binary);
|
||||||
f.write(output.data(), output.size());
|
f.write((char*) output.data(), output.size());
|
||||||
|
|
||||||
// compute MD5 while we have the file in memory
|
// compute MD5 while we have the file in memory
|
||||||
MD5_CTX md5;
|
memset(&md5Context, 0, sizeof(SG_MD5_CTX));
|
||||||
memset(&md5, 0, sizeof(MD5_CTX));
|
SG_MD5Init(&md5Context);
|
||||||
MD5Init(&md5);
|
SG_MD5Update(&md5Context, (unsigned char*) output.data(), output.size());
|
||||||
MD5Update(&md5, (unsigned char*) output.data(), output.size());
|
unsigned char digest[MD5_DIGEST_LENGTH];
|
||||||
MD5Final(&md5);
|
SG_MD5Final(digest, &md5Context);
|
||||||
decodedFileMd5 = strutils::encodeHex(md5.digest, 16);
|
decodedFileMd5 = strutils::encodeHex(digest, MD5_DIGEST_LENGTH);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -367,16 +392,15 @@ public:
|
|||||||
|
|
||||||
assert(tagStack.back() == name);
|
assert(tagStack.back() == name);
|
||||||
tagStack.pop_back();
|
tagStack.pop_back();
|
||||||
|
|
||||||
if (!strcmp(name, SVN_TXDELTA_TAG)) {
|
if (!strcmp(name, SVN_TXDELTA_TAG)) {
|
||||||
if (!decodeTextDelta(currentPath)) {
|
if (!decodeTextDelta(currentPath)) {
|
||||||
fail(SVNRepository::SVN_ERROR_TXDELTA);
|
fail(SVNRepository::SVN_ERROR_TXDELTA);
|
||||||
}
|
}
|
||||||
} else if (!strcmp(name, SVN_ADD_FILE_TAG)) {
|
} else if (!strcmp(name, SVN_ADD_FILE_TAG)) {
|
||||||
finishFile(currentDir->addChildFile(currentPath.file()));
|
finishFile(currentPath);
|
||||||
} else if (!strcmp(name, SVN_OPEN_FILE_TAG)) {
|
} else if (!strcmp(name, SVN_OPEN_FILE_TAG)) {
|
||||||
DAVResource* res = currentDir->collection()->childWithName(currentPath.file());
|
finishFile(currentPath);
|
||||||
assert(res);
|
|
||||||
finishFile(res);
|
|
||||||
} else if (!strcmp(name, SVN_ADD_DIRECTORY_TAG)) {
|
} else if (!strcmp(name, SVN_ADD_DIRECTORY_TAG)) {
|
||||||
// pop directory
|
// pop directory
|
||||||
currentPath = currentPath.dir();
|
currentPath = currentPath.dir();
|
||||||
@@ -399,23 +423,21 @@ public:
|
|||||||
fail(SVNRepository::SVN_ERROR_CHECKSUM);
|
fail(SVNRepository::SVN_ERROR_CHECKSUM);
|
||||||
}
|
}
|
||||||
} else if (!strcmp(name, SVN_OPEN_DIRECTORY_TAG)) {
|
} else if (!strcmp(name, SVN_OPEN_DIRECTORY_TAG)) {
|
||||||
|
currentDir->updateReportComplete();
|
||||||
if (currentDir->parent()) {
|
if (currentDir->parent()) {
|
||||||
// pop the collection stack
|
// pop the collection stack
|
||||||
currentDir = currentDir->parent();
|
currentDir = currentDir->parent();
|
||||||
}
|
}
|
||||||
|
|
||||||
currentDir->updateReportComplete();
|
|
||||||
currentPath = currentDir->fsPath();
|
currentPath = currentDir->fsPath();
|
||||||
} else {
|
} else {
|
||||||
// std::cout << "element:" << name;
|
// std::cout << "element:" << name;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void finishFile(DAVResource* res)
|
void finishFile(const SGPath& path)
|
||||||
{
|
{
|
||||||
res->setVersionName(currentVersionName);
|
currentPath = path.dir();
|
||||||
res->setMD5(md5Sum);
|
|
||||||
currentPath = currentPath.dir();
|
|
||||||
inFile = false;
|
inFile = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -426,11 +448,11 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (tagStack.back() == SVN_SET_PROP_TAG) {
|
if (tagStack.back() == SVN_SET_PROP_TAG) {
|
||||||
setPropValue += string(s, length);
|
setPropValue.append(s, length);
|
||||||
} else if (tagStack.back() == SVN_TXDELTA_TAG) {
|
} else if (tagStack.back() == SVN_TXDELTA_TAG) {
|
||||||
txDeltaData += string(s, length);
|
txDeltaData.append(s, length);
|
||||||
} else if (tagStack.back() == SVN_DAV_MD5_CHECKSUM) {
|
} else if (tagStack.back() == SVN_DAV_MD5_CHECKSUM) {
|
||||||
md5Sum += string(s, length);
|
md5Sum.append(s, length);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -467,6 +489,7 @@ public:
|
|||||||
bool inFile;
|
bool inFile;
|
||||||
|
|
||||||
unsigned int revision;
|
unsigned int revision;
|
||||||
|
SG_MD5_CTX md5Context;
|
||||||
string md5Sum, decodedFileMd5;
|
string md5Sum, decodedFileMd5;
|
||||||
std::string setPropName, setPropValue;
|
std::string setPropName, setPropValue;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -73,7 +73,8 @@ public:
|
|||||||
|
|
||||||
void updateFailed(HTTP::Request* req, SVNRepository::ResultCode err)
|
void updateFailed(HTTP::Request* req, SVNRepository::ResultCode err)
|
||||||
{
|
{
|
||||||
SG_LOG(SG_IO, SG_WARN, "SVN: failed to update from:" << req->url());
|
SG_LOG(SG_IO, SG_WARN, "SVN: failed to update from:" << req->url()
|
||||||
|
<< "\n(repository:" << p->baseUrl() << ")");
|
||||||
isUpdating = false;
|
isUpdating = false;
|
||||||
status = err;
|
status = err;
|
||||||
}
|
}
|
||||||
@@ -119,40 +120,9 @@ namespace { // anonmouse
|
|||||||
Request(repo->baseUrl, "PROPFIND"),
|
Request(repo->baseUrl, "PROPFIND"),
|
||||||
_repo(repo)
|
_repo(repo)
|
||||||
{
|
{
|
||||||
}
|
requestHeader("Depth") = "0";
|
||||||
|
setBodyData( PROPFIND_REQUEST_BODY,
|
||||||
virtual string_list requestHeaders() const
|
"application/xml; charset=\"utf-8\"" );
|
||||||
{
|
|
||||||
string_list r;
|
|
||||||
r.push_back("Depth");
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual string header(const string& name) const
|
|
||||||
{
|
|
||||||
if (name == "Depth") {
|
|
||||||
return "0";
|
|
||||||
}
|
|
||||||
|
|
||||||
return string();
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual string requestBodyType() const
|
|
||||||
{
|
|
||||||
return "application/xml; charset=\"utf-8\"";
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual int requestBodyLength() const
|
|
||||||
{
|
|
||||||
return strlen(PROPFIND_REQUEST_BODY);
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual int getBodyData(char* buf, int count) const
|
|
||||||
{
|
|
||||||
int bodyLen = strlen(PROPFIND_REQUEST_BODY);
|
|
||||||
assert(count >= bodyLen);
|
|
||||||
memcpy(buf, PROPFIND_REQUEST_BODY, bodyLen);
|
|
||||||
return bodyLen;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
@@ -166,14 +136,19 @@ namespace { // anonmouse
|
|||||||
SG_LOG(SG_IO, SG_WARN, "request for:" << url() <<
|
SG_LOG(SG_IO, SG_WARN, "request for:" << url() <<
|
||||||
" return code " << responseCode());
|
" return code " << responseCode());
|
||||||
_repo->propFindFailed(this, SVNRepository::SVN_ERROR_SOCKET);
|
_repo->propFindFailed(this, SVNRepository::SVN_ERROR_SOCKET);
|
||||||
|
_repo = NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void responseComplete()
|
virtual void onDone()
|
||||||
{
|
{
|
||||||
if (responseCode() == 207) {
|
if (responseCode() == 207) {
|
||||||
_davStatus.finishParse();
|
_davStatus.finishParse();
|
||||||
_repo->propFindComplete(this, (DAVCollection*) _davStatus.resource());
|
if (_davStatus.isValid()) {
|
||||||
|
_repo->propFindComplete(this, (DAVCollection*) _davStatus.resource());
|
||||||
|
} else {
|
||||||
|
_repo->propFindFailed(this, SVNRepository::SVN_ERROR_SOCKET);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -184,69 +159,58 @@ namespace { // anonmouse
|
|||||||
}
|
}
|
||||||
_davStatus.parseXML(s, n);
|
_davStatus.parseXML(s, n);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
virtual void onFail()
|
||||||
|
{
|
||||||
|
HTTP::Request::onFail();
|
||||||
|
if (_repo) {
|
||||||
|
_repo->propFindFailed(this, SVNRepository::SVN_ERROR_SOCKET);
|
||||||
|
_repo = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
SVNRepoPrivate* _repo;
|
SVNRepoPrivate* _repo;
|
||||||
DAVMultiStatus _davStatus;
|
DAVMultiStatus _davStatus;
|
||||||
};
|
};
|
||||||
|
|
||||||
class UpdateReportRequest : public HTTP::Request
|
class UpdateReportRequest:
|
||||||
|
public HTTP::Request
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
UpdateReportRequest(SVNRepoPrivate* repo,
|
UpdateReportRequest(SVNRepoPrivate* repo,
|
||||||
const std::string& aVersionName,
|
const std::string& aVersionName,
|
||||||
bool startEmpty) :
|
bool startEmpty) :
|
||||||
HTTP::Request("", "REPORT"),
|
HTTP::Request("", "REPORT"),
|
||||||
_requestSent(0),
|
|
||||||
_parser(repo->p),
|
_parser(repo->p),
|
||||||
_repo(repo),
|
_repo(repo),
|
||||||
_failed(false)
|
_failed(false)
|
||||||
{
|
{
|
||||||
setUrl(repo->vccUrl);
|
setUrl(repo->vccUrl);
|
||||||
|
std::string request =
|
||||||
_request =
|
|
||||||
"<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n"
|
"<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n"
|
||||||
"<S:update-report send-all=\"true\" xmlns:S=\"svn:\">\n"
|
"<S:update-report send-all=\"true\" xmlns:S=\"svn:\">\n"
|
||||||
"<S:src-path>" + repo->baseUrl + "</S:src-path>\n"
|
"<S:src-path>" + repo->baseUrl + "</S:src-path>\n"
|
||||||
"<S:depth>unknown</S:depth>\n";
|
"<S:depth>unknown</S:depth>\n"
|
||||||
|
"<S:entry rev=\"" + aVersionName + "\" depth=\"infinity\" start-empty=\"true\"/>\n";
|
||||||
|
|
||||||
_request += "<S:entry rev=\"" + aVersionName + "\" depth=\"infinity\" start-empty=\"true\"/>\n";
|
if( !startEmpty )
|
||||||
|
{
|
||||||
if (!startEmpty) {
|
string_list entries;
|
||||||
string_list entries;
|
_repo->rootCollection->mergeUpdateReportDetails(0, entries);
|
||||||
_repo->rootCollection->mergeUpdateReportDetails(0, entries);
|
BOOST_FOREACH(string e, entries)
|
||||||
BOOST_FOREACH(string e, entries) {
|
{
|
||||||
_request += e + "\n";
|
request += e + "\n";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_request += "</S:update-report>";
|
request += "</S:update-report>";
|
||||||
}
|
|
||||||
|
|
||||||
virtual string requestBodyType() const
|
setBodyData(request, "application/xml; charset=\"utf-8\"");
|
||||||
{
|
|
||||||
return "application/xml; charset=\"utf-8\"";
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual int requestBodyLength() const
|
|
||||||
{
|
|
||||||
return _request.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual int getBodyData(char* buf, int count) const
|
|
||||||
{
|
|
||||||
int len = std::min(count, requestBodyLength() - _requestSent);
|
|
||||||
memcpy(buf, _request.c_str() + _requestSent, len);
|
|
||||||
_requestSent += len;
|
|
||||||
return len;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void responseHeadersComplete()
|
virtual void onDone()
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void responseComplete()
|
|
||||||
{
|
{
|
||||||
if (_failed) {
|
if (_failed) {
|
||||||
return;
|
return;
|
||||||
@@ -265,8 +229,8 @@ protected:
|
|||||||
_failed = true;
|
_failed = true;
|
||||||
} else {
|
} else {
|
||||||
SG_LOG(SG_IO, SG_WARN, "SVN: request for:" << url() <<
|
SG_LOG(SG_IO, SG_WARN, "SVN: request for:" << url() <<
|
||||||
" return code " << responseCode());
|
" got HTTP status " << responseCode());
|
||||||
_repo->updateFailed(this, SVNRepository::SVN_ERROR_SOCKET);
|
_repo->updateFailed(this, SVNRepository::SVN_ERROR_HTTP);
|
||||||
_failed = true;
|
_failed = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -281,20 +245,24 @@ protected:
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
//cout << "body data:" << string(s, n) << endl;
|
|
||||||
SVNRepository::ResultCode err = _parser.parseXML(s, n);
|
SVNRepository::ResultCode err = _parser.parseXML(s, n);
|
||||||
if (err) {
|
if (err) {
|
||||||
_failed = true;
|
_failed = true;
|
||||||
SG_LOG(SG_IO, SG_WARN, "SVN: request for:" << url() <<
|
SG_LOG(SG_IO, SG_WARN, this << ": SVN: request for:" << url() << " failed:" << err);
|
||||||
" XML parse failed");
|
|
||||||
_repo->updateFailed(this, err);
|
_repo->updateFailed(this, err);
|
||||||
|
_repo = NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
virtual void onFail()
|
||||||
|
{
|
||||||
|
HTTP::Request::onFail();
|
||||||
|
if (_repo) {
|
||||||
|
_repo->updateFailed(this, SVNRepository::SVN_ERROR_SOCKET);
|
||||||
|
_repo = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
private:
|
private:
|
||||||
string _request;
|
|
||||||
mutable int _requestSent;
|
|
||||||
SVNReportParser _parser;
|
SVNReportParser _parser;
|
||||||
SVNRepoPrivate* _repo;
|
SVNRepoPrivate* _repo;
|
||||||
bool _failed;
|
bool _failed;
|
||||||
|
|||||||
@@ -61,7 +61,9 @@ public:
|
|||||||
SVN_ERROR_XML,
|
SVN_ERROR_XML,
|
||||||
SVN_ERROR_TXDELTA,
|
SVN_ERROR_TXDELTA,
|
||||||
SVN_ERROR_IO,
|
SVN_ERROR_IO,
|
||||||
SVN_ERROR_CHECKSUM
|
SVN_ERROR_CHECKSUM,
|
||||||
|
SVN_ERROR_FILE_NOT_FOUND,
|
||||||
|
SVN_ERROR_HTTP
|
||||||
};
|
};
|
||||||
|
|
||||||
ResultCode failure() const;
|
ResultCode failure() const;
|
||||||
|
|||||||
@@ -32,10 +32,12 @@ int main(int argc, char* argv[])
|
|||||||
httpClient = &cl;
|
httpClient = &cl;
|
||||||
|
|
||||||
|
|
||||||
SGPath p("/Users/jmt/Desktop/scenemodels");
|
SGPath p("/Users/jmt/Desktop/traffic");
|
||||||
SVNRepository airports(p, &cl);
|
SVNRepository airports(p, &cl);
|
||||||
// airports.setBaseUrl("http://svn.goneabitbursar.com/testproject1");
|
// airports.setBaseUrl("http://svn.goneabitbursar.com/testproject1");
|
||||||
airports.setBaseUrl("http://terrascenery.googlecode.com/svn/trunk/data/Scenery/Models");
|
// airports.setBaseUrl("http://terrascenery.googlecode.com/svn/trunk/data/Scenery/Models");
|
||||||
|
|
||||||
|
airports.setBaseUrl("http://fgfs.goneabitbursar.com/fgfsai/trunk/AI/Traffic");
|
||||||
|
|
||||||
// airports.setBaseUrl("http://terrascenery.googlecode.com/svn/trunk/data/Scenery/Airports");
|
// airports.setBaseUrl("http://terrascenery.googlecode.com/svn/trunk/data/Scenery/Airports");
|
||||||
airports.update();
|
airports.update();
|
||||||
|
|||||||
+2
-27
@@ -48,35 +48,11 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
string key = h.substr(0, colonPos);
|
string key = h.substr(0, colonPos);
|
||||||
_headers[key] = h.substr(colonPos + 1);
|
requestHeader(key) = h.substr(colonPos + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual string_list requestHeaders() const
|
|
||||||
{
|
|
||||||
string_list r;
|
|
||||||
std::map<string, string>::const_iterator it;
|
|
||||||
for (it = _headers.begin(); it != _headers.end(); ++it) {
|
|
||||||
r.push_back(it->first);
|
|
||||||
}
|
|
||||||
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual string header(const string& name) const
|
|
||||||
{
|
|
||||||
std::map<string, string>::const_iterator it = _headers.find(name);
|
|
||||||
if (it == _headers.end()) {
|
|
||||||
return string();
|
|
||||||
}
|
|
||||||
|
|
||||||
return it->second;
|
|
||||||
}
|
|
||||||
protected:
|
protected:
|
||||||
virtual void responseHeadersComplete()
|
virtual void onDone()
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void responseComplete()
|
|
||||||
{
|
{
|
||||||
_complete = true;
|
_complete = true;
|
||||||
}
|
}
|
||||||
@@ -88,7 +64,6 @@ protected:
|
|||||||
private:
|
private:
|
||||||
bool _complete;
|
bool _complete;
|
||||||
SGFile* _file;
|
SGFile* _file;
|
||||||
std::map<string, string> _headers;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
int main(int argc, char* argv[])
|
int main(int argc, char* argv[])
|
||||||
|
|||||||
@@ -454,6 +454,23 @@ bool Socket::open ( bool stream )
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef SO_NOSIGPIPE
|
||||||
|
// Do not generate SIGPIPE signal (which immediately terminates the program),
|
||||||
|
// instead ::send() will return -1 and errno will be set to EPIPE.
|
||||||
|
// SO_NOSIGPIPE should be available on Mac/BSD systems, but is not available
|
||||||
|
// within Posix/Linux.
|
||||||
|
// This only works for calls to ::send() but not for ::write():
|
||||||
|
// http://freebsd.1045724.n5.nabble.com/is-setsockopt-SO-NOSIGPIPE-work-tp4011054p4011055.html
|
||||||
|
int set = 1;
|
||||||
|
setsockopt(handle, SOL_SOCKET, SO_NOSIGPIPE, (void*)&set, sizeof(set));
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef MSG_NOSIGNAL
|
||||||
|
# define MSG_NOSIGNAL 0
|
||||||
|
#endif
|
||||||
|
// TODO supress SIGPIPE if neither SO_NOSIGPIPE nor MSG_NOSIGNAL is available
|
||||||
|
// http://krokisplace.blogspot.co.at/2010/02/suppressing-sigpipe-in-library.html
|
||||||
|
|
||||||
return (handle != -1);
|
return (handle != -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -592,7 +609,7 @@ int Socket::connect ( IPAddress* addr )
|
|||||||
int Socket::send (const void * buffer, int size, int flags)
|
int Socket::send (const void * buffer, int size, int flags)
|
||||||
{
|
{
|
||||||
assert ( handle != -1 ) ;
|
assert ( handle != -1 ) ;
|
||||||
return ::send (handle, (const char*)buffer, size, flags);
|
return ::send (handle, (const char*)buffer, size, flags | MSG_NOSIGNAL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -600,8 +617,12 @@ int Socket::sendto ( const void * buffer, int size,
|
|||||||
int flags, const IPAddress* to )
|
int flags, const IPAddress* to )
|
||||||
{
|
{
|
||||||
assert ( handle != -1 ) ;
|
assert ( handle != -1 ) ;
|
||||||
return ::sendto(handle,(const char*)buffer,size,flags,
|
return ::sendto( handle,
|
||||||
(const sockaddr*) to->getAddr(), to->getAddrLen());
|
(const char*)buffer,
|
||||||
|
size,
|
||||||
|
flags | MSG_NOSIGNAL,
|
||||||
|
(const sockaddr*)to->getAddr(),
|
||||||
|
to->getAddrLen() );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+22
-21
@@ -43,6 +43,7 @@
|
|||||||
#include <simgear/bucket/newbucket.hxx>
|
#include <simgear/bucket/newbucket.hxx>
|
||||||
#include <simgear/misc/sg_path.hxx>
|
#include <simgear/misc/sg_path.hxx>
|
||||||
#include <simgear/math/SGGeometry.hxx>
|
#include <simgear/math/SGGeometry.hxx>
|
||||||
|
#include <simgear/structure/exception.hxx>
|
||||||
|
|
||||||
#include "lowlevel.hxx"
|
#include "lowlevel.hxx"
|
||||||
#include "sg_binobj.hxx"
|
#include "sg_binobj.hxx"
|
||||||
@@ -319,15 +320,18 @@ void SGBinObject::read_object( gzFile fp,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ( sgReadError() ) {
|
if ( sgReadError() ) {
|
||||||
cout << "We detected an error reading object properties" << endl;
|
throw sg_exception("Error reading object properties");
|
||||||
return;
|
}
|
||||||
|
|
||||||
|
size_t indexCount = std::bitset<32>(idx_mask).count();
|
||||||
|
if (indexCount == 0) {
|
||||||
|
throw sg_exception("object index mask has no bits set");
|
||||||
}
|
}
|
||||||
|
|
||||||
for ( j = 0; j < nelements; ++j ) {
|
for ( j = 0; j < nelements; ++j ) {
|
||||||
sgReadUInt( fp, &nbytes );
|
sgReadUInt( fp, &nbytes );
|
||||||
if ( sgReadError() ) {
|
if ( sgReadError() ) {
|
||||||
cout << "We detected an error reading element size for :" << j << endl;
|
throw sg_exception("Error reading element size");
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
buf.resize( nbytes );
|
buf.resize( nbytes );
|
||||||
@@ -335,8 +339,7 @@ void SGBinObject::read_object( gzFile fp,
|
|||||||
sgReadBytes( fp, nbytes, ptr );
|
sgReadBytes( fp, nbytes, ptr );
|
||||||
|
|
||||||
if ( sgReadError() ) {
|
if ( sgReadError() ) {
|
||||||
cout << "We detected an error reading object element:" << j << "bytes="<< nbytes << endl;
|
throw sg_exception("Error reading element bytes");
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int_list vs;
|
int_list vs;
|
||||||
@@ -405,7 +408,7 @@ bool SGBinObject::read_bin( const string& file ) {
|
|||||||
SG_LOG( SG_EVENT, SG_ALERT,
|
SG_LOG( SG_EVENT, SG_ALERT,
|
||||||
"ERROR: opening " << file << " or " << filegz << " for reading!");
|
"ERROR: opening " << file << " or " << filegz << " for reading!");
|
||||||
|
|
||||||
return false;
|
throw sg_io_exception("Error opening for reading (and .gz)", sg_location(file));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -423,9 +426,7 @@ bool SGBinObject::read_bin( const string& file ) {
|
|||||||
} else {
|
} else {
|
||||||
// close the file before we return
|
// close the file before we return
|
||||||
gzclose(fp);
|
gzclose(fp);
|
||||||
SG_LOG( SG_EVENT, SG_ALERT,
|
throw sg_io_exception("Bad BTG magic/version", sg_location(file));
|
||||||
"ERROR: " << file << "has bad header");
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// read creation time
|
// read creation time
|
||||||
@@ -462,8 +463,7 @@ bool SGBinObject::read_bin( const string& file ) {
|
|||||||
//cout << "Total objects to read = " << nobjects << endl;
|
//cout << "Total objects to read = " << nobjects << endl;
|
||||||
|
|
||||||
if ( sgReadError() ) {
|
if ( sgReadError() ) {
|
||||||
cout << "Error while reading header of file " << file << "(.gz)" << endl;
|
throw sg_io_exception("Error reading BTG file header", sg_location(file));
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// read in objects
|
// read in objects
|
||||||
@@ -613,19 +613,13 @@ bool SGBinObject::read_bin( const string& file ) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ( sgReadError() ) {
|
if ( sgReadError() ) {
|
||||||
cout << "Error while reading object:" << i << " in file " << file << "(.gz)" << endl;
|
throw sg_io_exception("Error while reading object", sg_location(file, i));
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// close the file
|
// close the file
|
||||||
gzclose(fp);
|
gzclose(fp);
|
||||||
|
|
||||||
if ( sgReadError() ) {
|
|
||||||
cout << "Error while reading file " << file << "(.gz)" << endl;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -688,6 +682,13 @@ void SGBinObject::write_objects(gzFile fp, int type, const group_list& verts,
|
|||||||
if ( !normals.empty() && !normals.front().empty()) idx_mask |= SG_IDX_NORMALS;
|
if ( !normals.empty() && !normals.front().empty()) idx_mask |= SG_IDX_NORMALS;
|
||||||
if ( !colors.empty() && !colors.front().empty()) idx_mask |= SG_IDX_COLORS;
|
if ( !colors.empty() && !colors.front().empty()) idx_mask |= SG_IDX_COLORS;
|
||||||
if ( !texCoords.empty() && !texCoords.front().empty()) idx_mask |= SG_IDX_TEXCOORDS;
|
if ( !texCoords.empty() && !texCoords.front().empty()) idx_mask |= SG_IDX_TEXCOORDS;
|
||||||
|
|
||||||
|
if (idx_mask == 0) {
|
||||||
|
SG_LOG(SG_IO, SG_ALERT, "SGBinObject::write_objects: object with material:"
|
||||||
|
<< m << "has no indices set");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
sgWriteChar( fp, (char)SG_INDEX_TYPES ); // property
|
sgWriteChar( fp, (char)SG_INDEX_TYPES ); // property
|
||||||
sgWriteUInt( fp, 1 ); // nbytes
|
sgWriteUInt( fp, 1 ); // nbytes
|
||||||
sgWriteChar( fp, idx_mask );
|
sgWriteChar( fp, idx_mask );
|
||||||
@@ -934,7 +935,7 @@ bool SGBinObject::write_ascii( const string& base, const string& name,
|
|||||||
fprintf(fp, "\n");
|
fprintf(fp, "\n");
|
||||||
|
|
||||||
// dump individual triangles if they exist
|
// dump individual triangles if they exist
|
||||||
if ( tris_v.size() > 0 ) {
|
if ( ! tris_v.empty() ) {
|
||||||
fprintf(fp, "# triangle groups\n");
|
fprintf(fp, "# triangle groups\n");
|
||||||
|
|
||||||
int start = 0;
|
int start = 0;
|
||||||
@@ -982,7 +983,7 @@ bool SGBinObject::write_ascii( const string& base, const string& name,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// dump triangle groups
|
// dump triangle groups
|
||||||
if ( strips_v.size() > 0 ) {
|
if ( ! strips_v.empty() ) {
|
||||||
fprintf(fp, "# triangle strips\n");
|
fprintf(fp, "# triangle strips\n");
|
||||||
|
|
||||||
int start = 0;
|
int start = 0;
|
||||||
|
|||||||
@@ -123,6 +123,8 @@ public:
|
|||||||
void addChannel(NetChannel* channel);
|
void addChannel(NetChannel* channel);
|
||||||
void removeChannel(NetChannel* channel);
|
void removeChannel(NetChannel* channel);
|
||||||
|
|
||||||
|
bool hasChannels() const { return !channels.empty(); }
|
||||||
|
|
||||||
bool poll(unsigned int timeout = 0);
|
bool poll(unsigned int timeout = 0);
|
||||||
void loop(unsigned int timeout = 0);
|
void loop(unsigned int timeout = 0);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -70,6 +70,8 @@ class NetChat : public NetBufferChannel
|
|||||||
{
|
{
|
||||||
std::string terminator;
|
std::string terminator;
|
||||||
int bytesToCollect;
|
int bytesToCollect;
|
||||||
|
|
||||||
|
protected:
|
||||||
virtual void handleBufferRead (NetBuffer& buffer) ;
|
virtual void handleBufferRead (NetBuffer& buffer) ;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|||||||
@@ -103,9 +103,14 @@ int SGSocketUDP::read( char *buf, int length ) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (length <= 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
int result;
|
int result;
|
||||||
|
// prevent buffer overflow
|
||||||
|
int maxsize = std::min(length - 1, SG_IO_MAX_MSG_SIZE);
|
||||||
|
|
||||||
if ( (result = sock.recv(buf, SG_IO_MAX_MSG_SIZE, 0)) >= 0 ) {
|
if ( (result = sock.recv(buf, maxsize, 0)) >= 0 ) {
|
||||||
buf[result] = '\0';
|
buf[result] = '\0';
|
||||||
// printf("msg received = %s\n", buf);
|
// printf("msg received = %s\n", buf);
|
||||||
}
|
}
|
||||||
@@ -120,10 +125,16 @@ int SGSocketUDP::readline( char *buf, int length ) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (length <= 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
// cout << "sock = " << sock << endl;
|
// cout << "sock = " << sock << endl;
|
||||||
|
|
||||||
char *buf_ptr = save_buf + save_len;
|
char *buf_ptr = save_buf + save_len;
|
||||||
int result = sock.recv(buf_ptr, SG_IO_MAX_MSG_SIZE, 0);
|
// prevent buffer overflow (size of save_buf is 2 * SG_IO_MAX_MSG_SIZE)
|
||||||
|
int maxsize = save_len < SG_IO_MAX_MSG_SIZE ?
|
||||||
|
SG_IO_MAX_MSG_SIZE : 2 * SG_IO_MAX_MSG_SIZE - save_len;
|
||||||
|
int result = sock.recv(buf_ptr, maxsize, 0);
|
||||||
// printf("msg received = %s\n", buf);
|
// printf("msg received = %s\n", buf);
|
||||||
save_len += result;
|
save_len += result;
|
||||||
|
|
||||||
@@ -142,6 +153,8 @@ int SGSocketUDP::readline( char *buf, int length ) {
|
|||||||
// we found an end of line
|
// we found an end of line
|
||||||
|
|
||||||
// copy to external buffer
|
// copy to external buffer
|
||||||
|
// prevent buffer overflow
|
||||||
|
result = std::min(result,length - 1);
|
||||||
strncpy( buf, save_buf, result );
|
strncpy( buf, save_buf, result );
|
||||||
buf[result] = '\0';
|
buf[result] = '\0';
|
||||||
// cout << "sg_socket line = " << buf << endl;
|
// cout << "sg_socket line = " << buf << endl;
|
||||||
|
|||||||
@@ -53,48 +53,23 @@ public:
|
|||||||
bool complete;
|
bool complete;
|
||||||
bool failed;
|
bool failed;
|
||||||
string bodyData;
|
string bodyData;
|
||||||
string bodyContentType;
|
|
||||||
|
|
||||||
TestRequest(const std::string& url, const std::string method = "GET") :
|
TestRequest(const std::string& url, const std::string method = "GET") :
|
||||||
HTTP::Request(url, method),
|
HTTP::Request(url, method),
|
||||||
complete(false)
|
complete(false)
|
||||||
{
|
{
|
||||||
bodyContentType = "text/plain";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::map<string, string> sendHeaders;
|
|
||||||
std::map<string, string> headers;
|
std::map<string, string> headers;
|
||||||
protected:
|
protected:
|
||||||
string_list requestHeaders() const
|
|
||||||
{
|
|
||||||
string_list r;
|
|
||||||
std::map<string, string>::const_iterator it;
|
|
||||||
for (it = sendHeaders.begin(); it != sendHeaders.end(); ++it) {
|
|
||||||
r.push_back(it->first);
|
|
||||||
}
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
string header(const string& name) const
|
virtual void onDone()
|
||||||
{
|
|
||||||
std::map<string, string>::const_iterator it = sendHeaders.find(name);
|
|
||||||
if (it == sendHeaders.end()) {
|
|
||||||
return string();
|
|
||||||
}
|
|
||||||
|
|
||||||
return it->second;
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void responseHeadersComplete()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void responseComplete()
|
|
||||||
{
|
{
|
||||||
complete = true;
|
complete = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void failure()
|
virtual void onFail()
|
||||||
{
|
{
|
||||||
failed = true;
|
failed = true;
|
||||||
}
|
}
|
||||||
@@ -105,11 +80,6 @@ protected:
|
|||||||
bodyData += string(s, n);
|
bodyData += string(s, n);
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual std::string requestBodyType() const
|
|
||||||
{
|
|
||||||
return bodyContentType;
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void responseHeader(const string& header, const string& value)
|
virtual void responseHeader(const string& header, const string& value)
|
||||||
{
|
{
|
||||||
headers[header] = value;
|
headers[header] = value;
|
||||||
@@ -477,6 +447,8 @@ int main(int argc, char* argv[])
|
|||||||
{
|
{
|
||||||
|
|
||||||
HTTP::Client cl;
|
HTTP::Client cl;
|
||||||
|
// force all requests to use the same connection for this test
|
||||||
|
cl.setMaxConnections(1);
|
||||||
|
|
||||||
// test URL parsing
|
// test URL parsing
|
||||||
TestRequest* tr1 = new TestRequest("http://localhost.woo.zar:2000/test1?foo=bar");
|
TestRequest* tr1 = new TestRequest("http://localhost.woo.zar:2000/test1?foo=bar");
|
||||||
@@ -533,8 +505,8 @@ int main(int argc, char* argv[])
|
|||||||
{
|
{
|
||||||
TestRequest* tr = new TestRequest("http://localhost:2000/test_headers");
|
TestRequest* tr = new TestRequest("http://localhost:2000/test_headers");
|
||||||
HTTP::Request_ptr own(tr);
|
HTTP::Request_ptr own(tr);
|
||||||
tr->sendHeaders["X-Foo"] = "Bar";
|
tr->requestHeader("X-Foo") = "Bar";
|
||||||
tr->sendHeaders["X-AnotherHeader"] = "A longer value";
|
tr->requestHeader("X-AnotherHeader") = "A longer value";
|
||||||
cl.makeRequest(tr);
|
cl.makeRequest(tr);
|
||||||
|
|
||||||
waitForComplete(&cl, tr);
|
waitForComplete(&cl, tr);
|
||||||
@@ -657,6 +629,7 @@ int main(int argc, char* argv[])
|
|||||||
cout << "testing HTTP 1.1 pipelineing" << endl;
|
cout << "testing HTTP 1.1 pipelineing" << endl;
|
||||||
|
|
||||||
{
|
{
|
||||||
|
|
||||||
cl.setProxy("", 80);
|
cl.setProxy("", 80);
|
||||||
TestRequest* tr = new TestRequest("http://localhost:2000/test1");
|
TestRequest* tr = new TestRequest("http://localhost:2000/test1");
|
||||||
HTTP::Request_ptr own(tr);
|
HTTP::Request_ptr own(tr);
|
||||||
@@ -718,7 +691,7 @@ int main(int argc, char* argv[])
|
|||||||
{
|
{
|
||||||
cout << "POST" << endl;
|
cout << "POST" << endl;
|
||||||
TestRequest* tr = new TestRequest("http://localhost:2000/test_post?foo=abc&bar=1234&username=johndoe", "POST");
|
TestRequest* tr = new TestRequest("http://localhost:2000/test_post?foo=abc&bar=1234&username=johndoe", "POST");
|
||||||
tr->bodyContentType = "application/x-www-form-urlencoded";
|
tr->setBodyData("", "application/x-www-form-urlencoded");
|
||||||
|
|
||||||
HTTP::Request_ptr own(tr);
|
HTTP::Request_ptr own(tr);
|
||||||
cl.makeRequest(tr);
|
cl.makeRequest(tr);
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ include (SimGearComponent)
|
|||||||
|
|
||||||
set(HEADERS
|
set(HEADERS
|
||||||
CSSBorder.hxx
|
CSSBorder.hxx
|
||||||
|
ListDiff.hxx
|
||||||
ResourceManager.hxx
|
ResourceManager.hxx
|
||||||
interpolator.hxx
|
interpolator.hxx
|
||||||
make_new.hxx
|
make_new.hxx
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ namespace simgear
|
|||||||
{
|
{
|
||||||
ret.val[i] = offsets.val[i];
|
ret.val[i] = offsets.val[i];
|
||||||
if( !types.rel[i] )
|
if( !types.rel[i] )
|
||||||
ret.val[i] /= (i & 1) ? dim.height() : dim.width();
|
ret.val[i] /= (i & 1) ? dim.width() : dim.height();
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
@@ -75,7 +75,7 @@ namespace simgear
|
|||||||
{
|
{
|
||||||
ret.val[i] = offsets.val[i];
|
ret.val[i] = offsets.val[i];
|
||||||
if( types.rel[i] )
|
if( types.rel[i] )
|
||||||
ret.val[i] *= (i & 1) ? dim.height() : dim.width();
|
ret.val[i] *= (i & 1) ? dim.width() : dim.height();
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
|
|||||||
@@ -55,14 +55,21 @@ int main (int ac, char ** av)
|
|||||||
COMPARE(o.b, 15);
|
COMPARE(o.b, 15);
|
||||||
COMPARE(o.l, 20);
|
COMPARE(o.l, 20);
|
||||||
|
|
||||||
b = CSSBorder::parse("5% 10% 15% 20%");
|
b = CSSBorder::parse("5 10 15 20");
|
||||||
o = b.getAbsOffsets(SGRect<int>(0,0,200,200));
|
o = b.getRelOffsets(SGRect<int>(0,0,100,200));
|
||||||
COMPARE(o.t, 10);
|
COMPARE(o.t, 0.025);
|
||||||
COMPARE(o.r, 20);
|
COMPARE(o.r, 0.1);
|
||||||
COMPARE(o.b, 30);
|
COMPARE(o.b, 0.075);
|
||||||
COMPARE(o.l, 40);
|
COMPARE(o.l, 0.2);
|
||||||
|
|
||||||
o = b.getRelOffsets(SGRect<int>(0,0,200,200));
|
b = CSSBorder::parse("5% 10% 15% 20%");
|
||||||
|
o = b.getAbsOffsets(SGRect<int>(0,0,100,200));
|
||||||
|
COMPARE(o.t, 10);
|
||||||
|
COMPARE(o.r, 10);
|
||||||
|
COMPARE(o.b, 30);
|
||||||
|
COMPARE(o.l, 20);
|
||||||
|
|
||||||
|
o = b.getRelOffsets(SGRect<int>(0,0,100,200));
|
||||||
COMPARE(o.t, 0.05);
|
COMPARE(o.t, 0.05);
|
||||||
COMPARE(o.r, 0.1);
|
COMPARE(o.r, 0.1);
|
||||||
COMPARE(o.b, 0.15);
|
COMPARE(o.b, 0.15);
|
||||||
|
|||||||
@@ -0,0 +1,85 @@
|
|||||||
|
///@file
|
||||||
|
/// Compare lists and get differences
|
||||||
|
///
|
||||||
|
// Copyright (C) 2013 Thomas Geymayer <tomgey@gmail.com>
|
||||||
|
//
|
||||||
|
// This library is free software; you can redistribute it and/or
|
||||||
|
// modify it under the terms of the GNU Library General Public
|
||||||
|
// License as published by the Free Software Foundation; either
|
||||||
|
// version 2 of the License, or (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This library is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
// Library General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Library General Public
|
||||||
|
// License along with this library; if not, write to the Free Software
|
||||||
|
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||||
|
|
||||||
|
#ifndef SG_LISTDIFF_HXX_
|
||||||
|
#define SG_LISTDIFF_HXX_
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <boost/function.hpp>
|
||||||
|
|
||||||
|
namespace simgear
|
||||||
|
{
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
struct ListDiff
|
||||||
|
{
|
||||||
|
typedef std::vector<T> List;
|
||||||
|
typedef boost::function<void (T)> Callback;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Perform list diff in-place (modifies both lists) and call cb_add for
|
||||||
|
* every element in new_list not in old_list and cb_remove for every element
|
||||||
|
* in old_list not in new_list.
|
||||||
|
*/
|
||||||
|
static void inplace( List& old_list,
|
||||||
|
List& new_list,
|
||||||
|
Callback cb_add,
|
||||||
|
Callback cb_remove )
|
||||||
|
{
|
||||||
|
// Check which elements have been removed. (Removing first and adding
|
||||||
|
// second should keep the memory usage lower - not for this function, but
|
||||||
|
// probably for users of this function which use the callbacks to delete
|
||||||
|
// and create objects)
|
||||||
|
while( !old_list.empty() )
|
||||||
|
{
|
||||||
|
T& old_el = old_list.front();
|
||||||
|
typename List::iterator new_el =
|
||||||
|
std::find(new_list.begin(), new_list.end(), old_el);
|
||||||
|
|
||||||
|
if( new_el == new_list.end() )
|
||||||
|
{
|
||||||
|
if( cb_remove )
|
||||||
|
cb_remove(old_el);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Element is in both lists -> just ignore
|
||||||
|
*new_el = new_list.back();
|
||||||
|
new_list.pop_back();
|
||||||
|
}
|
||||||
|
|
||||||
|
old_list.front() = old_list.back();
|
||||||
|
old_list.pop_back();
|
||||||
|
}
|
||||||
|
|
||||||
|
// All remaing elements in new_list have not been in old_list, so call
|
||||||
|
// the add callback for every element if required.
|
||||||
|
if( cb_add )
|
||||||
|
{
|
||||||
|
for( typename List::iterator it = new_list.begin();
|
||||||
|
it != new_list.end();
|
||||||
|
++it )
|
||||||
|
cb_add(*it);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace simgear
|
||||||
|
|
||||||
|
#endif /* SG_LISTDIFF_HXX_ */
|
||||||
@@ -43,6 +43,30 @@ void test_dir()
|
|||||||
cout << temp.path().modTime() << endl;
|
cout << temp.path().modTime() << endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SGPath::Permissions validateNone(const SGPath&)
|
||||||
|
{
|
||||||
|
SGPath::Permissions p;
|
||||||
|
p.read = false;
|
||||||
|
p.write = false;
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
SGPath::Permissions validateRead(const SGPath&)
|
||||||
|
{
|
||||||
|
SGPath::Permissions p;
|
||||||
|
p.read = true;
|
||||||
|
p.write = false;
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
SGPath::Permissions validateWrite(const SGPath&)
|
||||||
|
{
|
||||||
|
SGPath::Permissions p;
|
||||||
|
p.read = false;
|
||||||
|
p.write = true;
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
int main(int argc, char* argv[])
|
int main(int argc, char* argv[])
|
||||||
{
|
{
|
||||||
SGPath pa;
|
SGPath pa;
|
||||||
@@ -144,6 +168,25 @@ int main(int argc, char* argv[])
|
|||||||
COMPARE(pf.lower_extension(), "gz");
|
COMPARE(pf.lower_extension(), "gz");
|
||||||
COMPARE(pf.complete_lower_extension(), "txt.gz");
|
COMPARE(pf.complete_lower_extension(), "txt.gz");
|
||||||
|
|
||||||
|
COMPARE(pf.canRead(), true);
|
||||||
|
COMPARE(pf.canWrite(), true);
|
||||||
|
|
||||||
|
SGPath pp(&validateNone);
|
||||||
|
COMPARE(pp.canRead(), false);
|
||||||
|
COMPARE(pp.canWrite(), false);
|
||||||
|
|
||||||
|
pp.append("./test-dir/file.txt");
|
||||||
|
COMPARE(pp.create_dir(0700), -3);
|
||||||
|
|
||||||
|
pp.setPermissonChecker(&validateRead);
|
||||||
|
COMPARE(pp.canRead(), true);
|
||||||
|
COMPARE(pp.canWrite(), false);
|
||||||
|
COMPARE(pp.create_dir(0700), -3);
|
||||||
|
|
||||||
|
pp.setPermissonChecker(&validateWrite);
|
||||||
|
COMPARE(pp.canRead(), false);
|
||||||
|
COMPARE(pp.canWrite(), true);
|
||||||
|
|
||||||
test_dir();
|
test_dir();
|
||||||
|
|
||||||
cout << "all tests passed OK" << endl;
|
cout << "all tests passed OK" << endl;
|
||||||
|
|||||||
@@ -149,7 +149,11 @@ PathList Dir::children(int types, const std::string& nameFilter) const
|
|||||||
WIN32_FIND_DATA fData;
|
WIN32_FIND_DATA fData;
|
||||||
HANDLE find = FindFirstFile(search.c_str(), &fData);
|
HANDLE find = FindFirstFile(search.c_str(), &fData);
|
||||||
if (find == INVALID_HANDLE_VALUE) {
|
if (find == INVALID_HANDLE_VALUE) {
|
||||||
SG_LOG(SG_GENERAL, SG_WARN, "Dir::children: FindFirstFile failed:" << _path.str());
|
int err = GetLastError();
|
||||||
|
if (err != ERROR_FILE_NOT_FOUND) {
|
||||||
|
SG_LOG(SG_GENERAL, SG_WARN, "Dir::children: FindFirstFile failed:" <<
|
||||||
|
_path.str() << " with error:" << err);
|
||||||
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+219
-73
@@ -55,6 +55,34 @@ static const char sgSearchPathSep = ';';
|
|||||||
static const char sgSearchPathSep = ':';
|
static const char sgSearchPathSep = ':';
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
|
||||||
|
#include <ShlObj.h> // for CSIDL
|
||||||
|
|
||||||
|
static SGPath pathForCSIDL(int csidl, const SGPath::PermissonChecker& checker)
|
||||||
|
{
|
||||||
|
typedef BOOL (WINAPI*GetSpecialFolderPath)(HWND, LPSTR, int, BOOL);
|
||||||
|
static GetSpecialFolderPath SHGetSpecialFolderPath = NULL;
|
||||||
|
|
||||||
|
// lazy open+resolve of shell32
|
||||||
|
if (!SHGetSpecialFolderPath) {
|
||||||
|
HINSTANCE shellDll = ::LoadLibrary("shell32");
|
||||||
|
SHGetSpecialFolderPath = (GetSpecialFolderPath) GetProcAddress(shellDll, "SHGetSpecialFolderPathA");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!SHGetSpecialFolderPath){
|
||||||
|
return SGPath();
|
||||||
|
}
|
||||||
|
|
||||||
|
char path[MAX_PATH];
|
||||||
|
if (SHGetSpecialFolderPath(0, path, csidl, false)) {
|
||||||
|
return SGPath(path, checker);
|
||||||
|
}
|
||||||
|
|
||||||
|
return SGPath();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
// For windows, replace "\" by "/".
|
// For windows, replace "\" by "/".
|
||||||
void
|
void
|
||||||
@@ -75,27 +103,35 @@ SGPath::fix()
|
|||||||
|
|
||||||
|
|
||||||
// default constructor
|
// default constructor
|
||||||
SGPath::SGPath()
|
SGPath::SGPath(PermissonChecker validator)
|
||||||
: path(""),
|
: path(""),
|
||||||
|
_permission_checker(validator),
|
||||||
_cached(false),
|
_cached(false),
|
||||||
|
_rwCached(false),
|
||||||
_cacheEnabled(true)
|
_cacheEnabled(true)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// create a path based on "path"
|
// create a path based on "path"
|
||||||
SGPath::SGPath( const std::string& p )
|
SGPath::SGPath( const std::string& p, PermissonChecker validator )
|
||||||
: path(p),
|
: path(p),
|
||||||
|
_permission_checker(validator),
|
||||||
_cached(false),
|
_cached(false),
|
||||||
|
_rwCached(false),
|
||||||
_cacheEnabled(true)
|
_cacheEnabled(true)
|
||||||
{
|
{
|
||||||
fix();
|
fix();
|
||||||
}
|
}
|
||||||
|
|
||||||
// create a path based on "path" and a "subpath"
|
// create a path based on "path" and a "subpath"
|
||||||
SGPath::SGPath( const SGPath& p, const std::string& r )
|
SGPath::SGPath( const SGPath& p,
|
||||||
|
const std::string& r,
|
||||||
|
PermissonChecker validator )
|
||||||
: path(p.path),
|
: path(p.path),
|
||||||
|
_permission_checker(validator),
|
||||||
_cached(false),
|
_cached(false),
|
||||||
|
_rwCached(false),
|
||||||
_cacheEnabled(p._cacheEnabled)
|
_cacheEnabled(p._cacheEnabled)
|
||||||
{
|
{
|
||||||
append(r);
|
append(r);
|
||||||
@@ -104,8 +140,12 @@ SGPath::SGPath( const SGPath& p, const std::string& r )
|
|||||||
|
|
||||||
SGPath::SGPath(const SGPath& p) :
|
SGPath::SGPath(const SGPath& p) :
|
||||||
path(p.path),
|
path(p.path),
|
||||||
|
_permission_checker(p._permission_checker),
|
||||||
_cached(p._cached),
|
_cached(p._cached),
|
||||||
|
_rwCached(p._rwCached),
|
||||||
_cacheEnabled(p._cacheEnabled),
|
_cacheEnabled(p._cacheEnabled),
|
||||||
|
_canRead(p._canRead),
|
||||||
|
_canWrite(p._canWrite),
|
||||||
_exists(p._exists),
|
_exists(p._exists),
|
||||||
_isDir(p._isDir),
|
_isDir(p._isDir),
|
||||||
_isFile(p._isFile),
|
_isFile(p._isFile),
|
||||||
@@ -116,8 +156,12 @@ SGPath::SGPath(const SGPath& p) :
|
|||||||
SGPath& SGPath::operator=(const SGPath& p)
|
SGPath& SGPath::operator=(const SGPath& p)
|
||||||
{
|
{
|
||||||
path = p.path;
|
path = p.path;
|
||||||
|
_permission_checker = p._permission_checker,
|
||||||
_cached = p._cached;
|
_cached = p._cached;
|
||||||
|
_rwCached = p._rwCached;
|
||||||
_cacheEnabled = p._cacheEnabled;
|
_cacheEnabled = p._cacheEnabled;
|
||||||
|
_canRead = p._canRead;
|
||||||
|
_canWrite = p._canWrite;
|
||||||
_exists = p._exists;
|
_exists = p._exists;
|
||||||
_isDir = p._isDir;
|
_isDir = p._isDir;
|
||||||
_isFile = p._isFile;
|
_isFile = p._isFile;
|
||||||
@@ -135,16 +179,31 @@ void SGPath::set( const string& p ) {
|
|||||||
path = p;
|
path = p;
|
||||||
fix();
|
fix();
|
||||||
_cached = false;
|
_cached = false;
|
||||||
|
_rwCached = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
void SGPath::setPermissonChecker(PermissonChecker validator)
|
||||||
|
{
|
||||||
|
_permission_checker = validator;
|
||||||
|
_rwCached = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
SGPath::PermissonChecker SGPath::getPermissonChecker() const
|
||||||
|
{
|
||||||
|
return _permission_checker;
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
void SGPath::set_cached(bool cached)
|
void SGPath::set_cached(bool cached)
|
||||||
{
|
{
|
||||||
_cacheEnabled = cached;
|
_cacheEnabled = cached;
|
||||||
}
|
}
|
||||||
|
|
||||||
// append another piece to the existing path
|
// append another piece to the existing path
|
||||||
void SGPath::append( const string& p ) {
|
void SGPath::append( const string& p ) {
|
||||||
if ( path.size() == 0 ) {
|
if ( path.empty() ) {
|
||||||
path = p;
|
path = p;
|
||||||
} else {
|
} else {
|
||||||
if ( p[0] != sgDirPathSep ) {
|
if ( p[0] != sgDirPathSep ) {
|
||||||
@@ -154,6 +213,7 @@ void SGPath::append( const string& p ) {
|
|||||||
}
|
}
|
||||||
fix();
|
fix();
|
||||||
_cached = false;
|
_cached = false;
|
||||||
|
_rwCached = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
@@ -173,13 +233,14 @@ void SGPath::add( const string& p ) {
|
|||||||
// concatenate a string to the end of the path without inserting a
|
// concatenate a string to the end of the path without inserting a
|
||||||
// path separator
|
// path separator
|
||||||
void SGPath::concat( const string& p ) {
|
void SGPath::concat( const string& p ) {
|
||||||
if ( path.size() == 0 ) {
|
if ( path.empty() ) {
|
||||||
path = p;
|
path = p;
|
||||||
} else {
|
} else {
|
||||||
path += p;
|
path += p;
|
||||||
}
|
}
|
||||||
fix();
|
fix();
|
||||||
_cached = false;
|
_cached = false;
|
||||||
|
_rwCached = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -279,13 +340,20 @@ void SGPath::validate() const
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (path.empty()) {
|
||||||
|
_exists = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
struct _stat buf ;
|
struct _stat buf ;
|
||||||
|
|
||||||
bool remove_trailing = false;
|
bool remove_trailing = false;
|
||||||
if ( path.length() > 1 && path[path.length()-1] == '/' )
|
string statPath(path);
|
||||||
remove_trailing=true;
|
if ((path.length() > 1) && (path.back() == '/')) {
|
||||||
if (_stat (path.substr(0,remove_trailing?path.length()-1:path.length()).c_str(), &buf ) < 0) {
|
statPath.pop_back();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_stat(statPath.c_str(), &buf ) < 0) {
|
||||||
_exists = false;
|
_exists = false;
|
||||||
} else {
|
} else {
|
||||||
_exists = true;
|
_exists = true;
|
||||||
@@ -310,12 +378,46 @@ void SGPath::validate() const
|
|||||||
_cached = true;
|
_cached = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SGPath::checkAccess() const
|
||||||
|
{
|
||||||
|
if( _rwCached && _cacheEnabled )
|
||||||
|
return;
|
||||||
|
|
||||||
|
if( _permission_checker )
|
||||||
|
{
|
||||||
|
Permissions p = _permission_checker(*this);
|
||||||
|
_canRead = p.read;
|
||||||
|
_canWrite = p.write;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_canRead = true;
|
||||||
|
_canWrite = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
_rwCached = true;
|
||||||
|
}
|
||||||
|
|
||||||
bool SGPath::exists() const
|
bool SGPath::exists() const
|
||||||
{
|
{
|
||||||
validate();
|
validate();
|
||||||
return _exists;
|
return _exists;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
bool SGPath::canRead() const
|
||||||
|
{
|
||||||
|
checkAccess();
|
||||||
|
return _canRead;
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
bool SGPath::canWrite() const
|
||||||
|
{
|
||||||
|
checkAccess();
|
||||||
|
return _canWrite;
|
||||||
|
}
|
||||||
|
|
||||||
bool SGPath::isDir() const
|
bool SGPath::isDir() const
|
||||||
{
|
{
|
||||||
validate();
|
validate();
|
||||||
@@ -344,7 +446,7 @@ int SGPath::create_dir( mode_t mode ) {
|
|||||||
bool absolute = !path.empty() && path[0] == sgDirPathSep;
|
bool absolute = !path.empty() && path[0] == sgDirPathSep;
|
||||||
|
|
||||||
unsigned int i = 1;
|
unsigned int i = 1;
|
||||||
SGPath dir = absolute ? string( 1, sgDirPathSep ) : "";
|
SGPath dir(absolute ? string( 1, sgDirPathSep ) : "", _permission_checker);
|
||||||
dir.concat( path_elements[0] );
|
dir.concat( path_elements[0] );
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
if ( dir.str().find(':') != string::npos && path_elements.size() >= 2 ) {
|
if ( dir.str().find(':') != string::npos && path_elements.size() >= 2 ) {
|
||||||
@@ -360,16 +462,26 @@ int SGPath::create_dir( mode_t mode ) {
|
|||||||
if ( r == 0 ) {
|
if ( r == 0 ) {
|
||||||
return 0; // Directory already exists
|
return 0; // Directory already exists
|
||||||
}
|
}
|
||||||
if ( sgMkDir( dir.c_str(), mode) ) {
|
for(;;)
|
||||||
SG_LOG( SG_IO, SG_ALERT, "Error creating directory: " + dir.str() );
|
{
|
||||||
|
if( !dir.canWrite() )
|
||||||
|
{
|
||||||
|
SG_LOG( SG_IO,
|
||||||
|
SG_WARN, "Error creating directory: (" << dir.str() << ")" <<
|
||||||
|
" reason: access denied" );
|
||||||
|
return -3;
|
||||||
|
}
|
||||||
|
else if( sgMkDir(dir.c_str(), mode) )
|
||||||
|
{
|
||||||
|
SG_LOG( SG_IO,
|
||||||
|
SG_ALERT, "Error creating directory: (" << dir.str() << ")" );
|
||||||
return -2;
|
return -2;
|
||||||
}
|
}
|
||||||
for(; i < path_elements.size(); i++) {
|
|
||||||
dir.append(path_elements[i]);
|
if( i >= path_elements.size() )
|
||||||
if ( sgMkDir( dir.c_str(), mode) ) {
|
return 0;
|
||||||
SG_LOG( SG_IO, SG_ALERT, "Error creating directory: " + dir.str() );
|
|
||||||
return -2;
|
dir.append(path_elements[i++]);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@@ -378,7 +490,7 @@ int SGPath::create_dir( mode_t mode ) {
|
|||||||
string_list sgPathBranchSplit( const string &dirpath ) {
|
string_list sgPathBranchSplit( const string &dirpath ) {
|
||||||
string_list path_elements;
|
string_list path_elements;
|
||||||
string element, path = dirpath;
|
string element, path = dirpath;
|
||||||
while ( path.size() ) {
|
while ( ! path.empty() ) {
|
||||||
size_t p = path.find( sgDirPathSep );
|
size_t p = path.find( sgDirPathSep );
|
||||||
if ( p != string::npos ) {
|
if ( p != string::npos ) {
|
||||||
element = path.substr( 0, p );
|
element = path.substr( 0, p );
|
||||||
@@ -387,7 +499,7 @@ string_list sgPathBranchSplit( const string &dirpath ) {
|
|||||||
element = path;
|
element = path;
|
||||||
path = "";
|
path = "";
|
||||||
}
|
}
|
||||||
if ( element.size() )
|
if ( ! element.empty() )
|
||||||
path_elements.push_back( element );
|
path_elements.push_back( element );
|
||||||
}
|
}
|
||||||
return path_elements;
|
return path_elements;
|
||||||
@@ -456,13 +568,27 @@ std::string SGPath::str_native() const
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
bool SGPath::remove()
|
bool SGPath::remove()
|
||||||
{
|
{
|
||||||
int err = ::unlink(c_str());
|
if( !canWrite() )
|
||||||
if (err) {
|
{
|
||||||
SG_LOG(SG_IO, SG_WARN, "file remove failed: (" << str() << ") " << strerror(errno));
|
SG_LOG( SG_IO, SG_WARN, "file remove failed: (" << str() << ")"
|
||||||
}
|
" reason: access denied" );
|
||||||
return (err == 0);
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int err = ::unlink(c_str());
|
||||||
|
if( err )
|
||||||
|
{
|
||||||
|
SG_LOG( SG_IO, SG_WARN, "file remove failed: (" << str() << ") "
|
||||||
|
" reason: " << strerror(errno) );
|
||||||
|
// TODO check if failed unlink can really change any of the cached values
|
||||||
|
}
|
||||||
|
|
||||||
|
_cached = false; // stat again if required
|
||||||
|
_rwCached = false;
|
||||||
|
return (err == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
time_t SGPath::modTime() const
|
time_t SGPath::modTime() const
|
||||||
@@ -481,17 +607,44 @@ bool SGPath::operator!=(const SGPath& other) const
|
|||||||
return (path != other.path);
|
return (path != other.path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
bool SGPath::rename(const SGPath& newName)
|
bool SGPath::rename(const SGPath& newName)
|
||||||
{
|
{
|
||||||
if (::rename(c_str(), newName.c_str()) != 0) {
|
if( !canRead() || !canWrite() || !newName.canWrite() )
|
||||||
SG_LOG(SG_IO, SG_WARN, "renamed failed: from " << str() << " to " << newName.str()
|
{
|
||||||
<< " reason: " << strerror(errno));
|
SG_LOG( SG_IO, SG_WARN, "rename failed: from " << str() <<
|
||||||
return false;
|
" to " << newName.str() <<
|
||||||
}
|
" reason: access denied" );
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
path = newName.path;
|
#ifdef SG_WINDOWS
|
||||||
_cached = false;
|
if (newName.exists()) {
|
||||||
return true;
|
SGPath r(newName);
|
||||||
|
if (!r.remove()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
if( ::rename(c_str(), newName.c_str()) != 0 )
|
||||||
|
{
|
||||||
|
SG_LOG( SG_IO, SG_WARN, "rename failed: from " << str() <<
|
||||||
|
" to " << newName.str() <<
|
||||||
|
" reason: " << strerror(errno) );
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
path = newName.path;
|
||||||
|
|
||||||
|
// Do not remove permission checker (could happen for example if just using
|
||||||
|
// a std::string as new name)
|
||||||
|
if( newName._permission_checker )
|
||||||
|
_permission_checker = newName._permission_checker;
|
||||||
|
|
||||||
|
_cached = false;
|
||||||
|
_rwCached = false;
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
@@ -499,75 +652,68 @@ SGPath SGPath::fromEnv(const char* name, const SGPath& def)
|
|||||||
{
|
{
|
||||||
const char* val = getenv(name);
|
const char* val = getenv(name);
|
||||||
if( val && val[0] )
|
if( val && val[0] )
|
||||||
return SGPath(val);
|
return SGPath(val, def._permission_checker);
|
||||||
return def;
|
return def;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
SGPath SGPath::home()
|
SGPath SGPath::home(const SGPath& def)
|
||||||
{
|
{
|
||||||
// TODO
|
// TODO
|
||||||
return SGPath();
|
return def;
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
SGPath SGPath::home()
|
SGPath SGPath::home(const SGPath& def)
|
||||||
{
|
{
|
||||||
return fromEnv("HOME");
|
return fromEnv("HOME", def);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
|
|
||||||
#include <ShlObj.h> // for CSIDL
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
SGPath SGPath::desktop()
|
SGPath SGPath::desktop(const SGPath& def)
|
||||||
{
|
{
|
||||||
typedef BOOL (WINAPI*GetSpecialFolderPath)(HWND, LPSTR, int, BOOL);
|
SGPath r = pathForCSIDL(CSIDL_DESKTOPDIRECTORY, def._permission_checker);
|
||||||
static GetSpecialFolderPath SHGetSpecialFolderPath = NULL;
|
if (!r.isNull()) {
|
||||||
|
return r;
|
||||||
// lazy open+resolve of shell32
|
|
||||||
if (!SHGetSpecialFolderPath) {
|
|
||||||
HINSTANCE shellDll = ::LoadLibrary("shell32");
|
|
||||||
SHGetSpecialFolderPath = (GetSpecialFolderPath) GetProcAddress(shellDll, "SHGetSpecialFolderPathA");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!SHGetSpecialFolderPath){
|
|
||||||
return SGPath();
|
|
||||||
}
|
|
||||||
|
|
||||||
char path[MAX_PATH];
|
|
||||||
if (SHGetSpecialFolderPath(0, path, CSIDL_DESKTOPDIRECTORY, false)) {
|
|
||||||
return SGPath(path);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SG_LOG(SG_GENERAL, SG_ALERT, "SGPath::desktop() failed, bad" );
|
SG_LOG(SG_GENERAL, SG_ALERT, "SGPath::desktop() failed, bad" );
|
||||||
return SGPath();
|
return def;
|
||||||
|
}
|
||||||
|
|
||||||
|
SGPath SGPath::documents(const SGPath& def)
|
||||||
|
{
|
||||||
|
SGPath r = pathForCSIDL(CSIDL_MYDOCUMENTS, def._permission_checker);
|
||||||
|
if (!r.isNull()) {
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
SG_LOG(SG_GENERAL, SG_ALERT, "SGPath::documents() failed, bad" );
|
||||||
|
return def;
|
||||||
}
|
}
|
||||||
#elif __APPLE__
|
#elif __APPLE__
|
||||||
#include <CoreServices/CoreServices.h>
|
#include <CoreServices/CoreServices.h>
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
SGPath SGPath::desktop()
|
SGPath SGPath::desktop(const SGPath& def)
|
||||||
{
|
{
|
||||||
FSRef ref;
|
FSRef ref;
|
||||||
OSErr err = FSFindFolder(kUserDomain, kDesktopFolderType, false, &ref);
|
OSErr err = FSFindFolder(kUserDomain, kDesktopFolderType, false, &ref);
|
||||||
if (err) {
|
if (err)
|
||||||
return SGPath();
|
return def;
|
||||||
}
|
|
||||||
|
|
||||||
unsigned char path[1024];
|
unsigned char path[1024];
|
||||||
if (FSRefMakePath(&ref, path, 1024) != noErr) {
|
if (FSRefMakePath(&ref, path, 1024) != noErr)
|
||||||
return SGPath();
|
return def;
|
||||||
}
|
|
||||||
|
|
||||||
return SGPath((const char*) path);
|
return SGPath((const char*) path, def._permission_checker);
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
SGPath SGPath::desktop()
|
SGPath SGPath::desktop(const SGPath& def)
|
||||||
{
|
{
|
||||||
// http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html
|
// http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html
|
||||||
|
|
||||||
@@ -594,12 +740,12 @@ SGPath SGPath::desktop()
|
|||||||
|
|
||||||
const std::string HOME = "$HOME";
|
const std::string HOME = "$HOME";
|
||||||
if( starts_with(line, HOME) )
|
if( starts_with(line, HOME) )
|
||||||
return home() / simgear::strutils::unescape(line.substr(HOME.length()));
|
return home(def) / simgear::strutils::unescape(line.substr(HOME.length()));
|
||||||
|
|
||||||
return SGPath(line);
|
return SGPath(line, def._permission_checker);
|
||||||
}
|
}
|
||||||
|
|
||||||
return home() / "Desktop";
|
return home(def) / "Desktop";
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|||||||
@@ -52,8 +52,15 @@ class SGPath {
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
struct Permissions
|
||||||
|
{
|
||||||
|
bool read : 1;
|
||||||
|
bool write : 1;
|
||||||
|
};
|
||||||
|
typedef Permissions (*PermissonChecker)(const SGPath&);
|
||||||
|
|
||||||
/** Default constructor */
|
/** Default constructor */
|
||||||
SGPath();
|
explicit SGPath(PermissonChecker validator = NULL);
|
||||||
|
|
||||||
/** Copy contructor */
|
/** Copy contructor */
|
||||||
SGPath(const SGPath& p);
|
SGPath(const SGPath& p);
|
||||||
@@ -64,14 +71,16 @@ public:
|
|||||||
* Construct a path based on the starting path provided.
|
* Construct a path based on the starting path provided.
|
||||||
* @param p initial path
|
* @param p initial path
|
||||||
*/
|
*/
|
||||||
SGPath( const std::string& p );
|
SGPath( const std::string& p, PermissonChecker validator = NULL );
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Construct a path based on the starting path provided and a relative subpath
|
* Construct a path based on the starting path provided and a relative subpath
|
||||||
* @param p initial path
|
* @param p initial path
|
||||||
* @param r relative subpath
|
* @param r relative subpath
|
||||||
*/
|
*/
|
||||||
SGPath( const SGPath& p, const std::string& r );
|
SGPath( const SGPath& p,
|
||||||
|
const std::string& r,
|
||||||
|
PermissonChecker validator = NULL );
|
||||||
|
|
||||||
/** Destructor */
|
/** Destructor */
|
||||||
~SGPath();
|
~SGPath();
|
||||||
@@ -86,6 +95,9 @@ public:
|
|||||||
bool operator==(const SGPath& other) const;
|
bool operator==(const SGPath& other) const;
|
||||||
bool operator!=(const SGPath& other) const;
|
bool operator!=(const SGPath& other) const;
|
||||||
|
|
||||||
|
void setPermissonChecker(PermissonChecker validator);
|
||||||
|
PermissonChecker getPermissonChecker() const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set if file information (exists, type, mod-time) is cached or
|
* Set if file information (exists, type, mod-time) is cached or
|
||||||
* retrieved each time it is queried. Caching is enabled by default
|
* retrieved each time it is queried. Caching is enabled by default
|
||||||
@@ -198,6 +210,17 @@ public:
|
|||||||
*/
|
*/
|
||||||
int create_dir(mode_t mode);
|
int create_dir(mode_t mode);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if reading file is allowed. Readabilty does not imply the existance
|
||||||
|
* of the file.
|
||||||
|
*
|
||||||
|
* @note By default all files will be marked as readable. No check is made
|
||||||
|
* if the operating system allows the given file to be read. Derived
|
||||||
|
* classes may actually implement custom read/write rights.
|
||||||
|
*/
|
||||||
|
bool canRead() const;
|
||||||
|
bool canWrite() const;
|
||||||
|
|
||||||
bool isFile() const;
|
bool isFile() const;
|
||||||
bool isDir() const;
|
bool isDir() const;
|
||||||
|
|
||||||
@@ -246,23 +269,32 @@ public:
|
|||||||
/**
|
/**
|
||||||
* Get path to user's home directory
|
* Get path to user's home directory
|
||||||
*/
|
*/
|
||||||
static SGPath home();
|
static SGPath home(const SGPath& def = SGPath());
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get path to the user's desktop directory
|
* Get path to the user's desktop directory
|
||||||
*/
|
*/
|
||||||
static SGPath desktop();
|
static SGPath desktop(const SGPath& def = SGPath());
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get path to the user's documents directory
|
||||||
|
*/
|
||||||
|
static SGPath documents(const SGPath& def = SGPath());
|
||||||
private:
|
private:
|
||||||
|
|
||||||
void fix();
|
void fix();
|
||||||
|
|
||||||
void validate() const;
|
void validate() const;
|
||||||
|
void checkAccess() const;
|
||||||
|
|
||||||
std::string path;
|
std::string path;
|
||||||
|
PermissonChecker _permission_checker;
|
||||||
|
|
||||||
mutable bool _cached : 1;
|
mutable bool _cached : 1;
|
||||||
|
mutable bool _rwCached : 1;
|
||||||
bool _cacheEnabled : 1; ///< cacheing can be disbled if required
|
bool _cacheEnabled : 1; ///< cacheing can be disbled if required
|
||||||
|
mutable bool _canRead : 1;
|
||||||
|
mutable bool _canWrite : 1;
|
||||||
mutable bool _exists : 1;
|
mutable bool _exists : 1;
|
||||||
mutable bool _isDir : 1;
|
mutable bool _isDir : 1;
|
||||||
mutable bool _isFile : 1;
|
mutable bool _isFile : 1;
|
||||||
|
|||||||
+107
-20
@@ -26,6 +26,8 @@
|
|||||||
|
|
||||||
#include "strutils.hxx"
|
#include "strutils.hxx"
|
||||||
|
|
||||||
|
#include <simgear/debug/logstream.hxx>
|
||||||
|
|
||||||
using std::string;
|
using std::string;
|
||||||
using std::vector;
|
using std::vector;
|
||||||
using std::stringstream;
|
using std::stringstream;
|
||||||
@@ -33,6 +35,39 @@ using std::stringstream;
|
|||||||
namespace simgear {
|
namespace simgear {
|
||||||
namespace strutils {
|
namespace strutils {
|
||||||
|
|
||||||
|
/*
|
||||||
|
* utf8ToLatin1() convert utf8 to latin, useful for accent character (i.e éâàîè...)
|
||||||
|
*/
|
||||||
|
template <typename Iterator> size_t get_length (Iterator p) {
|
||||||
|
unsigned char c = static_cast<unsigned char> (*p);
|
||||||
|
if (c < 0x80) return 1;
|
||||||
|
else if (!(c & 0x20)) return 2;
|
||||||
|
else if (!(c & 0x10)) return 3;
|
||||||
|
else if (!(c & 0x08)) return 4;
|
||||||
|
else if (!(c & 0x04)) return 5;
|
||||||
|
else return 6;
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef unsigned int value_type;
|
||||||
|
template <typename Iterator> value_type get_value (Iterator p) {
|
||||||
|
size_t len = get_length (p);
|
||||||
|
if (len == 1) return *p;
|
||||||
|
value_type res = static_cast<unsigned char> ( *p & (0xff >> (len + 1))) << ((len - 1) * 6 );
|
||||||
|
for (--len; len; --len)
|
||||||
|
res |= (static_cast<unsigned char> (*(++p)) - 0x80) << ((len - 1) * 6);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
string utf8ToLatin1( string& s_utf8 ) {
|
||||||
|
string s_latin1;
|
||||||
|
for (string::iterator p = s_utf8.begin(); p != s_utf8.end(); ++p) {
|
||||||
|
value_type value = get_value<string::iterator&>(p);
|
||||||
|
if (value > 0xff) SG_LOG(SG_IO, SG_WARN, "utf8ToLatin1: wrong char value: " << value);
|
||||||
|
s_latin1 += static_cast<char>(value);
|
||||||
|
}
|
||||||
|
return s_latin1;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
@@ -303,26 +338,51 @@ namespace simgear {
|
|||||||
return rslt;
|
return rslt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
string lowercase(const string &s) {
|
||||||
|
string rslt(s);
|
||||||
|
for(string::iterator p = rslt.begin(); p != rslt.end(); p++){
|
||||||
|
*p = tolower(*p);
|
||||||
|
}
|
||||||
|
return rslt;
|
||||||
|
}
|
||||||
|
|
||||||
|
void lowercase(string &s) {
|
||||||
|
for(string::iterator p = s.begin(); p != s.end(); p++){
|
||||||
|
*p = tolower(*p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(SG_WINDOWS)
|
||||||
|
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
static WCharVec convertMultiByteToWString(DWORD encoding, const std::string& a)
|
||||||
|
{
|
||||||
|
WCharVec result;
|
||||||
|
DWORD flags = 0;
|
||||||
|
int requiredWideChars = MultiByteToWideChar(encoding, flags,
|
||||||
|
a.c_str(), a.size(),
|
||||||
|
NULL, 0);
|
||||||
|
result.resize(requiredWideChars);
|
||||||
|
MultiByteToWideChar(encoding, flags, a.c_str(), a.size(),
|
||||||
|
result.data(), result.size());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
WCharVec convertUtf8ToWString(const std::string& a)
|
||||||
|
{
|
||||||
|
return convertMultiByteToWString(CP_UTF8, a);
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef SG_WINDOWS
|
|
||||||
#include <windows.h>
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
std::string convertWindowsLocal8BitToUtf8(const std::string& a)
|
std::string convertWindowsLocal8BitToUtf8(const std::string& a)
|
||||||
{
|
{
|
||||||
#ifdef SG_WINDOWS
|
#ifdef SG_WINDOWS
|
||||||
DWORD flags = 0;
|
DWORD flags = 0;
|
||||||
std::vector<wchar_t> wideString;
|
WCharVec wideString = convertMultiByteToWString(CP_ACP, a);
|
||||||
|
|
||||||
// call to query transform size
|
// convert down to UTF-8
|
||||||
int requiredWideChars = MultiByteToWideChar(CP_ACP, flags, a.c_str(), a.size(),
|
|
||||||
NULL, 0);
|
|
||||||
// allocate storage and call for real
|
|
||||||
wideString.resize(requiredWideChars);
|
|
||||||
MultiByteToWideChar(CP_ACP, flags, a.c_str(), a.size(),
|
|
||||||
wideString.data(), wideString.size());
|
|
||||||
|
|
||||||
// now convert back down to UTF-8
|
|
||||||
std::vector<char> result;
|
std::vector<char> result;
|
||||||
int requiredUTF8Chars = WideCharToMultiByte(CP_UTF8, flags,
|
int requiredUTF8Chars = WideCharToMultiByte(CP_UTF8, flags,
|
||||||
wideString.data(), wideString.size(),
|
wideString.data(), wideString.size(),
|
||||||
@@ -337,11 +397,30 @@ std::string convertWindowsLocal8BitToUtf8(const std::string& a)
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
static const std::string base64_chars =
|
static const std::string base64_chars =
|
||||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||||
"abcdefghijklmnopqrstuvwxyz"
|
"abcdefghijklmnopqrstuvwxyz"
|
||||||
"0123456789+/";
|
"0123456789+/";
|
||||||
|
|
||||||
|
static const unsigned char base64_decode_map[128] =
|
||||||
|
{
|
||||||
|
127, 127, 127, 127, 127, 127, 127, 127, 127, 127,
|
||||||
|
127, 127, 127, 127, 127, 127, 127, 127, 127, 127,
|
||||||
|
127, 127, 127, 127, 127, 127, 127, 127, 127, 127,
|
||||||
|
127, 127, 127, 127, 127, 127, 127, 127, 127, 127,
|
||||||
|
127, 127, 127, 62, 127, 127, 127, 63, 52, 53,
|
||||||
|
54, 55, 56, 57, 58, 59, 60, 61, 127, 127,
|
||||||
|
127, 64, 127, 127, 127, 0, 1, 2, 3, 4,
|
||||||
|
5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
|
||||||
|
15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
|
||||||
|
25, 127, 127, 127, 127, 127, 127, 26, 27, 28,
|
||||||
|
29, 30, 31, 32, 33, 34, 35, 36, 37, 38,
|
||||||
|
39, 40, 41, 42, 43, 44, 45, 46, 47, 48,
|
||||||
|
49, 50, 51, 127, 127, 127, 127, 127
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
static inline bool is_base64(unsigned char c) {
|
static inline bool is_base64(unsigned char c) {
|
||||||
return (isalnum(c) || (c == '+') || (c == '/'));
|
return (isalnum(c) || (c == '+') || (c == '/'));
|
||||||
@@ -351,14 +430,13 @@ static bool is_whitespace(unsigned char c) {
|
|||||||
return ((c == ' ') || (c == '\r') || (c == '\n'));
|
return ((c == ' ') || (c == '\r') || (c == '\n'));
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string decodeBase64(const std::string& encoded_string)
|
void decodeBase64(const std::string& encoded_string, std::vector<unsigned char>& ret)
|
||||||
{
|
{
|
||||||
int in_len = encoded_string.size();
|
int in_len = encoded_string.size();
|
||||||
int i = 0;
|
int i = 0;
|
||||||
int j = 0;
|
int j = 0;
|
||||||
int in_ = 0;
|
int in_ = 0;
|
||||||
unsigned char char_array_4[4], char_array_3[3];
|
unsigned char char_array_4[4], char_array_3[3];
|
||||||
std::string ret;
|
|
||||||
|
|
||||||
while (in_len-- && ( encoded_string[in_] != '=')) {
|
while (in_len-- && ( encoded_string[in_] != '=')) {
|
||||||
if (is_whitespace( encoded_string[in_])) {
|
if (is_whitespace( encoded_string[in_])) {
|
||||||
@@ -373,14 +451,14 @@ std::string decodeBase64(const std::string& encoded_string)
|
|||||||
char_array_4[i++] = encoded_string[in_]; in_++;
|
char_array_4[i++] = encoded_string[in_]; in_++;
|
||||||
if (i ==4) {
|
if (i ==4) {
|
||||||
for (i = 0; i <4; i++)
|
for (i = 0; i <4; i++)
|
||||||
char_array_4[i] = base64_chars.find(char_array_4[i]);
|
char_array_4[i] = base64_decode_map[char_array_4[i]];
|
||||||
|
|
||||||
char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
|
char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
|
||||||
char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
|
char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
|
||||||
char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
|
char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
|
||||||
|
|
||||||
for (i = 0; (i < 3); i++)
|
for (i = 0; (i < 3); i++)
|
||||||
ret += char_array_3[i];
|
ret.push_back(char_array_3[i]);
|
||||||
i = 0;
|
i = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -390,16 +468,14 @@ std::string decodeBase64(const std::string& encoded_string)
|
|||||||
char_array_4[j] = 0;
|
char_array_4[j] = 0;
|
||||||
|
|
||||||
for (j = 0; j <4; j++)
|
for (j = 0; j <4; j++)
|
||||||
char_array_4[j] = base64_chars.find(char_array_4[j]);
|
char_array_4[j] = base64_decode_map[char_array_4[j]];
|
||||||
|
|
||||||
char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
|
char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
|
||||||
char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
|
char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
|
||||||
char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
|
char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
|
||||||
|
|
||||||
for (j = 0; (j < i - 1); j++) ret += char_array_3[j];
|
for (j = 0; (j < i - 1); j++) ret.push_back(char_array_3[j]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const char hexChar[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
|
const char hexChar[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
|
||||||
@@ -484,6 +560,17 @@ std::string unescape(const char* s)
|
|||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
string sanitizePrintfFormat(const string& input)
|
||||||
|
{
|
||||||
|
string::size_type i = input.find("%n");
|
||||||
|
if (i != string::npos) {
|
||||||
|
SG_LOG(SG_IO, SG_WARN, "sanitizePrintfFormat: bad format string:" << input);
|
||||||
|
return string();
|
||||||
|
}
|
||||||
|
|
||||||
|
return input;
|
||||||
|
}
|
||||||
|
|
||||||
} // end namespace strutils
|
} // end namespace strutils
|
||||||
|
|
||||||
} // end namespace simgear
|
} // end namespace simgear
|
||||||
|
|||||||
@@ -38,6 +38,11 @@ typedef std::vector < std::string > string_list;
|
|||||||
namespace simgear {
|
namespace simgear {
|
||||||
namespace strutils {
|
namespace strutils {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* utf8ToLatin1() convert utf8 to latin, useful for accent character (i.e éâàîè...)
|
||||||
|
*/
|
||||||
|
std::string utf8ToLatin1( std::string & s_utf8 );
|
||||||
|
|
||||||
// /**
|
// /**
|
||||||
// * atof() wrapper for "string" type
|
// * atof() wrapper for "string" type
|
||||||
// */
|
// */
|
||||||
@@ -137,7 +142,7 @@ namespace simgear {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Like strcmp(), but for dotted versions strings NN.NN.NN
|
* Like strcmp(), but for dotted versions strings NN.NN.NN
|
||||||
* any number of terms are support.
|
* any number of terms are supported.
|
||||||
* @return 0 if versions match, -ve number if v1 is lower, +ve if v1
|
* @return 0 if versions match, -ve number if v1 is lower, +ve if v1
|
||||||
* is greater
|
* is greater
|
||||||
*/
|
*/
|
||||||
@@ -149,18 +154,34 @@ namespace simgear {
|
|||||||
*/
|
*/
|
||||||
std::string uppercase(const std::string &s);
|
std::string uppercase(const std::string &s);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert a string to lower case.
|
||||||
|
* @return lower case string
|
||||||
|
*/
|
||||||
|
std::string lowercase(const std::string &s);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert a string to lower case in place
|
||||||
|
*/
|
||||||
|
void lowercase(std::string &s);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* convert a string in the local Windows 8-bit encoding to UTF-8
|
* convert a string in the local Windows 8-bit encoding to UTF-8
|
||||||
* (no-op on other platforms)
|
* (no-op on other platforms)
|
||||||
*/
|
*/
|
||||||
std::string convertWindowsLocal8BitToUtf8(const std::string& a);
|
std::string convertWindowsLocal8BitToUtf8(const std::string& a);
|
||||||
|
|
||||||
|
#if defined(SG_WINDOWS)
|
||||||
|
typedef std::vector<wchar_t> WCharVec;
|
||||||
|
WCharVec convertUtf8ToWString(const std::string& a);
|
||||||
|
#endif
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* convert base-64 encoded data to raw bytes (possibly with embedded
|
* convert base-64 encoded data to raw bytes (possibly with embedded
|
||||||
* NULs). Throws an exception if input data is not base64, or is
|
* NULs). Throws an exception if input data is not base64, or is
|
||||||
* malformed
|
* malformed
|
||||||
*/
|
*/
|
||||||
std::string decodeBase64(const std::string& a);
|
void decodeBase64(const std::string& a, std::vector<unsigned char>& output);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* convert bytes to hexadecimal equivalent
|
* convert bytes to hexadecimal equivalent
|
||||||
@@ -181,6 +202,13 @@ namespace simgear {
|
|||||||
inline std::string unescape(const std::string& str)
|
inline std::string unescape(const std::string& str)
|
||||||
{ return unescape(str.c_str()); }
|
{ return unescape(str.c_str()); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check a printf-style format string for dangerous (buffer-overflowing,
|
||||||
|
* memory re-writing) format tokens. If a problematic token is
|
||||||
|
* found, logs an error (SG_WARN) and returns an empty format string.
|
||||||
|
*/
|
||||||
|
std::string sanitizePrintfFormat(const std::string& input);
|
||||||
|
|
||||||
} // end namespace strutils
|
} // end namespace strutils
|
||||||
} // end namespace simgear
|
} // end namespace simgear
|
||||||
|
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user