From d2d9a46beffbddab277fa24dfc0be3f34af41aea Mon Sep 17 00:00:00 2001 From: Nesterenko Vladyslav Date: Thu, 25 Jun 2026 03:48:54 +0200 Subject: [PATCH] chore: migrate code from IFloor, update OpenCV and fix build errors --- .github/workflows/wxOSX_arm64.yml | 144 +- .gitignore | 33 + CommonPluginBase/CMakeLists.txt | 40 + CommonPluginBase/CommonConfigWindowBase.cpp | 44 + CommonPluginBase/CommonConfigWindowBase.h | 37 + CommonPluginBase/CommonPlugin.h | 14 + CommonPluginBase/SerializableBase.cpp | 63 + CommonPluginBase/SerializableBase.h | 42 + CommonPluginBase/Win/CommonPluginBase.vcxproj | 260 + .../Win/CommonPluginBase.vcxproj.filters | 51 + MotionDetectorCore/AmplifyFilter.cpp | 55 + MotionDetectorCore/AmplifyFilter.h | 28 + MotionDetectorCore/BackgroundFilter.cpp | 202 + MotionDetectorCore/BackgroundFilter.h | 37 + .../BarrelDistortionCorrectionFilter.cpp | 163 + .../BarrelDistortionCorrectionFilter.h | 44 + .../BrightnessContrastFilter.cpp | 141 + MotionDetectorCore/BrightnessContrastFilter.h | 43 + MotionDetectorCore/CMakeLists.txt | 149 + MotionDetectorCore/ContourFinder.cpp | 180 + MotionDetectorCore/ContourFinder.h | 69 + MotionDetectorCore/CropFilter.cpp | 169 + MotionDetectorCore/CropFilter.h | 35 + MotionDetectorCore/DisplayGeometryWindow.cpp | 135 + MotionDetectorCore/DisplayGeometryWindow.h | 39 + MotionDetectorCore/DummyFilter.cpp | 28 + MotionDetectorCore/DummyFilter.h | 29 + MotionDetectorCore/DynamicBGFilter.cpp | 140 + MotionDetectorCore/DynamicBGFilter.h | 41 + MotionDetectorCore/EqualizeHistFilter.cpp | 61 + MotionDetectorCore/EqualizeHistFilter.h | 28 + MotionDetectorCore/FilterFactory.cpp | 41 + MotionDetectorCore/FilterFactory.h | 12 + MotionDetectorCore/FilterPreviewWindow.cpp | 100 + MotionDetectorCore/FilterPreviewWindow.h | 34 + MotionDetectorCore/FilterTemplate.cpp | 218 + MotionDetectorCore/FilterTemplate.h | 96 + MotionDetectorCore/FlipFilter.cpp | 89 + MotionDetectorCore/FlipFilter.h | 33 + MotionDetectorCore/HighpassFilter.cpp | 126 + MotionDetectorCore/HighpassFilter.h | 35 + .../IFloorImagePreprocessor.cpp | 178 + MotionDetectorCore/IFloorImagePreprocessor.h | 56 + .../IFloorImagePreprocessorSettings.cpp | 63 + .../IFloorImagePreprocessorSettings.h | 29 + MotionDetectorCore/Image.h | 62 + MotionDetectorCore/InvertFilter.cpp | 35 + MotionDetectorCore/InvertFilter.h | 24 + MotionDetectorCore/KinectBGFilter.cpp | 138 + MotionDetectorCore/KinectBGFilter.h | 42 + MotionDetectorCore/KinectDepthBGFilter.cpp | 91 + MotionDetectorCore/KinectDepthBGFilter.h | 30 + MotionDetectorCore/KinectMonoFilter.cpp | 77 + MotionDetectorCore/KinectMonoFilter.h | 23 + MotionDetectorCore/KinectThresholdFilter.cpp | 93 + MotionDetectorCore/KinectThresholdFilter.h | 33 + MotionDetectorCore/LinuxUtils.cpp | 11 + MotionDetectorCore/LinuxUtils.h | 4 + MotionDetectorCore/MonoFilter.cpp | 67 + MotionDetectorCore/MonoFilter.h | 26 + MotionDetectorCore/MonoKinectFilter.cpp | 0 MotionDetectorCore/MotionDetectorCorePlugin.h | 10 + MotionDetectorCore/PerspectiveFilter.cpp | 128 + MotionDetectorCore/PerspectiveFilter.h | 35 + MotionDetectorCore/RectifyFilter.cpp | 90 + MotionDetectorCore/RectifyFilter.h | 32 + MotionDetectorCore/ResizeFilter.cpp | 66 + MotionDetectorCore/ResizeFilter.h | 29 + MotionDetectorCore/ScalerFilter.cpp | 50 + MotionDetectorCore/ScalerFilter.h | 29 + MotionDetectorCore/ShapeFilter.cpp | 102 + MotionDetectorCore/ShapeFilter.h | 36 + MotionDetectorCore/SimpleBGFilter.cpp | 155 + MotionDetectorCore/SimpleBGFilter.h | 43 + MotionDetectorCore/SimpleHighpassFilter.cpp | 98 + MotionDetectorCore/SimpleHighpassFilter.h | 50 + MotionDetectorCore/SmoothingFilter.cpp | 70 + MotionDetectorCore/SmoothingFilter.h | 25 + MotionDetectorCore/SubOrBGFilter.cpp | 151 + MotionDetectorCore/SubOrBGFilter.h | 43 + MotionDetectorCore/ThresholdFilter.cpp | 58 + MotionDetectorCore/ThresholdFilter.h | 30 + MotionDetectorCore/Tracking.cpp | 219 + MotionDetectorCore/Tracking.h | 31 + .../Win/MotionDetectorCore.vcxproj | 481 + .../Win/MotionDetectorCore.vcxproj.filters | 273 + MotionDetectorCore/XorBGFilter.cpp | 146 + MotionDetectorCore/XorBGFilter.h | 43 + MotionDetectorCore/vector2d.h | 253 + MotionDetectorCore/wxBitmapEvent.h | 52 + MotionDetectorMultiCam/CMakeLists.txt | 69 + .../MotionDetectorMultiCam.cpp | 261 + .../MotionDetectorMultiCam.def | 6 + .../MotionDetectorMultiCam.h | 60 + .../MotionDetectorMultiCam.pjd | 1178 +- .../MotionDetectorMultiCamExports.cpp | 14 + .../MotionDetectorMultiCamGuiPlugin.cpp | 32 + .../MotionDetectorMultiCamGuiPlugin.h | 14 + .../MotionDetectorMultiCamPlugin.cpp | 36 + .../MotionDetectorMultiCamPlugin.h | 21 + .../MultiCamCameraManager.cpp | 234 + .../MultiCamCameraManager.h | 70 + .../MultiCamSettingsPanel.cpp | 390 + .../MultiCamSettingsPanel.h | 112 + MotionDetectorMultiCam/Win/ALL_BUILD.vcxproj | 123 + .../Win/ALL_BUILD.vcxproj.filters | 8 + .../Win/MotionDetectorMultiCam.sln | 175 + .../Win/MotionDetectorMultiCam.vcxproj | 325 + .../MotionDetectorMultiCam.vcxproj.filters | 48 + .../Win/MotionDetectorMultiCamGui.vcxproj | 325 + .../MotionDetectorMultiCamGui.vcxproj.filters | 42 + MotionDetectorPluginBase/CMakeLists.txt | 86 + MotionDetectorPluginBase/GeometryProvider.cpp | 28 + MotionDetectorPluginBase/GeometryProvider.h | 26 + .../IFloorCameraSettings.cpp | 91 + .../IFloorCameraSettings.h | 51 + .../MotionDetectorBase.cpp | 109 + MotionDetectorPluginBase/MotionDetectorBase.h | 68 + .../MotionDetectorConfigWindowBase.cpp | 50 + .../MotionDetectorConfigWindowBase.h | 32 + .../MotionDetectorPlugin.h | 14 + .../MotionDetectorPluginBase.cpp | 14 + .../MotionDetectorPluginBase.h | 28 + .../Win/MotionDetectorPluginBase.vcxproj | 283 + .../MotionDetectorPluginBase.vcxproj.filters | 69 + SampleGuiPlugin1/CMakeLists.txt | 78 - SampleGuiPlugin1/SampleGuiPlugin1.cpp | 35 - SampleGuiPlugin1/SampleGuiPlugin1.def | 5 - SampleGuiPlugin1/SampleGuiPlugin1.h | 16 - SampleGuiPlugin1/SampleGuiPlugin1.rc | 1 - SampleGuiPlugin1/SampleGuiPlugin1Exports.cpp | 13 - SampleGuiPlugin1/SampleGuiPluginWindow1.cpp | 192 - SampleGuiPlugin1/SampleGuiPluginWindow1.h | 98 - SampleGuiPlugin2/CMakeLists.txt | 79 - SampleGuiPlugin2/SampleGuiPlugin2.cpp | 35 - SampleGuiPlugin2/SampleGuiPlugin2.h | 16 - SampleGuiPlugin2/SampleGuiPlugin2.pjd | 506 - SampleGuiPlugin2/SampleGuiPlugin2.rc | 1 - SampleGuiPlugin2/SampleGuiPlugin2Exports.cpp | 13 - SampleGuiPlugin2/SampleGuiPluginWindow2.cpp | 190 - SampleGuiPlugin2/SampleGuiPluginWindow2.h | 98 - SampleNonGuiPlugin/CMakeLists.txt | 81 - SampleNonGuiPlugin/SampleNonGuiPlugin.cpp | 17 - SampleNonGuiPlugin/SampleNonGuiPlugin.def | 5 - SampleNonGuiPlugin/SampleNonGuiPlugin.h | 13 - .../SampleNonGuiPluginExports.cpp | 13 - ThirdParty/AppIndicatorHelper/AIHTest.cpp | 158 + ThirdParty/AppIndicatorHelper/AIHTest.h | 47 + ThirdParty/AppIndicatorHelper/CMakeLists.txt | 29 + .../Win/AppIndicatorHelper.vcxproj | 129 + .../Win/AppIndicatorHelper.vcxproj.filters | 21 + ThirdParty/AppIndicatorHelper/appindic.cpp | 171 + ThirdParty/AppIndicatorHelper/appindic.h | 156 + ThirdParty/AppIndicatorHelper/build.sh | 20 + ThirdParty/AppIndicatorHelper/gpl-2.0.txt | 339 + ThirdParty/AppIndicatorHelper/kde.png | Bin 0 -> 1467 bytes ThirdParty/AppIndicatorHelper/lgpl-2.1.txt | 502 + ThirdParty/AppIndicatorHelper/stop.xpm | 400 + ThirdParty/AppIndicatorHelper/tags | 175 + .../api-ms-win-appmodel-runtime-l1-1-0.dll | 0 .../api-ms-win-core-winrt-error-l1-1-0.dll | 0 .../arm/api-ms-win-core-winrt-l1-1-0.dll | 0 .../api-ms-win-core-winrt-robuffer-l1-1-0.dll | 0 .../api-ms-win-core-winrt-string-l1-1-0.dll | 0 .../arm/api-ms-win-shcore-scaling-l1-1-1.dll | 0 ThirdParty/DatabaseLayer/CMakeLists.txt | 54 + .../DatabaseLayer/Win/DatabaseLayer.vcxproj | 155 + .../Win/DatabaseLayer.vcxproj.filters | 90 + .../include/DatabaseErrorCodes.h | 23 + .../include/DatabaseErrorReporter.h | 43 + .../DatabaseLayer/include/DatabaseLayer.h | 182 + .../DatabaseLayer/include/DatabaseLayerDef.h | 14 + .../include/DatabaseLayerException.h | 26 + .../include/DatabaseQueryParser.h | 17 + .../DatabaseLayer/include/DatabaseResultSet.h | 95 + .../include/DatabaseStringConverter.h | 43 + .../DatabaseLayer/include/PreparedStatement.h | 82 + .../DatabaseLayer/include/ResultSetMetaData.h | 43 + .../include/SqliteDatabaseLayer.h | 73 + .../include/SqlitePreparedStatement.h | 63 + .../DatabaseLayer/include/SqliteResultSet.h | 50 + .../include/SqliteResultSetMetaData.h | 34 + ThirdParty/DatabaseLayer/sqlite3/sqlite3.c | 104497 +++++++++++++++ ThirdParty/DatabaseLayer/sqlite3/sqlite3.h | 5487 + .../src/DatabaseErrorReporter.cpp | 47 + .../DatabaseLayer/src/DatabaseLayer.cpp | 920 + .../DatabaseLayer/src/DatabaseQueryParser.cpp | 41 + .../DatabaseLayer/src/DatabaseResultSet.cpp | 133 + .../src/DatabaseStringConverter.cpp | 99 + .../DatabaseLayer/src/PreparedStatement.cpp | 58 + .../DatabaseLayer/src/SqliteDatabaseLayer.cpp | 632 + .../src/SqlitePreparedStatement.cpp | 307 + .../DatabaseLayer/src/SqliteResultSet.cpp | 220 + .../src/SqliteResultSetMetaData.cpp | 82 + ThirdParty/GeoSDK/DMIPC/include/IDMIPC.h | 157 + .../GeoSDK/DMIPC/include/IDMIPCClient.h | 196 + ThirdParty/GeoSDK/DMIPC/include/IPC_define.h | 339 + ThirdParty/GeoSDK/DMIPC/include/IPC_struct.h | 851 + .../GeoSDK/DMIPC/include/geoIPC_define.h | 26 + ThirdParty/GeoSDK/DMIPC/libWin/DMIPC.lib | Bin 0 -> 2840 bytes ThirdParty/MotionPrimitives/CMakeLists.txt | 21 + .../Win/MotionPrimitives.vcxproj | 131 + .../Win/MotionPrimitives.vcxproj.filters | 27 + ThirdParty/MotionPrimitives/iFloorBlob.cpp | 38 + ThirdParty/MotionPrimitives/iFloorBlob.h | 30 + .../MotionPrimitives/iFloorMotionTypes.cpp | 201 + .../MotionPrimitives/iFloorMotionTypes.h | 96 + ThirdParty/strmbas/CMakeLists.txt | 73 + ThirdParty/strmbas/Win/strmbas.vcxproj | 191 + .../strmbas/Win/strmbas.vcxproj.filters | 180 + ThirdParty/strmbas/activex.rcv | 142 + ThirdParty/strmbas/activex.ver | 56 + ThirdParty/strmbas/amextra.cpp | 111 + ThirdParty/strmbas/amextra.h | 56 + ThirdParty/strmbas/amfilter.cpp | 5203 + ThirdParty/strmbas/amfilter.h | 1587 + ThirdParty/strmbas/amvideo.cpp | 275 + ThirdParty/strmbas/baseclasses.sln | 27 + ThirdParty/strmbas/baseclasses.vcproj | 413 + ThirdParty/strmbas/cache.h | 74 + ThirdParty/strmbas/combase.cpp | 256 + ThirdParty/strmbas/combase.h | 319 + ThirdParty/strmbas/cprop.cpp | 380 + ThirdParty/strmbas/cprop.h | 95 + ThirdParty/strmbas/ctlutil.cpp | 2531 + ThirdParty/strmbas/ctlutil.h | 919 + ThirdParty/strmbas/ddmm.cpp | 129 + ThirdParty/strmbas/ddmm.h | 28 + ThirdParty/strmbas/dllentry.cpp | 334 + ThirdParty/strmbas/dllsetup.cpp | 727 + ThirdParty/strmbas/dllsetup.h | 46 + ThirdParty/strmbas/dsschedule.h | 128 + ThirdParty/strmbas/fourcc.h | 101 + ThirdParty/strmbas/makefile.x64 | 728 + ThirdParty/strmbas/measure.h | 222 + ThirdParty/strmbas/msgthrd.h | 120 + ThirdParty/strmbas/mtype.cpp | 477 + ThirdParty/strmbas/mtype.h | 89 + ThirdParty/strmbas/outputq.cpp | 794 + ThirdParty/strmbas/outputq.h | 137 + ThirdParty/strmbas/pstream.cpp | 196 + ThirdParty/strmbas/pstream.h | 114 + ThirdParty/strmbas/pullpin.cpp | 527 + ThirdParty/strmbas/pullpin.h | 152 + ThirdParty/strmbas/refclock.cpp | 340 + ThirdParty/strmbas/refclock.h | 171 + ThirdParty/strmbas/reftime.h | 116 + ThirdParty/strmbas/renbase.cpp | 2844 + ThirdParty/strmbas/renbase.h | 478 + ThirdParty/strmbas/schedule.cpp | 284 + ThirdParty/strmbas/schedule.h | 128 + ThirdParty/strmbas/seekpt.cpp | 83 + ThirdParty/strmbas/seekpt.h | 30 + ThirdParty/strmbas/source.cpp | 522 + ThirdParty/strmbas/source.h | 172 + ThirdParty/strmbas/streams.h | 192 + ThirdParty/strmbas/strmctl.cpp | 401 + ThirdParty/strmbas/strmctl.h | 157 + ThirdParty/strmbas/sysclock.cpp | 77 + ThirdParty/strmbas/sysclock.h | 39 + ThirdParty/strmbas/transfrm.cpp | 1016 + ThirdParty/strmbas/transfrm.h | 304 + ThirdParty/strmbas/transip.cpp | 966 + ThirdParty/strmbas/transip.h | 250 + ThirdParty/strmbas/videoctl.cpp | 715 + ThirdParty/strmbas/videoctl.h | 178 + ThirdParty/strmbas/vtrans.cpp | 468 + ThirdParty/strmbas/vtrans.h | 143 + ThirdParty/strmbas/winctrl.cpp | 2050 + ThirdParty/strmbas/winctrl.h | 224 + ThirdParty/strmbas/winutil.cpp | 2681 + ThirdParty/strmbas/winutil.h | 413 + ThirdParty/strmbas/wxdebug.cpp | 1420 + ThirdParty/strmbas/wxdebug.h | 393 + ThirdParty/strmbas/wxlist.cpp | 885 + ThirdParty/strmbas/wxlist.h | 545 + ThirdParty/strmbas/wxutil.cpp | 1256 + ThirdParty/strmbas/wxutil.h | 532 + ThirdParty/videoInput/CMakeLists.txt | 26 + ThirdParty/videoInput/Win/videoInput.vcxproj | 133 + .../videoInput/Win/videoInput.vcxproj.filters | 21 + ThirdParty/videoInput/videoInput.cpp | 2340 + ThirdParty/videoInput/videoInput.h | 385 + ThirdParty/websocket/include/base64.h | 4 + ThirdParty/websocket/include/connection.h | 120 + ThirdParty/websocket/include/frame.h | 68 + ThirdParty/websocket/include/sha1.h | 53 + ThirdParty/websocket/include/stdint.h | 232 + ThirdParty/websocket/include/tools.h | 51 + ThirdParty/websocket/include/websocket.h | 85 + ThirdParty/websocket/src/base64.cpp | 123 + ThirdParty/websocket/src/connection.cpp | 391 + ThirdParty/websocket/src/frame.cpp | 211 + ThirdParty/websocket/src/sha1.cpp | 209 + ThirdParty/websocket/src/websocket.cpp | 153 + ThirdParty/wxJSON/include/wx/json_defs.h | 212 + ThirdParty/wxJSON/include/wx/jsonreader.h | 150 + ThirdParty/wxJSON/include/wx/jsonval.h | 441 + ThirdParty/wxJSON/include/wx/jsonwriter.h | 119 + ThirdParty/wxJSON/src/jsonreader.cpp | 2132 + ThirdParty/wxJSON/src/jsonval.cpp | 3561 + ThirdParty/wxJSON/src/jsonwriter.cpp | 1274 + ThirdParty/wxTranslationHelper/CMakeLists.txt | 22 + .../Win/wxTranslationHelper.vcxproj | 129 + .../Win/wxTranslationHelper.vcxproj.filters | 21 + .../wxTranslationHelper.cpp | 191 + .../wxTranslationHelper/wxTranslationHelper.h | 26 + .../wxXS/include/wx/wxxmlserializer/Defs.h | 21 + .../include/wx/wxxmlserializer/PropertyIO.h | 313 + .../wx/wxxmlserializer/XmlSerializer.h | 1063 + ThirdParty/wxXS/src/PropertyIO.cpp | 1209 + ThirdParty/wxXS/src/XmlSerializer.cpp | 974 + ThirdParty/wxXS/src/wx_pch.cpp | 1 + ThirdParty/wxXS/src/wx_pch.h | 43 + Utils/BlobToJSONFormatter.cpp | 46 + Utils/BlobToJSONFormatter.h | 11 + Utils/CMakeLists.txt | 46 + Utils/DiskInfo.cpp | 932 + Utils/DiskInfo.h | 280 + Utils/Guid.cpp | 23 + Utils/Guid.h | 6 + Utils/JSONObjectFactory.cpp | 10 + Utils/JSONObjectFactory.h | 12 + Utils/MACAddressUtility.cpp | 214 + Utils/MACAddressUtility.h | 18 + Utils/Win/Utils.vcxproj | 235 + Utils/Win/Utils.vcxproj.filters | 72 + Utils/wxFPValidator.cpp | 265 + Utils/wxFPValidator.h | 25 + Utils/wxMACAddressUtility.h | 28 + Utils/wxTemplateClientData.h | 40 + VideoSourceDirectShow/CMakeLists.txt | 117 + VideoSourceDirectShow/DSNative/Crossbar.cpp | 235 + VideoSourceDirectShow/DSNative/Crossbar.h | 28 + VideoSourceDirectShow/DSNative/Device.cpp | 124 + VideoSourceDirectShow/DSNative/Device.h | 36 + .../DSNative/DeviceManager.cpp | 132 + .../DSNative/DeviceManager.h | 29 + .../DirectShow/Include/DSCategoryHelper.h | 64 + .../DSNative/DirectShow/Include/DSCommon.h | 96 + .../DSNative/DirectShow/Include/DSFilter.h | 52 + .../DirectShow/Include/DSFilterGraphBase.h | 100 + .../DirectShow/Include/DSFiltersDefinition.h | 243 + .../DirectShow/Include/DSObjectBase.h | 25 + .../DirectShow/Sources/DSCategoryHelper.cpp | 242 + .../DirectShow/Sources/DSFileWriter.cpp | 26 + .../DSNative/DirectShow/Sources/DSFilter.cpp | 392 + .../DirectShow/Sources/DSFilterGraphBase.cpp | 485 + .../DirectShow/Sources/DSObjectBase.cpp | 23 + .../DirectShow/Sources/DSSampleGrabber.cpp | 200 + VideoSourceDirectShow/DSNative/Format.cpp | 248 + VideoSourceDirectShow/DSNative/Format.h | 24 + .../DSNative/FrameGrabber.cpp | 251 + VideoSourceDirectShow/DSNative/FrameGrabber.h | 43 + .../VideoGrabberDirectShow.cpp | 253 + .../VideoGrabberDirectShow.h | 49 + .../VideoSourceDirectShow.def | 8 +- .../VideoSourceDirectShowApp.cpp | 9 + .../VideoSourceDirectShowApp.h | 10 + .../VideoSourceDirectShowExports.cpp | 13 + .../VideoSourceDirectShowPlugin.cpp | 90 + .../VideoSourceDirectShowPlugin.h | 31 + VideoSourceDirectShow/Win/Qedit.h | 142 + .../Win/VideoSourceDirectShow.dll | Bin 0 -> 56320 bytes .../Win/VideoSourceDirectShow.vcxproj | 392 + .../Win/VideoSourceDirectShow.vcxproj.filters | 146 + VideoSourcePluginBase/CMakeLists.txt | 48 + VideoSourcePluginBase/VideoGrabberBase.cpp | 27 + VideoSourcePluginBase/VideoGrabberBase.h | 31 + .../VideoSourceConfigWindowBase.cpp | 33 + .../VideoSourceConfigWindowBase.h | 28 + VideoSourcePluginBase/VideoSourcePlugin.h | 14 + .../VideoSourcePluginBase.cpp | 19 + VideoSourcePluginBase/VideoSourcePluginBase.h | 40 + .../Win/VideoSourcePluginBase.vcxproj | 273 + .../Win/VideoSourcePluginBase.vcxproj.filters | 57 + bin/CommonPluginBase.dll | Bin 0 -> 778240 bytes bin/MotionDetectorCore.dll | Bin 0 -> 976384 bytes bin/MotionDetectorMultiCam.dll | Bin 0 -> 294912 bytes bin/MotionDetectorMultiCamGui.dll | Bin 0 -> 305152 bytes bin/MotionDetectorPluginBase.dll | Bin 0 -> 145408 bytes bin/VideoSourceDirectShow.dll | Bin 0 -> 392192 bytes bin/VideoSourcePluginBase.dll | Bin 0 -> 111104 bytes bin/plugins/gui/MotionDetectorMultiCamGui.dll | Bin 0 -> 305152 bytes .../motion_detector/MotionDetectorCore.dll | Bin 0 -> 976384 bytes .../video_source/VideoSourceDirectShow.dll | Bin 0 -> 392192 bytes bin/wxGuiPluginBase.dll | Bin 0 -> 127488 bytes bin/wxModularHost.exe | Bin 0 -> 342016 bytes bin/wxNonGuiPluginBase.dll | Bin 0 -> 66560 bytes build/CMakeLists.txt | 123 - build/FindPackageHandleStandardArgs.cmake | 614 - build/FindPackageMessage.cmake | 49 - build/FindwxWidgets.cmake | 1245 - build/PCHSupport.cmake | 336 - build/cm.bat | 25 - build/cm.sh | 7 - build/cm64.bat | 7 - build/cm86.bat | 7 - build/cmAppleMac.sh | 7 - build/cmLinux.sh | 27 - build/wxModularHost.sln32 | 89 - build/wxModularHost.sln64 | 89 - include/qedit.h | 89 + include/stdwx.cpp | 2 +- include/stdwx.h | 100 +- wxGuiPluginBase/CMakeLists.txt | 86 +- wxGuiPluginBase/Declarations.h | 28 +- wxGuiPluginBase/Win/wxGuiPluginBase.vcxproj | 256 + .../Win/wxGuiPluginBase.vcxproj.filters | 56 + wxGuiPluginBase/wxGuiPluginBase.cpp | 48 +- wxGuiPluginBase/wxGuiPluginBase.h | 48 +- wxGuiPluginBase/wxGuiPluginBase.pjd | 558 +- wxGuiPluginBase/wxGuiPluginBase.rc | 2 +- wxGuiPluginBase/wxGuiPluginWindowBase.cpp | 298 +- wxGuiPluginBase/wxGuiPluginWindowBase.h | 214 +- wxModularCore/CMakeLists.txt | 60 +- wxModularCore/Win/wxModularCore.vcxproj | 218 + .../Win/wxModularCore.vcxproj.filters | 48 + wxModularCore/wxModularCore.cpp | 114 +- wxModularCore/wxModularCore.h | 280 +- wxModularCore/wxModularCoreSettings.cpp | 82 +- wxModularCore/wxModularCoreSettings.h | 32 +- wxModularHost/CMakeLists.txt | 208 +- wxModularHost/MainFrame.cpp | 390 +- wxModularHost/MainFrame.h | 214 +- wxModularHost/SampleModularCore.cpp | 128 +- wxModularHost/SampleModularCore.h | 62 +- wxModularHost/Win/wxModularHost.vcxproj | 275 + .../Win/wxModularHost.vcxproj.filters | 59 + wxModularHost/wxModularHost.pjd | 1174 +- wxModularHost/wxModularHost.rc | 2 +- wxModularHost/wxModularHostApp.cpp | 276 +- wxModularHost/wxModularHostApp.h | 176 +- wxNonGuiPluginBase/CMakeLists.txt | 88 +- wxNonGuiPluginBase/Declarations.h | 28 +- .../Win/wxNonGuiPluginBase.vcxproj | 247 + .../Win/wxNonGuiPluginBase.vcxproj.filters | 45 + wxNonGuiPluginBase/wxNonGuiPluginBase.cpp | 24 +- wxNonGuiPluginBase/wxNonGuiPluginBase.h | 30 +- 439 files changed, 194701 insertions(+), 7182 deletions(-) create mode 100644 .gitignore create mode 100644 CommonPluginBase/CMakeLists.txt create mode 100644 CommonPluginBase/CommonConfigWindowBase.cpp create mode 100644 CommonPluginBase/CommonConfigWindowBase.h create mode 100644 CommonPluginBase/CommonPlugin.h create mode 100644 CommonPluginBase/SerializableBase.cpp create mode 100644 CommonPluginBase/SerializableBase.h create mode 100644 CommonPluginBase/Win/CommonPluginBase.vcxproj create mode 100644 CommonPluginBase/Win/CommonPluginBase.vcxproj.filters create mode 100644 MotionDetectorCore/AmplifyFilter.cpp create mode 100644 MotionDetectorCore/AmplifyFilter.h create mode 100644 MotionDetectorCore/BackgroundFilter.cpp create mode 100644 MotionDetectorCore/BackgroundFilter.h create mode 100644 MotionDetectorCore/BarrelDistortionCorrectionFilter.cpp create mode 100644 MotionDetectorCore/BarrelDistortionCorrectionFilter.h create mode 100644 MotionDetectorCore/BrightnessContrastFilter.cpp create mode 100644 MotionDetectorCore/BrightnessContrastFilter.h create mode 100644 MotionDetectorCore/CMakeLists.txt create mode 100644 MotionDetectorCore/ContourFinder.cpp create mode 100644 MotionDetectorCore/ContourFinder.h create mode 100644 MotionDetectorCore/CropFilter.cpp create mode 100644 MotionDetectorCore/CropFilter.h create mode 100644 MotionDetectorCore/DisplayGeometryWindow.cpp create mode 100644 MotionDetectorCore/DisplayGeometryWindow.h create mode 100644 MotionDetectorCore/DummyFilter.cpp create mode 100644 MotionDetectorCore/DummyFilter.h create mode 100644 MotionDetectorCore/DynamicBGFilter.cpp create mode 100644 MotionDetectorCore/DynamicBGFilter.h create mode 100644 MotionDetectorCore/EqualizeHistFilter.cpp create mode 100644 MotionDetectorCore/EqualizeHistFilter.h create mode 100644 MotionDetectorCore/FilterFactory.cpp create mode 100644 MotionDetectorCore/FilterFactory.h create mode 100644 MotionDetectorCore/FilterPreviewWindow.cpp create mode 100644 MotionDetectorCore/FilterPreviewWindow.h create mode 100644 MotionDetectorCore/FilterTemplate.cpp create mode 100644 MotionDetectorCore/FilterTemplate.h create mode 100644 MotionDetectorCore/FlipFilter.cpp create mode 100644 MotionDetectorCore/FlipFilter.h create mode 100644 MotionDetectorCore/HighpassFilter.cpp create mode 100644 MotionDetectorCore/HighpassFilter.h create mode 100644 MotionDetectorCore/IFloorImagePreprocessor.cpp create mode 100644 MotionDetectorCore/IFloorImagePreprocessor.h create mode 100644 MotionDetectorCore/IFloorImagePreprocessorSettings.cpp create mode 100644 MotionDetectorCore/IFloorImagePreprocessorSettings.h create mode 100644 MotionDetectorCore/Image.h create mode 100644 MotionDetectorCore/InvertFilter.cpp create mode 100644 MotionDetectorCore/InvertFilter.h create mode 100644 MotionDetectorCore/KinectBGFilter.cpp create mode 100644 MotionDetectorCore/KinectBGFilter.h create mode 100644 MotionDetectorCore/KinectDepthBGFilter.cpp create mode 100644 MotionDetectorCore/KinectDepthBGFilter.h create mode 100644 MotionDetectorCore/KinectMonoFilter.cpp create mode 100644 MotionDetectorCore/KinectMonoFilter.h create mode 100644 MotionDetectorCore/KinectThresholdFilter.cpp create mode 100644 MotionDetectorCore/KinectThresholdFilter.h create mode 100644 MotionDetectorCore/LinuxUtils.cpp create mode 100644 MotionDetectorCore/LinuxUtils.h create mode 100644 MotionDetectorCore/MonoFilter.cpp create mode 100644 MotionDetectorCore/MonoFilter.h create mode 100644 MotionDetectorCore/MonoKinectFilter.cpp create mode 100644 MotionDetectorCore/MotionDetectorCorePlugin.h create mode 100644 MotionDetectorCore/PerspectiveFilter.cpp create mode 100644 MotionDetectorCore/PerspectiveFilter.h create mode 100644 MotionDetectorCore/RectifyFilter.cpp create mode 100644 MotionDetectorCore/RectifyFilter.h create mode 100644 MotionDetectorCore/ResizeFilter.cpp create mode 100644 MotionDetectorCore/ResizeFilter.h create mode 100644 MotionDetectorCore/ScalerFilter.cpp create mode 100644 MotionDetectorCore/ScalerFilter.h create mode 100644 MotionDetectorCore/ShapeFilter.cpp create mode 100644 MotionDetectorCore/ShapeFilter.h create mode 100644 MotionDetectorCore/SimpleBGFilter.cpp create mode 100644 MotionDetectorCore/SimpleBGFilter.h create mode 100644 MotionDetectorCore/SimpleHighpassFilter.cpp create mode 100644 MotionDetectorCore/SimpleHighpassFilter.h create mode 100644 MotionDetectorCore/SmoothingFilter.cpp create mode 100644 MotionDetectorCore/SmoothingFilter.h create mode 100644 MotionDetectorCore/SubOrBGFilter.cpp create mode 100644 MotionDetectorCore/SubOrBGFilter.h create mode 100644 MotionDetectorCore/ThresholdFilter.cpp create mode 100644 MotionDetectorCore/ThresholdFilter.h create mode 100644 MotionDetectorCore/Tracking.cpp create mode 100644 MotionDetectorCore/Tracking.h create mode 100644 MotionDetectorCore/Win/MotionDetectorCore.vcxproj create mode 100644 MotionDetectorCore/Win/MotionDetectorCore.vcxproj.filters create mode 100644 MotionDetectorCore/XorBGFilter.cpp create mode 100644 MotionDetectorCore/XorBGFilter.h create mode 100644 MotionDetectorCore/vector2d.h create mode 100644 MotionDetectorCore/wxBitmapEvent.h create mode 100644 MotionDetectorMultiCam/CMakeLists.txt create mode 100644 MotionDetectorMultiCam/MotionDetectorMultiCam.cpp create mode 100644 MotionDetectorMultiCam/MotionDetectorMultiCam.def create mode 100644 MotionDetectorMultiCam/MotionDetectorMultiCam.h rename SampleGuiPlugin1/SampleGuiPlugin1.pjd => MotionDetectorMultiCam/MotionDetectorMultiCam.pjd (58%) create mode 100644 MotionDetectorMultiCam/MotionDetectorMultiCamExports.cpp create mode 100644 MotionDetectorMultiCam/MotionDetectorMultiCamGuiPlugin.cpp create mode 100644 MotionDetectorMultiCam/MotionDetectorMultiCamGuiPlugin.h create mode 100644 MotionDetectorMultiCam/MotionDetectorMultiCamPlugin.cpp create mode 100644 MotionDetectorMultiCam/MotionDetectorMultiCamPlugin.h create mode 100644 MotionDetectorMultiCam/MultiCamCameraManager.cpp create mode 100644 MotionDetectorMultiCam/MultiCamCameraManager.h create mode 100644 MotionDetectorMultiCam/MultiCamSettingsPanel.cpp create mode 100644 MotionDetectorMultiCam/MultiCamSettingsPanel.h create mode 100644 MotionDetectorMultiCam/Win/ALL_BUILD.vcxproj create mode 100644 MotionDetectorMultiCam/Win/ALL_BUILD.vcxproj.filters create mode 100644 MotionDetectorMultiCam/Win/MotionDetectorMultiCam.sln create mode 100644 MotionDetectorMultiCam/Win/MotionDetectorMultiCam.vcxproj create mode 100644 MotionDetectorMultiCam/Win/MotionDetectorMultiCam.vcxproj.filters create mode 100644 MotionDetectorMultiCam/Win/MotionDetectorMultiCamGui.vcxproj create mode 100644 MotionDetectorMultiCam/Win/MotionDetectorMultiCamGui.vcxproj.filters create mode 100644 MotionDetectorPluginBase/CMakeLists.txt create mode 100644 MotionDetectorPluginBase/GeometryProvider.cpp create mode 100644 MotionDetectorPluginBase/GeometryProvider.h create mode 100644 MotionDetectorPluginBase/IFloorCameraSettings.cpp create mode 100644 MotionDetectorPluginBase/IFloorCameraSettings.h create mode 100644 MotionDetectorPluginBase/MotionDetectorBase.cpp create mode 100644 MotionDetectorPluginBase/MotionDetectorBase.h create mode 100644 MotionDetectorPluginBase/MotionDetectorConfigWindowBase.cpp create mode 100644 MotionDetectorPluginBase/MotionDetectorConfigWindowBase.h create mode 100644 MotionDetectorPluginBase/MotionDetectorPlugin.h create mode 100644 MotionDetectorPluginBase/MotionDetectorPluginBase.cpp create mode 100644 MotionDetectorPluginBase/MotionDetectorPluginBase.h create mode 100644 MotionDetectorPluginBase/Win/MotionDetectorPluginBase.vcxproj create mode 100644 MotionDetectorPluginBase/Win/MotionDetectorPluginBase.vcxproj.filters delete mode 100644 SampleGuiPlugin1/CMakeLists.txt delete mode 100644 SampleGuiPlugin1/SampleGuiPlugin1.cpp delete mode 100644 SampleGuiPlugin1/SampleGuiPlugin1.def delete mode 100644 SampleGuiPlugin1/SampleGuiPlugin1.h delete mode 100755 SampleGuiPlugin1/SampleGuiPlugin1.rc delete mode 100644 SampleGuiPlugin1/SampleGuiPlugin1Exports.cpp delete mode 100644 SampleGuiPlugin1/SampleGuiPluginWindow1.cpp delete mode 100644 SampleGuiPlugin1/SampleGuiPluginWindow1.h delete mode 100644 SampleGuiPlugin2/CMakeLists.txt delete mode 100644 SampleGuiPlugin2/SampleGuiPlugin2.cpp delete mode 100644 SampleGuiPlugin2/SampleGuiPlugin2.h delete mode 100644 SampleGuiPlugin2/SampleGuiPlugin2.pjd delete mode 100755 SampleGuiPlugin2/SampleGuiPlugin2.rc delete mode 100644 SampleGuiPlugin2/SampleGuiPlugin2Exports.cpp delete mode 100644 SampleGuiPlugin2/SampleGuiPluginWindow2.cpp delete mode 100644 SampleGuiPlugin2/SampleGuiPluginWindow2.h delete mode 100644 SampleNonGuiPlugin/CMakeLists.txt delete mode 100644 SampleNonGuiPlugin/SampleNonGuiPlugin.cpp delete mode 100644 SampleNonGuiPlugin/SampleNonGuiPlugin.def delete mode 100644 SampleNonGuiPlugin/SampleNonGuiPlugin.h delete mode 100644 SampleNonGuiPlugin/SampleNonGuiPluginExports.cpp create mode 100644 ThirdParty/AppIndicatorHelper/AIHTest.cpp create mode 100644 ThirdParty/AppIndicatorHelper/AIHTest.h create mode 100644 ThirdParty/AppIndicatorHelper/CMakeLists.txt create mode 100644 ThirdParty/AppIndicatorHelper/Win/AppIndicatorHelper.vcxproj create mode 100644 ThirdParty/AppIndicatorHelper/Win/AppIndicatorHelper.vcxproj.filters create mode 100644 ThirdParty/AppIndicatorHelper/appindic.cpp create mode 100644 ThirdParty/AppIndicatorHelper/appindic.h create mode 100644 ThirdParty/AppIndicatorHelper/build.sh create mode 100644 ThirdParty/AppIndicatorHelper/gpl-2.0.txt create mode 100644 ThirdParty/AppIndicatorHelper/kde.png create mode 100644 ThirdParty/AppIndicatorHelper/lgpl-2.1.txt create mode 100644 ThirdParty/AppIndicatorHelper/stop.xpm create mode 100644 ThirdParty/AppIndicatorHelper/tags create mode 100644 ThirdParty/C++RedistributableBinaries/arm/api-ms-win-appmodel-runtime-l1-1-0.dll create mode 100644 ThirdParty/C++RedistributableBinaries/arm/api-ms-win-core-winrt-error-l1-1-0.dll create mode 100644 ThirdParty/C++RedistributableBinaries/arm/api-ms-win-core-winrt-l1-1-0.dll create mode 100644 ThirdParty/C++RedistributableBinaries/arm/api-ms-win-core-winrt-robuffer-l1-1-0.dll create mode 100644 ThirdParty/C++RedistributableBinaries/arm/api-ms-win-core-winrt-string-l1-1-0.dll create mode 100644 ThirdParty/C++RedistributableBinaries/arm/api-ms-win-shcore-scaling-l1-1-1.dll create mode 100644 ThirdParty/DatabaseLayer/CMakeLists.txt create mode 100644 ThirdParty/DatabaseLayer/Win/DatabaseLayer.vcxproj create mode 100644 ThirdParty/DatabaseLayer/Win/DatabaseLayer.vcxproj.filters create mode 100644 ThirdParty/DatabaseLayer/include/DatabaseErrorCodes.h create mode 100644 ThirdParty/DatabaseLayer/include/DatabaseErrorReporter.h create mode 100644 ThirdParty/DatabaseLayer/include/DatabaseLayer.h create mode 100644 ThirdParty/DatabaseLayer/include/DatabaseLayerDef.h create mode 100644 ThirdParty/DatabaseLayer/include/DatabaseLayerException.h create mode 100644 ThirdParty/DatabaseLayer/include/DatabaseQueryParser.h create mode 100644 ThirdParty/DatabaseLayer/include/DatabaseResultSet.h create mode 100644 ThirdParty/DatabaseLayer/include/DatabaseStringConverter.h create mode 100644 ThirdParty/DatabaseLayer/include/PreparedStatement.h create mode 100644 ThirdParty/DatabaseLayer/include/ResultSetMetaData.h create mode 100644 ThirdParty/DatabaseLayer/include/SqliteDatabaseLayer.h create mode 100644 ThirdParty/DatabaseLayer/include/SqlitePreparedStatement.h create mode 100644 ThirdParty/DatabaseLayer/include/SqliteResultSet.h create mode 100644 ThirdParty/DatabaseLayer/include/SqliteResultSetMetaData.h create mode 100644 ThirdParty/DatabaseLayer/sqlite3/sqlite3.c create mode 100644 ThirdParty/DatabaseLayer/sqlite3/sqlite3.h create mode 100644 ThirdParty/DatabaseLayer/src/DatabaseErrorReporter.cpp create mode 100644 ThirdParty/DatabaseLayer/src/DatabaseLayer.cpp create mode 100644 ThirdParty/DatabaseLayer/src/DatabaseQueryParser.cpp create mode 100644 ThirdParty/DatabaseLayer/src/DatabaseResultSet.cpp create mode 100644 ThirdParty/DatabaseLayer/src/DatabaseStringConverter.cpp create mode 100644 ThirdParty/DatabaseLayer/src/PreparedStatement.cpp create mode 100644 ThirdParty/DatabaseLayer/src/SqliteDatabaseLayer.cpp create mode 100644 ThirdParty/DatabaseLayer/src/SqlitePreparedStatement.cpp create mode 100644 ThirdParty/DatabaseLayer/src/SqliteResultSet.cpp create mode 100644 ThirdParty/DatabaseLayer/src/SqliteResultSetMetaData.cpp create mode 100644 ThirdParty/GeoSDK/DMIPC/include/IDMIPC.h create mode 100644 ThirdParty/GeoSDK/DMIPC/include/IDMIPCClient.h create mode 100644 ThirdParty/GeoSDK/DMIPC/include/IPC_define.h create mode 100644 ThirdParty/GeoSDK/DMIPC/include/IPC_struct.h create mode 100644 ThirdParty/GeoSDK/DMIPC/include/geoIPC_define.h create mode 100644 ThirdParty/GeoSDK/DMIPC/libWin/DMIPC.lib create mode 100644 ThirdParty/MotionPrimitives/CMakeLists.txt create mode 100644 ThirdParty/MotionPrimitives/Win/MotionPrimitives.vcxproj create mode 100644 ThirdParty/MotionPrimitives/Win/MotionPrimitives.vcxproj.filters create mode 100644 ThirdParty/MotionPrimitives/iFloorBlob.cpp create mode 100644 ThirdParty/MotionPrimitives/iFloorBlob.h create mode 100644 ThirdParty/MotionPrimitives/iFloorMotionTypes.cpp create mode 100644 ThirdParty/MotionPrimitives/iFloorMotionTypes.h create mode 100644 ThirdParty/strmbas/CMakeLists.txt create mode 100644 ThirdParty/strmbas/Win/strmbas.vcxproj create mode 100644 ThirdParty/strmbas/Win/strmbas.vcxproj.filters create mode 100644 ThirdParty/strmbas/activex.rcv create mode 100644 ThirdParty/strmbas/activex.ver create mode 100644 ThirdParty/strmbas/amextra.cpp create mode 100644 ThirdParty/strmbas/amextra.h create mode 100644 ThirdParty/strmbas/amfilter.cpp create mode 100644 ThirdParty/strmbas/amfilter.h create mode 100644 ThirdParty/strmbas/amvideo.cpp create mode 100644 ThirdParty/strmbas/baseclasses.sln create mode 100644 ThirdParty/strmbas/baseclasses.vcproj create mode 100644 ThirdParty/strmbas/cache.h create mode 100644 ThirdParty/strmbas/combase.cpp create mode 100644 ThirdParty/strmbas/combase.h create mode 100644 ThirdParty/strmbas/cprop.cpp create mode 100644 ThirdParty/strmbas/cprop.h create mode 100644 ThirdParty/strmbas/ctlutil.cpp create mode 100644 ThirdParty/strmbas/ctlutil.h create mode 100644 ThirdParty/strmbas/ddmm.cpp create mode 100644 ThirdParty/strmbas/ddmm.h create mode 100644 ThirdParty/strmbas/dllentry.cpp create mode 100644 ThirdParty/strmbas/dllsetup.cpp create mode 100644 ThirdParty/strmbas/dllsetup.h create mode 100644 ThirdParty/strmbas/dsschedule.h create mode 100644 ThirdParty/strmbas/fourcc.h create mode 100644 ThirdParty/strmbas/makefile.x64 create mode 100644 ThirdParty/strmbas/measure.h create mode 100644 ThirdParty/strmbas/msgthrd.h create mode 100644 ThirdParty/strmbas/mtype.cpp create mode 100644 ThirdParty/strmbas/mtype.h create mode 100644 ThirdParty/strmbas/outputq.cpp create mode 100644 ThirdParty/strmbas/outputq.h create mode 100644 ThirdParty/strmbas/pstream.cpp create mode 100644 ThirdParty/strmbas/pstream.h create mode 100644 ThirdParty/strmbas/pullpin.cpp create mode 100644 ThirdParty/strmbas/pullpin.h create mode 100644 ThirdParty/strmbas/refclock.cpp create mode 100644 ThirdParty/strmbas/refclock.h create mode 100644 ThirdParty/strmbas/reftime.h create mode 100644 ThirdParty/strmbas/renbase.cpp create mode 100644 ThirdParty/strmbas/renbase.h create mode 100644 ThirdParty/strmbas/schedule.cpp create mode 100644 ThirdParty/strmbas/schedule.h create mode 100644 ThirdParty/strmbas/seekpt.cpp create mode 100644 ThirdParty/strmbas/seekpt.h create mode 100644 ThirdParty/strmbas/source.cpp create mode 100644 ThirdParty/strmbas/source.h create mode 100644 ThirdParty/strmbas/streams.h create mode 100644 ThirdParty/strmbas/strmctl.cpp create mode 100644 ThirdParty/strmbas/strmctl.h create mode 100644 ThirdParty/strmbas/sysclock.cpp create mode 100644 ThirdParty/strmbas/sysclock.h create mode 100644 ThirdParty/strmbas/transfrm.cpp create mode 100644 ThirdParty/strmbas/transfrm.h create mode 100644 ThirdParty/strmbas/transip.cpp create mode 100644 ThirdParty/strmbas/transip.h create mode 100644 ThirdParty/strmbas/videoctl.cpp create mode 100644 ThirdParty/strmbas/videoctl.h create mode 100644 ThirdParty/strmbas/vtrans.cpp create mode 100644 ThirdParty/strmbas/vtrans.h create mode 100644 ThirdParty/strmbas/winctrl.cpp create mode 100644 ThirdParty/strmbas/winctrl.h create mode 100644 ThirdParty/strmbas/winutil.cpp create mode 100644 ThirdParty/strmbas/winutil.h create mode 100644 ThirdParty/strmbas/wxdebug.cpp create mode 100644 ThirdParty/strmbas/wxdebug.h create mode 100644 ThirdParty/strmbas/wxlist.cpp create mode 100644 ThirdParty/strmbas/wxlist.h create mode 100644 ThirdParty/strmbas/wxutil.cpp create mode 100644 ThirdParty/strmbas/wxutil.h create mode 100644 ThirdParty/videoInput/CMakeLists.txt create mode 100644 ThirdParty/videoInput/Win/videoInput.vcxproj create mode 100644 ThirdParty/videoInput/Win/videoInput.vcxproj.filters create mode 100644 ThirdParty/videoInput/videoInput.cpp create mode 100644 ThirdParty/videoInput/videoInput.h create mode 100644 ThirdParty/websocket/include/base64.h create mode 100644 ThirdParty/websocket/include/connection.h create mode 100644 ThirdParty/websocket/include/frame.h create mode 100644 ThirdParty/websocket/include/sha1.h create mode 100644 ThirdParty/websocket/include/stdint.h create mode 100644 ThirdParty/websocket/include/tools.h create mode 100644 ThirdParty/websocket/include/websocket.h create mode 100644 ThirdParty/websocket/src/base64.cpp create mode 100644 ThirdParty/websocket/src/connection.cpp create mode 100644 ThirdParty/websocket/src/frame.cpp create mode 100644 ThirdParty/websocket/src/sha1.cpp create mode 100644 ThirdParty/websocket/src/websocket.cpp create mode 100644 ThirdParty/wxJSON/include/wx/json_defs.h create mode 100644 ThirdParty/wxJSON/include/wx/jsonreader.h create mode 100644 ThirdParty/wxJSON/include/wx/jsonval.h create mode 100644 ThirdParty/wxJSON/include/wx/jsonwriter.h create mode 100644 ThirdParty/wxJSON/src/jsonreader.cpp create mode 100644 ThirdParty/wxJSON/src/jsonval.cpp create mode 100644 ThirdParty/wxJSON/src/jsonwriter.cpp create mode 100644 ThirdParty/wxTranslationHelper/CMakeLists.txt create mode 100644 ThirdParty/wxTranslationHelper/Win/wxTranslationHelper.vcxproj create mode 100644 ThirdParty/wxTranslationHelper/Win/wxTranslationHelper.vcxproj.filters create mode 100644 ThirdParty/wxTranslationHelper/wxTranslationHelper.cpp create mode 100644 ThirdParty/wxTranslationHelper/wxTranslationHelper.h create mode 100644 ThirdParty/wxXS/include/wx/wxxmlserializer/Defs.h create mode 100644 ThirdParty/wxXS/include/wx/wxxmlserializer/PropertyIO.h create mode 100644 ThirdParty/wxXS/include/wx/wxxmlserializer/XmlSerializer.h create mode 100644 ThirdParty/wxXS/src/PropertyIO.cpp create mode 100644 ThirdParty/wxXS/src/XmlSerializer.cpp create mode 100644 ThirdParty/wxXS/src/wx_pch.cpp create mode 100644 ThirdParty/wxXS/src/wx_pch.h create mode 100644 Utils/BlobToJSONFormatter.cpp create mode 100644 Utils/BlobToJSONFormatter.h create mode 100644 Utils/CMakeLists.txt create mode 100644 Utils/DiskInfo.cpp create mode 100644 Utils/DiskInfo.h create mode 100644 Utils/Guid.cpp create mode 100644 Utils/Guid.h create mode 100644 Utils/JSONObjectFactory.cpp create mode 100644 Utils/JSONObjectFactory.h create mode 100644 Utils/MACAddressUtility.cpp create mode 100644 Utils/MACAddressUtility.h create mode 100644 Utils/Win/Utils.vcxproj create mode 100644 Utils/Win/Utils.vcxproj.filters create mode 100644 Utils/wxFPValidator.cpp create mode 100644 Utils/wxFPValidator.h create mode 100644 Utils/wxMACAddressUtility.h create mode 100644 Utils/wxTemplateClientData.h create mode 100644 VideoSourceDirectShow/CMakeLists.txt create mode 100644 VideoSourceDirectShow/DSNative/Crossbar.cpp create mode 100644 VideoSourceDirectShow/DSNative/Crossbar.h create mode 100644 VideoSourceDirectShow/DSNative/Device.cpp create mode 100644 VideoSourceDirectShow/DSNative/Device.h create mode 100644 VideoSourceDirectShow/DSNative/DeviceManager.cpp create mode 100644 VideoSourceDirectShow/DSNative/DeviceManager.h create mode 100644 VideoSourceDirectShow/DSNative/DirectShow/Include/DSCategoryHelper.h create mode 100644 VideoSourceDirectShow/DSNative/DirectShow/Include/DSCommon.h create mode 100644 VideoSourceDirectShow/DSNative/DirectShow/Include/DSFilter.h create mode 100644 VideoSourceDirectShow/DSNative/DirectShow/Include/DSFilterGraphBase.h create mode 100644 VideoSourceDirectShow/DSNative/DirectShow/Include/DSFiltersDefinition.h create mode 100644 VideoSourceDirectShow/DSNative/DirectShow/Include/DSObjectBase.h create mode 100644 VideoSourceDirectShow/DSNative/DirectShow/Sources/DSCategoryHelper.cpp create mode 100644 VideoSourceDirectShow/DSNative/DirectShow/Sources/DSFileWriter.cpp create mode 100644 VideoSourceDirectShow/DSNative/DirectShow/Sources/DSFilter.cpp create mode 100644 VideoSourceDirectShow/DSNative/DirectShow/Sources/DSFilterGraphBase.cpp create mode 100644 VideoSourceDirectShow/DSNative/DirectShow/Sources/DSObjectBase.cpp create mode 100644 VideoSourceDirectShow/DSNative/DirectShow/Sources/DSSampleGrabber.cpp create mode 100644 VideoSourceDirectShow/DSNative/Format.cpp create mode 100644 VideoSourceDirectShow/DSNative/Format.h create mode 100644 VideoSourceDirectShow/DSNative/FrameGrabber.cpp create mode 100644 VideoSourceDirectShow/DSNative/FrameGrabber.h create mode 100644 VideoSourceDirectShow/VideoGrabberDirectShow.cpp create mode 100644 VideoSourceDirectShow/VideoGrabberDirectShow.h rename SampleGuiPlugin2/SampleGuiPlugin2.def => VideoSourceDirectShow/VideoSourceDirectShow.def (65%) create mode 100644 VideoSourceDirectShow/VideoSourceDirectShowApp.cpp create mode 100644 VideoSourceDirectShow/VideoSourceDirectShowApp.h create mode 100644 VideoSourceDirectShow/VideoSourceDirectShowExports.cpp create mode 100644 VideoSourceDirectShow/VideoSourceDirectShowPlugin.cpp create mode 100644 VideoSourceDirectShow/VideoSourceDirectShowPlugin.h create mode 100644 VideoSourceDirectShow/Win/Qedit.h create mode 100644 VideoSourceDirectShow/Win/VideoSourceDirectShow.dll create mode 100644 VideoSourceDirectShow/Win/VideoSourceDirectShow.vcxproj create mode 100644 VideoSourceDirectShow/Win/VideoSourceDirectShow.vcxproj.filters create mode 100644 VideoSourcePluginBase/CMakeLists.txt create mode 100644 VideoSourcePluginBase/VideoGrabberBase.cpp create mode 100644 VideoSourcePluginBase/VideoGrabberBase.h create mode 100644 VideoSourcePluginBase/VideoSourceConfigWindowBase.cpp create mode 100644 VideoSourcePluginBase/VideoSourceConfigWindowBase.h create mode 100644 VideoSourcePluginBase/VideoSourcePlugin.h create mode 100644 VideoSourcePluginBase/VideoSourcePluginBase.cpp create mode 100644 VideoSourcePluginBase/VideoSourcePluginBase.h create mode 100644 VideoSourcePluginBase/Win/VideoSourcePluginBase.vcxproj create mode 100644 VideoSourcePluginBase/Win/VideoSourcePluginBase.vcxproj.filters create mode 100644 bin/CommonPluginBase.dll create mode 100644 bin/MotionDetectorCore.dll create mode 100644 bin/MotionDetectorMultiCam.dll create mode 100644 bin/MotionDetectorMultiCamGui.dll create mode 100644 bin/MotionDetectorPluginBase.dll create mode 100644 bin/VideoSourceDirectShow.dll create mode 100644 bin/VideoSourcePluginBase.dll create mode 100644 bin/plugins/gui/MotionDetectorMultiCamGui.dll create mode 100644 bin/plugins/motion_detector/MotionDetectorCore.dll create mode 100644 bin/plugins/video_source/VideoSourceDirectShow.dll create mode 100644 bin/wxGuiPluginBase.dll create mode 100644 bin/wxModularHost.exe create mode 100644 bin/wxNonGuiPluginBase.dll delete mode 100644 build/CMakeLists.txt delete mode 100644 build/FindPackageHandleStandardArgs.cmake delete mode 100644 build/FindPackageMessage.cmake delete mode 100644 build/FindwxWidgets.cmake delete mode 100644 build/PCHSupport.cmake delete mode 100644 build/cm.bat delete mode 100755 build/cm.sh delete mode 100644 build/cm64.bat delete mode 100644 build/cm86.bat delete mode 100644 build/cmAppleMac.sh delete mode 100755 build/cmLinux.sh delete mode 100644 build/wxModularHost.sln32 delete mode 100644 build/wxModularHost.sln64 create mode 100644 include/qedit.h create mode 100644 wxGuiPluginBase/Win/wxGuiPluginBase.vcxproj create mode 100644 wxGuiPluginBase/Win/wxGuiPluginBase.vcxproj.filters mode change 100755 => 100644 wxGuiPluginBase/wxGuiPluginBase.rc mode change 100755 => 100644 wxGuiPluginBase/wxGuiPluginWindowBase.cpp mode change 100755 => 100644 wxGuiPluginBase/wxGuiPluginWindowBase.h create mode 100644 wxModularCore/Win/wxModularCore.vcxproj create mode 100644 wxModularCore/Win/wxModularCore.vcxproj.filters create mode 100644 wxModularHost/Win/wxModularHost.vcxproj create mode 100644 wxModularHost/Win/wxModularHost.vcxproj.filters mode change 100755 => 100644 wxModularHost/wxModularHost.rc create mode 100644 wxNonGuiPluginBase/Win/wxNonGuiPluginBase.vcxproj create mode 100644 wxNonGuiPluginBase/Win/wxNonGuiPluginBase.vcxproj.filters diff --git a/.github/workflows/wxOSX_arm64.yml b/.github/workflows/wxOSX_arm64.yml index c148811..a6e6435 100644 --- a/.github/workflows/wxOSX_arm64.yml +++ b/.github/workflows/wxOSX_arm64.yml @@ -1,73 +1,73 @@ -name: wxOSX_arm64 - -on: -# push: -# branches: '*' -# pull_request: -# branches: '*' -# workflow_dispatch: -# branches: '*' +name: wxOSX_arm64 + +on: +# push: +# branches: '*' +# pull_request: +# branches: '*' +# workflow_dispatch: +# branches: '*' workflow_dispatch: - -jobs: - build: - - runs-on: macos-14 - - steps: - - name: Install boost - uses: MarkusJx/install-boost@v2.4.5 - id: install-boost - with: - # REQUIRED: Specify the required boost version - # A list of supported versions can be found here: - # https://github.com/MarkusJx/prebuilt-boost/blob/main/versions-manifest.json - boost_version: 1.86.0 - boost_install_dir: /tmp/wxModularAppOCX/boost/ - - - uses: actions/checkout@v4 - - name: Install wxWidgets - run: | - curl -L https://download.osgeo.org/libtiff/tiff-4.6.0.tar.gz --output tiff-4.6.0.tar.gz - tar xzf tiff-4.6.0.tar.gz - cd tiff-4.6.0 - ./configure - make -j$(getconf _NPROCESSORS_ONLN) - sudo make install - cd .. - curl -L https://github.com/wxWidgets/wxWidgets/releases/download/v3.2.5/wxWidgets-3.2.5.tar.bz2 --output wxWidgets-3.2.5.tar.bz2 - tar xjf wxWidgets-3.2.5.tar.bz2 - cd wxwidgets-3.2.5 - mkdir buildOSX - cd buildOSX - ../configure --enable-debug - make -j$(getconf _NPROCESSORS_ONLN) - sudo make install - cd ../.. - wx-config --list - wx-config --cxxflags - wx-config --libs - - - name: Run CMake - run: | - cd build - chmod +x ./cmAppleMac.sh - ./cmAppleMac.sh - cd .. - env: - BOOST_ROOT: ${{ steps.install-boost.outputs.BOOST_ROOT }} - - - name: Run GNU Make with GNU compiler - run: | - cd build - mkdir -p bin - mkdir -p bin/Debug - cd Mac - make VERBOSE=1 -j$(getconf _NPROCESSORS_ONLN) - cd ../.. - cp wxWidgets-3.2.5/buildOSX/lib/*.dylib build/bin/wxModularHost.app/Contents - - - uses: actions/upload-artifact@v4 - with: - name: artifacts_wxOSX_arm64 - path: build/bin/wxModularHost.app/Contents + +jobs: + build: + + runs-on: macos-14 + + steps: + - name: Install boost + uses: MarkusJx/install-boost@v2.4.5 + id: install-boost + with: + # REQUIRED: Specify the required boost version + # A list of supported versions can be found here: + # https://github.com/MarkusJx/prebuilt-boost/blob/main/versions-manifest.json + boost_version: 1.86.0 + boost_install_dir: /tmp/wxModularAppOCX/boost/ + + - uses: actions/checkout@v4 + - name: Install wxWidgets + run: | + curl -L https://download.osgeo.org/libtiff/tiff-4.6.0.tar.gz --output tiff-4.6.0.tar.gz + tar xzf tiff-4.6.0.tar.gz + cd tiff-4.6.0 + ./configure + make -j$(getconf _NPROCESSORS_ONLN) + sudo make install + cd .. + curl -L https://github.com/wxWidgets/wxWidgets/releases/download/v3.2.5/wxWidgets-3.2.5.tar.bz2 --output wxWidgets-3.2.5.tar.bz2 + tar xjf wxWidgets-3.2.5.tar.bz2 + cd wxwidgets-3.2.5 + mkdir buildOSX + cd buildOSX + ../configure --enable-debug + make -j$(getconf _NPROCESSORS_ONLN) + sudo make install + cd ../.. + wx-config --list + wx-config --cxxflags + wx-config --libs + + - name: Run CMake + run: | + cd build + chmod +x ./cmAppleMac.sh + ./cmAppleMac.sh + cd .. + env: + BOOST_ROOT: ${{ steps.install-boost.outputs.BOOST_ROOT }} + + - name: Run GNU Make with GNU compiler + run: | + cd build + mkdir -p bin + mkdir -p bin/Debug + cd Mac + make VERBOSE=1 -j$(getconf _NPROCESSORS_ONLN) + cd ../.. + cp wxWidgets-3.2.5/buildOSX/lib/*.dylib build/bin/wxModularHost.app/Contents + + - uses: actions/upload-artifact@v4 + with: + name: artifacts_wxOSX_arm64 + path: build/bin/wxModularHost.app/Contents diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1a4e60f --- /dev/null +++ b/.gitignore @@ -0,0 +1,33 @@ +build/ +.cmake/ +out/ +CMakeCache.txt +CMakeFiles/ +cmake_install.cmake +CMakeSettings.json + +Debug/ +Release/ +x64/ +x86/ + +*.pch +*.obj +*.o +*.tlog +*.pdb +*.ilk +*.exp + +.vs/ +*.VC.db +*.VC.opendb +*.suo +*.user +*.userosscache +*.sln.docstates + +.DS_Store +Thumbs.db +*.log +*.tmp \ No newline at end of file diff --git a/CommonPluginBase/CMakeLists.txt b/CommonPluginBase/CMakeLists.txt new file mode 100644 index 0000000..e5e9d82 --- /dev/null +++ b/CommonPluginBase/CMakeLists.txt @@ -0,0 +1,40 @@ +# +# Stuff the average programmer needs to change +# +set(SRCS + CommonConfigWindowBase.cpp + SerializableBase.cpp +) +set(HFILES + CommonConfigWindowBase.h + SerializableBase.h + CommonPlugin.h +) + +set(INCLUDE_DIRECTORIES + ${BASE_INCLUDE_DIRECTORIES} + ${THIRD_PARTY_DIR}/wxXS/include +) + +set(LIBRARY_NAME CommonPluginBase) + +if(WIN32) + set(PREPROCESSOR_DEFINITIONS ${PREPROCESSOR_DEFINITIONS};/D_USRDLL;/DIFLOOR_EXPORTS_COMMONPLUGINBASE) +endif(WIN32) + +set(SRCS ${SRCS} ${HFILES} ${PROJECT_ROOT_DIR}/include/stdwx.h ${PROJECT_ROOT_DIR}/include/stdwx.cpp) + +add_definitions(${PREPROCESSOR_DEFINITIONS}) +include_directories(${INCLUDE_DIRECTORIES}) + +add_library(${LIBRARY_NAME} SHARED ${SRCS}) + +target_link_libraries(${LIBRARY_NAME} + ${wxWidgets_LIBRARIES} + wxXS +) + +target_precompile_headers(${LIBRARY_NAME} + PRIVATE + "${PROJECT_ROOT_DIR}/include/stdwx.h" +) \ No newline at end of file diff --git a/CommonPluginBase/CommonConfigWindowBase.cpp b/CommonPluginBase/CommonConfigWindowBase.cpp new file mode 100644 index 0000000..a44757e --- /dev/null +++ b/CommonPluginBase/CommonConfigWindowBase.cpp @@ -0,0 +1,44 @@ +#include "stdwx.h" +#include "CommonConfigWindowBase.h" + +IMPLEMENT_DYNAMIC_CLASS(CommonConfigWindowBase, wxPanel) + +CommonConfigWindowBase::CommonConfigWindowBase() +{ + +} + +CommonConfigWindowBase::CommonConfigWindowBase(wxWindow *parent, + wxWindowID winid /*= wxID_ANY*/, + const wxPoint& pos /*= wxDefaultPosition*/, + const wxSize& size /*= wxDefaultSize*/, + long style /*= wxTAB_TRAVERSAL | wxNO_BORDER*/, + const wxString& name /*= wxPanelNameStr*/) +{ + Create(parent, winid, pos, size, style, name); +} + +bool CommonConfigWindowBase::Create(wxWindow *parent, + wxWindowID winid /*= wxID_ANY*/, + const wxPoint& pos /*= wxDefaultPosition*/, + const wxSize& size /*= wxDefaultSize*/, + long style /*= wxTAB_TRAVERSAL | wxNO_BORDER*/, + const wxString& name /*= wxPanelNameStr*/) +{ + return wxPanel::Create(parent, winid, pos, size, style, name); +} + +CommonConfigWindowBase::~CommonConfigWindowBase(void) +{ + +} + +bool CommonConfigWindowBase::ReadConfig() +{ + return true; +} + +bool CommonConfigWindowBase::SaveConfig() +{ + return true; +} \ No newline at end of file diff --git a/CommonPluginBase/CommonConfigWindowBase.h b/CommonPluginBase/CommonConfigWindowBase.h new file mode 100644 index 0000000..84c0a64 --- /dev/null +++ b/CommonPluginBase/CommonConfigWindowBase.h @@ -0,0 +1,37 @@ +#ifndef _COMMONCONFIGWINDOWBASE_H +#define _COMMONCONFIGWINDOWBASE_H +#include "CommonPlugin.h" +#include + +class IFLOOR_API_COMMONPLUGINBASE CommonConfigWindowBase : public wxPanel +{ + DECLARE_DYNAMIC_CLASS(CommonConfigWindowBase) +public: + /// Constructors + CommonConfigWindowBase(); + CommonConfigWindowBase(wxWindow *parent, + wxWindowID winid = wxID_ANY, + const wxPoint& pos = wxDefaultPosition, + const wxSize& size = wxDefaultSize, + long style = wxTAB_TRAVERSAL | wxNO_BORDER, + const wxString& name = wxPanelNameStr); + + /// Pseudo ctor + bool Create(wxWindow *parent, + wxWindowID winid = wxID_ANY, + const wxPoint& pos = wxDefaultPosition, + const wxSize& size = wxDefaultSize, + long style = wxTAB_TRAVERSAL | wxNO_BORDER, + const wxString& name = wxPanelNameStr); + + virtual ~CommonConfigWindowBase(); + + /// Reads config from the effect + virtual bool ReadConfig(); + + /// Saves config to the effect + virtual bool SaveConfig(); +}; + + +#endif // _COMMONCONFIGWINDOWBASE_H diff --git a/CommonPluginBase/CommonPlugin.h b/CommonPluginBase/CommonPlugin.h new file mode 100644 index 0000000..c36d560 --- /dev/null +++ b/CommonPluginBase/CommonPlugin.h @@ -0,0 +1,14 @@ +#ifndef _COMMONPLUGIN_H +#define _COMMONPLUGIN_H + +#if defined(__WXMSW__) +#ifdef IFLOOR_EXPORTS_COMMONPLUGINBASE +#define IFLOOR_API_COMMONPLUGINBASE __declspec(dllexport) +#else +#define IFLOOR_API_COMMONPLUGINBASE __declspec(dllimport) +#endif +#else +#define IFLOOR_API_COMMONPLUGINBASE +#endif + +#endif // _COMMONPLUGIN_H diff --git a/CommonPluginBase/SerializableBase.cpp b/CommonPluginBase/SerializableBase.cpp new file mode 100644 index 0000000..a738d8c --- /dev/null +++ b/CommonPluginBase/SerializableBase.cpp @@ -0,0 +1,63 @@ +#include "stdwx.h" +#include "SerializableBase.h" +#include + +IMPLEMENT_ABSTRACT_CLASS(SerializableBase, xsSerializable); + +WX_DEFINE_USER_EXPORTED_LIST(SerializableBaseList); + +SerializableBase::SerializableBase() +{ +} + +SerializableBase::~SerializableBase() +{ +} + +bool SerializableBase::Deserialize(wxInputStream & instream) +{ + return Deserialize(instream, *this); +} + +bool SerializableBase::Deserialize(wxInputStream & instream, xsSerializable & obj) +{ + wxXmlSerializer Serializer; + Serializer.EnableCloning(false); + Serializer.SetRootItem(&obj); + bool res = Serializer.DeserializeFromXml(instream); + Serializer.SetRootItem(nullptr, false); + obj.Reparent(nullptr); + obj.SetParentManager(nullptr); + return res; +} + +bool SerializableBase::Deserialize(const wxString & config, xsSerializable & obj) +{ + wxStringInputStream stream(config); + return Deserialize(stream, obj); +} + +bool SerializableBase::Serialize(wxOutputStream & outstream) +{ + return Serialize(outstream, *this); +} + +bool SerializableBase::Serialize(wxOutputStream & outstream, xsSerializable & obj) +{ + wxXmlSerializer Serializer; + Serializer.EnableCloning(false); + Serializer.SetRootItem(&obj); + bool res = Serializer.SerializeToXml(outstream, true); + Serializer.SetRootItem(nullptr, false); + obj.Reparent(nullptr); + obj.SetParentManager(nullptr); + return res; +} + +wxString SerializableBase::Serialize(xsSerializable & obj) +{ + wxString config; + wxStringOutputStream stream(&config); + Serialize(stream, obj); + return config; +} \ No newline at end of file diff --git a/CommonPluginBase/SerializableBase.h b/CommonPluginBase/SerializableBase.h new file mode 100644 index 0000000..8194db2 --- /dev/null +++ b/CommonPluginBase/SerializableBase.h @@ -0,0 +1,42 @@ +#ifndef _SERIALIZABLEBASE_H +#define _SERIALIZABLEBASE_H + +#include "CommonPlugin.h" +#include "CommonConfigWindowBase.h" +#include + +/// Base class for all iFloor effects +class IFLOOR_API_COMMONPLUGINBASE SerializableBase : public xsSerializable +{ + DECLARE_ABSTRACT_CLASS(SerializableBase) +public: + /// Default constructor + SerializableBase(); + /// Default destructor + virtual ~SerializableBase(); + /// Returns GUID (unique identifier) of effect + /// \return string which contains unique identifier of effect + virtual wxString GetID() const = 0; + /// Returns name of effect + /// \return string which contains human-readable name of effect + virtual wxString GetName() const = 0; + /// Creates settings panel where user can setup effect properties + /// \param parent parent window of settings panel + /// \return pointer to created settings panel. If function fails, return value is NULL + virtual CommonConfigWindowBase * CreateSettingsEditor(wxWindow * parent = NULL) = 0; + + virtual bool Deserialize(wxInputStream & instream); + virtual bool Serialize(wxOutputStream & outstream); + + static bool Deserialize(wxInputStream & instream, xsSerializable & obj); + static bool Serialize(wxOutputStream & outstream, xsSerializable & obj); + + static bool Deserialize(const wxString & config, xsSerializable & obj); + static wxString Serialize(xsSerializable & obj); +}; + +/// List of iFloor effects +WX_DECLARE_USER_EXPORTED_LIST(SerializableBase, SerializableBaseList, IFLOOR_API_COMMONPLUGINBASE); +WX_DECLARE_STRING_HASH_MAP(SerializableBase *, SerializableBaseDictionary); + +#endif // _SERIALIZABLEBASE_H \ No newline at end of file diff --git a/CommonPluginBase/Win/CommonPluginBase.vcxproj b/CommonPluginBase/Win/CommonPluginBase.vcxproj new file mode 100644 index 0000000..5465b0d --- /dev/null +++ b/CommonPluginBase/Win/CommonPluginBase.vcxproj @@ -0,0 +1,260 @@ + + + + x64 + + + + Debug + x64 + + + Release + x64 + + + + {751A15C0-76C2-3506-AB9D-0CD3545003FA} + Win32Proj + 10.0.26100.0 + x64 + CommonPluginBase + NoUpgrade + + + + DynamicLibrary + Unicode + v143 + + + DynamicLibrary + Unicode + v143 + + + + + + + + + + <_ProjectFileVersion>10.0.20506.1 + C:\Users\Admin\IFloor\projects\wxVideoProcessing\bin\ + CommonPluginBase.dir\Debug\ + CommonPluginBase + .dll + true + true + C:\Users\Admin\IFloor\projects\wxVideoProcessing\bin\ + CommonPluginBase.dir\Release\ + CommonPluginBase + .dll + false + true + + + + + C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\mswu;C:\Users\Admin\IFloor\libs\wxWidgets\include;C:\Users\Admin\IFloor\libs\opencv\build\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\Utils;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\MotionDetectorCore;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxJSON\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxxmlserializer\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxxmlserializer;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxXS\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\ThirdParty\wxXS\build\..\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\ThirdParty\wxJSON\build\..\include;%(AdditionalIncludeDirectories) + $(IntDir) + EnableFastChecks + + + ProgramDatabase + 4996 + Sync + + + Disabled + stdcpp17 + + true + Disabled + Use + C:/Users/Admin/IFloor/projects/wxVideoProcessing/CommonPluginBase/Win/CMakeFiles/CommonPluginBase.dir/Debug/cmake_pch.hxx + C:/Users/Admin/IFloor/projects/wxVideoProcessing/CommonPluginBase/Win/CommonPluginBase.dir/Debug//cmake_pch.pch + + MultiThreadedDebugDLL + true + + + false + %(PreprocessorDefinitions);WIN32;_WINDOWS;__WXDEBUG__=1;WIN32_LEAN_AND_MEAN;WXUSINGDLL;UNICODE;_UNICODE;_CRT_SECURE_NO_DEPRECATE;_USRDLL;IFLOOR_EXPORTS_COMMONPLUGINBASE;CMAKE_INTDIR="Debug";CommonPluginBase_EXPORTS + $(IntDir) + false + + + %(PreprocessorDefinitions);WIN32;_DEBUG;_WINDOWS;__WXDEBUG__=1;WIN32_LEAN_AND_MEAN;WXUSINGDLL;UNICODE;_UNICODE;_CRT_SECURE_NO_DEPRECATE;_USRDLL;IFLOOR_EXPORTS_COMMONPLUGINBASE;CMAKE_INTDIR=\"Debug\";CommonPluginBase_EXPORTS + C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\mswu;C:\Users\Admin\IFloor\libs\wxWidgets\include;C:\Users\Admin\IFloor\libs\opencv\build\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\Utils;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\MotionDetectorCore;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxJSON\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxxmlserializer\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxxmlserializer;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxXS\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\ThirdParty\wxXS\build\..\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\ThirdParty\wxJSON\build\..\include;%(AdditionalIncludeDirectories) + + + C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\mswu;C:\Users\Admin\IFloor\libs\wxWidgets\include;C:\Users\Admin\IFloor\libs\opencv\build\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\Utils;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\MotionDetectorCore;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxJSON\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxxmlserializer\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxxmlserializer;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxXS\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\ThirdParty\wxXS\build\..\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\ThirdParty\wxJSON\build\..\include;%(AdditionalIncludeDirectories) + $(ProjectDir)/$(IntDir) + %(Filename).h + %(Filename).tlb + %(Filename)_i.c + %(Filename)_p.c + + + C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxbase33ud.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33ud_core.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33ud_adv.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33ud_aui.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxbase33ud_net.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33ud_gl.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxbase33ud_xml.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33ud_propgrid.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33ud_html.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxpngd.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxtiffd.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxjpegd.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxzlibd.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxregexud.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxexpatd.lib;opengl32.lib;glu32.lib;winmm.lib;comctl32.lib;uuid.lib;oleacc.lib;uxtheme.lib;rpcrt4.lib;shlwapi.lib;version.lib;wsock32.lib;C:\Users\Admin\IFloor\projects\wxVideoProcessing\bin\lib\Debug\wxXS.lib;C:\Users\Admin\IFloor\projects\wxVideoProcessing\bin\lib\Debug\wxJSON.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxbase33ud.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33ud_core.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33ud_adv.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33ud_aui.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxbase33ud_net.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33ud_gl.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxbase33ud_xml.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33ud_propgrid.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33ud_html.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxpngd.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxtiffd.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxjpegd.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxzlibd.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxregexud.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxexpatd.lib;opengl32.lib;glu32.lib;winmm.lib;comctl32.lib;uuid.lib;oleacc.lib;uxtheme.lib;rpcrt4.lib;shlwapi.lib;version.lib;wsock32.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;comdlg32.lib;advapi32.lib + C:/Users/Admin/IFloor/projects/wxVideoProcessing/build/Win/lib;C:/Users/Admin/IFloor/projects/wxVideoProcessing/build/Win/lib/$(Configuration);%(AdditionalLibraryDirectories) + %(AdditionalOptions) /machine:x64 + + true + %(IgnoreSpecificDefaultLibraries) + + C:/Users/Admin/IFloor/projects/wxVideoProcessing/bin/lib/Debug/CommonPluginBase.lib + + C:/Users/Admin/IFloor/projects/wxVideoProcessing/bin/CommonPluginBase.pdb + + Console + + + false + + + + + C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\mswu;C:\Users\Admin\IFloor\libs\wxWidgets\include;C:\Users\Admin\IFloor\libs\opencv\build\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\Utils;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\MotionDetectorCore;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxJSON\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxxmlserializer\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxxmlserializer;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxXS\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\ThirdParty\wxXS\build\..\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\ThirdParty\wxJSON\build\..\include;%(AdditionalIncludeDirectories) + $(IntDir) + Default + + + 4996 + Sync + + + AnySuitable + stdcpp17 + + true + MaxSpeed + Use + C:/Users/Admin/IFloor/projects/wxVideoProcessing/CommonPluginBase/Win/CMakeFiles/CommonPluginBase.dir/Release/cmake_pch.hxx + C:/Users/Admin/IFloor/projects/wxVideoProcessing/CommonPluginBase/Win/CommonPluginBase.dir/Release//cmake_pch.pch + + MultiThreadedDLL + true + + + false + %(PreprocessorDefinitions);WIN32;_WINDOWS;NDEBUG;WIN32_LEAN_AND_MEAN;WXUSINGDLL;UNICODE;_UNICODE;_CRT_SECURE_NO_DEPRECATE;_USRDLL;IFLOOR_EXPORTS_COMMONPLUGINBASE;CMAKE_INTDIR="Release";CommonPluginBase_EXPORTS + $(IntDir) + + + false + + + %(PreprocessorDefinitions);WIN32;_WINDOWS;NDEBUG;WIN32_LEAN_AND_MEAN;WXUSINGDLL;UNICODE;_UNICODE;_CRT_SECURE_NO_DEPRECATE;_USRDLL;IFLOOR_EXPORTS_COMMONPLUGINBASE;CMAKE_INTDIR=\"Release\";CommonPluginBase_EXPORTS + C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\mswu;C:\Users\Admin\IFloor\libs\wxWidgets\include;C:\Users\Admin\IFloor\libs\opencv\build\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\Utils;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\MotionDetectorCore;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxJSON\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxxmlserializer\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxxmlserializer;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxXS\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\ThirdParty\wxXS\build\..\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\ThirdParty\wxJSON\build\..\include;%(AdditionalIncludeDirectories) + + + C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\mswu;C:\Users\Admin\IFloor\libs\wxWidgets\include;C:\Users\Admin\IFloor\libs\opencv\build\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\Utils;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\MotionDetectorCore;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxJSON\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxxmlserializer\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxxmlserializer;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxXS\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\ThirdParty\wxXS\build\..\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\ThirdParty\wxJSON\build\..\include;%(AdditionalIncludeDirectories) + $(ProjectDir)/$(IntDir) + %(Filename).h + %(Filename).tlb + %(Filename)_i.c + %(Filename)_p.c + + + C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxbase33u.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33u_core.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33u_adv.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33u_aui.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxbase33u_net.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33u_gl.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxbase33u_xml.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33u_propgrid.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33u_html.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxpng.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxtiff.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxjpeg.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxzlib.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxregexu.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxexpat.lib;opengl32.lib;glu32.lib;winmm.lib;comctl32.lib;uuid.lib;oleacc.lib;uxtheme.lib;rpcrt4.lib;shlwapi.lib;version.lib;wsock32.lib;C:\Users\Admin\IFloor\projects\wxVideoProcessing\bin\lib\Release\wxXS.lib;C:\Users\Admin\IFloor\projects\wxVideoProcessing\bin\lib\Release\wxJSON.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxbase33u.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33u_core.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33u_adv.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33u_aui.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxbase33u_net.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33u_gl.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxbase33u_xml.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33u_propgrid.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33u_html.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxpng.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxtiff.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxjpeg.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxzlib.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxregexu.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxexpat.lib;opengl32.lib;glu32.lib;winmm.lib;comctl32.lib;uuid.lib;oleacc.lib;uxtheme.lib;rpcrt4.lib;shlwapi.lib;version.lib;wsock32.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;comdlg32.lib;advapi32.lib + C:/Users/Admin/IFloor/projects/wxVideoProcessing/build/Win/lib;C:/Users/Admin/IFloor/projects/wxVideoProcessing/build/Win/lib/$(Configuration);%(AdditionalLibraryDirectories) + %(AdditionalOptions) /machine:x64 + + false + %(IgnoreSpecificDefaultLibraries) + + C:/Users/Admin/IFloor/projects/wxVideoProcessing/bin/lib/Release/CommonPluginBase.lib + + C:/Users/Admin/IFloor/projects/wxVideoProcessing/bin/CommonPluginBase.pdb + + Console + + + false + + + + + Always + Building Custom Rule C:/Users/Admin/IFloor/projects/wxVideoProcessing/CommonPluginBase/CMakeLists.txt + setlocal +"C:\Program Files\CMake\bin\cmake.exe" -SC:/Users/Admin/IFloor/projects/wxVideoProcessing/build -BC:/Users/Admin/IFloor/projects/wxVideoProcessing/build/Win --check-stamp-file C:/Users/Admin/IFloor/projects/wxVideoProcessing/CommonPluginBase/Win/CMakeFiles/generate.stamp +if %errorlevel% neq 0 goto :cmEnd +:cmEnd +endlocal & call :cmErrorLevel %errorlevel% & goto :cmDone +:cmErrorLevel +exit /b %1 +:cmDone +if %errorlevel% neq 0 goto :VCEnd + %(AdditionalInputs) + C:\Users\Admin\IFloor\projects\wxVideoProcessing\CommonPluginBase\Win\CMakeFiles\generate.stamp + false + Building Custom Rule C:/Users/Admin/IFloor/projects/wxVideoProcessing/CommonPluginBase/CMakeLists.txt + setlocal +"C:\Program Files\CMake\bin\cmake.exe" -SC:/Users/Admin/IFloor/projects/wxVideoProcessing/build -BC:/Users/Admin/IFloor/projects/wxVideoProcessing/build/Win --check-stamp-file C:/Users/Admin/IFloor/projects/wxVideoProcessing/CommonPluginBase/Win/CMakeFiles/generate.stamp +if %errorlevel% neq 0 goto :cmEnd +:cmEnd +endlocal & call :cmErrorLevel %errorlevel% & goto :cmDone +:cmErrorLevel +exit /b %1 +:cmDone +if %errorlevel% neq 0 goto :VCEnd + %(AdditionalInputs) + C:\Users\Admin\IFloor\projects\wxVideoProcessing\CommonPluginBase\Win\CMakeFiles\generate.stamp + false + + + + + C:/Users/Admin/IFloor/projects/wxVideoProcessing/CommonPluginBase/Win/CMakeFiles/CommonPluginBase.dir/Debug/cmake_pch.hxx;%(ForcedIncludeFiles) + Create + C:/Users/Admin/IFloor/projects/wxVideoProcessing/CommonPluginBase/Win/CMakeFiles/CommonPluginBase.dir/Debug/cmake_pch.hxx + C:/Users/Admin/IFloor/projects/wxVideoProcessing/CommonPluginBase/Win/CommonPluginBase.dir/Debug//cmake_pch.pch + C:/Users/Admin/IFloor/projects/wxVideoProcessing/CommonPluginBase/Win/CMakeFiles/CommonPluginBase.dir/Release/cmake_pch.hxx;%(ForcedIncludeFiles) + Create + C:/Users/Admin/IFloor/projects/wxVideoProcessing/CommonPluginBase/Win/CMakeFiles/CommonPluginBase.dir/Release/cmake_pch.hxx + C:/Users/Admin/IFloor/projects/wxVideoProcessing/CommonPluginBase/Win/CommonPluginBase.dir/Release//cmake_pch.pch + + + C:/Users/Admin/IFloor/projects/wxVideoProcessing/CommonPluginBase/Win/CMakeFiles/CommonPluginBase.dir/Debug/cmake_pch.hxx;%(ForcedIncludeFiles) + C:/Users/Admin/IFloor/projects/wxVideoProcessing/CommonPluginBase/Win/CMakeFiles/CommonPluginBase.dir/Release/cmake_pch.hxx;%(ForcedIncludeFiles) + + + C:/Users/Admin/IFloor/projects/wxVideoProcessing/CommonPluginBase/Win/CMakeFiles/CommonPluginBase.dir/Debug/cmake_pch.hxx;%(ForcedIncludeFiles) + C:/Users/Admin/IFloor/projects/wxVideoProcessing/CommonPluginBase/Win/CMakeFiles/CommonPluginBase.dir/Release/cmake_pch.hxx;%(ForcedIncludeFiles) + + + + + + + C:/Users/Admin/IFloor/projects/wxVideoProcessing/CommonPluginBase/Win/CMakeFiles/CommonPluginBase.dir/Debug/cmake_pch.hxx;%(ForcedIncludeFiles) + C:/Users/Admin/IFloor/projects/wxVideoProcessing/CommonPluginBase/Win/CMakeFiles/CommonPluginBase.dir/Release/cmake_pch.hxx;%(ForcedIncludeFiles) + + + + + + + + {6F4BDD20-5B99-3029-BDF0-7D719B283AF5} + ZERO_CHECK + false + Never + + + {7887B55C-77B9-3262-AFAD-ABA738C61029} + wxJSON + + + {78CFCEBE-5466-3128-9717-547416DBFFE5} + wxXS + + + + + + \ No newline at end of file diff --git a/CommonPluginBase/Win/CommonPluginBase.vcxproj.filters b/CommonPluginBase/Win/CommonPluginBase.vcxproj.filters new file mode 100644 index 0000000..3b81716 --- /dev/null +++ b/CommonPluginBase/Win/CommonPluginBase.vcxproj.filters @@ -0,0 +1,51 @@ + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Precompile Header File + + + Precompile Header File + + + + + + + + {4951AEE1-75F2-3C38-B593-C6BC8C809FB0} + + + {112D4227-BB51-3360-BB5B-5EA5DBA5A65F} + + + {05450AB2-CB7B-34EA-83DF-1EE86424EDF0} + + + diff --git a/MotionDetectorCore/AmplifyFilter.cpp b/MotionDetectorCore/AmplifyFilter.cpp new file mode 100644 index 0000000..bcefbb3 --- /dev/null +++ b/MotionDetectorCore/AmplifyFilter.cpp @@ -0,0 +1,55 @@ +//#include "stdwx.h" +#include "AmplifyFilter.h" + +IMPLEMENT_DYNAMIC_CLASS(AmplifyFilter, Filter); + +AmplifyFilter::AmplifyFilter() +{ + level = 0; + XS_SERIALIZE(level, wxT("AmplifyLevel")); +} + +AmplifyFilter::~AmplifyFilter() +{ +} + +wxString AmplifyFilter::GetName() const +{ + return _("Amplify"); +} + +#if defined(HAVE_CUDA) +bool AmplifyFilter::KernelGPU() +{ + if (m_src->empty()) + return false; + + double scalef = static_cast(level) / 128.0; + cv::cuda::multiply(*m_src, *m_src, m_dst, scalef); + return true; +} +#endif + +bool AmplifyFilter::Kernel() +{ + // derived class responsible for allocating storage for filtered image + if (source.empty()) + return false; + + if (destination.empty()) + { + destination = cv::Mat::zeros(source.size(), source.type()); + } + + float scalef = level / 128.0f; + + cv::multiply(source, source, destination, scalef); + return true; +} + +void AmplifyFilter::CreateParamInputs() +{ + AddParameterSpinInput(_("Amplify level:"), &level, 300); +} + +wxFORCE_LINK_THIS_MODULE(AmplifyFilter); \ No newline at end of file diff --git a/MotionDetectorCore/AmplifyFilter.h b/MotionDetectorCore/AmplifyFilter.h new file mode 100644 index 0000000..7876420 --- /dev/null +++ b/MotionDetectorCore/AmplifyFilter.h @@ -0,0 +1,28 @@ +#ifndef __TOUCHLIB_FILTER_AMPLIFY__ +#define __TOUCHLIB_FILTER_AMPLIFY__ + +#include "FilterTemplate.h" +#include + +class MOTION_DETECTOR_CORE_API AmplifyFilter : public Filter +{ + DECLARE_DYNAMIC_CLASS_NO_COPY(AmplifyFilter) +public: + AmplifyFilter(); + virtual ~AmplifyFilter(); + + bool Kernel(); +#if defined(HAVE_CUDA) + bool KernelGPU(); + cv::cuda::GpuMat m_floatGpuMat; +#endif + virtual wxString GetName() const; + + int level; + +protected: + virtual void CreateParamInputs(); + +}; + +#endif // __TOUCHLIB_FILTER_AMPLIFY__ diff --git a/MotionDetectorCore/BackgroundFilter.cpp b/MotionDetectorCore/BackgroundFilter.cpp new file mode 100644 index 0000000..eb79f42 --- /dev/null +++ b/MotionDetectorCore/BackgroundFilter.cpp @@ -0,0 +1,202 @@ +#include "stdwx.h" +#include "BackgroundFilter.h" +#include "vector2d.h" +//#include "Image.h" + +IMPLEMENT_DYNAMIC_CLASS(BackgroundFilter, Filter); + +#define ROWSTOSCAN 40 + +#define UPDATERATE_UP 2 +#define UPDATERATE_DOWN 1 + +#define DEFAULT_UPDATE_THRESH 50 + +BackgroundFilter::BackgroundFilter() +{ + nPolyMask = 1; + recapture = false; + count = -1; + //recapture = true; + //count = 10; + updateThreshold = DEFAULT_UPDATE_THRESH; + currentRow = 0; + XS_SERIALIZE(updateThreshold, wxT("Threshold")) +// pMap["threshold"] = iFloorImageRecognitionParam(INT_PARAM, &updateThreshold, 0, 255); +} + +BackgroundFilter::~BackgroundFilter() +{ + +} + +wxString BackgroundFilter::GetName() const +{ + return _("Background"); +} + +//void BackgroundFilter::setParameter(const char *name, const char *value) +//{ +// if(strcmp(name, "capture") == 0) +// { +// printf("Recap\n"); +// recapture = true; +// } else if(strcmp(name, "threshold") == 0) +// { +// updateThreshold = (int) atof(value); +// } else if(strcmp(name, "mask") == 0) +// { +// if(value) +// setMask((touchlib::vector2df*)value,GRID_X+1,GRID_Y+1); +// else +// clearMask(); +// } +//} + +//void BackgroundFilter::getParameters(iFloorParamMap& pMap) +//{ +// pMap[std::string("threshold")] = toString(updateThreshold); +//} + + +//#define ADAPTIVE_BACKGROUND + +bool BackgroundFilter::Kernel() +{ + // derived class responsible for allocating storage for filtered image + + if (source.empty()) + return false; + + if (destination.empty()) + { + destination = cv::Mat::zeros(source.size(), CV_8UC1); + } + + if (count > -1) + count--; + + if (reference.empty() || recapture || count == 0) + { + source.copyTo(reference); + + if (mask.empty()) { + mask = cv::Mat::zeros(reference.size(), reference.type()); + } + + // ßêùî ìàñêà çàäàíà, ìàëþºìî ¿¿ ñó÷àñíèì ìåòîäîì cv::fillConvexPoly + if (!polyMask.empty() && updateThreshold == 0) { + mask.setTo(cv::Scalar(255, 255, 255)); + cv::fillConvexPoly(mask, polyMask, cv::Scalar(0, 0, 0)); + } + + // Ñó÷àñíå â³äí³ìàííÿ ñêàëÿðà (çàì³ñòü cvSubS) + if (updateThreshold != 0) + { + reference -= cv::Scalar(updateThreshold, updateThreshold, updateThreshold); + } + + recapture = false; + + //count = updateThreshold; + recapture = false; + } + +#ifdef ADAPTIVE_BACKGROUND + BwImage imgSrc(source), imgRef(reference); + + int x, y; + int h, w; + h = source->height; + w = source->width; + + int stoprow = currentRow + ROWSTOSCAN; + + if (stoprow > h) + stoprow = h; + + // only do N number of rows per frame to speed up processing.. + for(y=currentRow; y ref) + { + ref += UPDATERATE_UP; + if (ref > pix) + ref = pix; + + imgRef[y][x] = ref; // update background + } + + // In most cases we won't really need to go 'down'.. + // as the screen gets dirtier, it gets brighter.. + // + //if (pix < ref) + //ref -= UPDATERATE_DOWN; + + } + + } + } + currentRow += ROWSTOSCAN; + + if (currentRow >= h) + currentRow = 0; +#endif + // destination = source-reference + cv::subtract(source, reference, destination); + return true; +} + +void BackgroundFilter::setMask(void* vaPoints, int xGrid, int yGrid) +{ + touchlib::vector2df* aPoints = (touchlib::vector2df*)vaPoints; + + // Ïåðåâèä³ëÿºìî ðîçì³ð íàøîãî âåêòîðà òî÷îê C++ + int totalPoints = 2 * (xGrid + yGrid - 2); + polyMask.resize(totalPoints); + + int countPoints = 0; + // top side + for (int i = 0; i < xGrid; i++) { + polyMask[countPoints++] = cv::Point(static_cast(aPoints[i].X), static_cast(aPoints[i].Y)); + } + // right side + for (int i = 2; i < yGrid; i++) { + polyMask[countPoints++] = cv::Point(static_cast(aPoints[i * xGrid - 1].X), static_cast(aPoints[i * xGrid - 1].Y)); + } + // bottom side + for (int i = xGrid - 1; i >= 0; i--) { + polyMask[countPoints++] = cv::Point(static_cast(aPoints[i + (yGrid - 1) * xGrid].X), static_cast(aPoints[i + (yGrid - 1) * xGrid].Y)); + } + // left side + for (int i = yGrid - 2; i > 0; i--) { + polyMask[countPoints++] = cv::Point(static_cast(aPoints[i * xGrid].X), static_cast(aPoints[i * xGrid].Y)); + } + + recapture = true; +}// deletes an old mask and tells Kernel func to recapture +void BackgroundFilter::clearMask() +{ + polyMask.clear(); + if (!mask.empty()) + { + mask.setTo(cv::Scalar(0, 0, 0)); + } + recapture = true; + +} + +void BackgroundFilter::CreateParamInputs() +{ + AddParameterSpinInput(_("Update threshold"), &updateThreshold); + AddParameterBoolInput(_("Recapture"), &recapture); +} + +wxFORCE_LINK_THIS_MODULE(BackgroundFilter); \ No newline at end of file diff --git a/MotionDetectorCore/BackgroundFilter.h b/MotionDetectorCore/BackgroundFilter.h new file mode 100644 index 0000000..837c23a --- /dev/null +++ b/MotionDetectorCore/BackgroundFilter.h @@ -0,0 +1,37 @@ +#ifndef __TOUCHLIB_FILTER_BACKGROUND__ +#define __TOUCHLIB_FILTER_BACKGROUND__ + +#include "FilterTemplate.h" +#include +#include + +class MOTION_DETECTOR_CORE_API BackgroundFilter : public Filter +{ + DECLARE_DYNAMIC_CLASS_NO_COPY(BackgroundFilter) +public: + BackgroundFilter(); + virtual ~BackgroundFilter(); + + bool Kernel(); +#if defined(HAVE_CUDA) + bool KernelGPU(){return false;} +#endif + virtual wxString GetName() const; + + void setMask(void *aPoints,int xRes, int yRes); + void clearMask(); +protected: + virtual void CreateParamInputs(); +private: + bool recapture; + cv::Mat reference; + bool ownsImage; + int updateThreshold; // anything above this threshold is considered a 'press' and not part of the background + int count; + int currentRow; + cv::Mat mask; + std::vector polyMask; + int nPolyMask; +}; + +#endif // __TOUCHLIB_FILTER_BACKGROUND__ diff --git a/MotionDetectorCore/BarrelDistortionCorrectionFilter.cpp b/MotionDetectorCore/BarrelDistortionCorrectionFilter.cpp new file mode 100644 index 0000000..9396978 --- /dev/null +++ b/MotionDetectorCore/BarrelDistortionCorrectionFilter.cpp @@ -0,0 +1,163 @@ +// Filter description +// Name: Barrel Distortion Correction Filter +// Purpose: Correcting the barrel distortion of the lens +// Original author: Laurence Muller (aka Falcon4ever) + +/* +Tool url: +http://www.multigesture.net/wp-content/uploads/2007/12/touchlib_barreldistortion_tool.zip + +example of usage: + + + + +Place the camera.yml (created by the calibration tool) in the same directory as the config.xml +*/ +#include "stdwx.h" +#include "BarrelDistortionCorrectionFilter.h" + +IMPLEMENT_DYNAMIC_CLASS(BarrelDistortionCorrectionFilter, Filter); + +BarrelDistortionCorrectionFilter::BarrelDistortionCorrectionFilter() +{ + const char* configfile = "camera.yml"; + + cv::FileStorage fs(configfile, cv::FileStorage::READ); + if (fs.isOpened()) + { + fs["camera_matrix"] >> camera; + fs["distortion_coefficients"] >> dist_coeffs; + fs.release(); + } + else + { + camera = cv::Mat::eye(3, 3, CV_64FC1); + dist_coeffs = cv::Mat::zeros(5, 1, CV_64FC1); + } + + border_size = 0; + init = false; + init2 = false; + + MapX = NULL; + MapY = NULL; +} + +BarrelDistortionCorrectionFilter::~BarrelDistortionCorrectionFilter() +{ + +} + +wxString BarrelDistortionCorrectionFilter::GetName() const +{ + return _("Barrel Distortion Correction"); +} + +//void BarrelDistortionCorrectionFilter::getParameters(iFloorParamMap& pMap) +//{ +// pMap[std::string("border_size")] = toString(border_size); +//} +// +//void BarrelDistortionCorrectionFilter::setParameter(const char *name, const char *value) +//{ +// if(strcmp(name, "border_size") == 0) +// { +// border_size = (int) atof(value); +// } +//} + +bool BarrelDistortionCorrectionFilter::Kernel() +{ + if (border_size > 0) + { + cv::Mat tempResult = undistorted_with_border2(source, camera, dist_coeffs, border_size); + + if (!tempResult.empty()) + { + cv::Rect roi(border_size, border_size, source.cols, source.rows); + tempResult(roi).copyTo(destination); + } + } + else + { + cv::Mat tempResult = undistorted_with_border2(source, camera, dist_coeffs, 0); + if (!tempResult.empty()) + { + tempResult.copyTo(destination); + } + } + + return true; +} + +cv::Mat BarrelDistortionCorrectionFilter::undistorted_with_border( const cv::Mat& image, const cv::Mat& intrinsic, const cv::Mat& distortion, short int border) +{ + if (!init) + { + if (intrinsic.empty() || distortion.empty()) + return cv::Mat(); + + intrinsic.copyTo(b_intrinsic); + + if (b_intrinsic.type() == CV_64FC1) { + b_intrinsic.at(0, 2) += border; + b_intrinsic.at(1, 2) += border; + } + else { + b_intrinsic.at(0, 2) += static_cast(border); + b_intrinsic.at(1, 2) += static_cast(border); + } + + init = true; + } + + if (b_intrinsic.empty()) + return cv::Mat(); + + cv::copyMakeBorder(image, bordered, border, border, border, border, cv::BORDER_CONSTANT, cv::Scalar::all(0)); + + cv::undistort(bordered, bordered_corr, b_intrinsic, distortion); + + return bordered_corr; +} + +cv::Mat BarrelDistortionCorrectionFilter::undistorted_with_border2(const cv::Mat& image, const cv::Mat& intrinsic, const cv::Mat& distortion, short int border) +{ + if (!init2) + { + if (intrinsic.empty() || distortion.empty()) + return cv::Mat(); + + intrinsic.copyTo(b_intrinsic); + + if (b_intrinsic.type() == CV_64FC1) { + b_intrinsic.at(0, 2) += border; + b_intrinsic.at(1, 2) += border; + } + else { + b_intrinsic.at(0, 2) += static_cast(border); + b_intrinsic.at(1, 2) += static_cast(border); + } + + cv::Size borderedSize(image.cols + 2 * border, image.rows + 2 * border); + + cv::initUndistortRectifyMap( + b_intrinsic, distortion, cv::Mat(), b_intrinsic, + borderedSize, CV_32FC1, MapX, MapY + ); + + init2 = true; + } + + if (MapX.empty() || MapY.empty()) + return cv::Mat(); + + cv::copyMakeBorder(image, bordered, border, border, border, border, cv::BORDER_CONSTANT, cv::Scalar::all(0)); + + cv::remap(bordered, bordered_corr, MapX, MapY, cv::INTER_LINEAR, cv::BORDER_CONSTANT, cv::Scalar::all(0)); + + return bordered_corr; +} + +wxFORCE_LINK_THIS_MODULE(BarrelDistortionCorrectionFilter); \ No newline at end of file diff --git a/MotionDetectorCore/BarrelDistortionCorrectionFilter.h b/MotionDetectorCore/BarrelDistortionCorrectionFilter.h new file mode 100644 index 0000000..e0f0c5c --- /dev/null +++ b/MotionDetectorCore/BarrelDistortionCorrectionFilter.h @@ -0,0 +1,44 @@ +// Filter description +// Name: Barrel Distortion Correction Filter +// Purpose: Correcting the barrel distortion of the lens +// Original author: Laurence Muller (aka Falcon4ever) + +#ifndef __TOUCHSCREEN_FILTER_BARRELDISTORTIONCORRECTION__ +#define __TOUCHSCREEN_FILTER_BARRELDISTORTIONCORRECTION__ + +#include "FilterTemplate.h" +#include + +class MOTION_DETECTOR_CORE_API BarrelDistortionCorrectionFilter : public Filter +{ + DECLARE_DYNAMIC_CLASS_NO_COPY(BarrelDistortionCorrectionFilter) +public: + BarrelDistortionCorrectionFilter(); + virtual ~BarrelDistortionCorrectionFilter(); + + bool Kernel(); +#if defined(HAVE_CUDA) + bool KernelGPU(){return false;} +#endif + virtual wxString GetName() const; + +private: + cv::Mat undistorted_with_border( const cv::Mat& image, const cv::Mat& intrinsic, const cv::Mat& distortion, short int border ); + cv::Mat undistorted_with_border2( const cv::Mat& image, const cv::Mat& intrinsic, const cv::Mat& distortion, short int border ); + cv::Mat camera; + cv::Mat dist_coeffs; + short int border_size; + + cv::Mat b_intrinsic; + cv::Mat bordered; + cv::Mat bordered_corr; + + bool init; + + // Method 2 + bool init2; + cv::Mat MapX; + cv::Mat MapY; +}; + +#endif // __TOUCHSCREEN_FILTER_BARRELDISTORTIONCORRECTION__ diff --git a/MotionDetectorCore/BrightnessContrastFilter.cpp b/MotionDetectorCore/BrightnessContrastFilter.cpp new file mode 100644 index 0000000..c06a4d0 --- /dev/null +++ b/MotionDetectorCore/BrightnessContrastFilter.cpp @@ -0,0 +1,141 @@ +#include "stdwx.h" +#include "BrightnessContrastFilter.h" + +IMPLEMENT_DYNAMIC_CLASS(BrightnessContrastFilter, Filter); + +BrightnessContrastFilter::BrightnessContrastFilter() +{ + brightness = (float) DEFAULT_BRIGHTNESS; + contrast = (float) DEFAULT_CONTRAST; + + // mapping from input to output values via lookup table, recalculated when brightness or contrast changed + lutMat = cv::Mat(1, 256, CV_8UC1); + + brightness_slider = 128; + contrast_slider = 128; + + updateLUT(); + + XS_SERIALIZE(brightness_slider, wxT("Brightness")); + XS_SERIALIZE(contrast_slider, wxT("Contrast")); +} + + +BrightnessContrastFilter::~BrightnessContrastFilter() +{ + +} + +wxString BrightnessContrastFilter::GetName() const +{ + return _("Brightness/Contrast"); +} + +void BrightnessContrastFilter::setBrightness(float value) +{ + brightness = value; + brightness_slider = static_cast((value * 128.0f) + 128.0f); + updateLUT(); +} + + +void BrightnessContrastFilter::setContrast(float value) +{ + contrast = value; + contrast_slider = static_cast((value * 128.0f) + 128.0f); + updateLUT(); +} + + +//void BrightnessContrastFilter::getParameters(iFloorParamMap& pMap) +//{ +// pMap[std::string("brightness")] = toString(brightness); +// pMap[std::string("contrast")] = toString(contrast); +//} +// +// +//void BrightnessContrastFilter::setParameter(const char *name, const char *value) +//{ +// if(strcmp(name, "brightness") == 0) +// { +// setBrightness(atof(value)); +// } else if(strcmp(name, "contrast") == 0) +// { +// setContrast(atof(value)); +// } +// +//} + + +void BrightnessContrastFilter::updateLUT( void ) +{ + + float tmp_brightness = 2.0f * ((static_cast(brightness_slider) / 255.0f)); + float tmp_contrast = 2.0f * ((static_cast(contrast_slider) / 255.0f)); + + if (brightness != tmp_brightness || contrast != tmp_contrast) + { + brightness = tmp_brightness; + contrast = tmp_contrast; + + if (contrast > 0) + { + float delta = 127.0f * contrast; + float a = 255.0f / (255.0f - delta * 2.0f); + float b = a * (brightness * 100.0f - delta); + for (int i = 0; i < 256; i++) + { + int v = cvRound(a * i + b); + if (v < 0) v = 0; + if (v > 255) v = 255; + lutMat.at(i) = static_cast(v); + } + } + else + { + float delta = -128.0f * contrast; + float a = (256.0f - delta * 2.0f) / 255.0f; + float b = a * brightness * 100.0f + delta; + for (int i = 0; i < 256; i++) + { + int v = cvRound(a * i + b); + if (v < 0) v = 0; + if (v > 255) v = 255; + lutMat.at(i) = static_cast(v); + } + } + } + +} +#if defined(HAVE_CUDA) +bool BrightnessContrastFilter::KernelGPU() +{ + updateLUT(); + cv::cuda::lookUpTable(*m_src, lutMat, m_dst); + return true; +} +#endif + +bool BrightnessContrastFilter::Kernel() +{ + + if (source.empty()) + return false; + + updateLUT(); + // derived class responsible for allocating storage for filtered image + if (destination.empty()) + { + destination = cv::Mat::zeros(source.size(), source.type()); + } + cv::LUT(source, lutMat, destination); + return true; +} + +void BrightnessContrastFilter::CreateParamInputs() +{ + AddParameterSpinInput(_("Brightness:"), &brightness_slider, 127, -127); + AddParameterSpinInput(_("Contrast:"), &contrast_slider, 127, -127); +} + +wxFORCE_LINK_THIS_MODULE(BrightnessContrastFilter); \ No newline at end of file diff --git a/MotionDetectorCore/BrightnessContrastFilter.h b/MotionDetectorCore/BrightnessContrastFilter.h new file mode 100644 index 0000000..321fa21 --- /dev/null +++ b/MotionDetectorCore/BrightnessContrastFilter.h @@ -0,0 +1,43 @@ +#ifndef __TOUCHLIB_FILTER_BRIGHTNESSCONTRAST__ +#define __TOUCHLIB_FILTER_BRIGHTNESSCONTRAST__ + +#include "FilterTemplate.h" +#include + +#define DEFAULT_BRIGHTNESS 0.0 +#define DEFAULT_CONTRAST 0.2 + +class MOTION_DETECTOR_CORE_API BrightnessContrastFilter : public Filter +{ + DECLARE_DYNAMIC_CLASS_NO_COPY(BrightnessContrastFilter) +public: + BrightnessContrastFilter(); + virtual ~BrightnessContrastFilter(); + + bool Kernel(); +#if defined(HAVE_CUDA) + bool KernelGPU(); +#endif + virtual wxString GetName() const; + + void setBrightness(float value); + void setContrast(float value); + float getContrast(void) {return contrast;} + float getBrightness(void) {return brightness;} + +protected: + void CreateParamInputs(); +private: + void updateLUT( void ); + + uchar lut[256*4]; + + int brightness_slider; + int contrast_slider; + + float brightness; + float contrast; + cv::Mat lutMat; +}; + +#endif // __TOUCHLIB_FILTER_BRIGHTNESSCONTRAST__ diff --git a/MotionDetectorCore/CMakeLists.txt b/MotionDetectorCore/CMakeLists.txt new file mode 100644 index 0000000..040d6e2 --- /dev/null +++ b/MotionDetectorCore/CMakeLists.txt @@ -0,0 +1,149 @@ +# +# Stuff the average programmer needs to change +# +set(SRCS + FilterPreviewWindow.cpp + AmplifyFilter.cpp + BackgroundFilter.cpp + BarrelDistortionCorrectionFilter.cpp + BrightnessContrastFilter.cpp + ContourFinder.cpp + CropFilter.cpp + DummyFilter.cpp + DynamicBGFilter.cpp + EqualizeHistFilter.cpp + FilterFactory.cpp + FilterTemplate.cpp + FlipFilter.cpp + HighpassFilter.cpp + IFloorImagePreprocessor.cpp + IFloorImagePreprocessorSettings.cpp + InvertFilter.cpp + MonoFilter.cpp + PerspectiveFilter.cpp + RectifyFilter.cpp + ResizeFilter.cpp + ScalerFilter.cpp + ShapeFilter.cpp + SimpleBGFilter.cpp + SubOrBGFilter.cpp + XorBGFilter.cpp + SimpleHighpassFilter.cpp + SmoothingFilter.cpp + ThresholdFilter.cpp + KinectMonoFilter.cpp + KinectDepthBGFilter.cpp + KinectBGFilter.cpp + KinectThresholdFilter.cpp + Tracking.cpp + DisplayGeometryWindow.cpp + LinuxUtils.cpp +) + +set(HFILES + MotionDetectorCorePlugin.h + FilterPreviewWindow.h + wxBitmapEvent.h + AmplifyFilter.h + BackgroundFilter.h + BarrelDistortionCorrectionFilter.h + BrightnessContrastFilter.h + ContourFinder.h + CropFilter.h + DummyFilter.h + DynamicBGFilter.h + EqualizeHistFilter.h + FilterFactory.h + FilterTemplate.h + FlipFilter.h + HighpassFilter.h + IFloorImagePreprocessor.h + IFloorImagePreprocessorSettings.h + Image.h + InvertFilter.h + MonoFilter.h + PerspectiveFilter.h + RectifyFilter.h + ResizeFilter.h + ScalerFilter.h + ShapeFilter.h + SimpleBGFilter.h + SubOrBGFilter.h + XorBGFilter.h + SimpleHighpassFilter.h + SmoothingFilter.h + ThresholdFilter.h + KinectMonoFilter.h + KinectDepthBGFilter.h + KinectBGFilter.h + KinectThresholdFilter.h + Tracking.h + vector2d.h + DisplayGeometryWindow.h + LinuxUtils.h +) + +source_group("Filters\\Headers" ".*Filter\\.h") +source_group("Filters\\Sources" ".*Filter\\.cpp") + +set(INCLUDE_DIRECTORIES ${BASE_INCLUDE_DIRECTORIES} + ${PROJECT_ROOT_DIR}/MotionDetectorPluginBase + ${PROJECT_ROOT_DIR}/CommonPluginBase + ${THIRD_PARTY_DIR}/wxXS/include + ${OpenCV_INCLUDE_DIRS} + ${THIRD_PARTY_DIR}/MotionPrimitives +) + +set(LIBRARY_NAME MotionDetectorCore) + +if(CUDA_FOUND) + set(PREPROCESSOR_DEFINITIONS ${PREPROCESSOR_DEFINITIONS};/DHAVE_CUDA) +endif(CUDA_FOUND) + +if(WIN32) + set(PREPROCESSOR_DEFINITIONS ${PREPROCESSOR_DEFINITIONS};/D_USRDLL;/DMOTION_DETECTOR_CORE_EXPORTS) +endif(WIN32) + + +set(SRCS ${SRCS} ${HFILES} ${PROJECT_ROOT_DIR}/include/stdwx.h ${PROJECT_ROOT_DIR}/include/stdwx.cpp) + +add_definitions(${PREPROCESSOR_DEFINITIONS}) +include_directories(${INCLUDE_DIRECTORIES}) + + +add_library(${LIBRARY_NAME} SHARED ${SRCS}) + + +target_link_libraries(${LIBRARY_NAME} + ${wxWidgets_LIBRARIES} + CommonPluginBase + Utils + MotionDetectorPluginBase + MotionPrimitives + ${OpenCV_LIBS} +) + +add_dependencies(${LIBRARY_NAME} + MotionDetectorPluginBase + CommonPluginBase +) + +target_precompile_headers(${LIBRARY_NAME} + PRIVATE + "${PROJECT_ROOT_DIR}/include/stdwx.h" +) + +if(LINUX) + add_custom_command(TARGET ${LIBRARY_NAME} POST_BUILD + COMMAND ${CMAKE_COMMAND} ARGS -E copy_if_different "${CMAKE_CURRENT_SOURCE_DIR}/${OS_BASE_NAME}${LIB_SUFFIX}/lib${LIBRARY_NAME}.so" "${PROJECT_SOURCE_DIR}/${DLL_DIR}${LIB_SUFFIX}/lib${LIBRARY_NAME}.so" + ) +endif(LINUX) + +set(PLUGIN_TARGET_DIR "${OUTPUT_BIN_DIR}/plugins/motion_detector") + +add_custom_command(TARGET ${LIBRARY_NAME} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E make_directory "${PLUGIN_TARGET_DIR}" + COMMAND ${CMAKE_COMMAND} -E copy_if_different + "$" + "${PLUGIN_TARGET_DIR}/$" +) \ No newline at end of file diff --git a/MotionDetectorCore/ContourFinder.cpp b/MotionDetectorCore/ContourFinder.cpp new file mode 100644 index 0000000..6b7f876 --- /dev/null +++ b/MotionDetectorCore/ContourFinder.cpp @@ -0,0 +1,180 @@ +#include "stdwx.h" +#include "ContourFinder.h" +#include + +static int qsort_carea_compare(void const * _a, void const * _b) +{ + // pointers, ugh.... sorry about this + auto a = reinterpret_cast(_a); + auto b = reinterpret_cast(_b); + // use opencv to calc size, then sort based on size + float areaa = a->Area; + float areab = b->Area; + // note, based on the -1 / 1 flip + // we sort biggest to smallest, not smallest to biggest + auto out = areaa > areab ? -1 : 1; + return out; +} + +ContourFinder::ContourFinder() +{ + reset(); +} + +ContourFinder::~ContourFinder() +{ +} + +void ContourFinder::reset() +{ + blobs.clear(); +} + +void ContourFinder::FillBlob(SeqArea& seqBlob, iFloorBlob& blob) +{ + if (seqBlob.Seq.empty()) return; + + cv::Point oldPt(0, 0); + size_t totalPoints = seqBlob.Seq.size(); + + for (size_t j = 0; j < totalPoints; ++j) + { + cv::Point pt = seqBlob.Seq[j]; + + switch (j) + { + case 0: + blob.pts.push_back(iFloorPoint(pt.x, pt.y)); + break; + default: + + if ((abs(oldPt.x - pt.x) + abs(oldPt.y - pt.y)) > 5 || (j == totalPoints - 1)) + { + blob.pts.push_back(iFloorPoint(pt.x, pt.y)); + } + break; + } + oldPt = pt; + } +} + +int ContourFinder::findContours(/*ofxCvGrayscaleImage& input,*/ + cv::Mat img, + int minArea, + int maxArea, + size_t nConsidered, + bool bFindHoles, + bool bUseApproximation) +{ + reset(); + + // opencv will clober the image it detects contours on, so we want to + // copy it into a copy before we detect contours. That copy is allocated + // if necessary (necessary = (a) not allocated or (b) wrong size) + // so be careful if you pass in different sized images to "findContours" + // there is a performance penalty, but we think there is not a memory leak + // to worry about better to create mutiple contour finders for different + // sizes, ie, if you are finding contours in a 640x480 image but also a + // 320x240 image better to make two ContourFinder objects then to use + // one, because you will get penalized less. + + //if( inputCopy.width == 0 ) { + // inputCopy.allocate( input.width, input.height ); + // inputCopy = input; + //} else { + // if( inputCopy.width == input.width && inputCopy.height == input.height ) { + // inputCopy = input; + // } else { + // // we are allocated, but to the wrong size -- + // // been checked for memory leaks, but a warning: + // // be careful if you call this function with alot of different + // // sized "input" images!, it does allocation every time + // // a new size is passed in.... + // //inputCopy.clear(); + // inputCopy.allocate( input.width, input.height ); + // inputCopy = input; + // } + //} + + if (img.empty()) return 0; + + cv::Mat grayImg; + if (img.channels() == 3) { + cv::cvtColor(img, grayImg, cv::COLOR_BGR2GRAY); + } + else { + grayImg = img.clone(); + } + + + std::vector> all_contours; + + int retrieve_mode = bFindHoles ? cv::RETR_LIST : cv::RETR_EXTERNAL; + int approx_mode = bUseApproximation ? cv::CHAIN_APPROX_SIMPLE : cv::CHAIN_APPROX_NONE; + + cv::findContours(grayImg, all_contours, retrieve_mode, approx_mode); + + cvSeqBlobs.clear(); + cvSeqBlobs.reserve(all_contours.size()); + + for (const auto& contour : all_contours) + { + std::vector approx_contour; + cv::approxPolyDP(contour, approx_contour, 3.0, true); + + double area = std::fabs(cv::contourArea(approx_contour)); + if ((area >= minArea) && (area < maxArea)) + { + cvSeqBlobs.push_back(SeqArea(approx_contour, area)); + } + } + + std::sort(cvSeqBlobs.begin(), cvSeqBlobs.end(), [](const SeqArea& a, const SeqArea& b) { + return a.Area > b.Area; + }); + + size_t nCvSeqsFound = cvSeqBlobs.size(); + nCvSeqsFound = std::min(nConsidered, nCvSeqsFound); + + blobs.resize(nCvSeqsFound); + + for (size_t i = 0; i < nCvSeqsFound; ++i) + { + SeqArea& seqBlob = cvSeqBlobs[i]; + iFloorBlob& blob = blobs[i]; + + cv::Moments myMoments = cv::moments(seqBlob.Seq); + + cv::Rect rect = cv::boundingRect(seqBlob.Seq); + blob.boundingRect.x = rect.x; + blob.boundingRect.y = rect.y; + blob.boundingRect.width = rect.width; + blob.boundingRect.height = rect.height; + + cv::RotatedRect box = cv::minAreaRect(seqBlob.Seq); + + blob.angleBoundingRect.x = box.center.x; + blob.angleBoundingRect.y = box.center.y; + blob.angleBoundingRect.width = box.size.height; + blob.angleBoundingRect.height = box.size.width; + blob.angle = box.angle; + + blob.area = seqBlob.Area; + + if (myMoments.m00 != 0) { + blob.centroid.x = (myMoments.m10 / myMoments.m00); + blob.centroid.y = (myMoments.m01 / myMoments.m00); + } + else { + blob.centroid.x = 0; + blob.centroid.y = 0; + } + + blob.lastCentroid.x = 0; + blob.lastCentroid.y = 0; + + FillBlob(seqBlob, blob); + } + + return static_cast(blobs.size()); +} \ No newline at end of file diff --git a/MotionDetectorCore/ContourFinder.h b/MotionDetectorCore/ContourFinder.h new file mode 100644 index 0000000..d4f68c4 --- /dev/null +++ b/MotionDetectorCore/ContourFinder.h @@ -0,0 +1,69 @@ +#ifndef IFLOORCONTOUR_FINDER_H +#define IFLOORCONTOUR_FINDER_H + +#if defined(__WXMSW__) +#include +#include +#include "iFloorBlob.h" +#include "MotionDetectorCorePlugin.h" + +struct SeqArea +{ + SeqArea() + : Area(0) {} + SeqArea(const std::vector& seq, double area) + : Seq(seq) + , Area(area) + { + + } + SeqArea(const SeqArea & seqArea) + : Seq(seqArea.Seq) + , Area(seqArea.Area) + { + + } + SeqArea & operator=(const SeqArea & seqArea) + { + if (this != &seqArea) + { + Seq = seqArea.Seq; + Area = seqArea.Area; + } + return *this; + } + std::vector Seq; + double Area; +}; + +class MOTION_DETECTOR_CORE_API ContourFinder +{ +public: + ContourFinder(); + ~ContourFinder(); + + int findContours( /*ofxCvGrayscaleImage& input,*/ + cv::Mat img, + int minArea, int maxArea, + size_t nConsidered, bool bFindHoles, + bool bUseApproximation = true); + // approximation = don't do points for all points of the contour, if the contour runs + // along a straight line, for example... + + iFloorBlobVector blobs; // the blobs, in a std::vector... +protected: + + // this is stuff, not for general public to touch -- we need + // this to do the blob detection, etc. + //ofxCvGrayscaleImage inputCopy; + + // internally, we find cvSeqs, they will become blobs. + std::vector cvSeqBlobs; + + // important!! + void reset(); + void FillBlob(SeqArea& seqBlob, iFloorBlob& blob); +}; + +#endif +#endif \ No newline at end of file diff --git a/MotionDetectorCore/CropFilter.cpp b/MotionDetectorCore/CropFilter.cpp new file mode 100644 index 0000000..dcaaba8 --- /dev/null +++ b/MotionDetectorCore/CropFilter.cpp @@ -0,0 +1,169 @@ +// Filter description +// Name: Crop Filter +// Purpose: Allows the user to crop the source image +// Original author: Laurence Muller (aka Falcon4ever) + +/* +example of usage: + + + + + + + +posX and posY specifies an offset from upperleft corner. +heigth and width specifies the crop size. +*/ +#include "stdwx.h" +#include "CropFilter.h" + +IMPLEMENT_DYNAMIC_CLASS(CropFilter, Filter); + +CropFilter::CropFilter() +{ + img_rect.x = 0; + img_rect.y = 0; + img_rect.height = DEFAULT_CROPHEIGHT; + img_rect.width = DEFAULT_CROPWIDTH; + + firsttime = true; + + XS_SERIALIZE(img_rect.x, wxT("x")); + XS_SERIALIZE(img_rect.y, wxT("y")); + XS_SERIALIZE(img_rect.width, wxT("width")); + XS_SERIALIZE(img_rect.height, wxT("height")); +} + +CropFilter::~CropFilter() +{ +} + +wxString CropFilter::GetName() const +{ + return _("Crop"); +} + +//void CropFilter::getParameters(iFloorParamMap& pMap) +//{ +// pMap[std::string("posX")] = toString(img_rect.x); +// pMap[std::string("posY")] = toString(img_rect.y); +// pMap[std::string("height")] = toString(img_rect.height); +// pMap[std::string("width")] = toString(img_rect.width); +//} +// +//void CropFilter::setParameter(const char *name, const char *value) +//{ +// if(strcmp(name, "posX") == 0) +// { +// img_rect.x = (int) atof(value); +// level_posX_slider = img_rect.x; +// +// if(show) +// cvSetTrackbarPos("posX", this->name.c_str(), level_posX_slider); +// } +// else if(strcmp(name, "posY") == 0) +// { +// img_rect.y = (int) atof(value); +// level_posY_slider = img_rect.x; +// +// if(show) +// cvSetTrackbarPos("posY", this->name.c_str(), level_posY_slider); +// } +// else if(strcmp(name, "width") == 0) +// { +// img_rect.width = (int) atof(value); +// level_width_slider = img_rect.width; +// +// //if(show) +// // cvSetTrackbarPos("width", this->name.c_str(), level_width_slider); +// +// if(destination) +// cvReleaseImage(&destination); +// +// destination = cvCreateImage(cvSize(img_rect.width,img_rect.height), 8, 1); +// } +// else if(strcmp(name, "height") == 0) +// { +// img_rect.height = (int) atof(value); +// level_height_slider = img_rect.height; +// +// //if(show) +// // cvSetTrackbarPos("height", this->name.c_str(), level_height_slider); +// +// if(destination) +// cvReleaseImage(&destination); +// +// destination = cvCreateImage(cvSize(img_rect.width,img_rect.height), 8, 1); +// } +//} + +bool CropFilter::Kernel() +{ + if (source.empty()) + return false; + + if (firsttime) + { + max_x = source.cols; + max_y = source.rows; + firsttime = false; + } + + //if (show) + { +/* + // Todo: Fix filter chain... + // Status: Currently disabled. + // + // Problem: + // When changing the width and height, a new destination image has to be created. + // The current destination image should be released first, the a new destination image should be + // created from the img_rect values. + // By dynamicly changing the destination size the next filter in the filterchain should adjust + // all its allocated images aswell (width and height). + // Currently this isnt done, which causes openCV to choke and crash. + // + // Current workaround: + // Set the width and height manual in the config.xml before starting the configapp or your application + + // Uncomment the following part if dynamic resizing is allowed: + if(img_rect.width != level_width_slider) + { + if(level_width_slider + img_rect.x < max_x) + img_rect.width = level_width_slider; + + if(destination) + cvReleaseImage(&destination); + } + + if(img_rect.height != level_height_slider) + { + if(level_height_slider + img_rect.y < max_y) + img_rect.height = level_height_slider; + + if(destination) + cvReleaseImage(&destination); + } +*/ + } + + if (img_rect.x < 0) img_rect.x = 0; + if (img_rect.y < 0) img_rect.y = 0; + if (img_rect.x >= source.cols) img_rect.x = source.cols - 1; + if (img_rect.y >= source.rows) img_rect.y = source.rows - 1; + + if (img_rect.x + img_rect.width > source.cols) + img_rect.width = source.cols - img_rect.x; + if (img_rect.y + img_rect.height > source.rows) + img_rect.height = source.rows - img_rect.y; + + if (img_rect.width <= 0 || img_rect.height <= 0) + return false; + + source(img_rect).copyTo(destination); + + return true; +} + +wxFORCE_LINK_THIS_MODULE(CropFilter); \ No newline at end of file diff --git a/MotionDetectorCore/CropFilter.h b/MotionDetectorCore/CropFilter.h new file mode 100644 index 0000000..98f71e1 --- /dev/null +++ b/MotionDetectorCore/CropFilter.h @@ -0,0 +1,35 @@ +// Filter description +// Name: Crop Filter +// Purpose: Allows the user to crop the source image +// Original author: Laurence Muller (aka Falcon4ever) + +#ifndef __TOUCHLIB_FILTER_CROP__ +#define __TOUCHLIB_FILTER_CROP__ + +#include "FilterTemplate.h" +#include + +#define DEFAULT_CROPWIDTH 640 +#define DEFAULT_CROPHEIGHT 480 + +class MOTION_DETECTOR_CORE_API CropFilter : public Filter +{ + DECLARE_DYNAMIC_CLASS_NO_COPY(CropFilter) +public: + CropFilter(); + virtual ~CropFilter(); + + bool Kernel(); +#if defined(HAVE_CUDA) + bool KernelGPU(){return false;} +#endif + virtual wxString GetName() const; + +private: + bool firsttime; + int max_x; + int max_y; + cv::Rect img_rect; +}; + +#endif // __TOUCHLIB_FILTER_CROP__ diff --git a/MotionDetectorCore/DisplayGeometryWindow.cpp b/MotionDetectorCore/DisplayGeometryWindow.cpp new file mode 100644 index 0000000..dffed40 --- /dev/null +++ b/MotionDetectorCore/DisplayGeometryWindow.cpp @@ -0,0 +1,135 @@ +#include "stdwx.h" +#include "DisplayGeometryWindow.h" +#include + +#if defined(USE_VLD) +#include +#endif + +IMPLEMENT_DYNAMIC_CLASS(DisplayGeometryWindow, wxWindow) + +BEGIN_EVENT_TABLE(DisplayGeometryWindow, wxWindow) + EVT_SIZE(DisplayGeometryWindow::OnSize) + EVT_PAINT(DisplayGeometryWindow::OnPaint) + EVT_ERASE_BACKGROUND(DisplayGeometryWindow::OnEraseBackground) +END_EVENT_TABLE() + +DisplayGeometryWindow::DisplayGeometryWindow() + : m_GeometryProvider(GeometryProvider(nullptr, nullptr, nullptr)) +{ + Init(); +} + +DisplayGeometryWindow::DisplayGeometryWindow(wxWindow* parent, const GeometryProvider & geometryProvider, wxWindowID id, const wxPoint& pos, const wxSize& size, long style) + : m_GeometryProvider(geometryProvider) +{ + Init(); + Create(parent, id, pos, size, style); +} + +bool DisplayGeometryWindow::Create(wxWindow* parent, wxWindowID id, const wxPoint& pos, const wxSize& size, long style) +{ + wxWindow::Create(parent, id, pos, size, style); + return true; +} + +DisplayGeometryWindow::~DisplayGeometryWindow() +{ +} + +void DisplayGeometryWindow::Init() +{ + m_Scale = 1; + m_Border = 1; +} + +void DisplayGeometryWindow::OnEraseBackground(wxEraseEvent& event) +{ +} + +void DisplayGeometryWindow::OnPaint(wxPaintEvent& event) +{ + wxBufferedPaintDC dc(this); + dc.SetBackground(wxBrush(GetBackgroundColour())); + dc.Clear(); + + if (m_Bitmap.IsOk()) + dc.DrawBitmap(m_Bitmap, m_DrawRect.GetTopLeft()); +} + +void DisplayGeometryWindow::OnSize(wxSizeEvent& event) +{ + CalcDrawRect(); +} + +void DisplayGeometryWindow::CalcDrawRect() +{ + wxRectVector displays; + m_GeometryProvider.GetGeometry(displays); + m_DisplayRect = GetDisplayRect(displays); + + wxSize clientSize = GetClientSize(); + wxSize bestSize = clientSize - wxSize(m_Border * 2, m_Border * 2); // Add border + wxSize bitmapSize = m_DisplayRect.GetSize(); + double kx = (double) bestSize.x / bitmapSize.x; + double ky = (double) bestSize.y / bitmapSize.y; + if (kx < ky) + { + m_DrawRect.width = bestSize.x; + m_DrawRect.height = (kx * bitmapSize.y); + m_Scale = kx; + } + else + { + m_DrawRect.height = bestSize.y; + m_DrawRect.width = (ky * bitmapSize.x); + m_Scale = ky; + } + m_DrawRect = m_DrawRect.CenterIn(wxRect(clientSize)); + m_DrawRect.x = m_Border; + + CreateBitmap(); + + Refresh(); +} + +wxRect DisplayGeometryWindow::GetDisplayRect(const wxRectVector & displays) +{ + wxRect resolution; + for (auto i = 0; i < displays.size(); ++i) + resolution.Union(displays[i]); + return resolution; +} + +void DisplayGeometryWindow::CreateBitmap() +{ + m_Bitmap = wxBitmap(m_DrawRect.GetSize() + wxSize(1, 1)); + wxMemoryDC mdc(m_Bitmap); + mdc.SetBackground(wxBrush(GetBackgroundColour())); + mdc.Clear(); + mdc.SetBrush(*wxTRANSPARENT_BRUSH); + mdc.SetPen(*wxBLACK_PEN); + mdc.SetTextForeground(*wxBLACK); + + wxRectVector displays; + m_GeometryProvider.GetGeometry(displays); + wxRect resolution = GetDisplayRect(displays); + + for (auto i = 0; i < displays.size(); ++i) + { + wxRect rect = displays[i]; + rect.Offset(-resolution.GetTopLeft()); + wxPoint p1 = rect.GetTopLeft(); + wxPoint p2 = rect.GetBottomRight(); + p1.x *= m_Scale; + p1.y *= m_Scale; + p2.x *= m_Scale; + p2.y *= m_Scale; + rect = wxRect(p1, p2); + + mdc.DrawRectangle(rect); + + mdc.DrawLabel(wxString::Format(wxT("#%d"), i + 1), rect, wxALIGN_CENTER); + } + +} \ No newline at end of file diff --git a/MotionDetectorCore/DisplayGeometryWindow.h b/MotionDetectorCore/DisplayGeometryWindow.h new file mode 100644 index 0000000..0509a0a --- /dev/null +++ b/MotionDetectorCore/DisplayGeometryWindow.h @@ -0,0 +1,39 @@ +#ifndef _DISPLAYGEOMETRYWINDOW_H +#define _DISPLAYGEOMETRYWINDOW_H + +#include "MotionDetectorCorePlugin.h" +#include + +class MOTION_DETECTOR_CORE_API DisplayGeometryWindow: public wxWindow +{ + DECLARE_DYNAMIC_CLASS(DisplayGeometryWindow) + DECLARE_EVENT_TABLE() +public: + DisplayGeometryWindow(); + DisplayGeometryWindow(wxWindow * parent, const GeometryProvider & geometryPrivoder, wxWindowID id = wxID_STATIC, const wxPoint & pos = wxDefaultPosition, const wxSize & size = wxDefaultSize, long style = wxSIMPLE_BORDER); + + bool Create(wxWindow * parent, wxWindowID id = wxID_STATIC, const wxPoint & pos = wxDefaultPosition, const wxSize & size = wxDefaultSize, long style = wxSIMPLE_BORDER); + + ~DisplayGeometryWindow(); + + void Init(); + +protected: + void OnSize(wxSizeEvent & event); + void OnPaint(wxPaintEvent & event); + void OnEraseBackground(wxEraseEvent & event); + + void CalcDrawRect(); + static wxRect GetDisplayRect(const wxRectVector & displays); + void CreateBitmap(); + +protected: + const GeometryProvider & m_GeometryProvider; + wxRect m_DrawRect; + wxBitmap m_Bitmap; + wxRect m_DisplayRect; + double m_Scale; + int m_Border; +}; + +#endif // _DISPLAYGEOMETRYWINDOW_H diff --git a/MotionDetectorCore/DummyFilter.cpp b/MotionDetectorCore/DummyFilter.cpp new file mode 100644 index 0000000..01a97fc --- /dev/null +++ b/MotionDetectorCore/DummyFilter.cpp @@ -0,0 +1,28 @@ +#include "stdwx.h" +#include "DummyFilter.h" + +IMPLEMENT_DYNAMIC_CLASS(DummyFilter, Filter); + +DummyFilter::DummyFilter() +{ +} + +DummyFilter::~DummyFilter() +{ +} + +wxString DummyFilter::GetName() const +{ + return _("Camera Preview"); +} + +bool DummyFilter::Kernel() +{ + return false; +} + +void DummyFilter::CreateParamInputs() +{ +} + +wxFORCE_LINK_THIS_MODULE(DummyFilter); \ No newline at end of file diff --git a/MotionDetectorCore/DummyFilter.h b/MotionDetectorCore/DummyFilter.h new file mode 100644 index 0000000..c446b98 --- /dev/null +++ b/MotionDetectorCore/DummyFilter.h @@ -0,0 +1,29 @@ +#ifndef _DUMMYFILTER_H +#define _DUMMYFILTER_H + +#include "FilterTemplate.h" + +class MOTION_DETECTOR_CORE_API DummyFilter : public Filter +{ + DECLARE_DYNAMIC_CLASS_NO_COPY(DummyFilter) +public: + DummyFilter(); + virtual ~DummyFilter(); + + bool Kernel(); +#if defined(HAVE_CUDA) + bool KernelGPU(){return false;} +#endif + virtual wxString GetName() const; + + // This filter is always disabled + virtual bool IsEnabled() { return false; }; + virtual bool IsMandatory() { return true; }; + +protected: + void CreateParamInputs(); +private: + +}; + +#endif // _DUMMYFILTER_H diff --git a/MotionDetectorCore/DynamicBGFilter.cpp b/MotionDetectorCore/DynamicBGFilter.cpp new file mode 100644 index 0000000..7c1836c --- /dev/null +++ b/MotionDetectorCore/DynamicBGFilter.cpp @@ -0,0 +1,140 @@ +#include "stdwx.h" +#include "DynamicBGFilter.h" + +#if defined(__LINUX__) +#include "LinuxUtils.h" +#endif +// LINUX_ + +IMPLEMENT_DYNAMIC_CLASS(DynamicBGFilter, Filter); + +DynamicBGFilter::DynamicBGFilter() +{ + m_bLearnBackground = false; + m_bTrackDark = false; + m_LearnRate = 400; + m_CameraExposureTime = 2200; +#if defined(HAVE_CUDA) + bStart = false; +#endif + + m_ExposureStartTime = timeGetTime(); + XS_SERIALIZE(m_bTrackDark, wxT("TrackDark")); + XS_SERIALIZE(m_LearnRate, wxT("LearnRate")); + XS_SERIALIZE(m_bLearnBackground, wxT("LearnBackground")); + XS_SERIALIZE(m_CameraExposureTime, wxT("CameraExposureTime")); + +} + +DynamicBGFilter::~DynamicBGFilter() +{ + +} + +wxString DynamicBGFilter::GetName() const +{ + return _("Dynamic BG"); +} + +#if defined(HAVE_CUDA) +bool DynamicBGFilter::KernelGPU() +{ + if ((int)(timeGetTime() - m_ExposureStartTime) > m_CameraExposureTime) + m_bLearnBackground = true; + + //Capture full background + if (m_bLearnBackground) + { + m_src->convertTo(m_matFloatBgImg, CV_32FC1); + m_bLearnBackground = false; + bStart = true; + m_ExposureStartTime = timeGetTime(); + } + if(!bStart) + return false; + //step1 + m_src->convertTo(m_matFloatBgImgTemp, CV_32FC1); + cv::Scalar alpha = (float)m_LearnRate * 0.001f; + cv::Scalar beta = 1.0f - (float)m_LearnRate * 0.001f; + cv::cuda::multiply(m_matFloatBgImgTemp, alpha, m_matFloatBgImgTemp); + cv::cuda::multiply(m_matFloatBgImg, beta, m_matFloatBgImg); + //step2 + cv::cuda::add(m_matFloatBgImgTemp, m_matFloatBgImg, m_matFloatBgImg); + //step3 + m_matFloatBgImg.convertTo(m_matGrayBg, m_dst.type()); + // Subtract background + if (m_bTrackDark){ + cv::cuda::subtract(m_matGrayBg, *m_src, m_dst); + } + else{ + cv::cuda::subtract(*m_src, m_matGrayBg, m_dst); + } + return true; +} +#endif + +bool DynamicBGFilter::Kernel() +{ + // derived class responsible for allocating storage for filtered image + if (source.empty()) + return false; + + if (destination.empty()) + { + destination = cv::Mat::zeros(source.size(), CV_8UC1); + } + if (floatBgImg.empty()) + { + floatBgImg = cv::Mat::zeros(source.size(), CV_16UC1); + floatBgImgTemp = cv::Mat::zeros(source.size(), CV_16UC1); + } + + if ((int)(timeGetTime() - m_ExposureStartTime) > m_CameraExposureTime) + m_bLearnBackground = true; + + //Capture full background + if (m_bLearnBackground) + { + source.convertTo(floatBgImg, CV_16UC1, 65535.0f / 255.0f, 0); + + if (grayBg.empty()) + { + grayBg = cv::Mat::zeros(source.size(), CV_8UC1); + } + //cvCopy(source, grayBg); + m_ExposureStartTime = timeGetTime(); + m_bLearnBackground = false; + } + if (grayBg.empty()) + return false; + + //step1 + source.convertTo(floatBgImgTemp, CV_16UC1, 65535.0f / 255.0f, 0); + //step2 + float fLearnRate = (float)m_LearnRate * 0.001f; + cv::addWeighted(floatBgImgTemp, fLearnRate, floatBgImg, 1.0f - fLearnRate, 0, floatBgImg); + //step3 + floatBgImg.convertTo(grayBg, CV_8UC1, 255.0f / 65535.0f, 0); + + //// Subtract background + if (m_bTrackDark) + cv::subtract(grayBg, source, destination); + else + cv::subtract(source, grayBg, destination); + //IplImage* destination1 = cvCreateImage(cvSize(source->width, source->height), IPL_DEPTH_8U, 1); + //cvSub(grayBg, source, destination1); + ////else + //cvSub(source, grayBg, destination); + //cvOr(destination, destination1, destination); + return true; +} + +void DynamicBGFilter::CreateParamInputs() +{ + AddParameterBoolInput(_("Learn background"), &m_bLearnBackground); + AddParameterBoolInput(_("Track dark blobs"), &m_bTrackDark); + AddParameterSpinInput(_("Background learn rate:"), &m_LearnRate, 1000, 0); + AddParameterSpinInput(_("Camera exposure time:"), &m_CameraExposureTime, 20000, 0); +} + +wxFORCE_LINK_THIS_MODULE(DynamicBGFilter); \ No newline at end of file diff --git a/MotionDetectorCore/DynamicBGFilter.h b/MotionDetectorCore/DynamicBGFilter.h new file mode 100644 index 0000000..600523d --- /dev/null +++ b/MotionDetectorCore/DynamicBGFilter.h @@ -0,0 +1,41 @@ +#ifndef __TOUCHLIB_FILTER_DYNAMICBG__ +#define __TOUCHLIB_FILTER_DYNAMICBG__ + +#include "FilterTemplate.h" +#include +#include + +class MOTION_DETECTOR_CORE_API DynamicBGFilter : public Filter +{ + DECLARE_DYNAMIC_CLASS_NO_COPY(DynamicBGFilter) +public: + DynamicBGFilter(); + virtual ~DynamicBGFilter(); + + bool Kernel(); +#if defined(HAVE_CUDA) + bool KernelGPU(); +#endif + virtual wxString GetName() const; + +protected: + void CreateParamInputs(); + +private: + cv::Mat floatBgImg; + cv::Mat floatBgImgTemp; + cv::Mat grayBg; +#if defined(HAVE_CUDA) + cv::cuda::GpuMat m_matFloatBgImg; + cv::cuda::GpuMat m_matFloatBgImgTemp; + cv::cuda::GpuMat m_matGrayBg; + bool bStart; +#endif + bool m_bTrackDark; + bool m_bLearnBackground; + int m_ExposureStartTime; + int m_CameraExposureTime; + int m_LearnRate; +}; + +#endif // __TOUCHLIB_FILTER_DYNAMICBG__ diff --git a/MotionDetectorCore/EqualizeHistFilter.cpp b/MotionDetectorCore/EqualizeHistFilter.cpp new file mode 100644 index 0000000..b6142e1 --- /dev/null +++ b/MotionDetectorCore/EqualizeHistFilter.cpp @@ -0,0 +1,61 @@ +#include "stdwx.h" +#include "EqualizeHistFilter.h" + +IMPLEMENT_DYNAMIC_CLASS(EqualizeHistFilter, Filter); + +EqualizeHistFilter::EqualizeHistFilter() +{ + level = 0; +} + +EqualizeHistFilter::~EqualizeHistFilter() +{ + +} + +wxString EqualizeHistFilter::GetName() const +{ + return _("Equalize Histogram"); +} + +#if defined(HAVE_CUDA) +bool EqualizeHistFilter::KernelGPU() +{ + return false; +} +#endif + +bool EqualizeHistFilter::Kernel() +{ + // derived class responsible for allocating storage for filtered image + if (source.empty()) + return false; + + if (destination.empty()) + { + destination = cv::Mat::zeros(source.size(), source.type()); + + if (source.channels() == 3) + { + cv::Mat hsv; + cv::cvtColor(source, hsv, cv::COLOR_BGR2HSV); + std::vector hsvChannels; + cv::split(hsv, hsvChannels); + cv::merge(hsvChannels, hsv); + cv::cvtColor(hsv, destination, cv::COLOR_HSV2BGR); + } + } + + else if (source.channels() == 1) + { + cv::equalizeHist(source, destination); + } + + return true; +} + +void EqualizeHistFilter::CreateParamInputs() +{ +} + +wxFORCE_LINK_THIS_MODULE(EqualizeHistFilter); \ No newline at end of file diff --git a/MotionDetectorCore/EqualizeHistFilter.h b/MotionDetectorCore/EqualizeHistFilter.h new file mode 100644 index 0000000..fd7ea0b --- /dev/null +++ b/MotionDetectorCore/EqualizeHistFilter.h @@ -0,0 +1,28 @@ +#ifndef __TOUCHLIB_FILTER_EQ_HIST__ +#define __TOUCHLIB_FILTER_EQ_HIST__ + +#include "FilterTemplate.h" +#include + +class MOTION_DETECTOR_CORE_API EqualizeHistFilter : public Filter +{ + DECLARE_DYNAMIC_CLASS_NO_COPY(EqualizeHistFilter) +public: + EqualizeHistFilter(); + virtual ~EqualizeHistFilter(); + + bool Kernel(); +#if defined(HAVE_CUDA) + bool KernelGPU(); + cv::cuda::GpuMat m_floatGpuMat; +#endif + virtual wxString GetName() const; + + int level; + +protected: + virtual void CreateParamInputs(); + +}; + +#endif // __TOUCHLIB_FILTER_EQ_HIST__ diff --git a/MotionDetectorCore/FilterFactory.cpp b/MotionDetectorCore/FilterFactory.cpp new file mode 100644 index 0000000..cff56f2 --- /dev/null +++ b/MotionDetectorCore/FilterFactory.cpp @@ -0,0 +1,41 @@ +#include "stdwx.h" +#include "FilterFactory.h" +#include "FilterTemplate.h" + +Filter * FilterFactory::CreateFilter(const wxString & type) +{ + wxClassInfo * info = wxClassInfo::FindClass(type); + if (!info) return nullptr; + wxObject * obj = info->CreateObject(); + Filter * filter = wxDynamicCast(obj, Filter); + if (!filter) wxDELETE(obj); + return filter; +} + +// Keep linker from discarding filters +wxFORCE_LINK_MODULE(AmplifyFilter); +wxFORCE_LINK_MODULE(BackgroundFilter); +wxFORCE_LINK_MODULE(BarrelDistortionCorrectionFilter); +wxFORCE_LINK_MODULE(BrightnessContrastFilter); +wxFORCE_LINK_MODULE(CropFilter); +wxFORCE_LINK_MODULE(DynamicBGFilter); +wxFORCE_LINK_MODULE(EqualizeHistFilter); +wxFORCE_LINK_MODULE(FlipFilter); +wxFORCE_LINK_MODULE(HighpassFilter); +wxFORCE_LINK_MODULE(InvertFilter); +wxFORCE_LINK_MODULE(MonoFilter); +wxFORCE_LINK_MODULE(PerspectiveFilter); +wxFORCE_LINK_MODULE(RectifyFilter); +wxFORCE_LINK_MODULE(ResizeFilter); +wxFORCE_LINK_MODULE(ScalerFilter); +wxFORCE_LINK_MODULE(ShapeFilter); +wxFORCE_LINK_MODULE(SimpleBGFilter); +wxFORCE_LINK_MODULE(SimpleHighpassFilter); +wxFORCE_LINK_MODULE(SmoothingFilter); +wxFORCE_LINK_MODULE(ThresholdFilter); + +wxFORCE_LINK_MODULE(KinectBGFilter); +wxFORCE_LINK_MODULE(KinectMonoFilter); +wxFORCE_LINK_MODULE(KinectThresholdFilter); + +wxFORCE_LINK_MODULE(DummyFilter); diff --git a/MotionDetectorCore/FilterFactory.h b/MotionDetectorCore/FilterFactory.h new file mode 100644 index 0000000..6946d0a --- /dev/null +++ b/MotionDetectorCore/FilterFactory.h @@ -0,0 +1,12 @@ +#ifndef _FILTERFACTORY_H +#define _FILTERFACTORY_H + +#include "FilterTemplate.h" + +class FilterFactory +{ +public: + static Filter * CreateFilter(const wxString & type); +}; + +#endif // _FILTERFACTORY_H \ No newline at end of file diff --git a/MotionDetectorCore/FilterPreviewWindow.cpp b/MotionDetectorCore/FilterPreviewWindow.cpp new file mode 100644 index 0000000..eae8486 --- /dev/null +++ b/MotionDetectorCore/FilterPreviewWindow.cpp @@ -0,0 +1,100 @@ +#include "stdwx.h" +#include "FilterPreviewWindow.h" +#include + +IMPLEMENT_DYNAMIC_CLASS(FilterPreviewWindow, wxWindow) + +BEGIN_EVENT_TABLE(FilterPreviewWindow, wxWindow) + EVT_SIZE(FilterPreviewWindow::OnSize) + EVT_PAINT(FilterPreviewWindow::OnPaint) + EVT_ERASE_BACKGROUND(FilterPreviewWindow::OnEraseBackground) +END_EVENT_TABLE() + +FilterPreviewWindow::FilterPreviewWindow() +{ + Init(); +} + +FilterPreviewWindow::FilterPreviewWindow(wxWindow* parent, wxWindowID id, const wxPoint& pos, const wxSize& size, long style) +{ + Init(); + Create(parent, id, pos, size, style); +} + +bool FilterPreviewWindow::Create(wxWindow* parent, wxWindowID id, const wxPoint& pos, const wxSize& size, long style) +{ + wxWindow::Create(parent, id, pos, size, style); + return true; +} + +FilterPreviewWindow::~FilterPreviewWindow() +{ +} + +void FilterPreviewWindow::Init() +{ +} + +void FilterPreviewWindow::OnEraseBackground(wxEraseEvent& event) +{ +} + +void FilterPreviewWindow::OnPaint(wxPaintEvent& event) +{ + wxBufferedPaintDC dc(this); + dc.SetBackground(wxBrush(GetBackgroundColour())); + dc.Clear(); + + if (m_Bitmap.IsOk()) + { + wxMemoryDC mdc(m_Bitmap); + dc.StretchBlit(m_DrawRect.GetTopLeft(), m_DrawRect.GetSize(), &mdc, wxPoint(0, 0), m_Bitmap.GetSize()); + } + else // No Image + { + dc.SetBrush(*wxBLACK_BRUSH); + dc.SetPen(*wxTRANSPARENT_PEN); + dc.DrawRectangle(m_DrawRect); + dc.SetTextForeground(*wxWHITE); + dc.DrawLabel(_("No Image"), m_DrawRect, wxALIGN_CENTER); + } +} + +void FilterPreviewWindow::OnSize(wxSizeEvent& event) +{ + CalcDrawRect(); +} + +void FilterPreviewWindow::SetBitmap(const wxBitmap & bitmap) +{ + m_Bitmap = bitmap; + CalcDrawRect(); + Refresh(); +} + +void FilterPreviewWindow::CalcDrawRect() +{ + wxSize size = GetClientSize(); + if (m_Bitmap.IsOk()) + { + wxSize bitmapSize = m_Bitmap.GetSize(); + double kx = (double) size.x / bitmapSize.x; + double ky = (double) size.y / bitmapSize.y; + if (kx < ky) + { + m_DrawRect.width = size.x; + m_DrawRect.height = (kx * bitmapSize.y); + } + else + { + m_DrawRect.height = size.y; + m_DrawRect.width = (ky * bitmapSize.x); + } + m_DrawRect = m_DrawRect.CenterIn(wxRect(size)); + } + else + { + m_DrawRect = wxRect(size); + } + Refresh(); +} diff --git a/MotionDetectorCore/FilterPreviewWindow.h b/MotionDetectorCore/FilterPreviewWindow.h new file mode 100644 index 0000000..6789fd8 --- /dev/null +++ b/MotionDetectorCore/FilterPreviewWindow.h @@ -0,0 +1,34 @@ +#ifndef _EFFECTPREVIEWWINDOW_H +#define _EFFECTPREVIEWWINDOW_H + +#include "MotionDetectorCorePlugin.h" + +class MOTION_DETECTOR_CORE_API FilterPreviewWindow: public wxWindow +{ + DECLARE_DYNAMIC_CLASS(FilterPreviewWindow) + DECLARE_EVENT_TABLE() +public: + FilterPreviewWindow(); + FilterPreviewWindow(wxWindow * parent, wxWindowID id = wxID_STATIC, const wxPoint & pos = wxDefaultPosition, const wxSize & size = wxDefaultSize, long style = wxSIMPLE_BORDER); + + bool Create(wxWindow * parent, wxWindowID id = wxID_STATIC, const wxPoint & pos = wxDefaultPosition, const wxSize & size = wxDefaultSize, long style = wxSIMPLE_BORDER); + + ~FilterPreviewWindow(); + + void Init(); + + void SetBitmap(const wxBitmap & bitmap); + +protected: + void OnSize(wxSizeEvent & event); + void OnPaint(wxPaintEvent & event); + void OnEraseBackground(wxEraseEvent & event); + + void CalcDrawRect(); + +protected: + wxRect m_DrawRect; + wxBitmap m_Bitmap; +}; + +#endif // _EFFECTPREVIEWWINDOW_H diff --git a/MotionDetectorCore/FilterTemplate.cpp b/MotionDetectorCore/FilterTemplate.cpp new file mode 100644 index 0000000..3481a7d --- /dev/null +++ b/MotionDetectorCore/FilterTemplate.cpp @@ -0,0 +1,218 @@ +#include "stdwx.h" +#include "FilterTemplate.h" +#include +#include + +IMPLEMENT_ABSTRACT_CLASS(Filter, xsSerializable); + +Filter::Filter() +{ + Init(); +} +//#ifdef HAVE_CUDA +//#undef HAVE_CUDA +//#endif +void Filter::Init() +{ +#ifdef HAVE_CUDA + bUseGPU = false;//cv::gpu::getCudaEnabledDeviceCount(); + m_src = NULL; +#else + bUseGPU = false; +#endif + + m_ChainedFilter = NULL; + m_FuncImageCallback = NULL; + m_CallbackData = NULL; + m_bEnabled = true; + XS_SERIALIZE(m_bEnabled, wxT("Enabled")); +} + +Filter::~Filter() +{ +#ifdef HAVE_CUDA + m_dst.release(); + m_srcGpu.release(); + m_resMat.release(); + dstMat.release(); + cvReleaseImage(&m_ResultImage); +#endif +} + +#ifdef HAVE_CUDA +cv::cuda::GpuMat& Filter::ProcessGPU(cv::cuda::GpuMat& frameMat) +{ + m_src = &frameMat; + if (IsEnabled()) + { + try + { + // Subclasses must implement this abstract method + if (!this->KernelGPU()){ + frameMat.copyTo(m_dst); + } + } + catch (...) + { + // There was error in the filter + frameMat.copyTo(m_dst); + throw 1; + } + } + else + frameMat.copyTo(m_dst); + + // We need to show result of current filter before it was processed by other filters + if (m_FuncImageCallback) + { + cv::Mat resMat; + m_dst.download(resMat); + m_FuncImageCallback(m_CameraID, GetClassInfo()->GetClassName(), &(IplImage)resMat, m_CallbackData); + } + + if (m_ChainedFilter) + return m_ChainedFilter->ProcessGPU(m_dst); + return m_dst; +} +#endif + +cv::Mat Filter::Process(cv::Mat frame) +{ + source = frame; + +#ifdef HAVE_CUDA +// cv::gpu::GpuMat srcGpu; + m_srcGpu.upload(source); + m_dst = ProcessGPU(m_srcGpu); + m_dst.download(dstMat); + //m_ResultImage = cvCreateImage(dstMat.size(), dstMat.depth(), dstMat.channels()); + m_ResultImage = dstMat.clone(); + return m_ResultImage; +#else + m_ResultImage = source; // If the filter is not enabled, then we should pass source image to the next filter + if (IsEnabled()) + { + try + { + // Subclasses must implement this abstract method + if (this->Kernel() && !destination.empty()) + m_ResultImage = destination; + } + catch (std::exception ex) + { + wxString str = ex.what(); + } + catch (...) + { + // There was error in the filter + + } + } + + // We need to show result of current filter before it was processed by other filters + if (m_FuncImageCallback) + { + m_FuncImageCallback(m_CameraID, GetClassInfo()->GetClassName(), m_ResultImage, m_CallbackData); + } + + // Pass the result to the next filter + if (m_ChainedFilter) + return m_ChainedFilter->Process(m_ResultImage); + + return m_ResultImage; +#endif +} + +void Filter::ConnectTo(Filter * chainedFilter) +{ + m_ChainedFilter = chainedFilter; +} + +void Filter::CreatePreviewAndParams(wxWindow * parent, wxSizer * parentSizer, wxWindow * previewWindow) +{ + parent->SetExtraStyle(wxWS_EX_VALIDATE_RECURSIVELY); + + wxStaticBox * itemStaticBox = new wxStaticBox(parent, wxID_ANY, GetName()); + wxSizer * mainFilterSizer = new wxStaticBoxSizer(itemStaticBox, wxHORIZONTAL); + parentSizer->Add(mainFilterSizer, 0, wxGROW|wxLEFT|wxRIGHT|wxBOTTOM, 5); + + m_ParentPanel = new wxPanel(parent); + m_ParentPanel->SetExtraStyle(wxWS_EX_VALIDATE_RECURSIVELY); + mainFilterSizer->Add(m_ParentPanel, 1, wxGROW, 0); + + mainFilterSizer = new wxBoxSizer(wxHORIZONTAL); + m_ParentPanel->SetSizer(mainFilterSizer); + + wxBoxSizer * previewSizer = new wxBoxSizer(wxVERTICAL); + mainFilterSizer->Add(previewSizer, 0, wxALIGN_LEFT|wxLEFT|wxRIGHT, 5); + + previewWindow->Reparent(m_ParentPanel); + previewSizer->Add(previewWindow, 0, wxALIGN_LEFT|wxBOTTOM, 5); + + m_ParamsContainerSizer = new wxBoxSizer(wxVERTICAL); + mainFilterSizer->Add(m_ParamsContainerSizer, 1, wxGROW, 5); + + wxCheckBox * cb = AddParameterBoolInput(_("Enabled"), &m_bEnabled); + if (IsMandatory()) + cb->Disable(); + + CreateParamInputs(); +} + +void Filter::CreateParamInputs() +{ +} + +void Filter::AddParameterSpinInput(wxString name, int * param, int maxValue, int minValue) +{ + wxBoxSizer * paramSizer = new wxBoxSizer(wxHORIZONTAL); + m_ParamsContainerSizer->Add(paramSizer, 0, wxGROW|wxBOTTOM, 5); + + wxStaticText * paramName = new wxStaticText(m_ParentPanel, wxID_STATIC, name); + paramSizer->Add(paramName, 0, wxALIGN_LEFT|wxRIGHT, 5); + + wxSpinCtrl * paramValue = new wxSpinCtrl(m_ParentPanel, wxID_ANY, wxT("0"), wxDefaultPosition, wxSize(100, -1), wxSP_ARROW_KEYS, minValue, maxValue, 0); + paramSizer->Add(paramValue, 0, wxALIGN_LEFT, 5); + + paramValue->SetValidator(wxGenericValidator(param)); + + paramValue->Bind(wxEVT_COMMAND_SPINCTRL_UPDATED, &Filter::OnSpinChanged, this); +} + +wxCheckBox * Filter::AddParameterBoolInput(wxString name, bool * param) +{ + wxCheckBox * paramValue = new wxCheckBox(m_ParentPanel, wxID_ANY, name); + paramValue->SetValue(*param); + m_ParamsContainerSizer->Add(paramValue, 0, wxBOTTOM, 5); + + paramValue->SetValidator(wxGenericValidator(param)); + + paramValue->Bind(wxEVT_COMMAND_CHECKBOX_CLICKED, &Filter::OnCheckBoxClicked, this); + return paramValue; +} + +void Filter::SetImageCallback(const FuncGetImage & val, int cameraID, void * userData /*= NULL*/) +{ + m_FuncImageCallback = val; + m_CameraID = cameraID; + m_CallbackData = userData; +} + +void Filter::OnCheckBoxClicked(wxCommandEvent & event) +{ + TransferFromWindow(event.GetEventObject()); +} + +void Filter::OnSpinChanged(wxSpinEvent & event) +{ + TransferFromWindow(event.GetEventObject()); +} + +void Filter::TransferFromWindow(wxObject * obj) +{ + wxWindow * win = wxDynamicCast(obj, wxWindow); + wxCHECK_MSG(win, , wxT("Where is the window?")); + wxValidator * validator = win->GetValidator(); + if (validator) + validator->TransferFromWindow(); +} \ No newline at end of file diff --git a/MotionDetectorCore/FilterTemplate.h b/MotionDetectorCore/FilterTemplate.h new file mode 100644 index 0000000..db34a1e --- /dev/null +++ b/MotionDetectorCore/FilterTemplate.h @@ -0,0 +1,96 @@ +#ifndef __TOUCHLIB_FILTER__ +#define __TOUCHLIB_FILTER__ + +#if defined(__WXMSW__) +#include +#include +#include "MotionDetectorCorePlugin.h" +#if defined(HAVE_CUDA) +#include +#endif +#include +// IMPORTANT NOTE: +// Linker will discard entire object file without this: +// wxFORCE_LINK_THIS_MODULE(FilterName); +// So add this line with the correct filter class name to the bottom of the cpp file +// with the filter class definition +// +// Also add line +// wxFORCE_LINK_MODULE(FilterName); +// to the FilterFactory.cpp file to keep linker from discarding the filter + +// Preview size +#define PREVIEW_WIDTH 120 +#define PREVIEW_HEIGHT 90 + +class FilterFactory; + +typedef void (*FuncGetImage)(int cameraID, const wxString & filterName, cv::Mat image, void * userData); + +class MOTION_DETECTOR_CORE_API Filter: public xsSerializable +{ + DECLARE_ABSTRACT_CLASS(Filter) + friend class FilterFactory; +public: + Filter(); + virtual ~Filter(); + + void Init(); + + cv::Mat Process(cv::Mat frame); + + virtual bool Kernel() = 0; +#if defined(HAVE_CUDA) + cv::cuda::GpuMat& ProcessGPU(cv::cuda::GpuMat& frameMat); + virtual bool KernelGPU(){return true;} +#endif + virtual wxString GetName() const = 0; + virtual bool IsEnabled() { return m_bEnabled; } + virtual bool IsMandatory() { return false; } + + void ConnectTo(Filter * chainedFilter); + cv::Mat GetOutput() { return m_ResultImage; } + void SetImageCallback(const FuncGetImage & val, int cameraID, void * userData = NULL); + + void CreatePreviewAndParams(wxWindow * parent, wxSizer * parentSizer, wxWindow * previewWindow); + void SetEnabled(bool enabled){m_bEnabled = enabled;} + +protected: + bool m_bEnabled; + bool bUseGPU; + cv::Mat source; + cv::Mat destination; +#if defined(HAVE_CUDA) + cv::cuda::GpuMat* m_src; + cv::cuda::GpuMat m_dst; + cv::cuda::GpuMat m_srcGpu; + cv::Mat m_resMat; +#endif + + Filter * m_ChainedFilter; + FuncGetImage m_FuncImageCallback; + void * m_CallbackData; + int m_CameraID; + + //Parameters setting panel + void AddParameterSpinInput(wxString name, int * param, int maxValue = 255, int minValue = 0); + wxCheckBox * AddParameterBoolInput(wxString name, bool * param); + virtual void CreateParamInputs(); + void OnCheckBoxClicked(wxCommandEvent & event); + void OnSpinChanged(wxSpinEvent & event); + + void TransferFromWindow(wxObject * obj); + + wxWindow * m_ParentPanel; + wxSizer * m_ParamsContainerSizer; + +private: + cv::Mat m_ResultImage; +#if defined(HAVE_CUDA) + cv::Mat dstMat; +#endif + +}; + +#endif //__TOUCHLIB_FILTER__ +#endif // __WXMSW__ diff --git a/MotionDetectorCore/FlipFilter.cpp b/MotionDetectorCore/FlipFilter.cpp new file mode 100644 index 0000000..92e7538 --- /dev/null +++ b/MotionDetectorCore/FlipFilter.cpp @@ -0,0 +1,89 @@ +#include "stdwx.h" +#include "FlipFilter.h" +#include + +IMPLEMENT_DYNAMIC_CLASS(FlipFilter, Filter); +FlipFilter::FlipFilter() +{ + bFlipVertically = false; + bFlipHorizontally = false; + //XS_SERIALIZE(bFlipVertically, wxT("FlipVertically")); + //XS_SERIALIZE(bFlipHorizontally, wxT("FlipHorizontally")); + +} + +FlipFilter::~FlipFilter() +{ +} + +wxString FlipFilter::GetName() const +{ + return _("Flip"); +} +#if defined(HAVE_CUDA) +bool FlipFilter::KernelGPU() +{ + if (m_src->empty()) + return false; + + int flipMode = 0; + + if (bFlipVertically && !bFlipHorizontally) + flipMode = 0; + else if (!bFlipVertically && bFlipHorizontally) + flipMode = 1; + else if (bFlipVertically && bFlipHorizontally) + flipMode = -1; + else + return false; + cv::cuda::flip(*m_src, m_dst, flipMode); + return true; +} +#endif + +bool FlipFilter::Kernel() +{ + // derived class responsible for allocating storage for filtered image + if (source.empty()) + return false; + + if (destination.empty()) + { + destination = cv::Mat::zeros(source.size(), source.type()); + } + int flipMode = 0; + + if (bFlipVertically && !bFlipHorizontally) + flipMode = 0; + else if (!bFlipVertically && bFlipHorizontally) + flipMode = 1; + else if (bFlipVertically && bFlipHorizontally) + flipMode = -1; + else + { + source.copyTo(destination); + return true; + }; + + cv::flip(source, destination, flipMode); + return true; +} + +void FlipFilter::CreateParamInputs() +{ + //AddParameterBoolInput(_("Flip image vertically"), &bFlipVertically); + //AddParameterBoolInput(_("Flip image horizontally"), &bFlipHorizontally); +} + +bool FlipFilter::IsEnabled() +{ + return m_bEnabled && (bFlipVertically || bFlipHorizontally); +} + +void FlipFilter::SetSettings(const IFloorCameraSettings & settings) +{ + bFlipHorizontally = settings.bHorizontalMirror; + bFlipVertically = settings.bVerticalMirror; +} + +wxFORCE_LINK_THIS_MODULE(FlipFilter); \ No newline at end of file diff --git a/MotionDetectorCore/FlipFilter.h b/MotionDetectorCore/FlipFilter.h new file mode 100644 index 0000000..e00a638 --- /dev/null +++ b/MotionDetectorCore/FlipFilter.h @@ -0,0 +1,33 @@ +#ifndef __TOUCHLIB_FILTER_FLIP__ +#define __TOUCHLIB_FILTER_FLIP__ + +#include "FilterTemplate.h" +#include + +class IFloorCameraSettings; + +class MOTION_DETECTOR_CORE_API FlipFilter : public Filter +{ + DECLARE_DYNAMIC_CLASS_NO_COPY(FlipFilter) +public: + FlipFilter(); + virtual ~FlipFilter(); + + bool Kernel(); +#if defined(HAVE_CUDA) + bool KernelGPU(); +#endif + virtual wxString GetName() const; + virtual bool IsEnabled(); + + void SetSettings(const IFloorCameraSettings & settings); + + bool bFlipVertically; + bool bFlipHorizontally; +protected: + void CreateParamInputs(); +private: + +}; + +#endif // __TOUCHLIB_FILTER_FLIP__ diff --git a/MotionDetectorCore/HighpassFilter.cpp b/MotionDetectorCore/HighpassFilter.cpp new file mode 100644 index 0000000..d7eb721 --- /dev/null +++ b/MotionDetectorCore/HighpassFilter.cpp @@ -0,0 +1,126 @@ +#include "stdwx.h" +#include "HighpassFilter.h" + +IMPLEMENT_DYNAMIC_CLASS(HighpassFilter, Filter); + +HighpassFilter::HighpassFilter() +{ + element = cv::getStructuringElement(cv::MORPH_ELLIPSE, cv::Size(3, 3)); + element2 = cv::getStructuringElement(cv::MORPH_ELLIPSE, cv::Size(9, 9)); + + filterLevel = 5; + filterLevel_slider = filterLevel; + + scale = 32; + scale_slider = scale; + + bErodeDialate = false; + XS_SERIALIZE(bErodeDialate, wxT("ErodeDialate")); + XS_SERIALIZE(filterLevel_slider, wxT("HighpassLevel")); + XS_SERIALIZE(scale_slider, wxT("HighpassScale")); + +} + + +HighpassFilter::~HighpassFilter() +{ + +} + +wxString HighpassFilter::GetName() const +{ + return _("Highpass"); +} + +//void HighpassFilter::getParameters(iFloorParamMap& pMap) +//{ +// pMap[std::string("filter")] = toString(filterLevel); +// pMap[std::string("scale")] = toString(scale); +// +// if(noErodeDialate) +// pMap[std::string("mode")] = "1"; +//} +// +// +//void HighpassFilter::setParameter(const char *name, const char *value) +//{ +// if(strcmp(name, "filter") == 0) +// { +// filterLevel = (int) atof(value); +// filterLevel_slider = filterLevel; +// if(show) +// cvSetTrackbarPos("filter", this->name.c_str(), filterLevel); +// } +// +// if(strcmp(name, "scale") == 0) +// { +// scale = (int) atof(value); +// scale_slider = scale; +// if(show) +// cvSetTrackbarPos("scale", this->name.c_str(), scale); +// } +// +// if(strcmp(name, "mode") == 0) +// { +// if(strcmp(value, "1") == 0) +// { +// noErodeDialate = true; +// +// } +// } +//} + +bool HighpassFilter::Kernel() +{ + if (source.empty()) + return false; + + filterLevel = filterLevel_slider; + scale = scale_slider; + + // derived class responsible for allocating storage for filtered image + if (destination.empty()) + { + destination = cv::Mat::zeros(source.size(), source.type()); + } + cv::erode(source, destination, element2, cv::Point(-1, -1), 2); + cv::dilate(destination, destination, element2, cv::Point(-1, -1), 2); + //cvConvertScale(source, outra); + ////CV_MEDIAN + //cvSmooth(outra, outra2, CV_GAUSSIAN, (filterLevel * 2) + 3, (filterLevel * 2) + 3, 0, 0); + + //cvSub(outra, outra2, outra2); + + //if (bErodeDialate) + //{ + // cvConvertScale(outra2, destination, ((double)scale + 1.0), 32); + // cvErode(destination, destination, element, 2); + // cvSmooth(destination, destination, CV_GAUSSIAN, 7, 7, 0, 0); + // cvDilate(destination, destination, element2, 1); + // cvDilate(destination, destination, element, 1); + //} + //else + //{ + // cvConvertScale(outra2, destination, ((double)scale + 1.0), 0); + // cvErode(destination, destination, element, 1); + // cvSmooth(destination, destination, CV_GAUSSIAN, 11, 11, 0, 0); + // cvDilate(destination, destination, element, 1); + //} + //TODO: remove this + //showOutput(false, 0, 0); + return true; +} + +void HighpassFilter::CreateParamInputs() +{ + //AddParameterBoolInput(_("Erode/Dialate"), &bErodeDialate); + //AddParameterSpinInput(_("Filter level:"), &filterLevel_slider, 255); + //AddParameterSpinInput(_("Filter scale:"), &scale_slider, 255); +} + +//void showOutput(bool value, int windowx, int windowy) +//{ +// wxLogDebug(_("sss")); +//} + +wxFORCE_LINK_THIS_MODULE(HighpassFilter); \ No newline at end of file diff --git a/MotionDetectorCore/HighpassFilter.h b/MotionDetectorCore/HighpassFilter.h new file mode 100644 index 0000000..c3f70a7 --- /dev/null +++ b/MotionDetectorCore/HighpassFilter.h @@ -0,0 +1,35 @@ +#ifndef __TOUCHSCREEN_FILTER_HIGHPASS__ +#define __TOUCHSCREEN_FILTER_HIGHPASS__ + +#include "FilterTemplate.h" + +class MOTION_DETECTOR_CORE_API HighpassFilter : public Filter +{ + DECLARE_DYNAMIC_CLASS_NO_COPY(HighpassFilter) +public: + HighpassFilter(); + virtual ~HighpassFilter(); + + bool Kernel(); +#if defined(HAVE_CUDA) + bool KernelGPU(){return false;} +#endif + virtual wxString GetName() const; + +protected: + void CreateParamInputs(); + +private: + int filterLevel; + int filterLevel_slider; + + int scale; + int scale_slider; + + cv::Mat element; + cv::Mat element2; + + bool bErodeDialate; +}; + +#endif // __TOUCHSCREEN_FILTER_HIGHPASS__ diff --git a/MotionDetectorCore/IFloorImagePreprocessor.cpp b/MotionDetectorCore/IFloorImagePreprocessor.cpp new file mode 100644 index 0000000..4980f4b --- /dev/null +++ b/MotionDetectorCore/IFloorImagePreprocessor.cpp @@ -0,0 +1,178 @@ +#include "stdwx.h" +#include "IFloorImagePreprocessor.h" + +#if defined(USE_VLD) +#include +#endif + +IFloorImagePreprocessor::IFloorImagePreprocessor(const IFloorCameraSettings * settings, void * buffer, size_t bufLength) + : m_Settings(settings) + , m_bNeedToResize(false) + , m_pBuffer(buffer) + , m_BufLength(bufLength) + , m_bHasRoi(false) +{ + if (m_pBuffer && m_Settings) + { + int numChannels = bufLength / (settings->CamWidth * settings->CamHeight); + + int type; + + if (numChannels == 4) + { + type = CV_8UC4; + } + else + { + if (numChannels == 3) + { + type = CV_8UC3; + } + else + { + type = CV_8UC1; + } + } + m_ImgCamera = cv::Mat(settings->CamHeight, settings->CamWidth, type, m_pBuffer); + } +} + +IFloorImagePreprocessor::~IFloorImagePreprocessor() +{ + wxYield(); + for (auto* filter : m_Filters) + { + delete filter; + } + m_Filters.clear(); +} + +void IFloorImagePreprocessor::QueryImage() +{ + if (m_bNeedToResize && !m_ImgCamera.empty()) + { + cv::resize(m_ImgCamera, m_ImgFlash, m_ImgFlash.size(), 0, 0, cv::INTER_LINEAR); + } +} + +void IFloorImagePreprocessor::PrepareImage(int nWidth, int nHeight) +{ + m_bNeedToResize = (nHeight != m_Settings->CamHeight) || (nWidth != m_Settings->CamWidth); + // If the image needs to resize then create destination image + if (m_bNeedToResize && !m_ImgCamera.empty()) + { + m_ImgFlash = cv::Mat::zeros(nHeight, nWidth, m_ImgCamera.type()); + } + else + { + m_ImgFlash = m_ImgCamera; + } +} + +cv::Mat IFloorImagePreprocessor::ProcessImage() +{ + // Get an image from camera + QueryImage(); + // Apply a chain of filters + if (m_ImgFlash.empty()) + return cv::Mat(); + + cv::Mat currentFrame = m_bHasRoi ? m_ImgFlash(m_Roi) : m_ImgFlash; + + cv::Mat result = currentFrame; + + if (!m_Filters.empty()) + { + Filter* firstFilter = m_Filters.front(); + result = firstFilter->Process(currentFrame); + } + + return result; +} + +bool IFloorImagePreprocessor::AddFilterToChain(const wxString & filterType, const wxString & config /*= wxEmptyString*/) +{ + Filter * f = FilterFactory::CreateFilter(filterType); + if (f != nullptr) + { + SetFilterSettings(f, config); + if (!m_Filters.empty()) + m_Filters.back()->ConnectTo(f); + m_Filters.push_back(f); + return true; + } + + return false; +} + +const IFloorCameraSettings * IFloorImagePreprocessor::GetSettings() const +{ + return m_Settings; +} + +bool IFloorImagePreprocessor::SetROI(int x, int y, int width, int height) +{ + if (m_ImgFlash.empty()) + return false; + + x = std::max(0, std::min(x, m_ImgFlash.cols)); + y = std::max(0, std::min(y, m_ImgFlash.rows)); + width = std::max(0, std::min(width, m_ImgFlash.cols - x)); + height = std::max(0, std::min(height, m_ImgFlash.rows - y)); + + // Check and correct coords and size + if (width > 0 && height > 0) + { + m_Roi = cv::Rect(x, y, width, height); + m_bHasRoi = true; + return true; + } + + m_bHasRoi = false; + return false; +} + +cv::Size IFloorImagePreprocessor::GetImageSize() const +{ + if (m_bHasRoi) + { + return m_Roi.size(); + } + if (!m_ImgFlash.empty()) + { + return m_ImgFlash.size(); + } + return cv::Size(m_Settings->CamWidth, m_Settings->CamHeight); +} + +ChainOfFilters & IFloorImagePreprocessor::GetFilters() +{ + return m_Filters; +} + +Filter * IFloorImagePreprocessor::GetFilter(const wxString & filterType) +{ + if(!m_Filters.empty()){ + auto end = m_Filters.end(); + for (auto it = m_Filters.begin(); it != end; ++it) + { + Filter * filter = *it; + if (filter->GetClassInfo()->GetClassName() == filterType) + return filter; + } + } + return nullptr; +} + +bool IFloorImagePreprocessor::SetFilterSettings(const wxString & filterType, const wxString & config) +{ + Filter * filter = GetFilter(filterType); + return SetFilterSettings(filter, config); +} + +bool IFloorImagePreprocessor::SetFilterSettings(Filter * filter, const wxString & config) +{ + if (!filter || config.IsEmpty()) + return false; + return SerializableBase::Deserialize(config, *filter); +} \ No newline at end of file diff --git a/MotionDetectorCore/IFloorImagePreprocessor.h b/MotionDetectorCore/IFloorImagePreprocessor.h new file mode 100644 index 0000000..364f82f --- /dev/null +++ b/MotionDetectorCore/IFloorImagePreprocessor.h @@ -0,0 +1,56 @@ +#ifndef IFLOORIMAGEPREPROCESSOR_H +#define IFLOORIMAGEPREPROCESSOR_H + +#include +#include "FilterFactory.h" +#include +#include +#include + +typedef std::vector ChainOfFilters; + +class MOTION_DETECTOR_CORE_API IFloorImagePreprocessor +{ +public: + IFloorImagePreprocessor(const IFloorCameraSettings * settings, void * buffer, size_t bufLength); + ~IFloorImagePreprocessor(); + // Create an image for resizing + void PrepareImage(int nWidth, int nHeight); + // Capture image and process all filters + cv::Mat ProcessImage(); + // Add a new filter to chain + bool AddFilterToChain(const wxString & filterType, const wxString & config = wxEmptyString); + // Returns current camera settings + const IFloorCameraSettings * GetSettings() const; + // Set the region of interest for the camera + bool SetROI(int x, int y, int width, int height); + // Return size of roi if it exists or size of the image otherwise + cv::Size GetImageSize() const; + // Returns filter from the chain with the specified type + Filter * GetFilter(const wxString & filterType); + // Deserializes filter in chain with specified type from the config string + bool SetFilterSettings(const wxString & filterType, const wxString & config); + // Deserializes filter from the config string + static bool SetFilterSettings(Filter * filter, const wxString & config); + + ChainOfFilters & GetFilters(); +protected: + void QueryImage(); + +protected: + const IFloorCameraSettings * m_Settings; + cv::Mat m_ImgCamera; + cv::Mat m_ImgFlash; + cv::Rect m_Roi; + bool m_bHasRoi; + + ChainOfFilters m_Filters; + bool m_bNeedToResize; + void * m_pBuffer; + size_t m_BufLength; +}; + +// Type definition for cameras vector +typedef std::vector IFloorImagePreprocessorVector; + +#endif //IFLOORIMAGEPREPROCESSOR_H \ No newline at end of file diff --git a/MotionDetectorCore/IFloorImagePreprocessorSettings.cpp b/MotionDetectorCore/IFloorImagePreprocessorSettings.cpp new file mode 100644 index 0000000..f8e47ce --- /dev/null +++ b/MotionDetectorCore/IFloorImagePreprocessorSettings.cpp @@ -0,0 +1,63 @@ +#include "stdwx.h" +#include "IFloorImagePreprocessorSettings.h" + +XS_IMPLEMENT_CLONABLE_CLASS(IFloorImagePreprocessorSettings, xsSerializable); + +#include +WX_DEFINE_OBJARRAY(IFloorImagePreprocessorSettingsArray); + +#if defined(USE_VLD) +#include +#endif + +IFloorImagePreprocessorSettings::IFloorImagePreprocessorSettings() +{ + InitSerialization(); +} + +IFloorImagePreprocessorSettings::IFloorImagePreprocessorSettings(const IFloorImagePreprocessorSettings & obj) +{ + InitSerialization(); + CopyFrom(obj); +} + +IFloorImagePreprocessorSettings& IFloorImagePreprocessorSettings::operator=(const IFloorImagePreprocessorSettings & obj) +{ + if (&obj != this) + { + CopyFrom(obj); + } + return *this; +} + +IFloorImagePreprocessorSettings::~IFloorImagePreprocessorSettings() +{ +} + +void IFloorImagePreprocessorSettings::InitSerialization() +{ + XS_SERIALIZE(m_Filters, wxT("Filters")); +} + +void IFloorImagePreprocessorSettings::CopyFrom(const IFloorImagePreprocessorSettings & obj) +{ + m_Filters = obj.m_Filters; +} + +wxString IFloorImagePreprocessorSettings::GetFilterConfig(const wxString & filterName) +{ + StringMap::iterator it = m_Filters.find(filterName); + if (it == m_Filters.end()) + return wxEmptyString; + return it->second; +} + +wxString IFloorImagePreprocessorSettings::operator[](const wxString & filterName) +{ + return GetFilterConfig(filterName); +} + +void IFloorImagePreprocessorSettings::SetFilterConfig(const wxString & filterName, const wxString & config) +{ + m_Filters[filterName] = config; +} \ No newline at end of file diff --git a/MotionDetectorCore/IFloorImagePreprocessorSettings.h b/MotionDetectorCore/IFloorImagePreprocessorSettings.h new file mode 100644 index 0000000..ffc32b9 --- /dev/null +++ b/MotionDetectorCore/IFloorImagePreprocessorSettings.h @@ -0,0 +1,29 @@ +#ifndef _IFLOORIMAGEPREPROCESSORSETTINGS_H +#define _IFLOORIMAGEPREPROCESSORSETTINGS_H + +#include +#include "MotionDetectorCorePlugin.h" + +class MOTION_DETECTOR_CORE_API IFloorImagePreprocessorSettings : public xsSerializable +{ + XS_DECLARE_CLONABLE_CLASS(IFloorImagePreprocessorSettings); +public: + IFloorImagePreprocessorSettings(); + IFloorImagePreprocessorSettings(const IFloorImagePreprocessorSettings & obj); + IFloorImagePreprocessorSettings& operator=(const IFloorImagePreprocessorSettings & obj); + virtual ~IFloorImagePreprocessorSettings(); + + wxString GetFilterConfig(const wxString & filterName); + void SetFilterConfig(const wxString & filterName, const wxString & config); + wxString operator[](const wxString & filterName); + +protected: + void InitSerialization(); + void CopyFrom(const IFloorImagePreprocessorSettings & obj); +private: + StringMap m_Filters; +}; + +WX_DECLARE_USER_EXPORTED_OBJARRAY(IFloorImagePreprocessorSettings, IFloorImagePreprocessorSettingsArray, MOTION_DETECTOR_CORE_API); + +#endif // _IFLOORIMAGEPREPROCESSORSETTINGS_H diff --git a/MotionDetectorCore/Image.h b/MotionDetectorCore/Image.h new file mode 100644 index 0000000..0fa841f --- /dev/null +++ b/MotionDetectorCore/Image.h @@ -0,0 +1,62 @@ +#ifndef __TOUCHLIB_IMAGE__ +#define __TOUCHLIB_IMAGE__ + +#include +//#include + +namespace touchlib +{ + + // This class serves as a wrapper to OpenCV's image class + // to provide a simple interface to pixels and height/width info. + + template + class /*TOUCHLIB_CORE_EXPORT*/ Image + { + public: + Image(cv::Mat* img = nullptr) { m_img = img; } + ~Image() { m_img = nullptr; } + + int getHeight() + { + if (m_img && !m_img->empty()) + return m_img->rows; + else + return 0; + } + + int getWidth() + { + if (m_img && !m_img->empty()) + return m_img->cols; + else + return 0; + } + + void operator=(cv::Mat* img) { m_img = img; } + + inline T* operator[](const int rowIndx) + { + return (T*)(m_img->data + rowIndx * m_img->step); + } + + cv::Mat* m_img; + }; + + typedef struct + { + unsigned char b,g,r; + } RgbPixel; + + typedef struct + { + float b,g,r; + } RgbPixelFloat; + + typedef Image RgbImage; + typedef Image RgbImageFloat; + typedef Image BwImage; + typedef Image BwImageFloat; +} + +#endif // __TOUCHLIB_IMAGE__ diff --git a/MotionDetectorCore/InvertFilter.cpp b/MotionDetectorCore/InvertFilter.cpp new file mode 100644 index 0000000..817308f --- /dev/null +++ b/MotionDetectorCore/InvertFilter.cpp @@ -0,0 +1,35 @@ +#include "stdwx.h" +#include "InvertFilter.h" + +IMPLEMENT_DYNAMIC_CLASS(InvertFilter, Filter); + +InvertFilter::InvertFilter() +{ +} + +InvertFilter::~InvertFilter() +{ +} + +wxString InvertFilter::GetName() const +{ + return _("Invert"); +} + +// The smooth filter really needs the blur size as a param +bool InvertFilter::Kernel() +{ + if (source.empty()) + return false; + + // derived class responsible for allocating storage for filtered image + if (destination.empty()) + { + destination = cv::Mat::zeros(source.size(), source.type()); + } + + destination = ~source; + return true; +} + +wxFORCE_LINK_THIS_MODULE(InvertFilter); \ No newline at end of file diff --git a/MotionDetectorCore/InvertFilter.h b/MotionDetectorCore/InvertFilter.h new file mode 100644 index 0000000..f12f75f --- /dev/null +++ b/MotionDetectorCore/InvertFilter.h @@ -0,0 +1,24 @@ +#ifndef __TOUCHSCREEN_FILTER_INVERT__ +#define __TOUCHSCREEN_FILTER_INVERT__ + +#include "FilterTemplate.h" +#include + +class MOTION_DETECTOR_CORE_API InvertFilter : public Filter +{ + DECLARE_DYNAMIC_CLASS_NO_COPY(InvertFilter) +public: + InvertFilter(); + virtual ~InvertFilter(); + + bool Kernel(); +#if defined(HAVE_CUDA) + bool KernelGPU(){return false;} +#endif + virtual wxString GetName() const; + +private: + +}; + +#endif // __TOUCHSCREEN_FILTER_INVERT__ diff --git a/MotionDetectorCore/KinectBGFilter.cpp b/MotionDetectorCore/KinectBGFilter.cpp new file mode 100644 index 0000000..999fb18 --- /dev/null +++ b/MotionDetectorCore/KinectBGFilter.cpp @@ -0,0 +1,138 @@ +#include "stdwx.h" +#include "KinectBGFilter.h" +//#include +#include +#if defined(__linux__) +#include "LinuxUtils.h" +#endif +// LINUX_ + +IMPLEMENT_DYNAMIC_CLASS(KinectBGFilter, Filter); + +KinectBGFilter::KinectBGFilter() +{ + m_bTrackDark = false; + m_ExposureStartTime = timeGetTime(); + m_bLearnBackground = false; + m_bStart = false; + m_CameraExposureTime = 2200; + m_bCalibrated = false; + XS_SERIALIZE(m_bTrackDark, wxT("TrackDark")); + XS_SERIALIZE(m_CameraExposureTime, wxT("CameraExposureTime")); + XS_SERIALIZE(m_bCalibrated, wxT("Calibrated")); + XS_SERIALIZE(m_filename, wxT("filename")); +} + +KinectBGFilter::~KinectBGFilter() +{ + +} + +wxString KinectBGFilter::GetName() const +{ + return _("Simple BG"); +} + +#if defined(HAVE_CUDA) +bool KinectBGFilter::KernelGPU() +{ + if (!m_bStart){ + int curTime = timeGetTime(); + if((int)(curTime - m_ExposureStartTime) > m_CameraExposureTime){ + m_bLearnBackground = true; + m_bStart = true; + } + else + return false; + } + //Capture full background + if (m_bLearnBackground) + { + m_src->copyTo(m_matBg); + m_bLearnBackground = false; + } + // Subtract background + if (m_bTrackDark){ + cv::cuda::subtract(m_matBg, *m_src, m_dst); + } + else{ + cv::cuda::subtract(*m_src, m_matBg, m_dst); + } + + return true; +} +#endif + + +bool KinectBGFilter::Kernel() +{ + // derived class responsible for allocating storage for filtered image + if (source.empty()) + return false; + + if (destination.empty()) + { + destination = cv::Mat::zeros(source.size(), source.type()); + } + if (!m_bStart){ + int curTime = timeGetTime(); + if((int)(curTime - m_ExposureStartTime) > m_CameraExposureTime){ + m_bLearnBackground = true; + m_bStart = true; + } + else + return false; + } + //Capture full background + if (m_bLearnBackground) + { + if(m_bCalibrated && wxFile::Exists(m_filename)){ + + background = cv::imread("c:\\background.jpg", cv::IMREAD_UNCHANGED); + } + else{ + if (background.empty()) + { + background = cv::Mat::zeros(source.size(), source.type()); + } + source.copyTo(background); + //ResetCalibration(); + } + m_bLearnBackground = false; + } + if (background.empty()) + return false; + + // Subtract background + if (m_bTrackDark) + cv::subtract(background, source, destination); + else + cv::subtract(source, background, destination); + + return true; +} + +void KinectBGFilter::CreateParamInputs() +{ + AddParameterBoolInput(_("Learn background"), &m_bLearnBackground); + AddParameterBoolInput(_("Track dark blobs"), &m_bTrackDark); + AddParameterSpinInput(_("Camera exposure time:"), &m_CameraExposureTime, 20000, 0); +} + +void KinectBGFilter::SaveBackground(wxString filename) +{ + m_bCalibrated = !background.empty(); + if(m_bCalibrated) + { + m_filename = filename; + cv::imwrite("c:\\background.jpg", background); + } +} + +void KinectBGFilter::ResetCalibration() +{ + m_bCalibrated = false; + m_bLearnBackground = true; +} + +wxFORCE_LINK_THIS_MODULE(KinectBGFilter); \ No newline at end of file diff --git a/MotionDetectorCore/KinectBGFilter.h b/MotionDetectorCore/KinectBGFilter.h new file mode 100644 index 0000000..bd1af4b --- /dev/null +++ b/MotionDetectorCore/KinectBGFilter.h @@ -0,0 +1,42 @@ +#ifndef __TOUCHLIB_FILTER_KINECTBG__ +#define __TOUCHLIB_FILTER_KINECTBG__ + +#include "FilterTemplate.h" +#include + +class MOTION_DETECTOR_CORE_API KinectBGFilter : public Filter +{ + DECLARE_DYNAMIC_CLASS_NO_COPY(KinectBGFilter) +public: + KinectBGFilter(); + virtual ~KinectBGFilter(); + + bool Kernel(); +#if defined(HAVE_CUDA) + bool KernelGPU(); +#endif + virtual wxString GetName() const; + void SaveBackground(wxString filename); + void ResetCalibration(); +protected: + void CreateParamInputs(); + +private: + cv::Mat background; + bool m_bTrackDark; + bool m_bLearnBackground; + int m_ExposureStartTime; + int m_CameraExposureTime; + bool m_bCalibrated; + bool m_bErodeDilate; + wxString m_filename; + cv::Mat m_erodeKernel; + cv::Mat m_dilateKernel; + bool m_bStart; + #if defined(HAVE_CUDA) + cv::cuda::GpuMat m_matBg; + #endif + +}; + +#endif // __TOUCHLIB_FILTER_KINECTBG__ diff --git a/MotionDetectorCore/KinectDepthBGFilter.cpp b/MotionDetectorCore/KinectDepthBGFilter.cpp new file mode 100644 index 0000000..c1b9290 --- /dev/null +++ b/MotionDetectorCore/KinectDepthBGFilter.cpp @@ -0,0 +1,91 @@ +#include "stdwx.h" +#include "KinectDepthBGFilter.h" + +#if defined(__linux__) +#include "LinuxUtils.h" +typedef unsigned short WORD; +#endif + +IMPLEMENT_DYNAMIC_CLASS(KinectDepthBGFilter, Filter); + +KinectDepthBGFilter::KinectDepthBGFilter() +{ + m_bStart = false; + m_bLearnBackground = false; + m_ExposureStartTime = timeGetTime(); + m_CameraExposureTime = 2200; + XS_SERIALIZE(m_CameraExposureTime, wxT("CameraExposureTime")); +} + +KinectDepthBGFilter::~KinectDepthBGFilter() +{ +} + +wxString KinectDepthBGFilter::GetName() const +{ + return _("Depth BG"); +} + +bool KinectDepthBGFilter::Kernel() +{ + if (source.empty()) + return false; + + if (destination.empty()) + { + destination = cv::Mat::zeros(source.size(), source.type()); + } + if (!m_bStart){ + int curTime = timeGetTime(); + if ((int)(curTime - m_ExposureStartTime) > m_CameraExposureTime){ + m_bLearnBackground = true; + m_bStart = true; + } + else + return false; + } + //Capture full background + if (m_bLearnBackground) + { + if (background.empty()) + { + background = cv::Mat::zeros(source.size(), source.type()); + } + source.copyTo(background); + + m_bLearnBackground = false; + } + if (background.empty()) + return false; + + int rows = source.rows; + int cols = source.cols; + + for (int y = 0; y < rows; y++) + { + const float* pSrc = source.ptr(y); + const float* pBg = background.ptr(y); + float* pDst = destination.ptr(y); + + for (int x = 0; x < cols; x++) + { + if (pSrc[x] == 0.0f || pBg[x] == 0.0f || pSrc[x] >= pBg[x]) + { + pDst[x] = 0.0f; + } + else + { + pDst[x] = pBg[x] - pSrc[x]; + } + } + } + return true; + +} +void KinectDepthBGFilter::CreateParamInputs() +{ + AddParameterBoolInput(_("Learn background"), &m_bLearnBackground); + AddParameterSpinInput(_("Camera exposure time:"), &m_CameraExposureTime, 20000, 0); +} + +wxFORCE_LINK_THIS_MODULE(KinectDepthBGFilter); \ No newline at end of file diff --git a/MotionDetectorCore/KinectDepthBGFilter.h b/MotionDetectorCore/KinectDepthBGFilter.h new file mode 100644 index 0000000..2049f2a --- /dev/null +++ b/MotionDetectorCore/KinectDepthBGFilter.h @@ -0,0 +1,30 @@ +#ifndef __TOUCHLIB_FILTER_KINECTDEPTHBG__ +#define __TOUCHLIB_FILTER_KINECTDEPTHBG__ + +#include "FilterTemplate.h" +#include +#include + +class MOTION_DETECTOR_CORE_API KinectDepthBGFilter : public Filter +{ + DECLARE_DYNAMIC_CLASS_NO_COPY(KinectDepthBGFilter) +public: + KinectDepthBGFilter(); + virtual ~KinectDepthBGFilter(); + + bool Kernel(); + virtual wxString GetName() const; + // This filter cannot be disabled in settings + //virtual bool IsMandatory() { return true; } +protected: + void CreateParamInputs(); + +private: + cv::Mat background; + bool m_bLearnBackground; + int m_ExposureStartTime; + int m_CameraExposureTime; + bool m_bStart; +}; + +#endif // __TOUCHLIB_FILTER_KinectDepthBGFilter__ diff --git a/MotionDetectorCore/KinectMonoFilter.cpp b/MotionDetectorCore/KinectMonoFilter.cpp new file mode 100644 index 0000000..27146a3 --- /dev/null +++ b/MotionDetectorCore/KinectMonoFilter.cpp @@ -0,0 +1,77 @@ +#include "stdwx.h" +#include "KinectMonoFilter.h" + +#if defined(__linux__) +typedef unsigned short WORD; +#endif + +IMPLEMENT_DYNAMIC_CLASS(KinectMonoFilter, Filter); + +KinectMonoFilter::KinectMonoFilter() +{ +} + +KinectMonoFilter::~KinectMonoFilter() +{ +} + +wxString KinectMonoFilter::GetName() const +{ + return _("Mono"); +} + +bool KinectMonoFilter::Kernel() +{ + if (source.empty()) + return false; + + if (destination.empty()) + { + destination = cv::Mat::zeros(source.size(), CV_32FC1); + } + + if (source.channels() == 3) + { + int rows = source.rows; + int cols = source.cols; + + for (int y = 0; y < rows; ++y) + { + const unsigned char* srcRow = source.ptr(y); + float* dstRow = destination.ptr(y); + + for (int x = 0; x < cols; ++x) + { + const unsigned char* dataPtr = srcRow + (x * 3) + 1; + + WORD depthValue = *reinterpret_cast(dataPtr); + dstRow[x] = static_cast(depthValue) / 4095.0f; + } + } + } + else if (source.channels() == 1) + { + + source.convertTo(destination, CV_32F, 1.0 / 4095.0); + } + else + { + return false; + } + //if (source->nChannels != 1 && destination->nChannels == 1) + //{ + // if (strcmpi(source->colorModel, "BGRA") == 0) + // cvCvtColor(source, destination, CV_BGRA2GRAY); + // else if (strcmpi(source->colorModel, "BGR") == 0) + // cvCvtColor(source, destination, CV_BGR2GRAY); + // else if (strcmpi(source->colorModel, "RGB") == 0) + // cvCvtColor(source, destination, CV_RGB2GRAY); + // else + // return false; + //} + //else + // return false; + return true; +} + +wxFORCE_LINK_THIS_MODULE(KinectMonoFilter); \ No newline at end of file diff --git a/MotionDetectorCore/KinectMonoFilter.h b/MotionDetectorCore/KinectMonoFilter.h new file mode 100644 index 0000000..f380d34 --- /dev/null +++ b/MotionDetectorCore/KinectMonoFilter.h @@ -0,0 +1,23 @@ +#ifndef __TOUCHLIB_FILTER_KINECTMONO__ +#define __TOUCHLIB_FILTER_KINECTMONO__ + +#include "FilterTemplate.h" +#include + +class MOTION_DETECTOR_CORE_API KinectMonoFilter : public Filter +{ + DECLARE_DYNAMIC_CLASS_NO_COPY(KinectMonoFilter) +public: + KinectMonoFilter(); + virtual ~KinectMonoFilter(); + + bool Kernel(); + virtual wxString GetName() const; + // This filter cannot be disabled in settings + virtual bool IsMandatory() { return true; } + +private: + +}; + +#endif // __TOUCHLIB_FILTER_KINECTMONO__ diff --git a/MotionDetectorCore/KinectThresholdFilter.cpp b/MotionDetectorCore/KinectThresholdFilter.cpp new file mode 100644 index 0000000..36d80e8 --- /dev/null +++ b/MotionDetectorCore/KinectThresholdFilter.cpp @@ -0,0 +1,93 @@ +#include "stdwx.h" +#include "KinectThresholdFilter.h" + +#if defined(__linux__) +#include "LinuxUtils.h" +#endif + +IMPLEMENT_DYNAMIC_CLASS(KinectThresholdFilter, Filter); + +KinectThresholdFilter::KinectThresholdFilter() +{ + thresholdSlider1 = 0; + thresholdSlider2 = 4095; + m_bLearnBackground = false; + m_LearnRate = 400; + m_CameraExposureTime = 2200; +#if defined(HAVE_CUDA) + bStart = false; +#endif + + m_ExposureStartTime = timeGetTime(); + XS_SERIALIZE(m_LearnRate, wxT("LearnRate")); + XS_SERIALIZE(m_bLearnBackground, wxT("LearnBackground")); + XS_SERIALIZE(m_CameraExposureTime, wxT("CameraExposureTime")); + XS_SERIALIZE(thresholdSlider1, wxT("Threshold1")); + XS_SERIALIZE(thresholdSlider2, wxT("Threshold2")); + +} + +KinectThresholdFilter::~KinectThresholdFilter() +{ +} + +wxString KinectThresholdFilter::GetName() const +{ + return _("Threshold & Dynamic BG"); +} + +bool KinectThresholdFilter::Kernel() +{ + if (source.empty()) + return false; + + if (destination.empty()) + { + destination = cv::Mat::zeros(source.size(), CV_8UC1); + } + //if (!floatBgImg) + //{ + // floatBgImg = cvCreateImage(cvSize(source->width, source->height), source->depth, source->nChannels); + // floatBgImgTemp = cvCreateImage(cvSize(source->width, source->height), source->depth, source->nChannels); + //} + + //if ((int)(timeGetTime() - m_ExposureStartTime) > m_CameraExposureTime) + // m_bLearnBackground = true; + + ////Capture full background + //if (m_bLearnBackground) + //{ + // //cvConvertScale(source, floatBgImg, 65535.0f / 255.0f, 0); + // cvCopy(source, floatBgImg); + // if (!grayBg) + // { + // grayBg = cvCreateImage(cvSize(source->width, source->height), 8, 1); + // } + // m_ExposureStartTime = timeGetTime(); + // m_bLearnBackground = false; + //} + //if (grayBg == NULL) + // return false; + + //float fLearnRate = (float)m_LearnRate * 0.001f; + //cvAddWeighted(source, fLearnRate, floatBgImg, 1.0f - fLearnRate, 0, floatBgImg); + //cvSub(source, floatBgImg, floatBgImg); + //cvConvertScale(floatBgImg, grayBg, 128.0f, 0); + + float minLevel = static_cast(std::min(thresholdSlider1, thresholdSlider2)) / 4095.0f; + float maxLevel = static_cast(std::max(thresholdSlider1, thresholdSlider2)) / 4095.0f; + + cv::inRange(source, cv::Scalar(minLevel), cv::Scalar(maxLevel), destination); + return true; +} + +void KinectThresholdFilter::CreateParamInputs() +{ + AddParameterSpinInput(_("Threshold level 1:"), &thresholdSlider1, 0xfff); + AddParameterSpinInput(_("Threshold level 2:"), &thresholdSlider2, 0xfff); + //AddParameterBoolInput(_("Learn background"), &m_bLearnBackground); + //AddParameterSpinInput(_("Background learn rate:"), &m_LearnRate, 1000, 0); + //AddParameterSpinInput(_("Camera exposure time:"), &m_CameraExposureTime, 20000, 0); +} + +wxFORCE_LINK_THIS_MODULE(KinectThresholdFilter); \ No newline at end of file diff --git a/MotionDetectorCore/KinectThresholdFilter.h b/MotionDetectorCore/KinectThresholdFilter.h new file mode 100644 index 0000000..050a98a --- /dev/null +++ b/MotionDetectorCore/KinectThresholdFilter.h @@ -0,0 +1,33 @@ +#ifndef __TOUCHSCREEN_FILTER_KINECTTHRESHOLD__ +#define __TOUCHSCREEN_FILTER_KINECTTHRESHOLD__ + +#include "FilterTemplate.h" +#include +#include + +// This filter has two modes. One that dynamically changes the threshold with the help +// of statistics that are recorded, and one which is static. Idea by Damian. +class MOTION_DETECTOR_CORE_API KinectThresholdFilter : public Filter +{ + DECLARE_DYNAMIC_CLASS_NO_COPY(KinectThresholdFilter) +public: + KinectThresholdFilter(); + virtual ~KinectThresholdFilter(); + + bool Kernel(); + virtual wxString GetName() const; + +private: + + int thresholdSlider1; + int thresholdSlider2; + bool m_bLearnBackground; + int m_ExposureStartTime; + int m_CameraExposureTime; + int m_LearnRate; + +protected: + void CreateParamInputs(); +}; + +#endif // __TOUCHSCREEN_FILTER_KINECTTHRESHOLD__ diff --git a/MotionDetectorCore/LinuxUtils.cpp b/MotionDetectorCore/LinuxUtils.cpp new file mode 100644 index 0000000..60437d7 --- /dev/null +++ b/MotionDetectorCore/LinuxUtils.cpp @@ -0,0 +1,11 @@ +#include "stdwx.h" +#if defined(__LINUX__) +#include + + unsigned int timeGetTime() +{ + struct timeval now; + gettimeofday(&now, NULL); + return (now.tv_sec) * 1000 + (now.tv_usec) / 1000; +} +#endif diff --git a/MotionDetectorCore/LinuxUtils.h b/MotionDetectorCore/LinuxUtils.h new file mode 100644 index 0000000..dc6435a --- /dev/null +++ b/MotionDetectorCore/LinuxUtils.h @@ -0,0 +1,4 @@ + +#include +unsigned int timeGetTime(); + diff --git a/MotionDetectorCore/MonoFilter.cpp b/MotionDetectorCore/MonoFilter.cpp new file mode 100644 index 0000000..1d17963 --- /dev/null +++ b/MotionDetectorCore/MonoFilter.cpp @@ -0,0 +1,67 @@ +#include "stdwx.h" +#include "MonoFilter.h" + + +IMPLEMENT_DYNAMIC_CLASS(MonoFilter, Filter); + +MonoFilter::MonoFilter() +{ +} + +MonoFilter::~MonoFilter() +{ +} + +wxString MonoFilter::GetName() const +{ + return _("Mono"); +} + +#if defined(HAVE_CUDA) +bool MonoFilter::KernelGPU() +{ + if(!m_src) + return false; + //if(!m_dst) m_dst = new cv::gpu::GpuMat(m_src->size(), CV_8UC1); + if (m_src->channels() == 4) + cv::cuda::cvtColor(*m_src, m_dst, cv::COLOR_BGRA2GRAY); + else if (m_src->channels() == 3) + cv::cuda::cvtColor(*m_src, m_dst, cv::COLOR_BGR2GRAY); + else + return false; + return true; +} +#endif + +bool MonoFilter::Kernel() +{ + if (source.empty()) + return false; + + if (destination.empty()) + { + destination = cv::Mat::zeros(source.size(), CV_8UC1); + } + + + if (source.channels() == 4) + { + cv::cvtColor(source, destination, cv::COLOR_BGRA2GRAY); + } + else if (source.channels() == 3) + { + cv::cvtColor(source, destination, cv::COLOR_BGR2GRAY); + } + else if (source.channels() == 1) + { + source.copyTo(destination); + } + else + { + return false; + } + + return true; +} + +wxFORCE_LINK_THIS_MODULE(MonoFilter); diff --git a/MotionDetectorCore/MonoFilter.h b/MotionDetectorCore/MonoFilter.h new file mode 100644 index 0000000..bdd26d4 --- /dev/null +++ b/MotionDetectorCore/MonoFilter.h @@ -0,0 +1,26 @@ +#ifndef __TOUCHLIB_FILTER_MONO__ +#define __TOUCHLIB_FILTER_MONO__ + +#include "FilterTemplate.h" +#include + +class MOTION_DETECTOR_CORE_API MonoFilter : public Filter +{ + DECLARE_DYNAMIC_CLASS_NO_COPY(MonoFilter) +public: + MonoFilter(); + virtual ~MonoFilter(); + + bool Kernel(); +#if defined(HAVE_CUDA) + virtual bool KernelGPU(); +#endif + virtual wxString GetName() const; + // This filter cannot be disabled in settings + virtual bool IsMandatory() { return true; } + +private: + +}; + +#endif // __TOUCHLIB_FILTER_MONO__ diff --git a/MotionDetectorCore/MonoKinectFilter.cpp b/MotionDetectorCore/MonoKinectFilter.cpp new file mode 100644 index 0000000..e69de29 diff --git a/MotionDetectorCore/MotionDetectorCorePlugin.h b/MotionDetectorCore/MotionDetectorCorePlugin.h new file mode 100644 index 0000000..e1068f5 --- /dev/null +++ b/MotionDetectorCore/MotionDetectorCorePlugin.h @@ -0,0 +1,10 @@ +#ifndef _MOTIONDETECTORCOREPLUGIN_H +#define _MOTIONDETECTORCOREPLUGIN_H + +#ifdef MOTION_DETECTOR_CORE_EXPORTS +# define MOTION_DETECTOR_CORE_API WXEXPORT +#else +# define MOTION_DETECTOR_CORE_API WXIMPORT +#endif + +#endif // _MOTIONDETECTORCOREPLUGIN_H diff --git a/MotionDetectorCore/PerspectiveFilter.cpp b/MotionDetectorCore/PerspectiveFilter.cpp new file mode 100644 index 0000000..606b3eb --- /dev/null +++ b/MotionDetectorCore/PerspectiveFilter.cpp @@ -0,0 +1,128 @@ +#include "stdwx.h" +#include "PerspectiveFilter.h" +#include +#include + +IMPLEMENT_DYNAMIC_CLASS(PerspectiveFilter, Filter); + +PerspectiveFilter::PerspectiveFilter() +{ + cvsrc.resize(4, cv::Point2f(0.0f, 0.0f)); + cvdst.resize(4, cv::Point2f(0.0f, 0.0f)); + + xMax = xMin = yMin = yMax = 0.0f; + +} + +PerspectiveFilter::~PerspectiveFilter() +{ + +} + +wxString PerspectiveFilter::GetName() const +{ + return _("Perspective"); +} + +#if defined(HAVE_CUDA) +bool PerspectiveFilter::KernelGPU() +{ + if(!Translate()) return false; + cv::Mat translateMat(translate); + m_src->download(src); + //cv::gpu::warpPerspective(*m_src, m_dst, translateMat, cv::Size(xMax - xMin, yMax - yMin)); + cv::warpPerspective(src, dst, translateMat, cv::Size(xMax - xMin, yMax - yMin)); + m_dst.upload(dst); + return true; +} +#endif + + //original code + /** + * A +-------------+ B + * / \ + * / \ + * / \ + * D +-------------------- + C + */ + //CvPoint2D32f cvsrc[4]; + //CvPoint2D32f cvdst[4]; + //CvMat* translate = cvCreateMat(3,3, CV_32FC1); + //cvSetZero(translate); + + //cvdst[0].x = 0; + //cvdst[0].y = 0; + //cvdst[1].x = width; + //cvdst[1].y = 0; + //cvdst[2].x = width; + //cvdst[2].y = height; + //cvdst[3].x = 0; + //cvdst[3].y = height; + + //cvsrc[0].x = A.x; + //cvsrc[0].y = A.y; + //cvsrc[1].x = B.x; + //cvsrc[1].y = B.y; + //cvsrc[2].x = C.x; + //cvsrc[2].y = C.y; + //cvsrc[3].x = D.x; + //cvsrc[3].y = D.y; + + //cvWarpPerspectiveQMatrix(cvsrc, cvdst, translate); // calculate homography + //cvWarpPerspective(cvImage, cvImageTemp, translate); + //swapTemp(); + //flagImageChanged(); + //cvReleaseMat(&translate); + +bool PerspectiveFilter::Translate() +{ + if (cvdst.empty() || cvdst.size() < 4) + return false; + + xMax = cvdst[0].x; + xMin = cvdst[0].x; + yMin = cvdst[0].y; + yMax = cvdst[0].y; + + for (int i = 1; i < 4; i++) + { + xMax = wxMax(xMax, cvdst[i].x); + xMin = wxMin(xMin, cvdst[i].x); + yMax = wxMax(yMax, cvdst[i].y); + yMin = wxMin(yMin, cvdst[i].y); + } + + if (xMin == xMax || yMin == yMax) + return false; + + _translate = cv::getPerspectiveTransform(cvsrc, cvdst); + + return !_translate.empty(); +} + +bool PerspectiveFilter::Kernel() +{ + if(!Translate()) return false; + // derived class responsible for allocating storage for filtered image + if (destination.empty()) + { + destination = cv::Mat::zeros(cv::Size(static_cast(xMax - xMin), static_cast(yMax - yMin)), source.type()); + } + //cvWarpPerspective(source, destination, translate); + cv::warpPerspective(source, destination, _translate, destination.size()); + return true; +} + +void PerspectiveFilter::SetSettings(const IFloorCameraSettings& settings) +{ + cvsrc[0] = cv::Point2f(settings.TopLeftX, settings.TopLeftY); + cvsrc[1] = cv::Point2f(settings.TopRightX, settings.TopRightY); + cvsrc[2] = cv::Point2f(settings.BottomRightX, settings.BottomRightY); + cvsrc[3] = cv::Point2f(settings.BottomLeftX, settings.BottomLeftY); + + cvdst[0] = cv::Point2f(0.0f, 0.0f); + cvdst[1] = cv::Point2f((float)settings.CamWidth, 0.0f); + cvdst[2] = cv::Point2f((float)settings.CamWidth, (float)settings.CamHeight); + cvdst[3] = cv::Point2f(0.0f, (float)settings.CamHeight); +} +wxFORCE_LINK_THIS_MODULE(PerspectiveFilter); \ No newline at end of file diff --git a/MotionDetectorCore/PerspectiveFilter.h b/MotionDetectorCore/PerspectiveFilter.h new file mode 100644 index 0000000..642b035 --- /dev/null +++ b/MotionDetectorCore/PerspectiveFilter.h @@ -0,0 +1,35 @@ +#ifndef __TOUCHSCREEN_FILTER_PERSPECTIVE__ +#define __TOUCHSCREEN_FILTER_PERSPECTIVE__ + +#include "FilterTemplate.h" + +class IFloorCameraSettings; + +class MOTION_DETECTOR_CORE_API PerspectiveFilter : public Filter +{ + DECLARE_DYNAMIC_CLASS_NO_COPY(PerspectiveFilter) +public: + PerspectiveFilter(); + virtual ~PerspectiveFilter(); + + bool Kernel(); +#if defined(HAVE_CUDA) + bool KernelGPU(); + cv::Mat src; + cv::Mat dst; + +#endif + virtual wxString GetName() const; + + void SetSettings(const IFloorCameraSettings & settings); + +private: + std::vector cvsrc; + std::vector cvdst; + cv::Mat _translate; + bool Translate(); + float xMax, xMin, yMin, yMax; + +}; + +#endif // __TOUCHSCREEN_FILTER_INVERT__ diff --git a/MotionDetectorCore/RectifyFilter.cpp b/MotionDetectorCore/RectifyFilter.cpp new file mode 100644 index 0000000..a363858 --- /dev/null +++ b/MotionDetectorCore/RectifyFilter.cpp @@ -0,0 +1,90 @@ +#include "stdwx.h" +#include "RectifyFilter.h" +#include "Image.h" + +IMPLEMENT_DYNAMIC_CLASS(RectifyFilter, Filter); + +RectifyFilter::RectifyFilter() +{ + level = (unsigned int) DEFAULT_RECTIFYLEVEL; + level_slider = level; + + bAutoSet = false; + XS_SERIALIZE(bAutoSet, wxT("AutoSet")); + XS_SERIALIZE(level_slider, wxT("Threshold")); +} + +RectifyFilter::~RectifyFilter() +{ +} + +wxString RectifyFilter::GetName() const +{ + return _("Rectify"); +} + +//void RectifyFilter::getParameters(iFloorParamMap& pMap) +//{ +// pMap[std::string("level")] = bAutoSet ? std::string("auto") : toString(level); +//} +// +//void RectifyFilter::setParameter(const char *name, const char *value) +//{ +// if(strcmp(name, "level") == 0) +// { +// if(strcmp(value, "auto") == 0) +// { +// bAutoSet = true; +// printf("Auto set\n"); +// } else +// { +// level = (int) atof(value); +// level_slider = level; +// if(show) +// cvSetTrackbarPos("level", this->name.c_str(), level); +// } +// } +// +//} + +bool RectifyFilter::Kernel() +{ + level = level_slider; + // derived class responsible for allocating storage for filtered image + if (destination.empty()) + { + destination = cv::Mat::zeros(source.size(), CV_8UC1); + } + + if (bAutoSet) + { + touchlib::BwImage img(&source); + + int h, w; + h = img.getHeight(); + w = img.getWidth(); + + unsigned char highest = 0; + + for (int y=0; y highest) + highest = img[y][x]; + } + + setLevel((unsigned int)highest); + bAutoSet = false; + } + + cv::threshold(source, destination, level, 255, cv::THRESH_TOZERO); //CV_THRESH_BINARY + return true; +} + +void RectifyFilter::CreateParamInputs() +{ + AddParameterBoolInput(_("Automatic level"), &bAutoSet); + AddParameterSpinInput(_("Threshold level:"), &level_slider, 255); +} + +wxFORCE_LINK_THIS_MODULE(RectifyFilter); \ No newline at end of file diff --git a/MotionDetectorCore/RectifyFilter.h b/MotionDetectorCore/RectifyFilter.h new file mode 100644 index 0000000..36e68d1 --- /dev/null +++ b/MotionDetectorCore/RectifyFilter.h @@ -0,0 +1,32 @@ +#ifndef __TOUCHSCREEN_FILTER_RECTIFY__ +#define __TOUCHSCREEN_FILTER_RECTIFY__ + +#include "FilterTemplate.h" + +#define DEFAULT_RECTIFYLEVEL 20 + +class MOTION_DETECTOR_CORE_API RectifyFilter : public Filter +{ + DECLARE_DYNAMIC_CLASS_NO_COPY(RectifyFilter) +public: + RectifyFilter(); + virtual ~RectifyFilter(); + + bool Kernel(); +#if defined(HAVE_CUDA) + bool KernelGPU(){return false;} +#endif + virtual wxString GetName() const; + + void setLevel(unsigned int value) {level = value; level_slider = level;} + unsigned int getLevel(void) {return level;} +protected: + void CreateParamInputs(); + +private: + bool bAutoSet; + int level_slider; + int level; +}; + +#endif // __TOUCHSCREEN_FILTER_RECTIFY__ diff --git a/MotionDetectorCore/ResizeFilter.cpp b/MotionDetectorCore/ResizeFilter.cpp new file mode 100644 index 0000000..62ce8ac --- /dev/null +++ b/MotionDetectorCore/ResizeFilter.cpp @@ -0,0 +1,66 @@ +#include "stdwx.h" +#include "ResizeFilter.h" + +IMPLEMENT_DYNAMIC_CLASS(ResizeFilter, Filter); + +ResizeFilter::ResizeFilter() +{ + sizeX = DEFAULT_RESIZEWIDTH; + sizeY = DEFAULT_RESIZEHEIGHT; +} + +ResizeFilter::~ResizeFilter() +{ + +} + +wxString ResizeFilter::GetName() const +{ + return _("Resize"); +} + +//void ResizeFilter::getParameters(iFloorParamMap& pMap) +//{ +// pMap[std::string("sizeX")] = toString(sizeX); +// pMap[std::string("sizeY")] = toString(sizeY); +//} +// +// +//void ResizeFilter::setParameter(const char *name, const char *value) +//{ +// if(strcmp(name, "sizeX") == 0) +// { +// sizeX = (int) atof(value); +// +// if(destination) +// cvReleaseImage(&destination); +// +// destination = cvCreateImage(cvSize(sizeX,sizeY), 8, 1); +// } +// if(strcmp(name, "sizeY") == 0) +// { +// sizeY = (int) atof(value); +// +// if(destination) +// cvReleaseImage(&destination); +// +// destination = cvCreateImage(cvSize(sizeX,sizeY), 8, 1); +// } +//} + +// We are assuming 8 bit depth, 1 channel.. so this filter should happen after the mono filter.. +bool ResizeFilter::Kernel() +{ + // derived class responsible for allocating storage for filtered image + if (source.empty()) + return false; + + if (sizeX <= 0 || sizeY <= 0) + return false; + + cv::resize(source, destination, cv::Size(sizeX, sizeY), 0, 0, cv::INTER_LINEAR); + + return true; +} + +wxFORCE_LINK_THIS_MODULE(ResizeFilter); \ No newline at end of file diff --git a/MotionDetectorCore/ResizeFilter.h b/MotionDetectorCore/ResizeFilter.h new file mode 100644 index 0000000..f5f27cb --- /dev/null +++ b/MotionDetectorCore/ResizeFilter.h @@ -0,0 +1,29 @@ +#ifndef __TOUCHLIB_FILTER_RESIZE__ +#define __TOUCHLIB_FILTER_RESIZE__ + +#include "FilterTemplate.h" +#include + +#define DEFAULT_RESIZEWIDTH 480 +#define DEFAULT_RESIZEHEIGHT 640 + +class MOTION_DETECTOR_CORE_API ResizeFilter : public Filter +{ + DECLARE_DYNAMIC_CLASS_NO_COPY(ResizeFilter) +public: + ResizeFilter(); + virtual ~ResizeFilter(); + + bool Kernel(); +#if defined(HAVE_CUDA) + bool KernelGPU(){return false;} +#endif + virtual wxString GetName() const; + +private: + + int sizeX; + int sizeY; +}; + +#endif // __TOUCHLIB_FILTER_RESIZE__ diff --git a/MotionDetectorCore/ScalerFilter.cpp b/MotionDetectorCore/ScalerFilter.cpp new file mode 100644 index 0000000..299b5e5 --- /dev/null +++ b/MotionDetectorCore/ScalerFilter.cpp @@ -0,0 +1,50 @@ +#include "stdwx.h" +#include "ScalerFilter.h" +#include "Image.h" + +IMPLEMENT_DYNAMIC_CLASS(ScalerFilter, Filter); + +ScalerFilter::ScalerFilter() +{ + level = (unsigned int) DEFAULT_RECTIFYLEVEL; +} + +ScalerFilter::~ScalerFilter() +{ +} + +wxString ScalerFilter::GetName() const +{ + return _("Scaler"); +} + +//void ScalerFilter::getParameters(iFloorParamMap& pMap) +//{ +// pMap[std::string("level")] = toString(level); +//} +// +//void ScalerFilter::setParameter(const char *name, const char *value) +//{ +// if(strcmp(name, "level") == 0) +// { +// level = (int) atof(value); +// level_slider = level; +// if(show) +// cvSetTrackbarPos("level", this->name.c_str(), level); +// } +//} + +bool ScalerFilter::Kernel() +{ + // derived class responsible for allocating storage for filtered image + if (destination.empty()) + { + destination = cv::Mat::zeros(source.size(), CV_8UC1); + } + + float scale = (float)level / 128.0f; + destination = source.mul(source) * scale; + return true; +} + +wxFORCE_LINK_THIS_MODULE(ScalerFilter); \ No newline at end of file diff --git a/MotionDetectorCore/ScalerFilter.h b/MotionDetectorCore/ScalerFilter.h new file mode 100644 index 0000000..9d091b2 --- /dev/null +++ b/MotionDetectorCore/ScalerFilter.h @@ -0,0 +1,29 @@ +#ifndef __TOUCHSCREEN_FILTER_SCALER__ +#define __TOUCHSCREEN_FILTER_SCALER__ + +#include "FilterTemplate.h" + +#define DEFAULT_RECTIFYLEVEL 20 + +class MOTION_DETECTOR_CORE_API ScalerFilter : public Filter +{ + DECLARE_DYNAMIC_CLASS_NO_COPY(ScalerFilter) +public: + ScalerFilter(); + virtual ~ScalerFilter(); + + bool Kernel(); +#if defined(HAVE_CUDA) + bool KernelGPU(){return false;} +#endif + virtual wxString GetName() const; + + void setLevel(unsigned int value) { level = value; } + unsigned int getLevel(void) { return level; } + +private: + bool bAutoSet; + unsigned int level; +}; + +#endif // __TOUCHSCREEN_FILTER_RECTIFY__ diff --git a/MotionDetectorCore/ShapeFilter.cpp b/MotionDetectorCore/ShapeFilter.cpp new file mode 100644 index 0000000..81666b5 --- /dev/null +++ b/MotionDetectorCore/ShapeFilter.cpp @@ -0,0 +1,102 @@ +#include "stdwx.h" +#include "ShapeFilter.h" +#include + +// ---- initialization of non-integral constants ---------------------------- + + +const char *ShapeFilter::TRACKBAR_LABEL_BLUR = "blur"; +const char *ShapeFilter::PARAMETER_BLUR = "blur"; + +const char *ShapeFilter::TRACKBAR_LABEL_LEVEL = "level"; +const char *ShapeFilter::PARAMETER_LEVEL = "level"; + + +// ---- implementations ----------------------------------------------------- + +IMPLEMENT_DYNAMIC_CLASS(ShapeFilter, Filter); + +ShapeFilter::ShapeFilter() +{ + blurLevel = DEFAULT_BLUR_LEVEL; + levelLevel = DEFAULT_LEVEL_LEVEL; +} + + +ShapeFilter::~ShapeFilter() +{ + +} + +wxString ShapeFilter::GetName() const +{ + return _("Shape"); +} + +bool ShapeFilter::Kernel() +{ + if (source.empty()) + { + return false; + } + + if (destination.empty()) + { + destination = cv::Mat::zeros(source.size(), source.type()); + } + if (buffer.empty()) + { + buffer = cv::Mat::zeros(source.size(), source.type()); + } + + destination.setTo(cv::Scalar::all(0)); + + // create the unsharp mask using a linear average filter + int blurParameter = blurLevel * 2 + 1; + cv::blur(source, buffer, cv::Size(blurParameter, blurParameter)); + + //cvSub(source, buffer, buffer); +// cvAbsDiff(source, buffer, buffer); + + cv::Mat tempSource = source.clone(); + cv::threshold(tempSource, tempSource, levelLevel, 255, cv::THRESH_TOZERO); + cv::Canny(tempSource, destination, (float)blurParameter, (float)blurParameter * 3, 3); + + return true; + +/* + //cvSmooth(buffer, buffer, CV_MEDIAN, 3, 1); + + CvMemStorage* storage = cvCreateMemStorage(0); + CvSeq* contours = 0; + CvSeq *result; + cvFindContours( buffer, storage, &contours, sizeof(CvContour), CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE ); + + for( ; contours != 0; contours = contours->h_next ) + { + int count = contours->total; // This is number point in contour + + // First we check to see if this contour looks like a square.. + + result = cvApproxPoly( contours, sizeof(CvContour), storage, + CV_POLY_APPROX_DP, cvContourPerimeter(contours)*0.02, 0 ); + + CvRect r = cvBoundingRect(result); + double area = fabs(cvContourArea(result,CV_WHOLE_SEQ)); + cvDrawContours(destination, result, CV_RGB(255,255,255), CV_RGB(255,255,255), 3, 2, CV_FILLED); + if( result->total > 2 && + area > 20 && r.width < 50 && r.height < 50) + { + + + } + + //contours = cvApproxPoly( contours, sizeof(CvContour), storage, CV_POLY_APPROX_DP, 3, 1 ); + + + } + cvReleaseMemStorage(&storage); +*/ +} + +wxFORCE_LINK_THIS_MODULE(ShapeFilter); \ No newline at end of file diff --git a/MotionDetectorCore/ShapeFilter.h b/MotionDetectorCore/ShapeFilter.h new file mode 100644 index 0000000..0330fc0 --- /dev/null +++ b/MotionDetectorCore/ShapeFilter.h @@ -0,0 +1,36 @@ +#ifndef __TOUCHSCREEN_FILTER_SHAPE__ +#define __TOUCHSCREEN_FILTER_SHAPE__ + +#include "FilterTemplate.h" + +class MOTION_DETECTOR_CORE_API ShapeFilter : public Filter +{ + DECLARE_DYNAMIC_CLASS_NO_COPY(ShapeFilter) +public: + ShapeFilter(); + virtual ~ShapeFilter(); + + bool Kernel(); +#if defined(HAVE_CUDA) + bool KernelGPU(){return false;} +#endif + virtual wxString GetName() const; + +private: + static const int DEFAULT_BLUR_LEVEL = 10; + static const char *TRACKBAR_LABEL_BLUR; + static const char *PARAMETER_BLUR; + + static const int DEFAULT_LEVEL_LEVEL = 0; + static const char *TRACKBAR_LABEL_LEVEL; + static const char *PARAMETER_LEVEL; + + int blurLevel; + int levelLevel; + + cv::Mat buffer; + + void setNoiseSmoothType(int noiseMethod); +}; + +#endif // __TOUCHSCREEN_FILTER_SIMPLE_HIGHPASS__ diff --git a/MotionDetectorCore/SimpleBGFilter.cpp b/MotionDetectorCore/SimpleBGFilter.cpp new file mode 100644 index 0000000..c52ebdf --- /dev/null +++ b/MotionDetectorCore/SimpleBGFilter.cpp @@ -0,0 +1,155 @@ +#include "stdwx.h" +#include "SimpleBGFilter.h" +//#include +#include + +#if defined(__LINUX__) +#include "LinuxUtils.h" +#endif +// LINUX_ + +IMPLEMENT_DYNAMIC_CLASS(SimpleBGFilter, Filter); + +SimpleBGFilter::SimpleBGFilter() +{ + m_bTrackDark = false; + m_ExposureStartTime = timeGetTime(); + m_bLearnBackground = false; + m_bStart = false; + m_CameraExposureTime = 2200; + m_bCalibrated = false; + XS_SERIALIZE(m_bTrackDark, wxT("TrackDark")); + XS_SERIALIZE(m_CameraExposureTime, wxT("CameraExposureTime")); + XS_SERIALIZE(m_bCalibrated, wxT("Calibrated")); + XS_SERIALIZE(m_filename, wxT("filename")); +} + +SimpleBGFilter::~SimpleBGFilter() +{ + +} + +wxString SimpleBGFilter::GetName() const +{ + return _("Simple BG"); +} + +#if defined(HAVE_CUDA) +bool SimpleBGFilter::KernelGPU() +{ + if (!m_bStart){ + int curTime = timeGetTime(); + if((int)(curTime - m_ExposureStartTime) > m_CameraExposureTime){ + m_bLearnBackground = true; + m_bStart = true; + } + else + return false; + } + //Capture full background + if (m_bLearnBackground) + { + m_src->copyTo(m_matBg); + m_bLearnBackground = false; + } + // Subtract background + if (m_bTrackDark){ + cv::gpu::subtract(m_matBg, *m_src, m_dst); + } + else{ + cv::gpu::subtract(*m_src, m_matBg, m_dst); + } + + return true; +} +#endif + + +bool SimpleBGFilter::Kernel() +{ + // derived class responsible for allocating storage for filtered image + if (source.empty()) + { + return false; + } + + if (destination.empty()) + { + destination = cv::Mat::zeros(source.size(), CV_8UC1); + } + if (!m_bStart) + { + int curTime = timeGetTime(); + //wxLogDebug(wxT("%i, %i %i > %i"), curTime, m_ExposureStartTime, curTime - m_ExposureStartTime, m_CameraExposureTime); + if((int)(curTime - m_ExposureStartTime) > m_CameraExposureTime) + { + m_bLearnBackground = true; + m_bStart = true; + } + else + { + return false; + } + } + //Capture full background + if (m_bLearnBackground) + { + if(m_bCalibrated && wxFile::Exists(m_filename)) + { + background = cv::imread("c:\\background.jpg", cv::IMREAD_GRAYSCALE); + } + else + { + if (background.empty()) + { + background = cv::Mat::zeros(source.size(), CV_8UC1); + } + source.copyTo(background); + //ResetCalibration(); + } + m_bLearnBackground = false; + } + if (background.empty()) + { + return false; + } + + //IplImage* destination1 = cvCreateImage(cvSize(source->width, source->height), IPL_DEPTH_8U, 1); + //cvSub(background, source, destination1); + //else + /*cvSub(source, background, destination); + cvOr(destination, destination1, destination); +*/ + // Subtract background + if (m_bTrackDark) + cv::subtract(background, source, destination); + else + cv::subtract(source, background, destination); + + return true; +} + +void SimpleBGFilter::CreateParamInputs() +{ + AddParameterBoolInput(_("Learn background"), &m_bLearnBackground); + AddParameterBoolInput(_("Track dark blobs"), &m_bTrackDark); + AddParameterSpinInput(_("Camera exposure time:"), &m_CameraExposureTime, 20000, 0); +} + +void SimpleBGFilter::SaveBackground(wxString filename) +{ + m_bCalibrated = background.empty(); + if(m_bCalibrated) + { + m_filename = filename; + cv::imwrite("c:\\background.jpg", background); + } +} + +void SimpleBGFilter::ResetCalibration() +{ + m_bCalibrated = false; + m_bLearnBackground = true; +} + +wxFORCE_LINK_THIS_MODULE(SimpleBGFilter); diff --git a/MotionDetectorCore/SimpleBGFilter.h b/MotionDetectorCore/SimpleBGFilter.h new file mode 100644 index 0000000..1270265 --- /dev/null +++ b/MotionDetectorCore/SimpleBGFilter.h @@ -0,0 +1,43 @@ +#ifndef __TOUCHLIB_FILTER_SIMPLEBG__ +#define __TOUCHLIB_FILTER_SIMPLEBG__ + +#include "FilterTemplate.h" +#include +#include + +class MOTION_DETECTOR_CORE_API SimpleBGFilter : public Filter +{ + DECLARE_DYNAMIC_CLASS_NO_COPY(SimpleBGFilter) +public: + SimpleBGFilter(); + virtual ~SimpleBGFilter(); + + bool Kernel(); +#if defined(HAVE_CUDA) + bool KernelGPU(); +#endif + virtual wxString GetName() const; + void SaveBackground(wxString filename); + void ResetCalibration(); +protected: + void CreateParamInputs(); + +private: + cv::Mat background; + bool m_bTrackDark; + bool m_bLearnBackground; + int m_ExposureStartTime; + int m_CameraExposureTime; + bool m_bCalibrated; + bool m_bErodeDilate; + wxString m_filename; + cv::Mat m_erodeKernel; + cv::Mat m_dilateKernel; + bool m_bStart; + #if defined(HAVE_CUDA) + cv::cuda::GpuMat m_matBg; + #endif + +}; + +#endif // __TOUCHLIB_FILTER_SIMPLEBG__ diff --git a/MotionDetectorCore/SimpleHighpassFilter.cpp b/MotionDetectorCore/SimpleHighpassFilter.cpp new file mode 100644 index 0000000..c0a1d0a --- /dev/null +++ b/MotionDetectorCore/SimpleHighpassFilter.cpp @@ -0,0 +1,98 @@ +#include "stdwx.h" +#include "SimpleHighpassFilter.h" + +IMPLEMENT_DYNAMIC_CLASS(SimpleHighpassFilter, Filter); + +SimpleHighpassFilter::SimpleHighpassFilter() +{ + noiseSmoothType = 1; + blurLevel = DEFAULT_BLUR_LEVEL; + + setNoiseSmoothType(DEFAULT_NOISE_METHOD); + + noiseLevel = DEFAULT_NOISE_LEVEL; + + XS_SERIALIZE(blurLevel, wxT("BlurLevel")); + XS_SERIALIZE(noiseLevel, wxT("NoiseLevel")); + XS_SERIALIZE(noiseSmoothType, wxT("NoiseSmoothType")); +} + +SimpleHighpassFilter::~SimpleHighpassFilter() +{ + +} + +wxString SimpleHighpassFilter::GetName() const +{ + return _("Simple Highpass"); +} + +void SimpleHighpassFilter::setNoiseSmoothType(int noiseMethod) +{ + //switch (noiseMethod) + //{ + //case NOISE_METHOD_MEDIAN: + // noiseSmoothType = CV_MEDIAN; + // break; + //case NOISE_METHOD_BLUR: + noiseSmoothType = 1; + // break; + //} +} +#if defined(HAVE_CUDA) +bool SimpleHighpassFilter::KernelGPU() +{ + int blurParameter = blurLevel * 2 + 1; + int noiseParameter = noiseLevel * 2 + 1; + cv::Mat tmp, tmpDst; + if(blurLevel){ + cv::cuda::blur(*m_src, m_tmpGpuMat, cv::Size(blurParameter, blurParameter)); + cv::cuda::subtract(*m_src, m_tmpGpuMat, m_tmpGpuMat); + m_tmpGpuMat.download(tmp); + } + else + m_src->download(tmp); + + cv::medianBlur(tmp, tmpDst, noiseParameter); + m_dst.upload(tmpDst); + return true; +} +#endif + + +bool SimpleHighpassFilter::Kernel() +{ + if (source.empty()) + { + return false; + } + + + if (destination.empty()) + { + destination = cv::Mat::zeros(source.size(), source.type()); + } + if (buffer.empty()) + { + buffer = cv::Mat::zeros(source.size(), source.type()); + } + + int blurParameter = blurLevel * 2 + 1; + int noiseParameter = noiseLevel * 2 + 1; + // create the unsharp mask using a linear average filter + cv::blur(source, buffer, cv::Size(blurParameter, blurParameter)); + //cvAbsDiff(source, buffer, buffer); + cv::subtract(source, buffer, buffer); + + // filter out the noise using a median filter + cv::medianBlur(buffer, destination, noiseParameter); + return true; +} + +void SimpleHighpassFilter::CreateParamInputs() +{ + AddParameterSpinInput(_("Blur level:"), &blurLevel, 200, 0); + AddParameterSpinInput(_("Noise level:"), &noiseLevel, 30, 0); +} + +wxFORCE_LINK_THIS_MODULE(SimpleHighpassFilter); diff --git a/MotionDetectorCore/SimpleHighpassFilter.h b/MotionDetectorCore/SimpleHighpassFilter.h new file mode 100644 index 0000000..105ace0 --- /dev/null +++ b/MotionDetectorCore/SimpleHighpassFilter.h @@ -0,0 +1,50 @@ +#ifndef __TOUCHSCREEN_FILTER_SIMPLE_HIGHPASS__ +#define __TOUCHSCREEN_FILTER_SIMPLE_HIGHPASS__ + +#include "FilterTemplate.h" +#include + +// This filter is used for filtering out sharp-edged objects from an image. +class MOTION_DETECTOR_CORE_API SimpleHighpassFilter : public Filter +{ + DECLARE_DYNAMIC_CLASS_NO_COPY(SimpleHighpassFilter) +public: + SimpleHighpassFilter(); + virtual ~SimpleHighpassFilter(); + + bool Kernel(); +#if defined(HAVE_CUDA) + bool KernelGPU(); +#endif + virtual wxString GetName() const; + +private: + + // ---- constants ---------------------------------------------------------- + + static const int DEFAULT_BLUR_LEVEL = 10; + static const int DEFAULT_NOISE_METHOD = 1; + static const int DEFAULT_NOISE_LEVEL = 3; + + static const int NOISE_METHOD_MEDIAN = 0; + static const int NOISE_METHOD_BLUR = 1; + + // ---- instance variables ------------------------------------------------- + + int blurLevel; + int noiseSmoothType; + int noiseLevel; + + cv::Mat buffer; +#if defined(HAVE_CUDA) + cv::cuda::GpuMat m_matBg; +#endif + + // ---- methods ------------------------------------------------------------ + + void setNoiseSmoothType(int noiseMethod); +protected: + void CreateParamInputs(); +}; + +#endif // __TOUCHSCREEN_FILTER_SIMPLE_HIGHPASS__ diff --git a/MotionDetectorCore/SmoothingFilter.cpp b/MotionDetectorCore/SmoothingFilter.cpp new file mode 100644 index 0000000..a07da02 --- /dev/null +++ b/MotionDetectorCore/SmoothingFilter.cpp @@ -0,0 +1,70 @@ +#include "stdwx.h" +#include "SmoothingFilter.h" + +IMPLEMENT_DYNAMIC_CLASS(SmoothingFilter, Filter); + +SmoothingFilter::SmoothingFilter() +{ + level = 0; + XS_SERIALIZE(level, "Level"); + +} + +SmoothingFilter::~SmoothingFilter() +{ +} + +wxString SmoothingFilter::GetName() const +{ + return _("Smoothing"); +} + +#if defined(HAVE_CUDA) +bool SmoothingFilter::KernelGPU() +{ + if (m_src->empty()) + return false; + + int lvl = level * 2 + 1; + if (lvl > 1) + { + cv::cuda::blur(*m_src, m_dst, cv::Size(lvl, lvl)); + } + else + { + m_src->copyTo(m_dst); + } + return true; +} +#endif + +// The smooth filter really needs the blur size as a param +bool SmoothingFilter::Kernel() +{ + if (source.empty()) + return false; + + // derived class responsible for allocating storage for filtered image + if (destination.empty()) + { + destination = cv::Mat::zeros(source.size(), source.type()); + } + + int lvl = level * 2 + 1; + + if (lvl > 1) + { + cv::blur(source, destination, cv::Size(lvl, lvl)); + } + else + { + source.copyTo(destination); + } +} + +void SmoothingFilter::CreateParamInputs() +{ + AddParameterSpinInput(_("Smooth level:"), &level, 15); +} + +wxFORCE_LINK_THIS_MODULE(SmoothingFilter); \ No newline at end of file diff --git a/MotionDetectorCore/SmoothingFilter.h b/MotionDetectorCore/SmoothingFilter.h new file mode 100644 index 0000000..d7aa699 --- /dev/null +++ b/MotionDetectorCore/SmoothingFilter.h @@ -0,0 +1,25 @@ +#ifndef __TOUCHSCREEN_FILTER_SMOOTHING__ +#define __TOUCHSCREEN_FILTER_SMOOTHING__ + +#include "FilterTemplate.h" +#include + +class MOTION_DETECTOR_CORE_API SmoothingFilter : public Filter +{ + DECLARE_DYNAMIC_CLASS_NO_COPY(SmoothingFilter) +public: + SmoothingFilter(); + virtual ~SmoothingFilter(); + + bool Kernel(); +#if defined(HAVE_CUDA) + bool KernelGPU(); +#endif + virtual wxString GetName() const; + + int level; +protected: + void CreateParamInputs(); +}; + +#endif // __TOUCHSCREEN_FILTER_SMOOTHING__ diff --git a/MotionDetectorCore/SubOrBGFilter.cpp b/MotionDetectorCore/SubOrBGFilter.cpp new file mode 100644 index 0000000..a4ecd46 --- /dev/null +++ b/MotionDetectorCore/SubOrBGFilter.cpp @@ -0,0 +1,151 @@ +#include "stdwx.h" +#include "SubOrBGFilter.h" +#include + +#if defined(__LINUX__) +#include "LinuxUtils.h" +#endif +// LINUX_ + +IMPLEMENT_DYNAMIC_CLASS(SubOrBGFilter, Filter); + +SubOrBGFilter::SubOrBGFilter() +{ + m_ExposureStartTime = timeGetTime(); + + m_bLearnBackground = false; + m_bStart = false; + m_CameraExposureTime = 2200; + m_bCalibrated = false; + XS_SERIALIZE(m_CameraExposureTime, wxT("CameraExposureTime")); + XS_SERIALIZE(m_bCalibrated, wxT("Calibrated")); + XS_SERIALIZE(m_filename, wxT("filename")); +} + +SubOrBGFilter::~SubOrBGFilter() +{ + +} + +wxString SubOrBGFilter::GetName() const +{ + return _("Substruct OR BG"); +} + +#if defined(HAVE_CUDA) +bool SubOrBGFilter::KernelGPU() +{ + if (!m_bStart){ + int curTime = timeGetTime(); + if ((int)(curTime - m_ExposureStartTime) > m_CameraExposureTime){ + m_bLearnBackground = true; + m_bStart = true; + } + else + return false; + } + //Capture full background + if (m_bLearnBackground) + { + m_src->copyTo(m_matBg); + m_bLearnBackground = false; + } + // Subtract background + if (m_bTrackDark){ + cv::cuda::subtract(m_matBg, *m_src, m_dst); + } + else{ + cv::cuda::subtract(*m_src, m_matBg, m_dst); + } + + return true; +} +#endif + + +bool SubOrBGFilter::Kernel() +{ + // derived class responsible for allocating storage for filtered image + if (source.empty()) + { + return false; + } + + if (destination.empty()) + { + destination = cv::Mat::zeros(source.size(), CV_8UC1); + } + if (!m_bStart) + { + int curTime = timeGetTime(); + //wxLogDebug(wxT("%i, %i %i > %i"), curTime, m_ExposureStartTime, curTime - m_ExposureStartTime, m_CameraExposureTime); + if ((int)(curTime - m_ExposureStartTime) > m_CameraExposureTime) + { + m_bLearnBackground = true; + m_bStart = true; + } + else + { + return false; + } + } + //Capture full background + if (m_bLearnBackground) + { + if (m_bCalibrated && wxFile::Exists(m_filename)) + { + background = cv::imread("c:\\background.jpg", cv::IMREAD_GRAYSCALE); + } + else + { + if (background.empty()) + { + background = cv::Mat::zeros(source.size(), CV_8UC1); + } + if (destination1.empty()) + { + destination1 = cv::Mat::zeros(source.size(), CV_8UC1); + } + source.copyTo(background); + //ResetCalibration(); + } + m_bLearnBackground = false; + } + if (background.empty()) + { + return false; + } + + cv::subtract(background, source, destination1); + cv::subtract(source, background, destination); + cv::bitwise_or(destination, destination1, destination); + return true; +} + +void SubOrBGFilter::CreateParamInputs() +{ + AddParameterBoolInput(_("Learn background"), &m_bLearnBackground); + AddParameterSpinInput(_("Camera exposure time:"), &m_CameraExposureTime, 20000, 0); +} + +void SubOrBGFilter::SaveBackground(wxString filename) +{ + m_bCalibrated = !background.empty(); + if (m_bCalibrated) + { + m_filename = filename; + // cvSaveImage(/*filename.c_str()*/"c:\\background.jpg", background); + } +} + +void SubOrBGFilter::ResetCalibration() +{ + m_bCalibrated = false; + m_bLearnBackground = true; +} + +wxFORCE_LINK_THIS_MODULE(SubOrBGFilter); +//cvSub(background, source, destination1); +////else +//cvSub(source, background, destination); +//cvOr(destination, destination1, destination); diff --git a/MotionDetectorCore/SubOrBGFilter.h b/MotionDetectorCore/SubOrBGFilter.h new file mode 100644 index 0000000..351359a --- /dev/null +++ b/MotionDetectorCore/SubOrBGFilter.h @@ -0,0 +1,43 @@ +#ifndef __TOUCHLIB_FILTER_SUBORBG__ +#define __TOUCHLIB_FILTER_SUBORBG__ + +#include "FilterTemplate.h" +#include +#include + +class MOTION_DETECTOR_CORE_API SubOrBGFilter : public Filter +{ + DECLARE_DYNAMIC_CLASS_NO_COPY(SubOrBGFilter) +public: + SubOrBGFilter(); + virtual ~SubOrBGFilter(); + + bool Kernel(); +#if defined(HAVE_CUDA) + bool KernelGPU(); +#endif + virtual wxString GetName() const; + void SaveBackground(wxString filename); + void ResetCalibration(); +protected: + void CreateParamInputs(); + +private: + cv::Mat background; + cv::Mat destination1; + bool m_bLearnBackground; + int m_ExposureStartTime; + int m_CameraExposureTime; + bool m_bCalibrated; + bool m_bErodeDilate; + wxString m_filename; + cv::Mat m_erodeKernel; + cv::Mat m_dilateKernel; + bool m_bStart; +#if defined(HAVE_CUDA) + cv::cuda::GpuMat m_matBg; +#endif + +}; + +#endif // __TOUCHLIB_FILTER_SUBORBG__ diff --git a/MotionDetectorCore/ThresholdFilter.cpp b/MotionDetectorCore/ThresholdFilter.cpp new file mode 100644 index 0000000..42c6e3f --- /dev/null +++ b/MotionDetectorCore/ThresholdFilter.cpp @@ -0,0 +1,58 @@ +#include "stdwx.h" +#include "ThresholdFilter.h" + + +// ---- initialization of non-integral constants ---------------------------- + + +// ---- implementations ----------------------------------------------------- + +IMPLEMENT_DYNAMIC_CLASS(ThresholdFilter, Filter); + +ThresholdFilter::ThresholdFilter() +{ + thresholdSlider1 = 1; + + XS_SERIALIZE(thresholdSlider1, wxT("Threshold1")); + +} + +ThresholdFilter::~ThresholdFilter() +{ +} + +wxString ThresholdFilter::GetName() const +{ + return _("Threshold"); +} + +#if defined(HAVE_CUDA) +bool ThresholdFilter::KernelGPU() +{ + cv::cuda::threshold(*m_src, m_dst, thresholdSlider1, 255, cv::THRESH_BINARY); + return true; +} +#endif + + +bool ThresholdFilter::Kernel() +{ + if (source.empty()) + return false; + + if (destination.empty()) + { + destination = cv::Mat::zeros(source.size(), source.type()); + } + + cv::threshold(source, destination, thresholdSlider1, 255, cv::THRESH_BINARY); + + return true; +} + +void ThresholdFilter::CreateParamInputs() +{ + AddParameterSpinInput(_("Threshold level:"), &thresholdSlider1, 255); +} + +wxFORCE_LINK_THIS_MODULE(ThresholdFilter); \ No newline at end of file diff --git a/MotionDetectorCore/ThresholdFilter.h b/MotionDetectorCore/ThresholdFilter.h new file mode 100644 index 0000000..8c17cc4 --- /dev/null +++ b/MotionDetectorCore/ThresholdFilter.h @@ -0,0 +1,30 @@ +#ifndef __TOUCHSCREEN_FILTER_THRESHOLD__ +#define __TOUCHSCREEN_FILTER_THRESHOLD__ + +#include "FilterTemplate.h" +#include + +// This filter has two modes. One that dynamically changes the threshold with the help +// of statistics that are recorded, and one which is static. Idea by Damian. +class MOTION_DETECTOR_CORE_API ThresholdFilter : public Filter +{ + DECLARE_DYNAMIC_CLASS_NO_COPY(ThresholdFilter) +public: + ThresholdFilter(); + virtual ~ThresholdFilter(); + + bool Kernel(); +#if defined(HAVE_CUDA) + bool KernelGPU(); +#endif + virtual wxString GetName() const; + +private: + + int thresholdSlider1; + +protected: + void CreateParamInputs(); +}; + +#endif // __TOUCHSCREEN_FILTER_THRESHOLD__ diff --git a/MotionDetectorCore/Tracking.cpp b/MotionDetectorCore/Tracking.cpp new file mode 100644 index 0000000..ca4d062 --- /dev/null +++ b/MotionDetectorCore/Tracking.cpp @@ -0,0 +1,219 @@ +#include "stdwx.h" +#include "Tracking.h" + +#if defined(USE_VLD) +#include +#endif + +BlobTracker::BlobTracker() +{ + IDCounter = 0; + isCalibrating = false; +} + +BlobTracker::~BlobTracker(){ + +} + +// Assigns IDs to each blob in the contourFinder +void BlobTracker::track(ContourFinder* newBlobs) +{ + //initialize ID's of all blobs + for(size_t i=0; iblobs.size(); i++) + newBlobs->blobs[i].id=-1; + + //go through all tracked blobs to compute nearest new point + for (size_t i=0; iblobs[winner].id!=-1) + { + //find the currently assigned blob + size_t j = 0; //j will be the index of it + for (j = 0; j < trackedBlobs.size(); j++) + { + if (trackedBlobs[j].id==newBlobs->blobs[winner].id) break; + } + + if (j==trackedBlobs.size())//got to end without finding it + { + newBlobs->blobs[winner].id = trackedBlobs[i].id; + trackedBlobs[i] = newBlobs->blobs[winner]; + } + else //found it, compare with current blob + { + double x = newBlobs->blobs[winner].centroid.x; + double y = newBlobs->blobs[winner].centroid.y; + double xOld = trackedBlobs[j].centroid.x; + double yOld = trackedBlobs[j].centroid.y; + double xNew = trackedBlobs[i].centroid.x; + double yNew = trackedBlobs[i].centroid.y; + double distOld = (x-xOld)*(x-xOld)+(y-yOld)*(y-yOld); + double distNew = (x-xNew)*(x-xNew)+(y-yNew)*(y-yNew); + + //if this track is closer, update the ID of the blob + //otherwise delete this track.. it's dead + if (distNew < distOld) //update + { + newBlobs->blobs[winner].id = trackedBlobs[i].id; + trackedBlobs[j].id = -1; +//------------------------------------------------------------------------------ + } + else //delete + { + trackedBlobs[i].id = -1; + } + } + } + else //no conflicts, so simply update + { + newBlobs->blobs[winner].id = trackedBlobs[i].id; + } + } + } + + //--Update All Current Tracks + //remove every track labeled as dead (ID='-1') + //find every track that's alive and copy it's data from newBlobs + + auto new_end = std::remove_if(trackedBlobs.begin(), trackedBlobs.end(), + [](const iFloorBlob& blob) { return blob.id == -1; }); + trackedBlobs.erase(new_end, trackedBlobs.end()); + + for (size_t i = 0; i < trackedBlobs.size(); i++) + { + for (size_t j = 0; j < newBlobs->blobs.size(); j++) + { + if (trackedBlobs[i].id == newBlobs->blobs[j].id) + { + //update track + iFloorPoint tempLastCentroid = trackedBlobs[i].centroid; // assign the new centroid to the old + trackedBlobs[i] = newBlobs->blobs[j]; + trackedBlobs[i].lastCentroid = tempLastCentroid; + } + } + } + //--Add New Living Tracks + //now every new blob should be either labeled with a tracked ID or\ + //have ID of -1... if the ID is -1... we need to make a new track + for(size_t i=0; iblobs.size(); i++) + { + if(newBlobs->blobs[i].id==-1) + { + //add new track + newBlobs->blobs[i].id=IDCounter++; + trackedBlobs.push_back(newBlobs->blobs[i]); + } + } +} + +const iFloorBlobVector & BlobTracker::getTrackedBlobs() const +{ + return trackedBlobs; +} + +/************************************************************************* +* Finds the blob in 'newBlobs' that is closest to the trackedBlob with index +* 'ind' according to the KNN algorithm and returns the index of the winner +* newBlobs = list of blobs detected in the latest frame +* track = current tracked blob being tested +* k = number of nearest neighbors to consider\ +* 1,3,or 5 are common numbers..\ +* must always be an odd number to avoid tying +* thresh = threshold for optimization +**************************************************************************/ + +int BlobTracker::trackKnn(ContourFinder *newBlobs, iFloorBlob *track, int k, double thresh = 0) +{ + + int winner = -1; //initially label track as '-1'=dead + if ((k%2)==0) k++; //if k is not an odd number, add 1 to it + + //if it exists, square the threshold to use as square distance + if (thresh>0) + thresh *= thresh; + + //list of neighbor point index and respective distances + std::list > nbors; + std::list >::iterator iter; + + //find 'k' closest neighbors of testpoint + double x, y, xT, yT, dist; + for (size_t i=0; iblobs.size(); i++) + { + x = newBlobs->blobs[i].centroid.x; + y = newBlobs->blobs[i].centroid.y; + + xT = track->centroid.x; + yT = track->centroid.y; + dist = (x-xT)*(x-xT)+(y-yT)*(y-yT); + + if (dist<=thresh)//it's good, apply label if no label yet and return + { + winner = i; + return winner; + } + + /**************************************************************** + * check if this blob is closer to the point than what we've seen + *so far and add it to the index/distance list if positive + ****************************************************************/ + + auto nborsEnd = nbors.end(); + //search the list for the first point with a longer distance + for (iter=nbors.begin(); iter != nborsEnd && dist >= iter->second; iter++); + + if ((iter != nborsEnd) || (nbors.size()<(size_t)k)) //it's valid, insert it + { + nbors.insert(iter, 1, std::pair(i, dist)); + //too many items in list, get rid of farthest neighbor + if (nbors.size()>(size_t)k) + nbors.pop_back(); + } + } + + /******************************************************************** + * we now have k nearest neighbors who cast a vote, and the majority + * wins. we use each class average distance to the target to break any + * possible ties. + *********************************************************************/ + + // a mapping from labels (IDs) to count/distance + std::map > votes; + + //remember: + //iter->first = index of newBlob + //iter->second = distance of newBlob to current tracked blob + auto nborsEnd = nbors.end(); + for (iter = nbors.begin(); iter != nborsEnd; iter++) + { + //add up how many counts each neighbor got + int count = ++(votes[iter->first].first); + double dist = (votes[iter->first].second+=iter->second); + + /* check for a possible tie and break with distance */ + if (count>votes[winner].first || count==votes[winner].first + && distfirst; + } + } + + return winner; +} diff --git a/MotionDetectorCore/Tracking.h b/MotionDetectorCore/Tracking.h new file mode 100644 index 0000000..166011a --- /dev/null +++ b/MotionDetectorCore/Tracking.h @@ -0,0 +1,31 @@ +#ifndef _TRACKING_H +#define _TRACKING_H + +#include +#include + +#include "ContourFinder.h" + +class MOTION_DETECTOR_CORE_API BlobTracker +{ +public: + BlobTracker(); + ~BlobTracker(); + + // Assigns IDs to each blob in the contourFinder + void track(ContourFinder* newBlobs); + bool isCalibrating; + int MIN_MOVEMENT_THRESHOLD; + const iFloorBlobVector & getTrackedBlobs() const; + +private: + int trackKnn(ContourFinder *newBlobs, iFloorBlob *track, int k, double thresh); + +private: + int IDCounter; //counter of last blob + int fightMongrel; + iFloorBlobVector trackedBlobs; //tracked blobs + std::map calibratedBlobs; +}; + +#endif diff --git a/MotionDetectorCore/Win/MotionDetectorCore.vcxproj b/MotionDetectorCore/Win/MotionDetectorCore.vcxproj new file mode 100644 index 0000000..8a9e3e2 --- /dev/null +++ b/MotionDetectorCore/Win/MotionDetectorCore.vcxproj @@ -0,0 +1,481 @@ + + + + x64 + + + + Debug + x64 + + + Release + x64 + + + + {6E933546-0B62-311B-8D7E-6CE2ECD4CAD0} + Win32Proj + 10.0.26100.0 + x64 + MotionDetectorCore + NoUpgrade + + + + DynamicLibrary + Unicode + v143 + + + DynamicLibrary + Unicode + v143 + + + + + + + + + + <_ProjectFileVersion>10.0.20506.1 + C:\Users\Admin\IFloor\projects\wxVideoProcessing\bin\ + MotionDetectorCore.dir\Debug\ + MotionDetectorCore + .dll + true + true + C:\Users\Admin\IFloor\projects\wxVideoProcessing\bin\ + MotionDetectorCore.dir\Release\ + MotionDetectorCore + .dll + false + true + + + + + C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\mswu;C:\Users\Admin\IFloor\libs\wxWidgets\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\Utils;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\MotionDetectorCore;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxJSON\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxxmlserializer\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxxmlserializer;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\MotionDetectorPluginBase;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\CommonPluginBase;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxXS\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\MotionPrimitives;C:\Users\Admin\IFloor\projects\wxVideoProcessing\ThirdParty\wxXS\build\..\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\ThirdParty\wxJSON\build\..\include;%(AdditionalIncludeDirectories) + %(AdditionalOptions) /external:I "C:/Users/Admin/IFloor/libs/opencv/build/include" + $(IntDir) + EnableFastChecks + + + ProgramDatabase + 4996 + Sync + TurnOffAllWarnings + + + Disabled + stdcpp17 + + true + Disabled + Use + C:/Users/Admin/IFloor/projects/wxVideoProcessing/MotionDetectorCore/Win/CMakeFiles/MotionDetectorCore.dir/Debug/cmake_pch.hxx + C:/Users/Admin/IFloor/projects/wxVideoProcessing/MotionDetectorCore/Win/MotionDetectorCore.dir/Debug//cmake_pch.pch + + MultiThreadedDebugDLL + true + + + false + %(PreprocessorDefinitions);WIN32;_WINDOWS;__WXDEBUG__=1;WIN32_LEAN_AND_MEAN;WXUSINGDLL;UNICODE;_UNICODE;_CRT_SECURE_NO_DEPRECATE;_USRDLL;MOTION_DETECTOR_CORE_EXPORTS;CMAKE_INTDIR="Debug";MotionDetectorCore_EXPORTS + $(IntDir) + false + + + %(PreprocessorDefinitions);WIN32;_DEBUG;_WINDOWS;__WXDEBUG__=1;WIN32_LEAN_AND_MEAN;WXUSINGDLL;UNICODE;_UNICODE;_CRT_SECURE_NO_DEPRECATE;_USRDLL;MOTION_DETECTOR_CORE_EXPORTS;CMAKE_INTDIR=\"Debug\";MotionDetectorCore_EXPORTS + C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\mswu;C:\Users\Admin\IFloor\libs\wxWidgets\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\Utils;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\MotionDetectorCore;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxJSON\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxxmlserializer\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxxmlserializer;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\MotionDetectorPluginBase;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\CommonPluginBase;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxXS\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\MotionPrimitives;C:\Users\Admin\IFloor\projects\wxVideoProcessing\ThirdParty\wxXS\build\..\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\ThirdParty\wxJSON\build\..\include;C:\Users\Admin\IFloor\libs\opencv\build\include;%(AdditionalIncludeDirectories) + + + C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\mswu;C:\Users\Admin\IFloor\libs\wxWidgets\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\Utils;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\MotionDetectorCore;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxJSON\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxxmlserializer\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxxmlserializer;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\MotionDetectorPluginBase;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\CommonPluginBase;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxXS\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\MotionPrimitives;C:\Users\Admin\IFloor\projects\wxVideoProcessing\ThirdParty\wxXS\build\..\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\ThirdParty\wxJSON\build\..\include;C:\Users\Admin\IFloor\libs\opencv\build\include;%(AdditionalIncludeDirectories) + $(ProjectDir)/$(IntDir) + %(Filename).h + %(Filename).tlb + %(Filename)_i.c + %(Filename)_p.c + + + + setlocal +"C:\Program Files\CMake\bin\cmake.exe" -E make_directory C:/Users/Admin/IFloor/projects/wxVideoProcessing/build/../bin/plugins/motion_detector +if %errorlevel% neq 0 goto :cmEnd +"C:\Program Files\CMake\bin\cmake.exe" -E copy_if_different C:/Users/Admin/IFloor/projects/wxVideoProcessing/bin/MotionDetectorCore.dll C:/Users/Admin/IFloor/projects/wxVideoProcessing/build/../bin/plugins/motion_detector/MotionDetectorCore.dll +if %errorlevel% neq 0 goto :cmEnd +:cmEnd +endlocal & call :cmErrorLevel %errorlevel% & goto :cmDone +:cmErrorLevel +exit /b %1 +:cmDone +if %errorlevel% neq 0 goto :VCEnd + + + C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxbase33ud.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33ud_core.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33ud_adv.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33ud_aui.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxbase33ud_net.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33ud_gl.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxbase33ud_xml.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33ud_propgrid.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33ud_html.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxpngd.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxtiffd.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxjpegd.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxzlibd.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxregexud.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxexpatd.lib;opengl32.lib;glu32.lib;winmm.lib;comctl32.lib;uuid.lib;oleacc.lib;uxtheme.lib;rpcrt4.lib;shlwapi.lib;version.lib;wsock32.lib;C:\Users\Admin\IFloor\projects\wxVideoProcessing\bin\lib\Debug\Utils.lib;C:\Users\Admin\IFloor\projects\wxVideoProcessing\bin\lib\Debug\MotionDetectorPluginBase.lib;C:\Users\Admin\IFloor\projects\wxVideoProcessing\bin\lib\Debug\MotionPrimitives.lib;C:\Users\Admin\IFloor\libs\opencv\build\x64\vc16\lib\opencv_world4120d.lib;C:\Users\Admin\IFloor\libs\opencv\build\x64\vc16\lib\opencv_world4120d.lib;C:\Users\Admin\IFloor\libs\opencv\build\x64\vc16\lib\opencv_world4120d.lib;C:\Users\Admin\IFloor\libs\opencv\build\x64\vc16\lib\opencv_world4120d.lib;C:\Users\Admin\IFloor\libs\opencv\build\x64\vc16\lib\opencv_world4120d.lib;C:\Users\Admin\IFloor\libs\opencv\build\x64\vc16\lib\opencv_world4120d.lib;C:\Users\Admin\IFloor\libs\opencv\build\x64\vc16\lib\opencv_world4120d.lib;C:\Users\Admin\IFloor\libs\opencv\build\x64\vc16\lib\opencv_world4120d.lib;C:\Users\Admin\IFloor\libs\opencv\build\x64\vc16\lib\opencv_world4120d.lib;C:\Users\Admin\IFloor\libs\opencv\build\x64\vc16\lib\opencv_world4120d.lib;C:\Users\Admin\IFloor\libs\opencv\build\x64\vc16\lib\opencv_world4120d.lib;C:\Users\Admin\IFloor\libs\opencv\build\x64\vc16\lib\opencv_world4120d.lib;C:\Users\Admin\IFloor\libs\opencv\build\x64\vc16\lib\opencv_world4120d.lib;C:\Users\Admin\IFloor\libs\opencv\build\x64\vc16\lib\opencv_world4120d.lib;C:\Users\Admin\IFloor\libs\opencv\build\x64\vc16\lib\opencv_world4120d.lib;C:\Users\Admin\IFloor\libs\opencv\build\x64\vc16\lib\opencv_world4120d.lib;C:\Users\Admin\IFloor\projects\wxVideoProcessing\bin\lib\Debug\CommonPluginBase.lib;C:\Users\Admin\IFloor\projects\wxVideoProcessing\bin\lib\Debug\Utils.lib;C:\Users\Admin\IFloor\projects\wxVideoProcessing\bin\lib\Debug\wxXS.lib;C:\Users\Admin\IFloor\projects\wxVideoProcessing\bin\lib\Debug\wxJSON.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxbase33ud.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33ud_core.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33ud_adv.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33ud_aui.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxbase33ud_net.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33ud_gl.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxbase33ud_xml.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33ud_propgrid.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33ud_html.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxpngd.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxtiffd.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxjpegd.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxzlibd.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxregexud.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxexpatd.lib;opengl32.lib;glu32.lib;winmm.lib;comctl32.lib;uuid.lib;oleacc.lib;uxtheme.lib;rpcrt4.lib;shlwapi.lib;version.lib;wsock32.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;comdlg32.lib;advapi32.lib + C:/Users/Admin/IFloor/projects/wxVideoProcessing/build/Win/lib;C:/Users/Admin/IFloor/projects/wxVideoProcessing/build/Win/lib/$(Configuration);%(AdditionalLibraryDirectories) + %(AdditionalOptions) /machine:x64 + + true + %(IgnoreSpecificDefaultLibraries) + + C:/Users/Admin/IFloor/projects/wxVideoProcessing/bin/lib/Debug/MotionDetectorCore.lib + + C:/Users/Admin/IFloor/projects/wxVideoProcessing/bin/MotionDetectorCore.pdb + + Console + + + false + + + + + C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\mswu;C:\Users\Admin\IFloor\libs\wxWidgets\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\Utils;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\MotionDetectorCore;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxJSON\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxxmlserializer\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxxmlserializer;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\MotionDetectorPluginBase;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\CommonPluginBase;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxXS\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\MotionPrimitives;C:\Users\Admin\IFloor\projects\wxVideoProcessing\ThirdParty\wxXS\build\..\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\ThirdParty\wxJSON\build\..\include;%(AdditionalIncludeDirectories) + %(AdditionalOptions) /external:I "C:/Users/Admin/IFloor/libs/opencv/build/include" + $(IntDir) + Default + + + 4996 + Sync + TurnOffAllWarnings + + + AnySuitable + stdcpp17 + + true + MaxSpeed + Use + C:/Users/Admin/IFloor/projects/wxVideoProcessing/MotionDetectorCore/Win/CMakeFiles/MotionDetectorCore.dir/Release/cmake_pch.hxx + C:/Users/Admin/IFloor/projects/wxVideoProcessing/MotionDetectorCore/Win/MotionDetectorCore.dir/Release//cmake_pch.pch + + MultiThreadedDLL + true + + + false + %(PreprocessorDefinitions);WIN32;_WINDOWS;NDEBUG;WIN32_LEAN_AND_MEAN;WXUSINGDLL;UNICODE;_UNICODE;_CRT_SECURE_NO_DEPRECATE;_USRDLL;MOTION_DETECTOR_CORE_EXPORTS;CMAKE_INTDIR="Release";MotionDetectorCore_EXPORTS + $(IntDir) + + + false + + + %(PreprocessorDefinitions);WIN32;_WINDOWS;NDEBUG;WIN32_LEAN_AND_MEAN;WXUSINGDLL;UNICODE;_UNICODE;_CRT_SECURE_NO_DEPRECATE;_USRDLL;MOTION_DETECTOR_CORE_EXPORTS;CMAKE_INTDIR=\"Release\";MotionDetectorCore_EXPORTS + C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\mswu;C:\Users\Admin\IFloor\libs\wxWidgets\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\Utils;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\MotionDetectorCore;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxJSON\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxxmlserializer\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxxmlserializer;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\MotionDetectorPluginBase;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\CommonPluginBase;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxXS\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\MotionPrimitives;C:\Users\Admin\IFloor\projects\wxVideoProcessing\ThirdParty\wxXS\build\..\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\ThirdParty\wxJSON\build\..\include;C:\Users\Admin\IFloor\libs\opencv\build\include;%(AdditionalIncludeDirectories) + + + C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\mswu;C:\Users\Admin\IFloor\libs\wxWidgets\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\Utils;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\MotionDetectorCore;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxJSON\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxxmlserializer\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxxmlserializer;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\MotionDetectorPluginBase;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\CommonPluginBase;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxXS\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\MotionPrimitives;C:\Users\Admin\IFloor\projects\wxVideoProcessing\ThirdParty\wxXS\build\..\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\ThirdParty\wxJSON\build\..\include;C:\Users\Admin\IFloor\libs\opencv\build\include;%(AdditionalIncludeDirectories) + $(ProjectDir)/$(IntDir) + %(Filename).h + %(Filename).tlb + %(Filename)_i.c + %(Filename)_p.c + + + + setlocal +"C:\Program Files\CMake\bin\cmake.exe" -E make_directory C:/Users/Admin/IFloor/projects/wxVideoProcessing/build/../bin/plugins/motion_detector +if %errorlevel% neq 0 goto :cmEnd +"C:\Program Files\CMake\bin\cmake.exe" -E copy_if_different C:/Users/Admin/IFloor/projects/wxVideoProcessing/bin/MotionDetectorCore.dll C:/Users/Admin/IFloor/projects/wxVideoProcessing/build/../bin/plugins/motion_detector/MotionDetectorCore.dll +if %errorlevel% neq 0 goto :cmEnd +:cmEnd +endlocal & call :cmErrorLevel %errorlevel% & goto :cmDone +:cmErrorLevel +exit /b %1 +:cmDone +if %errorlevel% neq 0 goto :VCEnd + + + C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxbase33u.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33u_core.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33u_adv.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33u_aui.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxbase33u_net.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33u_gl.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxbase33u_xml.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33u_propgrid.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33u_html.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxpng.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxtiff.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxjpeg.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxzlib.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxregexu.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxexpat.lib;opengl32.lib;glu32.lib;winmm.lib;comctl32.lib;uuid.lib;oleacc.lib;uxtheme.lib;rpcrt4.lib;shlwapi.lib;version.lib;wsock32.lib;C:\Users\Admin\IFloor\projects\wxVideoProcessing\bin\lib\Release\Utils.lib;C:\Users\Admin\IFloor\projects\wxVideoProcessing\bin\lib\Release\MotionDetectorPluginBase.lib;C:\Users\Admin\IFloor\projects\wxVideoProcessing\bin\lib\Release\MotionPrimitives.lib;C:\Users\Admin\IFloor\libs\opencv\build\x64\vc16\lib\opencv_world4120.lib;C:\Users\Admin\IFloor\libs\opencv\build\x64\vc16\lib\opencv_world4120.lib;C:\Users\Admin\IFloor\libs\opencv\build\x64\vc16\lib\opencv_world4120.lib;C:\Users\Admin\IFloor\libs\opencv\build\x64\vc16\lib\opencv_world4120.lib;C:\Users\Admin\IFloor\libs\opencv\build\x64\vc16\lib\opencv_world4120.lib;C:\Users\Admin\IFloor\libs\opencv\build\x64\vc16\lib\opencv_world4120.lib;C:\Users\Admin\IFloor\libs\opencv\build\x64\vc16\lib\opencv_world4120.lib;C:\Users\Admin\IFloor\libs\opencv\build\x64\vc16\lib\opencv_world4120.lib;C:\Users\Admin\IFloor\libs\opencv\build\x64\vc16\lib\opencv_world4120.lib;C:\Users\Admin\IFloor\libs\opencv\build\x64\vc16\lib\opencv_world4120.lib;C:\Users\Admin\IFloor\libs\opencv\build\x64\vc16\lib\opencv_world4120.lib;C:\Users\Admin\IFloor\libs\opencv\build\x64\vc16\lib\opencv_world4120.lib;C:\Users\Admin\IFloor\libs\opencv\build\x64\vc16\lib\opencv_world4120.lib;C:\Users\Admin\IFloor\libs\opencv\build\x64\vc16\lib\opencv_world4120.lib;C:\Users\Admin\IFloor\libs\opencv\build\x64\vc16\lib\opencv_world4120.lib;C:\Users\Admin\IFloor\libs\opencv\build\x64\vc16\lib\opencv_world4120.lib;C:\Users\Admin\IFloor\projects\wxVideoProcessing\bin\lib\Release\CommonPluginBase.lib;C:\Users\Admin\IFloor\projects\wxVideoProcessing\bin\lib\Release\Utils.lib;C:\Users\Admin\IFloor\projects\wxVideoProcessing\bin\lib\Release\wxXS.lib;C:\Users\Admin\IFloor\projects\wxVideoProcessing\bin\lib\Release\wxJSON.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxbase33u.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33u_core.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33u_adv.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33u_aui.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxbase33u_net.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33u_gl.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxbase33u_xml.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33u_propgrid.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33u_html.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxpng.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxtiff.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxjpeg.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxzlib.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxregexu.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxexpat.lib;opengl32.lib;glu32.lib;winmm.lib;comctl32.lib;uuid.lib;oleacc.lib;uxtheme.lib;rpcrt4.lib;shlwapi.lib;version.lib;wsock32.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;comdlg32.lib;advapi32.lib + C:/Users/Admin/IFloor/projects/wxVideoProcessing/build/Win/lib;C:/Users/Admin/IFloor/projects/wxVideoProcessing/build/Win/lib/$(Configuration);%(AdditionalLibraryDirectories) + %(AdditionalOptions) /machine:x64 + + false + %(IgnoreSpecificDefaultLibraries) + + C:/Users/Admin/IFloor/projects/wxVideoProcessing/bin/lib/Release/MotionDetectorCore.lib + + C:/Users/Admin/IFloor/projects/wxVideoProcessing/bin/MotionDetectorCore.pdb + + Console + + + false + + + + + Always + Building Custom Rule C:/Users/Admin/IFloor/projects/wxVideoProcessing/MotionDetectorCore/CMakeLists.txt + setlocal +"C:\Program Files\CMake\bin\cmake.exe" -SC:/Users/Admin/IFloor/projects/wxVideoProcessing/build -BC:/Users/Admin/IFloor/projects/wxVideoProcessing/build/Win --check-stamp-file C:/Users/Admin/IFloor/projects/wxVideoProcessing/MotionDetectorCore/Win/CMakeFiles/generate.stamp +if %errorlevel% neq 0 goto :cmEnd +:cmEnd +endlocal & call :cmErrorLevel %errorlevel% & goto :cmDone +:cmErrorLevel +exit /b %1 +:cmDone +if %errorlevel% neq 0 goto :VCEnd + %(AdditionalInputs) + C:\Users\Admin\IFloor\projects\wxVideoProcessing\MotionDetectorCore\Win\CMakeFiles\generate.stamp + false + Building Custom Rule C:/Users/Admin/IFloor/projects/wxVideoProcessing/MotionDetectorCore/CMakeLists.txt + setlocal +"C:\Program Files\CMake\bin\cmake.exe" -SC:/Users/Admin/IFloor/projects/wxVideoProcessing/build -BC:/Users/Admin/IFloor/projects/wxVideoProcessing/build/Win --check-stamp-file C:/Users/Admin/IFloor/projects/wxVideoProcessing/MotionDetectorCore/Win/CMakeFiles/generate.stamp +if %errorlevel% neq 0 goto :cmEnd +:cmEnd +endlocal & call :cmErrorLevel %errorlevel% & goto :cmDone +:cmErrorLevel +exit /b %1 +:cmDone +if %errorlevel% neq 0 goto :VCEnd + %(AdditionalInputs) + C:\Users\Admin\IFloor\projects\wxVideoProcessing\MotionDetectorCore\Win\CMakeFiles\generate.stamp + false + + + + + C:/Users/Admin/IFloor/projects/wxVideoProcessing/MotionDetectorCore/Win/CMakeFiles/MotionDetectorCore.dir/Debug/cmake_pch.hxx;%(ForcedIncludeFiles) + Create + C:/Users/Admin/IFloor/projects/wxVideoProcessing/MotionDetectorCore/Win/CMakeFiles/MotionDetectorCore.dir/Debug/cmake_pch.hxx + C:/Users/Admin/IFloor/projects/wxVideoProcessing/MotionDetectorCore/Win/MotionDetectorCore.dir/Debug//cmake_pch.pch + C:/Users/Admin/IFloor/projects/wxVideoProcessing/MotionDetectorCore/Win/CMakeFiles/MotionDetectorCore.dir/Release/cmake_pch.hxx;%(ForcedIncludeFiles) + Create + C:/Users/Admin/IFloor/projects/wxVideoProcessing/MotionDetectorCore/Win/CMakeFiles/MotionDetectorCore.dir/Release/cmake_pch.hxx + C:/Users/Admin/IFloor/projects/wxVideoProcessing/MotionDetectorCore/Win/MotionDetectorCore.dir/Release//cmake_pch.pch + + + C:/Users/Admin/IFloor/projects/wxVideoProcessing/MotionDetectorCore/Win/CMakeFiles/MotionDetectorCore.dir/Debug/cmake_pch.hxx;%(ForcedIncludeFiles) + C:/Users/Admin/IFloor/projects/wxVideoProcessing/MotionDetectorCore/Win/CMakeFiles/MotionDetectorCore.dir/Release/cmake_pch.hxx;%(ForcedIncludeFiles) + + + C:/Users/Admin/IFloor/projects/wxVideoProcessing/MotionDetectorCore/Win/CMakeFiles/MotionDetectorCore.dir/Debug/cmake_pch.hxx;%(ForcedIncludeFiles) + C:/Users/Admin/IFloor/projects/wxVideoProcessing/MotionDetectorCore/Win/CMakeFiles/MotionDetectorCore.dir/Release/cmake_pch.hxx;%(ForcedIncludeFiles) + + + C:/Users/Admin/IFloor/projects/wxVideoProcessing/MotionDetectorCore/Win/CMakeFiles/MotionDetectorCore.dir/Debug/cmake_pch.hxx;%(ForcedIncludeFiles) + C:/Users/Admin/IFloor/projects/wxVideoProcessing/MotionDetectorCore/Win/CMakeFiles/MotionDetectorCore.dir/Release/cmake_pch.hxx;%(ForcedIncludeFiles) + + + C:/Users/Admin/IFloor/projects/wxVideoProcessing/MotionDetectorCore/Win/CMakeFiles/MotionDetectorCore.dir/Debug/cmake_pch.hxx;%(ForcedIncludeFiles) + C:/Users/Admin/IFloor/projects/wxVideoProcessing/MotionDetectorCore/Win/CMakeFiles/MotionDetectorCore.dir/Release/cmake_pch.hxx;%(ForcedIncludeFiles) + + + C:/Users/Admin/IFloor/projects/wxVideoProcessing/MotionDetectorCore/Win/CMakeFiles/MotionDetectorCore.dir/Debug/cmake_pch.hxx;%(ForcedIncludeFiles) + C:/Users/Admin/IFloor/projects/wxVideoProcessing/MotionDetectorCore/Win/CMakeFiles/MotionDetectorCore.dir/Release/cmake_pch.hxx;%(ForcedIncludeFiles) + + + C:/Users/Admin/IFloor/projects/wxVideoProcessing/MotionDetectorCore/Win/CMakeFiles/MotionDetectorCore.dir/Debug/cmake_pch.hxx;%(ForcedIncludeFiles) + C:/Users/Admin/IFloor/projects/wxVideoProcessing/MotionDetectorCore/Win/CMakeFiles/MotionDetectorCore.dir/Release/cmake_pch.hxx;%(ForcedIncludeFiles) + + + C:/Users/Admin/IFloor/projects/wxVideoProcessing/MotionDetectorCore/Win/CMakeFiles/MotionDetectorCore.dir/Debug/cmake_pch.hxx;%(ForcedIncludeFiles) + C:/Users/Admin/IFloor/projects/wxVideoProcessing/MotionDetectorCore/Win/CMakeFiles/MotionDetectorCore.dir/Release/cmake_pch.hxx;%(ForcedIncludeFiles) + + + C:/Users/Admin/IFloor/projects/wxVideoProcessing/MotionDetectorCore/Win/CMakeFiles/MotionDetectorCore.dir/Debug/cmake_pch.hxx;%(ForcedIncludeFiles) + C:/Users/Admin/IFloor/projects/wxVideoProcessing/MotionDetectorCore/Win/CMakeFiles/MotionDetectorCore.dir/Release/cmake_pch.hxx;%(ForcedIncludeFiles) + + + C:/Users/Admin/IFloor/projects/wxVideoProcessing/MotionDetectorCore/Win/CMakeFiles/MotionDetectorCore.dir/Debug/cmake_pch.hxx;%(ForcedIncludeFiles) + C:/Users/Admin/IFloor/projects/wxVideoProcessing/MotionDetectorCore/Win/CMakeFiles/MotionDetectorCore.dir/Release/cmake_pch.hxx;%(ForcedIncludeFiles) + + + C:/Users/Admin/IFloor/projects/wxVideoProcessing/MotionDetectorCore/Win/CMakeFiles/MotionDetectorCore.dir/Debug/cmake_pch.hxx;%(ForcedIncludeFiles) + C:/Users/Admin/IFloor/projects/wxVideoProcessing/MotionDetectorCore/Win/CMakeFiles/MotionDetectorCore.dir/Release/cmake_pch.hxx;%(ForcedIncludeFiles) + + + C:/Users/Admin/IFloor/projects/wxVideoProcessing/MotionDetectorCore/Win/CMakeFiles/MotionDetectorCore.dir/Debug/cmake_pch.hxx;%(ForcedIncludeFiles) + C:/Users/Admin/IFloor/projects/wxVideoProcessing/MotionDetectorCore/Win/CMakeFiles/MotionDetectorCore.dir/Release/cmake_pch.hxx;%(ForcedIncludeFiles) + + + C:/Users/Admin/IFloor/projects/wxVideoProcessing/MotionDetectorCore/Win/CMakeFiles/MotionDetectorCore.dir/Debug/cmake_pch.hxx;%(ForcedIncludeFiles) + C:/Users/Admin/IFloor/projects/wxVideoProcessing/MotionDetectorCore/Win/CMakeFiles/MotionDetectorCore.dir/Release/cmake_pch.hxx;%(ForcedIncludeFiles) + + + C:/Users/Admin/IFloor/projects/wxVideoProcessing/MotionDetectorCore/Win/CMakeFiles/MotionDetectorCore.dir/Debug/cmake_pch.hxx;%(ForcedIncludeFiles) + C:/Users/Admin/IFloor/projects/wxVideoProcessing/MotionDetectorCore/Win/CMakeFiles/MotionDetectorCore.dir/Release/cmake_pch.hxx;%(ForcedIncludeFiles) + + + C:/Users/Admin/IFloor/projects/wxVideoProcessing/MotionDetectorCore/Win/CMakeFiles/MotionDetectorCore.dir/Debug/cmake_pch.hxx;%(ForcedIncludeFiles) + C:/Users/Admin/IFloor/projects/wxVideoProcessing/MotionDetectorCore/Win/CMakeFiles/MotionDetectorCore.dir/Release/cmake_pch.hxx;%(ForcedIncludeFiles) + + + C:/Users/Admin/IFloor/projects/wxVideoProcessing/MotionDetectorCore/Win/CMakeFiles/MotionDetectorCore.dir/Debug/cmake_pch.hxx;%(ForcedIncludeFiles) + C:/Users/Admin/IFloor/projects/wxVideoProcessing/MotionDetectorCore/Win/CMakeFiles/MotionDetectorCore.dir/Release/cmake_pch.hxx;%(ForcedIncludeFiles) + + + C:/Users/Admin/IFloor/projects/wxVideoProcessing/MotionDetectorCore/Win/CMakeFiles/MotionDetectorCore.dir/Debug/cmake_pch.hxx;%(ForcedIncludeFiles) + C:/Users/Admin/IFloor/projects/wxVideoProcessing/MotionDetectorCore/Win/CMakeFiles/MotionDetectorCore.dir/Release/cmake_pch.hxx;%(ForcedIncludeFiles) + + + C:/Users/Admin/IFloor/projects/wxVideoProcessing/MotionDetectorCore/Win/CMakeFiles/MotionDetectorCore.dir/Debug/cmake_pch.hxx;%(ForcedIncludeFiles) + C:/Users/Admin/IFloor/projects/wxVideoProcessing/MotionDetectorCore/Win/CMakeFiles/MotionDetectorCore.dir/Release/cmake_pch.hxx;%(ForcedIncludeFiles) + + + C:/Users/Admin/IFloor/projects/wxVideoProcessing/MotionDetectorCore/Win/CMakeFiles/MotionDetectorCore.dir/Debug/cmake_pch.hxx;%(ForcedIncludeFiles) + C:/Users/Admin/IFloor/projects/wxVideoProcessing/MotionDetectorCore/Win/CMakeFiles/MotionDetectorCore.dir/Release/cmake_pch.hxx;%(ForcedIncludeFiles) + + + C:/Users/Admin/IFloor/projects/wxVideoProcessing/MotionDetectorCore/Win/CMakeFiles/MotionDetectorCore.dir/Debug/cmake_pch.hxx;%(ForcedIncludeFiles) + C:/Users/Admin/IFloor/projects/wxVideoProcessing/MotionDetectorCore/Win/CMakeFiles/MotionDetectorCore.dir/Release/cmake_pch.hxx;%(ForcedIncludeFiles) + + + C:/Users/Admin/IFloor/projects/wxVideoProcessing/MotionDetectorCore/Win/CMakeFiles/MotionDetectorCore.dir/Debug/cmake_pch.hxx;%(ForcedIncludeFiles) + C:/Users/Admin/IFloor/projects/wxVideoProcessing/MotionDetectorCore/Win/CMakeFiles/MotionDetectorCore.dir/Release/cmake_pch.hxx;%(ForcedIncludeFiles) + + + C:/Users/Admin/IFloor/projects/wxVideoProcessing/MotionDetectorCore/Win/CMakeFiles/MotionDetectorCore.dir/Debug/cmake_pch.hxx;%(ForcedIncludeFiles) + C:/Users/Admin/IFloor/projects/wxVideoProcessing/MotionDetectorCore/Win/CMakeFiles/MotionDetectorCore.dir/Release/cmake_pch.hxx;%(ForcedIncludeFiles) + + + C:/Users/Admin/IFloor/projects/wxVideoProcessing/MotionDetectorCore/Win/CMakeFiles/MotionDetectorCore.dir/Debug/cmake_pch.hxx;%(ForcedIncludeFiles) + C:/Users/Admin/IFloor/projects/wxVideoProcessing/MotionDetectorCore/Win/CMakeFiles/MotionDetectorCore.dir/Release/cmake_pch.hxx;%(ForcedIncludeFiles) + + + C:/Users/Admin/IFloor/projects/wxVideoProcessing/MotionDetectorCore/Win/CMakeFiles/MotionDetectorCore.dir/Debug/cmake_pch.hxx;%(ForcedIncludeFiles) + C:/Users/Admin/IFloor/projects/wxVideoProcessing/MotionDetectorCore/Win/CMakeFiles/MotionDetectorCore.dir/Release/cmake_pch.hxx;%(ForcedIncludeFiles) + + + C:/Users/Admin/IFloor/projects/wxVideoProcessing/MotionDetectorCore/Win/CMakeFiles/MotionDetectorCore.dir/Debug/cmake_pch.hxx;%(ForcedIncludeFiles) + C:/Users/Admin/IFloor/projects/wxVideoProcessing/MotionDetectorCore/Win/CMakeFiles/MotionDetectorCore.dir/Release/cmake_pch.hxx;%(ForcedIncludeFiles) + + + C:/Users/Admin/IFloor/projects/wxVideoProcessing/MotionDetectorCore/Win/CMakeFiles/MotionDetectorCore.dir/Debug/cmake_pch.hxx;%(ForcedIncludeFiles) + C:/Users/Admin/IFloor/projects/wxVideoProcessing/MotionDetectorCore/Win/CMakeFiles/MotionDetectorCore.dir/Release/cmake_pch.hxx;%(ForcedIncludeFiles) + + + C:/Users/Admin/IFloor/projects/wxVideoProcessing/MotionDetectorCore/Win/CMakeFiles/MotionDetectorCore.dir/Debug/cmake_pch.hxx;%(ForcedIncludeFiles) + C:/Users/Admin/IFloor/projects/wxVideoProcessing/MotionDetectorCore/Win/CMakeFiles/MotionDetectorCore.dir/Release/cmake_pch.hxx;%(ForcedIncludeFiles) + + + C:/Users/Admin/IFloor/projects/wxVideoProcessing/MotionDetectorCore/Win/CMakeFiles/MotionDetectorCore.dir/Debug/cmake_pch.hxx;%(ForcedIncludeFiles) + C:/Users/Admin/IFloor/projects/wxVideoProcessing/MotionDetectorCore/Win/CMakeFiles/MotionDetectorCore.dir/Release/cmake_pch.hxx;%(ForcedIncludeFiles) + + + C:/Users/Admin/IFloor/projects/wxVideoProcessing/MotionDetectorCore/Win/CMakeFiles/MotionDetectorCore.dir/Debug/cmake_pch.hxx;%(ForcedIncludeFiles) + C:/Users/Admin/IFloor/projects/wxVideoProcessing/MotionDetectorCore/Win/CMakeFiles/MotionDetectorCore.dir/Release/cmake_pch.hxx;%(ForcedIncludeFiles) + + + C:/Users/Admin/IFloor/projects/wxVideoProcessing/MotionDetectorCore/Win/CMakeFiles/MotionDetectorCore.dir/Debug/cmake_pch.hxx;%(ForcedIncludeFiles) + C:/Users/Admin/IFloor/projects/wxVideoProcessing/MotionDetectorCore/Win/CMakeFiles/MotionDetectorCore.dir/Release/cmake_pch.hxx;%(ForcedIncludeFiles) + + + C:/Users/Admin/IFloor/projects/wxVideoProcessing/MotionDetectorCore/Win/CMakeFiles/MotionDetectorCore.dir/Debug/cmake_pch.hxx;%(ForcedIncludeFiles) + C:/Users/Admin/IFloor/projects/wxVideoProcessing/MotionDetectorCore/Win/CMakeFiles/MotionDetectorCore.dir/Release/cmake_pch.hxx;%(ForcedIncludeFiles) + + + C:/Users/Admin/IFloor/projects/wxVideoProcessing/MotionDetectorCore/Win/CMakeFiles/MotionDetectorCore.dir/Debug/cmake_pch.hxx;%(ForcedIncludeFiles) + C:/Users/Admin/IFloor/projects/wxVideoProcessing/MotionDetectorCore/Win/CMakeFiles/MotionDetectorCore.dir/Release/cmake_pch.hxx;%(ForcedIncludeFiles) + + + C:/Users/Admin/IFloor/projects/wxVideoProcessing/MotionDetectorCore/Win/CMakeFiles/MotionDetectorCore.dir/Debug/cmake_pch.hxx;%(ForcedIncludeFiles) + C:/Users/Admin/IFloor/projects/wxVideoProcessing/MotionDetectorCore/Win/CMakeFiles/MotionDetectorCore.dir/Release/cmake_pch.hxx;%(ForcedIncludeFiles) + + + C:/Users/Admin/IFloor/projects/wxVideoProcessing/MotionDetectorCore/Win/CMakeFiles/MotionDetectorCore.dir/Debug/cmake_pch.hxx;%(ForcedIncludeFiles) + C:/Users/Admin/IFloor/projects/wxVideoProcessing/MotionDetectorCore/Win/CMakeFiles/MotionDetectorCore.dir/Release/cmake_pch.hxx;%(ForcedIncludeFiles) + + + C:/Users/Admin/IFloor/projects/wxVideoProcessing/MotionDetectorCore/Win/CMakeFiles/MotionDetectorCore.dir/Debug/cmake_pch.hxx;%(ForcedIncludeFiles) + C:/Users/Admin/IFloor/projects/wxVideoProcessing/MotionDetectorCore/Win/CMakeFiles/MotionDetectorCore.dir/Release/cmake_pch.hxx;%(ForcedIncludeFiles) + + + C:/Users/Admin/IFloor/projects/wxVideoProcessing/MotionDetectorCore/Win/CMakeFiles/MotionDetectorCore.dir/Debug/cmake_pch.hxx;%(ForcedIncludeFiles) + C:/Users/Admin/IFloor/projects/wxVideoProcessing/MotionDetectorCore/Win/CMakeFiles/MotionDetectorCore.dir/Release/cmake_pch.hxx;%(ForcedIncludeFiles) + + + C:/Users/Admin/IFloor/projects/wxVideoProcessing/MotionDetectorCore/Win/CMakeFiles/MotionDetectorCore.dir/Debug/cmake_pch.hxx;%(ForcedIncludeFiles) + C:/Users/Admin/IFloor/projects/wxVideoProcessing/MotionDetectorCore/Win/CMakeFiles/MotionDetectorCore.dir/Release/cmake_pch.hxx;%(ForcedIncludeFiles) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + C:/Users/Admin/IFloor/projects/wxVideoProcessing/MotionDetectorCore/Win/CMakeFiles/MotionDetectorCore.dir/Debug/cmake_pch.hxx;%(ForcedIncludeFiles) + C:/Users/Admin/IFloor/projects/wxVideoProcessing/MotionDetectorCore/Win/CMakeFiles/MotionDetectorCore.dir/Release/cmake_pch.hxx;%(ForcedIncludeFiles) + + + + + + + + {6F4BDD20-5B99-3029-BDF0-7D719B283AF5} + ZERO_CHECK + false + Never + + + {751A15C0-76C2-3506-AB9D-0CD3545003FA} + CommonPluginBase + + + {1F4B7EAD-4694-3453-AD34-1757E387661E} + MotionDetectorPluginBase + + + {1903244B-9312-3A09-8AAD-8D78C1A4F977} + MotionPrimitives + + + {14D82890-BB7F-3748-8713-43304E91CFA1} + Utils + + + {7887B55C-77B9-3262-AFAD-ABA738C61029} + wxJSON + + + {78CFCEBE-5466-3128-9717-547416DBFFE5} + wxXS + + + + + + \ No newline at end of file diff --git a/MotionDetectorCore/Win/MotionDetectorCore.vcxproj.filters b/MotionDetectorCore/Win/MotionDetectorCore.vcxproj.filters new file mode 100644 index 0000000..a22b346 --- /dev/null +++ b/MotionDetectorCore/Win/MotionDetectorCore.vcxproj.filters @@ -0,0 +1,273 @@ + + + + + Source Files + + + Source Files + + + Filters\Sources + + + Filters\Sources + + + Filters\Sources + + + Filters\Sources + + + Source Files + + + Filters\Sources + + + Filters\Sources + + + Filters\Sources + + + Filters\Sources + + + Source Files + + + Source Files + + + Filters\Sources + + + Filters\Sources + + + Source Files + + + Source Files + + + Filters\Sources + + + Filters\Sources + + + Filters\Sources + + + Filters\Sources + + + Filters\Sources + + + Filters\Sources + + + Filters\Sources + + + Filters\Sources + + + Filters\Sources + + + Filters\Sources + + + Filters\Sources + + + Filters\Sources + + + Filters\Sources + + + Filters\Sources + + + Filters\Sources + + + Filters\Sources + + + Filters\Sources + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Header Files + + + Header Files + + + Header Files + + + Filters\Headers + + + Filters\Headers + + + Filters\Headers + + + Filters\Headers + + + Header Files + + + Filters\Headers + + + Filters\Headers + + + Filters\Headers + + + Filters\Headers + + + Header Files + + + Header Files + + + Filters\Headers + + + Filters\Headers + + + Header Files + + + Header Files + + + Header Files + + + Filters\Headers + + + Filters\Headers + + + Filters\Headers + + + Filters\Headers + + + Filters\Headers + + + Filters\Headers + + + Filters\Headers + + + Filters\Headers + + + Filters\Headers + + + Filters\Headers + + + Filters\Headers + + + Filters\Headers + + + Filters\Headers + + + Filters\Headers + + + Filters\Headers + + + Filters\Headers + + + Filters\Headers + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Precompile Header File + + + Precompile Header File + + + + + + + + {C253D8A8-FCEA-396C-8BC7-8C747007130B} + + + {47E23639-2555-37C5-95A9-E470DDCF6088} + + + {77D62B12-3DB6-37F6-B202-947DD58AC568} + + + {4951AEE1-75F2-3C38-B593-C6BC8C809FB0} + + + {112D4227-BB51-3360-BB5B-5EA5DBA5A65F} + + + {05450AB2-CB7B-34EA-83DF-1EE86424EDF0} + + + diff --git a/MotionDetectorCore/XorBGFilter.cpp b/MotionDetectorCore/XorBGFilter.cpp new file mode 100644 index 0000000..12cc8f1 --- /dev/null +++ b/MotionDetectorCore/XorBGFilter.cpp @@ -0,0 +1,146 @@ +#include "stdwx.h" +#include "XorBGFilter.h" +//#include +#include + +#if defined(__LINUX__) +#include "LinuxUtils.h" +#endif +// LINUX_ + +IMPLEMENT_DYNAMIC_CLASS(XorBGFilter, Filter); + +XorBGFilter::XorBGFilter() +{ + m_ExposureStartTime = timeGetTime(); + m_bLearnBackground = false; + m_bStart = false; + m_CameraExposureTime = 2200; + m_bCalibrated = false; + XS_SERIALIZE(m_CameraExposureTime, wxT("CameraExposureTime")); + XS_SERIALIZE(m_bCalibrated, wxT("Calibrated")); + XS_SERIALIZE(m_filename, wxT("filename")); +} + +XorBGFilter::~XorBGFilter() +{ +} + +wxString XorBGFilter::GetName() const +{ + return _("XOR Background"); +} + +#if defined(HAVE_CUDA) +bool XorBGFilter::KernelGPU() +{ + if (!m_bStart){ + int curTime = timeGetTime(); + if ((int)(curTime - m_ExposureStartTime) > m_CameraExposureTime){ + m_bLearnBackground = true; + m_bStart = true; + } + else + return false; + } + //Capture full background + if (m_bLearnBackground) + { + m_src->copyTo(m_matBg); + m_bLearnBackground = false; + } + // Subtract background + if (m_bTrackDark){ + cv::cuda::subtract(m_matBg, *m_src, m_dst); + } + else{ + cv::cuda::subtract(*m_src, m_matBg, m_dst); + } + + return true; +} +#endif + + +bool XorBGFilter::Kernel() +{ + // derived class responsible for allocating storage for filtered image + if (source.empty()) + { + return false; + } + + if (destination.empty()) + { + destination = cv::Mat::zeros(source.size(), source.type()); + } + if (!m_bStart) + { + int curTime = timeGetTime(); + //wxLogDebug(wxT("%i, %i %i > %i"), curTime, m_ExposureStartTime, curTime - m_ExposureStartTime, m_CameraExposureTime); + if ((int)(curTime - m_ExposureStartTime) > m_CameraExposureTime) + { + m_bLearnBackground = true; + m_bStart = true; + } + else + { + return false; + } + } + //Capture full background + if (m_bLearnBackground) + { + if (m_bCalibrated && wxFile::Exists(m_filename)) + { + background = cv::imread("c:\\background.jpg", cv::IMREAD_UNCHANGED); + } + else + { + if (background.empty()) + { + background = cv::Mat::zeros(source.size(), source.type()); + } + + // Êîï³þºìî âì³ñò ÷åðåç ñó÷àñíèé C++ ìåòîä + source.copyTo(background); + } + m_bLearnBackground = false; + } + + if (background.empty()) + { + return false; + } + + cv::bitwise_xor(background, source, destination); + return true; +} + +void XorBGFilter::CreateParamInputs() +{ + AddParameterBoolInput(_("Learn background"), &m_bLearnBackground); + AddParameterSpinInput(_("Camera exposure time:"), &m_CameraExposureTime, 20000, 0); +} + +void XorBGFilter::SaveBackground(wxString filename) +{ + m_bCalibrated = !background.empty(); + if (m_bCalibrated) + { + m_filename = filename; + cv::imwrite("c:\\background.jpg", background); + } +} + +void XorBGFilter::ResetCalibration() +{ + m_bCalibrated = false; + m_bLearnBackground = true; +} + +wxFORCE_LINK_THIS_MODULE(XorBGFilter); +//cvSub(background, source, destination1); +////else +//cvSub(source, background, destination); +//cvOr(destination, destination1, destination); diff --git a/MotionDetectorCore/XorBGFilter.h b/MotionDetectorCore/XorBGFilter.h new file mode 100644 index 0000000..b89dd80 --- /dev/null +++ b/MotionDetectorCore/XorBGFilter.h @@ -0,0 +1,43 @@ +#ifndef __TOUCHLIB_FILTER_XORBG__ +#define __TOUCHLIB_FILTER_XORBG__ + +#include "FilterTemplate.h" +#include +#include + +class MOTION_DETECTOR_CORE_API XorBGFilter : public Filter +{ + DECLARE_DYNAMIC_CLASS_NO_COPY(XorBGFilter) +public: + XorBGFilter(); + virtual ~XorBGFilter(); + + bool Kernel(); +#if defined(HAVE_CUDA) + bool KernelGPU(); +#endif + virtual wxString GetName() const; + void SaveBackground(wxString filename); + void ResetCalibration(); +protected: + void CreateParamInputs(); + +private: + cv::Mat background; + cv::Mat destination1; + bool m_bLearnBackground; + int m_ExposureStartTime; + int m_CameraExposureTime; + bool m_bCalibrated; + bool m_bErodeDilate; + wxString m_filename; + cv::Mat m_erodeKernel; + cv::Mat m_dilateKernel; + bool m_bStart; +#if defined(HAVE_CUDA) + cv::cuda::GpuMat m_matBg; +#endif + +}; + +#endif // __TOUCHLIB_FILTER_XORBG__ diff --git a/MotionDetectorCore/vector2d.h b/MotionDetectorCore/vector2d.h new file mode 100644 index 0000000..2fa449a --- /dev/null +++ b/MotionDetectorCore/vector2d.h @@ -0,0 +1,253 @@ +#ifndef __TOUCHLIB_VECTOR2D__ +#define __TOUCHLIB_VECTOR2D__ + + +//#include +#include +#define GRID_X 4 +#define GRID_Y 3 + +namespace touchlib +{ + + // The following code was originally written by Nikolaus Gebhardt as part of Irrlicht. + // See www.irrlicht3d.org + + // The Irrlicht Engine License + // Copyright © 2002-2005 Nikolaus Gebhardt + // This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held + // liable for any damages arising from the use of this software. + // + // Permission is granted to anyone to use this software for any purpose, including commercial applications, and to + // alter it and redistribute it freely, subject to the following restrictions: + // + // 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. + // If you use this software in a product, an acknowledgment in the product documentation would be appreciated but + // is not required. + // 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. + // 3. This notice may not be removed or altered from any source distribution. + + + const float GRAD_PI = 180.0f / 3.14159f; + const float GRAD_PI2 = 3.14159f / 180.0f; + const float PI = 3.14159f; + const float ROUNDING_ERROR = 0.0001f; + + + + template + class vector2d + { + public: + + vector2d(): X(0), Y(0) {}; + vector2d(T nx, T ny) : X(nx), Y(ny) {}; + vector2d(const vector2d& other) :X(other.X), Y(other.Y) {}; + + // operators + + vector2d operator-() const { return vector2d(-X, -Y); } + + vector2d& operator=(const vector2d& other) { X = other.X; Y = other.Y; return *this; } + + vector2d operator+(const vector2d& other) const { return vector2d(X + other.X, Y + other.Y); } + vector2d& operator+=(const vector2d& other) { X+=other.X; Y+=other.Y; return *this; } + + vector2d operator-(const vector2d& other) const { return vector2d(X - other.X, Y - other.Y); } + vector2d& operator-=(const vector2d& other) { X-=other.X; Y-=other.Y; return *this; } + + vector2d operator*(const vector2d& other) const { return vector2d(X * other.X, Y * other.Y); } + vector2d& operator*=(const vector2d& other) { X*=other.X; Y*=other.Y; return *this; } + vector2d operator*(const T v) const { return vector2d(X * v, Y * v); } + vector2d& operator*=(const T v) { X*=v; Y*=v; return *this; } + + vector2d operator/(const vector2d& other) const { return vector2d(X / other.X, Y / other.Y); } + vector2d& operator/=(const vector2d& other) { X/=other.X; Y/=other.Y; return *this; } + vector2d operator/(const T v) const { return vector2d(X / v, Y / v); } + vector2d& operator/=(const T v) { X/=v; Y/=v; return *this; } + + bool operator==(const vector2d& other) const { return other.X==X && other.Y==Y; } + bool operator!=(const vector2d& other) const { return other.X!=X || other.Y!=Y; } + + // functions + + void set(const T& nx, const T& ny) {X=nx; Y=ny; } + void set(const vector2d& p) { X=p.X; Y=p.Y;} + + //! Returns the length of the vector + //! \return Returns the length of the vector. + float getLength() const { return sqrt(X*X + Y*Y); } + float getLengthSQ() const { return (X*X + Y*Y); } + + //! Returns the dot product of this vector with an other. + T dotProduct(const vector2d& other) const + { + return X*other.X + Y*other.Y; + } + + //! Calculates the cross product with another vector + T crossProduct(const vector2d& p) const + { + return X * p.Y - Y * p.X; + } + + + //! Returns distance from an other point. Here, the vector is interpreted as + //! point in 2 dimensional space. + float getDistanceFrom(const vector2d& other) const + { + float vx = X - other.X; float vy = Y - other.Y; + return sqrt(vx*vx + vy*vy); + } + + //! Returns distance from an other point. Here, the vector is interpreted as + //! point in 2 dimensional space. + float getDistanceFromSQ(const vector2d& other) const + { + float vx = X - other.X; + float vy = Y - other.Y; + + return (vx*vx + vy*vy); + } + + //! rotates the point around a center by an amount of degrees. + void rotateBy(float degrees, const vector2d& center) + { + degrees *= GRAD_PI2; + T cs = (T)cos(degrees); + T sn = (T)sin(degrees); + + X -= center.X; + Y -= center.Y; + + set(X*cs - Y*sn, X*sn + Y*cs); + + X += center.X; + Y += center.Y; + } + + //! normalizes the vector. + vector2d& normalize() + { + T l = (T)getLength(); + if (l == 0) + return *this; + + l = (T)1.0 / l; + X *= l; + Y *= l; + return *this; + } + + //! Calculates the angle of this vector in grad in the trigonometric sense. + //! This method has been suggested by Pr3t3nd3r. + //! \return Returns a value between 0 and 360. + inline float getAngleTrig() const + { + if (X == 0.0) + return Y < 0.0 ? 270.0 : 90.0; + else + if (Y == 0) + return X < 0.0 ? 180.0 : 0.0; + + if ( Y > 0.0) + if (X > 0.0) + return atan(Y/X) * GRAD_PI; + else + return 180.0-atan(Y/-X) * GRAD_PI; + else + if (X > 0.0) + return 360.0-atan(-Y/X) * GRAD_PI; + else + return 180.0+atan(-Y/-X) * GRAD_PI; + } + + //! Calculates the angle of this vector in grad in the counter trigonometric sense. + //! \return Returns a value between 0 and 360. + inline float getAngle() const + { + if (Y == 0.0) // corrected thanks to a suggestion by Jox + return X < 0.0 ? 180.0 : 0.0; + else if (X == 0.0) + return Y < 0.0 ? 90.0 : 270.0; + + float tmp = Y / sqrt(X*X + Y*Y); + tmp = atan(sqrt(1 - tmp*tmp) / tmp) * GRAD_PI; + + if (X>0.0 && Y>0.0) + return tmp + 270; + else + if (X>0.0 && Y<0.0) + return tmp + 90; + else + if (X<0.0 && Y<0.0) + return 90 - tmp; + else + if (X<0.0 && Y>0.0) + return 270 - tmp; + + return tmp; + } + + //! Calculates the angle between this vector and another one in grad. + //! \return Returns a value between 0 and 90. + inline float getAngleWith(const vector2d& b) const + { + float tmp = X*b.X + Y*b.Y; + + if (tmp == 0.0) + return 90.0; + + tmp = tmp / sqrt((X*X + Y*Y) * (b.X*b.X + b.Y*b.Y)); + if (tmp < 0.0) tmp = -tmp; + + return atan(sqrt(1 - tmp*tmp) / tmp) * GRAD_PI; + } + + + //! returns interpolated vector + //! \param other: other vector to interpolate between + //! \param d: value between 0.0f and 1.0f. + vector2d getInterpolated(const vector2d& other, float d) const + { + float inv = 1.0f - d; + return vector2d(other.X*inv + X*d, + other.Y*inv + Y*d); + } + + //! Returns if this vector interpreted as a point is on a line between two other points. + /** It is assumed that the point is on the line. */ + bool isBetweenPoints(const vector2d& begin, const vector2d& end) const + { + float f = (float)(end - begin).getLengthSQ(); + return (float)getDistanceFromSQ(begin) < f && + (float)getDistanceFromSQ(end) < f; + } + + static bool isOnSameSide(vector2d p1, vector2d p2, vector2d a, vector2d b) + { + vector2d ba = b - a; + + float cp1 = ba.crossProduct(p1-a); + float cp2 = ba.crossProduct(p2-a); + + if (cp1*cp2 >= 0.0f) + return true; + else + return false; + } + + + // member variables + T X, Y; + }; + + //! Typedef for float 2d vector. + typedef vector2d vector2df; + //! Typedef for integer 2d vector. + typedef vector2d vector2di; + +} // end namespace touchscreen + + +#endif diff --git a/MotionDetectorCore/wxBitmapEvent.h b/MotionDetectorCore/wxBitmapEvent.h new file mode 100644 index 0000000..e13e911 --- /dev/null +++ b/MotionDetectorCore/wxBitmapEvent.h @@ -0,0 +1,52 @@ +#ifndef _WXBITMAPEVENT_H +#define _WXBITMAPEVENT_H + +class wxBitmapEvent: public wxCommandEvent +{ +public: + wxBitmapEvent(wxBitmap * bitmap = NULL, wxEventType commandType = wxEVT_NULL, int winid = 0) + : wxCommandEvent(commandType, winid) + , m_Bitmap(bitmap) + { + } + + wxBitmapEvent(const wxBitmapEvent & event) + : wxCommandEvent(event) + { + if (event.m_Bitmap) + m_Bitmap = new wxBitmap(*event.m_Bitmap); + else + m_Bitmap = NULL; + } + + ~wxBitmapEvent() + { + wxDELETE(m_Bitmap); + } + + void SetBitmap(wxBitmap * val) { m_Bitmap = val; } + wxBitmap * GetBitmap() const { return m_Bitmap; } + wxBitmap * DetachBitmap() + { + wxBitmap * tmp = m_Bitmap; + m_Bitmap = NULL; + return tmp; + } + + // implement the base class pure virtual + virtual wxEvent *Clone() const { return new wxBitmapEvent(*this); } + +private: + wxBitmap * m_Bitmap; +}; + + +wxDEFINE_EVENT(EVT_IMAGE_CALLBACK, wxBitmapEvent); + +#define wxBitmapEventHandler(func) (&func) + +#define EVT_BITMAP_SEND(id, func) \ + wx__DECLARE_EVT1(EVT_IMAGE_CALLBACK, id, wxBitmapEventHandler(func)) + + +#endif // _WXBITMAPEVENT_H \ No newline at end of file diff --git a/MotionDetectorMultiCam/CMakeLists.txt b/MotionDetectorMultiCam/CMakeLists.txt new file mode 100644 index 0000000..9da232c --- /dev/null +++ b/MotionDetectorMultiCam/CMakeLists.txt @@ -0,0 +1,69 @@ +cmake_minimum_required(VERSION 3.15) +project(MotionDetectorMultiCam) + +set(INCLUDE_DIRECTORIES ${BASE_INCLUDE_DIRECTORIES} + ${CMAKE_CURRENT_SOURCE_DIR} + ${PROJECT_ROOT_DIR}/MotionDetectorCore + ${PROJECT_ROOT_DIR}/MotionDetectorPluginBase + ${PROJECT_ROOT_DIR}/wxGuiPluginBase + ${PROJECT_ROOT_DIR}/CommonPluginBase + ${PROJECT_ROOT_DIR}/Utils + ${THIRD_PARTY_DIR}/wxXS/include + ${OpenCV_INCLUDE_DIRS} + ${THIRD_PARTY_DIR}/MotionPrimitives +) + +if(CUDA_FOUND) + set(PREPROCESSOR_DEFINITIONS ${PREPROCESSOR_DEFINITIONS};/DHAVE_CUDA) +endif(CUDA_FOUND) + +if(WIN32) + set(INCLUDE_DIRECTORIES ${INCLUDE_DIRECTORIES} ${QEDIT_PATH}) + set(PREPROCESSOR_DEFINITIONS ${PREPROCESSOR_DEFINITIONS};/D_USRDLL;/D_WIN32_WINNT=0x0501) +endif(WIN32) + +add_definitions(${PREPROCESSOR_DEFINITIONS}) +include_directories(${INCLUDE_DIRECTORIES}) + +set(STD_WX ${PROJECT_ROOT_DIR}/include/stdwx.h ${PROJECT_ROOT_DIR}/include/stdwx.cpp) + +set(NONGUI_NAME MotionDetectorMultiCam) +set(NONGUI_SRCS + MotionDetectorMultiCamPlugin.cpp + MotionDetectorMultiCamExports.cpp + MotionDetectorMultiCam.cpp + MultiCamCameraManager.cpp + ${STD_WX} +) + +add_library(${NONGUI_NAME} SHARED ${NONGUI_SRCS}) +target_compile_definitions(${NONGUI_NAME} PRIVATE -DMOTIONDETECTOR_MULTICAM_EXPORTS) + +target_link_libraries(${NONGUI_NAME} ${wxWidgets_LIBRARIES} CommonPluginBase Utils MotionDetectorPluginBase MotionDetectorCore MotionPrimitives videoInput ${OpenCV_LIBS}) +add_dependencies(${NONGUI_NAME} MotionDetectorPluginBase CommonPluginBase Utils MotionDetectorCore) +target_precompile_headers(${NONGUI_NAME} PRIVATE "${PROJECT_ROOT_DIR}/include/stdwx.h") + +set(TARGET_NONGUI_DIR "${OUTPUT_BIN_DIR}") +add_custom_command(TARGET ${NONGUI_NAME} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E make_directory "${TARGET_NONGUI_DIR}" + COMMAND ${CMAKE_COMMAND} -E copy_if_different "$" "${TARGET_NONGUI_DIR}/$" +) + +set(GUI_NAME MotionDetectorMultiCamGui) +set(GUI_SRCS + MotionDetectorMultiCamGuiPlugin.cpp + MultiCamSettingsPanel.cpp + ${STD_WX} +) + +add_library(${GUI_NAME} SHARED ${GUI_SRCS}) + +target_link_libraries(${GUI_NAME} ${wxWidgets_LIBRARIES} ${NONGUI_NAME} CommonPluginBase Utils wxGuiPluginBase MotionDetectorCore) +add_dependencies(${GUI_NAME} ${NONGUI_NAME} wxGuiPluginBase CommonPluginBase Utils MotionDetectorCore) +target_precompile_headers(${GUI_NAME} PRIVATE "${PROJECT_ROOT_DIR}/include/stdwx.h") + +set(TARGET_GUI_DIR "${OUTPUT_BIN_DIR}/plugins/gui") +add_custom_command(TARGET ${GUI_NAME} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E make_directory "${TARGET_GUI_DIR}" + COMMAND ${CMAKE_COMMAND} -E copy_if_different "$" "${TARGET_GUI_DIR}/$" +) \ No newline at end of file diff --git a/MotionDetectorMultiCam/MotionDetectorMultiCam.cpp b/MotionDetectorMultiCam/MotionDetectorMultiCam.cpp new file mode 100644 index 0000000..3863f9d --- /dev/null +++ b/MotionDetectorMultiCam/MotionDetectorMultiCam.cpp @@ -0,0 +1,261 @@ +#include "stdwx.h" +#include "MotionDetectorMultiCam.h" +#include "MultiCamSettingsPanel.h" +#include "MultiCamCameraManager.h" +#include "ContourFinder.h" +#include "Tracking.h" + +#include +#include "PerspectiveFilter.h" +#include "FlipFilter.h" + +#if defined(USE_VLD) +#include +#endif + +const wxString MotionDetectorMultiCam::DEFAULT_FILTERS[] = +{ + wxT("DummyFilter"), + //wxT("EqualizeHistFilter"), + wxT("BrightnessContrastFilter"), + wxT("MonoFilter"), + wxT("FlipFilter"), + wxT("SimpleBGFilter"), + wxT("DynamicBGFilter"), + wxT("SubOrBGFilter"), + wxT("XorBGFilter"), + wxT("PerspectiveFilter"), + wxT("SmoothingFilter"), + wxT("SimpleHighpassFilter"), + wxT("AmplifyFilter"), + wxT("InvertFilter"), + wxT("ThresholdFilter"), +}; + +IMPLEMENT_DYNAMIC_CLASS(MotionDetectorMultiCam, MotionDetectorBase); + +MotionDetectorMultiCam::MotionDetectorMultiCam() +: MotionDetectorBase(nullptr, GeometryProvider(nullptr, nullptr, nullptr)) +, m_CameraManager(NULL) +, m_ContourFinder(NULL) +, m_BlobTracker(NULL) +, m_minBlobArea(100), m_maxBlobArea(1000000), m_maxBlobCount(5), m_bUseOneOnMulti(false) +{ + wxLogDebug(wxT("Size of MotionDetectorMultiCam : %i"), (int)sizeof(MotionDetectorMultiCam)); + m_Settings.DeleteContents(true); + //m_hash = 8180; + //m_hash = 18540; //cpu1 + //m_hash = 23132; //cpu2 +} + +MotionDetectorMultiCam::MotionDetectorMultiCam(MotionDetectorPluginBase * owner, + GeometryProvider && geometryProvider) +: MotionDetectorBase(owner, std::move(geometryProvider)) +, m_CameraManager(NULL) +, m_ContourFinder(NULL) +, m_BlobTracker(NULL) +, m_minBlobArea(100), m_maxBlobArea(1000000), m_maxBlobCount(5), m_bUseOneOnMulti(false) +{ + wxLogDebug(wxT("Size of MotionDetectorMultiCam : %i"), (int)sizeof(MotionDetectorMultiCam)); + wxLogDebug(wxT("Size of IFLoorImageProcessorSettings : %i"), (int)sizeof(IFloorImagePreprocessorSettings)); + XS_SERIALIZE(m_minBlobArea, wxT("minBlobArea")); + XS_SERIALIZE(m_maxBlobArea, wxT("maxBlobArea")); + XS_SERIALIZE(m_maxBlobCount, wxT("maxBlobCount")); + XS_SERIALIZE(m_bUseOneOnMulti, wxT("useOneOnMulti")); + m_Settings.DeleteContents(true); + //m_hash = 8180; + //m_hash = 18540; //cpu1 + //m_hash = 23132; //cpu2 +} + +MotionDetectorMultiCam::~MotionDetectorMultiCam() +{ + Clear(); + m_Settings.Clear(); + m_Buffers.Clear(); + m_BufferSizes.Clear(); + m_CameraSettings.Clear(); + //StopDetection(); +} + +wxString MotionDetectorMultiCam::GetName() const +{ + static wxString id = wxT("MultiCam detector"); + return id; +} + +wxString MotionDetectorMultiCam::GetID() const +{ + static wxString id = wxT("{BFED0460-5C8D-4114-861B-96DE3FE9C607}"); + return id; +} + +void MotionDetectorMultiCam::StartDetection() +{ + Clear(); + + if (m_Settings.IsEmpty()) // wxT("There is no any camera settings") + return; + + m_CameraManager = new MultiCamCameraManager(m_GeometryProvider); + + m_ContourFinder = new ContourFinder(); + m_BlobTracker = new BlobTracker(); + + size_t index = 0; + + m_CameraManager->SetUseOneOnMulti(m_bUseOneOnMulti); + + for (IFloorCameraSettingsList::iterator it = m_Settings.begin(); it != m_Settings.end(); ++it, ++index) + { + m_CameraManager->AddCamera(*it, m_Buffers[index], m_BufferSizes[index]); + // Add filters for the camera + bool hasConfig = index < m_CameraSettings.Count(); + for (size_t i = 0; i < WXSIZEOF(DEFAULT_FILTERS); ++i) + m_CameraManager->AddFilter(index, DEFAULT_FILTERS[i], hasConfig ? m_CameraSettings[index][DEFAULT_FILTERS[i]] : wxString(wxEmptyString)); + + IFloorImagePreprocessor * camera = m_CameraManager->GetCameraByID(index); + + PerspectiveFilter * pf = wxDynamicCast(camera->GetFilter(wxT("PerspectiveFilter")), PerspectiveFilter); + if (pf) + pf->SetSettings(**it); + + FlipFilter * ff = wxDynamicCast(camera->GetFilter(wxT("FlipFilter")), FlipFilter); + if (ff) + ff->SetSettings(**it); + } + m_CameraManager->InitCameraParams(); + m_CameraManager->CreateFullView(); +} + +void MotionDetectorMultiCam::StopDetection() +{ + Clear(); + m_Settings.Clear(); + m_Buffers.Clear(); + m_BufferSizes.Clear(); +} + +IFloorImagePreprocessor * MotionDetectorMultiCam::GetCameraImagePreprocessor(int cameraID) +{ + if (!m_CameraManager) + return NULL; + return m_CameraManager->GetCameraByID(cameraID); +} + +int MotionDetectorMultiCam::GetBlobs(iFloorBlobVector * blobs) +{ + if (m_ContourFinder) + { + if (m_BlobTracker){ + *blobs = m_BlobTracker->getTrackedBlobs(); + }else{ + *blobs = m_ContourFinder->blobs; + } + for(size_t i = 0; i < blobs->size(); ++i){ + iFloorBlob& blob = (*blobs)[i]; + //blob.centroid.x *= m_hash; + //blob.centroid.y /= m_hash; + } + } + return 0; +} + +bool MotionDetectorMultiCam::Calibrate() +{ + return true; +} + +CommonConfigWindowBase * MotionDetectorMultiCam::CreateSettingsEditor(wxWindow * parent) +{ + return NULL; +} + +void MotionDetectorMultiCam::SetCameraSettings(const IFloorCameraSettings & settings, const int index) +{ + wxASSERT(index >= 0); + IFloorCameraSettings * newSettings = new IFloorCameraSettings(settings); + while (index > (int)m_Settings.GetCount()) + { + m_Settings.Append(new IFloorCameraSettings); // Add dummy settings to the list + } + if (index == m_Settings.GetCount()) + m_Settings.Append(newSettings); + else + m_Settings.Item(index)->SetData(newSettings); + + m_Buffers.SetCount(m_Settings.GetCount()); + m_BufferSizes.SetCount(m_Settings.GetCount()); +} + +void MotionDetectorMultiCam::SetDataBuffer(unsigned char * buffer, const size_t length, const int index) +{ + wxCHECK(index < (int)m_Buffers.GetCount(), ); + m_Buffers[index] = buffer; + m_BufferSizes[index] = length; +} + +void MotionDetectorMultiCam::ProcessData() +{ + wxCHECK_MSG(m_CameraManager && m_ContourFinder && m_BlobTracker, , wxT("You should call StartDetection() first")); + + cv::Mat image = m_CameraManager->GetImage(); + if (image.empty()) + return; + + m_ContourFinder->findContours(image, m_minBlobArea, m_maxBlobArea, m_maxBlobCount, false); + if (m_BlobTracker){ + m_BlobTracker->track(m_ContourFinder); + } +} + +bool MotionDetectorMultiCam::Deserialize(wxInputStream & instream) +{ + bool res = SerializableBase::Deserialize(instream, *this); + if (res) + { + m_CameraSettings.Clear(); + xsSerializable * child; + while (child = xsSerializable::GetFirstChild(CLASSINFO(IFloorImagePreprocessorSettings))) + { + child->Reparent(NULL); + child->SetParentManager(NULL); + m_CameraSettings.Add(wxDynamicCast(child, IFloorImagePreprocessorSettings)); + } + } + return res; +} + +bool MotionDetectorMultiCam::Serialize(wxOutputStream & outstream) +{ + for (size_t i = 0; i < m_CameraSettings.Count(); ++i) + { + xsSerializable::AddChild(&m_CameraSettings[i]); + } + bool res = SerializableBase::Serialize(outstream, *this); + + xsSerializable * child; + while ((child = xsSerializable::GetFirstChild(CLASSINFO(IFloorImagePreprocessorSettings)))) + { + child->Reparent(NULL); + child->SetParentManager(NULL); + } + return res; +} + +void MotionDetectorMultiCam::Clear() +{ + wxDELETE(m_BlobTracker); + wxDELETE(m_ContourFinder); + wxDELETE(m_CameraManager); +} + +IFloorImagePreprocessorSettingsArray & MotionDetectorMultiCam::GetCameraSettings() +{ + return m_CameraSettings; +} + +bool MotionDetectorMultiCam::IsRunning() +{ + return m_CameraManager != NULL; +} diff --git a/MotionDetectorMultiCam/MotionDetectorMultiCam.def b/MotionDetectorMultiCam/MotionDetectorMultiCam.def new file mode 100644 index 0000000..e0810f9 --- /dev/null +++ b/MotionDetectorMultiCam/MotionDetectorMultiCam.def @@ -0,0 +1,6 @@ +LIBRARY "MotionDetectorMultiCam" + +EXPORTS + CreatePlugin=CreatePlugin + DeletePlugin=DeletePlugin + \ No newline at end of file diff --git a/MotionDetectorMultiCam/MotionDetectorMultiCam.h b/MotionDetectorMultiCam/MotionDetectorMultiCam.h new file mode 100644 index 0000000..7ed8278 --- /dev/null +++ b/MotionDetectorMultiCam/MotionDetectorMultiCam.h @@ -0,0 +1,60 @@ +#ifndef _MOTIONDETECTORMULTICAM_H +#define _MOTIONDETECTORMULTICAM_H + +#include +#include +#include +#include + +class MultiCamCameraManager; +class ContourFinder; +class BlobTracker; +class IFloorImagePreprocessor; + +class __declspec(dllexport) MotionDetectorMultiCam : public MotionDetectorBase +{ + DECLARE_DYNAMIC_CLASS(MotionDetectorMultiCam); +public: + MotionDetectorMultiCam(); + MotionDetectorMultiCam(MotionDetectorPluginBase * owner, GeometryProvider && geometryProvider); + virtual ~MotionDetectorMultiCam(); + virtual wxString GetName() const; + virtual wxString GetID() const; + virtual bool Calibrate(); + virtual void StartDetection(); + virtual void StopDetection(); + virtual int GetBlobs(iFloorBlobVector * blobs); + virtual CommonConfigWindowBase * CreateSettingsEditor(wxWindow * parent = NULL); + virtual void SetCameraSettings(const IFloorCameraSettings & settings, const int index);; + virtual void SetDataBuffer(unsigned char * buffer, const size_t length, const int index); + virtual void ProcessData(); + IFloorImagePreprocessor * GetCameraImagePreprocessor(int cameraID); + + virtual bool Deserialize(wxInputStream & instream); + virtual bool Serialize(wxOutputStream & outstream); + + IFloorImagePreprocessorSettingsArray & GetCameraSettings(); + bool IsRunning(); + int m_minBlobArea, m_maxBlobArea, m_maxBlobCount; + bool m_bUseOneOnMulti; +protected: + void Clear(); + +private: + MultiCamCameraManager * m_CameraManager; + ContourFinder * m_ContourFinder; + BlobTracker * m_BlobTracker; + + // Settings that will be passed to the Camera Manager + IFloorCameraSettingsList m_Settings; + wxArrayPtrVoid m_Buffers; + wxArrayInt m_BufferSizes; + + IFloorImagePreprocessorSettingsArray m_CameraSettings; + + static const wxString DEFAULT_FILTERS[]; + + float m_hash; +}; + +#endif // _MOTIONDETECTORMULTICAM_H \ No newline at end of file diff --git a/SampleGuiPlugin1/SampleGuiPlugin1.pjd b/MotionDetectorMultiCam/MotionDetectorMultiCam.pjd similarity index 58% rename from SampleGuiPlugin1/SampleGuiPlugin1.pjd rename to MotionDetectorMultiCam/MotionDetectorMultiCam.pjd index 7f15f2c..21638ec 100644 --- a/SampleGuiPlugin1/SampleGuiPlugin1.pjd +++ b/MotionDetectorMultiCam/MotionDetectorMultiCam.pjd @@ -1,572 +1,606 @@ - - -
- 0 - "" - "" - "" - "" - "" - 0 - 0 - 0 - 1 - 1 - 1 - 1 - 0 - "Volodymyr (T-Rex) Triapichko" - "Volodymyr (T-Rex) Triapichko, 2013" - "" - 0 - 0 - 0 - 0 - "<All platforms>" - "2.9.5" - "Standard" - "///////////////////////////////////////////////////////////////////////////// -// Name: %HEADER-FILENAME% -// Purpose: -// Author: %AUTHOR% -// Modified by: -// Created: %DATE% -// RCS-ID: -// Copyright: %COPYRIGHT% -// Licence: -///////////////////////////////////////////////////////////////////////////// - -" - "///////////////////////////////////////////////////////////////////////////// -// Name: %SOURCE-FILENAME% -// Purpose: -// Author: %AUTHOR% -// Modified by: -// Created: %DATE% -// RCS-ID: -// Copyright: %COPYRIGHT% -// Licence: -///////////////////////////////////////////////////////////////////////////// - -" - "///////////////////////////////////////////////////////////////////////////// -// Name: %SYMBOLS-FILENAME% -// Purpose: Symbols file -// Author: %AUTHOR% -// Modified by: -// Created: %DATE% -// RCS-ID: -// Copyright: %COPYRIGHT% -// Licence: -///////////////////////////////////////////////////////////////////////////// - -" - "" - "// For compilers that support precompilation, includes "wx/wx.h". -#include "wx/wxprec.h" - -#ifdef __BORLANDC__ -#pragma hdrstop -#endif - -#ifndef WX_PRECOMP -#include "wx/wx.h" -#endif - -" - " /// %BODY% -" - " -/* - * %BODY% - */ - -" - "app_resources.h" - "app_resources.cpp" - "AppResources" - "app.h" - "app.cpp" - "Application" - 0 - "" - "<None>" - "iso-8859-1" - "utf-8" - "utf-8" - "" - 0 - 0 - 4 - " " - "" - 0 - 0 - 1 - 0 - 1 - 1 - 0 - 1 - 0 - 0 -
- - - "" - "data-document" - "" - "" - 0 - 1 - 0 - 0 - - "Configurations" - "config-data-document" - "" - "" - 0 - 1 - 0 - 0 - "" - 1 - -8519680 - "" - "Debug" - "Unicode" - "Static" - "Modular" - "GUI" - "wxMSW" - "Default" - "Dynamic" - "Yes" - "No" - "Yes" - "No" - "No" - "Yes" - "Yes" - "Yes" - "Yes" - "Yes" - "builtin" - "Yes" - "%EXECUTABLE%" - "" - "%AUTO%" - "%AUTO%" - "%AUTO%" - "%AUTO%" - "%WXVERSION%" - "%AUTO%" - "%AUTO%" - "%AUTO%" - "%AUTO%" - "%AUTO%" - "" - "%AUTO%" - "%AUTO%" - "%AUTO%" - "%AUTO%" - "%AUTO%" - "%AUTO%" - "%AUTO%" - "%AUTO%" - "%AUTO%" - "%AUTO%" - "%AUTO%" - "%AUTO%" - "%AUTO%" - "%AUTO%" - "%AUTO%" - "%AUTO%" - "%AUTO%" - "%AUTO%" - "%AUTO%" - "%AUTO%" - "%AUTO%" - "%AUTO%" - 0 - 1 - - - - - - - "Projects" - "root-document" - "" - "project" - 1 - 1 - 0 - 1 - - "Windows" - "html-document" - "" - "dialogsfolder" - 1 - 1 - 0 - 1 - - "SampleGuiPluginWindow1" - "dialog-document" - "" - "dialog" - 0 - 1 - 0 - 0 - "wbDialogProxy" - 10000 - 0 - "" - 0 - "" - "Standard" - 0 - 0 - "ID_SAMPLEGUIPLUGINWINDOW1" - 10000 - "SampleGuiPluginWindow1" - "wxGuiPluginWindowBase" - "wxPanel" - "SampleGuiPluginWindow1.cpp" - "SampleGuiPluginWindow1.h" - "" - "SampleGuiPluginWindow1" - 1 - "" - 0 - "" - "" - "" - "" - "" - 0 - 1 - "<Any platform>" - "" - "" - "" - "" - "" - "" - "Tiled" - 0 - 0 - 0 - 0 - 0 - 0 - 0 - 0 - 0 - 1 - 0 - 0 - 0 - 0 - 0 - 0 - 0 - 0 - 0 - 0 - 0 - 1 - 0 - 0 - 0 - "" - 0 - 1 - -1 - -1 - 400 - 300 - 0 - "" - - "wxBoxSizer V" - "dialog-control-document" - "" - "sizer" - 0 - 1 - 0 - 0 - "wbBoxSizerProxy" - "Vertical" - "" - 0 - 0 - 0 - 0 - "<Any platform>" - - "wxStaticText: wxID_STATIC" - "dialog-control-document" - "" - "statictext" - 0 - 1 - 0 - 0 - "wbStaticTextProxy" - "wxID_STATIC" - 5105 - "" - "wxStaticText" - "wxStaticText" - 1 - 0 - "" - "" - "" - "Enter some text here:" - -1 - "" - "" - "" - "" - "" - 0 - 1 - "<Any platform>" - "" - "" - "" - "" - "" - "" - "" - 0 - 0 - 0 - 0 - 0 - 0 - 0 - 0 - 0 - 0 - 0 - 0 - 0 - 0 - "" - -1 - -1 - -1 - -1 - "Left" - "Centre" - 0 - 5 - 1 - 1 - 1 - 0 - 0 - 0 - 0 - 0 - "" - "" - - - "wxTextCtrl: ID_SAMPLE_TEXTCTRL" - "dialog-control-document" - "" - "textctrl" - 0 - 1 - 0 - 0 - "wbTextCtrlProxy" - "ID_SAMPLE_TEXTCTRL" - 10001 - "" - "wxTextCtrl" - "wxTextCtrl" - 1 - 0 - "" - "" - "m_SamppleTextCtrl" - "Hello, GUI Plugin 2!" - 0 - "" - "" - "" - "" - "" - 0 - 1 - "<Any platform>" - "" - "" - "" - "" - "" - "" - "" - 0 - 0 - 0 - 0 - 0 - 0 - 0 - 0 - 0 - 0 - 0 - 0 - 0 - 0 - 0 - 0 - 0 - 0 - 0 - 0 - 0 - 0 - 0 - 0 - 0 - 0 - "" - -1 - -1 - -1 - -1 - "Expand" - "Centre" - 0 - 5 - 1 - 1 - 1 - 0 - 0 - 0 - 0 - 0 - "" - "" - - - "wxButton: ID_SEND_EVENT_BUTTON" - "dialog-control-document" - "" - "dialogcontrol" - 0 - 1 - 0 - 0 - "wbButtonProxy" - "wxEVT_COMMAND_BUTTON_CLICKED|OnSENDEVENTBUTTONClick|NONE||SampleGuiPluginWindow1" - "ID_SEND_EVENT_BUTTON" - 10002 - "" - "wxButton" - "wxButton" - 1 - 0 - "" - "" - "" - "Send event" - 0 - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - 0 - 1 - "<Any platform>" - 0 - 0 - 0 - 0 - 0 - 0 - 0 - 0 - 0 - "" - -1 - -1 - -1 - -1 - "Centre" - "Centre" - 0 - 5 - 1 - 1 - 1 - 1 - 0 - 0 - 0 - 0 - "" - "" - - - - - - "Sources" - "html-document" - "" - "sourcesfolder" - 1 - 1 - 0 - 1 - - "SampleGuiPlugin1.rc" - "source-editor-document" - "SampleGuiPlugin1.rc" - "source-editor" - 0 - 0 - 1 - 0 - "10/9/2013" - "" - - - - "Images" - "html-document" - "" - "bitmapsfolder" - 1 - 1 - 0 - 1 - - - - -
+ + +
+ 0 + "" + "" + "" + "" + "" + 1 + 0 + 0 + 1 + 1 + 1 + 1 + 0 + "IT-Dimension" + "" + "" + 0 + 1 + 0 + 0 + "<All platforms>" + "2.9.1" + "Standard" + "///////////////////////////////////////////////////////////////////////////// +// Name: %HEADER-FILENAME% +// Purpose: +// Author: %AUTHOR% +// Modified by: +// Created: %DATE% +// RCS-ID: +// Copyright: %COPYRIGHT% +// Licence: +///////////////////////////////////////////////////////////////////////////// + +" + "///////////////////////////////////////////////////////////////////////////// +// Name: %SOURCE-FILENAME% +// Purpose: +// Author: %AUTHOR% +// Modified by: +// Created: %DATE% +// RCS-ID: +// Copyright: %COPYRIGHT% +// Licence: +///////////////////////////////////////////////////////////////////////////// + +" + "///////////////////////////////////////////////////////////////////////////// +// Name: %SYMBOLS-FILENAME% +// Purpose: Symbols file +// Author: %AUTHOR% +// Modified by: +// Created: %DATE% +// RCS-ID: +// Copyright: %COPYRIGHT% +// Licence: +///////////////////////////////////////////////////////////////////////////// + +" + "" + "// For compilers that support precompilation, includes "wx/wx.h". +#include "wx/wxprec.h" + +#ifdef __BORLANDC__ +#pragma hdrstop +#endif + +#ifndef WX_PRECOMP +#include "wx/wx.h" +#endif + +" + " /// %BODY% +" + " +/* + * %BODY% + */ + +" + "app_resources.h" + "app_resources.cpp" + "AppResources" + "app.h" + "app.cpp" + "Application" + 0 + "" + "<None>" + "iso-8859-1" + "utf-8" + "utf-8" + "" + 0 + 1 + 4 + " " + "" + 0 + 0 + 1 + 0 + 1 + 1 + 0 + 1 + 0 + 0 +
+ + + "" + "data-document" + "" + "" + 0 + 1 + 0 + 0 + + "Configurations" + "config-data-document" + "" + "" + 0 + 1 + 0 + 0 + "" + 1 + -8519680 + "" + "Debug" + "Unicode" + "Static" + "Modular" + "GUI" + "wxMSW" + "Default" + "Dynamic" + "Yes" + "No" + "Yes" + "No" + "No" + "Yes" + "Yes" + "Yes" + "Yes" + "Yes" + "sys" + "Yes" + "%EXECUTABLE%" + "" + "%AUTO%" + "%AUTO%" + "%AUTO%" + "%AUTO%" + "%WXVERSION%" + "%AUTO%" + "%AUTO%" + "%AUTO%" + "%AUTO%" + "%AUTO%" + "" + "%AUTO%" + "%AUTO%" + "%AUTO%" + "%AUTO%" + "%AUTO%" + "%AUTO%" + "%AUTO%" + "%AUTO%" + "%AUTO%" + "%AUTO%" + "%AUTO%" + "%AUTO%" + "%AUTO%" + "%AUTO%" + "%AUTO%" + "%AUTO%" + "%AUTO%" + "%AUTO%" + "%AUTO%" + "%AUTO%" + "%AUTO%" + "%AUTO%" + 0 + 1 + + + + + + + "Projects" + "root-document" + "" + "project" + 1 + 1 + 0 + 1 + + "Windows" + "html-document" + "" + "dialogsfolder" + 1 + 1 + 0 + 1 + + "MultiCamSettingsPanel: ID_MULTICAMSETTINGSPANEL" + "dialog-document" + "" + "panel" + 0 + 1 + 0 + 0 + "wbPanelProxy" + 10000 + 0 + "" + 0 + "" + "Standard" + 0 + 0 + "ID_MULTICAMSETTINGSPANEL" + 10000 + "" + "MultiCamSettingsPanel" + "MotionDetectorConfigWindowBase" + 0 + 1 + "MultiCamSettingsPanel.cpp" + "MultiCamSettingsPanel.h" + "" + "" + "" + "" + "" + "" + 0 + 1 + "<Any platform>" + "" + "" + "" + "" + "" + "" + "Tiled" + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 1 + 0 + 1 + 0 + "" + 1 + -1 + -1 + -1 + -1 + "Centre" + "Centre" + 0 + 5 + 1 + 1 + 1 + 1 + 0 + 0 + 0 + "" + "" + "" + 0 + + "wxBoxSizer V" + "dialog-control-document" + "" + "sizer" + 0 + 1 + 0 + 0 + "wbBoxSizerProxy" + "Vertical" + "" + "Centre" + "Centre" + 0 + 5 + 1 + 1 + 1 + 1 + 0 + 0 + 0 + "<Any platform>" + + "wxStaticBoxSizer H" + "dialog-control-document" + "" + "sizer" + 0 + 1 + 0 + 0 + "wbStaticBoxSizerProxy" + "wxID_ANY" + -1 + "Display Settings" + "" + "" + "" + "" + 0 + 1 + "Use wxWidgets version" + "wxStaticBox" + "Horizontal" + "Expand" + "Centre" + 0 + 5 + 1 + 1 + 0 + 1 + 0 + 0 + 0 + "<Any platform>" + + "wxStaticText: wxID_STATIC" + "dialog-control-document" + "" + "statictext" + 0 + 1 + 0 + 0 + "wbStaticTextProxy" + "wxID_STATIC" + 5105 + "" + "wxStaticText" + "wxStaticText" + 1 + 0 + "" + "" + "" + "Displays layout:" + -1 + "" + "" + "" + "" + "" + 0 + 1 + "<Any platform>" + "" + "" + "" + "" + "" + "" + "" + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + "" + -1 + -1 + -1 + -1 + "Left" + "Top" + 0 + 5 + 1 + 1 + 1 + 1 + 0 + 0 + 0 + "" + "" + + + "DisplayGeometryWindow: ID_DisplayGeometry" + "dialog-control-document" + "" + "foreign" + 0 + 1 + 0 + 0 + "wbForeignCtrlProxy" + "ID_DisplayGeometry" + 10002 + "" + "DisplayGeometryWindow" + "wxWindow" + 1 + 0 + "" + "DisplayGeometryWindow.h" + "" + 1 + "" + "" + "" + "" + "" + 0 + 1 + "<Any platform>" + "" + "" + "" + "" + "" + "" + "" + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + "" + -1 + -1 + 120 + 80 + "Centre" + "Expand" + 1 + 5 + 1 + 1 + 1 + 1 + 0 + 0 + 0 + "" + "" + "" + + + + "wxListbook: ID_LISTBOOK" + "dialog-control-document" + "" + "notebook" + 0 + 1 + 0 + 0 + "wbListbookProxy" + "ID_LISTBOOK" + 10001 + "" + "wxListbook" + "wxListbook" + 1 + 0 + "" + "" + "m_Notebook" + "" + "" + "" + "" + "" + 0 + 1 + "<Any platform>" + "" + "" + "" + "" + "" + "" + "" + 0 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + "" + -1 + -1 + -1 + -1 + "Expand" + "Centre" + 1 + 5 + 1 + 1 + 1 + 1 + 0 + 0 + 0 + "" + "" + + + + + + "Sources" + "html-document" + "" + "sourcesfolder" + 1 + 1 + 0 + 1 + + "MotionDetectorMultiCam.rc" + "source-editor-document" + "MotionDetectorMultiCam.rc" + "source-editor" + 0 + 0 + 1 + 0 + "28/10/2010" + "" + + + + "Images" + "html-document" + "" + "bitmapsfolder" + 1 + 1 + 0 + 1 + + + + +
diff --git a/MotionDetectorMultiCam/MotionDetectorMultiCamExports.cpp b/MotionDetectorMultiCam/MotionDetectorMultiCamExports.cpp new file mode 100644 index 0000000..2d0f62c --- /dev/null +++ b/MotionDetectorMultiCam/MotionDetectorMultiCamExports.cpp @@ -0,0 +1,14 @@ +#include "stdwx.h" +#include +#include "MotionDetectorMultiCamPlugin.h" +#include "MotionDetectorMultiCamGuiPlugin.h" + +PLUGIN_EXPORTED_API MotionDetectorPluginBase * CreatePlugin() +{ + return new MotionDetectorMultiCamPlugin; +} + +PLUGIN_EXPORTED_API void DeletePlugin(MotionDetectorPluginBase * plugin) +{ + wxDELETE(plugin); +} diff --git a/MotionDetectorMultiCam/MotionDetectorMultiCamGuiPlugin.cpp b/MotionDetectorMultiCam/MotionDetectorMultiCamGuiPlugin.cpp new file mode 100644 index 0000000..3874067 --- /dev/null +++ b/MotionDetectorMultiCam/MotionDetectorMultiCamGuiPlugin.cpp @@ -0,0 +1,32 @@ +#include "stdwx.h" +#include "MotionDetectorMultiCamGuiPlugin.h" +#include "MultiCamSettingsPanel.h" + +IMPLEMENT_DYNAMIC_CLASS(MotionDetectorMultiCamGuiPlugin, wxGuiPluginBase) + +MotionDetectorMultiCamGuiPlugin::MotionDetectorMultiCamGuiPlugin(wxEvtHandler* handler) + : wxGuiPluginBase(handler) +{ +} + +MotionDetectorMultiCamGuiPlugin::~MotionDetectorMultiCamGuiPlugin() +{ +} + +wxString MotionDetectorMultiCamGuiPlugin::GetId() const +{ + return wxT("{DC7382A2-1813-414c-9BD4-4BDF09058A0E}"); +} + +wxString MotionDetectorMultiCamGuiPlugin::GetName() const +{ + return wxT("MultiCam Motion Detector"); +} + +wxWindow* MotionDetectorMultiCamGuiPlugin::CreatePanel(wxWindow* parent) +{ + MultiCamSettingsPanel* panel = new MultiCamSettingsPanel(nullptr, parent); + + return static_cast(panel); +} + diff --git a/MotionDetectorMultiCam/MotionDetectorMultiCamGuiPlugin.h b/MotionDetectorMultiCam/MotionDetectorMultiCamGuiPlugin.h new file mode 100644 index 0000000..698e350 --- /dev/null +++ b/MotionDetectorMultiCam/MotionDetectorMultiCamGuiPlugin.h @@ -0,0 +1,14 @@ +#pragma once +#include + +class MotionDetectorMultiCamGuiPlugin : public wxGuiPluginBase +{ + DECLARE_DYNAMIC_CLASS(MotionDetectorMultiCamGuiPlugin) +public: + MotionDetectorMultiCamGuiPlugin(wxEvtHandler* handler = nullptr); + virtual ~MotionDetectorMultiCamGuiPlugin(); + + virtual wxString GetName() const override; + virtual wxString GetId() const override; + virtual wxWindow* CreatePanel(wxWindow* parent) override; +}; \ No newline at end of file diff --git a/MotionDetectorMultiCam/MotionDetectorMultiCamPlugin.cpp b/MotionDetectorMultiCam/MotionDetectorMultiCamPlugin.cpp new file mode 100644 index 0000000..eed9e9b --- /dev/null +++ b/MotionDetectorMultiCam/MotionDetectorMultiCamPlugin.cpp @@ -0,0 +1,36 @@ +#include "stdwx.h" +#include "MotionDetectorMultiCamPlugin.h" +#include "MotionDetectorMultiCam.h" +#include "MultiCamSettingsPanel.h" +#if defined(USE_VLD) +#include s +#endif + +IMPLEMENT_DYNAMIC_CLASS(MotionDetectorMultiCamPlugin, MotionDetectorPluginBase) + +MotionDetectorMultiCamPlugin::MotionDetectorMultiCamPlugin() +: m_ID(wxT("{DC7382A2-1813-414c-9BD4-4BDF09058A0E}")) +{ + +} + +MotionDetectorMultiCamPlugin::~MotionDetectorMultiCamPlugin() +{ + +} + +wxString MotionDetectorMultiCamPlugin::GetID() const +{ + return m_ID; +} + +wxString MotionDetectorMultiCamPlugin::GetName() const +{ + return _("MultiCam Motion Detector"); +} + +MotionDetectorBase * MotionDetectorMultiCamPlugin::CreateDetector(GeometryProvider && geometryProvider) +{ + return new MotionDetectorMultiCam(this, std::move(geometryProvider)); +} + diff --git a/MotionDetectorMultiCam/MotionDetectorMultiCamPlugin.h b/MotionDetectorMultiCam/MotionDetectorMultiCamPlugin.h new file mode 100644 index 0000000..39fe25d --- /dev/null +++ b/MotionDetectorMultiCam/MotionDetectorMultiCamPlugin.h @@ -0,0 +1,21 @@ +#ifndef _MOTIONDETECTORMULTICAMPLUGIN_H +#define _MOTIONDETECTORMULTICAMPLUGIN_H + +#include + +/// Base class for all iFloor effect plugins +class MotionDetectorMultiCamPlugin : public MotionDetectorPluginBase +{ + DECLARE_DYNAMIC_CLASS(MotionDetectorMultiCamPlugin) +public: + MotionDetectorMultiCamPlugin(); + ~MotionDetectorMultiCamPlugin(); + virtual wxString GetID() const; + virtual wxString GetName() const; + + virtual MotionDetectorBase * CreateDetector(GeometryProvider && geometryProvider); +private: + wxString m_ID; +}; + +#endif // _MOTIONDETECTORMULTICAMPLUGIN_H \ No newline at end of file diff --git a/MotionDetectorMultiCam/MultiCamCameraManager.cpp b/MotionDetectorMultiCam/MultiCamCameraManager.cpp new file mode 100644 index 0000000..50121fe --- /dev/null +++ b/MotionDetectorMultiCam/MultiCamCameraManager.cpp @@ -0,0 +1,234 @@ +#include "stdwx.h" +#include +#include "MultiCamCameraManager.h" +#include +#include + +#if defined(USE_VLD) +#include +#endif + +#define CMAN_ERR_NO_ERROR 0 +#define CMAN_ERR_NO_INDEX 2 +#define CMAN_ERR_WRONG_SIZE 3 +#define CMAN_ERR_NO_FILTER 4 + +const int MultiCamCameraManager::MAX_WIDTH = INT_MAX; +const int MultiCamCameraManager::MAX_HEIGHT = INT_MAX; +const wxString MultiCamCameraManager::ERROR_MESSAGES[] = +{ + _("No error"), + _("The depth or the number of channels differ in cameras images"), + _("There is no camera with such index"), + _("Wrong height or width of the full view image"), + _("There is no filter with such name"), +}; + +MultiCamCameraManager::MultiCamCameraManager(GeometryProvider & geometryProvider) + : m_Height(MAX_WIDTH) + , m_Width(MAX_HEIGHT) + , m_LastError(0) + , m_bUseOneOnMulti(false) + , m_GeometryProvider(geometryProvider) +{ + //cvNamedWindow("1"); +} + +MultiCamCameraManager::~MultiCamCameraManager(void) +{ + //cvDestroyWindow("1"); + CleanDisplayImages(); + + for (IFloorImagePreprocessorVector::iterator it = m_ArrayOfCameras.begin(); it != m_ArrayOfCameras.end(); ++it) + { + delete *it; + } + m_ArrayOfCameras.clear(); +} + +IFloorImagePreprocessor * MultiCamCameraManager::GetCameraByID(int cameraID) +{ + if (cameraID < (int)m_ArrayOfCameras.size() ) + return m_ArrayOfCameras[cameraID]; + else + return nullptr; +} + +void MultiCamCameraManager::SetUseOneOnMulti(bool useOne) +{ + m_bUseOneOnMulti = useOne; +} + +void MultiCamCameraManager::AddCamera(const IFloorCameraSettings * settings, void * buffer, size_t bufLength) +{ + m_ArrayOfCameras.push_back(new IFloorImagePreprocessor(settings, buffer, bufLength)); +} + +bool MultiCamCameraManager::InitCameraParams() +{ + // Look for minimum sizes + for (size_t i = 0; i < m_ArrayOfCameras.size(); i++) + { + IFloorImagePreprocessor * capture = m_ArrayOfCameras[i]; + if (capture != nullptr) + { + m_Height = wxMin(capture->GetSettings()->CamHeight, m_Height); + m_Width = wxMin(capture->GetSettings()->CamWidth, m_Width); + } + } + + // Decided what capture we must resize + for (size_t i = 0; i < m_ArrayOfCameras.size(); i++) + { + IFloorImagePreprocessor * capture = m_ArrayOfCameras[i]; + if (capture != nullptr) + { + capture->PrepareImage(m_Width, m_Height); + } + } + return (m_Height != MAX_HEIGHT) || (m_Width != MAX_WIDTH); +} + +cv::Mat MultiCamCameraManager::GetImage() +{ + wxPoint pointIn; + wxRectVector displays; + m_GeometryProvider.GetGeometry(displays); + size_t count = wxMin(displays.size(), m_ArrayOfCameras.size()); + + if (m_ImgFullView.empty() && count > 0) + { + m_ImgFullView = cv::Mat::zeros(cv::Size(m_Width, m_Height), CV_8UC3); + } + + for (auto i = 0; i < count; ++i) + { + IFloorImagePreprocessor * capture = m_ArrayOfCameras[i]; + if (capture != nullptr) + { + wxRect & display = displays[i]; + cv::Mat img = capture->ProcessImage(); + if (img.empty()) + continue; + + // Create image if necessary + if (m_DispImages.size() < i + 1) + { + m_DispImages.push_back(cv::Mat::zeros(cv::Size(display.GetWidth(), display.GetHeight()), img.type())); + } + + if (m_bUseOneOnMulti) + { + cv::resize(img, m_ImgFullView, m_ImgFullView.size()); + } + else // If we use default system monitors geometry and one camera per monitor + { + + // Resize image to fit display size + cv::Mat displayImg = m_DispImages[i]; + cv::resize(img, displayImg, displayImg.size()); + // Calculate insertion point + pointIn = display.GetLeftTop(); + + pointIn -= m_LeftTop; + + cv::Rect roi(pointIn.x, pointIn.y, displayImg.cols, displayImg.rows); + + if (roi.x >= 0 && roi.y >= 0 && (roi.x + roi.width) <= m_ImgFullView.cols && (roi.y + roi.height) <= m_ImgFullView.rows) + { + displayImg.copyTo(m_ImgFullView(roi)); + } + } + } + } + /*if(rand() % 20 == 0) + { + cvSaveImage("test.bmp", m_ImgFullView); + IplImage * t = cvCreateImage(cvSize(m_ImgFullView->width*0.3, m_ImgFullView->height*0.3), m_ImgFullView->depth, m_ImgFullView->nChannels); + cvResize(m_ImgFullView, t); + cvShowImage("1", t); + cvReleaseImage(&t); + }*/ + + return m_ImgFullView; +} + + +bool MultiCamCameraManager::SetROI(int x, int y, int width, int height, int cameraID) +{ + + if (cameraID < (int)m_ArrayOfCameras.size()) + { + m_ArrayOfCameras[cameraID]->SetROI(x, y, width, height); + m_LastError = CMAN_ERR_NO_ERROR; + return true; + } + else{ + m_LastError = CMAN_ERR_NO_INDEX; + return false; + } +} + +bool MultiCamCameraManager::CreateFullView() +{ + wxRect workingArea; + + CleanDisplayImages(); + wxRectVector displays; + m_GeometryProvider.GetGeometry(displays); + for (auto i = 0; i < displays.size(); ++i) + { + wxRect & display = displays[i]; + workingArea.Union(display); + } + m_Height = workingArea.height; + m_Width = workingArea.width; + m_LeftTop = workingArea.GetLeftTop(); + m_ImgFullView.release(); + + return true; +} + +wxString MultiCamCameraManager::GetLastError() +{ + return ERROR_MESSAGES[m_LastError]; +} + +bool MultiCamCameraManager::AddFilter(int cameraID, const wxString & filterType, const wxString & config /*= wxEmptyString*/) +{ + if (cameraID < (int)m_ArrayOfCameras.size()) + { + if (m_ArrayOfCameras[cameraID]->AddFilterToChain(filterType, config)) + { + m_LastError = CMAN_ERR_NO_ERROR; + return true; + } + else + { + m_LastError = CMAN_ERR_NO_FILTER; + } + } + else + { + m_LastError = CMAN_ERR_NO_INDEX; + } + return false; +} + +bool MultiCamCameraManager::SetFilterSettings(int cameraID, const wxString & filterType, const wxString & config) +{ + IFloorImagePreprocessor * camera = GetCameraByID(cameraID); + if (!camera) + return false; + + return camera->SetFilterSettings(filterType, config); +} + +void MultiCamCameraManager::CleanDisplayImages() +{ + for (size_t i=0; i < m_DispImages.size(); i++) + { + m_DispImages[i].release(); + } + m_DispImages.clear(); +} diff --git a/MotionDetectorMultiCam/MultiCamCameraManager.h b/MotionDetectorMultiCam/MultiCamCameraManager.h new file mode 100644 index 0000000..e3350da --- /dev/null +++ b/MotionDetectorMultiCam/MultiCamCameraManager.h @@ -0,0 +1,70 @@ +#ifndef _MULTICAMCAMERAMANAGER_H +#define _MULTICAMCAMERAMANAGER_H +#include +#include + +// Type definition for display image vector +typedef std::vector ImageVector; + +class GeometryProvider; + +class MultiCamCameraManager +{ +public: + MultiCamCameraManager(GeometryProvider & geometryProvider); + ~MultiCamCameraManager(void); + // Add camera reference + void AddCamera(const IFloorCameraSettings * settings, void * buffer, size_t bufLength); + // Calculate camera view + bool InitCameraParams(); + // Set the region of interest for camera with specific ID + bool SetROI(int x, int y, int width, int height, int cameraID); + // Need to create a full camera view after all initializations + bool CreateFullView(); + // Get last error msg + wxString GetLastError(); + // Add a filter to the camera + bool AddFilter(int cameraID, const wxString & filterType, const wxString & config = wxEmptyString); + // Change filter settings + bool SetFilterSettings(int cameraID, const wxString & filterType, const wxString & config); + // Get concatenate view + cv::Mat GetImage(); + // Clean the display image vector + void CleanDisplayImages(); + // Get camera filter preprocessor by ID + IFloorImagePreprocessor * GetCameraByID(int cameraID); + // Use one sensor for all monitors + void SetUseOneOnMulti(bool useOne); + + bool IsUsingCustomGeometry(); +protected: + // Cameras map + IFloorImagePreprocessorVector m_ArrayOfCameras; + // Full camera view + cv::Mat m_ImgFullView; + // Display image vector + ImageVector m_DispImages; + // Base image size + int m_Height; + int m_Width; + // Arrays of heights and widths + wxArrayInt m_RowHeights; + wxArrayInt m_ColWidths; + // Arrays of offsets + wxArrayInt m_OffsetsX; + wxArrayInt m_OffsetsY; + // Last error number + int m_LastError; + // Left-top point of union display rectangle + wxPoint m_LeftTop; + // Use one sensor for all monitors + bool m_bUseOneOnMulti; + + GeometryProvider & m_GeometryProvider; +private: + static const int MAX_HEIGHT; + static const int MAX_WIDTH; + static const wxString ERROR_MESSAGES[]; +}; + +#endif // _MULTICAMCAMERAMANAGER_H \ No newline at end of file diff --git a/MotionDetectorMultiCam/MultiCamSettingsPanel.cpp b/MotionDetectorMultiCam/MultiCamSettingsPanel.cpp new file mode 100644 index 0000000..3616f47 --- /dev/null +++ b/MotionDetectorMultiCam/MultiCamSettingsPanel.cpp @@ -0,0 +1,390 @@ +#include "stdwx.h" +#include "MultiCamSettingsPanel.h" +#include "MotionDetectorMultiCam.h" +#include "IFloorImagePreprocessor.h" +#include "FilterPreviewWindow.h" +#include +#include +#include +#include + +////@begin includes +#include "DisplayGeometryWindow.h" +#include "wx/imaglist.h" +////@end includes + +#if defined(USE_VLD) +#include +#endif + +////@begin XPM images +////@end XPM images + +/* + * MultiCamSettingsPanel type definition + */ + +IMPLEMENT_DYNAMIC_CLASS( MultiCamSettingsPanel, MotionDetectorConfigWindowBase ) + + +/* + * MultiCamSettingsPanel event table definition + */ + +BEGIN_EVENT_TABLE( MultiCamSettingsPanel, MotionDetectorConfigWindowBase ) + +////@begin MultiCamSettingsPanel event table entries +////@end MultiCamSettingsPanel event table entries +EVT_BITMAP_SEND(wxID_ANY, MultiCamSettingsPanel::OnImageCallback) + +END_EVENT_TABLE() + + +/* + * MultiCamSettingsPanel constructors + */ + + MultiCamSettingsPanel::MultiCamSettingsPanel() +{ + Init(); +} + +MultiCamSettingsPanel::MultiCamSettingsPanel(MotionDetectorMultiCam * detector, wxWindow * parent) +{ + Init(); + Create(detector, parent); +} + + +/* + * MultiCamSettingsPanel creator + */ + +bool MultiCamSettingsPanel::Create(MotionDetectorMultiCam * detector, wxWindow * parent) +{ + MotionDetectorConfigWindowBase::Create(detector, parent); + CreateControls(); + ReadConfig(); + return true; +} + + +/* + * MultiCamSettingsPanel destructor + */ + +MultiCamSettingsPanel::~MultiCamSettingsPanel() +{ +////@begin MultiCamSettingsPanel destruction +////@end MultiCamSettingsPanel destruction + m_CameraSettings.Clear(); +} + + +/* + * Member initialisation + */ + +void MultiCamSettingsPanel::Init() +{ +////@begin MultiCamSettingsPanel member initialisation + m_Notebook = NULL; +////@end MultiCamSettingsPanel member initialisation +} + + +/* + * Control creation for MultiCamSettingsPanel + */ + +void MultiCamSettingsPanel::CreateControls() +{ +////@begin MultiCamSettingsPanel content construction + MultiCamSettingsPanel* itemMotionDetectorConfigWindowBase1 = this; + + wxBoxSizer* itemBoxSizer2 = new wxBoxSizer(wxVERTICAL); + itemMotionDetectorConfigWindowBase1->SetSizer(itemBoxSizer2); + + wxStaticBox* itemStaticBoxSizer3Static = new wxStaticBox(itemMotionDetectorConfigWindowBase1, wxID_ANY, _("Display Settings")); + wxStaticBoxSizer* itemStaticBoxSizer3 = new wxStaticBoxSizer(itemStaticBoxSizer3Static, wxHORIZONTAL); + itemBoxSizer2->Add(itemStaticBoxSizer3, 0, wxGROW|wxLEFT|wxRIGHT|wxBOTTOM, 5); + + wxStaticText* itemStaticText4 = new wxStaticText( itemStaticBoxSizer3->GetStaticBox(), wxID_STATIC, _("Displays layout:"), wxDefaultPosition, wxDefaultSize, 0 ); + itemStaticBoxSizer3->Add(itemStaticText4, 0, wxALIGN_TOP|wxALL, 5); + + DisplayGeometryWindow* itemWindow5 = new DisplayGeometryWindow( itemStaticBoxSizer3->GetStaticBox(), m_Detector->GetGeometryProvider(), ID_DisplayGeometry, wxDefaultPosition, wxSize(120, 80), wxNO_BORDER ); + itemStaticBoxSizer3->Add(itemWindow5, 1, wxGROW|wxALL, 5); + + m_Notebook = new wxListbook( itemMotionDetectorConfigWindowBase1, ID_LISTBOOK, wxDefaultPosition, wxDefaultSize, wxBK_TOP ); + + itemBoxSizer2->Add(m_Notebook, 1, wxGROW|wxALL, 5); + +////@end MultiCamSettingsPanel content construction + m_Notebook->GetListView()->SetSizeHints(-1, 50, -1, 50); + + wxStaticBox* itemStaticBoxSizer6Static = new wxStaticBox(itemMotionDetectorConfigWindowBase1, wxID_ANY, _("Blobs Limits")); + wxStaticBoxSizer* itemStaticBoxSizer6 = new wxStaticBoxSizer(itemStaticBoxSizer6Static, wxHORIZONTAL); + itemBoxSizer2->Add(itemStaticBoxSizer6, 0, wxGROW|wxLEFT|wxRIGHT|wxBOTTOM, 5); + + wxStaticText* itemStaticText7 = new wxStaticText( itemMotionDetectorConfigWindowBase1, wxID_STATIC, _("Maximum Blob Area Limit:"), wxDefaultPosition, wxDefaultSize, 0 ); + itemStaticBoxSizer6->Add(itemStaticText7, 0, wxALIGN_TOP|wxALL, 5); + + m_cbMaxBlobArea = new wxSpinCtrl( itemMotionDetectorConfigWindowBase1, ID_MAXBLOBAREA, _T("10000"), wxDefaultPosition, wxSize(70, -1), wxSP_ARROW_KEYS, 1000, 1000000, 0 ); + itemStaticBoxSizer6->Add(m_cbMaxBlobArea, 0, wxGROW|wxLEFT|wxRIGHT|wxBOTTOM, 5); + + wxStaticText* itemStaticText8 = new wxStaticText( itemMotionDetectorConfigWindowBase1, wxID_STATIC, _("Minimum Blob Area Limit:"), wxDefaultPosition, wxDefaultSize, 0 ); + itemStaticBoxSizer6->Add(itemStaticText8, 0, wxALIGN_TOP|wxALL, 5); + + m_cbMinBlobArea = new wxSpinCtrl( itemMotionDetectorConfigWindowBase1, ID_MINBLOBAREA, _T("100"), wxDefaultPosition, wxSize(70, -1), wxSP_ARROW_KEYS, 10, 10000, 0 ); + itemStaticBoxSizer6->Add(m_cbMinBlobArea, 0, wxGROW|wxLEFT|wxRIGHT|wxBOTTOM, 5); + + wxStaticText* itemStaticText9 = new wxStaticText( itemMotionDetectorConfigWindowBase1, wxID_STATIC, _("Maximum Blob Count:"), wxDefaultPosition, wxDefaultSize, 0 ); + itemStaticBoxSizer6->Add(itemStaticText9, 0, wxALIGN_TOP|wxALL, 5); + + m_cbMaxBlobCount = new wxSpinCtrl( itemMotionDetectorConfigWindowBase1, ID_MAXBLOBCOUNT, _T("5"), wxDefaultPosition, wxSize(70, -1), wxSP_ARROW_KEYS, 1, 200, 0 ); + itemStaticBoxSizer6->Add(m_cbMaxBlobCount, 0, wxGROW|wxLEFT|wxRIGHT|wxBOTTOM, 5); + + m_cbUseOneOnMulti = new wxCheckBox( itemMotionDetectorConfigWindowBase1, ID_MAXBLOBCOUNT, _T("Use one sensor on multiple monitors")); + itemStaticBoxSizer6->Add(m_cbUseOneOnMulti , 0, wxGROW|wxLEFT|wxRIGHT|wxBOTTOM, 5); + if(m_Detector != NULL){ + MotionDetectorMultiCam* detector = dynamic_cast(m_Detector); + m_cbMaxBlobArea->SetValidator(wxGenericValidator(&detector->m_maxBlobArea)); + m_cbMaxBlobArea->GetValidator()->TransferToWindow(); + + m_cbMinBlobArea->SetValidator(wxGenericValidator(&detector->m_minBlobArea)); + m_cbMinBlobArea->GetValidator()->TransferToWindow(); + + m_cbMaxBlobCount->SetValidator(wxGenericValidator(&detector->m_maxBlobCount)); + m_cbMaxBlobCount->GetValidator()->TransferToWindow(); + + m_cbUseOneOnMulti->SetValidator(wxGenericValidator(&detector->m_bUseOneOnMulti)); + m_cbUseOneOnMulti->GetValidator()->TransferToWindow(); + } +#if defined(__WXGTK__) + SetBackgroundColour(*wxWHITE); +#endif +} + + +/* + * Should we show tooltips? + */ + +bool MultiCamSettingsPanel::ShowToolTips() +{ + return true; +} + +/* + * Get bitmap resources + */ + +wxBitmap MultiCamSettingsPanel::GetBitmapResource( const wxString& name ) +{ + // Bitmap retrieval +////@begin MultiCamSettingsPanel bitmap retrieval + wxUnusedVar(name); + return wxNullBitmap; +////@end MultiCamSettingsPanel bitmap retrieval +} + +/* + * Get icon resources + */ + +wxIcon MultiCamSettingsPanel::GetIconResource( const wxString& name ) +{ + // Icon retrieval +////@begin MultiCamSettingsPanel icon retrieval + wxUnusedVar(name); + return wxNullIcon; +////@end MultiCamSettingsPanel icon retrieval +} + +bool MultiCamSettingsPanel::ReadConfig() +{ + if (m_Detector == nullptr) + { + return false; + } + + MotionDetectorMultiCam * detector = (MotionDetectorMultiCam *)m_Detector; + m_CameraSettings = detector->GetCameraSettings(); + return true; +} + +bool MultiCamSettingsPanel::SaveConfig() +{ + + if (m_Detector == nullptr) + { + return false; + } + + MotionDetectorMultiCam * detector = (MotionDetectorMultiCam *)m_Detector; + ReadCameraSettings(); + detector->GetCameraSettings() = m_CameraSettings; + return true; +} + +void MultiCamSettingsPanel::StartDetection() +{ + wxWindowUpdateLocker lock(this); + + int pos = m_Notebook->GetSelection(); + m_Notebook->DeleteAllPages(); + m_PreviewMap.clear(); + for (size_t i = 0; i < m_Detector->GetSupportedCamerasCount(); ++i) + { + CreateCameraSettings(i); + } + + TransferDataToWindow(); + + if (pos == wxNOT_FOUND) + pos = 0; + if (m_Notebook->GetPageCount() > 0) + m_Notebook->SetSelection(pos); +} + +void MultiCamSettingsPanel::StopDetection() +{ + ReadCameraSettings(); +} + +void MultiCamSettingsPanel::CreateCameraSettings(int index) +{ + MotionDetectorMultiCam * detector = (MotionDetectorMultiCam *)m_Detector; + IFloorImagePreprocessor * camera = detector->GetCameraImagePreprocessor(index); + + if (!camera) + return; + + bool hasConfig = index < (int)m_CameraSettings.GetCount(); + + wxScrolledWindow * page = new wxScrolledWindow(m_Notebook); +#if defined(__WXGTK__) + page->SetBackgroundColour(*wxWHITE); +#endif + page->SetClientData(camera); + wxSizer * sizer = new wxFlexGridSizer(2); + page->SetSizer(sizer); + ChainOfFilters & filters = camera->GetFilters(); + for (size_t i = 0; i < filters.size(); ++i) + { + Filter * filter = filters[i]; + if (hasConfig) + IFloorImagePreprocessor::SetFilterSettings(filter, m_CameraSettings[index][filter->GetClassInfo()->GetClassName()]); + FilterPreviewWindow * preview = new FilterPreviewWindow(page, wxID_STATIC, wxDefaultPosition, wxSize(PREVIEW_WIDTH, PREVIEW_HEIGHT), wxNO_BORDER); + m_PreviewMap[filter] = preview; + filter->CreatePreviewAndParams(page, sizer, preview); + filter->SetImageCallback(&MultiCamSettingsPanel::ImageCallback, index, this); + } + page->Layout(); + page->SetScrollbars(1, 1, 0, 0); + page->FitInside(); + + m_Notebook->AddPage(page, wxString::Format(_("Camera #%d"), index)); +} + +void MultiCamSettingsPanel::ImageCallback(int cameraID, const wxString& filterName, cv::Mat image, void* userData) +{ + MultiCamSettingsPanel * _this = (MultiCamSettingsPanel *)userData; + int pos = _this->m_Notebook->GetSelection(); + if (pos != cameraID || image.empty()) + return; + + cv::Mat resizedImg; + cv::resize(image, resizedImg, cv::Size(PREVIEW_WIDTH, PREVIEW_HEIGHT), 0, 0, cv::INTER_LINEAR); + + cv::Mat destination; + if (resizedImg.channels() == 1) + { + cv::cvtColor(resizedImg, destination, cv::COLOR_GRAY2RGB); + } + else if (resizedImg.channels() == 3) + { + cv::cvtColor(resizedImg, destination, cv::COLOR_BGR2RGB); + } + else if (resizedImg.channels() == 4) + { + cv::cvtColor(resizedImg, destination, cv::COLOR_BGRA2RGB); + } + else + wxFAIL_MSG(wxT("Unknown color model")); + + + if (!destination.empty()) + { + wxImage img(destination.cols, destination.rows, destination.data, true); + wxBitmap* bmp = new wxBitmap(img); + + wxBitmapEvent* event = new wxBitmapEvent(bmp, EVT_IMAGE_CALLBACK); + event->SetString(filterName); + event->SetInt(cameraID); + _this->GetEventHandler()->QueueEvent(event); + } +} + +void MultiCamSettingsPanel::OnImageCallback(wxBitmapEvent & event) +{ + wxBitmap * bmp = event.GetBitmap(); + + if (!bmp) + return; + + do + { + if (m_Notebook->GetSelection() != event.GetInt()) // CameraID + break; + + IFloorImagePreprocessor * camera = (IFloorImagePreprocessor *)m_Notebook->GetPage(event.GetInt())->GetClientData(); + if (!camera) + break; + + Filter * filter = camera->GetFilter(event.GetString()); + if (!filter) + break; + + FilterPreviewWindow * preview = m_PreviewMap[filter]; + if (!preview) + break; + preview->SetBitmap(*bmp); + } while (false); +} + +void MultiCamSettingsPanel::ReadCameraSettings() +{ + MotionDetectorMultiCam * detector = (MotionDetectorMultiCam *)m_Detector; + if (!detector->IsRunning()) + return; + m_CameraSettings.Clear(); + for (int i = 0; i < detector->GetSupportedCamerasCount(); ++i) + { + IFloorImagePreprocessorSettings * settings = new IFloorImagePreprocessorSettings(); + m_CameraSettings.Add(settings); + IFloorImagePreprocessor * camera = detector->GetCameraImagePreprocessor(i); + if (!camera) + continue; + ChainOfFilters & filters = camera->GetFilters(); + for (size_t j = 0; j < filters.size(); ++j) + { + Filter * filter = filters[j]; + if (!filter) + continue; + wxString config = SerializableBase::Serialize(*filter); + settings->SetFilterConfig(filter->GetClassInfo()->GetClassName(), config); + } + } +} + +bool MultiCamSettingsPanel::TransferDataFromWindow() +{ + MotionDetectorMultiCam * detector = (MotionDetectorMultiCam *)m_Detector; + m_cbMaxBlobArea->GetValidator()->TransferFromWindow(); + m_cbMinBlobArea->GetValidator()->TransferFromWindow(); + m_cbMaxBlobCount->GetValidator()->TransferFromWindow(); + if (!detector->IsRunning()) + return true; + return wxWindow::TransferDataFromWindow(); +} diff --git a/MotionDetectorMultiCam/MultiCamSettingsPanel.h b/MotionDetectorMultiCam/MultiCamSettingsPanel.h new file mode 100644 index 0000000..cc5512a --- /dev/null +++ b/MotionDetectorMultiCam/MultiCamSettingsPanel.h @@ -0,0 +1,112 @@ +#ifndef _MULTICAMSETTINGSPANEL_H +#define _MULTICAMSETTINGSPANEL_H + + +/*! + * Includes + */ + +////@begin includes +#include "wx/listbook.h" +////@end includes +#include "wx/spinctrl.h" // the base class +#include +#include +#include + +/*! + * Forward declarations + */ + +////@begin forward declarations +class MultiCamSettingsPanel; +class wxListbook; +////@end forward declarations +class MotionDetectorMultiCam; +class IFloorImagePreprocessor; +class FilterPreviewWindow; +class wxBitmapEvent; + +WX_DECLARE_VOIDPTR_HASH_MAP(FilterPreviewWindow *, FilterPreviewMap); + +/*! + * MultiCamSettingsPanel class declaration + */ + +class MultiCamSettingsPanel: public MotionDetectorConfigWindowBase +{ + DECLARE_DYNAMIC_CLASS( MultiCamSettingsPanel ) + DECLARE_EVENT_TABLE() + +public: + /// Constructors + MultiCamSettingsPanel(); + MultiCamSettingsPanel(MotionDetectorMultiCam * detector, wxWindow * parent); + + /// Creation + bool Create(MotionDetectorMultiCam * detector, wxWindow * parent); + + /// Destructor + ~MultiCamSettingsPanel(); + + /// Initialises member variables + void Init(); + + /// Creates the controls and sizers + void CreateControls(); + + // IFloorConfigureManager implementation + virtual bool ReadConfig(); + virtual bool SaveConfig(); + + virtual void StartDetection(); + virtual void StopDetection(); + + virtual bool TransferDataFromWindow(); + +////@begin MultiCamSettingsPanel event handler declarations + +////@end MultiCamSettingsPanel event handler declarations + void OnImageCallback(wxBitmapEvent & event); + +////@begin MultiCamSettingsPanel member function declarations + + /// Retrieves bitmap resources + wxBitmap GetBitmapResource( const wxString& name ); + + /// Retrieves icon resources + wxIcon GetIconResource( const wxString& name ); +////@end MultiCamSettingsPanel member function declarations + + /// Should we show tooltips? + static bool ShowToolTips(); + + void CreateCameraSettings(int index); + static void ImageCallback(int cameraID, const wxString& filterName, cv::Mat image, void* userData); + + void ReadCameraSettings(); + +////@begin MultiCamSettingsPanel member variables + wxListbook* m_Notebook; + /// Control identifiers + enum { + ID_MULTICAMSETTINGSPANEL = 10000, + ID_DisplayGeometry = 10002, + ID_LISTBOOK = 10001 + }; +////@end MultiCamSettingsPanel member variables + wxSpinCtrl* m_cbMaxBlobArea; + wxSpinCtrl* m_cbMinBlobArea; + wxSpinCtrl* m_cbMaxBlobCount; + wxCheckBox* m_cbUseOneOnMulti; + enum{ + ID_MAXBLOBAREA = 10003, + ID_MINBLOBAREA = 10004, + ID_MAXBLOBCOUNT = 10005, + ID_USEONE = 10006 + }; + FilterPreviewMap m_PreviewMap; + IFloorImagePreprocessorSettingsArray m_CameraSettings; +}; + +#endif // _MULTICAMSETTINGSPANEL_H diff --git a/MotionDetectorMultiCam/Win/ALL_BUILD.vcxproj b/MotionDetectorMultiCam/Win/ALL_BUILD.vcxproj new file mode 100644 index 0000000..2cab1fa --- /dev/null +++ b/MotionDetectorMultiCam/Win/ALL_BUILD.vcxproj @@ -0,0 +1,123 @@ + + + + x64 + + + false + + + + Debug + x64 + + + Release + x64 + + + + {79459F13-BE3F-3ABE-B665-62AD563E5B08} + Win32Proj + 10.0.26100.0 + x64 + ALL_BUILD + NoUpgrade + + + + Utility + MultiByte + v143 + + + Utility + MultiByte + v143 + + + + + + + + + + <_ProjectFileVersion>10.0.20506.1 + $(Platform)\$(Configuration)\$(ProjectName)\ + $(Platform)\$(Configuration)\$(ProjectName)\ + + + + C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\mswu;C:\Users\Admin\IFloor\libs\wxWidgets\include;C:\Users\Admin\IFloor\libs\opencv\build\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\Utils;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\MotionDetectorCore;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxJSON\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxxmlserializer\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxxmlserializer;C:\Users\Admin\IFloor\projects\wxVideoProcessing\MotionDetectorMultiCam;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\MotionDetectorPluginBase;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\wxGuiPluginBase;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\CommonPluginBase;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxXS\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\MotionPrimitives;%(AdditionalIncludeDirectories) + $(ProjectDir)/$(IntDir) + %(Filename).h + %(Filename).tlb + %(Filename)_i.c + %(Filename)_p.c + + + + + C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\mswu;C:\Users\Admin\IFloor\libs\wxWidgets\include;C:\Users\Admin\IFloor\libs\opencv\build\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\Utils;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\MotionDetectorCore;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxJSON\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxxmlserializer\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxxmlserializer;C:\Users\Admin\IFloor\projects\wxVideoProcessing\MotionDetectorMultiCam;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\MotionDetectorPluginBase;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\wxGuiPluginBase;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\CommonPluginBase;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxXS\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\MotionPrimitives;%(AdditionalIncludeDirectories) + $(ProjectDir)/$(IntDir) + %(Filename).h + %(Filename).tlb + %(Filename)_i.c + %(Filename)_p.c + + + + + Always + Building Custom Rule C:/Users/Admin/IFloor/projects/wxVideoProcessing/MotionDetectorMultiCam/CMakeLists.txt + setlocal +"C:\Program Files\CMake\bin\cmake.exe" -SC:/Users/Admin/IFloor/projects/wxVideoProcessing/build -BC:/Users/Admin/IFloor/projects/wxVideoProcessing/build/Win --check-stamp-file C:/Users/Admin/IFloor/projects/wxVideoProcessing/MotionDetectorMultiCam/Win/CMakeFiles/generate.stamp +if %errorlevel% neq 0 goto :cmEnd +:cmEnd +endlocal & call :cmErrorLevel %errorlevel% & goto :cmDone +:cmErrorLevel +exit /b %1 +:cmDone +if %errorlevel% neq 0 goto :VCEnd + %(AdditionalInputs) + C:\Users\Admin\IFloor\projects\wxVideoProcessing\MotionDetectorMultiCam\Win\CMakeFiles\generate.stamp + false + Building Custom Rule C:/Users/Admin/IFloor/projects/wxVideoProcessing/MotionDetectorMultiCam/CMakeLists.txt + setlocal +"C:\Program Files\CMake\bin\cmake.exe" -SC:/Users/Admin/IFloor/projects/wxVideoProcessing/build -BC:/Users/Admin/IFloor/projects/wxVideoProcessing/build/Win --check-stamp-file C:/Users/Admin/IFloor/projects/wxVideoProcessing/MotionDetectorMultiCam/Win/CMakeFiles/generate.stamp +if %errorlevel% neq 0 goto :cmEnd +:cmEnd +endlocal & call :cmErrorLevel %errorlevel% & goto :cmDone +:cmErrorLevel +exit /b %1 +:cmDone +if %errorlevel% neq 0 goto :VCEnd + %(AdditionalInputs) + C:\Users\Admin\IFloor\projects\wxVideoProcessing\MotionDetectorMultiCam\Win\CMakeFiles\generate.stamp + false + + + + + + + + {6F4BDD20-5B99-3029-BDF0-7D719B283AF5} + ZERO_CHECK + false + Never + + + {4877C470-9466-37C0-A451-03973F6EDABD} + MotionDetectorMultiCam + + + {38ABD20B-B450-3DE7-A759-71587CAB9A2A} + MotionDetectorMultiCamGui + + + + + + \ No newline at end of file diff --git a/MotionDetectorMultiCam/Win/ALL_BUILD.vcxproj.filters b/MotionDetectorMultiCam/Win/ALL_BUILD.vcxproj.filters new file mode 100644 index 0000000..4e1fc36 --- /dev/null +++ b/MotionDetectorMultiCam/Win/ALL_BUILD.vcxproj.filters @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/MotionDetectorMultiCam/Win/MotionDetectorMultiCam.sln b/MotionDetectorMultiCam/Win/MotionDetectorMultiCam.sln new file mode 100644 index 0000000..1dba1a1 --- /dev/null +++ b/MotionDetectorMultiCam/Win/MotionDetectorMultiCam.sln @@ -0,0 +1,175 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ALL_BUILD", "ALL_BUILD.vcxproj", "{79459F13-BE3F-3ABE-B665-62AD563E5B08}" + ProjectSection(ProjectDependencies) = postProject + {4877C470-9466-37C0-A451-03973F6EDABD} = {4877C470-9466-37C0-A451-03973F6EDABD} + {38ABD20B-B450-3DE7-A759-71587CAB9A2A} = {38ABD20B-B450-3DE7-A759-71587CAB9A2A} + {6F4BDD20-5B99-3029-BDF0-7D719B283AF5} = {6F4BDD20-5B99-3029-BDF0-7D719B283AF5} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CommonPluginBase", "C:\Users\Admin\IFloor\projects\wxVideoProcessing\CommonPluginBase\Win\CommonPluginBase.vcxproj", "{751A15C0-76C2-3506-AB9D-0CD3545003FA}" + ProjectSection(ProjectDependencies) = postProject + {6F4BDD20-5B99-3029-BDF0-7D719B283AF5} = {6F4BDD20-5B99-3029-BDF0-7D719B283AF5} + {7887B55C-77B9-3262-AFAD-ABA738C61029} = {7887B55C-77B9-3262-AFAD-ABA738C61029} + {78CFCEBE-5466-3128-9717-547416DBFFE5} = {78CFCEBE-5466-3128-9717-547416DBFFE5} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "MotionDetectorCore", "C:\Users\Admin\IFloor\projects\wxVideoProcessing\MotionDetectorCore\Win\MotionDetectorCore.vcxproj", "{6E933546-0B62-311B-8D7E-6CE2ECD4CAD0}" + ProjectSection(ProjectDependencies) = postProject + {751A15C0-76C2-3506-AB9D-0CD3545003FA} = {751A15C0-76C2-3506-AB9D-0CD3545003FA} + {1F4B7EAD-4694-3453-AD34-1757E387661E} = {1F4B7EAD-4694-3453-AD34-1757E387661E} + {1903244B-9312-3A09-8AAD-8D78C1A4F977} = {1903244B-9312-3A09-8AAD-8D78C1A4F977} + {14D82890-BB7F-3748-8713-43304E91CFA1} = {14D82890-BB7F-3748-8713-43304E91CFA1} + {6F4BDD20-5B99-3029-BDF0-7D719B283AF5} = {6F4BDD20-5B99-3029-BDF0-7D719B283AF5} + {7887B55C-77B9-3262-AFAD-ABA738C61029} = {7887B55C-77B9-3262-AFAD-ABA738C61029} + {78CFCEBE-5466-3128-9717-547416DBFFE5} = {78CFCEBE-5466-3128-9717-547416DBFFE5} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "MotionDetectorMultiCam", "MotionDetectorMultiCam.vcxproj", "{4877C470-9466-37C0-A451-03973F6EDABD}" + ProjectSection(ProjectDependencies) = postProject + {751A15C0-76C2-3506-AB9D-0CD3545003FA} = {751A15C0-76C2-3506-AB9D-0CD3545003FA} + {6E933546-0B62-311B-8D7E-6CE2ECD4CAD0} = {6E933546-0B62-311B-8D7E-6CE2ECD4CAD0} + {1F4B7EAD-4694-3453-AD34-1757E387661E} = {1F4B7EAD-4694-3453-AD34-1757E387661E} + {1903244B-9312-3A09-8AAD-8D78C1A4F977} = {1903244B-9312-3A09-8AAD-8D78C1A4F977} + {14D82890-BB7F-3748-8713-43304E91CFA1} = {14D82890-BB7F-3748-8713-43304E91CFA1} + {6F4BDD20-5B99-3029-BDF0-7D719B283AF5} = {6F4BDD20-5B99-3029-BDF0-7D719B283AF5} + {23CF91DE-A1B5-3EE0-827D-ACAFD80C7CB2} = {23CF91DE-A1B5-3EE0-827D-ACAFD80C7CB2} + {007E33A1-3B69-3DBB-8C44-8A54188A3A21} = {007E33A1-3B69-3DBB-8C44-8A54188A3A21} + {7887B55C-77B9-3262-AFAD-ABA738C61029} = {7887B55C-77B9-3262-AFAD-ABA738C61029} + {78CFCEBE-5466-3128-9717-547416DBFFE5} = {78CFCEBE-5466-3128-9717-547416DBFFE5} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "MotionDetectorMultiCamGui", "MotionDetectorMultiCamGui.vcxproj", "{38ABD20B-B450-3DE7-A759-71587CAB9A2A}" + ProjectSection(ProjectDependencies) = postProject + {751A15C0-76C2-3506-AB9D-0CD3545003FA} = {751A15C0-76C2-3506-AB9D-0CD3545003FA} + {6E933546-0B62-311B-8D7E-6CE2ECD4CAD0} = {6E933546-0B62-311B-8D7E-6CE2ECD4CAD0} + {4877C470-9466-37C0-A451-03973F6EDABD} = {4877C470-9466-37C0-A451-03973F6EDABD} + {1F4B7EAD-4694-3453-AD34-1757E387661E} = {1F4B7EAD-4694-3453-AD34-1757E387661E} + {1903244B-9312-3A09-8AAD-8D78C1A4F977} = {1903244B-9312-3A09-8AAD-8D78C1A4F977} + {14D82890-BB7F-3748-8713-43304E91CFA1} = {14D82890-BB7F-3748-8713-43304E91CFA1} + {6F4BDD20-5B99-3029-BDF0-7D719B283AF5} = {6F4BDD20-5B99-3029-BDF0-7D719B283AF5} + {23CF91DE-A1B5-3EE0-827D-ACAFD80C7CB2} = {23CF91DE-A1B5-3EE0-827D-ACAFD80C7CB2} + {007E33A1-3B69-3DBB-8C44-8A54188A3A21} = {007E33A1-3B69-3DBB-8C44-8A54188A3A21} + {016D40A8-D2BA-3C2B-8741-718BD5CE7B51} = {016D40A8-D2BA-3C2B-8741-718BD5CE7B51} + {7887B55C-77B9-3262-AFAD-ABA738C61029} = {7887B55C-77B9-3262-AFAD-ABA738C61029} + {78CFCEBE-5466-3128-9717-547416DBFFE5} = {78CFCEBE-5466-3128-9717-547416DBFFE5} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "MotionDetectorPluginBase", "C:\Users\Admin\IFloor\projects\wxVideoProcessing\MotionDetectorPluginBase\Win\MotionDetectorPluginBase.vcxproj", "{1F4B7EAD-4694-3453-AD34-1757E387661E}" + ProjectSection(ProjectDependencies) = postProject + {751A15C0-76C2-3506-AB9D-0CD3545003FA} = {751A15C0-76C2-3506-AB9D-0CD3545003FA} + {14D82890-BB7F-3748-8713-43304E91CFA1} = {14D82890-BB7F-3748-8713-43304E91CFA1} + {6F4BDD20-5B99-3029-BDF0-7D719B283AF5} = {6F4BDD20-5B99-3029-BDF0-7D719B283AF5} + {7887B55C-77B9-3262-AFAD-ABA738C61029} = {7887B55C-77B9-3262-AFAD-ABA738C61029} + {78CFCEBE-5466-3128-9717-547416DBFFE5} = {78CFCEBE-5466-3128-9717-547416DBFFE5} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "MotionPrimitives", "C:\Users\Admin\IFloor\projects\wxVideoProcessing\ThirdParty\build\MotionPrimitives\Win\MotionPrimitives.vcxproj", "{1903244B-9312-3A09-8AAD-8D78C1A4F977}" + ProjectSection(ProjectDependencies) = postProject + {6F4BDD20-5B99-3029-BDF0-7D719B283AF5} = {6F4BDD20-5B99-3029-BDF0-7D719B283AF5} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Utils", "C:\Users\Admin\IFloor\projects\wxVideoProcessing\Utils\Win\Utils.vcxproj", "{14D82890-BB7F-3748-8713-43304E91CFA1}" + ProjectSection(ProjectDependencies) = postProject + {6F4BDD20-5B99-3029-BDF0-7D719B283AF5} = {6F4BDD20-5B99-3029-BDF0-7D719B283AF5} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ZERO_CHECK", "C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\Win\ZERO_CHECK.vcxproj", "{6F4BDD20-5B99-3029-BDF0-7D719B283AF5}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "strmbas", "C:\Users\Admin\IFloor\projects\wxVideoProcessing\ThirdParty\build\strmbas\Win\strmbas.vcxproj", "{23CF91DE-A1B5-3EE0-827D-ACAFD80C7CB2}" + ProjectSection(ProjectDependencies) = postProject + {6F4BDD20-5B99-3029-BDF0-7D719B283AF5} = {6F4BDD20-5B99-3029-BDF0-7D719B283AF5} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "videoInput", "C:\Users\Admin\IFloor\projects\wxVideoProcessing\ThirdParty\build\videoInput\Win\videoInput.vcxproj", "{007E33A1-3B69-3DBB-8C44-8A54188A3A21}" + ProjectSection(ProjectDependencies) = postProject + {6F4BDD20-5B99-3029-BDF0-7D719B283AF5} = {6F4BDD20-5B99-3029-BDF0-7D719B283AF5} + {23CF91DE-A1B5-3EE0-827D-ACAFD80C7CB2} = {23CF91DE-A1B5-3EE0-827D-ACAFD80C7CB2} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "wxGuiPluginBase", "C:\Users\Admin\IFloor\projects\wxVideoProcessing\wxGuiPluginBase\Win\wxGuiPluginBase.vcxproj", "{016D40A8-D2BA-3C2B-8741-718BD5CE7B51}" + ProjectSection(ProjectDependencies) = postProject + {6F4BDD20-5B99-3029-BDF0-7D719B283AF5} = {6F4BDD20-5B99-3029-BDF0-7D719B283AF5} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "wxJSON", "C:\Users\Admin\IFloor\projects\wxVideoProcessing\ThirdParty\build\wxJSON\build\Win\wxJSON.vcxproj", "{7887B55C-77B9-3262-AFAD-ABA738C61029}" + ProjectSection(ProjectDependencies) = postProject + {6F4BDD20-5B99-3029-BDF0-7D719B283AF5} = {6F4BDD20-5B99-3029-BDF0-7D719B283AF5} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "wxXS", "C:\Users\Admin\IFloor\projects\wxVideoProcessing\ThirdParty\build\wxXS\build\Win\wxXS.vcxproj", "{78CFCEBE-5466-3128-9717-547416DBFFE5}" + ProjectSection(ProjectDependencies) = postProject + {6F4BDD20-5B99-3029-BDF0-7D719B283AF5} = {6F4BDD20-5B99-3029-BDF0-7D719B283AF5} + {7887B55C-77B9-3262-AFAD-ABA738C61029} = {7887B55C-77B9-3262-AFAD-ABA738C61029} + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {79459F13-BE3F-3ABE-B665-62AD563E5B08}.Debug|x64.ActiveCfg = Debug|x64 + {79459F13-BE3F-3ABE-B665-62AD563E5B08}.Release|x64.ActiveCfg = Release|x64 + {751A15C0-76C2-3506-AB9D-0CD3545003FA}.Debug|x64.ActiveCfg = Debug|x64 + {751A15C0-76C2-3506-AB9D-0CD3545003FA}.Debug|x64.Build.0 = Debug|x64 + {751A15C0-76C2-3506-AB9D-0CD3545003FA}.Release|x64.ActiveCfg = Release|x64 + {751A15C0-76C2-3506-AB9D-0CD3545003FA}.Release|x64.Build.0 = Release|x64 + {6E933546-0B62-311B-8D7E-6CE2ECD4CAD0}.Debug|x64.ActiveCfg = Debug|x64 + {6E933546-0B62-311B-8D7E-6CE2ECD4CAD0}.Debug|x64.Build.0 = Debug|x64 + {6E933546-0B62-311B-8D7E-6CE2ECD4CAD0}.Release|x64.ActiveCfg = Release|x64 + {6E933546-0B62-311B-8D7E-6CE2ECD4CAD0}.Release|x64.Build.0 = Release|x64 + {4877C470-9466-37C0-A451-03973F6EDABD}.Debug|x64.ActiveCfg = Debug|x64 + {4877C470-9466-37C0-A451-03973F6EDABD}.Debug|x64.Build.0 = Debug|x64 + {4877C470-9466-37C0-A451-03973F6EDABD}.Release|x64.ActiveCfg = Release|x64 + {4877C470-9466-37C0-A451-03973F6EDABD}.Release|x64.Build.0 = Release|x64 + {38ABD20B-B450-3DE7-A759-71587CAB9A2A}.Debug|x64.ActiveCfg = Debug|x64 + {38ABD20B-B450-3DE7-A759-71587CAB9A2A}.Debug|x64.Build.0 = Debug|x64 + {38ABD20B-B450-3DE7-A759-71587CAB9A2A}.Release|x64.ActiveCfg = Release|x64 + {38ABD20B-B450-3DE7-A759-71587CAB9A2A}.Release|x64.Build.0 = Release|x64 + {1F4B7EAD-4694-3453-AD34-1757E387661E}.Debug|x64.ActiveCfg = Debug|x64 + {1F4B7EAD-4694-3453-AD34-1757E387661E}.Debug|x64.Build.0 = Debug|x64 + {1F4B7EAD-4694-3453-AD34-1757E387661E}.Release|x64.ActiveCfg = Release|x64 + {1F4B7EAD-4694-3453-AD34-1757E387661E}.Release|x64.Build.0 = Release|x64 + {1903244B-9312-3A09-8AAD-8D78C1A4F977}.Debug|x64.ActiveCfg = Debug|x64 + {1903244B-9312-3A09-8AAD-8D78C1A4F977}.Debug|x64.Build.0 = Debug|x64 + {1903244B-9312-3A09-8AAD-8D78C1A4F977}.Release|x64.ActiveCfg = Release|x64 + {1903244B-9312-3A09-8AAD-8D78C1A4F977}.Release|x64.Build.0 = Release|x64 + {14D82890-BB7F-3748-8713-43304E91CFA1}.Debug|x64.ActiveCfg = Debug|x64 + {14D82890-BB7F-3748-8713-43304E91CFA1}.Debug|x64.Build.0 = Debug|x64 + {14D82890-BB7F-3748-8713-43304E91CFA1}.Release|x64.ActiveCfg = Release|x64 + {14D82890-BB7F-3748-8713-43304E91CFA1}.Release|x64.Build.0 = Release|x64 + {6F4BDD20-5B99-3029-BDF0-7D719B283AF5}.Debug|x64.ActiveCfg = Debug|x64 + {6F4BDD20-5B99-3029-BDF0-7D719B283AF5}.Debug|x64.Build.0 = Debug|x64 + {6F4BDD20-5B99-3029-BDF0-7D719B283AF5}.Release|x64.ActiveCfg = Release|x64 + {6F4BDD20-5B99-3029-BDF0-7D719B283AF5}.Release|x64.Build.0 = Release|x64 + {23CF91DE-A1B5-3EE0-827D-ACAFD80C7CB2}.Debug|x64.ActiveCfg = Debug|x64 + {23CF91DE-A1B5-3EE0-827D-ACAFD80C7CB2}.Debug|x64.Build.0 = Debug|x64 + {23CF91DE-A1B5-3EE0-827D-ACAFD80C7CB2}.Release|x64.ActiveCfg = Release|x64 + {23CF91DE-A1B5-3EE0-827D-ACAFD80C7CB2}.Release|x64.Build.0 = Release|x64 + {007E33A1-3B69-3DBB-8C44-8A54188A3A21}.Debug|x64.ActiveCfg = Debug|x64 + {007E33A1-3B69-3DBB-8C44-8A54188A3A21}.Debug|x64.Build.0 = Debug|x64 + {007E33A1-3B69-3DBB-8C44-8A54188A3A21}.Release|x64.ActiveCfg = Release|x64 + {007E33A1-3B69-3DBB-8C44-8A54188A3A21}.Release|x64.Build.0 = Release|x64 + {016D40A8-D2BA-3C2B-8741-718BD5CE7B51}.Debug|x64.ActiveCfg = Debug|x64 + {016D40A8-D2BA-3C2B-8741-718BD5CE7B51}.Debug|x64.Build.0 = Debug|x64 + {016D40A8-D2BA-3C2B-8741-718BD5CE7B51}.Release|x64.ActiveCfg = Release|x64 + {016D40A8-D2BA-3C2B-8741-718BD5CE7B51}.Release|x64.Build.0 = Release|x64 + {7887B55C-77B9-3262-AFAD-ABA738C61029}.Debug|x64.ActiveCfg = Debug|x64 + {7887B55C-77B9-3262-AFAD-ABA738C61029}.Debug|x64.Build.0 = Debug|x64 + {7887B55C-77B9-3262-AFAD-ABA738C61029}.Release|x64.ActiveCfg = Release|x64 + {7887B55C-77B9-3262-AFAD-ABA738C61029}.Release|x64.Build.0 = Release|x64 + {78CFCEBE-5466-3128-9717-547416DBFFE5}.Debug|x64.ActiveCfg = Debug|x64 + {78CFCEBE-5466-3128-9717-547416DBFFE5}.Debug|x64.Build.0 = Debug|x64 + {78CFCEBE-5466-3128-9717-547416DBFFE5}.Release|x64.ActiveCfg = Release|x64 + {78CFCEBE-5466-3128-9717-547416DBFFE5}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {3652083C-5B60-3F2D-A8B0-DB7D6E247A14} + EndGlobalSection + GlobalSection(ExtensibilityAddIns) = postSolution + EndGlobalSection +EndGlobal diff --git a/MotionDetectorMultiCam/Win/MotionDetectorMultiCam.vcxproj b/MotionDetectorMultiCam/Win/MotionDetectorMultiCam.vcxproj new file mode 100644 index 0000000..4830c46 --- /dev/null +++ b/MotionDetectorMultiCam/Win/MotionDetectorMultiCam.vcxproj @@ -0,0 +1,325 @@ + + + + x64 + + + + Debug + x64 + + + Release + x64 + + + + {4877C470-9466-37C0-A451-03973F6EDABD} + Win32Proj + 10.0.26100.0 + x64 + MotionDetectorMultiCam + NoUpgrade + + + + DynamicLibrary + Unicode + v143 + + + DynamicLibrary + Unicode + v143 + + + + + + + + + + <_ProjectFileVersion>10.0.20506.1 + C:\Users\Admin\IFloor\projects\wxVideoProcessing\bin\ + MotionDetectorMultiCam.dir\Debug\ + MotionDetectorMultiCam + .dll + true + true + C:\Users\Admin\IFloor\projects\wxVideoProcessing\bin\ + MotionDetectorMultiCam.dir\Release\ + MotionDetectorMultiCam + .dll + false + true + + + + + C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\mswu;C:\Users\Admin\IFloor\libs\wxWidgets\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\Utils;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\MotionDetectorCore;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxJSON\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxxmlserializer\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxxmlserializer;C:\Users\Admin\IFloor\projects\wxVideoProcessing\MotionDetectorMultiCam;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\MotionDetectorPluginBase;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\wxGuiPluginBase;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\CommonPluginBase;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxXS\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\MotionPrimitives;C:\Users\Admin\IFloor\projects\wxVideoProcessing\ThirdParty\wxXS\build\..\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\ThirdParty\wxJSON\build\..\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\ThirdParty\videoInput;C:\Users\Admin\IFloor\projects\wxVideoProcessing\ThirdParty\videoInput\..\strmbas;C:\Users\Admin\IFloor\projects\wxVideoProcessing\ThirdParty\strmbas;%(AdditionalIncludeDirectories) + %(AdditionalOptions) /external:I "C:/Users/Admin/IFloor/libs/opencv/build/include" + $(IntDir) + EnableFastChecks + + + ProgramDatabase + 4996 + Sync + TurnOffAllWarnings + + + Disabled + stdcpp17 + + true + Disabled + Use + C:/Users/Admin/IFloor/projects/wxVideoProcessing/MotionDetectorMultiCam/Win/CMakeFiles/MotionDetectorMultiCam.dir/Debug/cmake_pch.hxx + C:/Users/Admin/IFloor/projects/wxVideoProcessing/MotionDetectorMultiCam/Win/MotionDetectorMultiCam.dir/Debug//cmake_pch.pch + + MultiThreadedDebugDLL + true + + + false + %(PreprocessorDefinitions);WIN32;_WINDOWS;__WXDEBUG__=1;MOTIONDETECTOR_MULTICAM_EXPORTS;WIN32_LEAN_AND_MEAN;WXUSINGDLL;UNICODE;_UNICODE;_CRT_SECURE_NO_DEPRECATE;_USRDLL;_WIN32_WINNT=0x0501;CMAKE_INTDIR="Debug";MotionDetectorMultiCam_EXPORTS + $(IntDir) + false + + + %(PreprocessorDefinitions);WIN32;_DEBUG;_WINDOWS;__WXDEBUG__=1;MOTIONDETECTOR_MULTICAM_EXPORTS;WIN32_LEAN_AND_MEAN;WXUSINGDLL;UNICODE;_UNICODE;_CRT_SECURE_NO_DEPRECATE;_USRDLL;_WIN32_WINNT=0x0501;CMAKE_INTDIR=\"Debug\";MotionDetectorMultiCam_EXPORTS + C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\mswu;C:\Users\Admin\IFloor\libs\wxWidgets\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\Utils;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\MotionDetectorCore;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxJSON\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxxmlserializer\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxxmlserializer;C:\Users\Admin\IFloor\projects\wxVideoProcessing\MotionDetectorMultiCam;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\MotionDetectorPluginBase;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\wxGuiPluginBase;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\CommonPluginBase;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxXS\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\MotionPrimitives;C:\Users\Admin\IFloor\projects\wxVideoProcessing\ThirdParty\wxXS\build\..\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\ThirdParty\wxJSON\build\..\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\ThirdParty\videoInput;C:\Users\Admin\IFloor\projects\wxVideoProcessing\ThirdParty\videoInput\..\strmbas;C:\Users\Admin\IFloor\projects\wxVideoProcessing\ThirdParty\strmbas;C:\Users\Admin\IFloor\libs\opencv\build\include;%(AdditionalIncludeDirectories) + + + C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\mswu;C:\Users\Admin\IFloor\libs\wxWidgets\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\Utils;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\MotionDetectorCore;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxJSON\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxxmlserializer\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxxmlserializer;C:\Users\Admin\IFloor\projects\wxVideoProcessing\MotionDetectorMultiCam;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\MotionDetectorPluginBase;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\wxGuiPluginBase;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\CommonPluginBase;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxXS\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\MotionPrimitives;C:\Users\Admin\IFloor\projects\wxVideoProcessing\ThirdParty\wxXS\build\..\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\ThirdParty\wxJSON\build\..\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\ThirdParty\videoInput;C:\Users\Admin\IFloor\projects\wxVideoProcessing\ThirdParty\videoInput\..\strmbas;C:\Users\Admin\IFloor\projects\wxVideoProcessing\ThirdParty\strmbas;C:\Users\Admin\IFloor\libs\opencv\build\include;%(AdditionalIncludeDirectories) + $(ProjectDir)/$(IntDir) + %(Filename).h + %(Filename).tlb + %(Filename)_i.c + %(Filename)_p.c + + + + setlocal +"C:\Program Files\CMake\bin\cmake.exe" -E make_directory C:/Users/Admin/IFloor/projects/wxVideoProcessing/build/../bin +if %errorlevel% neq 0 goto :cmEnd +"C:\Program Files\CMake\bin\cmake.exe" -E copy_if_different C:/Users/Admin/IFloor/projects/wxVideoProcessing/bin/MotionDetectorMultiCam.dll C:/Users/Admin/IFloor/projects/wxVideoProcessing/build/../bin/MotionDetectorMultiCam.dll +if %errorlevel% neq 0 goto :cmEnd +:cmEnd +endlocal & call :cmErrorLevel %errorlevel% & goto :cmDone +:cmErrorLevel +exit /b %1 +:cmDone +if %errorlevel% neq 0 goto :VCEnd + + + C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxbase33ud.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33ud_core.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33ud_adv.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33ud_aui.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxbase33ud_net.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33ud_gl.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxbase33ud_xml.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33ud_propgrid.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33ud_html.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxpngd.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxtiffd.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxjpegd.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxzlibd.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxregexud.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxexpatd.lib;opengl32.lib;glu32.lib;winmm.lib;comctl32.lib;uuid.lib;oleacc.lib;uxtheme.lib;rpcrt4.lib;shlwapi.lib;version.lib;wsock32.lib;C:\Users\Admin\IFloor\projects\wxVideoProcessing\bin\lib\Debug\Utils.lib;C:\Users\Admin\IFloor\projects\wxVideoProcessing\bin\lib\Debug\MotionDetectorCore.lib;C:\Users\Admin\IFloor\projects\wxVideoProcessing\bin\lib\Debug\MotionPrimitives.lib;C:\Users\Admin\IFloor\projects\wxVideoProcessing\bin\lib\Debug\videoInput.lib;C:\Users\Admin\IFloor\libs\opencv\build\x64\vc16\lib\opencv_world4120d.lib;C:\Users\Admin\IFloor\libs\opencv\build\x64\vc16\lib\opencv_world4120d.lib;C:\Users\Admin\IFloor\libs\opencv\build\x64\vc16\lib\opencv_world4120d.lib;C:\Users\Admin\IFloor\libs\opencv\build\x64\vc16\lib\opencv_world4120d.lib;C:\Users\Admin\IFloor\libs\opencv\build\x64\vc16\lib\opencv_world4120d.lib;C:\Users\Admin\IFloor\libs\opencv\build\x64\vc16\lib\opencv_world4120d.lib;C:\Users\Admin\IFloor\libs\opencv\build\x64\vc16\lib\opencv_world4120d.lib;C:\Users\Admin\IFloor\libs\opencv\build\x64\vc16\lib\opencv_world4120d.lib;C:\Users\Admin\IFloor\libs\opencv\build\x64\vc16\lib\opencv_world4120d.lib;C:\Users\Admin\IFloor\libs\opencv\build\x64\vc16\lib\opencv_world4120d.lib;C:\Users\Admin\IFloor\libs\opencv\build\x64\vc16\lib\opencv_world4120d.lib;C:\Users\Admin\IFloor\libs\opencv\build\x64\vc16\lib\opencv_world4120d.lib;C:\Users\Admin\IFloor\libs\opencv\build\x64\vc16\lib\opencv_world4120d.lib;C:\Users\Admin\IFloor\libs\opencv\build\x64\vc16\lib\opencv_world4120d.lib;C:\Users\Admin\IFloor\libs\opencv\build\x64\vc16\lib\opencv_world4120d.lib;C:\Users\Admin\IFloor\libs\opencv\build\x64\vc16\lib\opencv_world4120d.lib;C:\Users\Admin\IFloor\projects\wxVideoProcessing\bin\lib\Debug\MotionDetectorPluginBase.lib;C:\Users\Admin\IFloor\projects\wxVideoProcessing\bin\lib\Debug\CommonPluginBase.lib;C:\Users\Admin\IFloor\projects\wxVideoProcessing\bin\lib\Debug\Utils.lib;C:\Users\Admin\IFloor\projects\wxVideoProcessing\bin\lib\Debug\wxXS.lib;C:\Users\Admin\IFloor\projects\wxVideoProcessing\bin\lib\Debug\wxJSON.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxbase33ud.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33ud_core.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33ud_adv.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33ud_aui.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxbase33ud_net.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33ud_gl.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxbase33ud_xml.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33ud_propgrid.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33ud_html.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxpngd.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxtiffd.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxjpegd.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxzlibd.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxregexud.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxexpatd.lib;opengl32.lib;glu32.lib;winmm.lib;comctl32.lib;uuid.lib;oleacc.lib;uxtheme.lib;rpcrt4.lib;shlwapi.lib;version.lib;wsock32.lib;C:\Users\Admin\IFloor\projects\wxVideoProcessing\bin\lib\Debug\strmbas.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;comdlg32.lib;advapi32.lib + C:/Users/Admin/IFloor/projects/wxVideoProcessing/build/Win/lib;C:/Users/Admin/IFloor/projects/wxVideoProcessing/build/Win/lib/$(Configuration);%(AdditionalLibraryDirectories) + %(AdditionalOptions) /machine:x64 + + true + %(IgnoreSpecificDefaultLibraries) + + C:/Users/Admin/IFloor/projects/wxVideoProcessing/bin/lib/Debug/MotionDetectorMultiCam.lib + + C:/Users/Admin/IFloor/projects/wxVideoProcessing/bin/MotionDetectorMultiCam.pdb + + Console + + + false + + + + + C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\mswu;C:\Users\Admin\IFloor\libs\wxWidgets\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\Utils;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\MotionDetectorCore;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxJSON\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxxmlserializer\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxxmlserializer;C:\Users\Admin\IFloor\projects\wxVideoProcessing\MotionDetectorMultiCam;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\MotionDetectorPluginBase;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\wxGuiPluginBase;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\CommonPluginBase;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxXS\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\MotionPrimitives;C:\Users\Admin\IFloor\projects\wxVideoProcessing\ThirdParty\wxXS\build\..\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\ThirdParty\wxJSON\build\..\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\ThirdParty\videoInput;C:\Users\Admin\IFloor\projects\wxVideoProcessing\ThirdParty\videoInput\..\strmbas;C:\Users\Admin\IFloor\projects\wxVideoProcessing\ThirdParty\strmbas;%(AdditionalIncludeDirectories) + %(AdditionalOptions) /external:I "C:/Users/Admin/IFloor/libs/opencv/build/include" + $(IntDir) + Default + + + 4996 + Sync + TurnOffAllWarnings + + + AnySuitable + stdcpp17 + + true + MaxSpeed + Use + C:/Users/Admin/IFloor/projects/wxVideoProcessing/MotionDetectorMultiCam/Win/CMakeFiles/MotionDetectorMultiCam.dir/Release/cmake_pch.hxx + C:/Users/Admin/IFloor/projects/wxVideoProcessing/MotionDetectorMultiCam/Win/MotionDetectorMultiCam.dir/Release//cmake_pch.pch + + MultiThreadedDLL + true + + + false + %(PreprocessorDefinitions);WIN32;_WINDOWS;NDEBUG;MOTIONDETECTOR_MULTICAM_EXPORTS;WIN32_LEAN_AND_MEAN;WXUSINGDLL;UNICODE;_UNICODE;_CRT_SECURE_NO_DEPRECATE;_USRDLL;_WIN32_WINNT=0x0501;CMAKE_INTDIR="Release";MotionDetectorMultiCam_EXPORTS + $(IntDir) + + + false + + + %(PreprocessorDefinitions);WIN32;_WINDOWS;NDEBUG;MOTIONDETECTOR_MULTICAM_EXPORTS;WIN32_LEAN_AND_MEAN;WXUSINGDLL;UNICODE;_UNICODE;_CRT_SECURE_NO_DEPRECATE;_USRDLL;_WIN32_WINNT=0x0501;CMAKE_INTDIR=\"Release\";MotionDetectorMultiCam_EXPORTS + C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\mswu;C:\Users\Admin\IFloor\libs\wxWidgets\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\Utils;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\MotionDetectorCore;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxJSON\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxxmlserializer\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxxmlserializer;C:\Users\Admin\IFloor\projects\wxVideoProcessing\MotionDetectorMultiCam;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\MotionDetectorPluginBase;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\wxGuiPluginBase;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\CommonPluginBase;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxXS\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\MotionPrimitives;C:\Users\Admin\IFloor\projects\wxVideoProcessing\ThirdParty\wxXS\build\..\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\ThirdParty\wxJSON\build\..\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\ThirdParty\videoInput;C:\Users\Admin\IFloor\projects\wxVideoProcessing\ThirdParty\videoInput\..\strmbas;C:\Users\Admin\IFloor\projects\wxVideoProcessing\ThirdParty\strmbas;C:\Users\Admin\IFloor\libs\opencv\build\include;%(AdditionalIncludeDirectories) + + + C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\mswu;C:\Users\Admin\IFloor\libs\wxWidgets\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\Utils;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\MotionDetectorCore;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxJSON\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxxmlserializer\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxxmlserializer;C:\Users\Admin\IFloor\projects\wxVideoProcessing\MotionDetectorMultiCam;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\MotionDetectorPluginBase;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\wxGuiPluginBase;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\CommonPluginBase;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxXS\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\MotionPrimitives;C:\Users\Admin\IFloor\projects\wxVideoProcessing\ThirdParty\wxXS\build\..\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\ThirdParty\wxJSON\build\..\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\ThirdParty\videoInput;C:\Users\Admin\IFloor\projects\wxVideoProcessing\ThirdParty\videoInput\..\strmbas;C:\Users\Admin\IFloor\projects\wxVideoProcessing\ThirdParty\strmbas;C:\Users\Admin\IFloor\libs\opencv\build\include;%(AdditionalIncludeDirectories) + $(ProjectDir)/$(IntDir) + %(Filename).h + %(Filename).tlb + %(Filename)_i.c + %(Filename)_p.c + + + + setlocal +"C:\Program Files\CMake\bin\cmake.exe" -E make_directory C:/Users/Admin/IFloor/projects/wxVideoProcessing/build/../bin +if %errorlevel% neq 0 goto :cmEnd +"C:\Program Files\CMake\bin\cmake.exe" -E copy_if_different C:/Users/Admin/IFloor/projects/wxVideoProcessing/bin/MotionDetectorMultiCam.dll C:/Users/Admin/IFloor/projects/wxVideoProcessing/build/../bin/MotionDetectorMultiCam.dll +if %errorlevel% neq 0 goto :cmEnd +:cmEnd +endlocal & call :cmErrorLevel %errorlevel% & goto :cmDone +:cmErrorLevel +exit /b %1 +:cmDone +if %errorlevel% neq 0 goto :VCEnd + + + C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxbase33u.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33u_core.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33u_adv.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33u_aui.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxbase33u_net.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33u_gl.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxbase33u_xml.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33u_propgrid.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33u_html.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxpng.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxtiff.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxjpeg.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxzlib.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxregexu.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxexpat.lib;opengl32.lib;glu32.lib;winmm.lib;comctl32.lib;uuid.lib;oleacc.lib;uxtheme.lib;rpcrt4.lib;shlwapi.lib;version.lib;wsock32.lib;C:\Users\Admin\IFloor\projects\wxVideoProcessing\bin\lib\Release\Utils.lib;C:\Users\Admin\IFloor\projects\wxVideoProcessing\bin\lib\Release\MotionDetectorCore.lib;C:\Users\Admin\IFloor\projects\wxVideoProcessing\bin\lib\Release\MotionPrimitives.lib;C:\Users\Admin\IFloor\projects\wxVideoProcessing\bin\lib\Release\videoInput.lib;C:\Users\Admin\IFloor\libs\opencv\build\x64\vc16\lib\opencv_world4120.lib;C:\Users\Admin\IFloor\libs\opencv\build\x64\vc16\lib\opencv_world4120.lib;C:\Users\Admin\IFloor\libs\opencv\build\x64\vc16\lib\opencv_world4120.lib;C:\Users\Admin\IFloor\libs\opencv\build\x64\vc16\lib\opencv_world4120.lib;C:\Users\Admin\IFloor\libs\opencv\build\x64\vc16\lib\opencv_world4120.lib;C:\Users\Admin\IFloor\libs\opencv\build\x64\vc16\lib\opencv_world4120.lib;C:\Users\Admin\IFloor\libs\opencv\build\x64\vc16\lib\opencv_world4120.lib;C:\Users\Admin\IFloor\libs\opencv\build\x64\vc16\lib\opencv_world4120.lib;C:\Users\Admin\IFloor\libs\opencv\build\x64\vc16\lib\opencv_world4120.lib;C:\Users\Admin\IFloor\libs\opencv\build\x64\vc16\lib\opencv_world4120.lib;C:\Users\Admin\IFloor\libs\opencv\build\x64\vc16\lib\opencv_world4120.lib;C:\Users\Admin\IFloor\libs\opencv\build\x64\vc16\lib\opencv_world4120.lib;C:\Users\Admin\IFloor\libs\opencv\build\x64\vc16\lib\opencv_world4120.lib;C:\Users\Admin\IFloor\libs\opencv\build\x64\vc16\lib\opencv_world4120.lib;C:\Users\Admin\IFloor\libs\opencv\build\x64\vc16\lib\opencv_world4120.lib;C:\Users\Admin\IFloor\libs\opencv\build\x64\vc16\lib\opencv_world4120.lib;C:\Users\Admin\IFloor\projects\wxVideoProcessing\bin\lib\Release\MotionDetectorPluginBase.lib;C:\Users\Admin\IFloor\projects\wxVideoProcessing\bin\lib\Release\CommonPluginBase.lib;C:\Users\Admin\IFloor\projects\wxVideoProcessing\bin\lib\Release\Utils.lib;C:\Users\Admin\IFloor\projects\wxVideoProcessing\bin\lib\Release\wxXS.lib;C:\Users\Admin\IFloor\projects\wxVideoProcessing\bin\lib\Release\wxJSON.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxbase33u.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33u_core.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33u_adv.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33u_aui.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxbase33u_net.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33u_gl.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxbase33u_xml.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33u_propgrid.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33u_html.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxpng.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxtiff.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxjpeg.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxzlib.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxregexu.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxexpat.lib;opengl32.lib;glu32.lib;winmm.lib;comctl32.lib;uuid.lib;oleacc.lib;uxtheme.lib;rpcrt4.lib;shlwapi.lib;version.lib;wsock32.lib;C:\Users\Admin\IFloor\projects\wxVideoProcessing\bin\lib\Release\strmbas.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;comdlg32.lib;advapi32.lib + C:/Users/Admin/IFloor/projects/wxVideoProcessing/build/Win/lib;C:/Users/Admin/IFloor/projects/wxVideoProcessing/build/Win/lib/$(Configuration);%(AdditionalLibraryDirectories) + %(AdditionalOptions) /machine:x64 + + false + %(IgnoreSpecificDefaultLibraries) + + C:/Users/Admin/IFloor/projects/wxVideoProcessing/bin/lib/Release/MotionDetectorMultiCam.lib + + C:/Users/Admin/IFloor/projects/wxVideoProcessing/bin/MotionDetectorMultiCam.pdb + + Console + + + false + + + + + Always + Building Custom Rule C:/Users/Admin/IFloor/projects/wxVideoProcessing/MotionDetectorMultiCam/CMakeLists.txt + setlocal +"C:\Program Files\CMake\bin\cmake.exe" -SC:/Users/Admin/IFloor/projects/wxVideoProcessing/build -BC:/Users/Admin/IFloor/projects/wxVideoProcessing/build/Win --check-stamp-file C:/Users/Admin/IFloor/projects/wxVideoProcessing/MotionDetectorMultiCam/Win/CMakeFiles/generate.stamp +if %errorlevel% neq 0 goto :cmEnd +:cmEnd +endlocal & call :cmErrorLevel %errorlevel% & goto :cmDone +:cmErrorLevel +exit /b %1 +:cmDone +if %errorlevel% neq 0 goto :VCEnd + %(AdditionalInputs) + C:\Users\Admin\IFloor\projects\wxVideoProcessing\MotionDetectorMultiCam\Win\CMakeFiles\generate.stamp + false + Building Custom Rule C:/Users/Admin/IFloor/projects/wxVideoProcessing/MotionDetectorMultiCam/CMakeLists.txt + setlocal +"C:\Program Files\CMake\bin\cmake.exe" -SC:/Users/Admin/IFloor/projects/wxVideoProcessing/build -BC:/Users/Admin/IFloor/projects/wxVideoProcessing/build/Win --check-stamp-file C:/Users/Admin/IFloor/projects/wxVideoProcessing/MotionDetectorMultiCam/Win/CMakeFiles/generate.stamp +if %errorlevel% neq 0 goto :cmEnd +:cmEnd +endlocal & call :cmErrorLevel %errorlevel% & goto :cmDone +:cmErrorLevel +exit /b %1 +:cmDone +if %errorlevel% neq 0 goto :VCEnd + %(AdditionalInputs) + C:\Users\Admin\IFloor\projects\wxVideoProcessing\MotionDetectorMultiCam\Win\CMakeFiles\generate.stamp + false + + + + + C:/Users/Admin/IFloor/projects/wxVideoProcessing/MotionDetectorMultiCam/Win/CMakeFiles/MotionDetectorMultiCam.dir/Debug/cmake_pch.hxx;%(ForcedIncludeFiles) + Create + C:/Users/Admin/IFloor/projects/wxVideoProcessing/MotionDetectorMultiCam/Win/CMakeFiles/MotionDetectorMultiCam.dir/Debug/cmake_pch.hxx + C:/Users/Admin/IFloor/projects/wxVideoProcessing/MotionDetectorMultiCam/Win/MotionDetectorMultiCam.dir/Debug//cmake_pch.pch + C:/Users/Admin/IFloor/projects/wxVideoProcessing/MotionDetectorMultiCam/Win/CMakeFiles/MotionDetectorMultiCam.dir/Release/cmake_pch.hxx;%(ForcedIncludeFiles) + Create + C:/Users/Admin/IFloor/projects/wxVideoProcessing/MotionDetectorMultiCam/Win/CMakeFiles/MotionDetectorMultiCam.dir/Release/cmake_pch.hxx + C:/Users/Admin/IFloor/projects/wxVideoProcessing/MotionDetectorMultiCam/Win/MotionDetectorMultiCam.dir/Release//cmake_pch.pch + + + C:/Users/Admin/IFloor/projects/wxVideoProcessing/MotionDetectorMultiCam/Win/CMakeFiles/MotionDetectorMultiCam.dir/Debug/cmake_pch.hxx;%(ForcedIncludeFiles) + C:/Users/Admin/IFloor/projects/wxVideoProcessing/MotionDetectorMultiCam/Win/CMakeFiles/MotionDetectorMultiCam.dir/Release/cmake_pch.hxx;%(ForcedIncludeFiles) + + + C:/Users/Admin/IFloor/projects/wxVideoProcessing/MotionDetectorMultiCam/Win/CMakeFiles/MotionDetectorMultiCam.dir/Debug/cmake_pch.hxx;%(ForcedIncludeFiles) + C:/Users/Admin/IFloor/projects/wxVideoProcessing/MotionDetectorMultiCam/Win/CMakeFiles/MotionDetectorMultiCam.dir/Release/cmake_pch.hxx;%(ForcedIncludeFiles) + + + C:/Users/Admin/IFloor/projects/wxVideoProcessing/MotionDetectorMultiCam/Win/CMakeFiles/MotionDetectorMultiCam.dir/Debug/cmake_pch.hxx;%(ForcedIncludeFiles) + C:/Users/Admin/IFloor/projects/wxVideoProcessing/MotionDetectorMultiCam/Win/CMakeFiles/MotionDetectorMultiCam.dir/Release/cmake_pch.hxx;%(ForcedIncludeFiles) + + + C:/Users/Admin/IFloor/projects/wxVideoProcessing/MotionDetectorMultiCam/Win/CMakeFiles/MotionDetectorMultiCam.dir/Debug/cmake_pch.hxx;%(ForcedIncludeFiles) + C:/Users/Admin/IFloor/projects/wxVideoProcessing/MotionDetectorMultiCam/Win/CMakeFiles/MotionDetectorMultiCam.dir/Release/cmake_pch.hxx;%(ForcedIncludeFiles) + + + + C:/Users/Admin/IFloor/projects/wxVideoProcessing/MotionDetectorMultiCam/Win/CMakeFiles/MotionDetectorMultiCam.dir/Debug/cmake_pch.hxx;%(ForcedIncludeFiles) + C:/Users/Admin/IFloor/projects/wxVideoProcessing/MotionDetectorMultiCam/Win/CMakeFiles/MotionDetectorMultiCam.dir/Release/cmake_pch.hxx;%(ForcedIncludeFiles) + + + + + + + + {6F4BDD20-5B99-3029-BDF0-7D719B283AF5} + ZERO_CHECK + false + Never + + + {751A15C0-76C2-3506-AB9D-0CD3545003FA} + CommonPluginBase + + + {6E933546-0B62-311B-8D7E-6CE2ECD4CAD0} + MotionDetectorCore + + + {1F4B7EAD-4694-3453-AD34-1757E387661E} + MotionDetectorPluginBase + + + {1903244B-9312-3A09-8AAD-8D78C1A4F977} + MotionPrimitives + + + {14D82890-BB7F-3748-8713-43304E91CFA1} + Utils + + + {23CF91DE-A1B5-3EE0-827D-ACAFD80C7CB2} + strmbas + + + {007E33A1-3B69-3DBB-8C44-8A54188A3A21} + videoInput + + + {7887B55C-77B9-3262-AFAD-ABA738C61029} + wxJSON + + + {78CFCEBE-5466-3128-9717-547416DBFFE5} + wxXS + + + + + + \ No newline at end of file diff --git a/MotionDetectorMultiCam/Win/MotionDetectorMultiCam.vcxproj.filters b/MotionDetectorMultiCam/Win/MotionDetectorMultiCam.vcxproj.filters new file mode 100644 index 0000000..9b250a5 --- /dev/null +++ b/MotionDetectorMultiCam/Win/MotionDetectorMultiCam.vcxproj.filters @@ -0,0 +1,48 @@ + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Header Files + + + Precompile Header File + + + Precompile Header File + + + + + + + + {4951AEE1-75F2-3C38-B593-C6BC8C809FB0} + + + {112D4227-BB51-3360-BB5B-5EA5DBA5A65F} + + + {05450AB2-CB7B-34EA-83DF-1EE86424EDF0} + + + diff --git a/MotionDetectorMultiCam/Win/MotionDetectorMultiCamGui.vcxproj b/MotionDetectorMultiCam/Win/MotionDetectorMultiCamGui.vcxproj new file mode 100644 index 0000000..8196d36 --- /dev/null +++ b/MotionDetectorMultiCam/Win/MotionDetectorMultiCamGui.vcxproj @@ -0,0 +1,325 @@ + + + + x64 + + + + Debug + x64 + + + Release + x64 + + + + {38ABD20B-B450-3DE7-A759-71587CAB9A2A} + Win32Proj + 10.0.26100.0 + x64 + MotionDetectorMultiCamGui + NoUpgrade + + + + DynamicLibrary + Unicode + v143 + + + DynamicLibrary + Unicode + v143 + + + + + + + + + + <_ProjectFileVersion>10.0.20506.1 + C:\Users\Admin\IFloor\projects\wxVideoProcessing\bin\ + MotionDetectorMultiCamGui.dir\Debug\ + MotionDetectorMultiCamGui + .dll + true + true + C:\Users\Admin\IFloor\projects\wxVideoProcessing\bin\ + MotionDetectorMultiCamGui.dir\Release\ + MotionDetectorMultiCamGui + .dll + false + true + + + + + C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\mswu;C:\Users\Admin\IFloor\libs\wxWidgets\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\Utils;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\MotionDetectorCore;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxJSON\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxxmlserializer\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxxmlserializer;C:\Users\Admin\IFloor\projects\wxVideoProcessing\MotionDetectorMultiCam;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\MotionDetectorPluginBase;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\wxGuiPluginBase;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\CommonPluginBase;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxXS\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\MotionPrimitives;C:\Users\Admin\IFloor\projects\wxVideoProcessing\ThirdParty\wxXS\build\..\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\ThirdParty\wxJSON\build\..\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\ThirdParty\videoInput;C:\Users\Admin\IFloor\projects\wxVideoProcessing\ThirdParty\videoInput\..\strmbas;C:\Users\Admin\IFloor\projects\wxVideoProcessing\ThirdParty\strmbas;%(AdditionalIncludeDirectories) + %(AdditionalOptions) /external:I "C:/Users/Admin/IFloor/libs/opencv/build/include" + $(IntDir) + EnableFastChecks + + + ProgramDatabase + 4996 + Sync + TurnOffAllWarnings + + + Disabled + stdcpp17 + + true + Disabled + Use + C:/Users/Admin/IFloor/projects/wxVideoProcessing/MotionDetectorMultiCam/Win/CMakeFiles/MotionDetectorMultiCamGui.dir/Debug/cmake_pch.hxx + C:/Users/Admin/IFloor/projects/wxVideoProcessing/MotionDetectorMultiCam/Win/MotionDetectorMultiCamGui.dir/Debug//cmake_pch.pch + + MultiThreadedDebugDLL + true + + + false + %(PreprocessorDefinitions);WIN32;_WINDOWS;__WXDEBUG__=1;WIN32_LEAN_AND_MEAN;WXUSINGDLL;UNICODE;_UNICODE;_CRT_SECURE_NO_DEPRECATE;_USRDLL;_WIN32_WINNT=0x0501;CMAKE_INTDIR="Debug";MotionDetectorMultiCamGui_EXPORTS + $(IntDir) + false + + + %(PreprocessorDefinitions);WIN32;_DEBUG;_WINDOWS;__WXDEBUG__=1;WIN32_LEAN_AND_MEAN;WXUSINGDLL;UNICODE;_UNICODE;_CRT_SECURE_NO_DEPRECATE;_USRDLL;_WIN32_WINNT=0x0501;CMAKE_INTDIR=\"Debug\";MotionDetectorMultiCamGui_EXPORTS + C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\mswu;C:\Users\Admin\IFloor\libs\wxWidgets\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\Utils;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\MotionDetectorCore;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxJSON\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxxmlserializer\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxxmlserializer;C:\Users\Admin\IFloor\projects\wxVideoProcessing\MotionDetectorMultiCam;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\MotionDetectorPluginBase;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\wxGuiPluginBase;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\CommonPluginBase;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxXS\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\MotionPrimitives;C:\Users\Admin\IFloor\projects\wxVideoProcessing\ThirdParty\wxXS\build\..\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\ThirdParty\wxJSON\build\..\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\ThirdParty\videoInput;C:\Users\Admin\IFloor\projects\wxVideoProcessing\ThirdParty\videoInput\..\strmbas;C:\Users\Admin\IFloor\projects\wxVideoProcessing\ThirdParty\strmbas;C:\Users\Admin\IFloor\libs\opencv\build\include;%(AdditionalIncludeDirectories) + + + C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\mswu;C:\Users\Admin\IFloor\libs\wxWidgets\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\Utils;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\MotionDetectorCore;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxJSON\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxxmlserializer\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxxmlserializer;C:\Users\Admin\IFloor\projects\wxVideoProcessing\MotionDetectorMultiCam;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\MotionDetectorPluginBase;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\wxGuiPluginBase;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\CommonPluginBase;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxXS\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\MotionPrimitives;C:\Users\Admin\IFloor\projects\wxVideoProcessing\ThirdParty\wxXS\build\..\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\ThirdParty\wxJSON\build\..\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\ThirdParty\videoInput;C:\Users\Admin\IFloor\projects\wxVideoProcessing\ThirdParty\videoInput\..\strmbas;C:\Users\Admin\IFloor\projects\wxVideoProcessing\ThirdParty\strmbas;C:\Users\Admin\IFloor\libs\opencv\build\include;%(AdditionalIncludeDirectories) + $(ProjectDir)/$(IntDir) + %(Filename).h + %(Filename).tlb + %(Filename)_i.c + %(Filename)_p.c + + + + setlocal +"C:\Program Files\CMake\bin\cmake.exe" -E make_directory C:/Users/Admin/IFloor/projects/wxVideoProcessing/build/../bin/plugins/gui +if %errorlevel% neq 0 goto :cmEnd +"C:\Program Files\CMake\bin\cmake.exe" -E copy_if_different C:/Users/Admin/IFloor/projects/wxVideoProcessing/bin/MotionDetectorMultiCamGui.dll C:/Users/Admin/IFloor/projects/wxVideoProcessing/build/../bin/plugins/gui/MotionDetectorMultiCamGui.dll +if %errorlevel% neq 0 goto :cmEnd +:cmEnd +endlocal & call :cmErrorLevel %errorlevel% & goto :cmDone +:cmErrorLevel +exit /b %1 +:cmDone +if %errorlevel% neq 0 goto :VCEnd + + + C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxbase33ud.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33ud_core.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33ud_adv.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33ud_aui.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxbase33ud_net.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33ud_gl.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxbase33ud_xml.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33ud_propgrid.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33ud_html.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxpngd.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxtiffd.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxjpegd.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxzlibd.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxregexud.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxexpatd.lib;opengl32.lib;glu32.lib;winmm.lib;comctl32.lib;uuid.lib;oleacc.lib;uxtheme.lib;rpcrt4.lib;shlwapi.lib;version.lib;wsock32.lib;C:\Users\Admin\IFloor\projects\wxVideoProcessing\bin\lib\Debug\MotionDetectorMultiCam.lib;C:\Users\Admin\IFloor\projects\wxVideoProcessing\bin\lib\Debug\Utils.lib;C:\Users\Admin\IFloor\projects\wxVideoProcessing\bin\lib\Debug\wxGuiPluginBase.lib;C:\Users\Admin\IFloor\projects\wxVideoProcessing\bin\lib\Debug\MotionDetectorCore.lib;C:\Users\Admin\IFloor\projects\wxVideoProcessing\bin\lib\Debug\MotionDetectorPluginBase.lib;C:\Users\Admin\IFloor\projects\wxVideoProcessing\bin\lib\Debug\CommonPluginBase.lib;C:\Users\Admin\IFloor\projects\wxVideoProcessing\bin\lib\Debug\Utils.lib;C:\Users\Admin\IFloor\projects\wxVideoProcessing\bin\lib\Debug\wxXS.lib;C:\Users\Admin\IFloor\projects\wxVideoProcessing\bin\lib\Debug\wxJSON.lib;C:\Users\Admin\IFloor\projects\wxVideoProcessing\bin\lib\Debug\MotionPrimitives.lib;C:\Users\Admin\IFloor\projects\wxVideoProcessing\bin\lib\Debug\videoInput.lib;C:\Users\Admin\IFloor\projects\wxVideoProcessing\bin\lib\Debug\strmbas.lib;C:\Users\Admin\IFloor\libs\opencv\build\x64\vc16\lib\opencv_world4120d.lib;C:\Users\Admin\IFloor\libs\opencv\build\x64\vc16\lib\opencv_world4120d.lib;C:\Users\Admin\IFloor\libs\opencv\build\x64\vc16\lib\opencv_world4120d.lib;C:\Users\Admin\IFloor\libs\opencv\build\x64\vc16\lib\opencv_world4120d.lib;C:\Users\Admin\IFloor\libs\opencv\build\x64\vc16\lib\opencv_world4120d.lib;C:\Users\Admin\IFloor\libs\opencv\build\x64\vc16\lib\opencv_world4120d.lib;C:\Users\Admin\IFloor\libs\opencv\build\x64\vc16\lib\opencv_world4120d.lib;C:\Users\Admin\IFloor\libs\opencv\build\x64\vc16\lib\opencv_world4120d.lib;C:\Users\Admin\IFloor\libs\opencv\build\x64\vc16\lib\opencv_world4120d.lib;C:\Users\Admin\IFloor\libs\opencv\build\x64\vc16\lib\opencv_world4120d.lib;C:\Users\Admin\IFloor\libs\opencv\build\x64\vc16\lib\opencv_world4120d.lib;C:\Users\Admin\IFloor\libs\opencv\build\x64\vc16\lib\opencv_world4120d.lib;C:\Users\Admin\IFloor\libs\opencv\build\x64\vc16\lib\opencv_world4120d.lib;C:\Users\Admin\IFloor\libs\opencv\build\x64\vc16\lib\opencv_world4120d.lib;C:\Users\Admin\IFloor\libs\opencv\build\x64\vc16\lib\opencv_world4120d.lib;C:\Users\Admin\IFloor\libs\opencv\build\x64\vc16\lib\opencv_world4120d.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxbase33ud.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33ud_core.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33ud_adv.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33ud_aui.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxbase33ud_net.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33ud_gl.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxbase33ud_xml.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33ud_propgrid.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33ud_html.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxpngd.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxtiffd.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxjpegd.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxzlibd.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxregexud.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxexpatd.lib;opengl32.lib;glu32.lib;winmm.lib;comctl32.lib;uuid.lib;oleacc.lib;uxtheme.lib;rpcrt4.lib;shlwapi.lib;version.lib;wsock32.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;comdlg32.lib;advapi32.lib + C:/Users/Admin/IFloor/projects/wxVideoProcessing/build/Win/lib;C:/Users/Admin/IFloor/projects/wxVideoProcessing/build/Win/lib/$(Configuration);%(AdditionalLibraryDirectories) + %(AdditionalOptions) /machine:x64 + + true + %(IgnoreSpecificDefaultLibraries) + + C:/Users/Admin/IFloor/projects/wxVideoProcessing/bin/lib/Debug/MotionDetectorMultiCamGui.lib + + C:/Users/Admin/IFloor/projects/wxVideoProcessing/bin/MotionDetectorMultiCamGui.pdb + + Console + + + false + + + + + C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\mswu;C:\Users\Admin\IFloor\libs\wxWidgets\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\Utils;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\MotionDetectorCore;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxJSON\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxxmlserializer\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxxmlserializer;C:\Users\Admin\IFloor\projects\wxVideoProcessing\MotionDetectorMultiCam;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\MotionDetectorPluginBase;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\wxGuiPluginBase;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\CommonPluginBase;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxXS\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\MotionPrimitives;C:\Users\Admin\IFloor\projects\wxVideoProcessing\ThirdParty\wxXS\build\..\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\ThirdParty\wxJSON\build\..\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\ThirdParty\videoInput;C:\Users\Admin\IFloor\projects\wxVideoProcessing\ThirdParty\videoInput\..\strmbas;C:\Users\Admin\IFloor\projects\wxVideoProcessing\ThirdParty\strmbas;%(AdditionalIncludeDirectories) + %(AdditionalOptions) /external:I "C:/Users/Admin/IFloor/libs/opencv/build/include" + $(IntDir) + Default + + + 4996 + Sync + TurnOffAllWarnings + + + AnySuitable + stdcpp17 + + true + MaxSpeed + Use + C:/Users/Admin/IFloor/projects/wxVideoProcessing/MotionDetectorMultiCam/Win/CMakeFiles/MotionDetectorMultiCamGui.dir/Release/cmake_pch.hxx + C:/Users/Admin/IFloor/projects/wxVideoProcessing/MotionDetectorMultiCam/Win/MotionDetectorMultiCamGui.dir/Release//cmake_pch.pch + + MultiThreadedDLL + true + + + false + %(PreprocessorDefinitions);WIN32;_WINDOWS;NDEBUG;WIN32_LEAN_AND_MEAN;WXUSINGDLL;UNICODE;_UNICODE;_CRT_SECURE_NO_DEPRECATE;_USRDLL;_WIN32_WINNT=0x0501;CMAKE_INTDIR="Release";MotionDetectorMultiCamGui_EXPORTS + $(IntDir) + + + false + + + %(PreprocessorDefinitions);WIN32;_WINDOWS;NDEBUG;WIN32_LEAN_AND_MEAN;WXUSINGDLL;UNICODE;_UNICODE;_CRT_SECURE_NO_DEPRECATE;_USRDLL;_WIN32_WINNT=0x0501;CMAKE_INTDIR=\"Release\";MotionDetectorMultiCamGui_EXPORTS + C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\mswu;C:\Users\Admin\IFloor\libs\wxWidgets\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\Utils;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\MotionDetectorCore;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxJSON\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxxmlserializer\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxxmlserializer;C:\Users\Admin\IFloor\projects\wxVideoProcessing\MotionDetectorMultiCam;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\MotionDetectorPluginBase;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\wxGuiPluginBase;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\CommonPluginBase;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxXS\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\MotionPrimitives;C:\Users\Admin\IFloor\projects\wxVideoProcessing\ThirdParty\wxXS\build\..\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\ThirdParty\wxJSON\build\..\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\ThirdParty\videoInput;C:\Users\Admin\IFloor\projects\wxVideoProcessing\ThirdParty\videoInput\..\strmbas;C:\Users\Admin\IFloor\projects\wxVideoProcessing\ThirdParty\strmbas;C:\Users\Admin\IFloor\libs\opencv\build\include;%(AdditionalIncludeDirectories) + + + C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\mswu;C:\Users\Admin\IFloor\libs\wxWidgets\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\Utils;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\MotionDetectorCore;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxJSON\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxxmlserializer\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxxmlserializer;C:\Users\Admin\IFloor\projects\wxVideoProcessing\MotionDetectorMultiCam;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\MotionDetectorPluginBase;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\wxGuiPluginBase;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\CommonPluginBase;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxXS\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\MotionPrimitives;C:\Users\Admin\IFloor\projects\wxVideoProcessing\ThirdParty\wxXS\build\..\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\ThirdParty\wxJSON\build\..\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\ThirdParty\videoInput;C:\Users\Admin\IFloor\projects\wxVideoProcessing\ThirdParty\videoInput\..\strmbas;C:\Users\Admin\IFloor\projects\wxVideoProcessing\ThirdParty\strmbas;C:\Users\Admin\IFloor\libs\opencv\build\include;%(AdditionalIncludeDirectories) + $(ProjectDir)/$(IntDir) + %(Filename).h + %(Filename).tlb + %(Filename)_i.c + %(Filename)_p.c + + + + setlocal +"C:\Program Files\CMake\bin\cmake.exe" -E make_directory C:/Users/Admin/IFloor/projects/wxVideoProcessing/build/../bin/plugins/gui +if %errorlevel% neq 0 goto :cmEnd +"C:\Program Files\CMake\bin\cmake.exe" -E copy_if_different C:/Users/Admin/IFloor/projects/wxVideoProcessing/bin/MotionDetectorMultiCamGui.dll C:/Users/Admin/IFloor/projects/wxVideoProcessing/build/../bin/plugins/gui/MotionDetectorMultiCamGui.dll +if %errorlevel% neq 0 goto :cmEnd +:cmEnd +endlocal & call :cmErrorLevel %errorlevel% & goto :cmDone +:cmErrorLevel +exit /b %1 +:cmDone +if %errorlevel% neq 0 goto :VCEnd + + + C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxbase33u.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33u_core.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33u_adv.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33u_aui.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxbase33u_net.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33u_gl.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxbase33u_xml.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33u_propgrid.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33u_html.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxpng.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxtiff.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxjpeg.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxzlib.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxregexu.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxexpat.lib;opengl32.lib;glu32.lib;winmm.lib;comctl32.lib;uuid.lib;oleacc.lib;uxtheme.lib;rpcrt4.lib;shlwapi.lib;version.lib;wsock32.lib;C:\Users\Admin\IFloor\projects\wxVideoProcessing\bin\lib\Release\MotionDetectorMultiCam.lib;C:\Users\Admin\IFloor\projects\wxVideoProcessing\bin\lib\Release\Utils.lib;C:\Users\Admin\IFloor\projects\wxVideoProcessing\bin\lib\Release\wxGuiPluginBase.lib;C:\Users\Admin\IFloor\projects\wxVideoProcessing\bin\lib\Release\MotionDetectorCore.lib;C:\Users\Admin\IFloor\projects\wxVideoProcessing\bin\lib\Release\MotionDetectorPluginBase.lib;C:\Users\Admin\IFloor\projects\wxVideoProcessing\bin\lib\Release\CommonPluginBase.lib;C:\Users\Admin\IFloor\projects\wxVideoProcessing\bin\lib\Release\Utils.lib;C:\Users\Admin\IFloor\projects\wxVideoProcessing\bin\lib\Release\wxXS.lib;C:\Users\Admin\IFloor\projects\wxVideoProcessing\bin\lib\Release\wxJSON.lib;C:\Users\Admin\IFloor\projects\wxVideoProcessing\bin\lib\Release\MotionPrimitives.lib;C:\Users\Admin\IFloor\projects\wxVideoProcessing\bin\lib\Release\videoInput.lib;C:\Users\Admin\IFloor\projects\wxVideoProcessing\bin\lib\Release\strmbas.lib;C:\Users\Admin\IFloor\libs\opencv\build\x64\vc16\lib\opencv_world4120.lib;C:\Users\Admin\IFloor\libs\opencv\build\x64\vc16\lib\opencv_world4120.lib;C:\Users\Admin\IFloor\libs\opencv\build\x64\vc16\lib\opencv_world4120.lib;C:\Users\Admin\IFloor\libs\opencv\build\x64\vc16\lib\opencv_world4120.lib;C:\Users\Admin\IFloor\libs\opencv\build\x64\vc16\lib\opencv_world4120.lib;C:\Users\Admin\IFloor\libs\opencv\build\x64\vc16\lib\opencv_world4120.lib;C:\Users\Admin\IFloor\libs\opencv\build\x64\vc16\lib\opencv_world4120.lib;C:\Users\Admin\IFloor\libs\opencv\build\x64\vc16\lib\opencv_world4120.lib;C:\Users\Admin\IFloor\libs\opencv\build\x64\vc16\lib\opencv_world4120.lib;C:\Users\Admin\IFloor\libs\opencv\build\x64\vc16\lib\opencv_world4120.lib;C:\Users\Admin\IFloor\libs\opencv\build\x64\vc16\lib\opencv_world4120.lib;C:\Users\Admin\IFloor\libs\opencv\build\x64\vc16\lib\opencv_world4120.lib;C:\Users\Admin\IFloor\libs\opencv\build\x64\vc16\lib\opencv_world4120.lib;C:\Users\Admin\IFloor\libs\opencv\build\x64\vc16\lib\opencv_world4120.lib;C:\Users\Admin\IFloor\libs\opencv\build\x64\vc16\lib\opencv_world4120.lib;C:\Users\Admin\IFloor\libs\opencv\build\x64\vc16\lib\opencv_world4120.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxbase33u.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33u_core.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33u_adv.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33u_aui.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxbase33u_net.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33u_gl.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxbase33u_xml.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33u_propgrid.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33u_html.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxpng.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxtiff.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxjpeg.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxzlib.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxregexu.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxexpat.lib;opengl32.lib;glu32.lib;winmm.lib;comctl32.lib;uuid.lib;oleacc.lib;uxtheme.lib;rpcrt4.lib;shlwapi.lib;version.lib;wsock32.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;comdlg32.lib;advapi32.lib + C:/Users/Admin/IFloor/projects/wxVideoProcessing/build/Win/lib;C:/Users/Admin/IFloor/projects/wxVideoProcessing/build/Win/lib/$(Configuration);%(AdditionalLibraryDirectories) + %(AdditionalOptions) /machine:x64 + + false + %(IgnoreSpecificDefaultLibraries) + + C:/Users/Admin/IFloor/projects/wxVideoProcessing/bin/lib/Release/MotionDetectorMultiCamGui.lib + + C:/Users/Admin/IFloor/projects/wxVideoProcessing/bin/MotionDetectorMultiCamGui.pdb + + Console + + + false + + + + + Always + Building Custom Rule C:/Users/Admin/IFloor/projects/wxVideoProcessing/MotionDetectorMultiCam/CMakeLists.txt + setlocal +"C:\Program Files\CMake\bin\cmake.exe" -SC:/Users/Admin/IFloor/projects/wxVideoProcessing/build -BC:/Users/Admin/IFloor/projects/wxVideoProcessing/build/Win --check-stamp-file C:/Users/Admin/IFloor/projects/wxVideoProcessing/MotionDetectorMultiCam/Win/CMakeFiles/generate.stamp +if %errorlevel% neq 0 goto :cmEnd +:cmEnd +endlocal & call :cmErrorLevel %errorlevel% & goto :cmDone +:cmErrorLevel +exit /b %1 +:cmDone +if %errorlevel% neq 0 goto :VCEnd + %(AdditionalInputs) + C:\Users\Admin\IFloor\projects\wxVideoProcessing\MotionDetectorMultiCam\Win\CMakeFiles\generate.stamp + false + Building Custom Rule C:/Users/Admin/IFloor/projects/wxVideoProcessing/MotionDetectorMultiCam/CMakeLists.txt + setlocal +"C:\Program Files\CMake\bin\cmake.exe" -SC:/Users/Admin/IFloor/projects/wxVideoProcessing/build -BC:/Users/Admin/IFloor/projects/wxVideoProcessing/build/Win --check-stamp-file C:/Users/Admin/IFloor/projects/wxVideoProcessing/MotionDetectorMultiCam/Win/CMakeFiles/generate.stamp +if %errorlevel% neq 0 goto :cmEnd +:cmEnd +endlocal & call :cmErrorLevel %errorlevel% & goto :cmDone +:cmErrorLevel +exit /b %1 +:cmDone +if %errorlevel% neq 0 goto :VCEnd + %(AdditionalInputs) + C:\Users\Admin\IFloor\projects\wxVideoProcessing\MotionDetectorMultiCam\Win\CMakeFiles\generate.stamp + false + + + + + C:/Users/Admin/IFloor/projects/wxVideoProcessing/MotionDetectorMultiCam/Win/CMakeFiles/MotionDetectorMultiCamGui.dir/Debug/cmake_pch.hxx;%(ForcedIncludeFiles) + Create + C:/Users/Admin/IFloor/projects/wxVideoProcessing/MotionDetectorMultiCam/Win/CMakeFiles/MotionDetectorMultiCamGui.dir/Debug/cmake_pch.hxx + C:/Users/Admin/IFloor/projects/wxVideoProcessing/MotionDetectorMultiCam/Win/MotionDetectorMultiCamGui.dir/Debug//cmake_pch.pch + C:/Users/Admin/IFloor/projects/wxVideoProcessing/MotionDetectorMultiCam/Win/CMakeFiles/MotionDetectorMultiCamGui.dir/Release/cmake_pch.hxx;%(ForcedIncludeFiles) + Create + C:/Users/Admin/IFloor/projects/wxVideoProcessing/MotionDetectorMultiCam/Win/CMakeFiles/MotionDetectorMultiCamGui.dir/Release/cmake_pch.hxx + C:/Users/Admin/IFloor/projects/wxVideoProcessing/MotionDetectorMultiCam/Win/MotionDetectorMultiCamGui.dir/Release//cmake_pch.pch + + + C:/Users/Admin/IFloor/projects/wxVideoProcessing/MotionDetectorMultiCam/Win/CMakeFiles/MotionDetectorMultiCamGui.dir/Debug/cmake_pch.hxx;%(ForcedIncludeFiles) + C:/Users/Admin/IFloor/projects/wxVideoProcessing/MotionDetectorMultiCam/Win/CMakeFiles/MotionDetectorMultiCamGui.dir/Release/cmake_pch.hxx;%(ForcedIncludeFiles) + + + C:/Users/Admin/IFloor/projects/wxVideoProcessing/MotionDetectorMultiCam/Win/CMakeFiles/MotionDetectorMultiCamGui.dir/Debug/cmake_pch.hxx;%(ForcedIncludeFiles) + C:/Users/Admin/IFloor/projects/wxVideoProcessing/MotionDetectorMultiCam/Win/CMakeFiles/MotionDetectorMultiCamGui.dir/Release/cmake_pch.hxx;%(ForcedIncludeFiles) + + + + C:/Users/Admin/IFloor/projects/wxVideoProcessing/MotionDetectorMultiCam/Win/CMakeFiles/MotionDetectorMultiCamGui.dir/Debug/cmake_pch.hxx;%(ForcedIncludeFiles) + C:/Users/Admin/IFloor/projects/wxVideoProcessing/MotionDetectorMultiCam/Win/CMakeFiles/MotionDetectorMultiCamGui.dir/Release/cmake_pch.hxx;%(ForcedIncludeFiles) + + + + + + + + {6F4BDD20-5B99-3029-BDF0-7D719B283AF5} + ZERO_CHECK + false + Never + + + {751A15C0-76C2-3506-AB9D-0CD3545003FA} + CommonPluginBase + + + {6E933546-0B62-311B-8D7E-6CE2ECD4CAD0} + MotionDetectorCore + + + {4877C470-9466-37C0-A451-03973F6EDABD} + MotionDetectorMultiCam + + + {1F4B7EAD-4694-3453-AD34-1757E387661E} + MotionDetectorPluginBase + + + {1903244B-9312-3A09-8AAD-8D78C1A4F977} + MotionPrimitives + + + {14D82890-BB7F-3748-8713-43304E91CFA1} + Utils + + + {23CF91DE-A1B5-3EE0-827D-ACAFD80C7CB2} + strmbas + + + {007E33A1-3B69-3DBB-8C44-8A54188A3A21} + videoInput + + + {016D40A8-D2BA-3C2B-8741-718BD5CE7B51} + wxGuiPluginBase + + + {7887B55C-77B9-3262-AFAD-ABA738C61029} + wxJSON + + + {78CFCEBE-5466-3128-9717-547416DBFFE5} + wxXS + + + + + + \ No newline at end of file diff --git a/MotionDetectorMultiCam/Win/MotionDetectorMultiCamGui.vcxproj.filters b/MotionDetectorMultiCam/Win/MotionDetectorMultiCamGui.vcxproj.filters new file mode 100644 index 0000000..ba68f63 --- /dev/null +++ b/MotionDetectorMultiCam/Win/MotionDetectorMultiCamGui.vcxproj.filters @@ -0,0 +1,42 @@ + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Header Files + + + Precompile Header File + + + Precompile Header File + + + + + + + + {4951AEE1-75F2-3C38-B593-C6BC8C809FB0} + + + {112D4227-BB51-3360-BB5B-5EA5DBA5A65F} + + + {05450AB2-CB7B-34EA-83DF-1EE86424EDF0} + + + diff --git a/MotionDetectorPluginBase/CMakeLists.txt b/MotionDetectorPluginBase/CMakeLists.txt new file mode 100644 index 0000000..fc44787 --- /dev/null +++ b/MotionDetectorPluginBase/CMakeLists.txt @@ -0,0 +1,86 @@ +# +# Stuff the average programmer needs to change +# +set(SRCS + GeometryProvider.cpp + MotionDetectorPluginBase.cpp + MotionDetectorBase.cpp + MotionDetectorConfigWindowBase.cpp + IFloorCameraSettings.cpp +) +set(HFILES + GeometryProvider.h + MotionDetectorPluginBase.h + MotionDetectorBase.h + MotionDetectorConfigWindowBase.h + MotionDetectorPlugin.h + IFloorCameraSettings.h +) +set(INCLUDE_DIRECTORIES ${BASE_INCLUDE_DIRECTORIES} + ${THIRD_PARTY_DIR}/wxXS/include + ${THIRD_PARTY_DIR}/wxJSON/include + ${THIRD_PARTY_DIR}/MotionPrimitives + ${PROJECT_ROOT_DIR}/CommonPluginBase + ${PROJECT_ROOT_DIR}/Utils +) +set(LIBRARY_NAME MotionDetectorPluginBase) +if(WIN32) + set(LINK_DIRECTORIES + ${PROJECT_ROOT_DIR}/CommonPluginBase/${OS_BASE_NAME}${LIB_SUFFIX}/$(ConfigurationName) + ${PROJECT_ROOT_DIR}/Utils/${OS_BASE_NAME}${LIB_SUFFIX}/$(ConfigurationName) + ${THIRD_PARTY_DIR}/wxXS/build/${OS_BASE_NAME}${LIB_SUFFIX}/$(ConfigurationName) + ${THIRD_PARTY_DIR}/wxJSON/build/${OS_BASE_NAME}${LIB_SUFFIX}/$(ConfigurationName) + ) + set(PREPROCESSOR_DEFINITIONS ${PREPROCESSOR_DEFINITIONS};/D_USRDLL;/DIFLOOR_EXPORTS) + set(IFLOOR_LIBS CommonPluginBase.lib Utils.lib) + set(THIRDPARTY_LIBS wxXS.lib wxJSON.lib) +endif(WIN32) +if(LINUX) + set(LINK_DIRECTORIES + ${PROJECT_ROOT_DIR}/CommonPluginBase/${OS_BASE_NAME}${LIB_SUFFIX} + ${PROJECT_ROOT_DIR}/Utils/${OS_BASE_NAME}${LIB_SUFFIX} + ${THIRD_PARTY_DIR}/wxXS/build/${OS_BASE_NAME}${LIB_SUFFIX} + ${THIRD_PARTY_DIR}/wxJSON/build/${OS_BASE_NAME}${LIB_SUFFIX} + ) + #set(PREPROCESSOR_DEFINITIONS ${PREPROCESSOR_DEFINITIONS};/D_USRDLL;/DIFLOOR_EXPORTS) + set(IFLOOR_LIBS CommonPluginBase Utils) + set(THIRDPARTY_LIBS wxXS wxJSON) +endif(LINUX) +set(SRCS ${SRCS} ${HFILES} ${PROJECT_ROOT_DIR}/include/stdwx.h ${PROJECT_ROOT_DIR}/include/stdwx.cpp) + +add_definitions(${PREPROCESSOR_DEFINITIONS}) +include_directories(${INCLUDE_DIRECTORIES}) +link_directories(${LINK_DIRECTORIES}) +add_library(${LIBRARY_NAME} SHARED ${SRCS}) + +set(DLL_DIR bin) +set(TARGET_LOCATION ${PROJECT_SOURCE_DIR}/${DLL_DIR}${LIB_SUFFIX}) +set_target_properties(${LIBRARY_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${TARGET_LOCATION}) + +target_link_libraries(${LIBRARY_NAME} + CommonPluginBase + Utils + wxXS + wxJSON + ${wxWidgets_LIBRARIES} +) +add_dependencies(${LIBRARY_NAME} + wxXS + CommonPluginBase +) + +# Precompiled header stuff must be after the target is added +#set_precompiled_header(${LIBRARY_NAME} ${PROJECT_ROOT_DIR}/include/stdwx.h ${PROJECT_ROOT_DIR}/include/stdwx.cpp) +target_precompile_headers(${LIBRARY_NAME} + PRIVATE + "${PROJECT_ROOT_DIR}/include/stdwx.h" +) + + +if(LINUX) + add_custom_command(TARGET ${LIBRARY_NAME} POST_BUILD + COMMAND ${CMAKE_COMMAND} ARGS -E copy_if_different "${CMAKE_CURRENT_SOURCE_DIR}/${OS_BASE_NAME}${LIB_SUFFIX}/lib${LIBRARY_NAME}.so" "${PROJECT_SOURCE_DIR}/${DLL_DIR}${LIB_SUFFIX}/lib${LIBRARY_NAME}.so" + ) +endif(LINUX) + +add_dependencies(MotionDetectorPluginBase wxXS) diff --git a/MotionDetectorPluginBase/GeometryProvider.cpp b/MotionDetectorPluginBase/GeometryProvider.cpp new file mode 100644 index 0000000..47de8db --- /dev/null +++ b/MotionDetectorPluginBase/GeometryProvider.cpp @@ -0,0 +1,28 @@ +#include "stdwx.h" +#include "GeometryProvider.h" + +GeometryProvider::GeometryProvider( + GeometryProvider::GetSupportedCameraCountCallback getSupportedCameraCountCallback + , GeometryProvider::GetGeometryCallback getGeometryCallback + , GetDisplaySizeCallback getDisplaySizeCallback) + : m_GetSupportedCameraCountCallback(getSupportedCameraCountCallback) + , m_GetGeometryCallback(getGeometryCallback) + , m_GetDisplaySizeCallback(getDisplaySizeCallback) +{ + +} + +size_t GeometryProvider::GetSupportedCameraCount() const +{ + return m_GetSupportedCameraCountCallback(); +} + +void GeometryProvider::GetGeometry(wxRectVector & result) const +{ + m_GetGeometryCallback(result); +} + +wxRect GeometryProvider::GetDisplaySize() const +{ + return m_GetDisplaySizeCallback(); +} \ No newline at end of file diff --git a/MotionDetectorPluginBase/GeometryProvider.h b/MotionDetectorPluginBase/GeometryProvider.h new file mode 100644 index 0000000..7477121 --- /dev/null +++ b/MotionDetectorPluginBase/GeometryProvider.h @@ -0,0 +1,26 @@ +#pragma once + +#include "MotionDetectorPlugin.h" + +typedef std::vector wxRectVector; + +class IFLOOR_API GeometryProvider +{ + + typedef std::function GetSupportedCameraCountCallback; + typedef std::function GetGeometryCallback; + typedef std::function GetDisplaySizeCallback; +public: + GeometryProvider(GetSupportedCameraCountCallback getSupportedCameraCountCallback, + GetGeometryCallback getGeometryCallback, + GetDisplaySizeCallback getDisplaySizeCallback); + + size_t GetSupportedCameraCount() const; + void GetGeometry(wxRectVector & result) const; + wxRect GetDisplaySize() const; + +private: + GetSupportedCameraCountCallback m_GetSupportedCameraCountCallback; + GetGeometryCallback m_GetGeometryCallback; + GetDisplaySizeCallback m_GetDisplaySizeCallback; +}; \ No newline at end of file diff --git a/MotionDetectorPluginBase/IFloorCameraSettings.cpp b/MotionDetectorPluginBase/IFloorCameraSettings.cpp new file mode 100644 index 0000000..78b41da --- /dev/null +++ b/MotionDetectorPluginBase/IFloorCameraSettings.cpp @@ -0,0 +1,91 @@ +#include "stdwx.h" +#include "IFloorCameraSettings.h" + +IMPLEMENT_DYNAMIC_CLASS(IFloorCameraSettings, xsSerializable) + +#include +WX_DEFINE_LIST(IFloorCameraSettingsList); + +IFloorCameraSettings::IFloorCameraSettings(void) +: bEnabled(false) +, TopLeftX(0), TopLeftY(0), TopRightX(639), TopRightY(0) +, BottomLeftX(0), BottomLeftY(479), BottomRightX(639), BottomRightY(479) +, WinWidth(640), WinHeight(480) +, CamWidth(640), CamHeight(480) +, bVerticalMirror(false) +, bHorizontalMirror(false) +{ + InitSerialization(); +} + +IFloorCameraSettings::IFloorCameraSettings(const IFloorCameraSettings & settings) +{ + InitSerialization(); + CopyFrom(settings); +} + +void IFloorCameraSettings::InitSerialization() +{ + XS_SERIALIZE(bEnabled, wxT("bEnabled")); + + XS_SERIALIZE(TopLeftX, wxT("TopLeftX")); + XS_SERIALIZE(TopLeftY, wxT("TopLeftY")); + XS_SERIALIZE(TopRightX, wxT("TopRightX")); + XS_SERIALIZE(TopRightY, wxT("TopRightY")); + XS_SERIALIZE(BottomLeftX, wxT("BottomLeftX")); + XS_SERIALIZE(BottomLeftY, wxT("BottomLeftY")); + XS_SERIALIZE(BottomRightX, wxT("BottomRightX")); + XS_SERIALIZE(BottomRightY, wxT("BottomRightY")); + XS_SERIALIZE(WinWidth, wxT("WinWidth")); + XS_SERIALIZE(WinHeight, wxT("WinHeight")); + XS_SERIALIZE(CamWidth, wxT("CamWidth")); + XS_SERIALIZE(CamHeight, wxT("CamHeight")); + + XS_SERIALIZE(bVerticalMirror, wxT("bVerticalMirror")); + XS_SERIALIZE(bHorizontalMirror, wxT("bHorizontalMirror")); + + XS_SERIALIZE(DeviceID, wxT("DeviceID")); + XS_SERIALIZE(VideoSourceID, wxT("VideoSourceID")); +} + +IFloorCameraSettings& IFloorCameraSettings::operator=(const IFloorCameraSettings & settings) +{ + if (&settings != this) + CopyFrom(settings); + return *this; +} + +IFloorCameraSettings::~IFloorCameraSettings(void) +{ +} + +bool IFloorCameraSettings::Deserialize(wxInputStream & instream) +{ + return SerializableBase::Deserialize(instream, *this); +} + +bool IFloorCameraSettings::Serialize(wxOutputStream & outstream) +{ + return SerializableBase::Serialize(outstream, *this); +} + +void IFloorCameraSettings::CopyFrom(const IFloorCameraSettings& settings) +{ + bEnabled = settings.bEnabled; + TopLeftX = settings.TopLeftX; + TopLeftY = settings.TopLeftY; + TopRightX = settings.TopRightX; + TopRightY = settings.TopRightY; + BottomLeftX = settings.BottomLeftX; + BottomLeftY = settings.BottomLeftY; + BottomRightX = settings.BottomRightX; + BottomRightY = settings.BottomRightY; + WinWidth = settings.WinWidth; + WinHeight = settings.WinHeight; + CamWidth = settings.CamWidth; + CamHeight = settings.CamHeight; + bVerticalMirror = settings.bVerticalMirror; + bHorizontalMirror = settings.bHorizontalMirror; + VideoSourceID = settings.VideoSourceID; + DeviceID = settings.DeviceID; +} \ No newline at end of file diff --git a/MotionDetectorPluginBase/IFloorCameraSettings.h b/MotionDetectorPluginBase/IFloorCameraSettings.h new file mode 100644 index 0000000..286e622 --- /dev/null +++ b/MotionDetectorPluginBase/IFloorCameraSettings.h @@ -0,0 +1,51 @@ +#ifndef _IFLOORCAMERASETTINGS_H +#define _IFLOORCAMERASETTINGS_H + +#include "MotionDetectorPlugin.h" +#include + +class IFLOOR_API IFloorCameraSettings : public xsSerializable +{ + DECLARE_DYNAMIC_CLASS(IFloorCameraSettings) +public: + IFloorCameraSettings(void); + IFloorCameraSettings(const IFloorCameraSettings & settings); + IFloorCameraSettings& operator=(const IFloorCameraSettings & settings); + + ~IFloorCameraSettings(void); + + bool Deserialize(wxInputStream & instream); + bool Serialize(wxOutputStream & outstream); + +private: + void InitSerialization(); + void CopyFrom(const IFloorCameraSettings & settings); + +public: + bool bEnabled; // Are these settings enabled + + int + TopLeftX, + TopLeftY, + TopRightX, + TopRightY, + BottomLeftX, + BottomLeftY, + BottomRightX, + BottomRightY, + WinWidth, + WinHeight, + CamWidth, + CamHeight; + bool + bVerticalMirror, + bHorizontalMirror; + + wxString + VideoSourceID, + DeviceID; +}; + +WX_DECLARE_USER_EXPORTED_LIST(IFloorCameraSettings, IFloorCameraSettingsList, IFLOOR_API); + +#endif // _IFLOORCAMERASETTINGS_H diff --git a/MotionDetectorPluginBase/MotionDetectorBase.cpp b/MotionDetectorPluginBase/MotionDetectorBase.cpp new file mode 100644 index 0000000..ad37d52 --- /dev/null +++ b/MotionDetectorPluginBase/MotionDetectorBase.cpp @@ -0,0 +1,109 @@ +#include "stdwx.h" +#include "MotionDetectorBase.h" +#include "MotionDetectorPluginBase.h" +#include "GeometryProvider.h" +#include +#include +#include + +#include + +#if defined(USE_VLD) +#include +#endif + +IMPLEMENT_ABSTRACT_CLASS(MotionDetectorBase, SerializableBase); + +WX_DEFINE_USER_EXPORTED_LIST(MotionDetectorBaseList); + +MotionDetectorBase::MotionDetectorBase() +: m_MotionDetector(nullptr) +, m_CalibrationStep(0) +, m_CalibrationCameraID(0) +, m_bShowCalibrationBlobs(false) +, m_bServiceMode(false) +, m_GeometryProvider(std::move(GeometryProvider(nullptr, nullptr, nullptr))) +{ +} + +MotionDetectorBase::MotionDetectorBase(MotionDetectorPluginBase * owner, GeometryProvider && geometryProvider) +: m_MotionDetector(owner) +, m_GeometryProvider(std::move(geometryProvider)) +, m_CalibrationCameraID(0) +, m_bServiceMode(false) +{ +} + +MotionDetectorBase::~MotionDetectorBase() +{ +} + +void MotionDetectorBase::SetServiceMode(bool mode) +{ + m_bServiceMode = mode; +} + +MotionDetectorPluginBase * MotionDetectorBase::GetMotionDetector() +{ + return m_MotionDetector; +} + +bool MotionDetectorBase::StartCalibration() +{ + return true; +} + +bool MotionDetectorBase::StopCalibration() +{ + return true; +} + +void MotionDetectorBase::ConfimCalibrationStep(int code) +{ + +} + +bool MotionDetectorBase::IsNextStepCalibrationEnabled() +{ + return true; +} + +void MotionDetectorBase::SetCalibrationCameraID(const int index) +{ + m_CalibrationCameraID = index; +} + +void MotionDetectorBase::StartDetection() +{ + +} + +void MotionDetectorBase::StopDetection() +{ + +} + +int MotionDetectorBase::GetBlobs(iFloorBlobVector * blobs) +{ + return 0; +} + +void MotionDetectorBase::SetCameraSettings(const IFloorCameraSettings & settings, const int index) +{ + +} + +bool MotionDetectorBase::IsServiceMode() +{ + return m_bServiceMode; +} + +size_t MotionDetectorBase::GetSupportedCamerasCount() +{ + return m_GeometryProvider.GetSupportedCameraCount(); +} + +const GeometryProvider & MotionDetectorBase::GetGeometryProvider() const +{ + return m_GeometryProvider; +} diff --git a/MotionDetectorPluginBase/MotionDetectorBase.h b/MotionDetectorPluginBase/MotionDetectorBase.h new file mode 100644 index 0000000..9367bb3 --- /dev/null +++ b/MotionDetectorPluginBase/MotionDetectorBase.h @@ -0,0 +1,68 @@ +#ifndef _MOTIONDETECTORBASE_H +#define _MOTIONDETECTORBASE_H + +#include "MotionDetectorPlugin.h" +#include +#include +#include + +typedef std::map MotionDetectorParametersMap; + +class MotionDetectorPluginBase; +class IFloorCameraSettings; +class MotionDetectorBase; +class GeometryProvider; + +class IFLOOR_API MotionDetectorBase : public SerializableBase +{ + + DECLARE_ABSTRACT_CLASS(MotionDetectorBase); +public: + MotionDetectorBase(); + MotionDetectorBase(MotionDetectorPluginBase * owner, GeometryProvider && geometryProvider); + virtual ~MotionDetectorBase(); + virtual void SetServiceMode(bool mode = true); +//Calibration API + virtual bool Calibrate() = 0; + virtual void SetCalibrationCameraID(const int index); + + virtual bool StartCalibration(); + virtual bool StopCalibration(); + unsigned int GetCalibrationStep() {return m_CalibrationStep;} + virtual void ConfimCalibrationStep(int code); + virtual bool IsNextStepCalibrationEnabled(); + bool IsCalibrationBlobsShowed(){return m_bShowCalibrationBlobs;} +//Common detection API + virtual void StartDetection(); + virtual void StopDetection(); + virtual void SetDataBuffer(unsigned char * buffer, const size_t length, const int index) = 0; + virtual void ProcessData() = 0; + // Returns how many cameras supports this detector + virtual size_t GetSupportedCamerasCount(); + virtual int GetBlobs(iFloorBlobVector * blobs); + virtual void SetCameraSettings(const IFloorCameraSettings & settings, const int index); + const GeometryProvider & GetGeometryProvider() const; + + //Service mode control + bool IsServiceMode(); + bool IsJSONmsgsMode(); + bool IsSendBlobsJmsgMode(); + MotionDetectorPluginBase * GetMotionDetector(); + +protected: + MotionDetectorPluginBase * m_MotionDetector; + GeometryProvider && m_GeometryProvider; + unsigned int m_CalibrationStep; + int m_CalibrationCameraID; + bool m_bShowCalibrationBlobs; + bool m_bServiceMode; +}; + + + +WX_DECLARE_USER_EXPORTED_LIST(MotionDetectorBase, MotionDetectorBaseList, IFLOOR_API); + +typedef MotionDetectorPluginBase * (*CreateMotionDetectorPlugin_function)(); +typedef void (*DeleteMotionDetectorPlugin_function)(MotionDetectorPluginBase * plugin); + +#endif // _MOTIONDETECTORBASE_H \ No newline at end of file diff --git a/MotionDetectorPluginBase/MotionDetectorConfigWindowBase.cpp b/MotionDetectorPluginBase/MotionDetectorConfigWindowBase.cpp new file mode 100644 index 0000000..3575f3e --- /dev/null +++ b/MotionDetectorPluginBase/MotionDetectorConfigWindowBase.cpp @@ -0,0 +1,50 @@ +#include "stdwx.h" +#include "MotionDetectorConfigWindowBase.h" + +IMPLEMENT_DYNAMIC_CLASS(MotionDetectorConfigWindowBase, CommonConfigWindowBase) + + +MotionDetectorConfigWindowBase::MotionDetectorConfigWindowBase() +: m_Detector(NULL), m_Started(false) +{ +} + +MotionDetectorConfigWindowBase::MotionDetectorConfigWindowBase(MotionDetectorBase * detector, wxWindow * parent) +{ + Create(detector, parent); +} + +bool MotionDetectorConfigWindowBase::Create(MotionDetectorBase * detector, wxWindow * parent) +{ + m_Detector = detector; + m_Started = false; + return CommonConfigWindowBase::Create(parent); +} + +MotionDetectorConfigWindowBase::~MotionDetectorConfigWindowBase(void) +{ +} + +bool MotionDetectorConfigWindowBase::ReadConfig() +{ + return false; +} + +bool MotionDetectorConfigWindowBase::SaveConfig() +{ + return false; +} + +void MotionDetectorConfigWindowBase::StartDetection() +{ + m_Started = true; +} +void MotionDetectorConfigWindowBase::StopDetection() +{ + m_Started = false; +} + +MotionDetectorBase * MotionDetectorConfigWindowBase::GetDetector() +{ + return m_Detector; +} \ No newline at end of file diff --git a/MotionDetectorPluginBase/MotionDetectorConfigWindowBase.h b/MotionDetectorPluginBase/MotionDetectorConfigWindowBase.h new file mode 100644 index 0000000..6bd3793 --- /dev/null +++ b/MotionDetectorPluginBase/MotionDetectorConfigWindowBase.h @@ -0,0 +1,32 @@ +#ifndef _MOTIONDETECTORCONFIGWINDOWBASE_H +#define _MOTIONDETECTORCONFIGWINDOWBASE_H +#include "MotionDetectorPlugin.h" +#include + +class MotionDetectorBase; + +class IFLOOR_API MotionDetectorConfigWindowBase : public CommonConfigWindowBase +{ + DECLARE_DYNAMIC_CLASS(MotionDetectorConfigWindowBase) +public: + MotionDetectorConfigWindowBase(); + MotionDetectorConfigWindowBase(MotionDetectorBase * detector, wxWindow * parent); + + bool Create(MotionDetectorBase * detector, wxWindow * parent); + virtual ~MotionDetectorConfigWindowBase(void); + + /// Reads config from the motion detector + virtual bool ReadConfig(); + + /// Saves config to the motion detector + virtual bool SaveConfig(); + + virtual MotionDetectorBase * GetDetector(); + virtual void StartDetection(); + virtual void StopDetection(); + +protected: + MotionDetectorBase * m_Detector; + bool m_Started; +}; +#endif // _MOTIONDETECTORCONFIGWINDOWBASE_H diff --git a/MotionDetectorPluginBase/MotionDetectorPlugin.h b/MotionDetectorPluginBase/MotionDetectorPlugin.h new file mode 100644 index 0000000..b9426cf --- /dev/null +++ b/MotionDetectorPluginBase/MotionDetectorPlugin.h @@ -0,0 +1,14 @@ +#ifndef _MOTIONDETECTORPLUGIN_H +#define _MOTIONDETECTORPLUGIN_H + +#if defined(__WXMSW__) +#ifdef IFLOOR_EXPORTS +#define IFLOOR_API __declspec(dllexport) +#else +#define IFLOOR_API __declspec(dllimport) +#endif +#else +#define IFLOOR_API +#endif + +#endif // _MOTIONDETECTORPLUGIN_H diff --git a/MotionDetectorPluginBase/MotionDetectorPluginBase.cpp b/MotionDetectorPluginBase/MotionDetectorPluginBase.cpp new file mode 100644 index 0000000..0d74c2c --- /dev/null +++ b/MotionDetectorPluginBase/MotionDetectorPluginBase.cpp @@ -0,0 +1,14 @@ +#include "stdwx.h" +#include "MotionDetectorPluginBase.h" +#include "MotionDetectorBase.h" + +IMPLEMENT_ABSTRACT_CLASS(MotionDetectorPluginBase, wxObject) + +MotionDetectorPluginBase::~MotionDetectorPluginBase() +{ +} + +void MotionDetectorPluginBase::DeleteDetector(MotionDetectorBase * detector) +{ + wxDELETE(detector); +} diff --git a/MotionDetectorPluginBase/MotionDetectorPluginBase.h b/MotionDetectorPluginBase/MotionDetectorPluginBase.h new file mode 100644 index 0000000..f32ee36 --- /dev/null +++ b/MotionDetectorPluginBase/MotionDetectorPluginBase.h @@ -0,0 +1,28 @@ +#ifndef _MOTIONDETECTORPLUGINBASE_H +#define _MOTIONDETECTORPLUGINBASE_H + +#include "MotionDetectorPlugin.h" + +class MotionDetectorBase; +class GeometryProvider; + +/// Base class for all iFloor effect plugins +class IFLOOR_API MotionDetectorPluginBase : public wxObject +{ + DECLARE_ABSTRACT_CLASS(MotionDetectorPluginBase) +public: + virtual ~MotionDetectorPluginBase(); + /// Returns GUID (unique identifier) of plugin + /// \return string which contains unique identifier of plugin + virtual wxString GetID() const = 0; + /// Returns name of plugin + /// \return string which contains human-readable name of plugin + virtual wxString GetName() const = 0; + virtual MotionDetectorBase * CreateDetector(GeometryProvider && geometryProvider) = 0; + virtual void DeleteDetector(MotionDetectorBase * detector); +}; + +typedef MotionDetectorPluginBase * (*CreateMotionDetectorPlugin_function)(); +typedef void (*DeleteMotionDetectorPlugin_function)(MotionDetectorPluginBase * plugin); + +#endif // _MOTIONDETECTORPLUGINBASE_H \ No newline at end of file diff --git a/MotionDetectorPluginBase/Win/MotionDetectorPluginBase.vcxproj b/MotionDetectorPluginBase/Win/MotionDetectorPluginBase.vcxproj new file mode 100644 index 0000000..8ba7969 --- /dev/null +++ b/MotionDetectorPluginBase/Win/MotionDetectorPluginBase.vcxproj @@ -0,0 +1,283 @@ + + + + x64 + + + + Debug + x64 + + + Release + x64 + + + + {1F4B7EAD-4694-3453-AD34-1757E387661E} + Win32Proj + 10.0.26100.0 + x64 + MotionDetectorPluginBase + NoUpgrade + + + + DynamicLibrary + Unicode + v143 + + + DynamicLibrary + Unicode + v143 + + + + + + + + + + <_ProjectFileVersion>10.0.20506.1 + C:\Users\Admin\IFloor\projects\wxVideoProcessing\bin\ + MotionDetectorPluginBase.dir\Debug\ + MotionDetectorPluginBase + .dll + true + true + C:\Users\Admin\IFloor\projects\wxVideoProcessing\bin\ + MotionDetectorPluginBase.dir\Release\ + MotionDetectorPluginBase + .dll + false + true + + + + + C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\mswu;C:\Users\Admin\IFloor\libs\wxWidgets\include;C:\Users\Admin\IFloor\libs\opencv\build\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\Utils;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\MotionDetectorCore;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxJSON\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxxmlserializer\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxxmlserializer;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxXS\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\MotionPrimitives;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\CommonPluginBase;C:\Users\Admin\IFloor\projects\wxVideoProcessing\ThirdParty\wxXS\build\..\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\ThirdParty\wxJSON\build\..\include;%(AdditionalIncludeDirectories) + $(IntDir) + EnableFastChecks + + + ProgramDatabase + 4996 + Sync + + + Disabled + stdcpp17 + + true + Disabled + Use + C:/Users/Admin/IFloor/projects/wxVideoProcessing/MotionDetectorPluginBase/Win/CMakeFiles/MotionDetectorPluginBase.dir/Debug/cmake_pch.hxx + C:/Users/Admin/IFloor/projects/wxVideoProcessing/MotionDetectorPluginBase/Win/MotionDetectorPluginBase.dir/Debug//cmake_pch.pch + + MultiThreadedDebugDLL + true + + + false + %(PreprocessorDefinitions);WIN32;_WINDOWS;__WXDEBUG__=1;WIN32_LEAN_AND_MEAN;WXUSINGDLL;UNICODE;_UNICODE;_CRT_SECURE_NO_DEPRECATE;_USRDLL;IFLOOR_EXPORTS;CMAKE_INTDIR="Debug";MotionDetectorPluginBase_EXPORTS + $(IntDir) + false + + + %(PreprocessorDefinitions);WIN32;_DEBUG;_WINDOWS;__WXDEBUG__=1;WIN32_LEAN_AND_MEAN;WXUSINGDLL;UNICODE;_UNICODE;_CRT_SECURE_NO_DEPRECATE;_USRDLL;IFLOOR_EXPORTS;CMAKE_INTDIR=\"Debug\";MotionDetectorPluginBase_EXPORTS + C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\mswu;C:\Users\Admin\IFloor\libs\wxWidgets\include;C:\Users\Admin\IFloor\libs\opencv\build\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\Utils;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\MotionDetectorCore;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxJSON\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxxmlserializer\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxxmlserializer;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxXS\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\MotionPrimitives;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\CommonPluginBase;C:\Users\Admin\IFloor\projects\wxVideoProcessing\ThirdParty\wxXS\build\..\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\ThirdParty\wxJSON\build\..\include;%(AdditionalIncludeDirectories) + + + C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\mswu;C:\Users\Admin\IFloor\libs\wxWidgets\include;C:\Users\Admin\IFloor\libs\opencv\build\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\Utils;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\MotionDetectorCore;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxJSON\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxxmlserializer\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxxmlserializer;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxXS\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\MotionPrimitives;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\CommonPluginBase;C:\Users\Admin\IFloor\projects\wxVideoProcessing\ThirdParty\wxXS\build\..\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\ThirdParty\wxJSON\build\..\include;%(AdditionalIncludeDirectories) + $(ProjectDir)/$(IntDir) + %(Filename).h + %(Filename).tlb + %(Filename)_i.c + %(Filename)_p.c + + + C:\Users\Admin\IFloor\projects\wxVideoProcessing\bin\lib\Debug\CommonPluginBase.lib;C:\Users\Admin\IFloor\projects\wxVideoProcessing\bin\lib\Debug\Utils.lib;C:\Users\Admin\IFloor\projects\wxVideoProcessing\bin\lib\Debug\wxXS.lib;C:\Users\Admin\IFloor\projects\wxVideoProcessing\bin\lib\Debug\wxJSON.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxbase33ud.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33ud_core.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33ud_adv.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33ud_aui.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxbase33ud_net.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33ud_gl.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxbase33ud_xml.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33ud_propgrid.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33ud_html.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxpngd.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxtiffd.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxjpegd.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxzlibd.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxregexud.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxexpatd.lib;opengl32.lib;glu32.lib;winmm.lib;comctl32.lib;uuid.lib;oleacc.lib;uxtheme.lib;rpcrt4.lib;shlwapi.lib;version.lib;wsock32.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;comdlg32.lib;advapi32.lib + C:/Users/Admin/IFloor/projects/wxVideoProcessing/build/Win/lib;C:/Users/Admin/IFloor/projects/wxVideoProcessing/build/Win/lib/$(Configuration);C:/Users/Admin/IFloor/projects/wxVideoProcessing/build/../CommonPluginBase/Win/$(ConfigurationName);C:/Users/Admin/IFloor/projects/wxVideoProcessing/build/../CommonPluginBase/Win/$(ConfigurationName)/$(Configuration);C:/Users/Admin/IFloor/projects/wxVideoProcessing/build/../Utils/Win/$(ConfigurationName);C:/Users/Admin/IFloor/projects/wxVideoProcessing/build/../Utils/Win/$(ConfigurationName)/$(Configuration);C:/Users/Admin/IFloor/projects/wxVideoProcessing/build/../ThirdParty/wxXS/build/Win/$(ConfigurationName);C:/Users/Admin/IFloor/projects/wxVideoProcessing/build/../ThirdParty/wxXS/build/Win/$(ConfigurationName)/$(Configuration);C:/Users/Admin/IFloor/projects/wxVideoProcessing/build/../ThirdParty/wxJSON/build/Win/$(ConfigurationName);C:/Users/Admin/IFloor/projects/wxVideoProcessing/build/../ThirdParty/wxJSON/build/Win/$(ConfigurationName)/$(Configuration);%(AdditionalLibraryDirectories) + %(AdditionalOptions) /machine:x64 + + true + %(IgnoreSpecificDefaultLibraries) + + C:/Users/Admin/IFloor/projects/wxVideoProcessing/bin/lib/Debug/MotionDetectorPluginBase.lib + + C:/Users/Admin/IFloor/projects/wxVideoProcessing/bin/MotionDetectorPluginBase.pdb + + Console + + + false + + + + + C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\mswu;C:\Users\Admin\IFloor\libs\wxWidgets\include;C:\Users\Admin\IFloor\libs\opencv\build\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\Utils;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\MotionDetectorCore;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxJSON\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxxmlserializer\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxxmlserializer;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxXS\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\MotionPrimitives;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\CommonPluginBase;C:\Users\Admin\IFloor\projects\wxVideoProcessing\ThirdParty\wxXS\build\..\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\ThirdParty\wxJSON\build\..\include;%(AdditionalIncludeDirectories) + $(IntDir) + Default + + + 4996 + Sync + + + AnySuitable + stdcpp17 + + true + MaxSpeed + Use + C:/Users/Admin/IFloor/projects/wxVideoProcessing/MotionDetectorPluginBase/Win/CMakeFiles/MotionDetectorPluginBase.dir/Release/cmake_pch.hxx + C:/Users/Admin/IFloor/projects/wxVideoProcessing/MotionDetectorPluginBase/Win/MotionDetectorPluginBase.dir/Release//cmake_pch.pch + + MultiThreadedDLL + true + + + false + %(PreprocessorDefinitions);WIN32;_WINDOWS;NDEBUG;WIN32_LEAN_AND_MEAN;WXUSINGDLL;UNICODE;_UNICODE;_CRT_SECURE_NO_DEPRECATE;_USRDLL;IFLOOR_EXPORTS;CMAKE_INTDIR="Release";MotionDetectorPluginBase_EXPORTS + $(IntDir) + + + false + + + %(PreprocessorDefinitions);WIN32;_WINDOWS;NDEBUG;WIN32_LEAN_AND_MEAN;WXUSINGDLL;UNICODE;_UNICODE;_CRT_SECURE_NO_DEPRECATE;_USRDLL;IFLOOR_EXPORTS;CMAKE_INTDIR=\"Release\";MotionDetectorPluginBase_EXPORTS + C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\mswu;C:\Users\Admin\IFloor\libs\wxWidgets\include;C:\Users\Admin\IFloor\libs\opencv\build\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\Utils;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\MotionDetectorCore;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxJSON\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxxmlserializer\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxxmlserializer;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxXS\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\MotionPrimitives;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\CommonPluginBase;C:\Users\Admin\IFloor\projects\wxVideoProcessing\ThirdParty\wxXS\build\..\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\ThirdParty\wxJSON\build\..\include;%(AdditionalIncludeDirectories) + + + C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\mswu;C:\Users\Admin\IFloor\libs\wxWidgets\include;C:\Users\Admin\IFloor\libs\opencv\build\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\Utils;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\MotionDetectorCore;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxJSON\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxxmlserializer\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxxmlserializer;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxXS\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\MotionPrimitives;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\CommonPluginBase;C:\Users\Admin\IFloor\projects\wxVideoProcessing\ThirdParty\wxXS\build\..\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\ThirdParty\wxJSON\build\..\include;%(AdditionalIncludeDirectories) + $(ProjectDir)/$(IntDir) + %(Filename).h + %(Filename).tlb + %(Filename)_i.c + %(Filename)_p.c + + + C:\Users\Admin\IFloor\projects\wxVideoProcessing\bin\lib\Release\CommonPluginBase.lib;C:\Users\Admin\IFloor\projects\wxVideoProcessing\bin\lib\Release\Utils.lib;C:\Users\Admin\IFloor\projects\wxVideoProcessing\bin\lib\Release\wxXS.lib;C:\Users\Admin\IFloor\projects\wxVideoProcessing\bin\lib\Release\wxJSON.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxbase33u.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33u_core.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33u_adv.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33u_aui.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxbase33u_net.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33u_gl.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxbase33u_xml.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33u_propgrid.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33u_html.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxpng.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxtiff.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxjpeg.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxzlib.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxregexu.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxexpat.lib;opengl32.lib;glu32.lib;winmm.lib;comctl32.lib;uuid.lib;oleacc.lib;uxtheme.lib;rpcrt4.lib;shlwapi.lib;version.lib;wsock32.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;comdlg32.lib;advapi32.lib + C:/Users/Admin/IFloor/projects/wxVideoProcessing/build/Win/lib;C:/Users/Admin/IFloor/projects/wxVideoProcessing/build/Win/lib/$(Configuration);C:/Users/Admin/IFloor/projects/wxVideoProcessing/build/../CommonPluginBase/Win/$(ConfigurationName);C:/Users/Admin/IFloor/projects/wxVideoProcessing/build/../CommonPluginBase/Win/$(ConfigurationName)/$(Configuration);C:/Users/Admin/IFloor/projects/wxVideoProcessing/build/../Utils/Win/$(ConfigurationName);C:/Users/Admin/IFloor/projects/wxVideoProcessing/build/../Utils/Win/$(ConfigurationName)/$(Configuration);C:/Users/Admin/IFloor/projects/wxVideoProcessing/build/../ThirdParty/wxXS/build/Win/$(ConfigurationName);C:/Users/Admin/IFloor/projects/wxVideoProcessing/build/../ThirdParty/wxXS/build/Win/$(ConfigurationName)/$(Configuration);C:/Users/Admin/IFloor/projects/wxVideoProcessing/build/../ThirdParty/wxJSON/build/Win/$(ConfigurationName);C:/Users/Admin/IFloor/projects/wxVideoProcessing/build/../ThirdParty/wxJSON/build/Win/$(ConfigurationName)/$(Configuration);%(AdditionalLibraryDirectories) + %(AdditionalOptions) /machine:x64 + + false + %(IgnoreSpecificDefaultLibraries) + + C:/Users/Admin/IFloor/projects/wxVideoProcessing/bin/lib/Release/MotionDetectorPluginBase.lib + + C:/Users/Admin/IFloor/projects/wxVideoProcessing/bin/MotionDetectorPluginBase.pdb + + Console + + + false + + + + + Always + Building Custom Rule C:/Users/Admin/IFloor/projects/wxVideoProcessing/MotionDetectorPluginBase/CMakeLists.txt + setlocal +"C:\Program Files\CMake\bin\cmake.exe" -SC:/Users/Admin/IFloor/projects/wxVideoProcessing/build -BC:/Users/Admin/IFloor/projects/wxVideoProcessing/build/Win --check-stamp-file C:/Users/Admin/IFloor/projects/wxVideoProcessing/MotionDetectorPluginBase/Win/CMakeFiles/generate.stamp +if %errorlevel% neq 0 goto :cmEnd +:cmEnd +endlocal & call :cmErrorLevel %errorlevel% & goto :cmDone +:cmErrorLevel +exit /b %1 +:cmDone +if %errorlevel% neq 0 goto :VCEnd + %(AdditionalInputs) + C:\Users\Admin\IFloor\projects\wxVideoProcessing\MotionDetectorPluginBase\Win\CMakeFiles\generate.stamp + false + Building Custom Rule C:/Users/Admin/IFloor/projects/wxVideoProcessing/MotionDetectorPluginBase/CMakeLists.txt + setlocal +"C:\Program Files\CMake\bin\cmake.exe" -SC:/Users/Admin/IFloor/projects/wxVideoProcessing/build -BC:/Users/Admin/IFloor/projects/wxVideoProcessing/build/Win --check-stamp-file C:/Users/Admin/IFloor/projects/wxVideoProcessing/MotionDetectorPluginBase/Win/CMakeFiles/generate.stamp +if %errorlevel% neq 0 goto :cmEnd +:cmEnd +endlocal & call :cmErrorLevel %errorlevel% & goto :cmDone +:cmErrorLevel +exit /b %1 +:cmDone +if %errorlevel% neq 0 goto :VCEnd + %(AdditionalInputs) + C:\Users\Admin\IFloor\projects\wxVideoProcessing\MotionDetectorPluginBase\Win\CMakeFiles\generate.stamp + false + + + + + C:/Users/Admin/IFloor/projects/wxVideoProcessing/MotionDetectorPluginBase/Win/CMakeFiles/MotionDetectorPluginBase.dir/Debug/cmake_pch.hxx;%(ForcedIncludeFiles) + Create + C:/Users/Admin/IFloor/projects/wxVideoProcessing/MotionDetectorPluginBase/Win/CMakeFiles/MotionDetectorPluginBase.dir/Debug/cmake_pch.hxx + C:/Users/Admin/IFloor/projects/wxVideoProcessing/MotionDetectorPluginBase/Win/MotionDetectorPluginBase.dir/Debug//cmake_pch.pch + C:/Users/Admin/IFloor/projects/wxVideoProcessing/MotionDetectorPluginBase/Win/CMakeFiles/MotionDetectorPluginBase.dir/Release/cmake_pch.hxx;%(ForcedIncludeFiles) + Create + C:/Users/Admin/IFloor/projects/wxVideoProcessing/MotionDetectorPluginBase/Win/CMakeFiles/MotionDetectorPluginBase.dir/Release/cmake_pch.hxx + C:/Users/Admin/IFloor/projects/wxVideoProcessing/MotionDetectorPluginBase/Win/MotionDetectorPluginBase.dir/Release//cmake_pch.pch + + + C:/Users/Admin/IFloor/projects/wxVideoProcessing/MotionDetectorPluginBase/Win/CMakeFiles/MotionDetectorPluginBase.dir/Debug/cmake_pch.hxx;%(ForcedIncludeFiles) + C:/Users/Admin/IFloor/projects/wxVideoProcessing/MotionDetectorPluginBase/Win/CMakeFiles/MotionDetectorPluginBase.dir/Release/cmake_pch.hxx;%(ForcedIncludeFiles) + + + C:/Users/Admin/IFloor/projects/wxVideoProcessing/MotionDetectorPluginBase/Win/CMakeFiles/MotionDetectorPluginBase.dir/Debug/cmake_pch.hxx;%(ForcedIncludeFiles) + C:/Users/Admin/IFloor/projects/wxVideoProcessing/MotionDetectorPluginBase/Win/CMakeFiles/MotionDetectorPluginBase.dir/Release/cmake_pch.hxx;%(ForcedIncludeFiles) + + + C:/Users/Admin/IFloor/projects/wxVideoProcessing/MotionDetectorPluginBase/Win/CMakeFiles/MotionDetectorPluginBase.dir/Debug/cmake_pch.hxx;%(ForcedIncludeFiles) + C:/Users/Admin/IFloor/projects/wxVideoProcessing/MotionDetectorPluginBase/Win/CMakeFiles/MotionDetectorPluginBase.dir/Release/cmake_pch.hxx;%(ForcedIncludeFiles) + + + C:/Users/Admin/IFloor/projects/wxVideoProcessing/MotionDetectorPluginBase/Win/CMakeFiles/MotionDetectorPluginBase.dir/Debug/cmake_pch.hxx;%(ForcedIncludeFiles) + C:/Users/Admin/IFloor/projects/wxVideoProcessing/MotionDetectorPluginBase/Win/CMakeFiles/MotionDetectorPluginBase.dir/Release/cmake_pch.hxx;%(ForcedIncludeFiles) + + + C:/Users/Admin/IFloor/projects/wxVideoProcessing/MotionDetectorPluginBase/Win/CMakeFiles/MotionDetectorPluginBase.dir/Debug/cmake_pch.hxx;%(ForcedIncludeFiles) + C:/Users/Admin/IFloor/projects/wxVideoProcessing/MotionDetectorPluginBase/Win/CMakeFiles/MotionDetectorPluginBase.dir/Release/cmake_pch.hxx;%(ForcedIncludeFiles) + + + + + + + + + + C:/Users/Admin/IFloor/projects/wxVideoProcessing/MotionDetectorPluginBase/Win/CMakeFiles/MotionDetectorPluginBase.dir/Debug/cmake_pch.hxx;%(ForcedIncludeFiles) + C:/Users/Admin/IFloor/projects/wxVideoProcessing/MotionDetectorPluginBase/Win/CMakeFiles/MotionDetectorPluginBase.dir/Release/cmake_pch.hxx;%(ForcedIncludeFiles) + + + + + + + + {6F4BDD20-5B99-3029-BDF0-7D719B283AF5} + ZERO_CHECK + false + Never + + + {751A15C0-76C2-3506-AB9D-0CD3545003FA} + CommonPluginBase + + + {14D82890-BB7F-3748-8713-43304E91CFA1} + Utils + + + {7887B55C-77B9-3262-AFAD-ABA738C61029} + wxJSON + + + {78CFCEBE-5466-3128-9717-547416DBFFE5} + wxXS + + + + + + \ No newline at end of file diff --git a/MotionDetectorPluginBase/Win/MotionDetectorPluginBase.vcxproj.filters b/MotionDetectorPluginBase/Win/MotionDetectorPluginBase.vcxproj.filters new file mode 100644 index 0000000..2cab44e --- /dev/null +++ b/MotionDetectorPluginBase/Win/MotionDetectorPluginBase.vcxproj.filters @@ -0,0 +1,69 @@ + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Precompile Header File + + + Precompile Header File + + + + + + + + {4951AEE1-75F2-3C38-B593-C6BC8C809FB0} + + + {112D4227-BB51-3360-BB5B-5EA5DBA5A65F} + + + {05450AB2-CB7B-34EA-83DF-1EE86424EDF0} + + + diff --git a/SampleGuiPlugin1/CMakeLists.txt b/SampleGuiPlugin1/CMakeLists.txt deleted file mode 100644 index 700a0f8..0000000 --- a/SampleGuiPlugin1/CMakeLists.txt +++ /dev/null @@ -1,78 +0,0 @@ -set (SRCS - SampleGuiPlugin1.cpp - SampleGuiPlugin1Exports.cpp - SampleGuiPluginWindow1.cpp) -set (HEADERS - SampleGuiPlugin1.h - SampleGuiPluginWindow1.h) - -set(LIBRARY_NAME SampleGuiPlugin1) - -if(WIN32) - set(SRCS ${SRCS} ${LIBRARY_NAME}.rc ${LIBRARY_NAME}.def) - set(PREPROCESSOR_DEFINITIONS ${PREPROCESSOR_DEFINITIONS};/D_USRDLL;/D__STDC_CONSTANT_MACROS) - set(LINK_DIRECTORIES - ${PROJECT_ROOT_DIR}/wxGuiPluginBase/${OS_BASE_NAME}${LIB_SUFFIX}/$(ConfigurationName)) - set(DEMO_LIBS wxGuiPluginBase.lib) -endif(WIN32) -if(LINUX OR APPLE) - set(DEMO_LIBS wxGuiPluginBase) - SET(CMAKE_SKIP_BUILD_RPATH FALSE) - SET(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE) - SET(CMAKE_INSTALL_RPATH ".:./../../") -endif(LINUX OR APPLE) - -set(SRCS ${SRCS} ${HEADERS} - ${PROJECT_ROOT_DIR}/include/stdwx.h - ${PROJECT_ROOT_DIR}/include/stdwx.cpp) - -add_definitions(${PREPROCESSOR_DEFINITIONS}) - -include_directories(${INCLUDE_DIRECTORIES} ${BASE_INCLUDE_DIRECTORIES} - ${PROJECT_ROOT_DIR}/wxGuiPluginBase) - -link_directories(${LINK_DIRECTORIES}) - -add_library(${LIBRARY_NAME} SHARED ${SRCS}) - -set(DLL_DIR bin) -if(APPLE) - set(BUNDLE_SUBFOLDER - "/$(CONFIGURATION)/${PROJECT_NAME}.app/Contents/PlugIns") -endif(APPLE) -if(WIN32) - set(TARGET_LOCATION - ${PROJECT_SOURCE_DIR}/${DLL_DIR}${LIB_SUFFIX}/${CMAKE_CFG_INTDIR}/plugins/gui) -elseif (LINUX) - set(TARGET_LOCATION - ${PROJECT_SOURCE_DIR}/${DLL_DIR}${LIB_SUFFIX}${BUNDLE_SUBFOLDER}/plugins/gui) -else() - set(TARGET_LOCATION - ${PROJECT_SOURCE_DIR}/${DLL_DIR}${LIB_SUFFIX}${BUNDLE_SUBFOLDER}/gui) -endif(WIN32) -if(LINUX OR APPLE) - get_target_property(RESULT_FULL_PATH ${LIBRARY_NAME} LOCATION) - get_filename_component(RESULT_FILE_NAME ${RESULT_FULL_PATH} NAME) -endif(LINUX OR APPLE) -set_target_properties(${LIBRARY_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${TARGET_LOCATION}) - -target_link_libraries(${LIBRARY_NAME} ${DEMO_LIBS} ${wxWidgets_LIBRARIES}) - -add_dependencies(${LIBRARY_NAME} wxGuiPluginBase) - -target_precompile_headers(${LIBRARY_NAME} PRIVATE ${PROJECT_ROOT_DIR}/include/stdwx.h ${PROJECT_ROOT_DIR}/include/stdwx.cpp) - -if(APPLE) - FOREACH(DEP_LIB ${DEMO_LIBS}) - get_filename_component(ABS_ROOT_DIR ${PROJECT_ROOT_DIR} ABSOLUTE) - set(LIBNAME_FULL "${ABS_ROOT_DIR}/${DEP_LIB}/${OS_BASE_NAME}${LIB_SUFFIX}/$(CONFIGURATION)/lib${DEP_LIB}.dylib") - add_custom_command(TARGET ${LIBRARY_NAME} POST_BUILD - COMMAND install_name_tool -change "${LIBNAME_FULL}" "@executable_path/../Frameworks/lib${DEP_LIB}.dylib" $) - ENDFOREACH(DEP_LIB) -endif(APPLE) -if(LINUX OR APPLE) - add_custom_command(TARGET ${LIBRARY_NAME} POST_BUILD - COMMAND ${CMAKE_COMMAND} -E make_directory ${TARGET_LOCATION} - COMMAND ${CMAKE_COMMAND} -E copy $ ${TARGET_LOCATION}/${RESULT_FILE_NAME} - ) -endif(LINUX OR APPLE) diff --git a/SampleGuiPlugin1/SampleGuiPlugin1.cpp b/SampleGuiPlugin1/SampleGuiPlugin1.cpp deleted file mode 100644 index fdcaef9..0000000 --- a/SampleGuiPlugin1/SampleGuiPlugin1.cpp +++ /dev/null @@ -1,35 +0,0 @@ -#include "stdwx.h" -#include "SampleGuiPlugin1.h" -#include "SampleGuiPluginWindow1.h" - -IMPLEMENT_DYNAMIC_CLASS(SampleGuiPlugin1, wxObject) - -SampleGuiPlugin1::SampleGuiPlugin1() -: wxGuiPluginBase(NULL) -{ - -} - -SampleGuiPlugin1::SampleGuiPlugin1(wxEvtHandler * handler) -: wxGuiPluginBase(handler) -{ -} - -SampleGuiPlugin1::~SampleGuiPlugin1() -{ -} - -wxString SampleGuiPlugin1::GetName() const -{ - return _("GUI Plugin 1"); -} - -wxString SampleGuiPlugin1::GetId() const -{ - return wxT("{4E97DF66-5FBB-4719-AF17-76C1C82D3FE1}"); -} - -wxWindow * SampleGuiPlugin1::CreatePanel(wxWindow * parent) -{ - return new SampleGuiPluginWindow1(this, parent); -} diff --git a/SampleGuiPlugin1/SampleGuiPlugin1.def b/SampleGuiPlugin1/SampleGuiPlugin1.def deleted file mode 100644 index 600afe1..0000000 --- a/SampleGuiPlugin1/SampleGuiPlugin1.def +++ /dev/null @@ -1,5 +0,0 @@ -LIBRARY "SampleGuiPlugin1" - -EXPORTS - CreatePlugin=CreatePlugin - DeletePlugin=DeletePlugin \ No newline at end of file diff --git a/SampleGuiPlugin1/SampleGuiPlugin1.h b/SampleGuiPlugin1/SampleGuiPlugin1.h deleted file mode 100644 index d792328..0000000 --- a/SampleGuiPlugin1/SampleGuiPlugin1.h +++ /dev/null @@ -1,16 +0,0 @@ -#pragma once - -#include - -class SampleGuiPlugin1 : public wxGuiPluginBase -{ - DECLARE_DYNAMIC_CLASS(SampleGuiPlugin1) -public: - SampleGuiPlugin1(); - SampleGuiPlugin1(wxEvtHandler * handler); - virtual ~SampleGuiPlugin1(); - - virtual wxString GetName() const; - virtual wxString GetId() const; - virtual wxWindow * CreatePanel(wxWindow * parent); -}; \ No newline at end of file diff --git a/SampleGuiPlugin1/SampleGuiPlugin1.rc b/SampleGuiPlugin1/SampleGuiPlugin1.rc deleted file mode 100755 index f63e693..0000000 --- a/SampleGuiPlugin1/SampleGuiPlugin1.rc +++ /dev/null @@ -1 +0,0 @@ -#include "wx/msw/wx.rc" diff --git a/SampleGuiPlugin1/SampleGuiPlugin1Exports.cpp b/SampleGuiPlugin1/SampleGuiPlugin1Exports.cpp deleted file mode 100644 index 8d4fa0e..0000000 --- a/SampleGuiPlugin1/SampleGuiPlugin1Exports.cpp +++ /dev/null @@ -1,13 +0,0 @@ -#include "stdwx.h" -#include -#include "SampleGuiPlugin1.h" - -PLUGIN_EXPORTED_API wxGuiPluginBase * CreatePlugin() -{ - return new SampleGuiPlugin1; -} - -PLUGIN_EXPORTED_API void DeletePlugin(wxGuiPluginBase * plugin) -{ - wxDELETE(plugin); -} diff --git a/SampleGuiPlugin1/SampleGuiPluginWindow1.cpp b/SampleGuiPlugin1/SampleGuiPluginWindow1.cpp deleted file mode 100644 index 5f30afb..0000000 --- a/SampleGuiPlugin1/SampleGuiPluginWindow1.cpp +++ /dev/null @@ -1,192 +0,0 @@ -///////////////////////////////////////////////////////////////////////////// -// Name: SampleGuiPluginWindow1.cpp -// Purpose: -// Author: Volodymyr (T-Rex) Triapichko -// Modified by: -// Created: 10/09/2013 00:01:49 -// RCS-ID: -// Copyright: Volodymyr (T-Rex) Triapichko, 2013 -// Licence: -///////////////////////////////////////////////////////////////////////////// - -// For compilers that support precompilation, includes "wx/wx.h". -#include "wx/wxprec.h" - -#ifdef __BORLANDC__ -#pragma hdrstop -#endif - -#ifndef WX_PRECOMP -#include "wx/wx.h" -#endif - -////@begin includes -////@end includes - -#include "SampleGuiPluginWindow1.h" -#include - -////@begin XPM images -////@end XPM images - - -/* - * SampleGuiPluginWindow1 type definition - */ - -IMPLEMENT_DYNAMIC_CLASS( SampleGuiPluginWindow1, wxGuiPluginWindowBase ) - - -/* - * SampleGuiPluginWindow1 event table definition - */ - -BEGIN_EVENT_TABLE( SampleGuiPluginWindow1, wxGuiPluginWindowBase ) - -////@begin SampleGuiPluginWindow1 event table entries - EVT_BUTTON( ID_SEND_EVENT_BUTTON, SampleGuiPluginWindow1::OnSENDEVENTBUTTONClick ) -////@end SampleGuiPluginWindow1 event table entries - -END_EVENT_TABLE() - - -/* - * SampleGuiPluginWindow1 constructors - */ - -SampleGuiPluginWindow1::SampleGuiPluginWindow1() -{ - Init(); -} - -SampleGuiPluginWindow1::SampleGuiPluginWindow1( wxGuiPluginBase * plugin, - wxWindow* parent, wxWindowID id, - const wxPoint& pos, const wxSize& size, - long style ) -{ - Init(); - Create(plugin, parent, id, pos, size, style); -} - - -/* - * SampleGuiPluginWindow1 creator - */ - -bool SampleGuiPluginWindow1::Create(wxGuiPluginBase * plugin, - wxWindow* parent, wxWindowID id, - const wxPoint& pos, const wxSize& size, - long style ) -{ - wxGuiPluginWindowBase::Create(plugin, parent, id, pos, size, style ); - - CreateControls(); - if (GetSizer()) - { - GetSizer()->SetSizeHints(this); - } - Centre(); - return true; -} - - -/* - * SampleGuiPluginWindow1 destructor - */ - -SampleGuiPluginWindow1::~SampleGuiPluginWindow1() -{ -////@begin SampleGuiPluginWindow1 destruction -////@end SampleGuiPluginWindow1 destruction -} - - -/* - * Member initialisation - */ - -void SampleGuiPluginWindow1::Init() -{ -////@begin SampleGuiPluginWindow1 member initialisation - m_SamppleTextCtrl = NULL; -////@end SampleGuiPluginWindow1 member initialisation -} - - -/* - * Control creation for SampleGuiPluginWindow1 - */ - -void SampleGuiPluginWindow1::CreateControls() -{ -////@begin SampleGuiPluginWindow1 content construction - SampleGuiPluginWindow1* itemGuiPluginWindowBase1 = this; - - wxBoxSizer* itemBoxSizer2 = new wxBoxSizer(wxVERTICAL); - itemGuiPluginWindowBase1->SetSizer(itemBoxSizer2); - - wxStaticText* itemStaticText3 = new wxStaticText( itemGuiPluginWindowBase1, wxID_STATIC, _("Enter some text here:"), wxDefaultPosition, wxDefaultSize, 0 ); - itemBoxSizer2->Add(itemStaticText3, 0, wxALIGN_LEFT|wxLEFT|wxRIGHT|wxTOP, 5); - - m_SamppleTextCtrl = new wxTextCtrl( itemGuiPluginWindowBase1, ID_SAMPLE_TEXTCTRL, _("Hello, GUI Plugin 2!"), wxDefaultPosition, wxDefaultSize, 0 ); - itemBoxSizer2->Add(m_SamppleTextCtrl, 0, wxGROW|wxLEFT|wxRIGHT|wxTOP, 5); - - wxButton* itemButton5 = new wxButton( itemGuiPluginWindowBase1, ID_SEND_EVENT_BUTTON, _("Send event"), wxDefaultPosition, wxDefaultSize, 0 ); - itemBoxSizer2->Add(itemButton5, 0, wxALIGN_CENTER_HORIZONTAL|wxALL, 5); - -////@end SampleGuiPluginWindow1 content construction -} - - -/* - * Should we show tooltips? - */ - -bool SampleGuiPluginWindow1::ShowToolTips() -{ - return true; -} - -/* - * Get bitmap resources - */ - -wxBitmap SampleGuiPluginWindow1::GetBitmapResource( const wxString& name ) -{ - // Bitmap retrieval -////@begin SampleGuiPluginWindow1 bitmap retrieval - wxUnusedVar(name); - return wxNullBitmap; -////@end SampleGuiPluginWindow1 bitmap retrieval -} - -/* - * Get icon resources - */ - -wxIcon SampleGuiPluginWindow1::GetIconResource( const wxString& name ) -{ - // Icon retrieval -////@begin SampleGuiPluginWindow1 icon retrieval - wxUnusedVar(name); - return wxNullIcon; -////@end SampleGuiPluginWindow1 icon retrieval -} - - -/* - * wxEVT_COMMAND_BUTTON_CLICKED event handler for ID_SEND_EVENT_BUTTON - */ - -void SampleGuiPluginWindow1::OnSENDEVENTBUTTONClick( wxCommandEvent& event ) -{ - wxCommandEvent e(wxEVT_GUI_PLUGIN_INTEROP); - e.SetString(m_SamppleTextCtrl->GetValue()); - GetPlugin()->GetEventHandler()->AddPendingEvent(e); - -////@begin wxEVT_COMMAND_BUTTON_CLICKED event handler for ID_SEND_EVENT_BUTTON in SampleGuiPluginWindow1. - // Before editing this code, remove the block markers. - event.Skip(); -////@end wxEVT_COMMAND_BUTTON_CLICKED event handler for ID_SEND_EVENT_BUTTON in SampleGuiPluginWindow1. -} - diff --git a/SampleGuiPlugin1/SampleGuiPluginWindow1.h b/SampleGuiPlugin1/SampleGuiPluginWindow1.h deleted file mode 100644 index 937922e..0000000 --- a/SampleGuiPlugin1/SampleGuiPluginWindow1.h +++ /dev/null @@ -1,98 +0,0 @@ -///////////////////////////////////////////////////////////////////////////// -// Name: SampleGuiPluginWindow1.h -// Purpose: -// Author: Volodymyr (T-Rex) Triapichko -// Modified by: -// Created: 10/09/2013 00:01:49 -// RCS-ID: -// Copyright: Volodymyr (T-Rex) Triapichko, 2013 -// Licence: -///////////////////////////////////////////////////////////////////////////// - -#ifndef _SAMPLEGUIPLUGINWINDOW1_H_ -#define _SAMPLEGUIPLUGINWINDOW1_H_ - - -/*! - * Includes - */ - -////@begin includes -////@end includes -#include - -/*! - * Forward declarations - */ - -////@begin forward declarations -////@end forward declarations - -/*! - * Control identifiers - */ - -////@begin control identifiers -#define ID_SAMPLEGUIPLUGINWINDOW1 10000 -#define ID_SAMPLE_TEXTCTRL 10001 -#define ID_SEND_EVENT_BUTTON 10002 -#define SYMBOL_SAMPLEGUIPLUGINWINDOW1_STYLE wxTAB_TRAVERSAL -#define SYMBOL_SAMPLEGUIPLUGINWINDOW1_TITLE _("SampleGuiPluginWindow1") -#define SYMBOL_SAMPLEGUIPLUGINWINDOW1_IDNAME ID_SAMPLEGUIPLUGINWINDOW1 -#define SYMBOL_SAMPLEGUIPLUGINWINDOW1_SIZE wxSize(400, 300) -#define SYMBOL_SAMPLEGUIPLUGINWINDOW1_POSITION wxDefaultPosition -////@end control identifiers - - -/*! - * SampleGuiPluginWindow1 class declaration - */ - -class SampleGuiPluginWindow1: public wxGuiPluginWindowBase -{ - DECLARE_DYNAMIC_CLASS( SampleGuiPluginWindow1 ) - DECLARE_EVENT_TABLE() - -public: - /// Constructors - SampleGuiPluginWindow1(); - SampleGuiPluginWindow1(wxGuiPluginBase * plugin, wxWindow* parent, wxWindowID id = SYMBOL_SAMPLEGUIPLUGINWINDOW1_IDNAME, const wxPoint& pos = SYMBOL_SAMPLEGUIPLUGINWINDOW1_POSITION, const wxSize& size = SYMBOL_SAMPLEGUIPLUGINWINDOW1_SIZE, long style = SYMBOL_SAMPLEGUIPLUGINWINDOW1_STYLE ); - - /// Creation - bool Create(wxGuiPluginBase * plugin, wxWindow* parent, wxWindowID id = SYMBOL_SAMPLEGUIPLUGINWINDOW1_IDNAME, const wxPoint& pos = SYMBOL_SAMPLEGUIPLUGINWINDOW1_POSITION, const wxSize& size = SYMBOL_SAMPLEGUIPLUGINWINDOW1_SIZE, long style = SYMBOL_SAMPLEGUIPLUGINWINDOW1_STYLE ); - - /// Destructor - ~SampleGuiPluginWindow1(); - - /// Initialises member variables - void Init(); - - /// Creates the controls and sizers - void CreateControls(); - -////@begin SampleGuiPluginWindow1 event handler declarations - - /// wxEVT_COMMAND_BUTTON_CLICKED event handler for ID_SEND_EVENT_BUTTON - void OnSENDEVENTBUTTONClick( wxCommandEvent& event ); - -////@end SampleGuiPluginWindow1 event handler declarations - -////@begin SampleGuiPluginWindow1 member function declarations - - /// Retrieves bitmap resources - wxBitmap GetBitmapResource( const wxString& name ); - - /// Retrieves icon resources - wxIcon GetIconResource( const wxString& name ); -////@end SampleGuiPluginWindow1 member function declarations - - /// Should we show tooltips? - static bool ShowToolTips(); - -////@begin SampleGuiPluginWindow1 member variables - wxTextCtrl* m_SamppleTextCtrl; -////@end SampleGuiPluginWindow1 member variables -}; - -#endif - // _SAMPLEGUIPLUGINWINDOW1_H_ diff --git a/SampleGuiPlugin2/CMakeLists.txt b/SampleGuiPlugin2/CMakeLists.txt deleted file mode 100644 index ee8234b..0000000 --- a/SampleGuiPlugin2/CMakeLists.txt +++ /dev/null @@ -1,79 +0,0 @@ -set (SRCS - SampleGuiPlugin2.cpp - SampleGuiPlugin2Exports.cpp - SampleGuiPluginWindow2.cpp) -set (HEADERS - SampleGuiPlugin2.h - SampleGuiPluginWindow2.h) - -set(LIBRARY_NAME SampleGuiPlugin2) - -if(WIN32) - set(SRCS ${SRCS} ${LIBRARY_NAME}.def) - set(PREPROCESSOR_DEFINITIONS ${PREPROCESSOR_DEFINITIONS};/D_USRDLL;/D__STDC_CONSTANT_MACROS) - set(LINK_DIRECTORIES - ${PROJECT_ROOT_DIR}/wxGuiPluginBase/${OS_BASE_NAME}${LIB_SUFFIX}/$(ConfigurationName)) - set(DEMO_LIBS wxGuiPluginBase.lib) -endif(WIN32) -if(LINUX OR APPLE) - set(DEMO_LIBS wxGuiPluginBase) - SET(CMAKE_SKIP_BUILD_RPATH FALSE) - SET(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE) - SET(CMAKE_INSTALL_RPATH ".:./../../") -endif(LINUX OR APPLE) - -set(SRCS ${SRCS} ${HEADERS} - ${PROJECT_ROOT_DIR}/include/stdwx.h - ${PROJECT_ROOT_DIR}/include/stdwx.cpp) - -add_definitions(${PREPROCESSOR_DEFINITIONS}) - -include_directories(${INCLUDE_DIRECTORIES} ${BASE_INCLUDE_DIRECTORIES} - ${PROJECT_ROOT_DIR}/wxGuiPluginBase) - -link_directories(${LINK_DIRECTORIES}) - -add_library(${LIBRARY_NAME} SHARED ${SRCS}) - -set(DLL_DIR bin) -if(APPLE) - set(BUNDLE_SUBFOLDER - "/$(CONFIGURATION)/${PROJECT_NAME}.app/Contents/PlugIns") -endif(APPLE) -if(WIN32) - set(TARGET_LOCATION - ${PROJECT_SOURCE_DIR}/${DLL_DIR}${LIB_SUFFIX}/${CMAKE_CFG_INTDIR}/plugins/gui) -elseif (LINUX) - set(TARGET_LOCATION - ${PROJECT_SOURCE_DIR}/${DLL_DIR}${LIB_SUFFIX}${BUNDLE_SUBFOLDER}/plugins/gui) -else() - set(TARGET_LOCATION - ${PROJECT_SOURCE_DIR}/${DLL_DIR}${LIB_SUFFIX}${BUNDLE_SUBFOLDER}/gui) -endif(WIN32) -if(LINUX OR APPLE) - get_target_property(RESULT_FULL_PATH ${LIBRARY_NAME} LOCATION) - get_filename_component(RESULT_FILE_NAME ${RESULT_FULL_PATH} NAME) -endif(LINUX OR APPLE) - -set_target_properties(${LIBRARY_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${TARGET_LOCATION}) - -target_link_libraries(${LIBRARY_NAME} ${DEMO_LIBS} ${wxWidgets_LIBRARIES}) - -add_dependencies(${LIBRARY_NAME} wxGuiPluginBase) - -target_precompile_headers(${LIBRARY_NAME} PRIVATE ${PROJECT_ROOT_DIR}/include/stdwx.h ${PROJECT_ROOT_DIR}/include/stdwx.cpp) - -if(APPLE) - FOREACH(DEP_LIB ${DEMO_LIBS}) - get_filename_component(ABS_ROOT_DIR ${PROJECT_ROOT_DIR} ABSOLUTE) - set(LIBNAME_FULL "${ABS_ROOT_DIR}/${DEP_LIB}/${OS_BASE_NAME}${LIB_SUFFIX}/$(CONFIGURATION)/lib${DEP_LIB}.dylib") - add_custom_command(TARGET ${LIBRARY_NAME} POST_BUILD - COMMAND install_name_tool -change "${LIBNAME_FULL}" "@executable_path/../Frameworks/lib${DEP_LIB}.dylib" $) - ENDFOREACH(DEP_LIB) -endif(APPLE) -if(LINUX OR APPLE) - add_custom_command(TARGET ${LIBRARY_NAME} POST_BUILD - COMMAND ${CMAKE_COMMAND} -E make_directory ${TARGET_LOCATION} - COMMAND ${CMAKE_COMMAND} -E copy $ ${TARGET_LOCATION}/${RESULT_FILE_NAME} - ) -endif(LINUX OR APPLE) diff --git a/SampleGuiPlugin2/SampleGuiPlugin2.cpp b/SampleGuiPlugin2/SampleGuiPlugin2.cpp deleted file mode 100644 index 451da39..0000000 --- a/SampleGuiPlugin2/SampleGuiPlugin2.cpp +++ /dev/null @@ -1,35 +0,0 @@ -#include "stdwx.h" -#include "SampleGuiPlugin2.h" -#include "SampleGuiPluginWindow2.h" - -IMPLEMENT_DYNAMIC_CLASS(SampleGuiPlugin2, wxObject) - -SampleGuiPlugin2::SampleGuiPlugin2() -: wxGuiPluginBase(NULL) -{ - -} - -SampleGuiPlugin2::SampleGuiPlugin2(wxEvtHandler * handler) -: wxGuiPluginBase(handler) -{ -} - -SampleGuiPlugin2::~SampleGuiPlugin2() -{ -} - -wxString SampleGuiPlugin2::GetName() const -{ - return _("GUI Plugin 2"); -} - -wxString SampleGuiPlugin2::GetId() const -{ - return wxT("{1B226C84-6436-4092-9AB8-B2B0D6731EBE}"); -} - -wxWindow * SampleGuiPlugin2::CreatePanel(wxWindow * parent) -{ - return new SampleGuiPluginWindow2(this, parent); -} diff --git a/SampleGuiPlugin2/SampleGuiPlugin2.h b/SampleGuiPlugin2/SampleGuiPlugin2.h deleted file mode 100644 index 7e46db4..0000000 --- a/SampleGuiPlugin2/SampleGuiPlugin2.h +++ /dev/null @@ -1,16 +0,0 @@ -#pragma once - -#include - -class SampleGuiPlugin2 : public wxGuiPluginBase -{ - DECLARE_DYNAMIC_CLASS(SampleGuiPlugin2) -public: - SampleGuiPlugin2(); - SampleGuiPlugin2(wxEvtHandler * handler); - virtual ~SampleGuiPlugin2(); - - virtual wxString GetName() const; - virtual wxString GetId() const; - virtual wxWindow * CreatePanel(wxWindow * parent); -}; \ No newline at end of file diff --git a/SampleGuiPlugin2/SampleGuiPlugin2.pjd b/SampleGuiPlugin2/SampleGuiPlugin2.pjd deleted file mode 100644 index f622f46..0000000 --- a/SampleGuiPlugin2/SampleGuiPlugin2.pjd +++ /dev/null @@ -1,506 +0,0 @@ - - -
- 0 - "" - "" - "" - "" - "" - 0 - 0 - 0 - 1 - 1 - 1 - 1 - 0 - "Volodymyr (T-Rex) Triapichko" - "Volodymyr (T-Rex) Triapichko, 2013" - "" - 0 - 0 - 0 - 0 - "<All platforms>" - "2.9.5" - "Standard" - "///////////////////////////////////////////////////////////////////////////// -// Name: %HEADER-FILENAME% -// Purpose: -// Author: %AUTHOR% -// Modified by: -// Created: %DATE% -// RCS-ID: -// Copyright: %COPYRIGHT% -// Licence: -///////////////////////////////////////////////////////////////////////////// - -" - "///////////////////////////////////////////////////////////////////////////// -// Name: %SOURCE-FILENAME% -// Purpose: -// Author: %AUTHOR% -// Modified by: -// Created: %DATE% -// RCS-ID: -// Copyright: %COPYRIGHT% -// Licence: -///////////////////////////////////////////////////////////////////////////// - -" - "///////////////////////////////////////////////////////////////////////////// -// Name: %SYMBOLS-FILENAME% -// Purpose: Symbols file -// Author: %AUTHOR% -// Modified by: -// Created: %DATE% -// RCS-ID: -// Copyright: %COPYRIGHT% -// Licence: -///////////////////////////////////////////////////////////////////////////// - -" - "" - "// For compilers that support precompilation, includes "wx/wx.h". -#include "wx/wxprec.h" - -#ifdef __BORLANDC__ -#pragma hdrstop -#endif - -#ifndef WX_PRECOMP -#include "wx/wx.h" -#endif - -" - " /// %BODY% -" - " -/* - * %BODY% - */ - -" - "app_resources.h" - "app_resources.cpp" - "AppResources" - "app.h" - "app.cpp" - "Application" - 0 - "" - "<None>" - "iso-8859-1" - "utf-8" - "utf-8" - "" - 0 - 0 - 4 - " " - "" - 0 - 0 - 1 - 0 - 1 - 1 - 0 - 1 - 0 - 0 -
- - - "" - "data-document" - "" - "" - 0 - 1 - 0 - 0 - - "Configurations" - "config-data-document" - "" - "" - 0 - 1 - 0 - 0 - "" - 1 - -8519680 - "" - "Debug" - "Unicode" - "Static" - "Modular" - "GUI" - "wxMSW" - "Default" - "Dynamic" - "Yes" - "No" - "Yes" - "No" - "No" - "Yes" - "Yes" - "Yes" - "Yes" - "Yes" - "builtin" - "Yes" - "%EXECUTABLE%" - "" - "%AUTO%" - "%AUTO%" - "%AUTO%" - "%AUTO%" - "%WXVERSION%" - "%AUTO%" - "%AUTO%" - "%AUTO%" - "%AUTO%" - "%AUTO%" - "" - "%AUTO%" - "%AUTO%" - "%AUTO%" - "%AUTO%" - "%AUTO%" - "%AUTO%" - "%AUTO%" - "%AUTO%" - "%AUTO%" - "%AUTO%" - "%AUTO%" - "%AUTO%" - "%AUTO%" - "%AUTO%" - "%AUTO%" - "%AUTO%" - "%AUTO%" - "%AUTO%" - "%AUTO%" - "%AUTO%" - "%AUTO%" - "%AUTO%" - 0 - 1 - - - - - - - "Projects" - "root-document" - "" - "project" - 1 - 1 - 0 - 1 - - "Windows" - "html-document" - "" - "dialogsfolder" - 1 - 1 - 0 - 1 - - "SampleGuiPluginWindow2" - "dialog-document" - "" - "dialog" - 0 - 1 - 0 - 0 - "wbDialogProxy" - 10000 - 0 - "" - 0 - "" - "Standard" - 0 - 0 - "wxEVT_DESTROY|OnDestroy|NONE||" - "ID_SAMPLEGUIPLUGINWINDOW2" - 10000 - "SampleGuiPluginWindow2" - "wxGuiPluginWindowBase" - "wxPanel" - "SampleGuiPluginWindow2.cpp" - "SampleGuiPluginWindow2.h" - "" - "SampleGuiPluginWindow2" - 1 - "" - 0 - "" - "" - "" - "" - "" - 0 - 1 - "<Any platform>" - "" - "" - "" - "" - "" - "" - "Tiled" - 0 - 0 - 0 - 0 - 0 - 0 - 0 - 0 - 0 - 1 - 0 - 0 - 0 - 0 - 0 - 0 - 0 - 0 - 0 - 0 - 0 - 1 - 0 - 0 - 0 - "" - 0 - 1 - -1 - -1 - 400 - 300 - 0 - "" - - "wxBoxSizer V" - "dialog-control-document" - "" - "sizer" - 0 - 1 - 0 - 0 - "wbBoxSizerProxy" - "Vertical" - "" - 0 - 0 - 0 - 0 - "<Any platform>" - - "wxStaticText: wxID_STATIC" - "dialog-control-document" - "" - "statictext" - 0 - 1 - 0 - 0 - "wbStaticTextProxy" - "wxID_STATIC" - 5105 - "" - "wxStaticText" - "wxStaticText" - 1 - 0 - "" - "" - "" - "This text box receives messages from GUI Plugin 1:" - -1 - "" - "" - "" - "" - "" - 0 - 1 - "<Any platform>" - "" - "" - "" - "" - "" - "" - "" - 0 - 0 - 0 - 0 - 0 - 0 - 0 - 0 - 0 - 0 - 0 - 0 - 0 - 0 - "" - -1 - -1 - -1 - -1 - "Left" - "Centre" - 0 - 5 - 1 - 1 - 1 - 0 - 0 - 0 - 0 - 0 - "" - "" - - - "wxTextCtrl: ID_GUI_PLUGIN2_MESSAGE_TEXTCTRL" - "dialog-control-document" - "" - "textctrl" - 0 - 1 - 0 - 0 - "wbTextCtrlProxy" - "ID_GUI_PLUGIN2_MESSAGE_TEXTCTRL" - 10001 - "" - "wxTextCtrl" - "wxTextCtrl" - 1 - 0 - "" - "" - "m_MessageTextCtrl" - "" - 0 - "" - "" - "" - "" - "" - 0 - 1 - "<Any platform>" - "" - "" - "" - "" - "" - "" - "" - 0 - 0 - 0 - 0 - 1 - 0 - 0 - 0 - 0 - 0 - 0 - 0 - 0 - 0 - 0 - 0 - 0 - 0 - 0 - 0 - 0 - 0 - 0 - 0 - 0 - 0 - "" - -1 - -1 - -1 - -1 - "Expand" - "Centre" - 0 - 5 - 1 - 1 - 1 - 1 - 0 - 0 - 0 - 0 - "" - "" - - - - - - "Sources" - "html-document" - "" - "sourcesfolder" - 1 - 1 - 0 - 1 - - "SampleGuiPlugin2.rc" - "source-editor-document" - "SampleGuiPlugin2.rc" - "source-editor" - 0 - 0 - 1 - 0 - "18/9/2013" - "" - - - - "Images" - "html-document" - "" - "bitmapsfolder" - 1 - 1 - 0 - 1 - - - - -
diff --git a/SampleGuiPlugin2/SampleGuiPlugin2.rc b/SampleGuiPlugin2/SampleGuiPlugin2.rc deleted file mode 100755 index f63e693..0000000 --- a/SampleGuiPlugin2/SampleGuiPlugin2.rc +++ /dev/null @@ -1 +0,0 @@ -#include "wx/msw/wx.rc" diff --git a/SampleGuiPlugin2/SampleGuiPlugin2Exports.cpp b/SampleGuiPlugin2/SampleGuiPlugin2Exports.cpp deleted file mode 100644 index 6e386d2..0000000 --- a/SampleGuiPlugin2/SampleGuiPlugin2Exports.cpp +++ /dev/null @@ -1,13 +0,0 @@ -#include "stdwx.h" -#include -#include "SampleGuiPlugin2.h" - -PLUGIN_EXPORTED_API wxGuiPluginBase * CreatePlugin() -{ - return new SampleGuiPlugin2; -} - -PLUGIN_EXPORTED_API void DeletePlugin(wxGuiPluginBase * plugin) -{ - wxDELETE(plugin); -} diff --git a/SampleGuiPlugin2/SampleGuiPluginWindow2.cpp b/SampleGuiPlugin2/SampleGuiPluginWindow2.cpp deleted file mode 100644 index 629c768..0000000 --- a/SampleGuiPlugin2/SampleGuiPluginWindow2.cpp +++ /dev/null @@ -1,190 +0,0 @@ -///////////////////////////////////////////////////////////////////////////// -// Name: SampleGuiPluginWindow2.cpp -// Purpose: -// Author: Volodymyr (T-Rex) Triapichko -// Modified by: -// Created: 18/09/2013 22:40:39 -// RCS-ID: -// Copyright: Volodymyr (T-Rex) Triapichko, 2013 -// Licence: -///////////////////////////////////////////////////////////////////////////// - -// For compilers that support precompilation, includes "wx/wx.h". -#include "wx/wxprec.h" - -#ifdef __BORLANDC__ -#pragma hdrstop -#endif - -#ifndef WX_PRECOMP -#include "wx/wx.h" -#endif - -////@begin includes -////@end includes - -#include "SampleGuiPluginWindow2.h" -#include - -////@begin XPM images -////@end XPM images - - -/* - * SampleGuiPluginWindow2 type definition - */ - -IMPLEMENT_DYNAMIC_CLASS( SampleGuiPluginWindow2, wxGuiPluginWindowBase ) - - -/* - * SampleGuiPluginWindow2 event table definition - */ - -BEGIN_EVENT_TABLE( SampleGuiPluginWindow2, wxGuiPluginWindowBase ) - -////@begin SampleGuiPluginWindow2 event table entries - EVT_WINDOW_DESTROY( SampleGuiPluginWindow2::OnDestroy ) -////@end SampleGuiPluginWindow2 event table entries - -END_EVENT_TABLE() - - -/* - * SampleGuiPluginWindow2 constructors - */ - -SampleGuiPluginWindow2::SampleGuiPluginWindow2() -{ - Init(); -} - -SampleGuiPluginWindow2::SampleGuiPluginWindow2( wxGuiPluginBase * plugin, wxWindow* parent, wxWindowID id, const wxPoint& pos, const wxSize& size, long style ) -{ - Init(); - Create(plugin, parent, id, pos, size, style); -} - - -/* - * SampleGuiPluginWindow2 creator - */ - -bool SampleGuiPluginWindow2::Create( wxGuiPluginBase * plugin, wxWindow* parent, wxWindowID id, const wxPoint& pos, const wxSize& size, long style ) -{ - wxGuiPluginWindowBase::Create(plugin, parent, id, pos, size, style ); - - CreateControls(); - if (GetSizer()) - { - GetSizer()->SetSizeHints(this); - } - Centre(); - return true; -} - - -/* - * SampleGuiPluginWindow2 destructor - */ - -SampleGuiPluginWindow2::~SampleGuiPluginWindow2() -{ -////@begin SampleGuiPluginWindow2 destruction -////@end SampleGuiPluginWindow2 destruction -} - - -/* - * Member initialisation - */ - -void SampleGuiPluginWindow2::Init() -{ -////@begin SampleGuiPluginWindow2 member initialisation - m_MessageTextCtrl = NULL; -////@end SampleGuiPluginWindow2 member initialisation -} - - -/* - * Control creation for SampleGuiPluginWindow2 - */ - -void SampleGuiPluginWindow2::CreateControls() -{ -////@begin SampleGuiPluginWindow2 content construction - SampleGuiPluginWindow2* itemGuiPluginWindowBase1 = this; - - wxBoxSizer* itemBoxSizer2 = new wxBoxSizer(wxVERTICAL); - itemGuiPluginWindowBase1->SetSizer(itemBoxSizer2); - - wxStaticText* itemStaticText3 = new wxStaticText( itemGuiPluginWindowBase1, wxID_STATIC, _("This text box receives messages from GUI Plugin 1:"), wxDefaultPosition, wxDefaultSize, 0 ); - itemBoxSizer2->Add(itemStaticText3, 0, wxALIGN_LEFT|wxLEFT|wxRIGHT|wxTOP, 5); - - m_MessageTextCtrl = new wxTextCtrl( itemGuiPluginWindowBase1, ID_GUI_PLUGIN2_MESSAGE_TEXTCTRL, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_READONLY ); - itemBoxSizer2->Add(m_MessageTextCtrl, 0, wxGROW|wxALL, 5); - - // Connect events and objects - itemGuiPluginWindowBase1->Connect(ID_SAMPLEGUIPLUGINWINDOW2, wxEVT_DESTROY, wxWindowDestroyEventHandler(SampleGuiPluginWindow2::OnDestroy), NULL, this); -////@end SampleGuiPluginWindow2 content construction - GetPlugin()->GetEventHandler()->Bind(wxEVT_GUI_PLUGIN_INTEROP, - wxCommandEventHandler(SampleGuiPluginWindow2::OnInteropMessageReceived), this); -} - - -/* - * Should we show tooltips? - */ - -bool SampleGuiPluginWindow2::ShowToolTips() -{ - return true; -} - -/* - * Get bitmap resources - */ - -wxBitmap SampleGuiPluginWindow2::GetBitmapResource( const wxString& name ) -{ - // Bitmap retrieval -////@begin SampleGuiPluginWindow2 bitmap retrieval - wxUnusedVar(name); - return wxNullBitmap; -////@end SampleGuiPluginWindow2 bitmap retrieval -} - -/* - * Get icon resources - */ - -wxIcon SampleGuiPluginWindow2::GetIconResource( const wxString& name ) -{ - // Icon retrieval -////@begin SampleGuiPluginWindow2 icon retrieval - wxUnusedVar(name); - return wxNullIcon; -////@end SampleGuiPluginWindow2 icon retrieval -} - -void SampleGuiPluginWindow2::OnInteropMessageReceived(wxCommandEvent & event) -{ - m_MessageTextCtrl->SetValue(event.GetString()); -} - - -/* - * wxEVT_DESTROY event handler for ID_SAMPLEGUIPLUGINWINDOW2 - */ - -void SampleGuiPluginWindow2::OnDestroy( wxWindowDestroyEvent& event ) -{ - GetPlugin()->GetEventHandler()->Unbind(wxEVT_GUI_PLUGIN_INTEROP, - wxCommandEventHandler(SampleGuiPluginWindow2::OnInteropMessageReceived), this); -////@begin wxEVT_DESTROY event handler for ID_SAMPLEGUIPLUGINWINDOW2 in SampleGuiPluginWindow2. - // Before editing this code, remove the block markers. - event.Skip(); -////@end wxEVT_DESTROY event handler for ID_SAMPLEGUIPLUGINWINDOW2 in SampleGuiPluginWindow2. -} - diff --git a/SampleGuiPlugin2/SampleGuiPluginWindow2.h b/SampleGuiPlugin2/SampleGuiPluginWindow2.h deleted file mode 100644 index 8ccdf2e..0000000 --- a/SampleGuiPlugin2/SampleGuiPluginWindow2.h +++ /dev/null @@ -1,98 +0,0 @@ -///////////////////////////////////////////////////////////////////////////// -// Name: SampleGuiPluginWindow2.h -// Purpose: -// Author: Volodymyr (T-Rex) Triapichko -// Modified by: -// Created: 18/09/2013 22:40:39 -// RCS-ID: -// Copyright: Volodymyr (T-Rex) Triapichko, 2013 -// Licence: -///////////////////////////////////////////////////////////////////////////// - -#ifndef _SAMPLEGUIPLUGINWINDOW2_H_ -#define _SAMPLEGUIPLUGINWINDOW2_H_ - - -/*! - * Includes - */ - -////@begin includes -////@end includes -#include - -/*! - * Forward declarations - */ - -////@begin forward declarations -////@end forward declarations - -/*! - * Control identifiers - */ - -////@begin control identifiers -#define ID_SAMPLEGUIPLUGINWINDOW2 10000 -#define ID_GUI_PLUGIN2_MESSAGE_TEXTCTRL 10001 -#define SYMBOL_SAMPLEGUIPLUGINWINDOW2_STYLE wxTAB_TRAVERSAL -#define SYMBOL_SAMPLEGUIPLUGINWINDOW2_TITLE _("SampleGuiPluginWindow2") -#define SYMBOL_SAMPLEGUIPLUGINWINDOW2_IDNAME ID_SAMPLEGUIPLUGINWINDOW2 -#define SYMBOL_SAMPLEGUIPLUGINWINDOW2_SIZE wxSize(400, 300) -#define SYMBOL_SAMPLEGUIPLUGINWINDOW2_POSITION wxDefaultPosition -////@end control identifiers - - -/*! - * SampleGuiPluginWindow2 class declaration - */ - -class SampleGuiPluginWindow2: public wxGuiPluginWindowBase -{ - DECLARE_DYNAMIC_CLASS( SampleGuiPluginWindow2 ) - DECLARE_EVENT_TABLE() - -public: - /// Constructors - SampleGuiPluginWindow2(); - SampleGuiPluginWindow2( wxGuiPluginBase * plugin, wxWindow* parent, wxWindowID id = SYMBOL_SAMPLEGUIPLUGINWINDOW2_IDNAME, const wxPoint& pos = SYMBOL_SAMPLEGUIPLUGINWINDOW2_POSITION, const wxSize& size = SYMBOL_SAMPLEGUIPLUGINWINDOW2_SIZE, long style = SYMBOL_SAMPLEGUIPLUGINWINDOW2_STYLE ); - - /// Creation - bool Create( wxGuiPluginBase * plugin, wxWindow* parent, wxWindowID id = SYMBOL_SAMPLEGUIPLUGINWINDOW2_IDNAME, const wxPoint& pos = SYMBOL_SAMPLEGUIPLUGINWINDOW2_POSITION, const wxSize& size = SYMBOL_SAMPLEGUIPLUGINWINDOW2_SIZE, long style = SYMBOL_SAMPLEGUIPLUGINWINDOW2_STYLE ); - - /// Destructor - ~SampleGuiPluginWindow2(); - - /// Initialises member variables - void Init(); - - /// Creates the controls and sizers - void CreateControls(); - -////@begin SampleGuiPluginWindow2 event handler declarations - - /// wxEVT_DESTROY event handler for ID_SAMPLEGUIPLUGINWINDOW2 - void OnDestroy( wxWindowDestroyEvent& event ); - -////@end SampleGuiPluginWindow2 event handler declarations - void OnInteropMessageReceived(wxCommandEvent & event); - -////@begin SampleGuiPluginWindow2 member function declarations - - /// Retrieves bitmap resources - wxBitmap GetBitmapResource( const wxString& name ); - - /// Retrieves icon resources - wxIcon GetIconResource( const wxString& name ); -////@end SampleGuiPluginWindow2 member function declarations - - /// Should we show tooltips? - static bool ShowToolTips(); - -////@begin SampleGuiPluginWindow2 member variables - wxTextCtrl* m_MessageTextCtrl; -////@end SampleGuiPluginWindow2 member variables -}; - -#endif - // _SAMPLEGUIPLUGINWINDOW2_H_ diff --git a/SampleNonGuiPlugin/CMakeLists.txt b/SampleNonGuiPlugin/CMakeLists.txt deleted file mode 100644 index 90cb79a..0000000 --- a/SampleNonGuiPlugin/CMakeLists.txt +++ /dev/null @@ -1,81 +0,0 @@ -set (SRCS - SampleNonGuiPlugin.cpp - SampleNonGuiPluginExports.cpp) -set (HEADERS - SampleNonGuiPlugin.h) - -set(LIBRARY_NAME SampleNonGuiPlugin) - -if(WIN32) - set(SRCS ${SRCS} ${LIBRARY_NAME}.def) - set(PREPROCESSOR_DEFINITIONS ${PREPROCESSOR_DEFINITIONS};/D_USRDLL;/D__STDC_CONSTANT_MACROS) - set(LINK_DIRECTORIES - ${PROJECT_ROOT_DIR}/wxNonGuiPluginBase/${OS_BASE_NAME}${LIB_SUFFIX}/$(ConfigurationName)) - set(DEMO_LIBS wxNonGuiPluginBase.lib) -endif(WIN32) -if(LINUX OR APPLE) - set(DEMO_LIBS wxNonGuiPluginBase) - SET(CMAKE_SKIP_BUILD_RPATH FALSE) - SET(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE) - if(LINUX) - SET(CMAKE_INSTALL_RPATH ".:./../../") - else() - SET(CMAKE_INSTALL_RPATH "@executable_path/../Frameworks") - endif(LINUX) -endif(LINUX OR APPLE) - -set(SRCS ${SRCS} ${HEADERS} - ${PROJECT_ROOT_DIR}/include/stdwx.h - ${PROJECT_ROOT_DIR}/include/stdwx.cpp) - -add_definitions(${PREPROCESSOR_DEFINITIONS}) - -include_directories(${INCLUDE_DIRECTORIES} ${BASE_INCLUDE_DIRECTORIES} - ${PROJECT_ROOT_DIR}/wxNonGuiPluginBase) - -link_directories(${LINK_DIRECTORIES}) - -add_library(${LIBRARY_NAME} SHARED ${SRCS}) - -set(DLL_DIR bin) -if(APPLE) - set(BUNDLE_SUBFOLDER - "/$(CONFIGURATION)/${PROJECT_NAME}.app/Contents/PlugIns") -endif(APPLE) -if(WIN32) - set(TARGET_LOCATION - ${PROJECT_SOURCE_DIR}/${DLL_DIR}${LIB_SUFFIX}/${CMAKE_CFG_INTDIR}/plugins/nongui) -elseif (LINUX) - set(TARGET_LOCATION - ${PROJECT_SOURCE_DIR}/${DLL_DIR}${LIB_SUFFIX}${BUNDLE_SUBFOLDER}/plugins/nongui) -else() - set(TARGET_LOCATION - ${PROJECT_SOURCE_DIR}/${DLL_DIR}${LIB_SUFFIX}${BUNDLE_SUBFOLDER}/nongui) -endif(WIN32) -if(LINUX OR APPLE) - get_target_property(RESULT_FULL_PATH ${LIBRARY_NAME} LOCATION) - get_filename_component(RESULT_FILE_NAME ${RESULT_FULL_PATH} NAME) -endif(LINUX OR APPLE) -set_target_properties(${LIBRARY_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${TARGET_LOCATION}) - -target_link_libraries(${LIBRARY_NAME} ${DEMO_LIBS} ${wxWidgets_LIBRARIES}) - -add_dependencies(${LIBRARY_NAME} wxNonGuiPluginBase) - -target_precompile_headers(${LIBRARY_NAME} PRIVATE ${PROJECT_ROOT_DIR}/include/stdwx.h ${PROJECT_ROOT_DIR}/include/stdwx.cpp) - -if(APPLE) - FOREACH(DEP_LIB ${DEMO_LIBS}) - get_filename_component(ABS_ROOT_DIR ${PROJECT_ROOT_DIR} ABSOLUTE) - set(LIBNAME_FULL "${ABS_ROOT_DIR}/${DEP_LIB}/${OS_BASE_NAME}${LIB_SUFFIX}/$(CONFIGURATION)/lib${DEP_LIB}.dylib") - add_custom_command(TARGET ${LIBRARY_NAME} POST_BUILD - COMMAND install_name_tool -change "${LIBNAME_FULL}" "@executable_path/../Frameworks/lib${DEP_LIB}.dylib" $) - ENDFOREACH(DEP_LIB) -endif(APPLE) -if(LINUX OR APPLE) - add_custom_command(TARGET ${LIBRARY_NAME} POST_BUILD - COMMAND ${CMAKE_COMMAND} -E make_directory ${TARGET_LOCATION} - COMMAND ${CMAKE_COMMAND} -E copy $ - ${TARGET_LOCATION}/${RESULT_FILE_NAME} - ) -endif(LINUX OR APPLE) diff --git a/SampleNonGuiPlugin/SampleNonGuiPlugin.cpp b/SampleNonGuiPlugin/SampleNonGuiPlugin.cpp deleted file mode 100644 index d0ba3db..0000000 --- a/SampleNonGuiPlugin/SampleNonGuiPlugin.cpp +++ /dev/null @@ -1,17 +0,0 @@ -#include "stdwx.h" -#include "SampleNonGuiPlugin.h" - -IMPLEMENT_DYNAMIC_CLASS(SampleNonGuiPlugin, wxObject) - -SampleNonGuiPlugin::SampleNonGuiPlugin() -{ -} - -SampleNonGuiPlugin::~SampleNonGuiPlugin() -{ -} - -int SampleNonGuiPlugin::Work() -{ - return 10; -} diff --git a/SampleNonGuiPlugin/SampleNonGuiPlugin.def b/SampleNonGuiPlugin/SampleNonGuiPlugin.def deleted file mode 100644 index 501eec2..0000000 --- a/SampleNonGuiPlugin/SampleNonGuiPlugin.def +++ /dev/null @@ -1,5 +0,0 @@ -LIBRARY "SampleNonGuiPlugin" - -EXPORTS - CreatePlugin=CreatePlugin - DeletePlugin=DeletePlugin \ No newline at end of file diff --git a/SampleNonGuiPlugin/SampleNonGuiPlugin.h b/SampleNonGuiPlugin/SampleNonGuiPlugin.h deleted file mode 100644 index ab833b4..0000000 --- a/SampleNonGuiPlugin/SampleNonGuiPlugin.h +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once - -#include - -class SampleNonGuiPlugin : public wxNonGuiPluginBase -{ - DECLARE_DYNAMIC_CLASS(SampleNonGuiPlugin) -public: - SampleNonGuiPlugin(); - virtual ~SampleNonGuiPlugin(); - - virtual int Work(); -}; \ No newline at end of file diff --git a/SampleNonGuiPlugin/SampleNonGuiPluginExports.cpp b/SampleNonGuiPlugin/SampleNonGuiPluginExports.cpp deleted file mode 100644 index d984954..0000000 --- a/SampleNonGuiPlugin/SampleNonGuiPluginExports.cpp +++ /dev/null @@ -1,13 +0,0 @@ -#include "stdwx.h" -#include -#include "SampleNonGuiPlugin.h" - -PLUGIN_EXPORTED_API wxNonGuiPluginBase * CreatePlugin() -{ - return new SampleNonGuiPlugin; -} - -PLUGIN_EXPORTED_API void DeletePlugin(wxNonGuiPluginBase * plugin) -{ - wxDELETE(plugin); -} diff --git a/ThirdParty/AppIndicatorHelper/AIHTest.cpp b/ThirdParty/AppIndicatorHelper/AIHTest.cpp new file mode 100644 index 0000000..d26df86 --- /dev/null +++ b/ThirdParty/AppIndicatorHelper/AIHTest.cpp @@ -0,0 +1,158 @@ +/* + * File: AIHTest.cpp + * Author: nmset@netcourrier.com + * License : irrelevant + * + * Created on 11 septembre 2015, 16:58 + */ + +#include "AIHTest.h" +#include +#include +#include "stop.xpm" +#include +using namespace std; + +IMPLEMENT_APP(AIHTest) + +AIHTest::AIHTest() +{ +} + +AIHTest::~AIHTest() +{ +} + +bool AIHTest::OnInit() +{ + AIHTestFrame * main = new AIHTestFrame(NULL, wxID_ANY, _T("AppIndicatorHelper test")); + SetAppName(_T("AIHTest")); + SetTopWindow(main); + main->Show(); + return true; +} + +int AIHTest::OnExit() +{ + return wxApp::OnExit(); +} + +AIHTestFrame::AIHTestFrame(wxWindow* parent, wxWindowID id, const wxString& title) +: wxFrame(parent, id, title) +{ + wxBoxSizer * szMain = new wxBoxSizer(wxVERTICAL); + SetSizer(szMain); + m_itemID = -10000; + m_subID = 0; + m_mainMenu = NULL; + m_removedMenu = NULL; + SetToolTip(_T("Right click : Popup menu.\nDouble click : Delete menu.")); + m_mainMenu = CreateMenu(); + Bind(wxEVT_RIGHT_UP, &AIHTestFrame::ShowMenu, this); + Bind(wxEVT_LEFT_DCLICK, &AIHTestFrame::DeleteMenu, this); + const wxFileName exe(wxStandardPaths::Get().GetExecutablePath()); + m_indicator = new AppIndicatorHelper(_T("AIH"), + exe.GetPath(wxPATH_GET_SEPARATOR) + _T("kde.png"), + APP_INDICATOR_CATEGORY_APPLICATION_STATUS); + m_indicator->SetTitle((_T("AppIndicator helper test"))); + m_indicator->SetMenu(m_mainMenu); + m_indicator->SetStatus(APP_INDICATOR_STATUS_ACTIVE); +} + +AIHTestFrame::~AIHTestFrame() +{ + delete m_mainMenu; + delete m_indicator; +} + +wxMenu* AIHTestFrame::CreateMenu() +{ + wxMenu * menu = new wxMenu(); + wxString label; + label = _T("SUB ") + wxVariant(m_subID).GetString(); + if (m_mainMenu != NULL) menu->SetTitle(label); + label = _T("New ") + wxVariant(m_itemID).GetString(); + menu->Append(m_itemID, label); + m_itemID++; + label = _T("Delete ") + wxVariant(m_itemID).GetString(); + wxMenuItem * item = new wxMenuItem(menu, m_itemID, label); + item->SetBitmap(stop_xpm); + menu->Append(item); + m_itemID++; + menu->AppendSeparator(); + + label = _T("RadioA ") + wxVariant(m_itemID).GetString(); + menu->AppendRadioItem(m_itemID, label); + m_itemID++; + label = _T("RadioB ") + wxVariant(m_itemID).GetString(); + menu->AppendRadioItem(m_itemID, label); + m_itemID++; + menu->AppendSeparator(); + + label = _T("Check ") + wxVariant(m_itemID).GetString(); + menu->AppendCheckItem(m_itemID, label); + m_itemID++; + menu->AppendSeparator(); + + label = _T("RadioC ") + wxVariant(m_itemID).GetString(); + menu->AppendRadioItem(m_itemID, label); + m_itemID++; + label = _T("RadioD ") + wxVariant(m_itemID).GetString(); + menu->AppendRadioItem(m_itemID, label); + m_itemID++; + menu->AppendSeparator(); + + menu->Bind(wxEVT_COMMAND_MENU_SELECTED, &AIHTestFrame::OnMenuItemClick, this); + m_subID++; + return menu; +} + +void AIHTestFrame::ShowMenu(wxMouseEvent& evt) +{ + if (m_mainMenu != NULL) PopupMenu(m_mainMenu); +} + +void AIHTestFrame::DeleteMenu(wxMouseEvent& evt) +{ + wxDELETE(m_mainMenu); + // If we destroy the wxWidgets reference menu, we must get rid of + // the indicator, to avoid an inevitable crash on item select. + wxDELETE(m_indicator); +} + +/* + * The 'New' and 'Delete' actions are just to show the need of calling SetMenu() + * again. This should be done whenever items are removed, added or modified. + * If check or radio items are clicked in the wxWidgets application, we need not + * call SetMenu(), this is done by AppIndicatorHelper. + */ +void AIHTestFrame::OnMenuItemClick(wxCommandEvent& evt) +{ + wxMenu * menu = static_cast (evt.GetEventObject()); + wxMenuItem * item = menu->FindItem(evt.GetId()); + const wxString label = item->GetItemLabelText(); + if (label.StartsWith(_T("New "))) { + wxMenu * subMenu = CreateMenu(); + wxMenuItem * subItem = menu->AppendSubMenu(subMenu, subMenu->GetTitle()); + subMenu->SetClientData(subItem); + m_indicator->SetMenu(m_mainMenu); // <- + } + if (label.StartsWith(_T("Delete "))) { + if (menu != m_mainMenu) { + wxMenuItem * subItem = static_cast (menu->GetClientData()); + // Can't destroy the submenu when it is shown. + subItem->GetMenu()->Remove(subItem); + m_indicator->SetMenu(m_mainMenu); // <- + // We resort to a trick by deleting the removed menu in idle time. + m_removedMenu = menu; + Bind(wxEVT_IDLE, &AIHTestFrame::OnIdle, this); + } + } + evt.Skip(); +} + +void AIHTestFrame::OnIdle(wxIdleEvent& evt) +{ + wxDELETE(m_removedMenu); + Unbind(wxEVT_IDLE, &AIHTestFrame::OnIdle, this); +} diff --git a/ThirdParty/AppIndicatorHelper/AIHTest.h b/ThirdParty/AppIndicatorHelper/AIHTest.h new file mode 100644 index 0000000..5d885cf --- /dev/null +++ b/ThirdParty/AppIndicatorHelper/AIHTest.h @@ -0,0 +1,47 @@ +/* + * File: AIHTest.h + * Author: nmset@netcourrier.com + * License : irrelevant + * + * Created on 11 septembre 2015, 16:57 + */ + +#ifndef AIHTEST_H +#define AIHTEST_H + +#include +#include "appindic.h" + +class AIHTest : public wxApp { +public: + AIHTest(); + virtual ~AIHTest(); + + bool OnInit(); + int OnExit(); +private: + +}; + +class AIHTestFrame : public wxFrame { +public: + AIHTestFrame(wxWindow * parent, wxWindowID id = wxID_ANY, const wxString& title = wxEmptyString); + virtual ~AIHTestFrame(); +private: + wxMenu * m_mainMenu; + wxMenu * m_removedMenu; + long m_itemID; + long m_subID; + AppIndicatorHelper * m_indicator; + + wxMenu * CreateMenu(); + // The menus is shown here for the purpose of the test. + // A real application needs not show this menu at all. + void ShowMenu(wxMouseEvent& evt); + void DeleteMenu(wxMouseEvent& evt); + void OnMenuItemClick(wxCommandEvent& evt); + void OnIdle(wxIdleEvent& evt); +}; + +#endif /* AIHTEST_H */ + diff --git a/ThirdParty/AppIndicatorHelper/CMakeLists.txt b/ThirdParty/AppIndicatorHelper/CMakeLists.txt new file mode 100644 index 0000000..08d442a --- /dev/null +++ b/ThirdParty/AppIndicatorHelper/CMakeLists.txt @@ -0,0 +1,29 @@ +# +# Stuff the average programmer needs to change +# +set(SRCS + appindic.cpp +) +set(HFILES + appindic.h +) +set(INCLUDE_DIRECTORIES ${BASE_INCLUDE_DIRECTORIES} + /usr/include/libappindicator3-0.1 + /usr/include/gtk-3.0 + /usr/include/glib-2.0 /usr/lib/x86_64-linux-gnu/glib-2.0/include + /usr/include/pango-1.0 + /usr/include/cairo + /usr/include/gdk-pixbuf-2.0 + /usr/include/atk-1.0) +set(LIBRARY_NAME AppIndicatorHelper) +if(WIN32) + set(PREPROCESSOR_DEFINITIONS ${PREPROCESSOR_DEFINITIONS};${wxWidgets_DEFINITIONS};/D_LIB) +endif(WIN32) +set(SRCS ${SRCS} ${HFILES}) + +add_definitions(${PREPROCESSOR_DEFINITIONS}) +include_directories(${INCLUDE_DIRECTORIES}) + +add_library(${LIBRARY_NAME} STATIC ${SRCS}) + +target_link_libraries(${LIBRARY_NAME} ${wxWidgets_LIBRARIES}) diff --git a/ThirdParty/AppIndicatorHelper/Win/AppIndicatorHelper.vcxproj b/ThirdParty/AppIndicatorHelper/Win/AppIndicatorHelper.vcxproj new file mode 100644 index 0000000..52730f7 --- /dev/null +++ b/ThirdParty/AppIndicatorHelper/Win/AppIndicatorHelper.vcxproj @@ -0,0 +1,129 @@ + + + + + Debug + x64 + + + Release + x64 + + + + {19B6E028-60C5-36E7-9F5A-2A047410B7D0} + 10.0.16299.0 + Win32Proj + x64 + AppIndicatorHelper + NoUpgrade + + + + StaticLibrary + Unicode + v141 + + + StaticLibrary + Unicode + v141 + + + + + + + + + + <_ProjectFileVersion>10.0.20506.1 + F:\IT-Dim\trunk\ThirdParty\AppIndicatorHelper\Win\Debug\ + AppIndicatorHelper.dir\Debug\ + AppIndicatorHelper + .lib + F:\IT-Dim\trunk\ThirdParty\AppIndicatorHelper\Win\Release\ + AppIndicatorHelper.dir\Release\ + AppIndicatorHelper + .lib + + + + F:\IT-Dim\wxWidgets\lib\vc_x64_dll\mswu;F:\IT-Dim\wxWidgets\include;F:\IT-Dim\trunk\ThirdParty\build\..\..\include;F:\IT-Dim\trunk\ThirdParty\build\..\..\ThirdParty\GLEW\include;\usr\include\libappindicator3-0.1;\usr\include\gtk-3.0;\usr\include\glib-2.0;\usr\lib\x86_64-linux-gnu\glib-2.0\include;\usr\include\pango-1.0;\usr\include\cairo;\usr\include\gdk-pixbuf-2.0;\usr\include\atk-1.0;%(AdditionalIncludeDirectories) + Debug/ + EnableFastChecks + CompileAsCpp + ProgramDatabase + 4996 + Sync + Disabled + true + Disabled + NotUsing + MultiThreadedDebugDLL + true + Level3 + WIN32;_WINDOWS;__WXDEBUG__=1;WXUSINGDLL;UNICODE;_UNICODE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;_LIB;CMAKE_INTDIR="Debug";%(PreprocessorDefinitions) + $(IntDir) + + + WIN32;_DEBUG;_WINDOWS;__WXDEBUG__=1;WXUSINGDLL;UNICODE;_UNICODE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;_LIB;CMAKE_INTDIR=\"Debug\";%(PreprocessorDefinitions) + F:\IT-Dim\wxWidgets\lib\vc_x64_dll\mswu;F:\IT-Dim\wxWidgets\include;F:\IT-Dim\trunk\ThirdParty\build\..\..\include;F:\IT-Dim\trunk\ThirdParty\build\..\..\ThirdParty\GLEW\include;\usr\include\libappindicator3-0.1;\usr\include\gtk-3.0;\usr\include\glib-2.0;\usr\lib\x86_64-linux-gnu\glib-2.0\include;\usr\include\pango-1.0;\usr\include\cairo;\usr\include\gdk-pixbuf-2.0;\usr\include\atk-1.0;%(AdditionalIncludeDirectories) + + + F:\IT-Dim\wxWidgets\lib\vc_x64_dll\mswu;F:\IT-Dim\wxWidgets\include;F:\IT-Dim\trunk\ThirdParty\build\..\..\include;F:\IT-Dim\trunk\ThirdParty\build\..\..\ThirdParty\GLEW\include;\usr\include\libappindicator3-0.1;\usr\include\gtk-3.0;\usr\include\glib-2.0;\usr\lib\x86_64-linux-gnu\glib-2.0\include;\usr\include\pango-1.0;\usr\include\cairo;\usr\include\gdk-pixbuf-2.0;\usr\include\atk-1.0;%(AdditionalIncludeDirectories) + $(ProjectDir)/$(IntDir) + %(Filename).h + %(Filename).tlb + %(Filename)_i.c + %(Filename)_p.c + + + %(AdditionalOptions) /machine:x64 + + + + + F:\IT-Dim\wxWidgets\lib\vc_x64_dll\mswu;F:\IT-Dim\wxWidgets\include;F:\IT-Dim\trunk\ThirdParty\build\..\..\include;F:\IT-Dim\trunk\ThirdParty\build\..\..\ThirdParty\GLEW\include;\usr\include\libappindicator3-0.1;\usr\include\gtk-3.0;\usr\include\glib-2.0;\usr\lib\x86_64-linux-gnu\glib-2.0\include;\usr\include\pango-1.0;\usr\include\cairo;\usr\include\gdk-pixbuf-2.0;\usr\include\atk-1.0;%(AdditionalIncludeDirectories) + Release/ + CompileAsCpp + 4996 + Sync + AnySuitable + true + MaxSpeed + NotUsing + MultiThreadedDLL + true + Level3 + WIN32;_WINDOWS;NDEBUG;WXUSINGDLL;UNICODE;_UNICODE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;__WXDEBUG__=1;_LIB;CMAKE_INTDIR="Release";%(PreprocessorDefinitions) + $(IntDir) + + + + + WIN32;_WINDOWS;NDEBUG;WXUSINGDLL;UNICODE;_UNICODE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;__WXDEBUG__=1;_LIB;CMAKE_INTDIR=\"Release\";%(PreprocessorDefinitions) + F:\IT-Dim\wxWidgets\lib\vc_x64_dll\mswu;F:\IT-Dim\wxWidgets\include;F:\IT-Dim\trunk\ThirdParty\build\..\..\include;F:\IT-Dim\trunk\ThirdParty\build\..\..\ThirdParty\GLEW\include;\usr\include\libappindicator3-0.1;\usr\include\gtk-3.0;\usr\include\glib-2.0;\usr\lib\x86_64-linux-gnu\glib-2.0\include;\usr\include\pango-1.0;\usr\include\cairo;\usr\include\gdk-pixbuf-2.0;\usr\include\atk-1.0;%(AdditionalIncludeDirectories) + + + F:\IT-Dim\wxWidgets\lib\vc_x64_dll\mswu;F:\IT-Dim\wxWidgets\include;F:\IT-Dim\trunk\ThirdParty\build\..\..\include;F:\IT-Dim\trunk\ThirdParty\build\..\..\ThirdParty\GLEW\include;\usr\include\libappindicator3-0.1;\usr\include\gtk-3.0;\usr\include\glib-2.0;\usr\lib\x86_64-linux-gnu\glib-2.0\include;\usr\include\pango-1.0;\usr\include\cairo;\usr\include\gdk-pixbuf-2.0;\usr\include\atk-1.0;%(AdditionalIncludeDirectories) + $(ProjectDir)/$(IntDir) + %(Filename).h + %(Filename).tlb + %(Filename)_i.c + %(Filename)_p.c + + + %(AdditionalOptions) /machine:x64 + + + + + + + + + + + + \ No newline at end of file diff --git a/ThirdParty/AppIndicatorHelper/Win/AppIndicatorHelper.vcxproj.filters b/ThirdParty/AppIndicatorHelper/Win/AppIndicatorHelper.vcxproj.filters new file mode 100644 index 0000000..e9f1875 --- /dev/null +++ b/ThirdParty/AppIndicatorHelper/Win/AppIndicatorHelper.vcxproj.filters @@ -0,0 +1,21 @@ + + + + + Source Files + + + + + Header Files + + + + + {51D244FA-C21C-3174-8217-FDB3769212C0} + + + {A07A887B-0B8A-3ED7-BD7F-43C26975F88D} + + + diff --git a/ThirdParty/AppIndicatorHelper/appindic.cpp b/ThirdParty/AppIndicatorHelper/appindic.cpp new file mode 100644 index 0000000..c89ac81 --- /dev/null +++ b/ThirdParty/AppIndicatorHelper/appindic.cpp @@ -0,0 +1,171 @@ +/* + * File: appindic.cpp + * Author: nmset@netcourrier.com (Work done as hobbyist) + * License: LGPL version 2.1 + * Copyright: nmset@netcourrier.com + * + * Created on 8 septembre 2015, 21:33 + */ + +#ifdef __WXGTK__ + +#include "appindic.h" + +IMPLEMENT_DYNAMIC_CLASS(AppIndicatorHelper, wxObject) + +AppIndicatorHelper::AppIndicatorHelper() +:wxObject() +{ + m_appMenu = NULL; +} + +AppIndicatorHelper::AppIndicatorHelper(const wxString& id, + const wxString& iconFileName, + const wxString& iconDirPath, + AppIndicatorCategory category) +:wxObject() +{ + m_appMenu = NULL; + printf("Icon: %s\n", iconFileName.utf8_str().data()); + m_indicator = app_indicator_new_with_path(id.utf8_str().data(), + iconFileName.utf8_str().data(), + category, + iconDirPath.utf8_str().data()); +} + +AppIndicatorHelper::AppIndicatorHelper(const wxString& id, const wxString& iconFullFilePath, AppIndicatorCategory category) +:wxObject() +{ + m_appMenu = NULL; + printf("Icon: %s\n", iconFullFilePath.utf8_str().data()); + m_indicator = app_indicator_new(id.utf8_str().data(), + iconFullFilePath.utf8_str().data(), + category); + if(m_indicator != NULL) + { + printf("Indicator created.\n"); + } +} + + +AppIndicatorHelper::~AppIndicatorHelper() +{ + g_object_unref((gpointer) m_indicator); +} + +// Main wrapper functions. +void AppIndicatorHelper::SetTitle(const wxString& title) +{ + app_indicator_set_title(m_indicator, title.utf8_str().data()); +} + +const wxString AppIndicatorHelper::GetTitle() const +{ + return app_indicator_get_title(m_indicator); +} + +void AppIndicatorHelper::SetStatus(AppIndicatorStatus status) +{ + app_indicator_set_status(m_indicator, status); +} + +const AppIndicatorStatus AppIndicatorHelper::GetStatus() const +{ + return app_indicator_get_status(m_indicator); +} + +// The main functionality of this class. +void AppIndicatorHelper::SetMenu(wxMenu * appMenu) +{ + wxASSERT_MSG(appMenu != NULL, _T("appMenu is NULL.")); + m_appMenu = appMenu; + app_indicator_set_menu(m_indicator, GTK_MENU(m_appMenu->m_menu)); +} + +// A series of secondary wrapper functions. +const wxString AppIndicatorHelper::GetId() const +{ + return app_indicator_get_id(m_indicator); +} + +const AppIndicatorCategory AppIndicatorHelper::GetCategory() const +{ + return app_indicator_get_category(m_indicator); +} + +void AppIndicatorHelper::SetIconDir(const wxString& iconDir) +{ + app_indicator_set_icon_theme_path(m_indicator, iconDir.utf8_str().data()); +} + +const wxString AppIndicatorHelper::GetIconDir() const +{ + return app_indicator_get_icon_theme_path(m_indicator); +} + +void AppIndicatorHelper::SetIcon(const wxString& iconFileName) +{ + app_indicator_set_icon(m_indicator, iconFileName.utf8_str().data()); +} + +const wxString AppIndicatorHelper::GetIcon() const +{ + return app_indicator_get_icon(m_indicator); +} + +void AppIndicatorHelper::SetIconFull(const wxString& iconFileName, const wxString& iconDesc) +{ + app_indicator_set_icon_full(m_indicator, iconFileName.utf8_str().data(), iconDesc.utf8_str().data()); +} + +const wxString AppIndicatorHelper::GetIconDesc() const +{ + return app_indicator_get_icon_desc(m_indicator); +} + +void AppIndicatorHelper::SetAttentionIcon(const wxString& iconFileName) +{ + app_indicator_set_attention_icon(m_indicator, iconFileName.utf8_str().data()); +} + +const wxString AppIndicatorHelper::GetAttentionIcon() const +{ + return app_indicator_get_attention_icon(m_indicator); +} + +void AppIndicatorHelper::SetAttentionIconFull(const wxString& iconFileName, const wxString& iconDesc) +{ + app_indicator_set_attention_icon_full(m_indicator, iconFileName.utf8_str().data(), iconDesc.utf8_str().data()); +} + +const wxString AppIndicatorHelper::GetAttentionIconDesc() const +{ + return app_indicator_get_attention_icon_desc(m_indicator); +} + +void AppIndicatorHelper::SetLabel(const wxString& label, const wxString& guide) +{ + app_indicator_set_label(m_indicator, label.utf8_str().data(), guide.utf8_str().data()); +} + +const wxString AppIndicatorHelper::GetLabel() const +{ + return app_indicator_get_label(m_indicator); +} + +const wxString AppIndicatorHelper::GetLabelGuide() const +{ + return app_indicator_get_label_guide(m_indicator); +} + +void AppIndicatorHelper::SetOrderingIndex(uint orderingIndex) +{ + app_indicator_set_ordering_index(m_indicator, orderingIndex); +} + +const uint AppIndicatorHelper::GetOrderingIndex() const +{ + return app_indicator_get_ordering_index(m_indicator); +} + +#endif diff --git a/ThirdParty/AppIndicatorHelper/appindic.h b/ThirdParty/AppIndicatorHelper/appindic.h new file mode 100644 index 0000000..b4d3294 --- /dev/null +++ b/ThirdParty/AppIndicatorHelper/appindic.h @@ -0,0 +1,156 @@ +/* + * File: appindic.h + * Author: nmset@netcourrier.com (Work done as hobbyist) + * License: LGPL version 2.1 + * Copyright: nmset@netcourrier.com + * + * Created on 8 septembre 2015, 21:33 + */ + +#ifdef __WXGTK__ +#ifndef APPINDICATORHELPER_H +#define APPINDICATORHELPER_H + +#include +#include + +/** + * This class is a helper for the libappindicator library. It's aim is to + * facilitate 'put an icon' in the system tray with wxWidgets, while hiding + * all C/GTK code. It is intended for WXGTK builds only. + * + * An application must pass a wxMenu object to an instance of + * this class, and it will show the GTK menu member of the wxMenu with a right + * click on the system tray icon. The passed wxMenu may or may not be accessed + * in the wxWidgets application window. + * + * The notion of a 'GTK icon theme' is downplayed. An icon is passed to this + * class with its complete file name and the directory path separately. + * Alternatively, a constructor is provided for a full path to the icon. + * + * The following libappindicator functions are simply ignored : + * app_indicator_set_secondary_activate_target, + * app_indicator_get_secondary_activate_target, + * app_indicator_build_menu_from_desktop. + * + * Note : If an application must delete the wxWidgets reference menu, it must + * delete the AppIndicatorHelper instance. + * + * This project is inspired from + * https://github.com/jarod/libappindicator-qt + * + * @return + */ + +class AppIndicatorHelper : public wxObject { + DECLARE_DYNAMIC_CLASS(AppIndicatorHelper); +public: + AppIndicatorHelper(); + /** + * + * @param id A unique identifier. + * @param iconFileName The filename of an icon, with its extension. + * @param iconDirPath The full path to the icon + * @param category The category of the icon, listed in AppIndicatorCategory + * enum. + */ + AppIndicatorHelper(const wxString& id, + const wxString& iconFileName, + const wxString& iconDirPath, + AppIndicatorCategory category); + /** + * + * @param id A unique identifier. + * @param iconFullFilePath The absolute path of an icon, with its extension. + * @param iconDirPath The full path to the icon + * @param category The category of the icon, listed in AppIndicatorCategory + * enum. + */ + AppIndicatorHelper(const wxString& id, + const wxString& iconFullFilePath, + AppIndicatorCategory category); + virtual ~AppIndicatorHelper(); + + void SetTitle(const wxString& title); + const wxString GetTitle() const; + void SetStatus(AppIndicatorStatus status); + const AppIndicatorStatus GetStatus() const; + /** + * + * @param appMenu A wxMenu managed in your application, which may or may not + * show this menu. + * + * No icon will appear in the system tray without a menu. + */ + void SetMenu(wxMenu * appMenu); + + const wxString GetId() const; + const AppIndicatorCategory GetCategory() const; + void SetIconDir(const wxString& iconDir); + const wxString GetIconDir() const; + void SetIcon(const wxString& iconFileName); + const wxString GetIcon() const; + /** + * + * @param iconFileName + * @param iconDesc From app-indicator.c + * The description of the regular icon that is shown for the indicator. + */ + void SetIconFull(const wxString& iconFileName, const wxString& iconDesc); + const wxString GetIconDesc() const; + /** + * + * @param iconFileName The file name with extension of an icon to use when + * the status is set to APP_INDICATOR_STATUS_ATTENTION. The icon must be in + * the directory given by GetIconDir(). + */ + void SetAttentionIcon(const wxString& iconFileName); + const wxString GetAttentionIcon() const; + void SetAttentionIconFull(const wxString& iconFileName, const wxString& iconDesc); + const wxString GetAttentionIconDesc() const; + /** + * + * @param label From app-indicator.c + * A label that can be shown next to the string in the application + * indicator. The label will not be shown unless there is an icon + * as well. The label is useful for numerical and other frequently + * updated information. In general, it shouldn't be shown unless a + * user requests it as it can take up a significant amount of space + * on the user's panel. This may not be shown in all visualizations. + * @param guide From app-indicator.c + * An optional string to provide guidance to the panel on how big + * the #AppIndicator:label string could get. If this is set correctly + * then the panel should never 'jiggle' as the string adjusts through + * out the range of options. For instance, if you were providing a + * percentage like "54% thrust" in #AppIndicator:label you'd want to + * set this string to "100% thrust" to ensure space when Scotty can + * get you enough power. + */ + void SetLabel(const wxString& label, const wxString& guide); + const wxString GetLabel() const; + const wxString GetLabelGuide()const; + /** + * + * @param orderingIndex From app-indicator.c + * The ordering index is an odd parameter, and if you think you don't need + * it you're probably right. In general, the application indicator try + * to place the applications in a recreatable place taking into account + * which category they're in to try and group them. But, there are some + * cases where you'd want to ensure indicators are next to each other. + * To do that you can override the generated ordering index and replace it + * with a new one. Again, you probably don't want to be doing this, but + * in case you do, this is the way. + */ + void SetOrderingIndex(uint orderingIndex); + const uint GetOrderingIndex() const; +private: + AppIndicator * m_indicator; + //The application's wxMenu. + wxWeakRef m_appMenu; + //m_appMenu.SetIndicator(m_indicator); + +}; + +#endif /* APPINDICATORHELPER_H */ +#endif + diff --git a/ThirdParty/AppIndicatorHelper/build.sh b/ThirdParty/AppIndicatorHelper/build.sh new file mode 100644 index 0000000..ac8c9bd --- /dev/null +++ b/ThirdParty/AppIndicatorHelper/build.sh @@ -0,0 +1,20 @@ +#!/bin/sh + +$(wx-config --cxx --cppflags --libs) \ + -I/usr/lib/x86_64-linux-gnu/gtk-2.0/include \ + -I/usr/lib/x86_64-linux-gnu/glib-2.0/include \ + -I/usr/include/libappindicator3-0.1 \ + -I/usr/include/gtk-3.0 \ + -I/usr/include/glib-2.0 \ + -I/usr/lib/glib-2.0/include \ + -I/usr/include/cairo \ + -I/usr/include/pango-1.0 \ + -I/usr/lib/gtk-2.0/include \ + -I/usr/include/gdk-pixbuf-2.0 \ + -I/usr/include/atk-1.0 \ + -lappindicator3 \ + -lgobject-2.0 \ + -lgtk-x11-2.0 \ + appindic.cpp AIHTest.cpp -o aihtest `wx-config --libs` -lappindicator3 -lgtk-3 -lgobject-2.0 -lgtk-x11-2.0 -Wno-deprecated-declarations + +exit 0 diff --git a/ThirdParty/AppIndicatorHelper/gpl-2.0.txt b/ThirdParty/AppIndicatorHelper/gpl-2.0.txt new file mode 100644 index 0000000..d159169 --- /dev/null +++ b/ThirdParty/AppIndicatorHelper/gpl-2.0.txt @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the 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. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/ThirdParty/AppIndicatorHelper/kde.png b/ThirdParty/AppIndicatorHelper/kde.png new file mode 100644 index 0000000000000000000000000000000000000000..f8f543dddb1b49b0c9743d640f0eedf3a0996f4e GIT binary patch literal 1467 zcmV;s1w{IZP)kGnX`(NpppkRISmfAhs0j zlZarf`ckB#6_X-fP+G7l^~nn=mLe!v@S)fjp%BcIRYVFt)TTlzQJaEIOq|JO&di*1 z_Sx69*XP9Ar@r|6iK_B85wU>qP68j`Ew8dmc_-b~@;{=g$Va}_8M?J6I-P|_JLzaS zb`jPXtQCYnAmC+=*PO`qh_pi_UE;V)WD`^jRXLq)Pd1(#umAP**0~={oa}PPzwynb zqX(Wma`*sI?1^k(*pJz>JYr?kXKA6su$K^mGRhm3~TlwKMmV)@sbJE#f^dj|{$F*fdyq+Ldf z3v5p_Cew^7S0|Y6>SusQ9bk;Or6?69*^FVohnN_Z7*9l_j@fp>$&Yu5oZ;y+bNYK0 z$R`<}e!t>u!nyM^elv;ilwgxSU<|v&7%8g)6^C^RY7&A(_+V**p=cmY65=?a^iWjF z1IH|P9T{?PUzhXOBI16JZg+qY1Kc474qLHS+MpO06C@&tWx6pYuUoRpGcOvVD8gAw zQMW9IE$(~Y0?tLe@Y0NVXA!Xx-TnZvMpzqK!`j`(7>l?Vh-k4yvkBjSnw|UynU}lq53DE9K|E+o24BriE68x>m$m8FiMdS?XIO)=6C}twQAk zvpD6~f4D)}!0BJS!ItYFB8UiUD+wSNfg&%+>zcf-scKJIwTQKZ zhR}M(n^V4fV#t~A+{H)lS>jKBpHO?y*6_9$U$1%b&zszG-+LFiyH~P3&1jm6HZ*|H zr;ia^Dkwocb(NDi5R|qJY-Wb0hPDM&1(c>0esuOGm#*d9^6k&xNw3WDb&2;iB1%Sv zF@*+p`KCg(<)LRT^2yV$apBE5cdl5}TS8+9A)u-lVq7}n^s|@AOV6NVc>Gg`(ApE~ z5)A>V3Bs{Q27|TZHx^e{QyV1=dLvd=_VeI-gaG2I-8rg&}HFulB-(R;Nj3xaECIyHxS6|rr2 z<4VKtUoJWI;E26T5Q3sQ!Z{(00#AOf&lA5Y@pVmI%&C1t@Lrk!#XCSl>(yYX%Q;n5 za&DvLt3SL%k?v=CpvhIX5>2mVN6~-9oPVr4cS>@#Uj9IqB zbh<@kQ}&GZadrKbKLV|&Dzg7WE8WH8&-eGjO@`N;$o7e} zOC(+5xJzUcRD^uK)m*#u(yKRLKl7z(eBpwqDz}J;14h7pKmg(Yqr7DkxDI&T^&e6L Vr)Oo}&?o=^002ovPDHLkV1i=q$Swc? literal 0 HcmV?d00001 diff --git a/ThirdParty/AppIndicatorHelper/lgpl-2.1.txt b/ThirdParty/AppIndicatorHelper/lgpl-2.1.txt new file mode 100644 index 0000000..4362b49 --- /dev/null +++ b/ThirdParty/AppIndicatorHelper/lgpl-2.1.txt @@ -0,0 +1,502 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! diff --git a/ThirdParty/AppIndicatorHelper/stop.xpm b/ThirdParty/AppIndicatorHelper/stop.xpm new file mode 100644 index 0000000..fc2a7ab --- /dev/null +++ b/ThirdParty/AppIndicatorHelper/stop.xpm @@ -0,0 +1,400 @@ +/* XPM */ +static char * stop_xpm[] = { +"32 32 365 2", +" c None", +". c #AB2F2F", +"+ c #BF4242", +"@ c #BE4242", +"# c #BD4040", +"$ c #BD3F3F", +"% c #B33434", +"& c #C14545", +"* c #EC7676", +"= c #E35B5B", +"- c #E35A5A", +"; c #E35959", +"> c #E25858", +", c #E25656", +"' c #E25454", +") c #E96666", +"! c #CE4F4F", +"~ c #C24747", +"{ c #EC7272", +"] c #D94141", +"^ c #D94040", +"/ c #D93F3F", +"( c #D93E3E", +"_ c #D83D3D", +": c #D83C3C", +"< c #D83B3B", +"[ c #D83A3A", +"} c #E65E5E", +"| c #CD4D4D", +"1 c #C44949", +"2 c #EC7373", +"3 c #D94242", +"4 c #E65D5D", +"5 c #CC4949", +"6 c #C64B4B", +"7 c #EC7171", +"8 c #DA4343", +"9 c #DA4444", +"0 c #D83939", +"a c #E65C5C", +"b c #C84545", +"c c #EB7171", +"d c #DA4545", +"e c #D73838", +"f c #E55B5B", +"g c #C64242", +"h c #C64D4D", +"i c #EB7070", +"j c #DB4343", +"k c #DF3D3D", +"l c #DA4646", +"m c #D93B3B", +"n c #DE3232", +"o c #D73A3A", +"p c #D63838", +"q c #D53737", +"r c #E65959", +"s c #C43D3D", +"t c #C84F4F", +"u c #EB6E6E", +"v c #F32424", +"w c #F91B1B", +"x c #E23A3A", +"y c #DA3D3D", +"z c #F22424", +"A c #F91C1C", +"B c #E13030", +"C c #D63A3A", +"D c #D53939", +"E c #D33737", +"F c #D23636", +"G c #E35858", +"H c #C13B3B", +"I c #E96C6C", +"J c #DB4141", +"K c #F32525", +"L c #E1AAAA", +"M c #DBD0D0", +"N c #F43434", +"O c #E23939", +"P c #DA3E3E", +"Q c #E4A4A4", +"R c #DBD3D3", +"S c #F23838", +"T c #E02F2F", +"U c #D43939", +"V c #D13636", +"W c #D03535", +"X c #E35555", +"Y c #BD3737", +"Z c #950E0E", +"` c #F07E7E", +" . c #F81616", +".. c #DBDADA", +"+. c #DCDBDB", +"@. c #DED3D3", +"#. c #F43535", +"$. c #E23737", +"%. c #DA3F3F", +"&. c #F32626", +"*. c #E6A7A7", +"=. c #DDDCDC", +"-. c #DCD4D4", +";. c #F72A2A", +">. c #DC2F2F", +",. c #D23737", +"'. c #CE3333", +"). c #E75E5E", +"!. c #910A0A", +"~. c #970D0D", +"{. c #EC2C2C", +"]. c #EC6D6D", +"^. c #DEDDDD", +"/. c #DFDEDE", +"(. c #E1D6D6", +"_. c #F53B3B", +":. c #E13838", +"<. c #F32E2E", +"[. c #E8ABAB", +"}. c #E1E0E0", +"|. c #E0DFDF", +"1. c #E3BCBC", +"2. c #F33333", +"3. c #D73535", +"4. c #D13737", +"5. c #D03636", +"6. c #CF3434", +"7. c #950C0C", +"8. c #EC3333", +"9. c #ED7474", +"0. c #E2E1E1", +"a. c #E3E2E2", +"b. c #E5DBDB", +"c. c #F54545", +"d. c #F92727", +"e. c #EAB0B0", +"f. c #E4E3E3", +"g. c #E6C1C1", +"h. c #F43C3C", +"i. c #D53838", +"j. c #CF3535", +"k. c #CE3434", +"l. c #CD3333", +"m. c #E15454", +"n. c #930A0A", +"o. c #EC3838", +"p. c #EF7C7C", +"q. c #E5E4E4", +"r. c #E6E5E5", +"s. c #E8DEDE", +"t. c #EBC9C9", +"u. c #E7E6E6", +"v. c #E9C5C5", +"w. c #F44343", +"x. c #D13838", +"y. c #D03737", +"z. c #CF3636", +"A. c #CB3232", +"B. c #E05454", +"C. c #ED3F3F", +"D. c #F18484", +"E. c #E8E7E7", +"F. c #E9E8E8", +"G. c #EAE9E9", +"H. c #ECCACA", +"I. c #F54C4C", +"J. c #CE3535", +"K. c #CD3434", +"L. c #CB3333", +"M. c #CA3232", +"N. c #DF5151", +"O. c #EC4444", +"P. c #F57D7D", +"Q. c #EBEAEA", +"R. c #ECEBEB", +"S. c #EED8D8", +"T. c #F94C4C", +"U. c #D53A3A", +"V. c #CC2929", +"W. c #C92020", +"X. c #C51919", +"Y. c #C41616", +"Z. c #C21212", +"`. c #C11414", +" + c #DB3939", +".+ c #900404", +"++ c #EB6F6F", +"@+ c #D93C3C", +"#+ c #F45050", +"$+ c #F1BFBF", +"%+ c #EEEDED", +"&+ c #EFEEEE", +"*+ c #EFE7E7", +"=+ c #F76969", +"-+ c #D72A2A", +";+ c #C60C0C", +">+ c #C20101", +",+ c #C10000", +"'+ c #BF0000", +")+ c #BE0000", +"!+ c #BD0000", +"~+ c #BC0000", +"{+ c #BB0000", +"]+ c #D82727", +"^+ c #8E0000", +"/+ c #960C0C", +"(+ c #D73737", +"_+ c #D93A3A", +":+ c #F45757", +"<+ c #F1C3C3", +"[+ c #F0EFEF", +"}+ c #F1F0F0", +"|+ c #F2F1F1", +"1+ c #F1E9E9", +"2+ c #F56E6E", +"3+ c #D01414", +"4+ c #C00000", +"5+ c #BA0000", +"6+ c #D72626", +"7+ c #EB6D6D", +"8+ c #D73636", +"9+ c #D83838", +"0+ c #F35D5D", +"a+ c #F1C5C5", +"b+ c #F3F2F2", +"c+ c #F6DADA", +"d+ c #F7A4A4", +"e+ c #F4F3F3", +"f+ c #F1EAEA", +"g+ c #F57777", +"h+ c #CD1717", +"i+ c #B90000", +"j+ c #D72525", +"k+ c #EB6C6C", +"l+ c #D73434", +"m+ c #F36363", +"n+ c #F0C7C7", +"o+ c #F7DDDD", +"p+ c #F46969", +"q+ c #E84848", +"r+ c #F7A6A6", +"s+ c #F5F4F4", +"t+ c #F48080", +"u+ c #CC1A1A", +"v+ c #B80000", +"w+ c #D62424", +"x+ c #EB6969", +"y+ c #D63333", +"z+ c #F76868", +"A+ c #EFC7C7", +"B+ c #EDECEC", +"C+ c #F7DFDF", +"D+ c #F46F6F", +"E+ c #C90909", +"F+ c #C40000", +"G+ c #E44A4A", +"H+ c #F7ADAD", +"I+ c #F6F5F5", +"J+ c #EFE9E9", +"K+ c #F88686", +"L+ c #C81919", +"M+ c #B60000", +"N+ c #D42323", +"O+ c #8D0000", +"P+ c #910606", +"Q+ c #E76A6A", +"R+ c #DD4444", +"S+ c #D63232", +"T+ c #ED6363", +"U+ c #F2AFAF", +"V+ c #F5DEDE", +"W+ c #F47777", +"X+ c #C20000", +"Y+ c #E25050", +"Z+ c #F7B2B2", +"`+ c #F1E1E1", +" @ c #F38383", +".@ c #C41414", +"+@ c #B60101", +"@@ c #DA2727", +"#@ c #980E0E", +"$@ c #E46464", +"%@ c #DE4343", +"&@ c #D63131", +"*@ c #D52E2E", +"=@ c #D12222", +"-@ c #E95D5D", +";@ c #F3B3B3", +">@ c #F3DFDF", +",@ c #F47E7E", +"'@ c #C90A0A", +")@ c #C30000", +"!@ c #E25757", +"~@ c #F5B7B7", +"{@ c #F4E5E5", +"]@ c #F28B8B", +"^@ c #C10E0E", +"/@ c #B70000", +"(@ c #D21F1F", +"_@ c #AE1111", +":@ c #950A0A", +"<@ c #E14C4C", +"[@ c #D82020", +"}@ c #CC0505", +"|@ c #CA0000", +"1@ c #C90000", +"2@ c #C80000", +"3@ c #E75D5D", +"4@ c #F98A8A", +"5@ c #C90B0B", +"6@ c #E05A5A", +"7@ c #FA9494", +"8@ c #D32121", +"9@ c #AB1010", +"0@ c #910404", +"a@ c #DD3F3F", +"b@ c #D31616", +"c@ c #C70000", +"d@ c #C60000", +"e@ c #D32222", +"f@ c #A80F0F", +"g@ c #910303", +"h@ c #DC3D3D", +"i@ c #D21515", +"j@ c #B70202", +"k@ c #A50D0D", +"l@ c #DB3A3A", +"m@ c #CF1515", +"n@ c #B70303", +"o@ c #D52424", +"p@ c #A20C0C", +"q@ c #8D0303", +"r@ c #D83737", +"s@ c #CF1414", +"t@ c #B90303", +"u@ c #D32424", +"v@ c #9D0B0B", +"w@ c #880303", +"x@ c #D63434", +"y@ c #CD1313", +"z@ c #B80404", +"A@ c #D42626", +"B@ c #980A0A", +"C@ c #840202", +"D@ c #CD2D2D", +"E@ c #D43030", +"F@ c #D32F2F", +"G@ c #D32D2D", +"H@ c #D12D2D", +"I@ c #D12B2B", +"J@ c #D12A2A", +"K@ c #D02828", +"L@ c #D02727", +"M@ c #D02626", +"N@ c #CF2424", +"O@ c #CF2626", +"P@ c #940909", +"Q@ c #560000", +"R@ c #510000", +"S@ c #4F0000", +"T@ c #4C0000", +"U@ c #4B0000", +"V@ c #4A0000", +"W@ c #4E0000", +"X@ c #540000", +" . + + + + + + @ # # $ % ", +" & * = = - - - ; > > , ' ) ! ", +" ~ { ] ^ ^ ^ ^ / ( ( _ : < [ } | ", +" 1 2 3 3 3 3 3 ] ^ ^ / ( _ : < [ 4 5 ", +" 6 7 3 8 8 9 8 8 3 3 ] ^ / ( : < [ 0 a b ", +" 6 c 3 8 9 d d d 9 9 8 3 ] / ( _ : < 0 e f g ", +" h i 3 8 9 d j k l l d 8 3 ] ^ / m n < o p q r s ", +" t u ] 3 8 d j v w x l d 9 8 ] ^ y z A B C D E F G H ", +" t I / ] 3 8 J K L M N O d 9 3 ] P z Q R S T U E V W X Y ", +" Z ` ( / ^ ] 3 .L ..+.@.#.$.8 3 %.&.*.=.+.-.;.>.,.V W '.).!. ", +" ~.2 _ ( ^ ] 3 {.].=.^./.(._.:.%.<.[.}.|./.1.2.3.4.5.6.'., 7. ", +" ~.{ _ ( / / ^ ] 8.9.}.0.a.b.c.d.e.f.a.0.g.h.i.4.5.j.k.l.m.n. ", +" ~.{ : _ _ ( / ^ ^ o.p.f.q.r.s.t.u.r.r.v.w.i.x.y.z.k.l.A.B.n. ", +" ~.{ < < : _ ( ( / / C.D.E.F.F.G.F.F.H.I.D x.y.z.J.K.L.M.N.n. ", +" ~.7 0 [ < : : _ _ _ _ O.P.Q.R.R.R.S.T.U.x.6.V.W.X.Y.Z.`. +.+ ", +" ~.++e 0 [ [ < < < : @+#+$+%+&+&+&+*+=+-+;+>+,+'+)+!+~+{+]+^+ ", +" /+++(+e e 0 0 [ [ _+:+<+&+[+}+|+}+}+1+2+3+4+'+)+!+~+{+5+6+^+ ", +" /+7+8+8+(+(+e e 9+0+a+&+}+b+c+d+e+b+|+f+g+h+)+!+~+{+5+i+j+^+ ", +" /+k+l+3.3.8+8+9+m+n+&+}+b+o+p+q+r+s+e+|+f+t+u+~+{+5+i+v+w+^+ ", +" /+x+y+y+l+l+3.z+A+B+[+|+C+D+E+F+G+H+I+b+}+J+K+L+5+i+v+M+N+O+ ", +" P+Q+R+S+S+y+y+T+U+%+[+V+W+E+F+X+,+Y+Z+e+|+`+ @.@i+v+M++@@@O+ ", +" #@$@%@&@&@*@=@-@;@>@,@'@)@X+,+4+'+!@~@{@]@^@i+/@M++@(@_@ ", +" :@<@[@}@|@1@2@3@4@5@)@X+,+4+'+)+!+6@7@^@i+/@M++@8@9@ ", +" 0@a@b@1@2@c@d@F+)@X+,+4+'+)+!+~+{+5+v+/@M++@e@f@ ", +" g@h@i@c@d@F+)@X+,+4+'+)+!+~+{+5+v+/@M+j@N+k@ ", +" g@l@m@F+)@X+,+4+'+)+!+~+{+i+v+/@M+n@o@p@ ", +" q@r@s@X+,+4+'+)+!+~+5+i+v+/@M+t@u@v@ ", +" w@x@y@4+'+)+!+~+5+i+v+/@M+z@A@B@ ", +" C@D@E@F@G@H@I@J@K@L@M@N@O@P@ ", +" Q@R@S@T@U@V@V@U@W@R@X@ ", +" ", +" "}; diff --git a/ThirdParty/AppIndicatorHelper/tags b/ThirdParty/AppIndicatorHelper/tags new file mode 100644 index 0000000..230374d --- /dev/null +++ b/ThirdParty/AppIndicatorHelper/tags @@ -0,0 +1,175 @@ +!_TAG_FILE_FORMAT 2 /extended format; --format=1 will not append ;" to lines/ +!_TAG_FILE_SORTED 1 /0=unsorted, 1=sorted, 2=foldcase/ +!_TAG_PROGRAM_AUTHOR Darren Hiebert /dhiebert@users.sourceforge.net/ +!_TAG_PROGRAM_NAME Exuberant Ctags // +!_TAG_PROGRAM_URL http://ctags.sourceforge.net /official site/ +!_TAG_PROGRAM_VERSION 5.9~svn20110310 // +AIHTEST_H AIHTest.h 10;" d +AIHTest AIHTest.cpp /^AIHTest::AIHTest()$/;" f class:AIHTest signature:() +AIHTest AIHTest.h /^ AIHTest();$/;" p class:AIHTest access:public signature:() +AIHTest AIHTest.h /^class AIHTest : public wxApp {$/;" c inherits:wxApp +AIHTest::AIHTest AIHTest.cpp /^AIHTest::AIHTest()$/;" f class:AIHTest signature:() +AIHTest::AIHTest AIHTest.h /^ AIHTest();$/;" p class:AIHTest access:public signature:() +AIHTest::OnExit AIHTest.cpp /^int AIHTest::OnExit()$/;" f class:AIHTest signature:() +AIHTest::OnExit AIHTest.h /^ int OnExit();$/;" p class:AIHTest access:public signature:() +AIHTest::OnInit AIHTest.cpp /^bool AIHTest::OnInit()$/;" f class:AIHTest signature:() +AIHTest::OnInit AIHTest.h /^ bool OnInit();$/;" p class:AIHTest access:public signature:() +AIHTest::~AIHTest AIHTest.cpp /^AIHTest::~AIHTest()$/;" f class:AIHTest signature:() +AIHTest::~AIHTest AIHTest.h /^ virtual ~AIHTest();$/;" p class:AIHTest access:public signature:() +AIHTestFrame AIHTest.cpp /^AIHTestFrame::AIHTestFrame(wxWindow* parent, wxWindowID id, const wxString& title)$/;" f class:AIHTestFrame signature:(wxWindow* parent, wxWindowID id, const wxString& title) +AIHTestFrame AIHTest.h /^ AIHTestFrame(wxWindow * parent, wxWindowID id = wxID_ANY, const wxString& title = wxEmptyString);$/;" p class:AIHTestFrame access:public signature:(wxWindow * parent, wxWindowID id = wxID_ANY, const wxString& title = wxEmptyString) +AIHTestFrame AIHTest.h /^class AIHTestFrame : public wxFrame {$/;" c inherits:wxFrame +AIHTestFrame::AIHTestFrame AIHTest.cpp /^AIHTestFrame::AIHTestFrame(wxWindow* parent, wxWindowID id, const wxString& title)$/;" f class:AIHTestFrame signature:(wxWindow* parent, wxWindowID id, const wxString& title) +AIHTestFrame::AIHTestFrame AIHTest.h /^ AIHTestFrame(wxWindow * parent, wxWindowID id = wxID_ANY, const wxString& title = wxEmptyString);$/;" p class:AIHTestFrame access:public signature:(wxWindow * parent, wxWindowID id = wxID_ANY, const wxString& title = wxEmptyString) +AIHTestFrame::CreateMenu AIHTest.cpp /^wxMenu* AIHTestFrame::CreateMenu()$/;" f class:AIHTestFrame signature:() +AIHTestFrame::CreateMenu AIHTest.h /^ wxMenu * CreateMenu();$/;" p class:AIHTestFrame access:private signature:() +AIHTestFrame::DeleteMenu AIHTest.cpp /^void AIHTestFrame::DeleteMenu(wxMouseEvent& evt)$/;" f class:AIHTestFrame signature:(wxMouseEvent& evt) +AIHTestFrame::DeleteMenu AIHTest.h /^ void DeleteMenu(wxMouseEvent& evt);$/;" p class:AIHTestFrame access:private signature:(wxMouseEvent& evt) +AIHTestFrame::OnIdle AIHTest.cpp /^void AIHTestFrame::OnIdle(wxIdleEvent& evt)$/;" f class:AIHTestFrame signature:(wxIdleEvent& evt) +AIHTestFrame::OnIdle AIHTest.h /^ void OnIdle(wxIdleEvent& evt);$/;" p class:AIHTestFrame access:private signature:(wxIdleEvent& evt) +AIHTestFrame::OnMenuItemClick AIHTest.cpp /^void AIHTestFrame::OnMenuItemClick(wxCommandEvent& evt)$/;" f class:AIHTestFrame signature:(wxCommandEvent& evt) +AIHTestFrame::OnMenuItemClick AIHTest.h /^ void OnMenuItemClick(wxCommandEvent& evt);$/;" p class:AIHTestFrame access:private signature:(wxCommandEvent& evt) +AIHTestFrame::ShowMenu AIHTest.cpp /^void AIHTestFrame::ShowMenu(wxMouseEvent& evt)$/;" f class:AIHTestFrame signature:(wxMouseEvent& evt) +AIHTestFrame::ShowMenu AIHTest.h /^ void ShowMenu(wxMouseEvent& evt);$/;" p class:AIHTestFrame access:private signature:(wxMouseEvent& evt) +AIHTestFrame::m_indicator AIHTest.h /^ AppIndicatorHelper * m_indicator;$/;" m class:AIHTestFrame access:private +AIHTestFrame::m_itemID AIHTest.h /^ long m_itemID;$/;" m class:AIHTestFrame access:private +AIHTestFrame::m_mainMenu AIHTest.h /^ wxMenu * m_mainMenu;$/;" m class:AIHTestFrame access:private +AIHTestFrame::m_removedMenu AIHTest.h /^ wxMenu * m_removedMenu;$/;" m class:AIHTestFrame access:private +AIHTestFrame::m_subID AIHTest.h /^ long m_subID;$/;" m class:AIHTestFrame access:private +AIHTestFrame::~AIHTestFrame AIHTest.cpp /^AIHTestFrame::~AIHTestFrame()$/;" f class:AIHTestFrame signature:() +AIHTestFrame::~AIHTestFrame AIHTest.h /^ virtual ~AIHTestFrame();$/;" p class:AIHTestFrame access:public signature:() +APPINDICATORHELPER_H appindic.h 12;" d +AppIndicatorHelper appindic.cpp /^AppIndicatorHelper::AppIndicatorHelper()$/;" f class:AppIndicatorHelper signature:() +AppIndicatorHelper appindic.cpp /^AppIndicatorHelper::AppIndicatorHelper(const wxString& id, const wxString& iconFullFilePath, AppIndicatorCategory category)$/;" f class:AppIndicatorHelper signature:(const wxString& id, const wxString& iconFullFilePath, AppIndicatorCategory category) +AppIndicatorHelper appindic.cpp /^AppIndicatorHelper::AppIndicatorHelper(const wxString& id,$/;" f class:AppIndicatorHelper signature:(const wxString& id, const wxString& iconFileName, const wxString& iconDirPath, AppIndicatorCategory category) +AppIndicatorHelper appindic.h /^ AppIndicatorHelper();$/;" p class:AppIndicatorHelper access:public signature:() +AppIndicatorHelper appindic.h /^ AppIndicatorHelper(const wxString& id,$/;" p class:AppIndicatorHelper access:public signature:(const wxString& id, const wxString& iconFileName, const wxString& iconDirPath, AppIndicatorCategory category) +AppIndicatorHelper appindic.h /^ AppIndicatorHelper(const wxString& id,$/;" p class:AppIndicatorHelper access:public signature:(const wxString& id, const wxString& iconFullFilePath, AppIndicatorCategory category) +AppIndicatorHelper appindic.h /^class AppIndicatorHelper : public wxObject {$/;" c inherits:wxObject +AppIndicatorHelper::AppIndicatorHelper appindic.cpp /^AppIndicatorHelper::AppIndicatorHelper()$/;" f class:AppIndicatorHelper signature:() +AppIndicatorHelper::AppIndicatorHelper appindic.cpp /^AppIndicatorHelper::AppIndicatorHelper(const wxString& id, const wxString& iconFullFilePath, AppIndicatorCategory category)$/;" f class:AppIndicatorHelper signature:(const wxString& id, const wxString& iconFullFilePath, AppIndicatorCategory category) +AppIndicatorHelper::AppIndicatorHelper appindic.cpp /^AppIndicatorHelper::AppIndicatorHelper(const wxString& id,$/;" f class:AppIndicatorHelper signature:(const wxString& id, const wxString& iconFileName, const wxString& iconDirPath, AppIndicatorCategory category) +AppIndicatorHelper::AppIndicatorHelper appindic.h /^ AppIndicatorHelper();$/;" p class:AppIndicatorHelper access:public signature:() +AppIndicatorHelper::AppIndicatorHelper appindic.h /^ AppIndicatorHelper(const wxString& id,$/;" p class:AppIndicatorHelper access:public signature:(const wxString& id, const wxString& iconFileName, const wxString& iconDirPath, AppIndicatorCategory category) +AppIndicatorHelper::AppIndicatorHelper appindic.h /^ AppIndicatorHelper(const wxString& id,$/;" p class:AppIndicatorHelper access:public signature:(const wxString& id, const wxString& iconFullFilePath, AppIndicatorCategory category) +AppIndicatorHelper::DECLARE_DYNAMIC_CLASS appindic.h /^ DECLARE_DYNAMIC_CLASS(AppIndicatorHelper);$/;" p class:AppIndicatorHelper access:private signature:(AppIndicatorHelper) +AppIndicatorHelper::GetAttentionIcon appindic.cpp /^const wxString AppIndicatorHelper::GetAttentionIcon() const$/;" f class:AppIndicatorHelper signature:() const +AppIndicatorHelper::GetAttentionIcon appindic.h /^ const wxString GetAttentionIcon() const;$/;" p class:AppIndicatorHelper access:public signature:() const +AppIndicatorHelper::GetAttentionIconDesc appindic.cpp /^const wxString AppIndicatorHelper::GetAttentionIconDesc() const$/;" f class:AppIndicatorHelper signature:() const +AppIndicatorHelper::GetAttentionIconDesc appindic.h /^ const wxString GetAttentionIconDesc() const;$/;" p class:AppIndicatorHelper access:public signature:() const +AppIndicatorHelper::GetCategory appindic.cpp /^const AppIndicatorCategory AppIndicatorHelper::GetCategory() const$/;" f class:AppIndicatorHelper signature:() const +AppIndicatorHelper::GetCategory appindic.h /^ const AppIndicatorCategory GetCategory() const;$/;" p class:AppIndicatorHelper access:public signature:() const +AppIndicatorHelper::GetIcon appindic.cpp /^const wxString AppIndicatorHelper::GetIcon() const$/;" f class:AppIndicatorHelper signature:() const +AppIndicatorHelper::GetIcon appindic.h /^ const wxString GetIcon() const;$/;" p class:AppIndicatorHelper access:public signature:() const +AppIndicatorHelper::GetIconDesc appindic.cpp /^const wxString AppIndicatorHelper::GetIconDesc() const$/;" f class:AppIndicatorHelper signature:() const +AppIndicatorHelper::GetIconDesc appindic.h /^ const wxString GetIconDesc() const;$/;" p class:AppIndicatorHelper access:public signature:() const +AppIndicatorHelper::GetIconDir appindic.cpp /^const wxString AppIndicatorHelper::GetIconDir() const$/;" f class:AppIndicatorHelper signature:() const +AppIndicatorHelper::GetIconDir appindic.h /^ const wxString GetIconDir() const;$/;" p class:AppIndicatorHelper access:public signature:() const +AppIndicatorHelper::GetId appindic.cpp /^const wxString AppIndicatorHelper::GetId() const$/;" f class:AppIndicatorHelper signature:() const +AppIndicatorHelper::GetId appindic.h /^ const wxString GetId() const;$/;" p class:AppIndicatorHelper access:public signature:() const +AppIndicatorHelper::GetLabel appindic.cpp /^const wxString AppIndicatorHelper::GetLabel() const$/;" f class:AppIndicatorHelper signature:() const +AppIndicatorHelper::GetLabel appindic.h /^ const wxString GetLabel() const;$/;" p class:AppIndicatorHelper access:public signature:() const +AppIndicatorHelper::GetLabelGuide appindic.cpp /^const wxString AppIndicatorHelper::GetLabelGuide() const$/;" f class:AppIndicatorHelper signature:() const +AppIndicatorHelper::GetLabelGuide appindic.h /^ const wxString GetLabelGuide()const;$/;" p class:AppIndicatorHelper access:public signature:() const +AppIndicatorHelper::GetOrderingIndex appindic.cpp /^const uint AppIndicatorHelper::GetOrderingIndex() const$/;" f class:AppIndicatorHelper signature:() const +AppIndicatorHelper::GetOrderingIndex appindic.h /^ const uint GetOrderingIndex() const;$/;" p class:AppIndicatorHelper access:public signature:() const +AppIndicatorHelper::GetStatus appindic.cpp /^const AppIndicatorStatus AppIndicatorHelper::GetStatus() const$/;" f class:AppIndicatorHelper signature:() const +AppIndicatorHelper::GetStatus appindic.h /^ const AppIndicatorStatus GetStatus() const;$/;" p class:AppIndicatorHelper access:public signature:() const +AppIndicatorHelper::GetTitle appindic.cpp /^const wxString AppIndicatorHelper::GetTitle() const$/;" f class:AppIndicatorHelper signature:() const +AppIndicatorHelper::GetTitle appindic.h /^ const wxString GetTitle() const;$/;" p class:AppIndicatorHelper access:public signature:() const +AppIndicatorHelper::SetAttentionIcon appindic.cpp /^void AppIndicatorHelper::SetAttentionIcon(const wxString& iconFileName)$/;" f class:AppIndicatorHelper signature:(const wxString& iconFileName) +AppIndicatorHelper::SetAttentionIcon appindic.h /^ void SetAttentionIcon(const wxString& iconFileName);$/;" p class:AppIndicatorHelper access:public signature:(const wxString& iconFileName) +AppIndicatorHelper::SetAttentionIconFull appindic.cpp /^void AppIndicatorHelper::SetAttentionIconFull(const wxString& iconFileName, const wxString& iconDesc)$/;" f class:AppIndicatorHelper signature:(const wxString& iconFileName, const wxString& iconDesc) +AppIndicatorHelper::SetAttentionIconFull appindic.h /^ void SetAttentionIconFull(const wxString& iconFileName, const wxString& iconDesc);$/;" p class:AppIndicatorHelper access:public signature:(const wxString& iconFileName, const wxString& iconDesc) +AppIndicatorHelper::SetIcon appindic.cpp /^void AppIndicatorHelper::SetIcon(const wxString& iconFileName)$/;" f class:AppIndicatorHelper signature:(const wxString& iconFileName) +AppIndicatorHelper::SetIcon appindic.h /^ void SetIcon(const wxString& iconFileName);$/;" p class:AppIndicatorHelper access:public signature:(const wxString& iconFileName) +AppIndicatorHelper::SetIconDir appindic.cpp /^void AppIndicatorHelper::SetIconDir(const wxString& iconDir)$/;" f class:AppIndicatorHelper signature:(const wxString& iconDir) +AppIndicatorHelper::SetIconDir appindic.h /^ void SetIconDir(const wxString& iconDir);$/;" p class:AppIndicatorHelper access:public signature:(const wxString& iconDir) +AppIndicatorHelper::SetIconFull appindic.cpp /^void AppIndicatorHelper::SetIconFull(const wxString& iconFileName, const wxString& iconDesc)$/;" f class:AppIndicatorHelper signature:(const wxString& iconFileName, const wxString& iconDesc) +AppIndicatorHelper::SetIconFull appindic.h /^ void SetIconFull(const wxString& iconFileName, const wxString& iconDesc);$/;" p class:AppIndicatorHelper access:public signature:(const wxString& iconFileName, const wxString& iconDesc) +AppIndicatorHelper::SetLabel appindic.cpp /^void AppIndicatorHelper::SetLabel(const wxString& label, const wxString& guide)$/;" f class:AppIndicatorHelper signature:(const wxString& label, const wxString& guide) +AppIndicatorHelper::SetLabel appindic.h /^ void SetLabel(const wxString& label, const wxString& guide);$/;" p class:AppIndicatorHelper access:public signature:(const wxString& label, const wxString& guide) +AppIndicatorHelper::SetMenu appindic.cpp /^void AppIndicatorHelper::SetMenu(wxMenu * appMenu)$/;" f class:AppIndicatorHelper signature:(wxMenu * appMenu) +AppIndicatorHelper::SetMenu appindic.h /^ void SetMenu(wxMenu * appMenu);$/;" p class:AppIndicatorHelper access:public signature:(wxMenu * appMenu) +AppIndicatorHelper::SetOrderingIndex appindic.cpp /^void AppIndicatorHelper::SetOrderingIndex(uint orderingIndex)$/;" f class:AppIndicatorHelper signature:(uint orderingIndex) +AppIndicatorHelper::SetOrderingIndex appindic.h /^ void SetOrderingIndex(uint orderingIndex);$/;" p class:AppIndicatorHelper access:public signature:(uint orderingIndex) +AppIndicatorHelper::SetStatus appindic.cpp /^void AppIndicatorHelper::SetStatus(AppIndicatorStatus status)$/;" f class:AppIndicatorHelper signature:(AppIndicatorStatus status) +AppIndicatorHelper::SetStatus appindic.h /^ void SetStatus(AppIndicatorStatus status);$/;" p class:AppIndicatorHelper access:public signature:(AppIndicatorStatus status) +AppIndicatorHelper::SetTitle appindic.cpp /^void AppIndicatorHelper::SetTitle(const wxString& title)$/;" f class:AppIndicatorHelper signature:(const wxString& title) +AppIndicatorHelper::SetTitle appindic.h /^ void SetTitle(const wxString& title);$/;" p class:AppIndicatorHelper access:public signature:(const wxString& title) +AppIndicatorHelper::m_appMenu appindic.h /^ wxWeakRef m_appMenu;$/;" m class:AppIndicatorHelper access:private +AppIndicatorHelper::m_indicator appindic.h /^ AppIndicator * m_indicator;$/;" m class:AppIndicatorHelper access:private +AppIndicatorHelper::~AppIndicatorHelper appindic.cpp /^AppIndicatorHelper::~AppIndicatorHelper()$/;" f class:AppIndicatorHelper signature:() +AppIndicatorHelper::~AppIndicatorHelper appindic.h /^ virtual ~AppIndicatorHelper();$/;" p class:AppIndicatorHelper access:public signature:() +CreateMenu AIHTest.cpp /^wxMenu* AIHTestFrame::CreateMenu()$/;" f class:AIHTestFrame signature:() +CreateMenu AIHTest.h /^ wxMenu * CreateMenu();$/;" p class:AIHTestFrame access:private signature:() +DECLARE_DYNAMIC_CLASS appindic.h /^ DECLARE_DYNAMIC_CLASS(AppIndicatorHelper);$/;" p class:AppIndicatorHelper access:private signature:(AppIndicatorHelper) +DeleteMenu AIHTest.cpp /^void AIHTestFrame::DeleteMenu(wxMouseEvent& evt)$/;" f class:AIHTestFrame signature:(wxMouseEvent& evt) +DeleteMenu AIHTest.h /^ void DeleteMenu(wxMouseEvent& evt);$/;" p class:AIHTestFrame access:private signature:(wxMouseEvent& evt) +GetAttentionIcon appindic.cpp /^const wxString AppIndicatorHelper::GetAttentionIcon() const$/;" f class:AppIndicatorHelper signature:() const +GetAttentionIcon appindic.h /^ const wxString GetAttentionIcon() const;$/;" p class:AppIndicatorHelper access:public signature:() const +GetAttentionIconDesc appindic.cpp /^const wxString AppIndicatorHelper::GetAttentionIconDesc() const$/;" f class:AppIndicatorHelper signature:() const +GetAttentionIconDesc appindic.h /^ const wxString GetAttentionIconDesc() const;$/;" p class:AppIndicatorHelper access:public signature:() const +GetCategory appindic.cpp /^const AppIndicatorCategory AppIndicatorHelper::GetCategory() const$/;" f class:AppIndicatorHelper signature:() const +GetCategory appindic.h /^ const AppIndicatorCategory GetCategory() const;$/;" p class:AppIndicatorHelper access:public signature:() const +GetIcon appindic.cpp /^const wxString AppIndicatorHelper::GetIcon() const$/;" f class:AppIndicatorHelper signature:() const +GetIcon appindic.h /^ const wxString GetIcon() const;$/;" p class:AppIndicatorHelper access:public signature:() const +GetIconDesc appindic.cpp /^const wxString AppIndicatorHelper::GetIconDesc() const$/;" f class:AppIndicatorHelper signature:() const +GetIconDesc appindic.h /^ const wxString GetIconDesc() const;$/;" p class:AppIndicatorHelper access:public signature:() const +GetIconDir appindic.cpp /^const wxString AppIndicatorHelper::GetIconDir() const$/;" f class:AppIndicatorHelper signature:() const +GetIconDir appindic.h /^ const wxString GetIconDir() const;$/;" p class:AppIndicatorHelper access:public signature:() const +GetId appindic.cpp /^const wxString AppIndicatorHelper::GetId() const$/;" f class:AppIndicatorHelper signature:() const +GetId appindic.h /^ const wxString GetId() const;$/;" p class:AppIndicatorHelper access:public signature:() const +GetLabel appindic.cpp /^const wxString AppIndicatorHelper::GetLabel() const$/;" f class:AppIndicatorHelper signature:() const +GetLabel appindic.h /^ const wxString GetLabel() const;$/;" p class:AppIndicatorHelper access:public signature:() const +GetLabelGuide appindic.cpp /^const wxString AppIndicatorHelper::GetLabelGuide() const$/;" f class:AppIndicatorHelper signature:() const +GetLabelGuide appindic.h /^ const wxString GetLabelGuide()const;$/;" p class:AppIndicatorHelper access:public signature:() const +GetOrderingIndex appindic.cpp /^const uint AppIndicatorHelper::GetOrderingIndex() const$/;" f class:AppIndicatorHelper signature:() const +GetOrderingIndex appindic.h /^ const uint GetOrderingIndex() const;$/;" p class:AppIndicatorHelper access:public signature:() const +GetStatus appindic.cpp /^const AppIndicatorStatus AppIndicatorHelper::GetStatus() const$/;" f class:AppIndicatorHelper signature:() const +GetStatus appindic.h /^ const AppIndicatorStatus GetStatus() const;$/;" p class:AppIndicatorHelper access:public signature:() const +GetTitle appindic.cpp /^const wxString AppIndicatorHelper::GetTitle() const$/;" f class:AppIndicatorHelper signature:() const +GetTitle appindic.h /^ const wxString GetTitle() const;$/;" p class:AppIndicatorHelper access:public signature:() const +OnExit AIHTest.cpp /^int AIHTest::OnExit()$/;" f class:AIHTest signature:() +OnExit AIHTest.h /^ int OnExit();$/;" p class:AIHTest access:public signature:() +OnIdle AIHTest.cpp /^void AIHTestFrame::OnIdle(wxIdleEvent& evt)$/;" f class:AIHTestFrame signature:(wxIdleEvent& evt) +OnIdle AIHTest.h /^ void OnIdle(wxIdleEvent& evt);$/;" p class:AIHTestFrame access:private signature:(wxIdleEvent& evt) +OnInit AIHTest.cpp /^bool AIHTest::OnInit()$/;" f class:AIHTest signature:() +OnInit AIHTest.h /^ bool OnInit();$/;" p class:AIHTest access:public signature:() +OnMenuItemClick AIHTest.cpp /^void AIHTestFrame::OnMenuItemClick(wxCommandEvent& evt)$/;" f class:AIHTestFrame signature:(wxCommandEvent& evt) +OnMenuItemClick AIHTest.h /^ void OnMenuItemClick(wxCommandEvent& evt);$/;" p class:AIHTestFrame access:private signature:(wxCommandEvent& evt) +SetAttentionIcon appindic.cpp /^void AppIndicatorHelper::SetAttentionIcon(const wxString& iconFileName)$/;" f class:AppIndicatorHelper signature:(const wxString& iconFileName) +SetAttentionIcon appindic.h /^ void SetAttentionIcon(const wxString& iconFileName);$/;" p class:AppIndicatorHelper access:public signature:(const wxString& iconFileName) +SetAttentionIconFull appindic.cpp /^void AppIndicatorHelper::SetAttentionIconFull(const wxString& iconFileName, const wxString& iconDesc)$/;" f class:AppIndicatorHelper signature:(const wxString& iconFileName, const wxString& iconDesc) +SetAttentionIconFull appindic.h /^ void SetAttentionIconFull(const wxString& iconFileName, const wxString& iconDesc);$/;" p class:AppIndicatorHelper access:public signature:(const wxString& iconFileName, const wxString& iconDesc) +SetIcon appindic.cpp /^void AppIndicatorHelper::SetIcon(const wxString& iconFileName)$/;" f class:AppIndicatorHelper signature:(const wxString& iconFileName) +SetIcon appindic.h /^ void SetIcon(const wxString& iconFileName);$/;" p class:AppIndicatorHelper access:public signature:(const wxString& iconFileName) +SetIconDir appindic.cpp /^void AppIndicatorHelper::SetIconDir(const wxString& iconDir)$/;" f class:AppIndicatorHelper signature:(const wxString& iconDir) +SetIconDir appindic.h /^ void SetIconDir(const wxString& iconDir);$/;" p class:AppIndicatorHelper access:public signature:(const wxString& iconDir) +SetIconFull appindic.cpp /^void AppIndicatorHelper::SetIconFull(const wxString& iconFileName, const wxString& iconDesc)$/;" f class:AppIndicatorHelper signature:(const wxString& iconFileName, const wxString& iconDesc) +SetIconFull appindic.h /^ void SetIconFull(const wxString& iconFileName, const wxString& iconDesc);$/;" p class:AppIndicatorHelper access:public signature:(const wxString& iconFileName, const wxString& iconDesc) +SetLabel appindic.cpp /^void AppIndicatorHelper::SetLabel(const wxString& label, const wxString& guide)$/;" f class:AppIndicatorHelper signature:(const wxString& label, const wxString& guide) +SetLabel appindic.h /^ void SetLabel(const wxString& label, const wxString& guide);$/;" p class:AppIndicatorHelper access:public signature:(const wxString& label, const wxString& guide) +SetMenu appindic.cpp /^void AppIndicatorHelper::SetMenu(wxMenu * appMenu)$/;" f class:AppIndicatorHelper signature:(wxMenu * appMenu) +SetMenu appindic.h /^ void SetMenu(wxMenu * appMenu);$/;" p class:AppIndicatorHelper access:public signature:(wxMenu * appMenu) +SetOrderingIndex appindic.cpp /^void AppIndicatorHelper::SetOrderingIndex(uint orderingIndex)$/;" f class:AppIndicatorHelper signature:(uint orderingIndex) +SetOrderingIndex appindic.h /^ void SetOrderingIndex(uint orderingIndex);$/;" p class:AppIndicatorHelper access:public signature:(uint orderingIndex) +SetStatus appindic.cpp /^void AppIndicatorHelper::SetStatus(AppIndicatorStatus status)$/;" f class:AppIndicatorHelper signature:(AppIndicatorStatus status) +SetStatus appindic.h /^ void SetStatus(AppIndicatorStatus status);$/;" p class:AppIndicatorHelper access:public signature:(AppIndicatorStatus status) +SetTitle appindic.cpp /^void AppIndicatorHelper::SetTitle(const wxString& title)$/;" f class:AppIndicatorHelper signature:(const wxString& title) +SetTitle appindic.h /^ void SetTitle(const wxString& title);$/;" p class:AppIndicatorHelper access:public signature:(const wxString& title) +ShowMenu AIHTest.cpp /^void AIHTestFrame::ShowMenu(wxMouseEvent& evt)$/;" f class:AIHTestFrame signature:(wxMouseEvent& evt) +ShowMenu AIHTest.h /^ void ShowMenu(wxMouseEvent& evt);$/;" p class:AIHTestFrame access:private signature:(wxMouseEvent& evt) +m_appMenu appindic.h /^ wxWeakRef m_appMenu;$/;" m class:AppIndicatorHelper access:private +m_indicator AIHTest.h /^ AppIndicatorHelper * m_indicator;$/;" m class:AIHTestFrame access:private +m_indicator appindic.h /^ AppIndicator * m_indicator;$/;" m class:AppIndicatorHelper access:private +m_itemID AIHTest.h /^ long m_itemID;$/;" m class:AIHTestFrame access:private +m_mainMenu AIHTest.h /^ wxMenu * m_mainMenu;$/;" m class:AIHTestFrame access:private +m_removedMenu AIHTest.h /^ wxMenu * m_removedMenu;$/;" m class:AIHTestFrame access:private +m_subID AIHTest.h /^ long m_subID;$/;" m class:AIHTestFrame access:private +~AIHTest AIHTest.cpp /^AIHTest::~AIHTest()$/;" f class:AIHTest signature:() +~AIHTest AIHTest.h /^ virtual ~AIHTest();$/;" p class:AIHTest access:public signature:() +~AIHTestFrame AIHTest.cpp /^AIHTestFrame::~AIHTestFrame()$/;" f class:AIHTestFrame signature:() +~AIHTestFrame AIHTest.h /^ virtual ~AIHTestFrame();$/;" p class:AIHTestFrame access:public signature:() +~AppIndicatorHelper appindic.cpp /^AppIndicatorHelper::~AppIndicatorHelper()$/;" f class:AppIndicatorHelper signature:() +~AppIndicatorHelper appindic.h /^ virtual ~AppIndicatorHelper();$/;" p class:AppIndicatorHelper access:public signature:() diff --git a/ThirdParty/C++RedistributableBinaries/arm/api-ms-win-appmodel-runtime-l1-1-0.dll b/ThirdParty/C++RedistributableBinaries/arm/api-ms-win-appmodel-runtime-l1-1-0.dll new file mode 100644 index 0000000..e69de29 diff --git a/ThirdParty/C++RedistributableBinaries/arm/api-ms-win-core-winrt-error-l1-1-0.dll b/ThirdParty/C++RedistributableBinaries/arm/api-ms-win-core-winrt-error-l1-1-0.dll new file mode 100644 index 0000000..e69de29 diff --git a/ThirdParty/C++RedistributableBinaries/arm/api-ms-win-core-winrt-l1-1-0.dll b/ThirdParty/C++RedistributableBinaries/arm/api-ms-win-core-winrt-l1-1-0.dll new file mode 100644 index 0000000..e69de29 diff --git a/ThirdParty/C++RedistributableBinaries/arm/api-ms-win-core-winrt-robuffer-l1-1-0.dll b/ThirdParty/C++RedistributableBinaries/arm/api-ms-win-core-winrt-robuffer-l1-1-0.dll new file mode 100644 index 0000000..e69de29 diff --git a/ThirdParty/C++RedistributableBinaries/arm/api-ms-win-core-winrt-string-l1-1-0.dll b/ThirdParty/C++RedistributableBinaries/arm/api-ms-win-core-winrt-string-l1-1-0.dll new file mode 100644 index 0000000..e69de29 diff --git a/ThirdParty/C++RedistributableBinaries/arm/api-ms-win-shcore-scaling-l1-1-1.dll b/ThirdParty/C++RedistributableBinaries/arm/api-ms-win-shcore-scaling-l1-1-1.dll new file mode 100644 index 0000000..e69de29 diff --git a/ThirdParty/DatabaseLayer/CMakeLists.txt b/ThirdParty/DatabaseLayer/CMakeLists.txt new file mode 100644 index 0000000..2ee8aa7 --- /dev/null +++ b/ThirdParty/DatabaseLayer/CMakeLists.txt @@ -0,0 +1,54 @@ +# +# Stuff the average programmer needs to change +# +set(SRCS src/DatabaseErrorReporter.cpp + src/DatabaseLayer.cpp + src/DatabaseQueryParser.cpp + src/DatabaseResultSet.cpp + src/DatabaseStringConverter.cpp + src/PreparedStatement.cpp + src/SqliteDatabaseLayer.cpp + src/SqlitePreparedStatement.cpp + src/SqliteResultSet.cpp + src/SqliteResultSetMetaData.cpp + sqlite3/sqlite3.c +) + +#set(SRCS ${SRCS} sqlite3/sqlite3.c) + + +set(HFILES include/DatabaseErrorCodes.h + include/DatabaseErrorReporter.h + include/DatabaseLayer.h + include/DatabaseLayerDef.h + include/DatabaseLayerException.h + include/DatabaseQueryParser.h + include/DatabaseResultSet.h + include/DatabaseStringConverter.h + include/PreparedStatement.h + include/ResultSetMetaData.h + include/SqliteDatabaseLayer.h + include/SqlitePreparedStatement.h + include/SqliteResultSet.h + include/SqliteResultSetMetaData.h) +set(INCLUDE_DIRECTORIES ${BASE_INCLUDE_DIRECTORIES} include sqlite3) +set(LIBRARY_NAME DatabaseLayer) +if(WIN32) + set(PREPROCESSOR_DEFINITIONS ${PREPROCESSOR_DEFINITIONS};${wxWidgets_DEFINITIONS};/D_LIB) +endif(WIN32) +set(SRCS ${SRCS} ${HFILES}) + +add_library(${LIBRARY_NAME} STATIC ${SRCS}) + + +target_compile_definitions(${LIBRARY_NAME} PRIVATE ${PREPROCESSOR_DEFINITIONS}) + +target_include_directories(${LIBRARY_NAME} PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}/include + ${CMAKE_CURRENT_SOURCE_DIR}/sqlite3 + ${BASE_INCLUDE_DIRECTORIES} + ${wxWidgets_INCLUDE_DIRS} +) + + +target_link_libraries(${LIBRARY_NAME} PUBLIC ${wxWidgets_LIBRARIES}) \ No newline at end of file diff --git a/ThirdParty/DatabaseLayer/Win/DatabaseLayer.vcxproj b/ThirdParty/DatabaseLayer/Win/DatabaseLayer.vcxproj new file mode 100644 index 0000000..975b288 --- /dev/null +++ b/ThirdParty/DatabaseLayer/Win/DatabaseLayer.vcxproj @@ -0,0 +1,155 @@ + + + + + Debug + x64 + + + Release + x64 + + + + {05A3B61F-EB04-36AA-BEB7-F3837774E597} + 10.0.16299.0 + Win32Proj + x64 + DatabaseLayer + NoUpgrade + + + + StaticLibrary + Unicode + v141 + + + StaticLibrary + Unicode + v141 + + + + + + + + + + <_ProjectFileVersion>10.0.20506.1 + F:\IT-Dim\trunk\ThirdParty\DatabaseLayer\Win\Debug\ + DatabaseLayer.dir\Debug\ + DatabaseLayer + .lib + F:\IT-Dim\trunk\ThirdParty\DatabaseLayer\Win\Release\ + DatabaseLayer.dir\Release\ + DatabaseLayer + .lib + + + + F:\IT-Dim\wxWidgets\lib\vc_x64_dll\mswu;F:\IT-Dim\wxWidgets\include;F:\IT-Dim\trunk\ThirdParty\build\..\..\include;F:\IT-Dim\trunk\ThirdParty\build\..\..\ThirdParty\GLEW\include;F:\IT-Dim\trunk\ThirdParty\DatabaseLayer\include;F:\IT-Dim\trunk\ThirdParty\DatabaseLayer\sqlite3;%(AdditionalIncludeDirectories) + Debug/ + EnableFastChecks + CompileAsCpp + ProgramDatabase + 4996 + Sync + Disabled + true + Disabled + NotUsing + MultiThreadedDebugDLL + true + Level3 + WIN32;_WINDOWS;__WXDEBUG__=1;WXUSINGDLL;UNICODE;_UNICODE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;_LIB;CMAKE_INTDIR="Debug";%(PreprocessorDefinitions) + $(IntDir) + + + WIN32;_DEBUG;_WINDOWS;__WXDEBUG__=1;WXUSINGDLL;UNICODE;_UNICODE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;_LIB;CMAKE_INTDIR=\"Debug\";%(PreprocessorDefinitions) + F:\IT-Dim\wxWidgets\lib\vc_x64_dll\mswu;F:\IT-Dim\wxWidgets\include;F:\IT-Dim\trunk\ThirdParty\build\..\..\include;F:\IT-Dim\trunk\ThirdParty\build\..\..\ThirdParty\GLEW\include;F:\IT-Dim\trunk\ThirdParty\DatabaseLayer\include;F:\IT-Dim\trunk\ThirdParty\DatabaseLayer\sqlite3;%(AdditionalIncludeDirectories) + + + F:\IT-Dim\wxWidgets\lib\vc_x64_dll\mswu;F:\IT-Dim\wxWidgets\include;F:\IT-Dim\trunk\ThirdParty\build\..\..\include;F:\IT-Dim\trunk\ThirdParty\build\..\..\ThirdParty\GLEW\include;F:\IT-Dim\trunk\ThirdParty\DatabaseLayer\include;F:\IT-Dim\trunk\ThirdParty\DatabaseLayer\sqlite3;%(AdditionalIncludeDirectories) + $(ProjectDir)/$(IntDir) + %(Filename).h + %(Filename).tlb + %(Filename)_i.c + %(Filename)_p.c + + + %(AdditionalOptions) /machine:x64 + + + + + F:\IT-Dim\wxWidgets\lib\vc_x64_dll\mswu;F:\IT-Dim\wxWidgets\include;F:\IT-Dim\trunk\ThirdParty\build\..\..\include;F:\IT-Dim\trunk\ThirdParty\build\..\..\ThirdParty\GLEW\include;F:\IT-Dim\trunk\ThirdParty\DatabaseLayer\include;F:\IT-Dim\trunk\ThirdParty\DatabaseLayer\sqlite3;%(AdditionalIncludeDirectories) + Release/ + CompileAsCpp + 4996 + Sync + AnySuitable + true + MaxSpeed + NotUsing + MultiThreadedDLL + true + Level3 + WIN32;_WINDOWS;NDEBUG;WXUSINGDLL;UNICODE;_UNICODE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;__WXDEBUG__=1;_LIB;CMAKE_INTDIR="Release";%(PreprocessorDefinitions) + $(IntDir) + + + + + WIN32;_WINDOWS;NDEBUG;WXUSINGDLL;UNICODE;_UNICODE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;__WXDEBUG__=1;_LIB;CMAKE_INTDIR=\"Release\";%(PreprocessorDefinitions) + F:\IT-Dim\wxWidgets\lib\vc_x64_dll\mswu;F:\IT-Dim\wxWidgets\include;F:\IT-Dim\trunk\ThirdParty\build\..\..\include;F:\IT-Dim\trunk\ThirdParty\build\..\..\ThirdParty\GLEW\include;F:\IT-Dim\trunk\ThirdParty\DatabaseLayer\include;F:\IT-Dim\trunk\ThirdParty\DatabaseLayer\sqlite3;%(AdditionalIncludeDirectories) + + + F:\IT-Dim\wxWidgets\lib\vc_x64_dll\mswu;F:\IT-Dim\wxWidgets\include;F:\IT-Dim\trunk\ThirdParty\build\..\..\include;F:\IT-Dim\trunk\ThirdParty\build\..\..\ThirdParty\GLEW\include;F:\IT-Dim\trunk\ThirdParty\DatabaseLayer\include;F:\IT-Dim\trunk\ThirdParty\DatabaseLayer\sqlite3;%(AdditionalIncludeDirectories) + $(ProjectDir)/$(IntDir) + %(Filename).h + %(Filename).tlb + %(Filename)_i.c + %(Filename)_p.c + + + %(AdditionalOptions) /machine:x64 + + + + + + + + + + + + + + + CompileAsC + CompileAsC + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ThirdParty/DatabaseLayer/Win/DatabaseLayer.vcxproj.filters b/ThirdParty/DatabaseLayer/Win/DatabaseLayer.vcxproj.filters new file mode 100644 index 0000000..003a1f6 --- /dev/null +++ b/ThirdParty/DatabaseLayer/Win/DatabaseLayer.vcxproj.filters @@ -0,0 +1,90 @@ + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + + + {51D244FA-C21C-3174-8217-FDB3769212C0} + + + {A07A887B-0B8A-3ED7-BD7F-43C26975F88D} + + + diff --git a/ThirdParty/DatabaseLayer/include/DatabaseErrorCodes.h b/ThirdParty/DatabaseLayer/include/DatabaseErrorCodes.h new file mode 100644 index 0000000..97c1dc2 --- /dev/null +++ b/ThirdParty/DatabaseLayer/include/DatabaseErrorCodes.h @@ -0,0 +1,23 @@ +#ifndef __DATABASE_ERROR_CODES_H__ +#define __DATABASE_ERROR_CODES_H__ + +#define DATABASE_LAYER_OK 0 +#define DATABASE_LAYER_ERROR 1 +#define DATABASE_LAYER_INVALID_USER 2 +#define DATABASE_LAYER_BAD_PASSWORD 3 +#define DATABASE_LAYER_CONSTRAINT_VIOLATION 4 +#define DATABASE_LAYER_SQL_SYNTAX_ERROR 5 +#define DATABASE_LAYER_ALLOCATION_ERROR 6 +#define DATABASE_LAYER_INCOMPATIBLE_FIELD_TYPE 7 +#define DATABASE_LAYER_FIELD_NOT_IN_RESULTSET 8 +#define DATABASE_LAYER_NO_ROWS_FOUND 9 +#define DATABASE_LAYER_NON_UNIQUE_RESULTSET 10 +#define DATABASE_LAYER_UNSUPPORTED_OPERATION 11 +#define DATABASE_LAYER_ERROR_LOADING_LIBRARY 12 + +// Using 0 for now since this is replacing a +// boolean for the return code and we don't want +// to break existing code +#define DATABASE_LAYER_QUERY_RESULT_ERROR 0 + +#endif // __DATABASE_ERROR_CODES_H__ diff --git a/ThirdParty/DatabaseLayer/include/DatabaseErrorReporter.h b/ThirdParty/DatabaseLayer/include/DatabaseErrorReporter.h new file mode 100644 index 0000000..93b2ddd --- /dev/null +++ b/ThirdParty/DatabaseLayer/include/DatabaseErrorReporter.h @@ -0,0 +1,43 @@ +#ifndef __DATABASE_ERROR_REPORTER_H__ +#define __DATABASE_ERROR_REPORTER_H__ + +// For compilers that support precompilation, includes "wx.h". +#include "wx/wxprec.h" + +#ifdef __BORLANDC__ + #pragma hdrstop +#endif + +#ifndef WX_PRECOMP + #include "wx/wx.h" +#endif + +#include "DatabaseLayerDef.h" + +class WXDLLIMPEXP_DATABASELAYER DatabaseErrorReporter +{ +public: + // ctor + DatabaseErrorReporter(); + + // dtor + virtual ~DatabaseErrorReporter(); + + const wxString& GetErrorMessage(); + int GetErrorCode(); + + void ResetErrorCodes(); + +protected: + void SetErrorMessage(const wxString& strErrorMessage); + void SetErrorCode(int nErrorCode); + + void ThrowDatabaseException(); + +private: + wxString m_strErrorMessage; + int m_nErrorCode; +}; + +#endif // __DATABASE_ERROR_REPORTER_H__ + diff --git a/ThirdParty/DatabaseLayer/include/DatabaseLayer.h b/ThirdParty/DatabaseLayer/include/DatabaseLayer.h new file mode 100644 index 0000000..a6eed1d --- /dev/null +++ b/ThirdParty/DatabaseLayer/include/DatabaseLayer.h @@ -0,0 +1,182 @@ +#ifndef __DATABASE_LAYER_H__ +#define __DATABASE_LAYER_H__ + +// For compilers that support precompilation, includes "wx.h". +#include "wx/wxprec.h" + +#ifdef __BORLANDC__ + #pragma hdrstop +#endif + +#ifndef WX_PRECOMP + #include "wx/wx.h" +#endif + +#include "wx/hashset.h" +#include "wx/arrstr.h" +#include "wx/variant.h" + +#include "DatabaseLayerDef.h" +#include "DatabaseErrorReporter.h" +#include "DatabaseStringConverter.h" +#include "DatabaseResultSet.h" +#include "PreparedStatement.h" + +WX_DECLARE_HASH_SET( DatabaseResultSet*, wxPointerHash, wxPointerEqual, DatabaseResultSetHashSet ); +WX_DECLARE_HASH_SET( PreparedStatement*, wxPointerHash, wxPointerEqual, DatabaseStatementHashSet ); + +class WXDLLIMPEXP_DATABASELAYER DatabaseLayer : public DatabaseErrorReporter, public DatabaseStringConverter +{ +public: + /// Constructor + DatabaseLayer(); + + /// Destructor + virtual ~DatabaseLayer(); + + // Open database + virtual bool Open(const wxString& strDatabase) = 0; + + /// close database + virtual bool Close() = 0; + + /// Is the connection to the database open? + virtual bool IsOpen() = 0; + + // transaction support + /// Begin a transaction + virtual void BeginTransaction() = 0; + /// Commit the current transaction + virtual void Commit() = 0; + /// Rollback the current transaction + virtual void RollBack() = 0; + + // query database + /// Run an insert, update, or delete query on the database + virtual int RunQuery(const wxString& strQuery); + /// Run an insert, update, or delete query on the database + virtual int RunQuery(const wxString& strQuery, bool bParseQueries) = 0; + /// Run a select query on the database + virtual DatabaseResultSet* RunQueryWithResults(const wxString& strQuery) = 0; + + /// Close a result set returned by the database or a prepared statement previously + virtual bool CloseResultSet(DatabaseResultSet* pResultSet); + + // PreparedStatement support + /// Prepare a SQL statement which can be reused with different parameters + virtual PreparedStatement* PrepareStatement(const wxString& strQuery) = 0; + /// Close a prepared statement previously prepared by the database + virtual bool CloseStatement(PreparedStatement* pStatement); + + // function names more consistent with JDBC and wxSQLite3 + // these just provide wrappers for existing functions + /// See RunQuery + int ExecuteUpdate(const wxString& strQuery) { return RunQuery(strQuery); } + /// See RunQueryWithResults + DatabaseResultSet* ExecuteQuery(const wxString& strQuery) { return RunQueryWithResults(strQuery); } + + // Database schema API contributed by M. Szeftel (author of wxActiveRecordGenerator) + /// Check for the existence of a table by name + virtual bool TableExists(const wxString& table) = 0; + /// Check for the existence of a view by name + virtual bool ViewExists(const wxString& view) = 0; + /// Retrieve all table names + virtual wxArrayString GetTables() = 0; + /// Retrieve all view names + virtual wxArrayString GetViews() = 0; + /// Retrieve all column names for a table + virtual wxArrayString GetColumns(const wxString& table) = 0; + + // Database single result retrieval API contributed by Guru Kathiresan + /// With the GetSingleResultX API, two additional exception types are thrown: + /// DATABASE_LAYER_NO_ROWS_FOUND - No database rows were returned + /// DATABASE_LAYER_NON_UNIQUE_RESULTSET - More than one database row was returned + + /// Retrieve a single integer value from a query + /// If multiple records are returned from the query, a DATABASE_LAYER_NON_UNIQUE_RESULTSET exception + /// is thrown unless bRequireUniqueResult is false + virtual int GetSingleResultInt(const wxString& strSQL, int nField, bool bRequireUniqueResult = true); + virtual int GetSingleResultInt(const wxString& strSQL, const wxString& strField, bool bRequireUniqueResult = true); + + /// Retrieve a single string value from a query + /// If multiple records are returned from the query, a DATABASE_LAYER_NON_UNIQUE_RESULTSET exception + /// is thrown unless bRequireUniqueResult is false + virtual wxString GetSingleResultString(const wxString& strSQL, int nField, bool bRequireUniqueResult = true); + virtual wxString GetSingleResultString(const wxString& strSQL, const wxString& strField, bool bRequireUniqueResult = true); + + /// Retrieve a single long value from a query + /// If multiple records are returned from the query, a DATABASE_LAYER_NON_UNIQUE_RESULTSET exception + /// is thrown unless bRequireUniqueResult is false + virtual long GetSingleResultLong(const wxString& strSQL, int nField, bool bRequireUniqueResult = true); + virtual long GetSingleResultLong(const wxString& strSQL, const wxString& strField, bool bRequireUniqueResult = true); + + /// Retrieve a single bool value from a query + /// If multiple records are returned from the query, a DATABASE_LAYER_NON_UNIQUE_RESULTSET exception + /// is thrown unless bRequireUniqueResult is false + virtual bool GetSingleResultBool(const wxString& strSQL, int nField, bool bRequireUniqueResult = true); + virtual bool GetSingleResultBool(const wxString& strSQL, const wxString& strField, bool bRequireUniqueResult = true); + + /// Retrieve a single date/time value from a query + /// If multiple records are returned from the query, a DATABASE_LAYER_NON_UNIQUE_RESULTSET exception + /// is thrown unless bRequireUniqueResult is false + virtual wxDateTime GetSingleResultDate(const wxString& strSQL, int nField, bool bRequireUniqueResult = true); + virtual wxDateTime GetSingleResultDate(const wxString& strSQL, const wxString& strField, bool bRequireUniqueResult = true); + + /// Retrieve a single Blob value from a query + /// If multiple records are returned from the query, a DATABASE_LAYER_NON_UNIQUE_RESULTSET exception + /// is thrown unless bRequireUniqueResult is false + virtual void* GetSingleResultBlob(const wxString& strSQL, int nField, wxMemoryBuffer& Buffer, bool bRequireUniqueResult = true); + virtual void* GetSingleResultBlob(const wxString& strSQL, const wxString& strField, wxMemoryBuffer& Buffer, bool bRequireUniqueResult = true); + + /// Retrieve a single double value from a query + /// If multiple records are returned from the query, a DATABASE_LAYER_NON_UNIQUE_RESULTSET exception + /// is thrown unless bRequireUniqueResult is false + virtual double GetSingleResultDouble(const wxString& strSQL, int nField, bool bRequireUniqueResult = true); + virtual double GetSingleResultDouble(const wxString& strSQL, const wxString& strField, bool bRequireUniqueResult = true); + + /// Retrieve all the values of one field in a result set + virtual wxArrayInt GetResultsArrayInt(const wxString& strSQL, int nField); + virtual wxArrayInt GetResultsArrayInt(const wxString& strSQL, const wxString& Field); + + virtual wxArrayString GetResultsArrayString(const wxString& strSQL, int nField); + virtual wxArrayString GetResultsArrayString(const wxString& strSQL, const wxString& Field); + + virtual wxArrayLong GetResultsArrayLong(const wxString& strSQL, int nField); + virtual wxArrayLong GetResultsArrayLong(const wxString& strSQL, const wxString& Field); +#if wxCHECK_VERSION(2, 7, 0) + virtual wxArrayDouble GetResultsArrayDouble(const wxString& strSQL, int nField); + virtual wxArrayDouble GetResultsArrayDouble(const wxString& strSQL, const wxString& Field); +#endif + + /// Close all result set objects that have been generated but not yet closed + void CloseResultSets(); + /// Close all prepared statement objects that have been generated but not yet closed + void CloseStatements(); + +protected: + /// Add result set object pointer to the list for "garbage collection" + void LogResultSetForCleanup(DatabaseResultSet* pResultSet) { m_ResultSets.insert(pResultSet); } + /// Add prepared statement object pointer to the list for "garbage collection" + void LogStatementForCleanup(PreparedStatement* pStatement) { m_Statements.insert(pStatement); } + +private: + int GetSingleResultInt(const wxString& strSQL, const wxVariant* field, bool bRequireUniqueResult = true); + wxString GetSingleResultString(const wxString& strSQL, const wxVariant* field, bool bRequireUniqueResult = true); + long GetSingleResultLong(const wxString& strSQL, const wxVariant* field, bool bRequireUniqueResult = true); + bool GetSingleResultBool(const wxString& strSQL, const wxVariant* field, bool bRequireUniqueResult = true); + wxDateTime GetSingleResultDate(const wxString& strSQL, const wxVariant* field, bool bRequireUniqueResult = true); + void* GetSingleResultBlob(const wxString& strSQL, const wxVariant* field, wxMemoryBuffer& Buffer, bool bRequireUniqueResult = true); + double GetSingleResultDouble(const wxString& strSQL, const wxVariant* field, bool bRequireUniqueResult = true); + wxArrayInt GetResultsArrayInt(const wxString& strSQL, const wxVariant* field); + wxArrayString GetResultsArrayString(const wxString& strSQL, const wxVariant* field); + wxArrayLong GetResultsArrayLong(const wxString& strSQL, const wxVariant* field); +#if wxCHECK_VERSION(2, 7, 0) + wxArrayDouble GetResultsArrayDouble(const wxString& strSQL, const wxVariant* field); +#endif + + DatabaseResultSetHashSet m_ResultSets; + DatabaseStatementHashSet m_Statements; +}; + +#endif // __DATABASE_LAYER_H__ + diff --git a/ThirdParty/DatabaseLayer/include/DatabaseLayerDef.h b/ThirdParty/DatabaseLayer/include/DatabaseLayerDef.h new file mode 100644 index 0000000..6043097 --- /dev/null +++ b/ThirdParty/DatabaseLayer/include/DatabaseLayerDef.h @@ -0,0 +1,14 @@ +#ifndef __DATABASELAYER_DEF_H__ +#define __DATABASELAYER_DEF_H__ + +#if defined(WXMAKINGLIB_DATABASELAYER) + #define WXDLLIMPEXP_DATABASELAYER +#elif defined(WXMAKINGDLL_DATABASELAYER) + #define WXDLLIMPEXP_DATABASELAYER WXEXPORT +#elif defined(WXUSINGDLL_DATABASELAYER) + #define WXDLLIMPEXP_DATABASELAYER WXIMPORT +#else // not making nor using DLL + #define WXDLLIMPEXP_DATABASELAYER +#endif + +#endif // __DATABASELAYER_DEF_H__ diff --git a/ThirdParty/DatabaseLayer/include/DatabaseLayerException.h b/ThirdParty/DatabaseLayer/include/DatabaseLayerException.h new file mode 100644 index 0000000..9576100 --- /dev/null +++ b/ThirdParty/DatabaseLayer/include/DatabaseLayerException.h @@ -0,0 +1,26 @@ +#ifndef __DATABASE_LAYER_EXCEPTION_H__ +#define __DATABASE_LAYER_EXCEPTION_H__ + +#ifndef DONT_USE_DATABASE_LAYER_EXCEPTIONS + +class DatabaseLayerException +{ +public: + DatabaseLayerException(int nCode, const wxString& strError) + { + m_nErrorCode = nCode; + m_strErrorMessage = strError; + } + + const wxString& GetErrorMessage() const { return m_strErrorMessage; } + const int GetErrorCode() const { return m_nErrorCode; } + + // Add functions for stack traces ?? +private: + wxString m_strErrorMessage; + int m_nErrorCode; +}; + +#endif // DONT_USE_DATABASE_LAYER_EXCEPTIONS + +#endif // __DATABASE_LAYER_EXCEPTION_H__ diff --git a/ThirdParty/DatabaseLayer/include/DatabaseQueryParser.h b/ThirdParty/DatabaseLayer/include/DatabaseQueryParser.h new file mode 100644 index 0000000..39d6ad2 --- /dev/null +++ b/ThirdParty/DatabaseLayer/include/DatabaseQueryParser.h @@ -0,0 +1,17 @@ +#ifndef _DATABASEQUERYPARSER_H_ +#define _DATABASEQUERYPARSER_H_ + +// For compilers that support precompilation, includes "wx.h". +#include "wx/wxprec.h" + +#ifdef __BORLANDC__ + #pragma hdrstop +#endif + +#ifndef WX_PRECOMP + #include "wx/wx.h" +#endif + +wxArrayString ParseQueries(const wxString& strQuery); + +#endif // _DATABASEQUERYPARSER_H_ diff --git a/ThirdParty/DatabaseLayer/include/DatabaseResultSet.h b/ThirdParty/DatabaseLayer/include/DatabaseResultSet.h new file mode 100644 index 0000000..c22b181 --- /dev/null +++ b/ThirdParty/DatabaseLayer/include/DatabaseResultSet.h @@ -0,0 +1,95 @@ +#ifndef __DATABASE_RESULT_SET_H__ +#define __DATABASE_RESULT_SET_H__ + +// For compilers that support precompilation, includes "wx.h". +#include "wx/wxprec.h" + +#ifdef __BORLANDC__ + #pragma hdrstop +#endif + +#ifndef WX_PRECOMP + #include "wx/wx.h" +#endif + +#include "wx/hashmap.h" +#include "wx/hashset.h" +#include "wx/datetime.h" + +#include "DatabaseLayerDef.h" +#include "DatabaseErrorReporter.h" +#include "DatabaseStringConverter.h" +#include "ResultSetMetaData.h" + +WX_DECLARE_STRING_HASH_MAP(int, StringToIntMap); +WX_DECLARE_HASH_SET( ResultSetMetaData*, wxPointerHash, wxPointerEqual, MetaDataHashSet ); + +class WXDLLIMPEXP_DATABASELAYER DatabaseResultSet : public DatabaseErrorReporter, public DatabaseStringConverter +{ +public: + /// Constructor + DatabaseResultSet(); + + /// Destructor + virtual ~DatabaseResultSet(); + + /// Move to the next record in the result set + virtual bool Next() = 0; + /// Close the result set (call DatabaseLayer::CloseResultSet() instead on the result set) + virtual void Close() = 0; + + virtual int LookupField(const wxString& strField) = 0; + + // get field + /// Retrieve an integer from the result set by the 1-based field index + virtual int GetResultInt(int nField) = 0; + /// Retrieve a wxString from the result set by the 1-based field index + virtual wxString GetResultString(int nField) = 0; + /// Retrieve a long from the result set by the 1-based field index + virtual long GetResultLong(int nField) = 0; + /// Retrieve a boolean from the result set by the 1-based field index + virtual bool GetResultBool(int nField) = 0; + /// Retrieve a wxDateTime from the result set by the 1-based field index + virtual wxDateTime GetResultDate(int nField) = 0; + /// Retrieve a BLOB from the result set by the 1-based field index + virtual void* GetResultBlob(int nField, wxMemoryBuffer& Buffer) = 0; + /// Retrieve a double from the result set by the 1-based field index + virtual double GetResultDouble(int nField) = 0; + /// Check if a field in the current result set record is NULL + virtual bool IsFieldNull(int nField) = 0; + + /// Retrieve an integer from the result set by the result set column name + virtual int GetResultInt(const wxString& strField); + /// Retrieve a wxString from the result set by the result set column name + virtual wxString GetResultString(const wxString& strField); + /// Retrieve a long from the result set by the result set column name + virtual long GetResultLong(const wxString& strField); + /// Retrieve a boolean from the result set by the result set column name + virtual bool GetResultBool(const wxString& strField); + /// Retrieve a wxDateTime from the result set by the result set column name + virtual wxDateTime GetResultDate(const wxString& strField); + /// Retrieve a BLOB from the result set by the result set column name + virtual void* GetResultBlob(const wxString& strField, wxMemoryBuffer& Buffer); + /// Retrieve a double from the result set by the result set column name + virtual double GetResultDouble(const wxString& strField); + /// Check if a field in the current result set record is NULL + virtual bool IsFieldNull(const wxString& strField); + + // get MetaData + /// Retrieve the MetaData associated with this result set + virtual ResultSetMetaData* GetMetaData() = 0; + /// Close MetaData previously returned by the result set + virtual bool CloseMetaData(ResultSetMetaData* pMetaData); + +protected: + /// Close all meta data objects that have been generated but not yet closed + void CloseMetaData(); + /// Add meta data object pointer to the list for "garbage collection" + void LogMetaDataForCleanup(ResultSetMetaData* pMetaData) { m_MetaData.insert(pMetaData); } + +private: + MetaDataHashSet m_MetaData; +}; + +#endif // __DATABASE_RESULT_SET_H__ + diff --git a/ThirdParty/DatabaseLayer/include/DatabaseStringConverter.h b/ThirdParty/DatabaseLayer/include/DatabaseStringConverter.h new file mode 100644 index 0000000..bf7b217 --- /dev/null +++ b/ThirdParty/DatabaseLayer/include/DatabaseStringConverter.h @@ -0,0 +1,43 @@ +#ifndef __DATABASE_STRING_CONVERTER_H__ +#define __DATABASE_STRING_CONVERTER_H__ + +// For compilers that support precompilation, includes "wx.h". +#include "wx/wxprec.h" + +#ifdef __BORLANDC__ + #pragma hdrstop +#endif + +#ifndef WX_PRECOMP + #include "wx/wx.h" +#endif + +#include "DatabaseLayerDef.h" + +class WXDLLIMPEXP_DATABASELAYER DatabaseStringConverter +{ +public: + // ctor + DatabaseStringConverter(); + DatabaseStringConverter(const wxChar* charset); + + // dtor + virtual ~DatabaseStringConverter() { } + + void SetEncoding(wxFontEncoding encoding); + void SetEncoding(const wxCSConv* conv); + const wxCSConv* GetEncoding() { return &m_Encoding; } + + virtual const wxCharBuffer ConvertToUnicodeStream(const wxString& inputString); + virtual size_t GetEncodedStreamLength(const wxString& inputString); + virtual wxString ConvertFromUnicodeStream(const char* inputBuffer); + + static const wxCharBuffer ConvertToUnicodeStream(const wxString& inputString, const char* encoding); + static wxString ConvertFromUnicodeStream(const char* inputBuffer, const char* encoding); + static size_t GetEncodedStreamLength(const wxString& inputString, const char* encoding); + +private: + wxCSConv m_Encoding; +}; + +#endif // __DATABASE_STRING_CONVERTER_H__ diff --git a/ThirdParty/DatabaseLayer/include/PreparedStatement.h b/ThirdParty/DatabaseLayer/include/PreparedStatement.h new file mode 100644 index 0000000..4b971a9 --- /dev/null +++ b/ThirdParty/DatabaseLayer/include/PreparedStatement.h @@ -0,0 +1,82 @@ +#ifndef __PREPARED_STATEMENT_H__ +#define __PREPARED_STATEMENT_H__ + +// For compilers that support precompilation, includes "wx.h". +#include "wx/wxprec.h" + +#ifdef __BORLANDC__ + #pragma hdrstop +#endif + +#ifndef WX_PRECOMP + #include "wx/wx.h" +#endif + +#include "wx/hashset.h" + +#include "DatabaseLayerDef.h" +#include "DatabaseErrorReporter.h" +#include "DatabaseStringConverter.h" +#include "DatabaseResultSet.h" +#include "DatabaseQueryParser.h" + +WX_DECLARE_HASH_SET( DatabaseResultSet*, wxPointerHash, wxPointerEqual, StatementResultSetHashSet ); + +class WXDLLIMPEXP_DATABASELAYER PreparedStatement : public DatabaseErrorReporter, public DatabaseStringConverter +{ +public: + /// Constructor + PreparedStatement(); + + /// Destructor + virtual ~PreparedStatement(); + + /// Close the result set (call DatabaseLayer::ClosePreparedStatement() instead on the statement) + virtual void Close() = 0; + + // set parameters + /// Set the parameter at the 1-based position to an int value + virtual void SetParamInt(int nPosition, int nValue) = 0; + /// Set the parameter at the 1-based position to a double value + virtual void SetParamDouble(int nPosition, double dblValue) = 0; + /// Set the parameter at the 1-based position to a wxString value + virtual void SetParamString(int nPosition, const wxString& strValue) = 0; + /// Set the parameter at the 1-based position to a NULL value + virtual void SetParamNull(int nPosition) = 0; + /// Set the parameter at the 1-based position to a Blob value + virtual void SetParamBlob(int nPosition, const wxMemoryBuffer& buffer); + /// Set the parameter at the 1-based position to a Blob value + virtual void SetParamBlob(int nPosition, const void* pData, long nDataLength) = 0; + /// Set the parameter at the 1-based position to a wxDateTime value + virtual void SetParamDate(int nPosition, const wxDateTime& dateValue) = 0; + /// Set the parameter at the 1-based position to a boolean value + virtual void SetParamBool(int nPosition, bool bValue) = 0; + virtual int GetParameterCount() = 0; + + /// Run an insert, update, or delete query on the database + virtual int RunQuery() = 0; + /// Run an insert, update, or delete query on the database + virtual DatabaseResultSet* RunQueryWithResults() = 0; + + // function names more consistent with JDBC and wxSQLite3 + // these just provide wrappers for existing functions + /// See RunQuery + int ExecuteUpdate() { return RunQuery(); } + /// See RunQueryWithResults + DatabaseResultSet* ExecuteQuery() { return RunQueryWithResults(); } + + /// Close a result set returned by the database or a prepared statement previously + virtual bool CloseResultSet(DatabaseResultSet* pResultSet); + +protected: + /// Close all result set objects that have been generated but not yet closed + void CloseResultSets(); + /// Add result set object pointer to the list for "garbage collection" + void LogResultSetForCleanup(DatabaseResultSet* pResultSet) { m_ResultSets.insert(pResultSet); } + +private: + StatementResultSetHashSet m_ResultSets; +}; + +#endif // __PREPARED_STATEMENT_H__ + diff --git a/ThirdParty/DatabaseLayer/include/ResultSetMetaData.h b/ThirdParty/DatabaseLayer/include/ResultSetMetaData.h new file mode 100644 index 0000000..d8073be --- /dev/null +++ b/ThirdParty/DatabaseLayer/include/ResultSetMetaData.h @@ -0,0 +1,43 @@ +#ifndef __RESULT_SET_METADATA_H__ +#define __RESULT_SET_METADATA_H__ + +// For compilers that support precompilation, includes "wx.h". +#include "wx/wxprec.h" + +#ifdef __BORLANDC__ + #pragma hdrstop +#endif + +#ifndef WX_PRECOMP + #include "wx/wx.h" +#endif + +#include "DatabaseLayerDef.h" +#include "DatabaseStringConverter.h" + +class WXDLLIMPEXP_DATABASELAYER ResultSetMetaData : public DatabaseStringConverter +{ +public: + /// Retrieve a column's type + virtual int GetColumnType(int i) = 0; + /// Retrieve a column's size + virtual int GetColumnSize(int i) = 0; + /// Retrieve a column's name + virtual wxString GetColumnName(int i) = 0; + /// Retrieve the number of columns in the result set + virtual int GetColumnCount() = 0; + + enum { + COLUMN_UNKNOWN = 0, + COLUMN_NULL, + COLUMN_INTEGER, + COLUMN_STRING, + COLUMN_DOUBLE, + COLUMN_BOOL, + COLUMN_BLOB, + COLUMN_DATE, + }; +}; + +#endif // __RESULT_SET_METADATA_H__ + diff --git a/ThirdParty/DatabaseLayer/include/SqliteDatabaseLayer.h b/ThirdParty/DatabaseLayer/include/SqliteDatabaseLayer.h new file mode 100644 index 0000000..ebe27a5 --- /dev/null +++ b/ThirdParty/DatabaseLayer/include/SqliteDatabaseLayer.h @@ -0,0 +1,73 @@ +#ifndef __SQLITE_DATABASE_LAYER_H__ +#define __SQLITE_DATABASE_LAYER_H__ + +// For compilers that support precompilation, includes "wx.h". +#include "wx/wxprec.h" + +#ifdef __BORLANDC__ + #pragma hdrstop +#endif + +#ifndef WX_PRECOMP + #include "wx/wx.h" +#endif + +#include "wx/arrstr.h" + +#include "DatabaseLayerDef.h" +#include "DatabaseLayer.h" + + +class PreparedStatement; + +class WXDLLIMPEXP_DATABASELAYER SqliteDatabaseLayer : public DatabaseLayer +{ +public: + // ctor() + SqliteDatabaseLayer(); + SqliteDatabaseLayer(const wxString& strDatabase, bool mustExist = false); + SqliteDatabaseLayer(void* pDatabase) { m_pDatabase = pDatabase; } + + // dtor() + virtual ~SqliteDatabaseLayer(); + + // open database + virtual bool Open(const wxString& strDatabase); + virtual bool Open(const wxString& strDatabase, bool mustExist); + + // close database + virtual bool Close(); + + // Is the connection to the database open? + virtual bool IsOpen(); + + // transaction support + virtual void BeginTransaction(); + virtual void Commit(); + virtual void RollBack(); + + // query database + virtual int RunQuery(const wxString& strQuery, bool bParseQuery); + virtual DatabaseResultSet* RunQueryWithResults(const wxString& strQuery); + + // PreparedStatement support + virtual PreparedStatement* PrepareStatement(const wxString& strQuery); + PreparedStatement* PrepareStatement(const wxString& strQuery, bool bLogForCleanup); + + // Database schema API contributed by M. Szeftel (author of wxActiveRecordGenerator) + virtual bool TableExists(const wxString& table); + virtual bool ViewExists(const wxString& view); + virtual wxArrayString GetTables(); + virtual wxArrayString GetViews(); + virtual wxArrayString GetColumns(const wxString& table); + + static int TranslateErrorCode(int nCode); + +private: + + //sqlite3* m_pDatabase; + void* m_pDatabase; +}; + +#endif // __SQLITE_DATABASE_LAYER_H__ + diff --git a/ThirdParty/DatabaseLayer/include/SqlitePreparedStatement.h b/ThirdParty/DatabaseLayer/include/SqlitePreparedStatement.h new file mode 100644 index 0000000..e545e6f --- /dev/null +++ b/ThirdParty/DatabaseLayer/include/SqlitePreparedStatement.h @@ -0,0 +1,63 @@ +#ifndef __SQLITE_PREPARED_STATEMENT_H__ +#define __SQLITE_PREPARED_STATEMENT_H__ + +// For compilers that support precompilation, includes "wx.h". +#include "wx/wxprec.h" + +#ifdef __BORLANDC__ + #pragma hdrstop +#endif + +#ifndef WX_PRECOMP + #include "wx/wx.h" +#endif + +#include "wx/dynarray.h" + +#include "PreparedStatement.h" + +#include "sqlite3.h" + +WX_DEFINE_ARRAY_PTR(sqlite3_stmt*, StatementVector); + +class DatabaseResultSet; + +class SqlitePreparedStatement : public PreparedStatement +{ +public: + // ctor + SqlitePreparedStatement(sqlite3* pDatabase); + SqlitePreparedStatement(sqlite3* pDatabase, sqlite3_stmt* pStatement); + SqlitePreparedStatement(sqlite3* pDatabase, StatementVector statements); + + // dtor + virtual ~SqlitePreparedStatement(); + + virtual void Close(); + + void AddPreparedStatement(sqlite3_stmt* pStatement); + + // get field + virtual void SetParamInt(int nPosition, int nValue); + virtual void SetParamDouble(int nPosition, double dblValue); + virtual void SetParamString(int nPosition, const wxString& strValue); + virtual void SetParamNull(int nPosition); + virtual void SetParamBlob(int nPosition, const void* pData, long nDataLength); + virtual void SetParamDate(int nPosition, const wxDateTime& dateValue); + virtual void SetParamBool(int nPosition, bool bValue); + virtual int GetParameterCount(); + + virtual int RunQuery(); + virtual DatabaseResultSet* RunQueryWithResults(); + + sqlite3_stmt* GetLastStatement() { return (m_Statements.size() > 0) ? m_Statements[m_Statements.size()-1] : NULL; } + +private: + int FindStatementAndAdjustPositionIndex(int* pPosition); + + sqlite3* m_pDatabase; // Database pointer needed for error messages + StatementVector m_Statements; +}; + +#endif // __SQLITE_PREPARED_STATEMENT_H__ + diff --git a/ThirdParty/DatabaseLayer/include/SqliteResultSet.h b/ThirdParty/DatabaseLayer/include/SqliteResultSet.h new file mode 100644 index 0000000..d60cbac --- /dev/null +++ b/ThirdParty/DatabaseLayer/include/SqliteResultSet.h @@ -0,0 +1,50 @@ +#ifndef __SQLITE_RESULT_SET_H__ +#define __SQLITE_RESULT_SET_H__ + +#include "DatabaseResultSet.h" + +#include "sqlite3.h" + +class SqlitePreparedStatement; +class ResultSetMetaData; + +class SqliteResultSet : public DatabaseResultSet +{ +public: + // ctor + SqliteResultSet(); + SqliteResultSet(SqlitePreparedStatement* pStatement, bool bManageStatement = false); + + // dtor + virtual ~SqliteResultSet(); + + virtual bool Next(); + virtual void Close(); + + virtual int LookupField(const wxString& strField); + + // get field + virtual int GetResultInt(int nField); + virtual wxString GetResultString(int nField); + virtual long GetResultLong(int nField); + virtual bool GetResultBool(int nField); + virtual wxDateTime GetResultDate(int nField); + virtual void* GetResultBlob(int nField, wxMemoryBuffer& Buffer); + virtual double GetResultDouble(int nField); + virtual bool IsFieldNull(int nField); + + // get MetaData + virtual ResultSetMetaData* GetMetaData(); + +private: + + SqlitePreparedStatement* m_pStatement; + sqlite3_stmt* m_pSqliteStatement; + + StringToIntMap m_FieldLookupMap; + + bool m_bManageStatement; +}; + +#endif // __SQLITE_RESULT_SET_H__ + diff --git a/ThirdParty/DatabaseLayer/include/SqliteResultSetMetaData.h b/ThirdParty/DatabaseLayer/include/SqliteResultSetMetaData.h new file mode 100644 index 0000000..e9add7c --- /dev/null +++ b/ThirdParty/DatabaseLayer/include/SqliteResultSetMetaData.h @@ -0,0 +1,34 @@ +#ifndef __SQLITE_RESULT_SET_METADATA_H__ +#define __SQLITE_RESULT_SET_METADATA_H__ + +// For compilers that support precompilation, includes "wx.h". +#include "wx/wxprec.h" + +#ifdef __BORLANDC__ + #pragma hdrstop +#endif + +#ifndef WX_PRECOMP + #include "wx/wx.h" +#endif + +#include "ResultSetMetaData.h" + +#include "sqlite3.h" + +class SqliteResultSetMetaData : public ResultSetMetaData +{ +public: + // ctor + SqliteResultSetMetaData(sqlite3_stmt* pStmt); + + virtual int GetColumnType(int i); + virtual int GetColumnSize(int i); + virtual wxString GetColumnName(int i); + virtual int GetColumnCount(); + +private: + sqlite3_stmt* m_pSqliteStatement; +}; + +#endif // __SQLITE_RESULT_SET_METADATA_H__ diff --git a/ThirdParty/DatabaseLayer/sqlite3/sqlite3.c b/ThirdParty/DatabaseLayer/sqlite3/sqlite3.c new file mode 100644 index 0000000..a7b24a2 --- /dev/null +++ b/ThirdParty/DatabaseLayer/sqlite3/sqlite3.c @@ -0,0 +1,104497 @@ +/****************************************************************************** +** This file is an amalgamation of many separate C source files from SQLite +** version 3.6.12. By combining all the individual C code files into this +** single large file, the entire code can be compiled as a one translation +** unit. This allows many compilers to do optimizations that would not be +** possible if the files were compiled separately. Performance improvements +** of 5% are more are commonly seen when SQLite is compiled as a single +** translation unit. +** +** This file is all you need to compile SQLite. To use SQLite in other +** programs, you need this file and the "sqlite3.h" header file that defines +** the programming interface to the SQLite library. (If you do not have +** the "sqlite3.h" header file at hand, you will find a copy in the first +** 5487 lines past this header comment.) Additional code files may be +** needed if you want a wrapper to interface SQLite with your choice of +** programming language. The code for the "sqlite3" command-line shell +** is also in a separate file. This file contains only code for the core +** SQLite library. +** +** This amalgamation was generated on 2009-03-31 12:19:59 UTC. +*/ +#define SQLITE_CORE 1 +#define SQLITE_AMALGAMATION 1 +#ifndef SQLITE_PRIVATE +# define SQLITE_PRIVATE static +#endif +#ifndef SQLITE_API +# define SQLITE_API +#endif +/************** Begin file sqliteInt.h ***************************************/ +/* +** 2001 September 15 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +************************************************************************* +** Internal interface definitions for SQLite. +** +** @(#) $Id: sqliteInt.h,v 1.848 2009/03/25 16:51:43 drh Exp $ +*/ +#ifndef _SQLITEINT_H_ +#define _SQLITEINT_H_ + +/* +** Include the configuration header output by 'configure' if we're using the +** autoconf-based build +*/ +#ifdef _HAVE_SQLITE_CONFIG_H +#include "config.h" +#endif + +/************** Include sqliteLimit.h in the middle of sqliteInt.h ***********/ +/************** Begin file sqliteLimit.h *************************************/ +/* +** 2007 May 7 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +************************************************************************* +** +** This file defines various limits of what SQLite can process. +** +** @(#) $Id: sqliteLimit.h,v 1.10 2009/01/10 16:15:09 danielk1977 Exp $ +*/ + +/* +** The maximum length of a TEXT or BLOB in bytes. This also +** limits the size of a row in a table or index. +** +** The hard limit is the ability of a 32-bit signed integer +** to count the size: 2^31-1 or 2147483647. +*/ +#ifndef SQLITE_MAX_LENGTH +# define SQLITE_MAX_LENGTH 1000000000 +#endif + +/* +** This is the maximum number of +** +** * Columns in a table +** * Columns in an index +** * Columns in a view +** * Terms in the SET clause of an UPDATE statement +** * Terms in the result set of a SELECT statement +** * Terms in the GROUP BY or ORDER BY clauses of a SELECT statement. +** * Terms in the VALUES clause of an INSERT statement +** +** The hard upper limit here is 32676. Most database people will +** tell you that in a well-normalized database, you usually should +** not have more than a dozen or so columns in any table. And if +** that is the case, there is no point in having more than a few +** dozen values in any of the other situations described above. +*/ +#ifndef SQLITE_MAX_COLUMN +# define SQLITE_MAX_COLUMN 2000 +#endif + +/* +** The maximum length of a single SQL statement in bytes. +** +** It used to be the case that setting this value to zero would +** turn the limit off. That is no longer true. It is not possible +** to turn this limit off. +*/ +#ifndef SQLITE_MAX_SQL_LENGTH +# define SQLITE_MAX_SQL_LENGTH 1000000000 +#endif + +/* +** The maximum depth of an expression tree. This is limited to +** some extent by SQLITE_MAX_SQL_LENGTH. But sometime you might +** want to place more severe limits on the complexity of an +** expression. +** +** A value of 0 used to mean that the limit was not enforced. +** But that is no longer true. The limit is now strictly enforced +** at all times. +*/ +#ifndef SQLITE_MAX_EXPR_DEPTH +# define SQLITE_MAX_EXPR_DEPTH 1000 +#endif + +/* +** The maximum number of terms in a compound SELECT statement. +** The code generator for compound SELECT statements does one +** level of recursion for each term. A stack overflow can result +** if the number of terms is too large. In practice, most SQL +** never has more than 3 or 4 terms. Use a value of 0 to disable +** any limit on the number of terms in a compount SELECT. +*/ +#ifndef SQLITE_MAX_COMPOUND_SELECT +# define SQLITE_MAX_COMPOUND_SELECT 500 +#endif + +/* +** The maximum number of opcodes in a VDBE program. +** Not currently enforced. +*/ +#ifndef SQLITE_MAX_VDBE_OP +# define SQLITE_MAX_VDBE_OP 25000 +#endif + +/* +** The maximum number of arguments to an SQL function. +*/ +#ifndef SQLITE_MAX_FUNCTION_ARG +# define SQLITE_MAX_FUNCTION_ARG 127 +#endif + +/* +** The maximum number of in-memory pages to use for the main database +** table and for temporary tables. The SQLITE_DEFAULT_CACHE_SIZE +*/ +#ifndef SQLITE_DEFAULT_CACHE_SIZE +# define SQLITE_DEFAULT_CACHE_SIZE 2000 +#endif +#ifndef SQLITE_DEFAULT_TEMP_CACHE_SIZE +# define SQLITE_DEFAULT_TEMP_CACHE_SIZE 500 +#endif + +/* +** The maximum number of attached databases. This must be between 0 +** and 30. The upper bound on 30 is because a 32-bit integer bitmap +** is used internally to track attached databases. +*/ +#ifndef SQLITE_MAX_ATTACHED +# define SQLITE_MAX_ATTACHED 10 +#endif + + +/* +** The maximum value of a ?nnn wildcard that the parser will accept. +*/ +#ifndef SQLITE_MAX_VARIABLE_NUMBER +# define SQLITE_MAX_VARIABLE_NUMBER 999 +#endif + +/* Maximum page size. The upper bound on this value is 32768. This a limit +** imposed by the necessity of storing the value in a 2-byte unsigned integer +** and the fact that the page size must be a power of 2. +** +** If this limit is changed, then the compiled library is technically +** incompatible with an SQLite library compiled with a different limit. If +** a process operating on a database with a page-size of 65536 bytes +** crashes, then an instance of SQLite compiled with the default page-size +** limit will not be able to rollback the aborted transaction. This could +** lead to database corruption. +*/ +#ifndef SQLITE_MAX_PAGE_SIZE +# define SQLITE_MAX_PAGE_SIZE 32768 +#endif + + +/* +** The default size of a database page. +*/ +#ifndef SQLITE_DEFAULT_PAGE_SIZE +# define SQLITE_DEFAULT_PAGE_SIZE 1024 +#endif +#if SQLITE_DEFAULT_PAGE_SIZE>SQLITE_MAX_PAGE_SIZE +# undef SQLITE_DEFAULT_PAGE_SIZE +# define SQLITE_DEFAULT_PAGE_SIZE SQLITE_MAX_PAGE_SIZE +#endif + +/* +** Ordinarily, if no value is explicitly provided, SQLite creates databases +** with page size SQLITE_DEFAULT_PAGE_SIZE. However, based on certain +** device characteristics (sector-size and atomic write() support), +** SQLite may choose a larger value. This constant is the maximum value +** SQLite will choose on its own. +*/ +#ifndef SQLITE_MAX_DEFAULT_PAGE_SIZE +# define SQLITE_MAX_DEFAULT_PAGE_SIZE 8192 +#endif +#if SQLITE_MAX_DEFAULT_PAGE_SIZE>SQLITE_MAX_PAGE_SIZE +# undef SQLITE_MAX_DEFAULT_PAGE_SIZE +# define SQLITE_MAX_DEFAULT_PAGE_SIZE SQLITE_MAX_PAGE_SIZE +#endif + + +/* +** Maximum number of pages in one database file. +** +** This is really just the default value for the max_page_count pragma. +** This value can be lowered (or raised) at run-time using that the +** max_page_count macro. +*/ +#ifndef SQLITE_MAX_PAGE_COUNT +# define SQLITE_MAX_PAGE_COUNT 1073741823 +#endif + +/* +** Maximum length (in bytes) of the pattern in a LIKE or GLOB +** operator. +*/ +#ifndef SQLITE_MAX_LIKE_PATTERN_LENGTH +# define SQLITE_MAX_LIKE_PATTERN_LENGTH 50000 +#endif + +/************** End of sqliteLimit.h *****************************************/ +/************** Continuing where we left off in sqliteInt.h ******************/ + +/* Disable nuisance warnings on Borland compilers */ +#if defined(__BORLANDC__) +#pragma warn -rch /* unreachable code */ +#pragma warn -ccc /* Condition is always true or false */ +#pragma warn -aus /* Assigned value is never used */ +#pragma warn -csu /* Comparing signed and unsigned */ +#pragma warn -spa /* Suspicious pointer arithmetic */ +#endif + +/* Needed for various definitions... */ +#ifndef _GNU_SOURCE +# define _GNU_SOURCE +#endif + +/* +** Include standard header files as necessary +*/ +#ifdef HAVE_STDINT_H +#include +#endif +#ifdef HAVE_INTTYPES_H +#include +#endif + +/* + * This macro is used to "hide" some ugliness in casting an int + * value to a ptr value under the MSVC 64-bit compiler. Casting + * non 64-bit values to ptr types results in a "hard" error with + * the MSVC 64-bit compiler which this attempts to avoid. + * + * A simple compiler pragma or casting sequence could not be found + * to correct this in all situations, so this macro was introduced. + * + * It could be argued that the intptr_t type could be used in this + * case, but that type is not available on all compilers, or + * requires the #include of specific headers which differs between + * platforms. + */ +#define SQLITE_INT_TO_PTR(X) ((void*)&((char*)0)[X]) +#define SQLITE_PTR_TO_INT(X) ((int)(((char*)X)-(char*)0)) + +/* +** These #defines should enable >2GB file support on POSIX if the +** underlying operating system supports it. If the OS lacks +** large file support, or if the OS is windows, these should be no-ops. +** +** Ticket #2739: The _LARGEFILE_SOURCE macro must appear before any +** system #includes. Hence, this block of code must be the very first +** code in all source files. +** +** Large file support can be disabled using the -DSQLITE_DISABLE_LFS switch +** on the compiler command line. This is necessary if you are compiling +** on a recent machine (ex: Red Hat 7.2) but you want your code to work +** on an older machine (ex: Red Hat 6.0). If you compile on Red Hat 7.2 +** without this option, LFS is enable. But LFS does not exist in the kernel +** in Red Hat 6.0, so the code won't work. Hence, for maximum binary +** portability you should omit LFS. +** +** Similar is true for Mac OS X. LFS is only supported on Mac OS X 9 and later. +*/ +#ifndef SQLITE_DISABLE_LFS +# define _LARGE_FILE 1 +# ifndef _FILE_OFFSET_BITS +# define _FILE_OFFSET_BITS 64 +# endif +# define _LARGEFILE_SOURCE 1 +#endif + + +/* +** The SQLITE_THREADSAFE macro must be defined as either 0 or 1. +** Older versions of SQLite used an optional THREADSAFE macro. +** We support that for legacy +*/ +#if !defined(SQLITE_THREADSAFE) +#if defined(THREADSAFE) +# define SQLITE_THREADSAFE THREADSAFE +#else +# define SQLITE_THREADSAFE 1 +#endif +#endif + +/* +** The SQLITE_DEFAULT_MEMSTATUS macro must be defined as either 0 or 1. +** It determines whether or not the features related to +** SQLITE_CONFIG_MEMSTATUS are available by default or not. This value can +** be overridden at runtime using the sqlite3_config() API. +*/ +#if !defined(SQLITE_DEFAULT_MEMSTATUS) +# define SQLITE_DEFAULT_MEMSTATUS 1 +#endif + +/* +** Exactly one of the following macros must be defined in order to +** specify which memory allocation subsystem to use. +** +** SQLITE_SYSTEM_MALLOC // Use normal system malloc() +** SQLITE_MEMDEBUG // Debugging version of system malloc() +** SQLITE_MEMORY_SIZE // internal allocator #1 +** SQLITE_MMAP_HEAP_SIZE // internal mmap() allocator +** SQLITE_POW2_MEMORY_SIZE // internal power-of-two allocator +** +** If none of the above are defined, then set SQLITE_SYSTEM_MALLOC as +** the default. +*/ +#if defined(SQLITE_SYSTEM_MALLOC)+defined(SQLITE_MEMDEBUG)+\ + defined(SQLITE_MEMORY_SIZE)+defined(SQLITE_MMAP_HEAP_SIZE)+\ + defined(SQLITE_POW2_MEMORY_SIZE)>1 +# error "At most one of the following compile-time configuration options\ + is allows: SQLITE_SYSTEM_MALLOC, SQLITE_MEMDEBUG, SQLITE_MEMORY_SIZE,\ + SQLITE_MMAP_HEAP_SIZE, SQLITE_POW2_MEMORY_SIZE" +#endif +#if defined(SQLITE_SYSTEM_MALLOC)+defined(SQLITE_MEMDEBUG)+\ + defined(SQLITE_MEMORY_SIZE)+defined(SQLITE_MMAP_HEAP_SIZE)+\ + defined(SQLITE_POW2_MEMORY_SIZE)==0 +# define SQLITE_SYSTEM_MALLOC 1 +#endif + +/* +** If SQLITE_MALLOC_SOFT_LIMIT is defined, then try to keep the +** sizes of memory allocations below this value where possible. +*/ +#if defined(SQLITE_POW2_MEMORY_SIZE) && !defined(SQLITE_MALLOC_SOFT_LIMIT) +# define SQLITE_MALLOC_SOFT_LIMIT 1024 +#endif + +/* +** We need to define _XOPEN_SOURCE as follows in order to enable +** recursive mutexes on most Unix systems. But Mac OS X is different. +** The _XOPEN_SOURCE define causes problems for Mac OS X we are told, +** so it is omitted there. See ticket #2673. +** +** Later we learn that _XOPEN_SOURCE is poorly or incorrectly +** implemented on some systems. So we avoid defining it at all +** if it is already defined or if it is unneeded because we are +** not doing a threadsafe build. Ticket #2681. +** +** See also ticket #2741. +*/ +#if !defined(_XOPEN_SOURCE) && !defined(__DARWIN__) && !defined(__APPLE__) && SQLITE_THREADSAFE +# define _XOPEN_SOURCE 500 /* Needed to enable pthread recursive mutexes */ +#endif + +/* +** The TCL headers are only needed when compiling the TCL bindings. +*/ +#if defined(SQLITE_TCL) || defined(TCLSH) +# include +#endif + +/* +** Many people are failing to set -DNDEBUG=1 when compiling SQLite. +** Setting NDEBUG makes the code smaller and run faster. So the following +** lines are added to automatically set NDEBUG unless the -DSQLITE_DEBUG=1 +** option is set. Thus NDEBUG becomes an opt-in rather than an opt-out +** feature. +*/ +#if !defined(NDEBUG) && !defined(SQLITE_DEBUG) +# define NDEBUG 1 +#endif + +/* +** The testcase() macro is used to aid in coverage testing. When +** doing coverage testing, the condition inside the argument to +** testcase() must be evaluated both true and false in order to +** get full branch coverage. The testcase() macro is inserted +** to help ensure adequate test coverage in places where simple +** condition/decision coverage is inadequate. For example, testcase() +** can be used to make sure boundary values are tested. For +** bitmask tests, testcase() can be used to make sure each bit +** is significant and used at least once. On switch statements +** where multiple cases go to the same block of code, testcase() +** can insure that all cases are evaluated. +** +*/ +#ifdef SQLITE_COVERAGE_TEST +SQLITE_PRIVATE void sqlite3Coverage(int); +# define testcase(X) if( X ){ sqlite3Coverage(__LINE__); } +#else +# define testcase(X) +#endif + +/* +** The TESTONLY macro is used to enclose variable declarations or +** other bits of code that are needed to support the arguments +** within testcase() and assert() macros. +*/ +#if !defined(NDEBUG) || defined(SQLITE_COVERAGE_TEST) +# define TESTONLY(X) X +#else +# define TESTONLY(X) +#endif + +/* +** The ALWAYS and NEVER macros surround boolean expressions which +** are intended to always be true or false, respectively. Such +** expressions could be omitted from the code completely. But they +** are included in a few cases in order to enhance the resilience +** of SQLite to unexpected behavior - to make the code "self-healing" +** or "ductile" rather than being "brittle" and crashing at the first +** hint of unplanned behavior. +** +** In other words, ALWAYS and NEVER are added for defensive code. +** +** When doing coverage testing ALWAYS and NEVER are hard-coded to +** be true and false so that the unreachable code then specify will +** not be counted as untested code. +*/ +#if defined(SQLITE_COVERAGE_TEST) +# define ALWAYS(X) (1) +# define NEVER(X) (0) +#elif !defined(NDEBUG) +SQLITE_PRIVATE int sqlite3Assert(void); +# define ALWAYS(X) ((X)?1:sqlite3Assert()) +# define NEVER(X) ((X)?sqlite3Assert():0) +#else +# define ALWAYS(X) (X) +# define NEVER(X) (X) +#endif + +/* +** The macro unlikely() is a hint that surrounds a boolean +** expression that is usually false. Macro likely() surrounds +** a boolean expression that is usually true. GCC is able to +** use these hints to generate better code, sometimes. +*/ +#if defined(__GNUC__) && 0 +# define likely(X) __builtin_expect((X),1) +# define unlikely(X) __builtin_expect((X),0) +#else +# define likely(X) !!(X) +# define unlikely(X) !!(X) +#endif + +/* +** Sometimes we need a small amount of code such as a variable initialization +** to setup for a later assert() statement. We do not want this code to +** appear when assert() is disabled. The following macro is therefore +** used to contain that setup code. The "VVA" acronym stands for +** "Verification, Validation, and Accreditation". In other words, the +** code within VVA_ONLY() will only run during verification processes. +*/ +#ifndef NDEBUG +# define VVA_ONLY(X) X +#else +# define VVA_ONLY(X) +#endif + +/************** Include sqlite3.h in the middle of sqliteInt.h ***************/ +/************** Begin file sqlite3.h *****************************************/ +/* +** 2001 September 15 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +************************************************************************* +** This header file defines the interface that the SQLite library +** presents to client programs. If a C-function, structure, datatype, +** or constant definition does not appear in this file, then it is +** not a published API of SQLite, is subject to change without +** notice, and should not be referenced by programs that use SQLite. +** +** Some of the definitions that are in this file are marked as +** "experimental". Experimental interfaces are normally new +** features recently added to SQLite. We do not anticipate changes +** to experimental interfaces but reserve to make minor changes if +** experience from use "in the wild" suggest such changes are prudent. +** +** The official C-language API documentation for SQLite is derived +** from comments in this file. This file is the authoritative source +** on how SQLite interfaces are suppose to operate. +** +** The name of this file under configuration management is "sqlite.h.in". +** The makefile makes some minor changes to this file (such as inserting +** the version number) and changes its name to "sqlite3.h" as +** part of the build process. +** +** @(#) $Id: sqlite.h.in,v 1.436 2009/03/20 13:15:30 drh Exp $ +*/ +#ifndef _SQLITE3_H_ +#define _SQLITE3_H_ +#include /* Needed for the definition of va_list */ + +/* +** Make sure we can call this stuff from C++. +*/ +#if 0 +extern "C" { +#endif + + +/* +** Add the ability to override 'extern' +*/ +#ifndef SQLITE_EXTERN +# define SQLITE_EXTERN extern +#endif + +/* +** These no-op macros are used in front of interfaces to mark those +** interfaces as either deprecated or experimental. New applications +** should not use deprecated intrfaces - they are support for backwards +** compatibility only. Application writers should be aware that +** experimental interfaces are subject to change in point releases. +** +** These macros used to resolve to various kinds of compiler magic that +** would generate warning messages when they were used. But that +** compiler magic ended up generating such a flurry of bug reports +** that we have taken it all out and gone back to using simple +** noop macros. +*/ +#define SQLITE_DEPRECATED +#define SQLITE_EXPERIMENTAL + +/* +** Ensure these symbols were not defined by some previous header file. +*/ +#ifdef SQLITE_VERSION +# undef SQLITE_VERSION +#endif +#ifdef SQLITE_VERSION_NUMBER +# undef SQLITE_VERSION_NUMBER +#endif + +/* +** CAPI3REF: Compile-Time Library Version Numbers {H10010} +** +** The SQLITE_VERSION and SQLITE_VERSION_NUMBER #defines in +** the sqlite3.h file specify the version of SQLite with which +** that header file is associated. +** +** The "version" of SQLite is a string of the form "X.Y.Z". +** The phrase "alpha" or "beta" might be appended after the Z. +** The X value is major version number always 3 in SQLite3. +** The X value only changes when backwards compatibility is +** broken and we intend to never break backwards compatibility. +** The Y value is the minor version number and only changes when +** there are major feature enhancements that are forwards compatible +** but not backwards compatible. +** The Z value is the release number and is incremented with +** each release but resets back to 0 whenever Y is incremented. +** +** See also: [sqlite3_libversion()] and [sqlite3_libversion_number()]. +** +** Requirements: [H10011] [H10014] +*/ +#define SQLITE_VERSION "3.6.12" +#define SQLITE_VERSION_NUMBER 3006012 + +/* +** CAPI3REF: Run-Time Library Version Numbers {H10020} +** KEYWORDS: sqlite3_version +** +** These features provide the same information as the [SQLITE_VERSION] +** and [SQLITE_VERSION_NUMBER] #defines in the header, but are associated +** with the library instead of the header file. Cautious programmers might +** include a check in their application to verify that +** sqlite3_libversion_number() always returns the value +** [SQLITE_VERSION_NUMBER]. +** +** The sqlite3_libversion() function returns the same information as is +** in the sqlite3_version[] string constant. The function is provided +** for use in DLLs since DLL users usually do not have direct access to string +** constants within the DLL. +** +** Requirements: [H10021] [H10022] [H10023] +*/ +SQLITE_API const char sqlite3_version[] = SQLITE_VERSION; +SQLITE_API const char *sqlite3_libversion(void); +SQLITE_API int sqlite3_libversion_number(void); + +/* +** CAPI3REF: Test To See If The Library Is Threadsafe {H10100} +** +** SQLite can be compiled with or without mutexes. When +** the [SQLITE_THREADSAFE] C preprocessor macro 1 or 2, mutexes +** are enabled and SQLite is threadsafe. When the +** [SQLITE_THREADSAFE] macro is 0, +** the mutexes are omitted. Without the mutexes, it is not safe +** to use SQLite concurrently from more than one thread. +** +** Enabling mutexes incurs a measurable performance penalty. +** So if speed is of utmost importance, it makes sense to disable +** the mutexes. But for maximum safety, mutexes should be enabled. +** The default behavior is for mutexes to be enabled. +** +** This interface can be used by a program to make sure that the +** version of SQLite that it is linking against was compiled with +** the desired setting of the [SQLITE_THREADSAFE] macro. +** +** This interface only reports on the compile-time mutex setting +** of the [SQLITE_THREADSAFE] flag. If SQLite is compiled with +** SQLITE_THREADSAFE=1 then mutexes are enabled by default but +** can be fully or partially disabled using a call to [sqlite3_config()] +** with the verbs [SQLITE_CONFIG_SINGLETHREAD], [SQLITE_CONFIG_MULTITHREAD], +** or [SQLITE_CONFIG_MUTEX]. The return value of this function shows +** only the default compile-time setting, not any run-time changes +** to that setting. +** +** See the [threading mode] documentation for additional information. +** +** Requirements: [H10101] [H10102] +*/ +SQLITE_API int sqlite3_threadsafe(void); + +/* +** CAPI3REF: Database Connection Handle {H12000} +** KEYWORDS: {database connection} {database connections} +** +** Each open SQLite database is represented by a pointer to an instance of +** the opaque structure named "sqlite3". It is useful to think of an sqlite3 +** pointer as an object. The [sqlite3_open()], [sqlite3_open16()], and +** [sqlite3_open_v2()] interfaces are its constructors, and [sqlite3_close()] +** is its destructor. There are many other interfaces (such as +** [sqlite3_prepare_v2()], [sqlite3_create_function()], and +** [sqlite3_busy_timeout()] to name but three) that are methods on an +** sqlite3 object. +*/ +typedef struct sqlite3 sqlite3; + +/* +** CAPI3REF: 64-Bit Integer Types {H10200} +** KEYWORDS: sqlite_int64 sqlite_uint64 +** +** Because there is no cross-platform way to specify 64-bit integer types +** SQLite includes typedefs for 64-bit signed and unsigned integers. +** +** The sqlite3_int64 and sqlite3_uint64 are the preferred type definitions. +** The sqlite_int64 and sqlite_uint64 types are supported for backwards +** compatibility only. +** +** Requirements: [H10201] [H10202] +*/ +#ifdef SQLITE_INT64_TYPE + typedef SQLITE_INT64_TYPE sqlite_int64; + typedef unsigned SQLITE_INT64_TYPE sqlite_uint64; +#elif defined(_MSC_VER) || defined(__BORLANDC__) + typedef __int64 sqlite_int64; + typedef unsigned __int64 sqlite_uint64; +#else + typedef long long int sqlite_int64; + typedef unsigned long long int sqlite_uint64; +#endif +typedef sqlite_int64 sqlite3_int64; +typedef sqlite_uint64 sqlite3_uint64; + +/* +** If compiling for a processor that lacks floating point support, +** substitute integer for floating-point. +*/ +#ifdef SQLITE_OMIT_FLOATING_POINT +# define double sqlite3_int64 +#endif + +/* +** CAPI3REF: Closing A Database Connection {H12010} +** +** This routine is the destructor for the [sqlite3] object. +** +** Applications should [sqlite3_finalize | finalize] all [prepared statements] +** and [sqlite3_blob_close | close] all [BLOB handles] associated with +** the [sqlite3] object prior to attempting to close the object. +** The [sqlite3_next_stmt()] interface can be used to locate all +** [prepared statements] associated with a [database connection] if desired. +** Typical code might look like this: +** +**
+** sqlite3_stmt *pStmt;
+** while( (pStmt = sqlite3_next_stmt(db, 0))!=0 ){
+**     sqlite3_finalize(pStmt);
+** }
+** 
+** +** If [sqlite3_close()] is invoked while a transaction is open, +** the transaction is automatically rolled back. +** +** The C parameter to [sqlite3_close(C)] must be either a NULL +** pointer or an [sqlite3] object pointer obtained +** from [sqlite3_open()], [sqlite3_open16()], or +** [sqlite3_open_v2()], and not previously closed. +** +** Requirements: +** [H12011] [H12012] [H12013] [H12014] [H12015] [H12019] +*/ +SQLITE_API int sqlite3_close(sqlite3 *); + +/* +** The type for a callback function. +** This is legacy and deprecated. It is included for historical +** compatibility and is not documented. +*/ +typedef int (*sqlite3_callback)(void*,int,char**, char**); + +/* +** CAPI3REF: One-Step Query Execution Interface {H12100} +** +** The sqlite3_exec() interface is a convenient way of running one or more +** SQL statements without having to write a lot of C code. The UTF-8 encoded +** SQL statements are passed in as the second parameter to sqlite3_exec(). +** The statements are evaluated one by one until either an error or +** an interrupt is encountered, or until they are all done. The 3rd parameter +** is an optional callback that is invoked once for each row of any query +** results produced by the SQL statements. The 5th parameter tells where +** to write any error messages. +** +** The error message passed back through the 5th parameter is held +** in memory obtained from [sqlite3_malloc()]. To avoid a memory leak, +** the calling application should call [sqlite3_free()] on any error +** message returned through the 5th parameter when it has finished using +** the error message. +** +** If the SQL statement in the 2nd parameter is NULL or an empty string +** or a string containing only whitespace and comments, then no SQL +** statements are evaluated and the database is not changed. +** +** The sqlite3_exec() interface is implemented in terms of +** [sqlite3_prepare_v2()], [sqlite3_step()], and [sqlite3_finalize()]. +** The sqlite3_exec() routine does nothing to the database that cannot be done +** by [sqlite3_prepare_v2()], [sqlite3_step()], and [sqlite3_finalize()]. +** +** The first parameter to [sqlite3_exec()] must be an valid and open +** [database connection]. +** +** The database connection must not be closed while +** [sqlite3_exec()] is running. +** +** The calling function should use [sqlite3_free()] to free +** the memory that *errmsg is left pointing at once the error +** message is no longer needed. +** +** The SQL statement text in the 2nd parameter to [sqlite3_exec()] +** must remain unchanged while [sqlite3_exec()] is running. +** +** Requirements: +** [H12101] [H12102] [H12104] [H12105] [H12107] [H12110] [H12113] [H12116] +** [H12119] [H12122] [H12125] [H12131] [H12134] [H12137] [H12138] +*/ +SQLITE_API int sqlite3_exec( + sqlite3*, /* An open database */ + const char *sql, /* SQL to be evaluated */ + int (*callback)(void*,int,char**,char**), /* Callback function */ + void *, /* 1st argument to callback */ + char **errmsg /* Error msg written here */ +); + +/* +** CAPI3REF: Result Codes {H10210} +** KEYWORDS: SQLITE_OK {error code} {error codes} +** KEYWORDS: {result code} {result codes} +** +** Many SQLite functions return an integer result code from the set shown +** here in order to indicates success or failure. +** +** New error codes may be added in future versions of SQLite. +** +** See also: [SQLITE_IOERR_READ | extended result codes] +*/ +#define SQLITE_OK 0 /* Successful result */ +/* beginning-of-error-codes */ +#define SQLITE_ERROR 1 /* SQL error or missing database */ +#define SQLITE_INTERNAL 2 /* Internal logic error in SQLite */ +#define SQLITE_PERM 3 /* Access permission denied */ +#define SQLITE_ABORT 4 /* Callback routine requested an abort */ +#define SQLITE_BUSY 5 /* The database file is locked */ +#define SQLITE_LOCKED 6 /* A table in the database is locked */ +#define SQLITE_NOMEM 7 /* A malloc() failed */ +#define SQLITE_READONLY 8 /* Attempt to write a readonly database */ +#define SQLITE_INTERRUPT 9 /* Operation terminated by sqlite3_interrupt()*/ +#define SQLITE_IOERR 10 /* Some kind of disk I/O error occurred */ +#define SQLITE_CORRUPT 11 /* The database disk image is malformed */ +#define SQLITE_NOTFOUND 12 /* NOT USED. Table or record not found */ +#define SQLITE_FULL 13 /* Insertion failed because database is full */ +#define SQLITE_CANTOPEN 14 /* Unable to open the database file */ +#define SQLITE_PROTOCOL 15 /* NOT USED. Database lock protocol error */ +#define SQLITE_EMPTY 16 /* Database is empty */ +#define SQLITE_SCHEMA 17 /* The database schema changed */ +#define SQLITE_TOOBIG 18 /* String or BLOB exceeds size limit */ +#define SQLITE_CONSTRAINT 19 /* Abort due to constraint violation */ +#define SQLITE_MISMATCH 20 /* Data type mismatch */ +#define SQLITE_MISUSE 21 /* Library used incorrectly */ +#define SQLITE_NOLFS 22 /* Uses OS features not supported on host */ +#define SQLITE_AUTH 23 /* Authorization denied */ +#define SQLITE_FORMAT 24 /* Auxiliary database format error */ +#define SQLITE_RANGE 25 /* 2nd parameter to sqlite3_bind out of range */ +#define SQLITE_NOTADB 26 /* File opened that is not a database file */ +#define SQLITE_ROW 100 /* sqlite3_step() has another row ready */ +#define SQLITE_DONE 101 /* sqlite3_step() has finished executing */ +/* end-of-error-codes */ + +/* +** CAPI3REF: Extended Result Codes {H10220} +** KEYWORDS: {extended error code} {extended error codes} +** KEYWORDS: {extended result code} {extended result codes} +** +** In its default configuration, SQLite API routines return one of 26 integer +** [SQLITE_OK | result codes]. However, experience has shown that many of +** these result codes are too coarse-grained. They do not provide as +** much information about problems as programmers might like. In an effort to +** address this, newer versions of SQLite (version 3.3.8 and later) include +** support for additional result codes that provide more detailed information +** about errors. The extended result codes are enabled or disabled +** on a per database connection basis using the +** [sqlite3_extended_result_codes()] API. +** +** Some of the available extended result codes are listed here. +** One may expect the number of extended result codes will be expand +** over time. Software that uses extended result codes should expect +** to see new result codes in future releases of SQLite. +** +** The SQLITE_OK result code will never be extended. It will always +** be exactly zero. +*/ +#define SQLITE_IOERR_READ (SQLITE_IOERR | (1<<8)) +#define SQLITE_IOERR_SHORT_READ (SQLITE_IOERR | (2<<8)) +#define SQLITE_IOERR_WRITE (SQLITE_IOERR | (3<<8)) +#define SQLITE_IOERR_FSYNC (SQLITE_IOERR | (4<<8)) +#define SQLITE_IOERR_DIR_FSYNC (SQLITE_IOERR | (5<<8)) +#define SQLITE_IOERR_TRUNCATE (SQLITE_IOERR | (6<<8)) +#define SQLITE_IOERR_FSTAT (SQLITE_IOERR | (7<<8)) +#define SQLITE_IOERR_UNLOCK (SQLITE_IOERR | (8<<8)) +#define SQLITE_IOERR_RDLOCK (SQLITE_IOERR | (9<<8)) +#define SQLITE_IOERR_DELETE (SQLITE_IOERR | (10<<8)) +#define SQLITE_IOERR_BLOCKED (SQLITE_IOERR | (11<<8)) +#define SQLITE_IOERR_NOMEM (SQLITE_IOERR | (12<<8)) +#define SQLITE_IOERR_ACCESS (SQLITE_IOERR | (13<<8)) +#define SQLITE_IOERR_CHECKRESERVEDLOCK (SQLITE_IOERR | (14<<8)) +#define SQLITE_IOERR_LOCK (SQLITE_IOERR | (15<<8)) +#define SQLITE_IOERR_CLOSE (SQLITE_IOERR | (16<<8)) +#define SQLITE_IOERR_DIR_CLOSE (SQLITE_IOERR | (17<<8)) + +#define SQLITE_LOCKED_SHAREDCACHE (SQLITE_LOCKED | (1<<8) ) + +/* +** CAPI3REF: Flags For File Open Operations {H10230} +** +** These bit values are intended for use in the +** 3rd parameter to the [sqlite3_open_v2()] interface and +** in the 4th parameter to the xOpen method of the +** [sqlite3_vfs] object. +*/ +#define SQLITE_OPEN_READONLY 0x00000001 +#define SQLITE_OPEN_READWRITE 0x00000002 +#define SQLITE_OPEN_CREATE 0x00000004 +#define SQLITE_OPEN_DELETEONCLOSE 0x00000008 +#define SQLITE_OPEN_EXCLUSIVE 0x00000010 +#define SQLITE_OPEN_MAIN_DB 0x00000100 +#define SQLITE_OPEN_TEMP_DB 0x00000200 +#define SQLITE_OPEN_TRANSIENT_DB 0x00000400 +#define SQLITE_OPEN_MAIN_JOURNAL 0x00000800 +#define SQLITE_OPEN_TEMP_JOURNAL 0x00001000 +#define SQLITE_OPEN_SUBJOURNAL 0x00002000 +#define SQLITE_OPEN_MASTER_JOURNAL 0x00004000 +#define SQLITE_OPEN_NOMUTEX 0x00008000 +#define SQLITE_OPEN_FULLMUTEX 0x00010000 + +/* +** CAPI3REF: Device Characteristics {H10240} +** +** The xDeviceCapabilities method of the [sqlite3_io_methods] +** object returns an integer which is a vector of the these +** bit values expressing I/O characteristics of the mass storage +** device that holds the file that the [sqlite3_io_methods] +** refers to. +** +** The SQLITE_IOCAP_ATOMIC property means that all writes of +** any size are atomic. The SQLITE_IOCAP_ATOMICnnn values +** mean that writes of blocks that are nnn bytes in size and +** are aligned to an address which is an integer multiple of +** nnn are atomic. The SQLITE_IOCAP_SAFE_APPEND value means +** that when data is appended to a file, the data is appended +** first then the size of the file is extended, never the other +** way around. The SQLITE_IOCAP_SEQUENTIAL property means that +** information is written to disk in the same order as calls +** to xWrite(). +*/ +#define SQLITE_IOCAP_ATOMIC 0x00000001 +#define SQLITE_IOCAP_ATOMIC512 0x00000002 +#define SQLITE_IOCAP_ATOMIC1K 0x00000004 +#define SQLITE_IOCAP_ATOMIC2K 0x00000008 +#define SQLITE_IOCAP_ATOMIC4K 0x00000010 +#define SQLITE_IOCAP_ATOMIC8K 0x00000020 +#define SQLITE_IOCAP_ATOMIC16K 0x00000040 +#define SQLITE_IOCAP_ATOMIC32K 0x00000080 +#define SQLITE_IOCAP_ATOMIC64K 0x00000100 +#define SQLITE_IOCAP_SAFE_APPEND 0x00000200 +#define SQLITE_IOCAP_SEQUENTIAL 0x00000400 + +/* +** CAPI3REF: File Locking Levels {H10250} +** +** SQLite uses one of these integer values as the second +** argument to calls it makes to the xLock() and xUnlock() methods +** of an [sqlite3_io_methods] object. +*/ +#define SQLITE_LOCK_NONE 0 +#define SQLITE_LOCK_SHARED 1 +#define SQLITE_LOCK_RESERVED 2 +#define SQLITE_LOCK_PENDING 3 +#define SQLITE_LOCK_EXCLUSIVE 4 + +/* +** CAPI3REF: Synchronization Type Flags {H10260} +** +** When SQLite invokes the xSync() method of an +** [sqlite3_io_methods] object it uses a combination of +** these integer values as the second argument. +** +** When the SQLITE_SYNC_DATAONLY flag is used, it means that the +** sync operation only needs to flush data to mass storage. Inode +** information need not be flushed. The SQLITE_SYNC_NORMAL flag means +** to use normal fsync() semantics. The SQLITE_SYNC_FULL flag means +** to use Mac OS X style fullsync instead of fsync(). +*/ +#define SQLITE_SYNC_NORMAL 0x00002 +#define SQLITE_SYNC_FULL 0x00003 +#define SQLITE_SYNC_DATAONLY 0x00010 + +/* +** CAPI3REF: OS Interface Open File Handle {H11110} +** +** An [sqlite3_file] object represents an open file in the OS +** interface layer. Individual OS interface implementations will +** want to subclass this object by appending additional fields +** for their own use. The pMethods entry is a pointer to an +** [sqlite3_io_methods] object that defines methods for performing +** I/O operations on the open file. +*/ +typedef struct sqlite3_file sqlite3_file; +struct sqlite3_file { + const struct sqlite3_io_methods *pMethods; /* Methods for an open file */ +}; + +/* +** CAPI3REF: OS Interface File Virtual Methods Object {H11120} +** +** Every file opened by the [sqlite3_vfs] xOpen method populates an +** [sqlite3_file] object (or, more commonly, a subclass of the +** [sqlite3_file] object) with a pointer to an instance of this object. +** This object defines the methods used to perform various operations +** against the open file represented by the [sqlite3_file] object. +** +** The flags argument to xSync may be one of [SQLITE_SYNC_NORMAL] or +** [SQLITE_SYNC_FULL]. The first choice is the normal fsync(). +** The second choice is a Mac OS X style fullsync. The [SQLITE_SYNC_DATAONLY] +** flag may be ORed in to indicate that only the data of the file +** and not its inode needs to be synced. +** +** The integer values to xLock() and xUnlock() are one of +**
    +**
  • [SQLITE_LOCK_NONE], +**
  • [SQLITE_LOCK_SHARED], +**
  • [SQLITE_LOCK_RESERVED], +**
  • [SQLITE_LOCK_PENDING], or +**
  • [SQLITE_LOCK_EXCLUSIVE]. +**
+** xLock() increases the lock. xUnlock() decreases the lock. +** The xCheckReservedLock() method checks whether any database connection, +** either in this process or in some other process, is holding a RESERVED, +** PENDING, or EXCLUSIVE lock on the file. It returns true +** if such a lock exists and false otherwise. +** +** The xFileControl() method is a generic interface that allows custom +** VFS implementations to directly control an open file using the +** [sqlite3_file_control()] interface. The second "op" argument is an +** integer opcode. The third argument is a generic pointer intended to +** point to a structure that may contain arguments or space in which to +** write return values. Potential uses for xFileControl() might be +** functions to enable blocking locks with timeouts, to change the +** locking strategy (for example to use dot-file locks), to inquire +** about the status of a lock, or to break stale locks. The SQLite +** core reserves all opcodes less than 100 for its own use. +** A [SQLITE_FCNTL_LOCKSTATE | list of opcodes] less than 100 is available. +** Applications that define a custom xFileControl method should use opcodes +** greater than 100 to avoid conflicts. +** +** The xSectorSize() method returns the sector size of the +** device that underlies the file. The sector size is the +** minimum write that can be performed without disturbing +** other bytes in the file. The xDeviceCharacteristics() +** method returns a bit vector describing behaviors of the +** underlying device: +** +**
    +**
  • [SQLITE_IOCAP_ATOMIC] +**
  • [SQLITE_IOCAP_ATOMIC512] +**
  • [SQLITE_IOCAP_ATOMIC1K] +**
  • [SQLITE_IOCAP_ATOMIC2K] +**
  • [SQLITE_IOCAP_ATOMIC4K] +**
  • [SQLITE_IOCAP_ATOMIC8K] +**
  • [SQLITE_IOCAP_ATOMIC16K] +**
  • [SQLITE_IOCAP_ATOMIC32K] +**
  • [SQLITE_IOCAP_ATOMIC64K] +**
  • [SQLITE_IOCAP_SAFE_APPEND] +**
  • [SQLITE_IOCAP_SEQUENTIAL] +**
+** +** The SQLITE_IOCAP_ATOMIC property means that all writes of +** any size are atomic. The SQLITE_IOCAP_ATOMICnnn values +** mean that writes of blocks that are nnn bytes in size and +** are aligned to an address which is an integer multiple of +** nnn are atomic. The SQLITE_IOCAP_SAFE_APPEND value means +** that when data is appended to a file, the data is appended +** first then the size of the file is extended, never the other +** way around. The SQLITE_IOCAP_SEQUENTIAL property means that +** information is written to disk in the same order as calls +** to xWrite(). +** +** If xRead() returns SQLITE_IOERR_SHORT_READ it must also fill +** in the unread portions of the buffer with zeros. A VFS that +** fails to zero-fill short reads might seem to work. However, +** failure to zero-fill short reads will eventually lead to +** database corruption. +*/ +typedef struct sqlite3_io_methods sqlite3_io_methods; +struct sqlite3_io_methods { + int iVersion; + int (*xClose)(sqlite3_file*); + int (*xRead)(sqlite3_file*, void*, int iAmt, sqlite3_int64 iOfst); + int (*xWrite)(sqlite3_file*, const void*, int iAmt, sqlite3_int64 iOfst); + int (*xTruncate)(sqlite3_file*, sqlite3_int64 size); + int (*xSync)(sqlite3_file*, int flags); + int (*xFileSize)(sqlite3_file*, sqlite3_int64 *pSize); + int (*xLock)(sqlite3_file*, int); + int (*xUnlock)(sqlite3_file*, int); + int (*xCheckReservedLock)(sqlite3_file*, int *pResOut); + int (*xFileControl)(sqlite3_file*, int op, void *pArg); + int (*xSectorSize)(sqlite3_file*); + int (*xDeviceCharacteristics)(sqlite3_file*); + /* Additional methods may be added in future releases */ +}; + +/* +** CAPI3REF: Standard File Control Opcodes {H11310} +** +** These integer constants are opcodes for the xFileControl method +** of the [sqlite3_io_methods] object and for the [sqlite3_file_control()] +** interface. +** +** The [SQLITE_FCNTL_LOCKSTATE] opcode is used for debugging. This +** opcode causes the xFileControl method to write the current state of +** the lock (one of [SQLITE_LOCK_NONE], [SQLITE_LOCK_SHARED], +** [SQLITE_LOCK_RESERVED], [SQLITE_LOCK_PENDING], or [SQLITE_LOCK_EXCLUSIVE]) +** into an integer that the pArg argument points to. This capability +** is used during testing and only needs to be supported when SQLITE_TEST +** is defined. +*/ +#define SQLITE_FCNTL_LOCKSTATE 1 +#define SQLITE_GET_LOCKPROXYFILE 2 +#define SQLITE_SET_LOCKPROXYFILE 3 +#define SQLITE_LAST_ERRNO 4 + +/* +** CAPI3REF: Mutex Handle {H17110} +** +** The mutex module within SQLite defines [sqlite3_mutex] to be an +** abstract type for a mutex object. The SQLite core never looks +** at the internal representation of an [sqlite3_mutex]. It only +** deals with pointers to the [sqlite3_mutex] object. +** +** Mutexes are created using [sqlite3_mutex_alloc()]. +*/ +typedef struct sqlite3_mutex sqlite3_mutex; + +/* +** CAPI3REF: OS Interface Object {H11140} +** +** An instance of the sqlite3_vfs object defines the interface between +** the SQLite core and the underlying operating system. The "vfs" +** in the name of the object stands for "virtual file system". +** +** The value of the iVersion field is initially 1 but may be larger in +** future versions of SQLite. Additional fields may be appended to this +** object when the iVersion value is increased. Note that the structure +** of the sqlite3_vfs object changes in the transaction between +** SQLite version 3.5.9 and 3.6.0 and yet the iVersion field was not +** modified. +** +** The szOsFile field is the size of the subclassed [sqlite3_file] +** structure used by this VFS. mxPathname is the maximum length of +** a pathname in this VFS. +** +** Registered sqlite3_vfs objects are kept on a linked list formed by +** the pNext pointer. The [sqlite3_vfs_register()] +** and [sqlite3_vfs_unregister()] interfaces manage this list +** in a thread-safe way. The [sqlite3_vfs_find()] interface +** searches the list. Neither the application code nor the VFS +** implementation should use the pNext pointer. +** +** The pNext field is the only field in the sqlite3_vfs +** structure that SQLite will ever modify. SQLite will only access +** or modify this field while holding a particular static mutex. +** The application should never modify anything within the sqlite3_vfs +** object once the object has been registered. +** +** The zName field holds the name of the VFS module. The name must +** be unique across all VFS modules. +** +** SQLite will guarantee that the zFilename parameter to xOpen +** is either a NULL pointer or string obtained +** from xFullPathname(). SQLite further guarantees that +** the string will be valid and unchanged until xClose() is +** called. Because of the previous sentense, +** the [sqlite3_file] can safely store a pointer to the +** filename if it needs to remember the filename for some reason. +** If the zFilename parameter is xOpen is a NULL pointer then xOpen +** must invite its own temporary name for the file. Whenever the +** xFilename parameter is NULL it will also be the case that the +** flags parameter will include [SQLITE_OPEN_DELETEONCLOSE]. +** +** The flags argument to xOpen() includes all bits set in +** the flags argument to [sqlite3_open_v2()]. Or if [sqlite3_open()] +** or [sqlite3_open16()] is used, then flags includes at least +** [SQLITE_OPEN_READWRITE] | [SQLITE_OPEN_CREATE]. +** If xOpen() opens a file read-only then it sets *pOutFlags to +** include [SQLITE_OPEN_READONLY]. Other bits in *pOutFlags may be set. +** +** SQLite will also add one of the following flags to the xOpen() +** call, depending on the object being opened: +** +**
    +**
  • [SQLITE_OPEN_MAIN_DB] +**
  • [SQLITE_OPEN_MAIN_JOURNAL] +**
  • [SQLITE_OPEN_TEMP_DB] +**
  • [SQLITE_OPEN_TEMP_JOURNAL] +**
  • [SQLITE_OPEN_TRANSIENT_DB] +**
  • [SQLITE_OPEN_SUBJOURNAL] +**
  • [SQLITE_OPEN_MASTER_JOURNAL] +**
+** +** The file I/O implementation can use the object type flags to +** change the way it deals with files. For example, an application +** that does not care about crash recovery or rollback might make +** the open of a journal file a no-op. Writes to this journal would +** also be no-ops, and any attempt to read the journal would return +** SQLITE_IOERR. Or the implementation might recognize that a database +** file will be doing page-aligned sector reads and writes in a random +** order and set up its I/O subsystem accordingly. +** +** SQLite might also add one of the following flags to the xOpen method: +** +**
    +**
  • [SQLITE_OPEN_DELETEONCLOSE] +**
  • [SQLITE_OPEN_EXCLUSIVE] +**
+** +** The [SQLITE_OPEN_DELETEONCLOSE] flag means the file should be +** deleted when it is closed. The [SQLITE_OPEN_DELETEONCLOSE] +** will be set for TEMP databases, journals and for subjournals. +** +** The [SQLITE_OPEN_EXCLUSIVE] flag means the file should be opened +** for exclusive access. This flag is set for all files except +** for the main database file. +** +** At least szOsFile bytes of memory are allocated by SQLite +** to hold the [sqlite3_file] structure passed as the third +** argument to xOpen. The xOpen method does not have to +** allocate the structure; it should just fill it in. +** +** The flags argument to xAccess() may be [SQLITE_ACCESS_EXISTS] +** to test for the existence of a file, or [SQLITE_ACCESS_READWRITE] to +** test whether a file is readable and writable, or [SQLITE_ACCESS_READ] +** to test whether a file is at least readable. The file can be a +** directory. +** +** SQLite will always allocate at least mxPathname+1 bytes for the +** output buffer xFullPathname. The exact size of the output buffer +** is also passed as a parameter to both methods. If the output buffer +** is not large enough, [SQLITE_CANTOPEN] should be returned. Since this is +** handled as a fatal error by SQLite, vfs implementations should endeavor +** to prevent this by setting mxPathname to a sufficiently large value. +** +** The xRandomness(), xSleep(), and xCurrentTime() interfaces +** are not strictly a part of the filesystem, but they are +** included in the VFS structure for completeness. +** The xRandomness() function attempts to return nBytes bytes +** of good-quality randomness into zOut. The return value is +** the actual number of bytes of randomness obtained. +** The xSleep() method causes the calling thread to sleep for at +** least the number of microseconds given. The xCurrentTime() +** method returns a Julian Day Number for the current date and time. +** +*/ +typedef struct sqlite3_vfs sqlite3_vfs; +struct sqlite3_vfs { + int iVersion; /* Structure version number */ + int szOsFile; /* Size of subclassed sqlite3_file */ + int mxPathname; /* Maximum file pathname length */ + sqlite3_vfs *pNext; /* Next registered VFS */ + const char *zName; /* Name of this virtual file system */ + void *pAppData; /* Pointer to application-specific data */ + int (*xOpen)(sqlite3_vfs*, const char *zName, sqlite3_file*, + int flags, int *pOutFlags); + int (*xDelete)(sqlite3_vfs*, const char *zName, int syncDir); + int (*xAccess)(sqlite3_vfs*, const char *zName, int flags, int *pResOut); + int (*xFullPathname)(sqlite3_vfs*, const char *zName, int nOut, char *zOut); + void *(*xDlOpen)(sqlite3_vfs*, const char *zFilename); + void (*xDlError)(sqlite3_vfs*, int nByte, char *zErrMsg); + void (*(*xDlSym)(sqlite3_vfs*,void*, const char *zSymbol))(void); + void (*xDlClose)(sqlite3_vfs*, void*); + int (*xRandomness)(sqlite3_vfs*, int nByte, char *zOut); + int (*xSleep)(sqlite3_vfs*, int microseconds); + int (*xCurrentTime)(sqlite3_vfs*, double*); + int (*xGetLastError)(sqlite3_vfs*, int, char *); + /* New fields may be appended in figure versions. The iVersion + ** value will increment whenever this happens. */ +}; + +/* +** CAPI3REF: Flags for the xAccess VFS method {H11190} +** +** These integer constants can be used as the third parameter to +** the xAccess method of an [sqlite3_vfs] object. {END} They determine +** what kind of permissions the xAccess method is looking for. +** With SQLITE_ACCESS_EXISTS, the xAccess method +** simply checks whether the file exists. +** With SQLITE_ACCESS_READWRITE, the xAccess method +** checks whether the file is both readable and writable. +** With SQLITE_ACCESS_READ, the xAccess method +** checks whether the file is readable. +*/ +#define SQLITE_ACCESS_EXISTS 0 +#define SQLITE_ACCESS_READWRITE 1 +#define SQLITE_ACCESS_READ 2 + +/* +** CAPI3REF: Initialize The SQLite Library {H10130} +** +** The sqlite3_initialize() routine initializes the +** SQLite library. The sqlite3_shutdown() routine +** deallocates any resources that were allocated by sqlite3_initialize(). +** +** A call to sqlite3_initialize() is an "effective" call if it is +** the first time sqlite3_initialize() is invoked during the lifetime of +** the process, or if it is the first time sqlite3_initialize() is invoked +** following a call to sqlite3_shutdown(). Only an effective call +** of sqlite3_initialize() does any initialization. All other calls +** are harmless no-ops. +** +** Among other things, sqlite3_initialize() shall invoke +** sqlite3_os_init(). Similarly, sqlite3_shutdown() +** shall invoke sqlite3_os_end(). +** +** The sqlite3_initialize() routine returns [SQLITE_OK] on success. +** If for some reason, sqlite3_initialize() is unable to initialize +** the library (perhaps it is unable to allocate a needed resource such +** as a mutex) it returns an [error code] other than [SQLITE_OK]. +** +** The sqlite3_initialize() routine is called internally by many other +** SQLite interfaces so that an application usually does not need to +** invoke sqlite3_initialize() directly. For example, [sqlite3_open()] +** calls sqlite3_initialize() so the SQLite library will be automatically +** initialized when [sqlite3_open()] is called if it has not be initialized +** already. However, if SQLite is compiled with the [SQLITE_OMIT_AUTOINIT] +** compile-time option, then the automatic calls to sqlite3_initialize() +** are omitted and the application must call sqlite3_initialize() directly +** prior to using any other SQLite interface. For maximum portability, +** it is recommended that applications always invoke sqlite3_initialize() +** directly prior to using any other SQLite interface. Future releases +** of SQLite may require this. In other words, the behavior exhibited +** when SQLite is compiled with [SQLITE_OMIT_AUTOINIT] might become the +** default behavior in some future release of SQLite. +** +** The sqlite3_os_init() routine does operating-system specific +** initialization of the SQLite library. The sqlite3_os_end() +** routine undoes the effect of sqlite3_os_init(). Typical tasks +** performed by these routines include allocation or deallocation +** of static resources, initialization of global variables, +** setting up a default [sqlite3_vfs] module, or setting up +** a default configuration using [sqlite3_config()]. +** +** The application should never invoke either sqlite3_os_init() +** or sqlite3_os_end() directly. The application should only invoke +** sqlite3_initialize() and sqlite3_shutdown(). The sqlite3_os_init() +** interface is called automatically by sqlite3_initialize() and +** sqlite3_os_end() is called by sqlite3_shutdown(). Appropriate +** implementations for sqlite3_os_init() and sqlite3_os_end() +** are built into SQLite when it is compiled for unix, windows, or os/2. +** When built for other platforms (using the [SQLITE_OS_OTHER=1] compile-time +** option) the application must supply a suitable implementation for +** sqlite3_os_init() and sqlite3_os_end(). An application-supplied +** implementation of sqlite3_os_init() or sqlite3_os_end() +** must return [SQLITE_OK] on success and some other [error code] upon +** failure. +*/ +SQLITE_API int sqlite3_initialize(void); +SQLITE_API int sqlite3_shutdown(void); +SQLITE_API int sqlite3_os_init(void); +SQLITE_API int sqlite3_os_end(void); + +/* +** CAPI3REF: Configuring The SQLite Library {H14100} +** EXPERIMENTAL +** +** The sqlite3_config() interface is used to make global configuration +** changes to SQLite in order to tune SQLite to the specific needs of +** the application. The default configuration is recommended for most +** applications and so this routine is usually not necessary. It is +** provided to support rare applications with unusual needs. +** +** The sqlite3_config() interface is not threadsafe. The application +** must insure that no other SQLite interfaces are invoked by other +** threads while sqlite3_config() is running. Furthermore, sqlite3_config() +** may only be invoked prior to library initialization using +** [sqlite3_initialize()] or after shutdown by [sqlite3_shutdown()]. +** Note, however, that sqlite3_config() can be called as part of the +** implementation of an application-defined [sqlite3_os_init()]. +** +** The first argument to sqlite3_config() is an integer +** [SQLITE_CONFIG_SINGLETHREAD | configuration option] that determines +** what property of SQLite is to be configured. Subsequent arguments +** vary depending on the [SQLITE_CONFIG_SINGLETHREAD | configuration option] +** in the first argument. +** +** When a configuration option is set, sqlite3_config() returns [SQLITE_OK]. +** If the option is unknown or SQLite is unable to set the option +** then this routine returns a non-zero [error code]. +** +** Requirements: +** [H14103] [H14106] [H14120] [H14123] [H14126] [H14129] [H14132] [H14135] +** [H14138] [H14141] [H14144] [H14147] [H14150] [H14153] [H14156] [H14159] +** [H14162] [H14165] [H14168] +*/ +SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_config(int, ...); + +/* +** CAPI3REF: Configure database connections {H14200} +** EXPERIMENTAL +** +** The sqlite3_db_config() interface is used to make configuration +** changes to a [database connection]. The interface is similar to +** [sqlite3_config()] except that the changes apply to a single +** [database connection] (specified in the first argument). The +** sqlite3_db_config() interface can only be used immediately after +** the database connection is created using [sqlite3_open()], +** [sqlite3_open16()], or [sqlite3_open_v2()]. +** +** The second argument to sqlite3_db_config(D,V,...) is the +** configuration verb - an integer code that indicates what +** aspect of the [database connection] is being configured. +** The only choice for this value is [SQLITE_DBCONFIG_LOOKASIDE]. +** New verbs are likely to be added in future releases of SQLite. +** Additional arguments depend on the verb. +** +** Requirements: +** [H14203] [H14206] [H14209] [H14212] [H14215] +*/ +SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_db_config(sqlite3*, int op, ...); + +/* +** CAPI3REF: Memory Allocation Routines {H10155} +** EXPERIMENTAL +** +** An instance of this object defines the interface between SQLite +** and low-level memory allocation routines. +** +** This object is used in only one place in the SQLite interface. +** A pointer to an instance of this object is the argument to +** [sqlite3_config()] when the configuration option is +** [SQLITE_CONFIG_MALLOC]. By creating an instance of this object +** and passing it to [sqlite3_config()] during configuration, an +** application can specify an alternative memory allocation subsystem +** for SQLite to use for all of its dynamic memory needs. +** +** Note that SQLite comes with a built-in memory allocator that is +** perfectly adequate for the overwhelming majority of applications +** and that this object is only useful to a tiny minority of applications +** with specialized memory allocation requirements. This object is +** also used during testing of SQLite in order to specify an alternative +** memory allocator that simulates memory out-of-memory conditions in +** order to verify that SQLite recovers gracefully from such +** conditions. +** +** The xMalloc, xFree, and xRealloc methods must work like the +** malloc(), free(), and realloc() functions from the standard library. +** +** xSize should return the allocated size of a memory allocation +** previously obtained from xMalloc or xRealloc. The allocated size +** is always at least as big as the requested size but may be larger. +** +** The xRoundup method returns what would be the allocated size of +** a memory allocation given a particular requested size. Most memory +** allocators round up memory allocations at least to the next multiple +** of 8. Some allocators round up to a larger multiple or to a power of 2. +** +** The xInit method initializes the memory allocator. (For example, +** it might allocate any require mutexes or initialize internal data +** structures. The xShutdown method is invoked (indirectly) by +** [sqlite3_shutdown()] and should deallocate any resources acquired +** by xInit. The pAppData pointer is used as the only parameter to +** xInit and xShutdown. +*/ +typedef struct sqlite3_mem_methods sqlite3_mem_methods; +struct sqlite3_mem_methods { + void *(*xMalloc)(int); /* Memory allocation function */ + void (*xFree)(void*); /* Free a prior allocation */ + void *(*xRealloc)(void*,int); /* Resize an allocation */ + int (*xSize)(void*); /* Return the size of an allocation */ + int (*xRoundup)(int); /* Round up request size to allocation size */ + int (*xInit)(void*); /* Initialize the memory allocator */ + void (*xShutdown)(void*); /* Deinitialize the memory allocator */ + void *pAppData; /* Argument to xInit() and xShutdown() */ +}; + +/* +** CAPI3REF: Configuration Options {H10160} +** EXPERIMENTAL +** +** These constants are the available integer configuration options that +** can be passed as the first argument to the [sqlite3_config()] interface. +** +** New configuration options may be added in future releases of SQLite. +** Existing configuration options might be discontinued. Applications +** should check the return code from [sqlite3_config()] to make sure that +** the call worked. The [sqlite3_config()] interface will return a +** non-zero [error code] if a discontinued or unsupported configuration option +** is invoked. +** +**
+**
SQLITE_CONFIG_SINGLETHREAD
+**
There are no arguments to this option. This option disables +** all mutexing and puts SQLite into a mode where it can only be used +** by a single thread.
+** +**
SQLITE_CONFIG_MULTITHREAD
+**
There are no arguments to this option. This option disables +** mutexing on [database connection] and [prepared statement] objects. +** The application is responsible for serializing access to +** [database connections] and [prepared statements]. But other mutexes +** are enabled so that SQLite will be safe to use in a multi-threaded +** environment as long as no two threads attempt to use the same +** [database connection] at the same time. See the [threading mode] +** documentation for additional information.
+** +**
SQLITE_CONFIG_SERIALIZED
+**
There are no arguments to this option. This option enables +** all mutexes including the recursive +** mutexes on [database connection] and [prepared statement] objects. +** In this mode (which is the default when SQLite is compiled with +** [SQLITE_THREADSAFE=1]) the SQLite library will itself serialize access +** to [database connections] and [prepared statements] so that the +** application is free to use the same [database connection] or the +** same [prepared statement] in different threads at the same time. +** See the [threading mode] documentation for additional information.
+** +**
SQLITE_CONFIG_MALLOC
+**
This option takes a single argument which is a pointer to an +** instance of the [sqlite3_mem_methods] structure. The argument specifies +** alternative low-level memory allocation routines to be used in place of +** the memory allocation routines built into SQLite.
+** +**
SQLITE_CONFIG_GETMALLOC
+**
This option takes a single argument which is a pointer to an +** instance of the [sqlite3_mem_methods] structure. The [sqlite3_mem_methods] +** structure is filled with the currently defined memory allocation routines. +** This option can be used to overload the default memory allocation +** routines with a wrapper that simulations memory allocation failure or +** tracks memory usage, for example.
+** +**
SQLITE_CONFIG_MEMSTATUS
+**
This option takes single argument of type int, interpreted as a +** boolean, which enables or disables the collection of memory allocation +** statistics. When disabled, the following SQLite interfaces become +** non-operational: +**
    +**
  • [sqlite3_memory_used()] +**
  • [sqlite3_memory_highwater()] +**
  • [sqlite3_soft_heap_limit()] +**
  • [sqlite3_status()] +**
+**
+** +**
SQLITE_CONFIG_SCRATCH
+**
This option specifies a static memory buffer that SQLite can use for +** scratch memory. There are three arguments: A pointer to the memory, the +** size of each scratch buffer (sz), and the number of buffers (N). The sz +** argument must be a multiple of 16. The sz parameter should be a few bytes +** larger than the actual scratch space required due internal overhead. +** The first +** argument should point to an allocation of at least sz*N bytes of memory. +** SQLite will use no more than one scratch buffer at once per thread, so +** N should be set to the expected maximum number of threads. The sz +** parameter should be 6 times the size of the largest database page size. +** Scratch buffers are used as part of the btree balance operation. If +** The btree balancer needs additional memory beyond what is provided by +** scratch buffers or if no scratch buffer space is specified, then SQLite +** goes to [sqlite3_malloc()] to obtain the memory it needs.
+** +**
SQLITE_CONFIG_PAGECACHE
+**
This option specifies a static memory buffer that SQLite can use for +** the database page cache with the default page cache implemenation. +** This configuration should not be used if an application-define page +** cache implementation is loaded using the SQLITE_CONFIG_PCACHE option. +** There are three arguments to this option: A pointer to the +** memory, the size of each page buffer (sz), and the number of pages (N). +** The sz argument must be a power of two between 512 and 32768. The first +** argument should point to an allocation of at least sz*N bytes of memory. +** SQLite will use the memory provided by the first argument to satisfy its +** memory needs for the first N pages that it adds to cache. If additional +** page cache memory is needed beyond what is provided by this option, then +** SQLite goes to [sqlite3_malloc()] for the additional storage space. +** The implementation might use one or more of the N buffers to hold +** memory accounting information.
+** +**
SQLITE_CONFIG_HEAP
+**
This option specifies a static memory buffer that SQLite will use +** for all of its dynamic memory allocation needs beyond those provided +** for by [SQLITE_CONFIG_SCRATCH] and [SQLITE_CONFIG_PAGECACHE]. +** There are three arguments: A pointer to the memory, the number of +** bytes in the memory buffer, and the minimum allocation size. If +** the first pointer (the memory pointer) is NULL, then SQLite reverts +** to using its default memory allocator (the system malloc() implementation), +** undoing any prior invocation of [SQLITE_CONFIG_MALLOC]. If the +** memory pointer is not NULL and either [SQLITE_ENABLE_MEMSYS3] or +** [SQLITE_ENABLE_MEMSYS5] are defined, then the alternative memory +** allocator is engaged to handle all of SQLites memory allocation needs.
+** +**
SQLITE_CONFIG_MUTEX
+**
This option takes a single argument which is a pointer to an +** instance of the [sqlite3_mutex_methods] structure. The argument specifies +** alternative low-level mutex routines to be used in place +** the mutex routines built into SQLite.
+** +**
SQLITE_CONFIG_GETMUTEX
+**
This option takes a single argument which is a pointer to an +** instance of the [sqlite3_mutex_methods] structure. The +** [sqlite3_mutex_methods] +** structure is filled with the currently defined mutex routines. +** This option can be used to overload the default mutex allocation +** routines with a wrapper used to track mutex usage for performance +** profiling or testing, for example.
+** +**
SQLITE_CONFIG_LOOKASIDE
+**
This option takes two arguments that determine the default +** memory allcation lookaside optimization. The first argument is the +** size of each lookaside buffer slot and the second is the number of +** slots allocated to each database connection.
+** +**
SQLITE_CONFIG_PCACHE
+**
This option takes a single argument which is a pointer to +** an [sqlite3_pcache_methods] object. This object specifies the interface +** to a custom page cache implementation. SQLite makes a copy of the +** object and uses it for page cache memory allocations.
+** +**
SQLITE_CONFIG_GETPCACHE
+**
This option takes a single argument which is a pointer to an +** [sqlite3_pcache_methods] object. SQLite copies of the current +** page cache implementation into that object.
+** +**
+*/ +#define SQLITE_CONFIG_SINGLETHREAD 1 /* nil */ +#define SQLITE_CONFIG_MULTITHREAD 2 /* nil */ +#define SQLITE_CONFIG_SERIALIZED 3 /* nil */ +#define SQLITE_CONFIG_MALLOC 4 /* sqlite3_mem_methods* */ +#define SQLITE_CONFIG_GETMALLOC 5 /* sqlite3_mem_methods* */ +#define SQLITE_CONFIG_SCRATCH 6 /* void*, int sz, int N */ +#define SQLITE_CONFIG_PAGECACHE 7 /* void*, int sz, int N */ +#define SQLITE_CONFIG_HEAP 8 /* void*, int nByte, int min */ +#define SQLITE_CONFIG_MEMSTATUS 9 /* boolean */ +#define SQLITE_CONFIG_MUTEX 10 /* sqlite3_mutex_methods* */ +#define SQLITE_CONFIG_GETMUTEX 11 /* sqlite3_mutex_methods* */ +/* previously SQLITE_CONFIG_CHUNKALLOC 12 which is now unused. */ +#define SQLITE_CONFIG_LOOKASIDE 13 /* int int */ +#define SQLITE_CONFIG_PCACHE 14 /* sqlite3_pcache_methods* */ +#define SQLITE_CONFIG_GETPCACHE 15 /* sqlite3_pcache_methods* */ + +/* +** CAPI3REF: Configuration Options {H10170} +** EXPERIMENTAL +** +** These constants are the available integer configuration options that +** can be passed as the second argument to the [sqlite3_db_config()] interface. +** +** New configuration options may be added in future releases of SQLite. +** Existing configuration options might be discontinued. Applications +** should check the return code from [sqlite3_db_config()] to make sure that +** the call worked. The [sqlite3_db_config()] interface will return a +** non-zero [error code] if a discontinued or unsupported configuration option +** is invoked. +** +**
+**
SQLITE_DBCONFIG_LOOKASIDE
+**
This option takes three additional arguments that determine the +** [lookaside memory allocator] configuration for the [database connection]. +** The first argument (the third parameter to [sqlite3_db_config()] is a +** pointer to a memory buffer to use for lookaside memory. The first +** argument may be NULL in which case SQLite will allocate the lookaside +** buffer itself using [sqlite3_malloc()]. The second argument is the +** size of each lookaside buffer slot and the third argument is the number of +** slots. The size of the buffer in the first argument must be greater than +** or equal to the product of the second and third arguments.
+** +**
+*/ +#define SQLITE_DBCONFIG_LOOKASIDE 1001 /* void* int int */ + + +/* +** CAPI3REF: Enable Or Disable Extended Result Codes {H12200} +** +** The sqlite3_extended_result_codes() routine enables or disables the +** [extended result codes] feature of SQLite. The extended result +** codes are disabled by default for historical compatibility considerations. +** +** Requirements: +** [H12201] [H12202] +*/ +SQLITE_API int sqlite3_extended_result_codes(sqlite3*, int onoff); + +/* +** CAPI3REF: Last Insert Rowid {H12220} +** +** Each entry in an SQLite table has a unique 64-bit signed +** integer key called the [ROWID | "rowid"]. The rowid is always available +** as an undeclared column named ROWID, OID, or _ROWID_ as long as those +** names are not also used by explicitly declared columns. If +** the table has a column of type [INTEGER PRIMARY KEY] then that column +** is another alias for the rowid. +** +** This routine returns the [rowid] of the most recent +** successful [INSERT] into the database from the [database connection] +** in the first argument. If no successful [INSERT]s +** have ever occurred on that database connection, zero is returned. +** +** If an [INSERT] occurs within a trigger, then the [rowid] of the inserted +** row is returned by this routine as long as the trigger is running. +** But once the trigger terminates, the value returned by this routine +** reverts to the last value inserted before the trigger fired. +** +** An [INSERT] that fails due to a constraint violation is not a +** successful [INSERT] and does not change the value returned by this +** routine. Thus INSERT OR FAIL, INSERT OR IGNORE, INSERT OR ROLLBACK, +** and INSERT OR ABORT make no changes to the return value of this +** routine when their insertion fails. When INSERT OR REPLACE +** encounters a constraint violation, it does not fail. The +** INSERT continues to completion after deleting rows that caused +** the constraint problem so INSERT OR REPLACE will always change +** the return value of this interface. +** +** For the purposes of this routine, an [INSERT] is considered to +** be successful even if it is subsequently rolled back. +** +** Requirements: +** [H12221] [H12223] +** +** If a separate thread performs a new [INSERT] on the same +** database connection while the [sqlite3_last_insert_rowid()] +** function is running and thus changes the last insert [rowid], +** then the value returned by [sqlite3_last_insert_rowid()] is +** unpredictable and might not equal either the old or the new +** last insert [rowid]. +*/ +SQLITE_API sqlite3_int64 sqlite3_last_insert_rowid(sqlite3*); + +/* +** CAPI3REF: Count The Number Of Rows Modified {H12240} +** +** This function returns the number of database rows that were changed +** or inserted or deleted by the most recently completed SQL statement +** on the [database connection] specified by the first parameter. +** Only changes that are directly specified by the [INSERT], [UPDATE], +** or [DELETE] statement are counted. Auxiliary changes caused by +** triggers are not counted. Use the [sqlite3_total_changes()] function +** to find the total number of changes including changes caused by triggers. +** +** A "row change" is a change to a single row of a single table +** caused by an INSERT, DELETE, or UPDATE statement. Rows that +** are changed as side effects of REPLACE constraint resolution, +** rollback, ABORT processing, DROP TABLE, or by any other +** mechanisms do not count as direct row changes. +** +** A "trigger context" is a scope of execution that begins and +** ends with the script of a trigger. Most SQL statements are +** evaluated outside of any trigger. This is the "top level" +** trigger context. If a trigger fires from the top level, a +** new trigger context is entered for the duration of that one +** trigger. Subtriggers create subcontexts for their duration. +** +** Calling [sqlite3_exec()] or [sqlite3_step()] recursively does +** not create a new trigger context. +** +** This function returns the number of direct row changes in the +** most recent INSERT, UPDATE, or DELETE statement within the same +** trigger context. +** +** Thus, when called from the top level, this function returns the +** number of changes in the most recent INSERT, UPDATE, or DELETE +** that also occurred at the top level. Within the body of a trigger, +** the sqlite3_changes() interface can be called to find the number of +** changes in the most recently completed INSERT, UPDATE, or DELETE +** statement within the body of the same trigger. +** However, the number returned does not include changes +** caused by subtriggers since those have their own context. +** +** SQLite implements the command "DELETE FROM table" without a WHERE clause +** by dropping and recreating the table. Doing so is much faster than going +** through and deleting individual elements from the table. Because of this +** optimization, the deletions in "DELETE FROM table" are not row changes and +** will not be counted by the sqlite3_changes() or [sqlite3_total_changes()] +** functions, regardless of the number of elements that were originally +** in the table. To get an accurate count of the number of rows deleted, use +** "DELETE FROM table WHERE 1" instead. Or recompile using the +** [SQLITE_OMIT_TRUNCATE_OPTIMIZATION] compile-time option to disable the +** optimization on all queries. +** +** Requirements: +** [H12241] [H12243] +** +** If a separate thread makes changes on the same database connection +** while [sqlite3_changes()] is running then the value returned +** is unpredictable and not meaningful. +*/ +SQLITE_API int sqlite3_changes(sqlite3*); + +/* +** CAPI3REF: Total Number Of Rows Modified {H12260} +** +** This function returns the number of row changes caused by INSERT, +** UPDATE or DELETE statements since the [database connection] was opened. +** The count includes all changes from all trigger contexts. However, +** the count does not include changes used to implement REPLACE constraints, +** do rollbacks or ABORT processing, or DROP table processing. +** The changes are counted as soon as the statement that makes them is +** completed (when the statement handle is passed to [sqlite3_reset()] or +** [sqlite3_finalize()]). +** +** SQLite implements the command "DELETE FROM table" without a WHERE clause +** by dropping and recreating the table. (This is much faster than going +** through and deleting individual elements from the table.) Because of this +** optimization, the deletions in "DELETE FROM table" are not row changes and +** will not be counted by the sqlite3_changes() or [sqlite3_total_changes()] +** functions, regardless of the number of elements that were originally +** in the table. To get an accurate count of the number of rows deleted, use +** "DELETE FROM table WHERE 1" instead. Or recompile using the +** [SQLITE_OMIT_TRUNCATE_OPTIMIZATION] compile-time option to disable the +** optimization on all queries. +** +** See also the [sqlite3_changes()] interface. +** +** Requirements: +** [H12261] [H12263] +** +** If a separate thread makes changes on the same database connection +** while [sqlite3_total_changes()] is running then the value +** returned is unpredictable and not meaningful. +*/ +SQLITE_API int sqlite3_total_changes(sqlite3*); + +/* +** CAPI3REF: Interrupt A Long-Running Query {H12270} +** +** This function causes any pending database operation to abort and +** return at its earliest opportunity. This routine is typically +** called in response to a user action such as pressing "Cancel" +** or Ctrl-C where the user wants a long query operation to halt +** immediately. +** +** It is safe to call this routine from a thread different from the +** thread that is currently running the database operation. But it +** is not safe to call this routine with a [database connection] that +** is closed or might close before sqlite3_interrupt() returns. +** +** If an SQL operation is very nearly finished at the time when +** sqlite3_interrupt() is called, then it might not have an opportunity +** to be interrupted and might continue to completion. +** +** An SQL operation that is interrupted will return [SQLITE_INTERRUPT]. +** If the interrupted SQL operation is an INSERT, UPDATE, or DELETE +** that is inside an explicit transaction, then the entire transaction +** will be rolled back automatically. +** +** A call to sqlite3_interrupt() has no effect on SQL statements +** that are started after sqlite3_interrupt() returns. +** +** Requirements: +** [H12271] [H12272] +** +** If the database connection closes while [sqlite3_interrupt()] +** is running then bad things will likely happen. +*/ +SQLITE_API void sqlite3_interrupt(sqlite3*); + +/* +** CAPI3REF: Determine If An SQL Statement Is Complete {H10510} +** +** These routines are useful for command-line input to determine if the +** currently entered text seems to form complete a SQL statement or +** if additional input is needed before sending the text into +** SQLite for parsing. These routines return true if the input string +** appears to be a complete SQL statement. A statement is judged to be +** complete if it ends with a semicolon token and is not a fragment of a +** CREATE TRIGGER statement. Semicolons that are embedded within +** string literals or quoted identifier names or comments are not +** independent tokens (they are part of the token in which they are +** embedded) and thus do not count as a statement terminator. +** +** These routines do not parse the SQL statements thus +** will not detect syntactically incorrect SQL. +** +** Requirements: [H10511] [H10512] +** +** The input to [sqlite3_complete()] must be a zero-terminated +** UTF-8 string. +** +** The input to [sqlite3_complete16()] must be a zero-terminated +** UTF-16 string in native byte order. +*/ +SQLITE_API int sqlite3_complete(const char *sql); +SQLITE_API int sqlite3_complete16(const void *sql); + +/* +** CAPI3REF: Register A Callback To Handle SQLITE_BUSY Errors {H12310} +** +** This routine sets a callback function that might be invoked whenever +** an attempt is made to open a database table that another thread +** or process has locked. +** +** If the busy callback is NULL, then [SQLITE_BUSY] or [SQLITE_IOERR_BLOCKED] +** is returned immediately upon encountering the lock. If the busy callback +** is not NULL, then the callback will be invoked with two arguments. +** +** The first argument to the handler is a copy of the void* pointer which +** is the third argument to sqlite3_busy_handler(). The second argument to +** the handler callback is the number of times that the busy handler has +** been invoked for this locking event. If the +** busy callback returns 0, then no additional attempts are made to +** access the database and [SQLITE_BUSY] or [SQLITE_IOERR_BLOCKED] is returned. +** If the callback returns non-zero, then another attempt +** is made to open the database for reading and the cycle repeats. +** +** The presence of a busy handler does not guarantee that it will be invoked +** when there is lock contention. If SQLite determines that invoking the busy +** handler could result in a deadlock, it will go ahead and return [SQLITE_BUSY] +** or [SQLITE_IOERR_BLOCKED] instead of invoking the busy handler. +** Consider a scenario where one process is holding a read lock that +** it is trying to promote to a reserved lock and +** a second process is holding a reserved lock that it is trying +** to promote to an exclusive lock. The first process cannot proceed +** because it is blocked by the second and the second process cannot +** proceed because it is blocked by the first. If both processes +** invoke the busy handlers, neither will make any progress. Therefore, +** SQLite returns [SQLITE_BUSY] for the first process, hoping that this +** will induce the first process to release its read lock and allow +** the second process to proceed. +** +** The default busy callback is NULL. +** +** The [SQLITE_BUSY] error is converted to [SQLITE_IOERR_BLOCKED] +** when SQLite is in the middle of a large transaction where all the +** changes will not fit into the in-memory cache. SQLite will +** already hold a RESERVED lock on the database file, but it needs +** to promote this lock to EXCLUSIVE so that it can spill cache +** pages into the database file without harm to concurrent +** readers. If it is unable to promote the lock, then the in-memory +** cache will be left in an inconsistent state and so the error +** code is promoted from the relatively benign [SQLITE_BUSY] to +** the more severe [SQLITE_IOERR_BLOCKED]. This error code promotion +** forces an automatic rollback of the changes. See the +** +** CorruptionFollowingBusyError wiki page for a discussion of why +** this is important. +** +** There can only be a single busy handler defined for each +** [database connection]. Setting a new busy handler clears any +** previously set handler. Note that calling [sqlite3_busy_timeout()] +** will also set or clear the busy handler. +** +** The busy callback should not take any actions which modify the +** database connection that invoked the busy handler. Any such actions +** result in undefined behavior. +** +** Requirements: +** [H12311] [H12312] [H12314] [H12316] [H12318] +** +** A busy handler must not close the database connection +** or [prepared statement] that invoked the busy handler. +*/ +SQLITE_API int sqlite3_busy_handler(sqlite3*, int(*)(void*,int), void*); + +/* +** CAPI3REF: Set A Busy Timeout {H12340} +** +** This routine sets a [sqlite3_busy_handler | busy handler] that sleeps +** for a specified amount of time when a table is locked. The handler +** will sleep multiple times until at least "ms" milliseconds of sleeping +** have accumulated. {H12343} After "ms" milliseconds of sleeping, +** the handler returns 0 which causes [sqlite3_step()] to return +** [SQLITE_BUSY] or [SQLITE_IOERR_BLOCKED]. +** +** Calling this routine with an argument less than or equal to zero +** turns off all busy handlers. +** +** There can only be a single busy handler for a particular +** [database connection] any any given moment. If another busy handler +** was defined (using [sqlite3_busy_handler()]) prior to calling +** this routine, that other busy handler is cleared. +** +** Requirements: +** [H12341] [H12343] [H12344] +*/ +SQLITE_API int sqlite3_busy_timeout(sqlite3*, int ms); + +/* +** CAPI3REF: Convenience Routines For Running Queries {H12370} +** +** Definition: A result table is memory data structure created by the +** [sqlite3_get_table()] interface. A result table records the +** complete query results from one or more queries. +** +** The table conceptually has a number of rows and columns. But +** these numbers are not part of the result table itself. These +** numbers are obtained separately. Let N be the number of rows +** and M be the number of columns. +** +** A result table is an array of pointers to zero-terminated UTF-8 strings. +** There are (N+1)*M elements in the array. The first M pointers point +** to zero-terminated strings that contain the names of the columns. +** The remaining entries all point to query results. NULL values result +** in NULL pointers. All other values are in their UTF-8 zero-terminated +** string representation as returned by [sqlite3_column_text()]. +** +** A result table might consist of one or more memory allocations. +** It is not safe to pass a result table directly to [sqlite3_free()]. +** A result table should be deallocated using [sqlite3_free_table()]. +** +** As an example of the result table format, suppose a query result +** is as follows: +** +**
+**        Name        | Age
+**        -----------------------
+**        Alice       | 43
+**        Bob         | 28
+**        Cindy       | 21
+** 
+** +** There are two column (M==2) and three rows (N==3). Thus the +** result table has 8 entries. Suppose the result table is stored +** in an array names azResult. Then azResult holds this content: +** +**
+**        azResult[0] = "Name";
+**        azResult[1] = "Age";
+**        azResult[2] = "Alice";
+**        azResult[3] = "43";
+**        azResult[4] = "Bob";
+**        azResult[5] = "28";
+**        azResult[6] = "Cindy";
+**        azResult[7] = "21";
+** 
+** +** The sqlite3_get_table() function evaluates one or more +** semicolon-separated SQL statements in the zero-terminated UTF-8 +** string of its 2nd parameter. It returns a result table to the +** pointer given in its 3rd parameter. +** +** After the calling function has finished using the result, it should +** pass the pointer to the result table to sqlite3_free_table() in order to +** release the memory that was malloced. Because of the way the +** [sqlite3_malloc()] happens within sqlite3_get_table(), the calling +** function must not try to call [sqlite3_free()] directly. Only +** [sqlite3_free_table()] is able to release the memory properly and safely. +** +** The sqlite3_get_table() interface is implemented as a wrapper around +** [sqlite3_exec()]. The sqlite3_get_table() routine does not have access +** to any internal data structures of SQLite. It uses only the public +** interface defined here. As a consequence, errors that occur in the +** wrapper layer outside of the internal [sqlite3_exec()] call are not +** reflected in subsequent calls to [sqlite3_errcode()] or [sqlite3_errmsg()]. +** +** Requirements: +** [H12371] [H12373] [H12374] [H12376] [H12379] [H12382] +*/ +SQLITE_API int sqlite3_get_table( + sqlite3 *db, /* An open database */ + const char *zSql, /* SQL to be evaluated */ + char ***pazResult, /* Results of the query */ + int *pnRow, /* Number of result rows written here */ + int *pnColumn, /* Number of result columns written here */ + char **pzErrmsg /* Error msg written here */ +); +SQLITE_API void sqlite3_free_table(char **result); + +/* +** CAPI3REF: Formatted String Printing Functions {H17400} +** +** These routines are workalikes of the "printf()" family of functions +** from the standard C library. +** +** The sqlite3_mprintf() and sqlite3_vmprintf() routines write their +** results into memory obtained from [sqlite3_malloc()]. +** The strings returned by these two routines should be +** released by [sqlite3_free()]. Both routines return a +** NULL pointer if [sqlite3_malloc()] is unable to allocate enough +** memory to hold the resulting string. +** +** In sqlite3_snprintf() routine is similar to "snprintf()" from +** the standard C library. The result is written into the +** buffer supplied as the second parameter whose size is given by +** the first parameter. Note that the order of the +** first two parameters is reversed from snprintf(). This is an +** historical accident that cannot be fixed without breaking +** backwards compatibility. Note also that sqlite3_snprintf() +** returns a pointer to its buffer instead of the number of +** characters actually written into the buffer. We admit that +** the number of characters written would be a more useful return +** value but we cannot change the implementation of sqlite3_snprintf() +** now without breaking compatibility. +** +** As long as the buffer size is greater than zero, sqlite3_snprintf() +** guarantees that the buffer is always zero-terminated. The first +** parameter "n" is the total size of the buffer, including space for +** the zero terminator. So the longest string that can be completely +** written will be n-1 characters. +** +** These routines all implement some additional formatting +** options that are useful for constructing SQL statements. +** All of the usual printf() formatting options apply. In addition, there +** is are "%q", "%Q", and "%z" options. +** +** The %q option works like %s in that it substitutes a null-terminated +** string from the argument list. But %q also doubles every '\'' character. +** %q is designed for use inside a string literal. By doubling each '\'' +** character it escapes that character and allows it to be inserted into +** the string. +** +** For example, assume the string variable zText contains text as follows: +** +**
+**  char *zText = "It's a happy day!";
+** 
+** +** One can use this text in an SQL statement as follows: +** +**
+**  char *zSQL = sqlite3_mprintf("INSERT INTO table VALUES('%q')", zText);
+**  sqlite3_exec(db, zSQL, 0, 0, 0);
+**  sqlite3_free(zSQL);
+** 
+** +** Because the %q format string is used, the '\'' character in zText +** is escaped and the SQL generated is as follows: +** +**
+**  INSERT INTO table1 VALUES('It''s a happy day!')
+** 
+** +** This is correct. Had we used %s instead of %q, the generated SQL +** would have looked like this: +** +**
+**  INSERT INTO table1 VALUES('It's a happy day!');
+** 
+** +** This second example is an SQL syntax error. As a general rule you should +** always use %q instead of %s when inserting text into a string literal. +** +** The %Q option works like %q except it also adds single quotes around +** the outside of the total string. Additionally, if the parameter in the +** argument list is a NULL pointer, %Q substitutes the text "NULL" (without +** single quotes) in place of the %Q option. So, for example, one could say: +** +**
+**  char *zSQL = sqlite3_mprintf("INSERT INTO table VALUES(%Q)", zText);
+**  sqlite3_exec(db, zSQL, 0, 0, 0);
+**  sqlite3_free(zSQL);
+** 
+** +** The code above will render a correct SQL statement in the zSQL +** variable even if the zText variable is a NULL pointer. +** +** The "%z" formatting option works exactly like "%s" with the +** addition that after the string has been read and copied into +** the result, [sqlite3_free()] is called on the input string. {END} +** +** Requirements: +** [H17403] [H17406] [H17407] +*/ +SQLITE_API char *sqlite3_mprintf(const char*,...); +SQLITE_API char *sqlite3_vmprintf(const char*, va_list); +SQLITE_API char *sqlite3_snprintf(int,char*,const char*, ...); + +/* +** CAPI3REF: Memory Allocation Subsystem {H17300} +** +** The SQLite core uses these three routines for all of its own +** internal memory allocation needs. "Core" in the previous sentence +** does not include operating-system specific VFS implementation. The +** Windows VFS uses native malloc() and free() for some operations. +** +** The sqlite3_malloc() routine returns a pointer to a block +** of memory at least N bytes in length, where N is the parameter. +** If sqlite3_malloc() is unable to obtain sufficient free +** memory, it returns a NULL pointer. If the parameter N to +** sqlite3_malloc() is zero or negative then sqlite3_malloc() returns +** a NULL pointer. +** +** Calling sqlite3_free() with a pointer previously returned +** by sqlite3_malloc() or sqlite3_realloc() releases that memory so +** that it might be reused. The sqlite3_free() routine is +** a no-op if is called with a NULL pointer. Passing a NULL pointer +** to sqlite3_free() is harmless. After being freed, memory +** should neither be read nor written. Even reading previously freed +** memory might result in a segmentation fault or other severe error. +** Memory corruption, a segmentation fault, or other severe error +** might result if sqlite3_free() is called with a non-NULL pointer that +** was not obtained from sqlite3_malloc() or sqlite3_realloc(). +** +** The sqlite3_realloc() interface attempts to resize a +** prior memory allocation to be at least N bytes, where N is the +** second parameter. The memory allocation to be resized is the first +** parameter. If the first parameter to sqlite3_realloc() +** is a NULL pointer then its behavior is identical to calling +** sqlite3_malloc(N) where N is the second parameter to sqlite3_realloc(). +** If the second parameter to sqlite3_realloc() is zero or +** negative then the behavior is exactly the same as calling +** sqlite3_free(P) where P is the first parameter to sqlite3_realloc(). +** sqlite3_realloc() returns a pointer to a memory allocation +** of at least N bytes in size or NULL if sufficient memory is unavailable. +** If M is the size of the prior allocation, then min(N,M) bytes +** of the prior allocation are copied into the beginning of buffer returned +** by sqlite3_realloc() and the prior allocation is freed. +** If sqlite3_realloc() returns NULL, then the prior allocation +** is not freed. +** +** The memory returned by sqlite3_malloc() and sqlite3_realloc() +** is always aligned to at least an 8 byte boundary. {END} +** +** The default implementation of the memory allocation subsystem uses +** the malloc(), realloc() and free() provided by the standard C library. +** {H17382} However, if SQLite is compiled with the +** SQLITE_MEMORY_SIZE=NNN C preprocessor macro (where NNN +** is an integer), then SQLite create a static array of at least +** NNN bytes in size and uses that array for all of its dynamic +** memory allocation needs. {END} Additional memory allocator options +** may be added in future releases. +** +** In SQLite version 3.5.0 and 3.5.1, it was possible to define +** the SQLITE_OMIT_MEMORY_ALLOCATION which would cause the built-in +** implementation of these routines to be omitted. That capability +** is no longer provided. Only built-in memory allocators can be used. +** +** The Windows OS interface layer calls +** the system malloc() and free() directly when converting +** filenames between the UTF-8 encoding used by SQLite +** and whatever filename encoding is used by the particular Windows +** installation. Memory allocation errors are detected, but +** they are reported back as [SQLITE_CANTOPEN] or +** [SQLITE_IOERR] rather than [SQLITE_NOMEM]. +** +** Requirements: +** [H17303] [H17304] [H17305] [H17306] [H17310] [H17312] [H17315] [H17318] +** [H17321] [H17322] [H17323] +** +** The pointer arguments to [sqlite3_free()] and [sqlite3_realloc()] +** must be either NULL or else pointers obtained from a prior +** invocation of [sqlite3_malloc()] or [sqlite3_realloc()] that have +** not yet been released. +** +** The application must not read or write any part of +** a block of memory after it has been released using +** [sqlite3_free()] or [sqlite3_realloc()]. +*/ +SQLITE_API void *sqlite3_malloc(int); +SQLITE_API void *sqlite3_realloc(void*, int); +SQLITE_API void sqlite3_free(void*); + +/* +** CAPI3REF: Memory Allocator Statistics {H17370} +** +** SQLite provides these two interfaces for reporting on the status +** of the [sqlite3_malloc()], [sqlite3_free()], and [sqlite3_realloc()] +** routines, which form the built-in memory allocation subsystem. +** +** Requirements: +** [H17371] [H17373] [H17374] [H17375] +*/ +SQLITE_API sqlite3_int64 sqlite3_memory_used(void); +SQLITE_API sqlite3_int64 sqlite3_memory_highwater(int resetFlag); + +/* +** CAPI3REF: Pseudo-Random Number Generator {H17390} +** +** SQLite contains a high-quality pseudo-random number generator (PRNG) used to +** select random [ROWID | ROWIDs] when inserting new records into a table that +** already uses the largest possible [ROWID]. The PRNG is also used for +** the build-in random() and randomblob() SQL functions. This interface allows +** applications to access the same PRNG for other purposes. +** +** A call to this routine stores N bytes of randomness into buffer P. +** +** The first time this routine is invoked (either internally or by +** the application) the PRNG is seeded using randomness obtained +** from the xRandomness method of the default [sqlite3_vfs] object. +** On all subsequent invocations, the pseudo-randomness is generated +** internally and without recourse to the [sqlite3_vfs] xRandomness +** method. +** +** Requirements: +** [H17392] +*/ +SQLITE_API void sqlite3_randomness(int N, void *P); + +/* +** CAPI3REF: Compile-Time Authorization Callbacks {H12500} +** +** This routine registers a authorizer callback with a particular +** [database connection], supplied in the first argument. +** The authorizer callback is invoked as SQL statements are being compiled +** by [sqlite3_prepare()] or its variants [sqlite3_prepare_v2()], +** [sqlite3_prepare16()] and [sqlite3_prepare16_v2()]. At various +** points during the compilation process, as logic is being created +** to perform various actions, the authorizer callback is invoked to +** see if those actions are allowed. The authorizer callback should +** return [SQLITE_OK] to allow the action, [SQLITE_IGNORE] to disallow the +** specific action but allow the SQL statement to continue to be +** compiled, or [SQLITE_DENY] to cause the entire SQL statement to be +** rejected with an error. If the authorizer callback returns +** any value other than [SQLITE_IGNORE], [SQLITE_OK], or [SQLITE_DENY] +** then the [sqlite3_prepare_v2()] or equivalent call that triggered +** the authorizer will fail with an error message. +** +** When the callback returns [SQLITE_OK], that means the operation +** requested is ok. When the callback returns [SQLITE_DENY], the +** [sqlite3_prepare_v2()] or equivalent call that triggered the +** authorizer will fail with an error message explaining that +** access is denied. If the authorizer code is [SQLITE_READ] +** and the callback returns [SQLITE_IGNORE] then the +** [prepared statement] statement is constructed to substitute +** a NULL value in place of the table column that would have +** been read if [SQLITE_OK] had been returned. The [SQLITE_IGNORE] +** return can be used to deny an untrusted user access to individual +** columns of a table. +** +** The first parameter to the authorizer callback is a copy of the third +** parameter to the sqlite3_set_authorizer() interface. The second parameter +** to the callback is an integer [SQLITE_COPY | action code] that specifies +** the particular action to be authorized. The third through sixth parameters +** to the callback are zero-terminated strings that contain additional +** details about the action to be authorized. +** +** An authorizer is used when [sqlite3_prepare | preparing] +** SQL statements from an untrusted source, to ensure that the SQL statements +** do not try to access data they are not allowed to see, or that they do not +** try to execute malicious statements that damage the database. For +** example, an application may allow a user to enter arbitrary +** SQL queries for evaluation by a database. But the application does +** not want the user to be able to make arbitrary changes to the +** database. An authorizer could then be put in place while the +** user-entered SQL is being [sqlite3_prepare | prepared] that +** disallows everything except [SELECT] statements. +** +** Applications that need to process SQL from untrusted sources +** might also consider lowering resource limits using [sqlite3_limit()] +** and limiting database size using the [max_page_count] [PRAGMA] +** in addition to using an authorizer. +** +** Only a single authorizer can be in place on a database connection +** at a time. Each call to sqlite3_set_authorizer overrides the +** previous call. Disable the authorizer by installing a NULL callback. +** The authorizer is disabled by default. +** +** The authorizer callback must not do anything that will modify +** the database connection that invoked the authorizer callback. +** Note that [sqlite3_prepare_v2()] and [sqlite3_step()] both modify their +** database connections for the meaning of "modify" in this paragraph. +** +** When [sqlite3_prepare_v2()] is used to prepare a statement, the +** statement might be reprepared during [sqlite3_step()] due to a +** schema change. Hence, the application should ensure that the +** correct authorizer callback remains in place during the [sqlite3_step()]. +** +** Note that the authorizer callback is invoked only during +** [sqlite3_prepare()] or its variants. Authorization is not +** performed during statement evaluation in [sqlite3_step()]. +** +** Requirements: +** [H12501] [H12502] [H12503] [H12504] [H12505] [H12506] [H12507] [H12510] +** [H12511] [H12512] [H12520] [H12521] [H12522] +*/ +SQLITE_API int sqlite3_set_authorizer( + sqlite3*, + int (*xAuth)(void*,int,const char*,const char*,const char*,const char*), + void *pUserData +); + +/* +** CAPI3REF: Authorizer Return Codes {H12590} +** +** The [sqlite3_set_authorizer | authorizer callback function] must +** return either [SQLITE_OK] or one of these two constants in order +** to signal SQLite whether or not the action is permitted. See the +** [sqlite3_set_authorizer | authorizer documentation] for additional +** information. +*/ +#define SQLITE_DENY 1 /* Abort the SQL statement with an error */ +#define SQLITE_IGNORE 2 /* Don't allow access, but don't generate an error */ + +/* +** CAPI3REF: Authorizer Action Codes {H12550} +** +** The [sqlite3_set_authorizer()] interface registers a callback function +** that is invoked to authorize certain SQL statement actions. The +** second parameter to the callback is an integer code that specifies +** what action is being authorized. These are the integer action codes that +** the authorizer callback may be passed. +** +** These action code values signify what kind of operation is to be +** authorized. The 3rd and 4th parameters to the authorization +** callback function will be parameters or NULL depending on which of these +** codes is used as the second parameter. The 5th parameter to the +** authorizer callback is the name of the database ("main", "temp", +** etc.) if applicable. The 6th parameter to the authorizer callback +** is the name of the inner-most trigger or view that is responsible for +** the access attempt or NULL if this access attempt is directly from +** top-level SQL code. +** +** Requirements: +** [H12551] [H12552] [H12553] [H12554] +*/ +/******************************************* 3rd ************ 4th ***********/ +#define SQLITE_CREATE_INDEX 1 /* Index Name Table Name */ +#define SQLITE_CREATE_TABLE 2 /* Table Name NULL */ +#define SQLITE_CREATE_TEMP_INDEX 3 /* Index Name Table Name */ +#define SQLITE_CREATE_TEMP_TABLE 4 /* Table Name NULL */ +#define SQLITE_CREATE_TEMP_TRIGGER 5 /* Trigger Name Table Name */ +#define SQLITE_CREATE_TEMP_VIEW 6 /* View Name NULL */ +#define SQLITE_CREATE_TRIGGER 7 /* Trigger Name Table Name */ +#define SQLITE_CREATE_VIEW 8 /* View Name NULL */ +#define SQLITE_DELETE 9 /* Table Name NULL */ +#define SQLITE_DROP_INDEX 10 /* Index Name Table Name */ +#define SQLITE_DROP_TABLE 11 /* Table Name NULL */ +#define SQLITE_DROP_TEMP_INDEX 12 /* Index Name Table Name */ +#define SQLITE_DROP_TEMP_TABLE 13 /* Table Name NULL */ +#define SQLITE_DROP_TEMP_TRIGGER 14 /* Trigger Name Table Name */ +#define SQLITE_DROP_TEMP_VIEW 15 /* View Name NULL */ +#define SQLITE_DROP_TRIGGER 16 /* Trigger Name Table Name */ +#define SQLITE_DROP_VIEW 17 /* View Name NULL */ +#define SQLITE_INSERT 18 /* Table Name NULL */ +#define SQLITE_PRAGMA 19 /* Pragma Name 1st arg or NULL */ +#define SQLITE_READ 20 /* Table Name Column Name */ +#define SQLITE_SELECT 21 /* NULL NULL */ +#define SQLITE_TRANSACTION 22 /* Operation NULL */ +#define SQLITE_UPDATE 23 /* Table Name Column Name */ +#define SQLITE_ATTACH 24 /* Filename NULL */ +#define SQLITE_DETACH 25 /* Database Name NULL */ +#define SQLITE_ALTER_TABLE 26 /* Database Name Table Name */ +#define SQLITE_REINDEX 27 /* Index Name NULL */ +#define SQLITE_ANALYZE 28 /* Table Name NULL */ +#define SQLITE_CREATE_VTABLE 29 /* Table Name Module Name */ +#define SQLITE_DROP_VTABLE 30 /* Table Name Module Name */ +#define SQLITE_FUNCTION 31 /* NULL Function Name */ +#define SQLITE_SAVEPOINT 32 /* Operation Savepoint Name */ +#define SQLITE_COPY 0 /* No longer used */ + +/* +** CAPI3REF: Tracing And Profiling Functions {H12280} +** EXPERIMENTAL +** +** These routines register callback functions that can be used for +** tracing and profiling the execution of SQL statements. +** +** The callback function registered by sqlite3_trace() is invoked at +** various times when an SQL statement is being run by [sqlite3_step()]. +** The callback returns a UTF-8 rendering of the SQL statement text +** as the statement first begins executing. Additional callbacks occur +** as each triggered subprogram is entered. The callbacks for triggers +** contain a UTF-8 SQL comment that identifies the trigger. +** +** The callback function registered by sqlite3_profile() is invoked +** as each SQL statement finishes. The profile callback contains +** the original statement text and an estimate of wall-clock time +** of how long that statement took to run. +** +** Requirements: +** [H12281] [H12282] [H12283] [H12284] [H12285] [H12287] [H12288] [H12289] +** [H12290] +*/ +SQLITE_API SQLITE_EXPERIMENTAL void *sqlite3_trace(sqlite3*, void(*xTrace)(void*,const char*), void*); +SQLITE_API SQLITE_EXPERIMENTAL void *sqlite3_profile(sqlite3*, + void(*xProfile)(void*,const char*,sqlite3_uint64), void*); + +/* +** CAPI3REF: Query Progress Callbacks {H12910} +** +** This routine configures a callback function - the +** progress callback - that is invoked periodically during long +** running calls to [sqlite3_exec()], [sqlite3_step()] and +** [sqlite3_get_table()]. An example use for this +** interface is to keep a GUI updated during a large query. +** +** If the progress callback returns non-zero, the operation is +** interrupted. This feature can be used to implement a +** "Cancel" button on a GUI progress dialog box. +** +** The progress handler must not do anything that will modify +** the database connection that invoked the progress handler. +** Note that [sqlite3_prepare_v2()] and [sqlite3_step()] both modify their +** database connections for the meaning of "modify" in this paragraph. +** +** Requirements: +** [H12911] [H12912] [H12913] [H12914] [H12915] [H12916] [H12917] [H12918] +** +*/ +SQLITE_API void sqlite3_progress_handler(sqlite3*, int, int(*)(void*), void*); + +/* +** CAPI3REF: Opening A New Database Connection {H12700} +** +** These routines open an SQLite database file whose name is given by the +** filename argument. The filename argument is interpreted as UTF-8 for +** sqlite3_open() and sqlite3_open_v2() and as UTF-16 in the native byte +** order for sqlite3_open16(). A [database connection] handle is usually +** returned in *ppDb, even if an error occurs. The only exception is that +** if SQLite is unable to allocate memory to hold the [sqlite3] object, +** a NULL will be written into *ppDb instead of a pointer to the [sqlite3] +** object. If the database is opened (and/or created) successfully, then +** [SQLITE_OK] is returned. Otherwise an [error code] is returned. The +** [sqlite3_errmsg()] or [sqlite3_errmsg16()] routines can be used to obtain +** an English language description of the error. +** +** The default encoding for the database will be UTF-8 if +** sqlite3_open() or sqlite3_open_v2() is called and +** UTF-16 in the native byte order if sqlite3_open16() is used. +** +** Whether or not an error occurs when it is opened, resources +** associated with the [database connection] handle should be released by +** passing it to [sqlite3_close()] when it is no longer required. +** +** The sqlite3_open_v2() interface works like sqlite3_open() +** except that it accepts two additional parameters for additional control +** over the new database connection. The flags parameter can take one of +** the following three values, optionally combined with the +** [SQLITE_OPEN_NOMUTEX] or [SQLITE_OPEN_FULLMUTEX] flags: +** +**
+**
[SQLITE_OPEN_READONLY]
+**
The database is opened in read-only mode. If the database does not +** already exist, an error is returned.
+** +**
[SQLITE_OPEN_READWRITE]
+**
The database is opened for reading and writing if possible, or reading +** only if the file is write protected by the operating system. In either +** case the database must already exist, otherwise an error is returned.
+** +**
[SQLITE_OPEN_READWRITE] | [SQLITE_OPEN_CREATE]
+**
The database is opened for reading and writing, and is creates it if +** it does not already exist. This is the behavior that is always used for +** sqlite3_open() and sqlite3_open16().
+**
+** +** If the 3rd parameter to sqlite3_open_v2() is not one of the +** combinations shown above or one of the combinations shown above combined +** with the [SQLITE_OPEN_NOMUTEX] or [SQLITE_OPEN_FULLMUTEX] flags, +** then the behavior is undefined. +** +** If the [SQLITE_OPEN_NOMUTEX] flag is set, then the database connection +** opens in the multi-thread [threading mode] as long as the single-thread +** mode has not been set at compile-time or start-time. If the +** [SQLITE_OPEN_FULLMUTEX] flag is set then the database connection opens +** in the serialized [threading mode] unless single-thread was +** previously selected at compile-time or start-time. +** +** If the filename is ":memory:", then a private, temporary in-memory database +** is created for the connection. This in-memory database will vanish when +** the database connection is closed. Future versions of SQLite might +** make use of additional special filenames that begin with the ":" character. +** It is recommended that when a database filename actually does begin with +** a ":" character you should prefix the filename with a pathname such as +** "./" to avoid ambiguity. +** +** If the filename is an empty string, then a private, temporary +** on-disk database will be created. This private database will be +** automatically deleted as soon as the database connection is closed. +** +** The fourth parameter to sqlite3_open_v2() is the name of the +** [sqlite3_vfs] object that defines the operating system interface that +** the new database connection should use. If the fourth parameter is +** a NULL pointer then the default [sqlite3_vfs] object is used. +** +** Note to Windows users: The encoding used for the filename argument +** of sqlite3_open() and sqlite3_open_v2() must be UTF-8, not whatever +** codepage is currently defined. Filenames containing international +** characters must be converted to UTF-8 prior to passing them into +** sqlite3_open() or sqlite3_open_v2(). +** +** Requirements: +** [H12701] [H12702] [H12703] [H12704] [H12706] [H12707] [H12709] [H12711] +** [H12712] [H12713] [H12714] [H12717] [H12719] [H12721] [H12723] +*/ +SQLITE_API int sqlite3_open( + const char *filename, /* Database filename (UTF-8) */ + sqlite3 **ppDb /* OUT: SQLite db handle */ +); +SQLITE_API int sqlite3_open16( + const void *filename, /* Database filename (UTF-16) */ + sqlite3 **ppDb /* OUT: SQLite db handle */ +); +SQLITE_API int sqlite3_open_v2( + const char *filename, /* Database filename (UTF-8) */ + sqlite3 **ppDb, /* OUT: SQLite db handle */ + int flags, /* Flags */ + const char *zVfs /* Name of VFS module to use */ +); + +/* +** CAPI3REF: Error Codes And Messages {H12800} +** +** The sqlite3_errcode() interface returns the numeric [result code] or +** [extended result code] for the most recent failed sqlite3_* API call +** associated with a [database connection]. If a prior API call failed +** but the most recent API call succeeded, the return value from +** sqlite3_errcode() is undefined. The sqlite3_extended_errcode() +** interface is the same except that it always returns the +** [extended result code] even when extended result codes are +** disabled. +** +** The sqlite3_errmsg() and sqlite3_errmsg16() return English-language +** text that describes the error, as either UTF-8 or UTF-16 respectively. +** Memory to hold the error message string is managed internally. +** The application does not need to worry about freeing the result. +** However, the error string might be overwritten or deallocated by +** subsequent calls to other SQLite interface functions. +** +** When the serialized [threading mode] is in use, it might be the +** case that a second error occurs on a separate thread in between +** the time of the first error and the call to these interfaces. +** When that happens, the second error will be reported since these +** interfaces always report the most recent result. To avoid +** this, each thread can obtain exclusive use of the [database connection] D +** by invoking [sqlite3_mutex_enter]([sqlite3_db_mutex](D)) before beginning +** to use D and invoking [sqlite3_mutex_leave]([sqlite3_db_mutex](D)) after +** all calls to the interfaces listed here are completed. +** +** If an interface fails with SQLITE_MISUSE, that means the interface +** was invoked incorrectly by the application. In that case, the +** error code and message may or may not be set. +** +** Requirements: +** [H12801] [H12802] [H12803] [H12807] [H12808] [H12809] +*/ +SQLITE_API int sqlite3_errcode(sqlite3 *db); +SQLITE_API int sqlite3_extended_errcode(sqlite3 *db); +SQLITE_API const char *sqlite3_errmsg(sqlite3*); +SQLITE_API const void *sqlite3_errmsg16(sqlite3*); + +/* +** CAPI3REF: SQL Statement Object {H13000} +** KEYWORDS: {prepared statement} {prepared statements} +** +** An instance of this object represents a single SQL statement. +** This object is variously known as a "prepared statement" or a +** "compiled SQL statement" or simply as a "statement". +** +** The life of a statement object goes something like this: +** +**
    +**
  1. Create the object using [sqlite3_prepare_v2()] or a related +** function. +**
  2. Bind values to [host parameters] using the sqlite3_bind_*() +** interfaces. +**
  3. Run the SQL by calling [sqlite3_step()] one or more times. +**
  4. Reset the statement using [sqlite3_reset()] then go back +** to step 2. Do this zero or more times. +**
  5. Destroy the object using [sqlite3_finalize()]. +**
+** +** Refer to documentation on individual methods above for additional +** information. +*/ +typedef struct sqlite3_stmt sqlite3_stmt; + +/* +** CAPI3REF: Run-time Limits {H12760} +** +** This interface allows the size of various constructs to be limited +** on a connection by connection basis. The first parameter is the +** [database connection] whose limit is to be set or queried. The +** second parameter is one of the [limit categories] that define a +** class of constructs to be size limited. The third parameter is the +** new limit for that construct. The function returns the old limit. +** +** If the new limit is a negative number, the limit is unchanged. +** For the limit category of SQLITE_LIMIT_XYZ there is a +** [limits | hard upper bound] +** set by a compile-time C preprocessor macro named +** [limits | SQLITE_MAX_XYZ]. +** (The "_LIMIT_" in the name is changed to "_MAX_".) +** Attempts to increase a limit above its hard upper bound are +** silently truncated to the hard upper limit. +** +** Run time limits are intended for use in applications that manage +** both their own internal database and also databases that are controlled +** by untrusted external sources. An example application might be a +** web browser that has its own databases for storing history and +** separate databases controlled by JavaScript applications downloaded +** off the Internet. The internal databases can be given the +** large, default limits. Databases managed by external sources can +** be given much smaller limits designed to prevent a denial of service +** attack. Developers might also want to use the [sqlite3_set_authorizer()] +** interface to further control untrusted SQL. The size of the database +** created by an untrusted script can be contained using the +** [max_page_count] [PRAGMA]. +** +** New run-time limit categories may be added in future releases. +** +** Requirements: +** [H12762] [H12766] [H12769] +*/ +SQLITE_API int sqlite3_limit(sqlite3*, int id, int newVal); + +/* +** CAPI3REF: Run-Time Limit Categories {H12790} +** KEYWORDS: {limit category} {limit categories} +** +** These constants define various performance limits +** that can be lowered at run-time using [sqlite3_limit()]. +** The synopsis of the meanings of the various limits is shown below. +** Additional information is available at [limits | Limits in SQLite]. +** +**
+**
SQLITE_LIMIT_LENGTH
+**
The maximum size of any string or BLOB or table row.
+** +**
SQLITE_LIMIT_SQL_LENGTH
+**
The maximum length of an SQL statement.
+** +**
SQLITE_LIMIT_COLUMN
+**
The maximum number of columns in a table definition or in the +** result set of a [SELECT] or the maximum number of columns in an index +** or in an ORDER BY or GROUP BY clause.
+** +**
SQLITE_LIMIT_EXPR_DEPTH
+**
The maximum depth of the parse tree on any expression.
+** +**
SQLITE_LIMIT_COMPOUND_SELECT
+**
The maximum number of terms in a compound SELECT statement.
+** +**
SQLITE_LIMIT_VDBE_OP
+**
The maximum number of instructions in a virtual machine program +** used to implement an SQL statement.
+** +**
SQLITE_LIMIT_FUNCTION_ARG
+**
The maximum number of arguments on a function.
+** +**
SQLITE_LIMIT_ATTACHED
+**
The maximum number of [ATTACH | attached databases].
+** +**
SQLITE_LIMIT_LIKE_PATTERN_LENGTH
+**
The maximum length of the pattern argument to the [LIKE] or +** [GLOB] operators.
+** +**
SQLITE_LIMIT_VARIABLE_NUMBER
+**
The maximum number of variables in an SQL statement that can +** be bound.
+**
+*/ +#define SQLITE_LIMIT_LENGTH 0 +#define SQLITE_LIMIT_SQL_LENGTH 1 +#define SQLITE_LIMIT_COLUMN 2 +#define SQLITE_LIMIT_EXPR_DEPTH 3 +#define SQLITE_LIMIT_COMPOUND_SELECT 4 +#define SQLITE_LIMIT_VDBE_OP 5 +#define SQLITE_LIMIT_FUNCTION_ARG 6 +#define SQLITE_LIMIT_ATTACHED 7 +#define SQLITE_LIMIT_LIKE_PATTERN_LENGTH 8 +#define SQLITE_LIMIT_VARIABLE_NUMBER 9 + +/* +** CAPI3REF: Compiling An SQL Statement {H13010} +** KEYWORDS: {SQL statement compiler} +** +** To execute an SQL query, it must first be compiled into a byte-code +** program using one of these routines. +** +** The first argument, "db", is a [database connection] obtained from a +** prior call to [sqlite3_open()], [sqlite3_open_v2()] or [sqlite3_open16()]. +** +** The second argument, "zSql", is the statement to be compiled, encoded +** as either UTF-8 or UTF-16. The sqlite3_prepare() and sqlite3_prepare_v2() +** interfaces use UTF-8, and sqlite3_prepare16() and sqlite3_prepare16_v2() +** use UTF-16. +** +** If the nByte argument is less than zero, then zSql is read up to the +** first zero terminator. If nByte is non-negative, then it is the maximum +** number of bytes read from zSql. When nByte is non-negative, the +** zSql string ends at either the first '\000' or '\u0000' character or +** the nByte-th byte, whichever comes first. If the caller knows +** that the supplied string is nul-terminated, then there is a small +** performance advantage to be gained by passing an nByte parameter that +** is equal to the number of bytes in the input string including +** the nul-terminator bytes. +** +** *pzTail is made to point to the first byte past the end of the +** first SQL statement in zSql. These routines only compile the first +** statement in zSql, so *pzTail is left pointing to what remains +** uncompiled. +** +** *ppStmt is left pointing to a compiled [prepared statement] that can be +** executed using [sqlite3_step()]. If there is an error, *ppStmt is set +** to NULL. If the input text contains no SQL (if the input is an empty +** string or a comment) then *ppStmt is set to NULL. +** {A13018} The calling procedure is responsible for deleting the compiled +** SQL statement using [sqlite3_finalize()] after it has finished with it. +** +** On success, [SQLITE_OK] is returned, otherwise an [error code] is returned. +** +** The sqlite3_prepare_v2() and sqlite3_prepare16_v2() interfaces are +** recommended for all new programs. The two older interfaces are retained +** for backwards compatibility, but their use is discouraged. +** In the "v2" interfaces, the prepared statement +** that is returned (the [sqlite3_stmt] object) contains a copy of the +** original SQL text. This causes the [sqlite3_step()] interface to +** behave a differently in two ways: +** +**
    +**
  1. +** If the database schema changes, instead of returning [SQLITE_SCHEMA] as it +** always used to do, [sqlite3_step()] will automatically recompile the SQL +** statement and try to run it again. If the schema has changed in +** a way that makes the statement no longer valid, [sqlite3_step()] will still +** return [SQLITE_SCHEMA]. But unlike the legacy behavior, [SQLITE_SCHEMA] is +** now a fatal error. Calling [sqlite3_prepare_v2()] again will not make the +** error go away. Note: use [sqlite3_errmsg()] to find the text +** of the parsing error that results in an [SQLITE_SCHEMA] return. +**
  2. +** +**
  3. +** When an error occurs, [sqlite3_step()] will return one of the detailed +** [error codes] or [extended error codes]. The legacy behavior was that +** [sqlite3_step()] would only return a generic [SQLITE_ERROR] result code +** and you would have to make a second call to [sqlite3_reset()] in order +** to find the underlying cause of the problem. With the "v2" prepare +** interfaces, the underlying reason for the error is returned immediately. +**
  4. +**
+** +** Requirements: +** [H13011] [H13012] [H13013] [H13014] [H13015] [H13016] [H13019] [H13021] +** +*/ +SQLITE_API int sqlite3_prepare( + sqlite3 *db, /* Database handle */ + const char *zSql, /* SQL statement, UTF-8 encoded */ + int nByte, /* Maximum length of zSql in bytes. */ + sqlite3_stmt **ppStmt, /* OUT: Statement handle */ + const char **pzTail /* OUT: Pointer to unused portion of zSql */ +); +SQLITE_API int sqlite3_prepare_v2( + sqlite3 *db, /* Database handle */ + const char *zSql, /* SQL statement, UTF-8 encoded */ + int nByte, /* Maximum length of zSql in bytes. */ + sqlite3_stmt **ppStmt, /* OUT: Statement handle */ + const char **pzTail /* OUT: Pointer to unused portion of zSql */ +); +SQLITE_API int sqlite3_prepare16( + sqlite3 *db, /* Database handle */ + const void *zSql, /* SQL statement, UTF-16 encoded */ + int nByte, /* Maximum length of zSql in bytes. */ + sqlite3_stmt **ppStmt, /* OUT: Statement handle */ + const void **pzTail /* OUT: Pointer to unused portion of zSql */ +); +SQLITE_API int sqlite3_prepare16_v2( + sqlite3 *db, /* Database handle */ + const void *zSql, /* SQL statement, UTF-16 encoded */ + int nByte, /* Maximum length of zSql in bytes. */ + sqlite3_stmt **ppStmt, /* OUT: Statement handle */ + const void **pzTail /* OUT: Pointer to unused portion of zSql */ +); + +/* +** CAPI3REF: Retrieving Statement SQL {H13100} +** +** This interface can be used to retrieve a saved copy of the original +** SQL text used to create a [prepared statement] if that statement was +** compiled using either [sqlite3_prepare_v2()] or [sqlite3_prepare16_v2()]. +** +** Requirements: +** [H13101] [H13102] [H13103] +*/ +SQLITE_API const char *sqlite3_sql(sqlite3_stmt *pStmt); + +/* +** CAPI3REF: Dynamically Typed Value Object {H15000} +** KEYWORDS: {protected sqlite3_value} {unprotected sqlite3_value} +** +** SQLite uses the sqlite3_value object to represent all values +** that can be stored in a database table. SQLite uses dynamic typing +** for the values it stores. Values stored in sqlite3_value objects +** can be integers, floating point values, strings, BLOBs, or NULL. +** +** An sqlite3_value object may be either "protected" or "unprotected". +** Some interfaces require a protected sqlite3_value. Other interfaces +** will accept either a protected or an unprotected sqlite3_value. +** Every interface that accepts sqlite3_value arguments specifies +** whether or not it requires a protected sqlite3_value. +** +** The terms "protected" and "unprotected" refer to whether or not +** a mutex is held. A internal mutex is held for a protected +** sqlite3_value object but no mutex is held for an unprotected +** sqlite3_value object. If SQLite is compiled to be single-threaded +** (with [SQLITE_THREADSAFE=0] and with [sqlite3_threadsafe()] returning 0) +** or if SQLite is run in one of reduced mutex modes +** [SQLITE_CONFIG_SINGLETHREAD] or [SQLITE_CONFIG_MULTITHREAD] +** then there is no distinction between protected and unprotected +** sqlite3_value objects and they can be used interchangeably. However, +** for maximum code portability it is recommended that applications +** still make the distinction between between protected and unprotected +** sqlite3_value objects even when not strictly required. +** +** The sqlite3_value objects that are passed as parameters into the +** implementation of [application-defined SQL functions] are protected. +** The sqlite3_value object returned by +** [sqlite3_column_value()] is unprotected. +** Unprotected sqlite3_value objects may only be used with +** [sqlite3_result_value()] and [sqlite3_bind_value()]. +** The [sqlite3_value_blob | sqlite3_value_type()] family of +** interfaces require protected sqlite3_value objects. +*/ +typedef struct Mem sqlite3_value; + +/* +** CAPI3REF: SQL Function Context Object {H16001} +** +** The context in which an SQL function executes is stored in an +** sqlite3_context object. A pointer to an sqlite3_context object +** is always first parameter to [application-defined SQL functions]. +** The application-defined SQL function implementation will pass this +** pointer through into calls to [sqlite3_result_int | sqlite3_result()], +** [sqlite3_aggregate_context()], [sqlite3_user_data()], +** [sqlite3_context_db_handle()], [sqlite3_get_auxdata()], +** and/or [sqlite3_set_auxdata()]. +*/ +typedef struct sqlite3_context sqlite3_context; + +/* +** CAPI3REF: Binding Values To Prepared Statements {H13500} +** KEYWORDS: {host parameter} {host parameters} {host parameter name} +** KEYWORDS: {SQL parameter} {SQL parameters} {parameter binding} +** +** In the SQL strings input to [sqlite3_prepare_v2()] and its variants, +** literals may be replaced by a [parameter] in one of these forms: +** +**
    +**
  • ? +**
  • ?NNN +**
  • :VVV +**
  • @VVV +**
  • $VVV +**
+** +** In the parameter forms shown above NNN is an integer literal, +** and VVV is an alpha-numeric parameter name. The values of these +** parameters (also called "host parameter names" or "SQL parameters") +** can be set using the sqlite3_bind_*() routines defined here. +** +** The first argument to the sqlite3_bind_*() routines is always +** a pointer to the [sqlite3_stmt] object returned from +** [sqlite3_prepare_v2()] or its variants. +** +** The second argument is the index of the SQL parameter to be set. +** The leftmost SQL parameter has an index of 1. When the same named +** SQL parameter is used more than once, second and subsequent +** occurrences have the same index as the first occurrence. +** The index for named parameters can be looked up using the +** [sqlite3_bind_parameter_index()] API if desired. The index +** for "?NNN" parameters is the value of NNN. +** The NNN value must be between 1 and the [sqlite3_limit()] +** parameter [SQLITE_LIMIT_VARIABLE_NUMBER] (default value: 999). +** +** The third argument is the value to bind to the parameter. +** +** In those routines that have a fourth argument, its value is the +** number of bytes in the parameter. To be clear: the value is the +** number of bytes in the value, not the number of characters. +** If the fourth parameter is negative, the length of the string is +** the number of bytes up to the first zero terminator. +** +** The fifth argument to sqlite3_bind_blob(), sqlite3_bind_text(), and +** sqlite3_bind_text16() is a destructor used to dispose of the BLOB or +** string after SQLite has finished with it. If the fifth argument is +** the special value [SQLITE_STATIC], then SQLite assumes that the +** information is in static, unmanaged space and does not need to be freed. +** If the fifth argument has the value [SQLITE_TRANSIENT], then +** SQLite makes its own private copy of the data immediately, before +** the sqlite3_bind_*() routine returns. +** +** The sqlite3_bind_zeroblob() routine binds a BLOB of length N that +** is filled with zeroes. A zeroblob uses a fixed amount of memory +** (just an integer to hold its size) while it is being processed. +** Zeroblobs are intended to serve as placeholders for BLOBs whose +** content is later written using +** [sqlite3_blob_open | incremental BLOB I/O] routines. +** A negative value for the zeroblob results in a zero-length BLOB. +** +** The sqlite3_bind_*() routines must be called after +** [sqlite3_prepare_v2()] (and its variants) or [sqlite3_reset()] and +** before [sqlite3_step()]. +** Bindings are not cleared by the [sqlite3_reset()] routine. +** Unbound parameters are interpreted as NULL. +** +** These routines return [SQLITE_OK] on success or an error code if +** anything goes wrong. [SQLITE_RANGE] is returned if the parameter +** index is out of range. [SQLITE_NOMEM] is returned if malloc() fails. +** [SQLITE_MISUSE] might be returned if these routines are called on a +** virtual machine that is the wrong state or which has already been finalized. +** Detection of misuse is unreliable. Applications should not depend +** on SQLITE_MISUSE returns. SQLITE_MISUSE is intended to indicate a +** a logic error in the application. Future versions of SQLite might +** panic rather than return SQLITE_MISUSE. +** +** See also: [sqlite3_bind_parameter_count()], +** [sqlite3_bind_parameter_name()], and [sqlite3_bind_parameter_index()]. +** +** Requirements: +** [H13506] [H13509] [H13512] [H13515] [H13518] [H13521] [H13524] [H13527] +** [H13530] [H13533] [H13536] [H13539] [H13542] [H13545] [H13548] [H13551] +** +*/ +SQLITE_API int sqlite3_bind_blob(sqlite3_stmt*, int, const void*, int n, void(*)(void*)); +SQLITE_API int sqlite3_bind_double(sqlite3_stmt*, int, double); +SQLITE_API int sqlite3_bind_int(sqlite3_stmt*, int, int); +SQLITE_API int sqlite3_bind_int64(sqlite3_stmt*, int, sqlite3_int64); +SQLITE_API int sqlite3_bind_null(sqlite3_stmt*, int); +SQLITE_API int sqlite3_bind_text(sqlite3_stmt*, int, const char*, int n, void(*)(void*)); +SQLITE_API int sqlite3_bind_text16(sqlite3_stmt*, int, const void*, int, void(*)(void*)); +SQLITE_API int sqlite3_bind_value(sqlite3_stmt*, int, const sqlite3_value*); +SQLITE_API int sqlite3_bind_zeroblob(sqlite3_stmt*, int, int n); + +/* +** CAPI3REF: Number Of SQL Parameters {H13600} +** +** This routine can be used to find the number of [SQL parameters] +** in a [prepared statement]. SQL parameters are tokens of the +** form "?", "?NNN", ":AAA", "$AAA", or "@AAA" that serve as +** placeholders for values that are [sqlite3_bind_blob | bound] +** to the parameters at a later time. +** +** This routine actually returns the index of the largest (rightmost) +** parameter. For all forms except ?NNN, this will correspond to the +** number of unique parameters. If parameters of the ?NNN are used, +** there may be gaps in the list. +** +** See also: [sqlite3_bind_blob|sqlite3_bind()], +** [sqlite3_bind_parameter_name()], and +** [sqlite3_bind_parameter_index()]. +** +** Requirements: +** [H13601] +*/ +SQLITE_API int sqlite3_bind_parameter_count(sqlite3_stmt*); + +/* +** CAPI3REF: Name Of A Host Parameter {H13620} +** +** This routine returns a pointer to the name of the n-th +** [SQL parameter] in a [prepared statement]. +** SQL parameters of the form "?NNN" or ":AAA" or "@AAA" or "$AAA" +** have a name which is the string "?NNN" or ":AAA" or "@AAA" or "$AAA" +** respectively. +** In other words, the initial ":" or "$" or "@" or "?" +** is included as part of the name. +** Parameters of the form "?" without a following integer have no name +** and are also referred to as "anonymous parameters". +** +** The first host parameter has an index of 1, not 0. +** +** If the value n is out of range or if the n-th parameter is +** nameless, then NULL is returned. The returned string is +** always in UTF-8 encoding even if the named parameter was +** originally specified as UTF-16 in [sqlite3_prepare16()] or +** [sqlite3_prepare16_v2()]. +** +** See also: [sqlite3_bind_blob|sqlite3_bind()], +** [sqlite3_bind_parameter_count()], and +** [sqlite3_bind_parameter_index()]. +** +** Requirements: +** [H13621] +*/ +SQLITE_API const char *sqlite3_bind_parameter_name(sqlite3_stmt*, int); + +/* +** CAPI3REF: Index Of A Parameter With A Given Name {H13640} +** +** Return the index of an SQL parameter given its name. The +** index value returned is suitable for use as the second +** parameter to [sqlite3_bind_blob|sqlite3_bind()]. A zero +** is returned if no matching parameter is found. The parameter +** name must be given in UTF-8 even if the original statement +** was prepared from UTF-16 text using [sqlite3_prepare16_v2()]. +** +** See also: [sqlite3_bind_blob|sqlite3_bind()], +** [sqlite3_bind_parameter_count()], and +** [sqlite3_bind_parameter_index()]. +** +** Requirements: +** [H13641] +*/ +SQLITE_API int sqlite3_bind_parameter_index(sqlite3_stmt*, const char *zName); + +/* +** CAPI3REF: Reset All Bindings On A Prepared Statement {H13660} +** +** Contrary to the intuition of many, [sqlite3_reset()] does not reset +** the [sqlite3_bind_blob | bindings] on a [prepared statement]. +** Use this routine to reset all host parameters to NULL. +** +** Requirements: +** [H13661] +*/ +SQLITE_API int sqlite3_clear_bindings(sqlite3_stmt*); + +/* +** CAPI3REF: Number Of Columns In A Result Set {H13710} +** +** Return the number of columns in the result set returned by the +** [prepared statement]. This routine returns 0 if pStmt is an SQL +** statement that does not return data (for example an [UPDATE]). +** +** Requirements: +** [H13711] +*/ +SQLITE_API int sqlite3_column_count(sqlite3_stmt *pStmt); + +/* +** CAPI3REF: Column Names In A Result Set {H13720} +** +** These routines return the name assigned to a particular column +** in the result set of a [SELECT] statement. The sqlite3_column_name() +** interface returns a pointer to a zero-terminated UTF-8 string +** and sqlite3_column_name16() returns a pointer to a zero-terminated +** UTF-16 string. The first parameter is the [prepared statement] +** that implements the [SELECT] statement. The second parameter is the +** column number. The leftmost column is number 0. +** +** The returned string pointer is valid until either the [prepared statement] +** is destroyed by [sqlite3_finalize()] or until the next call to +** sqlite3_column_name() or sqlite3_column_name16() on the same column. +** +** If sqlite3_malloc() fails during the processing of either routine +** (for example during a conversion from UTF-8 to UTF-16) then a +** NULL pointer is returned. +** +** The name of a result column is the value of the "AS" clause for +** that column, if there is an AS clause. If there is no AS clause +** then the name of the column is unspecified and may change from +** one release of SQLite to the next. +** +** Requirements: +** [H13721] [H13723] [H13724] [H13725] [H13726] [H13727] +*/ +SQLITE_API const char *sqlite3_column_name(sqlite3_stmt*, int N); +SQLITE_API const void *sqlite3_column_name16(sqlite3_stmt*, int N); + +/* +** CAPI3REF: Source Of Data In A Query Result {H13740} +** +** These routines provide a means to determine what column of what +** table in which database a result of a [SELECT] statement comes from. +** The name of the database or table or column can be returned as +** either a UTF-8 or UTF-16 string. The _database_ routines return +** the database name, the _table_ routines return the table name, and +** the origin_ routines return the column name. +** The returned string is valid until the [prepared statement] is destroyed +** using [sqlite3_finalize()] or until the same information is requested +** again in a different encoding. +** +** The names returned are the original un-aliased names of the +** database, table, and column. +** +** The first argument to the following calls is a [prepared statement]. +** These functions return information about the Nth column returned by +** the statement, where N is the second function argument. +** +** If the Nth column returned by the statement is an expression or +** subquery and is not a column value, then all of these functions return +** NULL. These routine might also return NULL if a memory allocation error +** occurs. Otherwise, they return the name of the attached database, table +** and column that query result column was extracted from. +** +** As with all other SQLite APIs, those postfixed with "16" return +** UTF-16 encoded strings, the other functions return UTF-8. {END} +** +** These APIs are only available if the library was compiled with the +** [SQLITE_ENABLE_COLUMN_METADATA] C-preprocessor symbol defined. +** +** {A13751} +** If two or more threads call one or more of these routines against the same +** prepared statement and column at the same time then the results are +** undefined. +** +** Requirements: +** [H13741] [H13742] [H13743] [H13744] [H13745] [H13746] [H13748] +** +** If two or more threads call one or more +** [sqlite3_column_database_name | column metadata interfaces] +** for the same [prepared statement] and result column +** at the same time then the results are undefined. +*/ +SQLITE_API const char *sqlite3_column_database_name(sqlite3_stmt*,int); +SQLITE_API const void *sqlite3_column_database_name16(sqlite3_stmt*,int); +SQLITE_API const char *sqlite3_column_table_name(sqlite3_stmt*,int); +SQLITE_API const void *sqlite3_column_table_name16(sqlite3_stmt*,int); +SQLITE_API const char *sqlite3_column_origin_name(sqlite3_stmt*,int); +SQLITE_API const void *sqlite3_column_origin_name16(sqlite3_stmt*,int); + +/* +** CAPI3REF: Declared Datatype Of A Query Result {H13760} +** +** The first parameter is a [prepared statement]. +** If this statement is a [SELECT] statement and the Nth column of the +** returned result set of that [SELECT] is a table column (not an +** expression or subquery) then the declared type of the table +** column is returned. If the Nth column of the result set is an +** expression or subquery, then a NULL pointer is returned. +** The returned string is always UTF-8 encoded. {END} +** +** For example, given the database schema: +** +** CREATE TABLE t1(c1 VARIANT); +** +** and the following statement to be compiled: +** +** SELECT c1 + 1, c1 FROM t1; +** +** this routine would return the string "VARIANT" for the second result +** column (i==1), and a NULL pointer for the first result column (i==0). +** +** SQLite uses dynamic run-time typing. So just because a column +** is declared to contain a particular type does not mean that the +** data stored in that column is of the declared type. SQLite is +** strongly typed, but the typing is dynamic not static. Type +** is associated with individual values, not with the containers +** used to hold those values. +** +** Requirements: +** [H13761] [H13762] [H13763] +*/ +SQLITE_API const char *sqlite3_column_decltype(sqlite3_stmt*,int); +SQLITE_API const void *sqlite3_column_decltype16(sqlite3_stmt*,int); + +/* +** CAPI3REF: Evaluate An SQL Statement {H13200} +** +** After a [prepared statement] has been prepared using either +** [sqlite3_prepare_v2()] or [sqlite3_prepare16_v2()] or one of the legacy +** interfaces [sqlite3_prepare()] or [sqlite3_prepare16()], this function +** must be called one or more times to evaluate the statement. +** +** The details of the behavior of the sqlite3_step() interface depend +** on whether the statement was prepared using the newer "v2" interface +** [sqlite3_prepare_v2()] and [sqlite3_prepare16_v2()] or the older legacy +** interface [sqlite3_prepare()] and [sqlite3_prepare16()]. The use of the +** new "v2" interface is recommended for new applications but the legacy +** interface will continue to be supported. +** +** In the legacy interface, the return value will be either [SQLITE_BUSY], +** [SQLITE_DONE], [SQLITE_ROW], [SQLITE_ERROR], or [SQLITE_MISUSE]. +** With the "v2" interface, any of the other [result codes] or +** [extended result codes] might be returned as well. +** +** [SQLITE_BUSY] means that the database engine was unable to acquire the +** database locks it needs to do its job. If the statement is a [COMMIT] +** or occurs outside of an explicit transaction, then you can retry the +** statement. If the statement is not a [COMMIT] and occurs within a +** explicit transaction then you should rollback the transaction before +** continuing. +** +** [SQLITE_DONE] means that the statement has finished executing +** successfully. sqlite3_step() should not be called again on this virtual +** machine without first calling [sqlite3_reset()] to reset the virtual +** machine back to its initial state. +** +** If the SQL statement being executed returns any data, then [SQLITE_ROW] +** is returned each time a new row of data is ready for processing by the +** caller. The values may be accessed using the [column access functions]. +** sqlite3_step() is called again to retrieve the next row of data. +** +** [SQLITE_ERROR] means that a run-time error (such as a constraint +** violation) has occurred. sqlite3_step() should not be called again on +** the VM. More information may be found by calling [sqlite3_errmsg()]. +** With the legacy interface, a more specific error code (for example, +** [SQLITE_INTERRUPT], [SQLITE_SCHEMA], [SQLITE_CORRUPT], and so forth) +** can be obtained by calling [sqlite3_reset()] on the +** [prepared statement]. In the "v2" interface, +** the more specific error code is returned directly by sqlite3_step(). +** +** [SQLITE_MISUSE] means that the this routine was called inappropriately. +** Perhaps it was called on a [prepared statement] that has +** already been [sqlite3_finalize | finalized] or on one that had +** previously returned [SQLITE_ERROR] or [SQLITE_DONE]. Or it could +** be the case that the same database connection is being used by two or +** more threads at the same moment in time. +** +** Goofy Interface Alert: In the legacy interface, the sqlite3_step() +** API always returns a generic error code, [SQLITE_ERROR], following any +** error other than [SQLITE_BUSY] and [SQLITE_MISUSE]. You must call +** [sqlite3_reset()] or [sqlite3_finalize()] in order to find one of the +** specific [error codes] that better describes the error. +** We admit that this is a goofy design. The problem has been fixed +** with the "v2" interface. If you prepare all of your SQL statements +** using either [sqlite3_prepare_v2()] or [sqlite3_prepare16_v2()] instead +** of the legacy [sqlite3_prepare()] and [sqlite3_prepare16()] interfaces, +** then the more specific [error codes] are returned directly +** by sqlite3_step(). The use of the "v2" interface is recommended. +** +** Requirements: +** [H13202] [H15304] [H15306] [H15308] [H15310] +*/ +SQLITE_API int sqlite3_step(sqlite3_stmt*); + +/* +** CAPI3REF: Number of columns in a result set {H13770} +** +** Returns the number of values in the current row of the result set. +** +** Requirements: +** [H13771] [H13772] +*/ +SQLITE_API int sqlite3_data_count(sqlite3_stmt *pStmt); + +/* +** CAPI3REF: Fundamental Datatypes {H10265} +** KEYWORDS: SQLITE_TEXT +** +** {H10266} Every value in SQLite has one of five fundamental datatypes: +** +**
    +**
  • 64-bit signed integer +**
  • 64-bit IEEE floating point number +**
  • string +**
  • BLOB +**
  • NULL +**
{END} +** +** These constants are codes for each of those types. +** +** Note that the SQLITE_TEXT constant was also used in SQLite version 2 +** for a completely different meaning. Software that links against both +** SQLite version 2 and SQLite version 3 should use SQLITE3_TEXT, not +** SQLITE_TEXT. +*/ +#define SQLITE_INTEGER 1 +#define SQLITE_FLOAT 2 +#define SQLITE_BLOB 4 +#define SQLITE_NULL 5 +#ifdef SQLITE_TEXT +# undef SQLITE_TEXT +#else +# define SQLITE_TEXT 3 +#endif +#define SQLITE3_TEXT 3 + +/* +** CAPI3REF: Result Values From A Query {H13800} +** KEYWORDS: {column access functions} +** +** These routines form the "result set query" interface. +** +** These routines return information about a single column of the current +** result row of a query. In every case the first argument is a pointer +** to the [prepared statement] that is being evaluated (the [sqlite3_stmt*] +** that was returned from [sqlite3_prepare_v2()] or one of its variants) +** and the second argument is the index of the column for which information +** should be returned. The leftmost column of the result set has the index 0. +** +** If the SQL statement does not currently point to a valid row, or if the +** column index is out of range, the result is undefined. +** These routines may only be called when the most recent call to +** [sqlite3_step()] has returned [SQLITE_ROW] and neither +** [sqlite3_reset()] nor [sqlite3_finalize()] have been called subsequently. +** If any of these routines are called after [sqlite3_reset()] or +** [sqlite3_finalize()] or after [sqlite3_step()] has returned +** something other than [SQLITE_ROW], the results are undefined. +** If [sqlite3_step()] or [sqlite3_reset()] or [sqlite3_finalize()] +** are called from a different thread while any of these routines +** are pending, then the results are undefined. +** +** The sqlite3_column_type() routine returns the +** [SQLITE_INTEGER | datatype code] for the initial data type +** of the result column. The returned value is one of [SQLITE_INTEGER], +** [SQLITE_FLOAT], [SQLITE_TEXT], [SQLITE_BLOB], or [SQLITE_NULL]. The value +** returned by sqlite3_column_type() is only meaningful if no type +** conversions have occurred as described below. After a type conversion, +** the value returned by sqlite3_column_type() is undefined. Future +** versions of SQLite may change the behavior of sqlite3_column_type() +** following a type conversion. +** +** If the result is a BLOB or UTF-8 string then the sqlite3_column_bytes() +** routine returns the number of bytes in that BLOB or string. +** If the result is a UTF-16 string, then sqlite3_column_bytes() converts +** the string to UTF-8 and then returns the number of bytes. +** If the result is a numeric value then sqlite3_column_bytes() uses +** [sqlite3_snprintf()] to convert that value to a UTF-8 string and returns +** the number of bytes in that string. +** The value returned does not include the zero terminator at the end +** of the string. For clarity: the value returned is the number of +** bytes in the string, not the number of characters. +** +** Strings returned by sqlite3_column_text() and sqlite3_column_text16(), +** even empty strings, are always zero terminated. The return +** value from sqlite3_column_blob() for a zero-length BLOB is an arbitrary +** pointer, possibly even a NULL pointer. +** +** The sqlite3_column_bytes16() routine is similar to sqlite3_column_bytes() +** but leaves the result in UTF-16 in native byte order instead of UTF-8. +** The zero terminator is not included in this count. +** +** The object returned by [sqlite3_column_value()] is an +** [unprotected sqlite3_value] object. An unprotected sqlite3_value object +** may only be used with [sqlite3_bind_value()] and [sqlite3_result_value()]. +** If the [unprotected sqlite3_value] object returned by +** [sqlite3_column_value()] is used in any other way, including calls +** to routines like [sqlite3_value_int()], [sqlite3_value_text()], +** or [sqlite3_value_bytes()], then the behavior is undefined. +** +** These routines attempt to convert the value where appropriate. For +** example, if the internal representation is FLOAT and a text result +** is requested, [sqlite3_snprintf()] is used internally to perform the +** conversion automatically. The following table details the conversions +** that are applied: +** +**
+** +**
Internal
Type
Requested
Type
Conversion +** +**
NULL INTEGER Result is 0 +**
NULL FLOAT Result is 0.0 +**
NULL TEXT Result is NULL pointer +**
NULL BLOB Result is NULL pointer +**
INTEGER FLOAT Convert from integer to float +**
INTEGER TEXT ASCII rendering of the integer +**
INTEGER BLOB Same as INTEGER->TEXT +**
FLOAT INTEGER Convert from float to integer +**
FLOAT TEXT ASCII rendering of the float +**
FLOAT BLOB Same as FLOAT->TEXT +**
TEXT INTEGER Use atoi() +**
TEXT FLOAT Use atof() +**
TEXT BLOB No change +**
BLOB INTEGER Convert to TEXT then use atoi() +**
BLOB FLOAT Convert to TEXT then use atof() +**
BLOB TEXT Add a zero terminator if needed +**
+**
+** +** The table above makes reference to standard C library functions atoi() +** and atof(). SQLite does not really use these functions. It has its +** own equivalent internal routines. The atoi() and atof() names are +** used in the table for brevity and because they are familiar to most +** C programmers. +** +** Note that when type conversions occur, pointers returned by prior +** calls to sqlite3_column_blob(), sqlite3_column_text(), and/or +** sqlite3_column_text16() may be invalidated. +** Type conversions and pointer invalidations might occur +** in the following cases: +** +**
    +**
  • The initial content is a BLOB and sqlite3_column_text() or +** sqlite3_column_text16() is called. A zero-terminator might +** need to be added to the string.
  • +**
  • The initial content is UTF-8 text and sqlite3_column_bytes16() or +** sqlite3_column_text16() is called. The content must be converted +** to UTF-16.
  • +**
  • The initial content is UTF-16 text and sqlite3_column_bytes() or +** sqlite3_column_text() is called. The content must be converted +** to UTF-8.
  • +**
+** +** Conversions between UTF-16be and UTF-16le are always done in place and do +** not invalidate a prior pointer, though of course the content of the buffer +** that the prior pointer points to will have been modified. Other kinds +** of conversion are done in place when it is possible, but sometimes they +** are not possible and in those cases prior pointers are invalidated. +** +** The safest and easiest to remember policy is to invoke these routines +** in one of the following ways: +** +**
    +**
  • sqlite3_column_text() followed by sqlite3_column_bytes()
  • +**
  • sqlite3_column_blob() followed by sqlite3_column_bytes()
  • +**
  • sqlite3_column_text16() followed by sqlite3_column_bytes16()
  • +**
+** +** In other words, you should call sqlite3_column_text(), +** sqlite3_column_blob(), or sqlite3_column_text16() first to force the result +** into the desired format, then invoke sqlite3_column_bytes() or +** sqlite3_column_bytes16() to find the size of the result. Do not mix calls +** to sqlite3_column_text() or sqlite3_column_blob() with calls to +** sqlite3_column_bytes16(), and do not mix calls to sqlite3_column_text16() +** with calls to sqlite3_column_bytes(). +** +** The pointers returned are valid until a type conversion occurs as +** described above, or until [sqlite3_step()] or [sqlite3_reset()] or +** [sqlite3_finalize()] is called. The memory space used to hold strings +** and BLOBs is freed automatically. Do not pass the pointers returned +** [sqlite3_column_blob()], [sqlite3_column_text()], etc. into +** [sqlite3_free()]. +** +** If a memory allocation error occurs during the evaluation of any +** of these routines, a default value is returned. The default value +** is either the integer 0, the floating point number 0.0, or a NULL +** pointer. Subsequent calls to [sqlite3_errcode()] will return +** [SQLITE_NOMEM]. +** +** Requirements: +** [H13803] [H13806] [H13809] [H13812] [H13815] [H13818] [H13821] [H13824] +** [H13827] [H13830] +*/ +SQLITE_API const void *sqlite3_column_blob(sqlite3_stmt*, int iCol); +SQLITE_API int sqlite3_column_bytes(sqlite3_stmt*, int iCol); +SQLITE_API int sqlite3_column_bytes16(sqlite3_stmt*, int iCol); +SQLITE_API double sqlite3_column_double(sqlite3_stmt*, int iCol); +SQLITE_API int sqlite3_column_int(sqlite3_stmt*, int iCol); +SQLITE_API sqlite3_int64 sqlite3_column_int64(sqlite3_stmt*, int iCol); +SQLITE_API const unsigned char *sqlite3_column_text(sqlite3_stmt*, int iCol); +SQLITE_API const void *sqlite3_column_text16(sqlite3_stmt*, int iCol); +SQLITE_API int sqlite3_column_type(sqlite3_stmt*, int iCol); +SQLITE_API sqlite3_value *sqlite3_column_value(sqlite3_stmt*, int iCol); + +/* +** CAPI3REF: Destroy A Prepared Statement Object {H13300} +** +** The sqlite3_finalize() function is called to delete a [prepared statement]. +** If the statement was executed successfully or not executed at all, then +** SQLITE_OK is returned. If execution of the statement failed then an +** [error code] or [extended error code] is returned. +** +** This routine can be called at any point during the execution of the +** [prepared statement]. If the virtual machine has not +** completed execution when this routine is called, that is like +** encountering an error or an [sqlite3_interrupt | interrupt]. +** Incomplete updates may be rolled back and transactions canceled, +** depending on the circumstances, and the +** [error code] returned will be [SQLITE_ABORT]. +** +** Requirements: +** [H11302] [H11304] +*/ +SQLITE_API int sqlite3_finalize(sqlite3_stmt *pStmt); + +/* +** CAPI3REF: Reset A Prepared Statement Object {H13330} +** +** The sqlite3_reset() function is called to reset a [prepared statement] +** object back to its initial state, ready to be re-executed. +** Any SQL statement variables that had values bound to them using +** the [sqlite3_bind_blob | sqlite3_bind_*() API] retain their values. +** Use [sqlite3_clear_bindings()] to reset the bindings. +** +** {H11332} The [sqlite3_reset(S)] interface resets the [prepared statement] S +** back to the beginning of its program. +** +** {H11334} If the most recent call to [sqlite3_step(S)] for the +** [prepared statement] S returned [SQLITE_ROW] or [SQLITE_DONE], +** or if [sqlite3_step(S)] has never before been called on S, +** then [sqlite3_reset(S)] returns [SQLITE_OK]. +** +** {H11336} If the most recent call to [sqlite3_step(S)] for the +** [prepared statement] S indicated an error, then +** [sqlite3_reset(S)] returns an appropriate [error code]. +** +** {H11338} The [sqlite3_reset(S)] interface does not change the values +** of any [sqlite3_bind_blob|bindings] on the [prepared statement] S. +*/ +SQLITE_API int sqlite3_reset(sqlite3_stmt *pStmt); + +/* +** CAPI3REF: Create Or Redefine SQL Functions {H16100} +** KEYWORDS: {function creation routines} +** KEYWORDS: {application-defined SQL function} +** KEYWORDS: {application-defined SQL functions} +** +** These two functions (collectively known as "function creation routines") +** are used to add SQL functions or aggregates or to redefine the behavior +** of existing SQL functions or aggregates. The only difference between the +** two is that the second parameter, the name of the (scalar) function or +** aggregate, is encoded in UTF-8 for sqlite3_create_function() and UTF-16 +** for sqlite3_create_function16(). +** +** The first parameter is the [database connection] to which the SQL +** function is to be added. If a single program uses more than one database +** connection internally, then SQL functions must be added individually to +** each database connection. +** +** The second parameter is the name of the SQL function to be created or +** redefined. The length of the name is limited to 255 bytes, exclusive of +** the zero-terminator. Note that the name length limit is in bytes, not +** characters. Any attempt to create a function with a longer name +** will result in [SQLITE_ERROR] being returned. +** +** The third parameter (nArg) +** is the number of arguments that the SQL function or +** aggregate takes. If this parameter is negative, then the SQL function or +** aggregate may take any number of arguments. +** +** The fourth parameter, eTextRep, specifies what +** [SQLITE_UTF8 | text encoding] this SQL function prefers for +** its parameters. Any SQL function implementation should be able to work +** work with UTF-8, UTF-16le, or UTF-16be. But some implementations may be +** more efficient with one encoding than another. It is allowed to +** invoke sqlite3_create_function() or sqlite3_create_function16() multiple +** times with the same function but with different values of eTextRep. +** When multiple implementations of the same function are available, SQLite +** will pick the one that involves the least amount of data conversion. +** If there is only a single implementation which does not care what text +** encoding is used, then the fourth argument should be [SQLITE_ANY]. +** +** The fifth parameter is an arbitrary pointer. The implementation of the +** function can gain access to this pointer using [sqlite3_user_data()]. +** +** The seventh, eighth and ninth parameters, xFunc, xStep and xFinal, are +** pointers to C-language functions that implement the SQL function or +** aggregate. A scalar SQL function requires an implementation of the xFunc +** callback only, NULL pointers should be passed as the xStep and xFinal +** parameters. An aggregate SQL function requires an implementation of xStep +** and xFinal and NULL should be passed for xFunc. To delete an existing +** SQL function or aggregate, pass NULL for all three function callbacks. +** +** It is permitted to register multiple implementations of the same +** functions with the same name but with either differing numbers of +** arguments or differing preferred text encodings. SQLite will use +** the implementation most closely matches the way in which the +** SQL function is used. A function implementation with a non-negative +** nArg parameter is a better match than a function implementation with +** a negative nArg. A function where the preferred text encoding +** matches the database encoding is a better +** match than a function where the encoding is different. +** A function where the encoding difference is between UTF16le and UTF16be +** is a closer match than a function where the encoding difference is +** between UTF8 and UTF16. +** +** Built-in functions may be overloaded by new application-defined functions. +** The first application-defined function with a given name overrides all +** built-in functions in the same [database connection] with the same name. +** Subsequent application-defined functions of the same name only override +** prior application-defined functions that are an exact match for the +** number of parameters and preferred encoding. +** +** An application-defined function is permitted to call other +** SQLite interfaces. However, such calls must not +** close the database connection nor finalize or reset the prepared +** statement in which the function is running. +** +** Requirements: +** [H16103] [H16106] [H16109] [H16112] [H16118] [H16121] [H16124] [H16127] +** [H16130] [H16133] [H16136] [H16139] [H16142] +*/ +SQLITE_API int sqlite3_create_function( + sqlite3 *db, + const char *zFunctionName, + int nArg, + int eTextRep, + void *pApp, + void (*xFunc)(sqlite3_context*,int,sqlite3_value**), + void (*xStep)(sqlite3_context*,int,sqlite3_value**), + void (*xFinal)(sqlite3_context*) +); +SQLITE_API int sqlite3_create_function16( + sqlite3 *db, + const void *zFunctionName, + int nArg, + int eTextRep, + void *pApp, + void (*xFunc)(sqlite3_context*,int,sqlite3_value**), + void (*xStep)(sqlite3_context*,int,sqlite3_value**), + void (*xFinal)(sqlite3_context*) +); + +/* +** CAPI3REF: Text Encodings {H10267} +** +** These constant define integer codes that represent the various +** text encodings supported by SQLite. +*/ +#define SQLITE_UTF8 1 +#define SQLITE_UTF16LE 2 +#define SQLITE_UTF16BE 3 +#define SQLITE_UTF16 4 /* Use native byte order */ +#define SQLITE_ANY 5 /* sqlite3_create_function only */ +#define SQLITE_UTF16_ALIGNED 8 /* sqlite3_create_collation only */ + +/* +** CAPI3REF: Deprecated Functions +** DEPRECATED +** +** These functions are [deprecated]. In order to maintain +** backwards compatibility with older code, these functions continue +** to be supported. However, new applications should avoid +** the use of these functions. To help encourage people to avoid +** using these functions, we are not going to tell you what they do. +*/ +#ifndef SQLITE_OMIT_DEPRECATED +SQLITE_API SQLITE_DEPRECATED int sqlite3_aggregate_count(sqlite3_context*); +SQLITE_API SQLITE_DEPRECATED int sqlite3_expired(sqlite3_stmt*); +SQLITE_API SQLITE_DEPRECATED int sqlite3_transfer_bindings(sqlite3_stmt*, sqlite3_stmt*); +SQLITE_API SQLITE_DEPRECATED int sqlite3_global_recover(void); +SQLITE_API SQLITE_DEPRECATED void sqlite3_thread_cleanup(void); +SQLITE_API SQLITE_DEPRECATED int sqlite3_memory_alarm(void(*)(void*,sqlite3_int64,int),void*,sqlite3_int64); +#endif + +/* +** CAPI3REF: Obtaining SQL Function Parameter Values {H15100} +** +** The C-language implementation of SQL functions and aggregates uses +** this set of interface routines to access the parameter values on +** the function or aggregate. +** +** The xFunc (for scalar functions) or xStep (for aggregates) parameters +** to [sqlite3_create_function()] and [sqlite3_create_function16()] +** define callbacks that implement the SQL functions and aggregates. +** The 4th parameter to these callbacks is an array of pointers to +** [protected sqlite3_value] objects. There is one [sqlite3_value] object for +** each parameter to the SQL function. These routines are used to +** extract values from the [sqlite3_value] objects. +** +** These routines work only with [protected sqlite3_value] objects. +** Any attempt to use these routines on an [unprotected sqlite3_value] +** object results in undefined behavior. +** +** These routines work just like the corresponding [column access functions] +** except that these routines take a single [protected sqlite3_value] object +** pointer instead of a [sqlite3_stmt*] pointer and an integer column number. +** +** The sqlite3_value_text16() interface extracts a UTF-16 string +** in the native byte-order of the host machine. The +** sqlite3_value_text16be() and sqlite3_value_text16le() interfaces +** extract UTF-16 strings as big-endian and little-endian respectively. +** +** The sqlite3_value_numeric_type() interface attempts to apply +** numeric affinity to the value. This means that an attempt is +** made to convert the value to an integer or floating point. If +** such a conversion is possible without loss of information (in other +** words, if the value is a string that looks like a number) +** then the conversion is performed. Otherwise no conversion occurs. +** The [SQLITE_INTEGER | datatype] after conversion is returned. +** +** Please pay particular attention to the fact that the pointer returned +** from [sqlite3_value_blob()], [sqlite3_value_text()], or +** [sqlite3_value_text16()] can be invalidated by a subsequent call to +** [sqlite3_value_bytes()], [sqlite3_value_bytes16()], [sqlite3_value_text()], +** or [sqlite3_value_text16()]. +** +** These routines must be called from the same thread as +** the SQL function that supplied the [sqlite3_value*] parameters. +** +** Requirements: +** [H15103] [H15106] [H15109] [H15112] [H15115] [H15118] [H15121] [H15124] +** [H15127] [H15130] [H15133] [H15136] +*/ +SQLITE_API const void *sqlite3_value_blob(sqlite3_value*); +SQLITE_API int sqlite3_value_bytes(sqlite3_value*); +SQLITE_API int sqlite3_value_bytes16(sqlite3_value*); +SQLITE_API double sqlite3_value_double(sqlite3_value*); +SQLITE_API int sqlite3_value_int(sqlite3_value*); +SQLITE_API sqlite3_int64 sqlite3_value_int64(sqlite3_value*); +SQLITE_API const unsigned char *sqlite3_value_text(sqlite3_value*); +SQLITE_API const void *sqlite3_value_text16(sqlite3_value*); +SQLITE_API const void *sqlite3_value_text16le(sqlite3_value*); +SQLITE_API const void *sqlite3_value_text16be(sqlite3_value*); +SQLITE_API int sqlite3_value_type(sqlite3_value*); +SQLITE_API int sqlite3_value_numeric_type(sqlite3_value*); + +/* +** CAPI3REF: Obtain Aggregate Function Context {H16210} +** +** The implementation of aggregate SQL functions use this routine to allocate +** a structure for storing their state. +** +** The first time the sqlite3_aggregate_context() routine is called for a +** particular aggregate, SQLite allocates nBytes of memory, zeroes out that +** memory, and returns a pointer to it. On second and subsequent calls to +** sqlite3_aggregate_context() for the same aggregate function index, +** the same buffer is returned. The implementation of the aggregate can use +** the returned buffer to accumulate data. +** +** SQLite automatically frees the allocated buffer when the aggregate +** query concludes. +** +** The first parameter should be a copy of the +** [sqlite3_context | SQL function context] that is the first parameter +** to the callback routine that implements the aggregate function. +** +** This routine must be called from the same thread in which +** the aggregate SQL function is running. +** +** Requirements: +** [H16211] [H16213] [H16215] [H16217] +*/ +SQLITE_API void *sqlite3_aggregate_context(sqlite3_context*, int nBytes); + +/* +** CAPI3REF: User Data For Functions {H16240} +** +** The sqlite3_user_data() interface returns a copy of +** the pointer that was the pUserData parameter (the 5th parameter) +** of the [sqlite3_create_function()] +** and [sqlite3_create_function16()] routines that originally +** registered the application defined function. {END} +** +** This routine must be called from the same thread in which +** the application-defined function is running. +** +** Requirements: +** [H16243] +*/ +SQLITE_API void *sqlite3_user_data(sqlite3_context*); + +/* +** CAPI3REF: Database Connection For Functions {H16250} +** +** The sqlite3_context_db_handle() interface returns a copy of +** the pointer to the [database connection] (the 1st parameter) +** of the [sqlite3_create_function()] +** and [sqlite3_create_function16()] routines that originally +** registered the application defined function. +** +** Requirements: +** [H16253] +*/ +SQLITE_API sqlite3 *sqlite3_context_db_handle(sqlite3_context*); + +/* +** CAPI3REF: Function Auxiliary Data {H16270} +** +** The following two functions may be used by scalar SQL functions to +** associate metadata with argument values. If the same value is passed to +** multiple invocations of the same SQL function during query execution, under +** some circumstances the associated metadata may be preserved. This may +** be used, for example, to add a regular-expression matching scalar +** function. The compiled version of the regular expression is stored as +** metadata associated with the SQL value passed as the regular expression +** pattern. The compiled regular expression can be reused on multiple +** invocations of the same function so that the original pattern string +** does not need to be recompiled on each invocation. +** +** The sqlite3_get_auxdata() interface returns a pointer to the metadata +** associated by the sqlite3_set_auxdata() function with the Nth argument +** value to the application-defined function. If no metadata has been ever +** been set for the Nth argument of the function, or if the corresponding +** function parameter has changed since the meta-data was set, +** then sqlite3_get_auxdata() returns a NULL pointer. +** +** The sqlite3_set_auxdata() interface saves the metadata +** pointed to by its 3rd parameter as the metadata for the N-th +** argument of the application-defined function. Subsequent +** calls to sqlite3_get_auxdata() might return this data, if it has +** not been destroyed. +** If it is not NULL, SQLite will invoke the destructor +** function given by the 4th parameter to sqlite3_set_auxdata() on +** the metadata when the corresponding function parameter changes +** or when the SQL statement completes, whichever comes first. +** +** SQLite is free to call the destructor and drop metadata on any +** parameter of any function at any time. The only guarantee is that +** the destructor will be called before the metadata is dropped. +** +** In practice, metadata is preserved between function calls for +** expressions that are constant at compile time. This includes literal +** values and SQL variables. +** +** These routines must be called from the same thread in which +** the SQL function is running. +** +** Requirements: +** [H16272] [H16274] [H16276] [H16277] [H16278] [H16279] +*/ +SQLITE_API void *sqlite3_get_auxdata(sqlite3_context*, int N); +SQLITE_API void sqlite3_set_auxdata(sqlite3_context*, int N, void*, void (*)(void*)); + + +/* +** CAPI3REF: Constants Defining Special Destructor Behavior {H10280} +** +** These are special values for the destructor that is passed in as the +** final argument to routines like [sqlite3_result_blob()]. If the destructor +** argument is SQLITE_STATIC, it means that the content pointer is constant +** and will never change. It does not need to be destroyed. The +** SQLITE_TRANSIENT value means that the content will likely change in +** the near future and that SQLite should make its own private copy of +** the content before returning. +** +** The typedef is necessary to work around problems in certain +** C++ compilers. See ticket #2191. +*/ +typedef void (*sqlite3_destructor_type)(void*); +#define SQLITE_STATIC ((sqlite3_destructor_type)0) +#define SQLITE_TRANSIENT ((sqlite3_destructor_type)-1) + +/* +** CAPI3REF: Setting The Result Of An SQL Function {H16400} +** +** These routines are used by the xFunc or xFinal callbacks that +** implement SQL functions and aggregates. See +** [sqlite3_create_function()] and [sqlite3_create_function16()] +** for additional information. +** +** These functions work very much like the [parameter binding] family of +** functions used to bind values to host parameters in prepared statements. +** Refer to the [SQL parameter] documentation for additional information. +** +** The sqlite3_result_blob() interface sets the result from +** an application-defined function to be the BLOB whose content is pointed +** to by the second parameter and which is N bytes long where N is the +** third parameter. +** +** The sqlite3_result_zeroblob() interfaces set the result of +** the application-defined function to be a BLOB containing all zero +** bytes and N bytes in size, where N is the value of the 2nd parameter. +** +** The sqlite3_result_double() interface sets the result from +** an application-defined function to be a floating point value specified +** by its 2nd argument. +** +** The sqlite3_result_error() and sqlite3_result_error16() functions +** cause the implemented SQL function to throw an exception. +** SQLite uses the string pointed to by the +** 2nd parameter of sqlite3_result_error() or sqlite3_result_error16() +** as the text of an error message. SQLite interprets the error +** message string from sqlite3_result_error() as UTF-8. SQLite +** interprets the string from sqlite3_result_error16() as UTF-16 in native +** byte order. If the third parameter to sqlite3_result_error() +** or sqlite3_result_error16() is negative then SQLite takes as the error +** message all text up through the first zero character. +** If the third parameter to sqlite3_result_error() or +** sqlite3_result_error16() is non-negative then SQLite takes that many +** bytes (not characters) from the 2nd parameter as the error message. +** The sqlite3_result_error() and sqlite3_result_error16() +** routines make a private copy of the error message text before +** they return. Hence, the calling function can deallocate or +** modify the text after they return without harm. +** The sqlite3_result_error_code() function changes the error code +** returned by SQLite as a result of an error in a function. By default, +** the error code is SQLITE_ERROR. A subsequent call to sqlite3_result_error() +** or sqlite3_result_error16() resets the error code to SQLITE_ERROR. +** +** The sqlite3_result_toobig() interface causes SQLite to throw an error +** indicating that a string or BLOB is to long to represent. +** +** The sqlite3_result_nomem() interface causes SQLite to throw an error +** indicating that a memory allocation failed. +** +** The sqlite3_result_int() interface sets the return value +** of the application-defined function to be the 32-bit signed integer +** value given in the 2nd argument. +** The sqlite3_result_int64() interface sets the return value +** of the application-defined function to be the 64-bit signed integer +** value given in the 2nd argument. +** +** The sqlite3_result_null() interface sets the return value +** of the application-defined function to be NULL. +** +** The sqlite3_result_text(), sqlite3_result_text16(), +** sqlite3_result_text16le(), and sqlite3_result_text16be() interfaces +** set the return value of the application-defined function to be +** a text string which is represented as UTF-8, UTF-16 native byte order, +** UTF-16 little endian, or UTF-16 big endian, respectively. +** SQLite takes the text result from the application from +** the 2nd parameter of the sqlite3_result_text* interfaces. +** If the 3rd parameter to the sqlite3_result_text* interfaces +** is negative, then SQLite takes result text from the 2nd parameter +** through the first zero character. +** If the 3rd parameter to the sqlite3_result_text* interfaces +** is non-negative, then as many bytes (not characters) of the text +** pointed to by the 2nd parameter are taken as the application-defined +** function result. +** If the 4th parameter to the sqlite3_result_text* interfaces +** or sqlite3_result_blob is a non-NULL pointer, then SQLite calls that +** function as the destructor on the text or BLOB result when it has +** finished using that result. +** If the 4th parameter to the sqlite3_result_text* interfaces or +** sqlite3_result_blob is the special constant SQLITE_STATIC, then SQLite +** assumes that the text or BLOB result is in constant space and does not +** copy the it or call a destructor when it has finished using that result. +** If the 4th parameter to the sqlite3_result_text* interfaces +** or sqlite3_result_blob is the special constant SQLITE_TRANSIENT +** then SQLite makes a copy of the result into space obtained from +** from [sqlite3_malloc()] before it returns. +** +** The sqlite3_result_value() interface sets the result of +** the application-defined function to be a copy the +** [unprotected sqlite3_value] object specified by the 2nd parameter. The +** sqlite3_result_value() interface makes a copy of the [sqlite3_value] +** so that the [sqlite3_value] specified in the parameter may change or +** be deallocated after sqlite3_result_value() returns without harm. +** A [protected sqlite3_value] object may always be used where an +** [unprotected sqlite3_value] object is required, so either +** kind of [sqlite3_value] object can be used with this interface. +** +** If these routines are called from within the different thread +** than the one containing the application-defined function that received +** the [sqlite3_context] pointer, the results are undefined. +** +** Requirements: +** [H16403] [H16406] [H16409] [H16412] [H16415] [H16418] [H16421] [H16424] +** [H16427] [H16430] [H16433] [H16436] [H16439] [H16442] [H16445] [H16448] +** [H16451] [H16454] [H16457] [H16460] [H16463] +*/ +SQLITE_API void sqlite3_result_blob(sqlite3_context*, const void*, int, void(*)(void*)); +SQLITE_API void sqlite3_result_double(sqlite3_context*, double); +SQLITE_API void sqlite3_result_error(sqlite3_context*, const char*, int); +SQLITE_API void sqlite3_result_error16(sqlite3_context*, const void*, int); +SQLITE_API void sqlite3_result_error_toobig(sqlite3_context*); +SQLITE_API void sqlite3_result_error_nomem(sqlite3_context*); +SQLITE_API void sqlite3_result_error_code(sqlite3_context*, int); +SQLITE_API void sqlite3_result_int(sqlite3_context*, int); +SQLITE_API void sqlite3_result_int64(sqlite3_context*, sqlite3_int64); +SQLITE_API void sqlite3_result_null(sqlite3_context*); +SQLITE_API void sqlite3_result_text(sqlite3_context*, const char*, int, void(*)(void*)); +SQLITE_API void sqlite3_result_text16(sqlite3_context*, const void*, int, void(*)(void*)); +SQLITE_API void sqlite3_result_text16le(sqlite3_context*, const void*, int,void(*)(void*)); +SQLITE_API void sqlite3_result_text16be(sqlite3_context*, const void*, int,void(*)(void*)); +SQLITE_API void sqlite3_result_value(sqlite3_context*, sqlite3_value*); +SQLITE_API void sqlite3_result_zeroblob(sqlite3_context*, int n); + +/* +** CAPI3REF: Define New Collating Sequences {H16600} +** +** These functions are used to add new collation sequences to the +** [database connection] specified as the first argument. +** +** The name of the new collation sequence is specified as a UTF-8 string +** for sqlite3_create_collation() and sqlite3_create_collation_v2() +** and a UTF-16 string for sqlite3_create_collation16(). In all cases +** the name is passed as the second function argument. +** +** The third argument may be one of the constants [SQLITE_UTF8], +** [SQLITE_UTF16LE] or [SQLITE_UTF16BE], indicating that the user-supplied +** routine expects to be passed pointers to strings encoded using UTF-8, +** UTF-16 little-endian, or UTF-16 big-endian, respectively. The +** third argument might also be [SQLITE_UTF16_ALIGNED] to indicate that +** the routine expects pointers to 16-bit word aligned strings +** of UTF-16 in the native byte order of the host computer. +** +** A pointer to the user supplied routine must be passed as the fifth +** argument. If it is NULL, this is the same as deleting the collation +** sequence (so that SQLite cannot call it anymore). +** Each time the application supplied function is invoked, it is passed +** as its first parameter a copy of the void* passed as the fourth argument +** to sqlite3_create_collation() or sqlite3_create_collation16(). +** +** The remaining arguments to the application-supplied routine are two strings, +** each represented by a (length, data) pair and encoded in the encoding +** that was passed as the third argument when the collation sequence was +** registered. {END} The application defined collation routine should +** return negative, zero or positive if the first string is less than, +** equal to, or greater than the second string. i.e. (STRING1 - STRING2). +** +** The sqlite3_create_collation_v2() works like sqlite3_create_collation() +** except that it takes an extra argument which is a destructor for +** the collation. The destructor is called when the collation is +** destroyed and is passed a copy of the fourth parameter void* pointer +** of the sqlite3_create_collation_v2(). +** Collations are destroyed when they are overridden by later calls to the +** collation creation functions or when the [database connection] is closed +** using [sqlite3_close()]. +** +** Requirements: +** [H16603] [H16604] [H16606] [H16609] [H16612] [H16615] [H16618] [H16621] +** [H16624] [H16627] [H16630] +*/ +SQLITE_API int sqlite3_create_collation( + sqlite3*, + const char *zName, + int eTextRep, + void*, + int(*xCompare)(void*,int,const void*,int,const void*) +); +SQLITE_API int sqlite3_create_collation_v2( + sqlite3*, + const char *zName, + int eTextRep, + void*, + int(*xCompare)(void*,int,const void*,int,const void*), + void(*xDestroy)(void*) +); +SQLITE_API int sqlite3_create_collation16( + sqlite3*, + const void *zName, + int eTextRep, + void*, + int(*xCompare)(void*,int,const void*,int,const void*) +); + +/* +** CAPI3REF: Collation Needed Callbacks {H16700} +** +** To avoid having to register all collation sequences before a database +** can be used, a single callback function may be registered with the +** [database connection] to be called whenever an undefined collation +** sequence is required. +** +** If the function is registered using the sqlite3_collation_needed() API, +** then it is passed the names of undefined collation sequences as strings +** encoded in UTF-8. {H16703} If sqlite3_collation_needed16() is used, +** the names are passed as UTF-16 in machine native byte order. +** A call to either function replaces any existing callback. +** +** When the callback is invoked, the first argument passed is a copy +** of the second argument to sqlite3_collation_needed() or +** sqlite3_collation_needed16(). The second argument is the database +** connection. The third argument is one of [SQLITE_UTF8], [SQLITE_UTF16BE], +** or [SQLITE_UTF16LE], indicating the most desirable form of the collation +** sequence function required. The fourth parameter is the name of the +** required collation sequence. +** +** The callback function should register the desired collation using +** [sqlite3_create_collation()], [sqlite3_create_collation16()], or +** [sqlite3_create_collation_v2()]. +** +** Requirements: +** [H16702] [H16704] [H16706] +*/ +SQLITE_API int sqlite3_collation_needed( + sqlite3*, + void*, + void(*)(void*,sqlite3*,int eTextRep,const char*) +); +SQLITE_API int sqlite3_collation_needed16( + sqlite3*, + void*, + void(*)(void*,sqlite3*,int eTextRep,const void*) +); + +/* +** Specify the key for an encrypted database. This routine should be +** called right after sqlite3_open(). +** +** The code to implement this API is not available in the public release +** of SQLite. +*/ +SQLITE_API int sqlite3_key( + sqlite3 *db, /* Database to be rekeyed */ + const void *pKey, int nKey /* The key */ +); + +/* +** Change the key on an open database. If the current database is not +** encrypted, this routine will encrypt it. If pNew==0 or nNew==0, the +** database is decrypted. +** +** The code to implement this API is not available in the public release +** of SQLite. +*/ +SQLITE_API int sqlite3_rekey( + sqlite3 *db, /* Database to be rekeyed */ + const void *pKey, int nKey /* The new key */ +); + +/* +** CAPI3REF: Suspend Execution For A Short Time {H10530} +** +** The sqlite3_sleep() function causes the current thread to suspend execution +** for at least a number of milliseconds specified in its parameter. +** +** If the operating system does not support sleep requests with +** millisecond time resolution, then the time will be rounded up to +** the nearest second. The number of milliseconds of sleep actually +** requested from the operating system is returned. +** +** SQLite implements this interface by calling the xSleep() +** method of the default [sqlite3_vfs] object. +** +** Requirements: [H10533] [H10536] +*/ +SQLITE_API int sqlite3_sleep(int); + +/* +** CAPI3REF: Name Of The Folder Holding Temporary Files {H10310} +** +** If this global variable is made to point to a string which is +** the name of a folder (a.k.a. directory), then all temporary files +** created by SQLite will be placed in that directory. If this variable +** is a NULL pointer, then SQLite performs a search for an appropriate +** temporary file directory. +** +** It is not safe to modify this variable once a [database connection] +** has been opened. It is intended that this variable be set once +** as part of process initialization and before any SQLite interface +** routines have been call and remain unchanged thereafter. +*/ +SQLITE_API char *sqlite3_temp_directory; + +/* +** CAPI3REF: Test For Auto-Commit Mode {H12930} +** KEYWORDS: {autocommit mode} +** +** The sqlite3_get_autocommit() interface returns non-zero or +** zero if the given database connection is or is not in autocommit mode, +** respectively. Autocommit mode is on by default. +** Autocommit mode is disabled by a [BEGIN] statement. +** Autocommit mode is re-enabled by a [COMMIT] or [ROLLBACK]. +** +** If certain kinds of errors occur on a statement within a multi-statement +** transaction (errors including [SQLITE_FULL], [SQLITE_IOERR], +** [SQLITE_NOMEM], [SQLITE_BUSY], and [SQLITE_INTERRUPT]) then the +** transaction might be rolled back automatically. The only way to +** find out whether SQLite automatically rolled back the transaction after +** an error is to use this function. +** +** If another thread changes the autocommit status of the database +** connection while this routine is running, then the return value +** is undefined. +** +** Requirements: [H12931] [H12932] [H12933] [H12934] +*/ +SQLITE_API int sqlite3_get_autocommit(sqlite3*); + +/* +** CAPI3REF: Find The Database Handle Of A Prepared Statement {H13120} +** +** The sqlite3_db_handle interface returns the [database connection] handle +** to which a [prepared statement] belongs. The [database connection] +** returned by sqlite3_db_handle is the same [database connection] that was the first argument +** to the [sqlite3_prepare_v2()] call (or its variants) that was used to +** create the statement in the first place. +** +** Requirements: [H13123] +*/ +SQLITE_API sqlite3 *sqlite3_db_handle(sqlite3_stmt*); + +/* +** CAPI3REF: Find the next prepared statement {H13140} +** +** This interface returns a pointer to the next [prepared statement] after +** pStmt associated with the [database connection] pDb. If pStmt is NULL +** then this interface returns a pointer to the first prepared statement +** associated with the database connection pDb. If no prepared statement +** satisfies the conditions of this routine, it returns NULL. +** +** The [database connection] pointer D in a call to +** [sqlite3_next_stmt(D,S)] must refer to an open database +** connection and in particular must not be a NULL pointer. +** +** Requirements: [H13143] [H13146] [H13149] [H13152] +*/ +SQLITE_API sqlite3_stmt *sqlite3_next_stmt(sqlite3 *pDb, sqlite3_stmt *pStmt); + +/* +** CAPI3REF: Commit And Rollback Notification Callbacks {H12950} +** +** The sqlite3_commit_hook() interface registers a callback +** function to be invoked whenever a transaction is committed. +** Any callback set by a previous call to sqlite3_commit_hook() +** for the same database connection is overridden. +** The sqlite3_rollback_hook() interface registers a callback +** function to be invoked whenever a transaction is committed. +** Any callback set by a previous call to sqlite3_commit_hook() +** for the same database connection is overridden. +** The pArg argument is passed through to the callback. +** If the callback on a commit hook function returns non-zero, +** then the commit is converted into a rollback. +** +** If another function was previously registered, its +** pArg value is returned. Otherwise NULL is returned. +** +** The callback implementation must not do anything that will modify +** the database connection that invoked the callback. Any actions +** to modify the database connection must be deferred until after the +** completion of the [sqlite3_step()] call that triggered the commit +** or rollback hook in the first place. +** Note that [sqlite3_prepare_v2()] and [sqlite3_step()] both modify their +** database connections for the meaning of "modify" in this paragraph. +** +** Registering a NULL function disables the callback. +** +** For the purposes of this API, a transaction is said to have been +** rolled back if an explicit "ROLLBACK" statement is executed, or +** an error or constraint causes an implicit rollback to occur. +** The rollback callback is not invoked if a transaction is +** automatically rolled back because the database connection is closed. +** The rollback callback is not invoked if a transaction is +** rolled back because a commit callback returned non-zero. +** Check on this +** +** Requirements: +** [H12951] [H12952] [H12953] [H12954] [H12955] +** [H12961] [H12962] [H12963] [H12964] +*/ +SQLITE_API void *sqlite3_commit_hook(sqlite3*, int(*)(void*), void*); +SQLITE_API void *sqlite3_rollback_hook(sqlite3*, void(*)(void *), void*); + +/* +** CAPI3REF: Data Change Notification Callbacks {H12970} +** +** The sqlite3_update_hook() interface registers a callback function +** with the [database connection] identified by the first argument +** to be invoked whenever a row is updated, inserted or deleted. +** Any callback set by a previous call to this function +** for the same database connection is overridden. +** +** The second argument is a pointer to the function to invoke when a +** row is updated, inserted or deleted. +** The first argument to the callback is a copy of the third argument +** to sqlite3_update_hook(). +** The second callback argument is one of [SQLITE_INSERT], [SQLITE_DELETE], +** or [SQLITE_UPDATE], depending on the operation that caused the callback +** to be invoked. +** The third and fourth arguments to the callback contain pointers to the +** database and table name containing the affected row. +** The final callback parameter is the [rowid] of the row. +** In the case of an update, this is the [rowid] after the update takes place. +** +** The update hook is not invoked when internal system tables are +** modified (i.e. sqlite_master and sqlite_sequence). +** +** The update hook implementation must not do anything that will modify +** the database connection that invoked the update hook. Any actions +** to modify the database connection must be deferred until after the +** completion of the [sqlite3_step()] call that triggered the update hook. +** Note that [sqlite3_prepare_v2()] and [sqlite3_step()] both modify their +** database connections for the meaning of "modify" in this paragraph. +** +** If another function was previously registered, its pArg value +** is returned. Otherwise NULL is returned. +** +** Requirements: +** [H12971] [H12973] [H12975] [H12977] [H12979] [H12981] [H12983] [H12986] +*/ +SQLITE_API void *sqlite3_update_hook( + sqlite3*, + void(*)(void *,int ,char const *,char const *,sqlite3_int64), + void* +); + +/* +** CAPI3REF: Enable Or Disable Shared Pager Cache {H10330} +** KEYWORDS: {shared cache} {shared cache mode} +** +** This routine enables or disables the sharing of the database cache +** and schema data structures between [database connection | connections] +** to the same database. Sharing is enabled if the argument is true +** and disabled if the argument is false. +** +** Cache sharing is enabled and disabled for an entire process. +** This is a change as of SQLite version 3.5.0. In prior versions of SQLite, +** sharing was enabled or disabled for each thread separately. +** +** The cache sharing mode set by this interface effects all subsequent +** calls to [sqlite3_open()], [sqlite3_open_v2()], and [sqlite3_open16()]. +** Existing database connections continue use the sharing mode +** that was in effect at the time they were opened. +** +** Virtual tables cannot be used with a shared cache. When shared +** cache is enabled, the [sqlite3_create_module()] API used to register +** virtual tables will always return an error. +** +** This routine returns [SQLITE_OK] if shared cache was enabled or disabled +** successfully. An [error code] is returned otherwise. +** +** Shared cache is disabled by default. But this might change in +** future releases of SQLite. Applications that care about shared +** cache setting should set it explicitly. +** +** See Also: [SQLite Shared-Cache Mode] +** +** Requirements: [H10331] [H10336] [H10337] [H10339] +*/ +SQLITE_API int sqlite3_enable_shared_cache(int); + +/* +** CAPI3REF: Attempt To Free Heap Memory {H17340} +** +** The sqlite3_release_memory() interface attempts to free N bytes +** of heap memory by deallocating non-essential memory allocations +** held by the database library. {END} Memory used to cache database +** pages to improve performance is an example of non-essential memory. +** sqlite3_release_memory() returns the number of bytes actually freed, +** which might be more or less than the amount requested. +** +** Requirements: [H17341] [H17342] +*/ +SQLITE_API int sqlite3_release_memory(int); + +/* +** CAPI3REF: Impose A Limit On Heap Size {H17350} +** +** The sqlite3_soft_heap_limit() interface places a "soft" limit +** on the amount of heap memory that may be allocated by SQLite. +** If an internal allocation is requested that would exceed the +** soft heap limit, [sqlite3_release_memory()] is invoked one or +** more times to free up some space before the allocation is performed. +** +** The limit is called "soft", because if [sqlite3_release_memory()] +** cannot free sufficient memory to prevent the limit from being exceeded, +** the memory is allocated anyway and the current operation proceeds. +** +** A negative or zero value for N means that there is no soft heap limit and +** [sqlite3_release_memory()] will only be called when memory is exhausted. +** The default value for the soft heap limit is zero. +** +** SQLite makes a best effort to honor the soft heap limit. +** But if the soft heap limit cannot be honored, execution will +** continue without error or notification. This is why the limit is +** called a "soft" limit. It is advisory only. +** +** Prior to SQLite version 3.5.0, this routine only constrained the memory +** allocated by a single thread - the same thread in which this routine +** runs. Beginning with SQLite version 3.5.0, the soft heap limit is +** applied to all threads. The value specified for the soft heap limit +** is an upper bound on the total memory allocation for all threads. In +** version 3.5.0 there is no mechanism for limiting the heap usage for +** individual threads. +** +** Requirements: +** [H16351] [H16352] [H16353] [H16354] [H16355] [H16358] +*/ +SQLITE_API void sqlite3_soft_heap_limit(int); + +/* +** CAPI3REF: Extract Metadata About A Column Of A Table {H12850} +** +** This routine returns metadata about a specific column of a specific +** database table accessible using the [database connection] handle +** passed as the first function argument. +** +** The column is identified by the second, third and fourth parameters to +** this function. The second parameter is either the name of the database +** (i.e. "main", "temp" or an attached database) containing the specified +** table or NULL. If it is NULL, then all attached databases are searched +** for the table using the same algorithm used by the database engine to +** resolve unqualified table references. +** +** The third and fourth parameters to this function are the table and column +** name of the desired column, respectively. Neither of these parameters +** may be NULL. +** +** Metadata is returned by writing to the memory locations passed as the 5th +** and subsequent parameters to this function. Any of these arguments may be +** NULL, in which case the corresponding element of metadata is omitted. +** +**
+** +**
Parameter Output
Type
Description +** +**
5th const char* Data type +**
6th const char* Name of default collation sequence +**
7th int True if column has a NOT NULL constraint +**
8th int True if column is part of the PRIMARY KEY +**
9th int True if column is [AUTOINCREMENT] +**
+**
+** +** The memory pointed to by the character pointers returned for the +** declaration type and collation sequence is valid only until the next +** call to any SQLite API function. +** +** If the specified table is actually a view, an [error code] is returned. +** +** If the specified column is "rowid", "oid" or "_rowid_" and an +** [INTEGER PRIMARY KEY] column has been explicitly declared, then the output +** parameters are set for the explicitly declared column. If there is no +** explicitly declared [INTEGER PRIMARY KEY] column, then the output +** parameters are set as follows: +** +**
+**     data type: "INTEGER"
+**     collation sequence: "BINARY"
+**     not null: 0
+**     primary key: 1
+**     auto increment: 0
+** 
+** +** This function may load one or more schemas from database files. If an +** error occurs during this process, or if the requested table or column +** cannot be found, an [error code] is returned and an error message left +** in the [database connection] (to be retrieved using sqlite3_errmsg()). +** +** This API is only available if the library was compiled with the +** [SQLITE_ENABLE_COLUMN_METADATA] C-preprocessor symbol defined. +*/ +SQLITE_API int sqlite3_table_column_metadata( + sqlite3 *db, /* Connection handle */ + const char *zDbName, /* Database name or NULL */ + const char *zTableName, /* Table name */ + const char *zColumnName, /* Column name */ + char const **pzDataType, /* OUTPUT: Declared data type */ + char const **pzCollSeq, /* OUTPUT: Collation sequence name */ + int *pNotNull, /* OUTPUT: True if NOT NULL constraint exists */ + int *pPrimaryKey, /* OUTPUT: True if column part of PK */ + int *pAutoinc /* OUTPUT: True if column is auto-increment */ +); + +/* +** CAPI3REF: Load An Extension {H12600} +** +** This interface loads an SQLite extension library from the named file. +** +** {H12601} The sqlite3_load_extension() interface attempts to load an +** SQLite extension library contained in the file zFile. +** +** {H12602} The entry point is zProc. +** +** {H12603} zProc may be 0, in which case the name of the entry point +** defaults to "sqlite3_extension_init". +** +** {H12604} The sqlite3_load_extension() interface shall return +** [SQLITE_OK] on success and [SQLITE_ERROR] if something goes wrong. +** +** {H12605} If an error occurs and pzErrMsg is not 0, then the +** [sqlite3_load_extension()] interface shall attempt to +** fill *pzErrMsg with error message text stored in memory +** obtained from [sqlite3_malloc()]. {END} The calling function +** should free this memory by calling [sqlite3_free()]. +** +** {H12606} Extension loading must be enabled using +** [sqlite3_enable_load_extension()] prior to calling this API, +** otherwise an error will be returned. +*/ +SQLITE_API int sqlite3_load_extension( + sqlite3 *db, /* Load the extension into this database connection */ + const char *zFile, /* Name of the shared library containing extension */ + const char *zProc, /* Entry point. Derived from zFile if 0 */ + char **pzErrMsg /* Put error message here if not 0 */ +); + +/* +** CAPI3REF: Enable Or Disable Extension Loading {H12620} +** +** So as not to open security holes in older applications that are +** unprepared to deal with extension loading, and as a means of disabling +** extension loading while evaluating user-entered SQL, the following API +** is provided to turn the [sqlite3_load_extension()] mechanism on and off. +** +** Extension loading is off by default. See ticket #1863. +** +** {H12621} Call the sqlite3_enable_load_extension() routine with onoff==1 +** to turn extension loading on and call it with onoff==0 to turn +** it back off again. +** +** {H12622} Extension loading is off by default. +*/ +SQLITE_API int sqlite3_enable_load_extension(sqlite3 *db, int onoff); + +/* +** CAPI3REF: Automatically Load An Extensions {H12640} +** +** This API can be invoked at program startup in order to register +** one or more statically linked extensions that will be available +** to all new [database connections]. {END} +** +** This routine stores a pointer to the extension in an array that is +** obtained from [sqlite3_malloc()]. If you run a memory leak checker +** on your program and it reports a leak because of this array, invoke +** [sqlite3_reset_auto_extension()] prior to shutdown to free the memory. +** +** {H12641} This function registers an extension entry point that is +** automatically invoked whenever a new [database connection] +** is opened using [sqlite3_open()], [sqlite3_open16()], +** or [sqlite3_open_v2()]. +** +** {H12642} Duplicate extensions are detected so calling this routine +** multiple times with the same extension is harmless. +** +** {H12643} This routine stores a pointer to the extension in an array +** that is obtained from [sqlite3_malloc()]. +** +** {H12644} Automatic extensions apply across all threads. +*/ +SQLITE_API int sqlite3_auto_extension(void (*xEntryPoint)(void)); + +/* +** CAPI3REF: Reset Automatic Extension Loading {H12660} +** +** This function disables all previously registered automatic +** extensions. {END} It undoes the effect of all prior +** [sqlite3_auto_extension()] calls. +** +** {H12661} This function disables all previously registered +** automatic extensions. +** +** {H12662} This function disables automatic extensions in all threads. +*/ +SQLITE_API void sqlite3_reset_auto_extension(void); + +/* +****** EXPERIMENTAL - subject to change without notice ************** +** +** The interface to the virtual-table mechanism is currently considered +** to be experimental. The interface might change in incompatible ways. +** If this is a problem for you, do not use the interface at this time. +** +** When the virtual-table mechanism stabilizes, we will declare the +** interface fixed, support it indefinitely, and remove this comment. +*/ + +/* +** Structures used by the virtual table interface +*/ +typedef struct sqlite3_vtab sqlite3_vtab; +typedef struct sqlite3_index_info sqlite3_index_info; +typedef struct sqlite3_vtab_cursor sqlite3_vtab_cursor; +typedef struct sqlite3_module sqlite3_module; + +/* +** CAPI3REF: Virtual Table Object {H18000} +** KEYWORDS: sqlite3_module +** EXPERIMENTAL +** +** A module is a class of virtual tables. Each module is defined +** by an instance of the following structure. This structure consists +** mostly of methods for the module. +** +** This interface is experimental and is subject to change or +** removal in future releases of SQLite. +*/ +struct sqlite3_module { + int iVersion; + int (*xCreate)(sqlite3*, void *pAux, + int argc, const char *const*argv, + sqlite3_vtab **ppVTab, char**); + int (*xConnect)(sqlite3*, void *pAux, + int argc, const char *const*argv, + sqlite3_vtab **ppVTab, char**); + int (*xBestIndex)(sqlite3_vtab *pVTab, sqlite3_index_info*); + int (*xDisconnect)(sqlite3_vtab *pVTab); + int (*xDestroy)(sqlite3_vtab *pVTab); + int (*xOpen)(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor); + int (*xClose)(sqlite3_vtab_cursor*); + int (*xFilter)(sqlite3_vtab_cursor*, int idxNum, const char *idxStr, + int argc, sqlite3_value **argv); + int (*xNext)(sqlite3_vtab_cursor*); + int (*xEof)(sqlite3_vtab_cursor*); + int (*xColumn)(sqlite3_vtab_cursor*, sqlite3_context*, int); + int (*xRowid)(sqlite3_vtab_cursor*, sqlite3_int64 *pRowid); + int (*xUpdate)(sqlite3_vtab *, int, sqlite3_value **, sqlite3_int64 *); + int (*xBegin)(sqlite3_vtab *pVTab); + int (*xSync)(sqlite3_vtab *pVTab); + int (*xCommit)(sqlite3_vtab *pVTab); + int (*xRollback)(sqlite3_vtab *pVTab); + int (*xFindFunction)(sqlite3_vtab *pVtab, int nArg, const char *zName, + void (**pxFunc)(sqlite3_context*,int,sqlite3_value**), + void **ppArg); + int (*xRename)(sqlite3_vtab *pVtab, const char *zNew); +}; + +/* +** CAPI3REF: Virtual Table Indexing Information {H18100} +** KEYWORDS: sqlite3_index_info +** EXPERIMENTAL +** +** The sqlite3_index_info structure and its substructures is used to +** pass information into and receive the reply from the xBestIndex +** method of an sqlite3_module. The fields under **Inputs** are the +** inputs to xBestIndex and are read-only. xBestIndex inserts its +** results into the **Outputs** fields. +** +** The aConstraint[] array records WHERE clause constraints of the form: +** +**
column OP expr
+** +** where OP is =, <, <=, >, or >=. The particular operator is +** stored in aConstraint[].op. The index of the column is stored in +** aConstraint[].iColumn. aConstraint[].usable is TRUE if the +** expr on the right-hand side can be evaluated (and thus the constraint +** is usable) and false if it cannot. +** +** The optimizer automatically inverts terms of the form "expr OP column" +** and makes other simplifications to the WHERE clause in an attempt to +** get as many WHERE clause terms into the form shown above as possible. +** The aConstraint[] array only reports WHERE clause terms in the correct +** form that refer to the particular virtual table being queried. +** +** Information about the ORDER BY clause is stored in aOrderBy[]. +** Each term of aOrderBy records a column of the ORDER BY clause. +** +** The xBestIndex method must fill aConstraintUsage[] with information +** about what parameters to pass to xFilter. If argvIndex>0 then +** the right-hand side of the corresponding aConstraint[] is evaluated +** and becomes the argvIndex-th entry in argv. If aConstraintUsage[].omit +** is true, then the constraint is assumed to be fully handled by the +** virtual table and is not checked again by SQLite. +** +** The idxNum and idxPtr values are recorded and passed into xFilter. +** sqlite3_free() is used to free idxPtr if needToFreeIdxPtr is true. +** +** The orderByConsumed means that output from xFilter will occur in +** the correct order to satisfy the ORDER BY clause so that no separate +** sorting step is required. +** +** The estimatedCost value is an estimate of the cost of doing the +** particular lookup. A full scan of a table with N entries should have +** a cost of N. A binary search of a table of N entries should have a +** cost of approximately log(N). +** +** This interface is experimental and is subject to change or +** removal in future releases of SQLite. +*/ +struct sqlite3_index_info { + /* Inputs */ + int nConstraint; /* Number of entries in aConstraint */ + struct sqlite3_index_constraint { + int iColumn; /* Column on left-hand side of constraint */ + unsigned char op; /* Constraint operator */ + unsigned char usable; /* True if this constraint is usable */ + int iTermOffset; /* Used internally - xBestIndex should ignore */ + } *aConstraint; /* Table of WHERE clause constraints */ + int nOrderBy; /* Number of terms in the ORDER BY clause */ + struct sqlite3_index_orderby { + int iColumn; /* Column number */ + unsigned char desc; /* True for DESC. False for ASC. */ + } *aOrderBy; /* The ORDER BY clause */ + /* Outputs */ + struct sqlite3_index_constraint_usage { + int argvIndex; /* if >0, constraint is part of argv to xFilter */ + unsigned char omit; /* Do not code a test for this constraint */ + } *aConstraintUsage; + int idxNum; /* Number used to identify the index */ + char *idxStr; /* String, possibly obtained from sqlite3_malloc */ + int needToFreeIdxStr; /* Free idxStr using sqlite3_free() if true */ + int orderByConsumed; /* True if output is already ordered */ + double estimatedCost; /* Estimated cost of using this index */ +}; +#define SQLITE_INDEX_CONSTRAINT_EQ 2 +#define SQLITE_INDEX_CONSTRAINT_GT 4 +#define SQLITE_INDEX_CONSTRAINT_LE 8 +#define SQLITE_INDEX_CONSTRAINT_LT 16 +#define SQLITE_INDEX_CONSTRAINT_GE 32 +#define SQLITE_INDEX_CONSTRAINT_MATCH 64 + +/* +** CAPI3REF: Register A Virtual Table Implementation {H18200} +** EXPERIMENTAL +** +** This routine is used to register a new module name with a +** [database connection]. Module names must be registered before +** creating new virtual tables on the module, or before using +** preexisting virtual tables of the module. +** +** This interface is experimental and is subject to change or +** removal in future releases of SQLite. +*/ +SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_create_module( + sqlite3 *db, /* SQLite connection to register module with */ + const char *zName, /* Name of the module */ + const sqlite3_module *, /* Methods for the module */ + void * /* Client data for xCreate/xConnect */ +); + +/* +** CAPI3REF: Register A Virtual Table Implementation {H18210} +** EXPERIMENTAL +** +** This routine is identical to the [sqlite3_create_module()] method above, +** except that it allows a destructor function to be specified. It is +** even more experimental than the rest of the virtual tables API. +*/ +SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_create_module_v2( + sqlite3 *db, /* SQLite connection to register module with */ + const char *zName, /* Name of the module */ + const sqlite3_module *, /* Methods for the module */ + void *, /* Client data for xCreate/xConnect */ + void(*xDestroy)(void*) /* Module destructor function */ +); + +/* +** CAPI3REF: Virtual Table Instance Object {H18010} +** KEYWORDS: sqlite3_vtab +** EXPERIMENTAL +** +** Every module implementation uses a subclass of the following structure +** to describe a particular instance of the module. Each subclass will +** be tailored to the specific needs of the module implementation. +** The purpose of this superclass is to define certain fields that are +** common to all module implementations. +** +** Virtual tables methods can set an error message by assigning a +** string obtained from [sqlite3_mprintf()] to zErrMsg. The method should +** take care that any prior string is freed by a call to [sqlite3_free()] +** prior to assigning a new string to zErrMsg. After the error message +** is delivered up to the client application, the string will be automatically +** freed by sqlite3_free() and the zErrMsg field will be zeroed. Note +** that sqlite3_mprintf() and sqlite3_free() are used on the zErrMsg field +** since virtual tables are commonly implemented in loadable extensions which +** do not have access to sqlite3MPrintf() or sqlite3Free(). +** +** This interface is experimental and is subject to change or +** removal in future releases of SQLite. +*/ +struct sqlite3_vtab { + const sqlite3_module *pModule; /* The module for this virtual table */ + int nRef; /* Used internally */ + char *zErrMsg; /* Error message from sqlite3_mprintf() */ + /* Virtual table implementations will typically add additional fields */ +}; + +/* +** CAPI3REF: Virtual Table Cursor Object {H18020} +** KEYWORDS: sqlite3_vtab_cursor +** EXPERIMENTAL +** +** Every module implementation uses a subclass of the following structure +** to describe cursors that point into the virtual table and are used +** to loop through the virtual table. Cursors are created using the +** xOpen method of the module. Each module implementation will define +** the content of a cursor structure to suit its own needs. +** +** This superclass exists in order to define fields of the cursor that +** are common to all implementations. +** +** This interface is experimental and is subject to change or +** removal in future releases of SQLite. +*/ +struct sqlite3_vtab_cursor { + sqlite3_vtab *pVtab; /* Virtual table of this cursor */ + /* Virtual table implementations will typically add additional fields */ +}; + +/* +** CAPI3REF: Declare The Schema Of A Virtual Table {H18280} +** EXPERIMENTAL +** +** The xCreate and xConnect methods of a module use the following API +** to declare the format (the names and datatypes of the columns) of +** the virtual tables they implement. +** +** This interface is experimental and is subject to change or +** removal in future releases of SQLite. +*/ +SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_declare_vtab(sqlite3*, const char *zCreateTable); + +/* +** CAPI3REF: Overload A Function For A Virtual Table {H18300} +** EXPERIMENTAL +** +** Virtual tables can provide alternative implementations of functions +** using the xFindFunction method. But global versions of those functions +** must exist in order to be overloaded. +** +** This API makes sure a global version of a function with a particular +** name and number of parameters exists. If no such function exists +** before this API is called, a new function is created. The implementation +** of the new function always causes an exception to be thrown. So +** the new function is not good for anything by itself. Its only +** purpose is to be a placeholder function that can be overloaded +** by virtual tables. +** +** This API should be considered part of the virtual table interface, +** which is experimental and subject to change. +*/ +SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_overload_function(sqlite3*, const char *zFuncName, int nArg); + +/* +** The interface to the virtual-table mechanism defined above (back up +** to a comment remarkably similar to this one) is currently considered +** to be experimental. The interface might change in incompatible ways. +** If this is a problem for you, do not use the interface at this time. +** +** When the virtual-table mechanism stabilizes, we will declare the +** interface fixed, support it indefinitely, and remove this comment. +** +****** EXPERIMENTAL - subject to change without notice ************** +*/ + +/* +** CAPI3REF: A Handle To An Open BLOB {H17800} +** KEYWORDS: {BLOB handle} {BLOB handles} +** +** An instance of this object represents an open BLOB on which +** [sqlite3_blob_open | incremental BLOB I/O] can be performed. +** Objects of this type are created by [sqlite3_blob_open()] +** and destroyed by [sqlite3_blob_close()]. +** The [sqlite3_blob_read()] and [sqlite3_blob_write()] interfaces +** can be used to read or write small subsections of the BLOB. +** The [sqlite3_blob_bytes()] interface returns the size of the BLOB in bytes. +*/ +typedef struct sqlite3_blob sqlite3_blob; + +/* +** CAPI3REF: Open A BLOB For Incremental I/O {H17810} +** +** This interfaces opens a [BLOB handle | handle] to the BLOB located +** in row iRow, column zColumn, table zTable in database zDb; +** in other words, the same BLOB that would be selected by: +** +**
+**     SELECT zColumn FROM zDb.zTable WHERE [rowid] = iRow;
+** 
{END} +** +** If the flags parameter is non-zero, the the BLOB is opened for read +** and write access. If it is zero, the BLOB is opened for read access. +** +** Note that the database name is not the filename that contains +** the database but rather the symbolic name of the database that +** is assigned when the database is connected using [ATTACH]. +** For the main database file, the database name is "main". +** For TEMP tables, the database name is "temp". +** +** On success, [SQLITE_OK] is returned and the new [BLOB handle] is written +** to *ppBlob. Otherwise an [error code] is returned and any value written +** to *ppBlob should not be used by the caller. +** This function sets the [database connection] error code and message +** accessible via [sqlite3_errcode()] and [sqlite3_errmsg()]. +** +** If the row that a BLOB handle points to is modified by an +** [UPDATE], [DELETE], or by [ON CONFLICT] side-effects +** then the BLOB handle is marked as "expired". +** This is true if any column of the row is changed, even a column +** other than the one the BLOB handle is open on. +** Calls to [sqlite3_blob_read()] and [sqlite3_blob_write()] for +** a expired BLOB handle fail with an return code of [SQLITE_ABORT]. +** Changes written into a BLOB prior to the BLOB expiring are not +** rollback by the expiration of the BLOB. Such changes will eventually +** commit if the transaction continues to completion. +** +** Requirements: +** [H17813] [H17814] [H17816] [H17819] [H17821] [H17824] +*/ +SQLITE_API int sqlite3_blob_open( + sqlite3*, + const char *zDb, + const char *zTable, + const char *zColumn, + sqlite3_int64 iRow, + int flags, + sqlite3_blob **ppBlob +); + +/* +** CAPI3REF: Close A BLOB Handle {H17830} +** +** Closes an open [BLOB handle]. +** +** Closing a BLOB shall cause the current transaction to commit +** if there are no other BLOBs, no pending prepared statements, and the +** database connection is in [autocommit mode]. +** If any writes were made to the BLOB, they might be held in cache +** until the close operation if they will fit. {END} +** +** Closing the BLOB often forces the changes +** out to disk and so if any I/O errors occur, they will likely occur +** at the time when the BLOB is closed. {H17833} Any errors that occur during +** closing are reported as a non-zero return value. +** +** The BLOB is closed unconditionally. Even if this routine returns +** an error code, the BLOB is still closed. +** +** Requirements: +** [H17833] [H17836] [H17839] +*/ +SQLITE_API int sqlite3_blob_close(sqlite3_blob *); + +/* +** CAPI3REF: Return The Size Of An Open BLOB {H17840} +** +** Returns the size in bytes of the BLOB accessible via the open +** []BLOB handle] in its only argument. +** +** Requirements: +** [H17843] +*/ +SQLITE_API int sqlite3_blob_bytes(sqlite3_blob *); + +/* +** CAPI3REF: Read Data From A BLOB Incrementally {H17850} +** +** This function is used to read data from an open [BLOB handle] into a +** caller-supplied buffer. N bytes of data are copied into buffer Z +** from the open BLOB, starting at offset iOffset. +** +** If offset iOffset is less than N bytes from the end of the BLOB, +** [SQLITE_ERROR] is returned and no data is read. If N or iOffset is +** less than zero, [SQLITE_ERROR] is returned and no data is read. +** +** An attempt to read from an expired [BLOB handle] fails with an +** error code of [SQLITE_ABORT]. +** +** On success, SQLITE_OK is returned. +** Otherwise, an [error code] or an [extended error code] is returned. +** +** Requirements: +** [H17853] [H17856] [H17859] [H17862] [H17863] [H17865] [H17868] +*/ +SQLITE_API int sqlite3_blob_read(sqlite3_blob *, void *Z, int N, int iOffset); + +/* +** CAPI3REF: Write Data Into A BLOB Incrementally {H17870} +** +** This function is used to write data into an open [BLOB handle] from a +** caller-supplied buffer. N bytes of data are copied from the buffer Z +** into the open BLOB, starting at offset iOffset. +** +** If the [BLOB handle] passed as the first argument was not opened for +** writing (the flags parameter to [sqlite3_blob_open()] was zero), +** this function returns [SQLITE_READONLY]. +** +** This function may only modify the contents of the BLOB; it is +** not possible to increase the size of a BLOB using this API. +** If offset iOffset is less than N bytes from the end of the BLOB, +** [SQLITE_ERROR] is returned and no data is written. If N is +** less than zero [SQLITE_ERROR] is returned and no data is written. +** +** An attempt to write to an expired [BLOB handle] fails with an +** error code of [SQLITE_ABORT]. Writes to the BLOB that occurred +** before the [BLOB handle] expired are not rolled back by the +** expiration of the handle, though of course those changes might +** have been overwritten by the statement that expired the BLOB handle +** or by other independent statements. +** +** On success, SQLITE_OK is returned. +** Otherwise, an [error code] or an [extended error code] is returned. +** +** Requirements: +** [H17873] [H17874] [H17875] [H17876] [H17877] [H17879] [H17882] [H17885] +** [H17888] +*/ +SQLITE_API int sqlite3_blob_write(sqlite3_blob *, const void *z, int n, int iOffset); + +/* +** CAPI3REF: Virtual File System Objects {H11200} +** +** A virtual filesystem (VFS) is an [sqlite3_vfs] object +** that SQLite uses to interact +** with the underlying operating system. Most SQLite builds come with a +** single default VFS that is appropriate for the host computer. +** New VFSes can be registered and existing VFSes can be unregistered. +** The following interfaces are provided. +** +** The sqlite3_vfs_find() interface returns a pointer to a VFS given its name. +** Names are case sensitive. +** Names are zero-terminated UTF-8 strings. +** If there is no match, a NULL pointer is returned. +** If zVfsName is NULL then the default VFS is returned. +** +** New VFSes are registered with sqlite3_vfs_register(). +** Each new VFS becomes the default VFS if the makeDflt flag is set. +** The same VFS can be registered multiple times without injury. +** To make an existing VFS into the default VFS, register it again +** with the makeDflt flag set. If two different VFSes with the +** same name are registered, the behavior is undefined. If a +** VFS is registered with a name that is NULL or an empty string, +** then the behavior is undefined. +** +** Unregister a VFS with the sqlite3_vfs_unregister() interface. +** If the default VFS is unregistered, another VFS is chosen as +** the default. The choice for the new VFS is arbitrary. +** +** Requirements: +** [H11203] [H11206] [H11209] [H11212] [H11215] [H11218] +*/ +SQLITE_API sqlite3_vfs *sqlite3_vfs_find(const char *zVfsName); +SQLITE_API int sqlite3_vfs_register(sqlite3_vfs*, int makeDflt); +SQLITE_API int sqlite3_vfs_unregister(sqlite3_vfs*); + +/* +** CAPI3REF: Mutexes {H17000} +** +** The SQLite core uses these routines for thread +** synchronization. Though they are intended for internal +** use by SQLite, code that links against SQLite is +** permitted to use any of these routines. +** +** The SQLite source code contains multiple implementations +** of these mutex routines. An appropriate implementation +** is selected automatically at compile-time. The following +** implementations are available in the SQLite core: +** +**
    +**
  • SQLITE_MUTEX_OS2 +**
  • SQLITE_MUTEX_PTHREAD +**
  • SQLITE_MUTEX_W32 +**
  • SQLITE_MUTEX_NOOP +**
+** +** The SQLITE_MUTEX_NOOP implementation is a set of routines +** that does no real locking and is appropriate for use in +** a single-threaded application. The SQLITE_MUTEX_OS2, +** SQLITE_MUTEX_PTHREAD, and SQLITE_MUTEX_W32 implementations +** are appropriate for use on OS/2, Unix, and Windows. +** +** If SQLite is compiled with the SQLITE_MUTEX_APPDEF preprocessor +** macro defined (with "-DSQLITE_MUTEX_APPDEF=1"), then no mutex +** implementation is included with the library. In this case the +** application must supply a custom mutex implementation using the +** [SQLITE_CONFIG_MUTEX] option of the sqlite3_config() function +** before calling sqlite3_initialize() or any other public sqlite3_ +** function that calls sqlite3_initialize(). +** +** {H17011} The sqlite3_mutex_alloc() routine allocates a new +** mutex and returns a pointer to it. {H17012} If it returns NULL +** that means that a mutex could not be allocated. {H17013} SQLite +** will unwind its stack and return an error. {H17014} The argument +** to sqlite3_mutex_alloc() is one of these integer constants: +** +**
    +**
  • SQLITE_MUTEX_FAST +**
  • SQLITE_MUTEX_RECURSIVE +**
  • SQLITE_MUTEX_STATIC_MASTER +**
  • SQLITE_MUTEX_STATIC_MEM +**
  • SQLITE_MUTEX_STATIC_MEM2 +**
  • SQLITE_MUTEX_STATIC_PRNG +**
  • SQLITE_MUTEX_STATIC_LRU +**
  • SQLITE_MUTEX_STATIC_LRU2 +**
+** +** {H17015} The first two constants cause sqlite3_mutex_alloc() to create +** a new mutex. The new mutex is recursive when SQLITE_MUTEX_RECURSIVE +** is used but not necessarily so when SQLITE_MUTEX_FAST is used. {END} +** The mutex implementation does not need to make a distinction +** between SQLITE_MUTEX_RECURSIVE and SQLITE_MUTEX_FAST if it does +** not want to. {H17016} But SQLite will only request a recursive mutex in +** cases where it really needs one. {END} If a faster non-recursive mutex +** implementation is available on the host platform, the mutex subsystem +** might return such a mutex in response to SQLITE_MUTEX_FAST. +** +** {H17017} The other allowed parameters to sqlite3_mutex_alloc() each return +** a pointer to a static preexisting mutex. {END} Four static mutexes are +** used by the current version of SQLite. Future versions of SQLite +** may add additional static mutexes. Static mutexes are for internal +** use by SQLite only. Applications that use SQLite mutexes should +** use only the dynamic mutexes returned by SQLITE_MUTEX_FAST or +** SQLITE_MUTEX_RECURSIVE. +** +** {H17018} Note that if one of the dynamic mutex parameters (SQLITE_MUTEX_FAST +** or SQLITE_MUTEX_RECURSIVE) is used then sqlite3_mutex_alloc() +** returns a different mutex on every call. {H17034} But for the static +** mutex types, the same mutex is returned on every call that has +** the same type number. +** +** {H17019} The sqlite3_mutex_free() routine deallocates a previously +** allocated dynamic mutex. {H17020} SQLite is careful to deallocate every +** dynamic mutex that it allocates. {A17021} The dynamic mutexes must not be in +** use when they are deallocated. {A17022} Attempting to deallocate a static +** mutex results in undefined behavior. {H17023} SQLite never deallocates +** a static mutex. {END} +** +** The sqlite3_mutex_enter() and sqlite3_mutex_try() routines attempt +** to enter a mutex. {H17024} If another thread is already within the mutex, +** sqlite3_mutex_enter() will block and sqlite3_mutex_try() will return +** SQLITE_BUSY. {H17025} The sqlite3_mutex_try() interface returns [SQLITE_OK] +** upon successful entry. {H17026} Mutexes created using +** SQLITE_MUTEX_RECURSIVE can be entered multiple times by the same thread. +** {H17027} In such cases the, +** mutex must be exited an equal number of times before another thread +** can enter. {A17028} If the same thread tries to enter any other +** kind of mutex more than once, the behavior is undefined. +** {H17029} SQLite will never exhibit +** such behavior in its own use of mutexes. +** +** Some systems (for example, Windows 95) do not support the operation +** implemented by sqlite3_mutex_try(). On those systems, sqlite3_mutex_try() +** will always return SQLITE_BUSY. {H17030} The SQLite core only ever uses +** sqlite3_mutex_try() as an optimization so this is acceptable behavior. +** +** {H17031} The sqlite3_mutex_leave() routine exits a mutex that was +** previously entered by the same thread. {A17032} The behavior +** is undefined if the mutex is not currently entered by the +** calling thread or is not currently allocated. {H17033} SQLite will +** never do either. {END} +** +** If the argument to sqlite3_mutex_enter(), sqlite3_mutex_try(), or +** sqlite3_mutex_leave() is a NULL pointer, then all three routines +** behave as no-ops. +** +** See also: [sqlite3_mutex_held()] and [sqlite3_mutex_notheld()]. +*/ +SQLITE_API sqlite3_mutex *sqlite3_mutex_alloc(int); +SQLITE_API void sqlite3_mutex_free(sqlite3_mutex*); +SQLITE_API void sqlite3_mutex_enter(sqlite3_mutex*); +SQLITE_API int sqlite3_mutex_try(sqlite3_mutex*); +SQLITE_API void sqlite3_mutex_leave(sqlite3_mutex*); + +/* +** CAPI3REF: Mutex Methods Object {H17120} +** EXPERIMENTAL +** +** An instance of this structure defines the low-level routines +** used to allocate and use mutexes. +** +** Usually, the default mutex implementations provided by SQLite are +** sufficient, however the user has the option of substituting a custom +** implementation for specialized deployments or systems for which SQLite +** does not provide a suitable implementation. In this case, the user +** creates and populates an instance of this structure to pass +** to sqlite3_config() along with the [SQLITE_CONFIG_MUTEX] option. +** Additionally, an instance of this structure can be used as an +** output variable when querying the system for the current mutex +** implementation, using the [SQLITE_CONFIG_GETMUTEX] option. +** +** The xMutexInit method defined by this structure is invoked as +** part of system initialization by the sqlite3_initialize() function. +** {H17001} The xMutexInit routine shall be called by SQLite once for each +** effective call to [sqlite3_initialize()]. +** +** The xMutexEnd method defined by this structure is invoked as +** part of system shutdown by the sqlite3_shutdown() function. The +** implementation of this method is expected to release all outstanding +** resources obtained by the mutex methods implementation, especially +** those obtained by the xMutexInit method. {H17003} The xMutexEnd() +** interface shall be invoked once for each call to [sqlite3_shutdown()]. +** +** The remaining seven methods defined by this structure (xMutexAlloc, +** xMutexFree, xMutexEnter, xMutexTry, xMutexLeave, xMutexHeld and +** xMutexNotheld) implement the following interfaces (respectively): +** +**
    +**
  • [sqlite3_mutex_alloc()]
  • +**
  • [sqlite3_mutex_free()]
  • +**
  • [sqlite3_mutex_enter()]
  • +**
  • [sqlite3_mutex_try()]
  • +**
  • [sqlite3_mutex_leave()]
  • +**
  • [sqlite3_mutex_held()]
  • +**
  • [sqlite3_mutex_notheld()]
  • +**
+** +** The only difference is that the public sqlite3_XXX functions enumerated +** above silently ignore any invocations that pass a NULL pointer instead +** of a valid mutex handle. The implementations of the methods defined +** by this structure are not required to handle this case, the results +** of passing a NULL pointer instead of a valid mutex handle are undefined +** (i.e. it is acceptable to provide an implementation that segfaults if +** it is passed a NULL pointer). +*/ +typedef struct sqlite3_mutex_methods sqlite3_mutex_methods; +struct sqlite3_mutex_methods { + int (*xMutexInit)(void); + int (*xMutexEnd)(void); + sqlite3_mutex *(*xMutexAlloc)(int); + void (*xMutexFree)(sqlite3_mutex *); + void (*xMutexEnter)(sqlite3_mutex *); + int (*xMutexTry)(sqlite3_mutex *); + void (*xMutexLeave)(sqlite3_mutex *); + int (*xMutexHeld)(sqlite3_mutex *); + int (*xMutexNotheld)(sqlite3_mutex *); +}; + +/* +** CAPI3REF: Mutex Verification Routines {H17080} +** +** The sqlite3_mutex_held() and sqlite3_mutex_notheld() routines +** are intended for use inside assert() statements. {H17081} The SQLite core +** never uses these routines except inside an assert() and applications +** are advised to follow the lead of the core. {H17082} The core only +** provides implementations for these routines when it is compiled +** with the SQLITE_DEBUG flag. {A17087} External mutex implementations +** are only required to provide these routines if SQLITE_DEBUG is +** defined and if NDEBUG is not defined. +** +** {H17083} These routines should return true if the mutex in their argument +** is held or not held, respectively, by the calling thread. +** +** {X17084} The implementation is not required to provided versions of these +** routines that actually work. If the implementation does not provide working +** versions of these routines, it should at least provide stubs that always +** return true so that one does not get spurious assertion failures. +** +** {H17085} If the argument to sqlite3_mutex_held() is a NULL pointer then +** the routine should return 1. {END} This seems counter-intuitive since +** clearly the mutex cannot be held if it does not exist. But the +** the reason the mutex does not exist is because the build is not +** using mutexes. And we do not want the assert() containing the +** call to sqlite3_mutex_held() to fail, so a non-zero return is +** the appropriate thing to do. {H17086} The sqlite3_mutex_notheld() +** interface should also return 1 when given a NULL pointer. +*/ +SQLITE_API int sqlite3_mutex_held(sqlite3_mutex*); +SQLITE_API int sqlite3_mutex_notheld(sqlite3_mutex*); + +/* +** CAPI3REF: Mutex Types {H17001} +** +** The [sqlite3_mutex_alloc()] interface takes a single argument +** which is one of these integer constants. +** +** The set of static mutexes may change from one SQLite release to the +** next. Applications that override the built-in mutex logic must be +** prepared to accommodate additional static mutexes. +*/ +#define SQLITE_MUTEX_FAST 0 +#define SQLITE_MUTEX_RECURSIVE 1 +#define SQLITE_MUTEX_STATIC_MASTER 2 +#define SQLITE_MUTEX_STATIC_MEM 3 /* sqlite3_malloc() */ +#define SQLITE_MUTEX_STATIC_MEM2 4 /* NOT USED */ +#define SQLITE_MUTEX_STATIC_OPEN 4 /* sqlite3BtreeOpen() */ +#define SQLITE_MUTEX_STATIC_PRNG 5 /* sqlite3_random() */ +#define SQLITE_MUTEX_STATIC_LRU 6 /* lru page list */ +#define SQLITE_MUTEX_STATIC_LRU2 7 /* lru page list */ + +/* +** CAPI3REF: Retrieve the mutex for a database connection {H17002} +** +** This interface returns a pointer the [sqlite3_mutex] object that +** serializes access to the [database connection] given in the argument +** when the [threading mode] is Serialized. +** If the [threading mode] is Single-thread or Multi-thread then this +** routine returns a NULL pointer. +*/ +SQLITE_API sqlite3_mutex *sqlite3_db_mutex(sqlite3*); + +/* +** CAPI3REF: Low-Level Control Of Database Files {H11300} +** +** {H11301} The [sqlite3_file_control()] interface makes a direct call to the +** xFileControl method for the [sqlite3_io_methods] object associated +** with a particular database identified by the second argument. {H11302} The +** name of the database is the name assigned to the database by the +** ATTACH SQL command that opened the +** database. {H11303} To control the main database file, use the name "main" +** or a NULL pointer. {H11304} The third and fourth parameters to this routine +** are passed directly through to the second and third parameters of +** the xFileControl method. {H11305} The return value of the xFileControl +** method becomes the return value of this routine. +** +** {H11306} If the second parameter (zDbName) does not match the name of any +** open database file, then SQLITE_ERROR is returned. {H11307} This error +** code is not remembered and will not be recalled by [sqlite3_errcode()] +** or [sqlite3_errmsg()]. {A11308} The underlying xFileControl method might +** also return SQLITE_ERROR. {A11309} There is no way to distinguish between +** an incorrect zDbName and an SQLITE_ERROR return from the underlying +** xFileControl method. {END} +** +** See also: [SQLITE_FCNTL_LOCKSTATE] +*/ +SQLITE_API int sqlite3_file_control(sqlite3*, const char *zDbName, int op, void*); + +/* +** CAPI3REF: Testing Interface {H11400} +** +** The sqlite3_test_control() interface is used to read out internal +** state of SQLite and to inject faults into SQLite for testing +** purposes. The first parameter is an operation code that determines +** the number, meaning, and operation of all subsequent parameters. +** +** This interface is not for use by applications. It exists solely +** for verifying the correct operation of the SQLite library. Depending +** on how the SQLite library is compiled, this interface might not exist. +** +** The details of the operation codes, their meanings, the parameters +** they take, and what they do are all subject to change without notice. +** Unlike most of the SQLite API, this function is not guaranteed to +** operate consistently from one release to the next. +*/ +SQLITE_API int sqlite3_test_control(int op, ...); + +/* +** CAPI3REF: Testing Interface Operation Codes {H11410} +** +** These constants are the valid operation code parameters used +** as the first argument to [sqlite3_test_control()]. +** +** These parameters and their meanings are subject to change +** without notice. These values are for testing purposes only. +** Applications should not use any of these parameters or the +** [sqlite3_test_control()] interface. +*/ +#define SQLITE_TESTCTRL_PRNG_SAVE 5 +#define SQLITE_TESTCTRL_PRNG_RESTORE 6 +#define SQLITE_TESTCTRL_PRNG_RESET 7 +#define SQLITE_TESTCTRL_BITVEC_TEST 8 +#define SQLITE_TESTCTRL_FAULT_INSTALL 9 +#define SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS 10 +#define SQLITE_TESTCTRL_PENDING_BYTE 11 + +/* +** CAPI3REF: SQLite Runtime Status {H17200} +** EXPERIMENTAL +** +** This interface is used to retrieve runtime status information +** about the preformance of SQLite, and optionally to reset various +** highwater marks. The first argument is an integer code for +** the specific parameter to measure. Recognized integer codes +** are of the form [SQLITE_STATUS_MEMORY_USED | SQLITE_STATUS_...]. +** The current value of the parameter is returned into *pCurrent. +** The highest recorded value is returned in *pHighwater. If the +** resetFlag is true, then the highest record value is reset after +** *pHighwater is written. Some parameters do not record the highest +** value. For those parameters +** nothing is written into *pHighwater and the resetFlag is ignored. +** Other parameters record only the highwater mark and not the current +** value. For these latter parameters nothing is written into *pCurrent. +** +** This routine returns SQLITE_OK on success and a non-zero +** [error code] on failure. +** +** This routine is threadsafe but is not atomic. This routine can +** called while other threads are running the same or different SQLite +** interfaces. However the values returned in *pCurrent and +** *pHighwater reflect the status of SQLite at different points in time +** and it is possible that another thread might change the parameter +** in between the times when *pCurrent and *pHighwater are written. +** +** See also: [sqlite3_db_status()] +*/ +SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_status(int op, int *pCurrent, int *pHighwater, int resetFlag); + + +/* +** CAPI3REF: Status Parameters {H17250} +** EXPERIMENTAL +** +** These integer constants designate various run-time status parameters +** that can be returned by [sqlite3_status()]. +** +**
+**
SQLITE_STATUS_MEMORY_USED
+**
This parameter is the current amount of memory checked out +** using [sqlite3_malloc()], either directly or indirectly. The +** figure includes calls made to [sqlite3_malloc()] by the application +** and internal memory usage by the SQLite library. Scratch memory +** controlled by [SQLITE_CONFIG_SCRATCH] and auxiliary page-cache +** memory controlled by [SQLITE_CONFIG_PAGECACHE] is not included in +** this parameter. The amount returned is the sum of the allocation +** sizes as reported by the xSize method in [sqlite3_mem_methods].
+** +**
SQLITE_STATUS_MALLOC_SIZE
+**
This parameter records the largest memory allocation request +** handed to [sqlite3_malloc()] or [sqlite3_realloc()] (or their +** internal equivalents). Only the value returned in the +** *pHighwater parameter to [sqlite3_status()] is of interest. +** The value written into the *pCurrent parameter is undefined.
+** +**
SQLITE_STATUS_PAGECACHE_USED
+**
This parameter returns the number of pages used out of the +** [pagecache memory allocator] that was configured using +** [SQLITE_CONFIG_PAGECACHE]. The +** value returned is in pages, not in bytes.
+** +**
SQLITE_STATUS_PAGECACHE_OVERFLOW
+**
This parameter returns the number of bytes of page cache +** allocation which could not be statisfied by the [SQLITE_CONFIG_PAGECACHE] +** buffer and where forced to overflow to [sqlite3_malloc()]. The +** returned value includes allocations that overflowed because they +** where too large (they were larger than the "sz" parameter to +** [SQLITE_CONFIG_PAGECACHE]) and allocations that overflowed because +** no space was left in the page cache.
+** +**
SQLITE_STATUS_PAGECACHE_SIZE
+**
This parameter records the largest memory allocation request +** handed to [pagecache memory allocator]. Only the value returned in the +** *pHighwater parameter to [sqlite3_status()] is of interest. +** The value written into the *pCurrent parameter is undefined.
+** +**
SQLITE_STATUS_SCRATCH_USED
+**
This parameter returns the number of allocations used out of the +** [scratch memory allocator] configured using +** [SQLITE_CONFIG_SCRATCH]. The value returned is in allocations, not +** in bytes. Since a single thread may only have one scratch allocation +** outstanding at time, this parameter also reports the number of threads +** using scratch memory at the same time.
+** +**
SQLITE_STATUS_SCRATCH_OVERFLOW
+**
This parameter returns the number of bytes of scratch memory +** allocation which could not be statisfied by the [SQLITE_CONFIG_SCRATCH] +** buffer and where forced to overflow to [sqlite3_malloc()]. The values +** returned include overflows because the requested allocation was too +** larger (that is, because the requested allocation was larger than the +** "sz" parameter to [SQLITE_CONFIG_SCRATCH]) and because no scratch buffer +** slots were available. +**
+** +**
SQLITE_STATUS_SCRATCH_SIZE
+**
This parameter records the largest memory allocation request +** handed to [scratch memory allocator]. Only the value returned in the +** *pHighwater parameter to [sqlite3_status()] is of interest. +** The value written into the *pCurrent parameter is undefined.
+** +**
SQLITE_STATUS_PARSER_STACK
+**
This parameter records the deepest parser stack. It is only +** meaningful if SQLite is compiled with [YYTRACKMAXSTACKDEPTH].
+**
+** +** New status parameters may be added from time to time. +*/ +#define SQLITE_STATUS_MEMORY_USED 0 +#define SQLITE_STATUS_PAGECACHE_USED 1 +#define SQLITE_STATUS_PAGECACHE_OVERFLOW 2 +#define SQLITE_STATUS_SCRATCH_USED 3 +#define SQLITE_STATUS_SCRATCH_OVERFLOW 4 +#define SQLITE_STATUS_MALLOC_SIZE 5 +#define SQLITE_STATUS_PARSER_STACK 6 +#define SQLITE_STATUS_PAGECACHE_SIZE 7 +#define SQLITE_STATUS_SCRATCH_SIZE 8 + +/* +** CAPI3REF: Database Connection Status {H17500} +** EXPERIMENTAL +** +** This interface is used to retrieve runtime status information +** about a single [database connection]. The first argument is the +** database connection object to be interrogated. The second argument +** is the parameter to interrogate. Currently, the only allowed value +** for the second parameter is [SQLITE_DBSTATUS_LOOKASIDE_USED]. +** Additional options will likely appear in future releases of SQLite. +** +** The current value of the requested parameter is written into *pCur +** and the highest instantaneous value is written into *pHiwtr. If +** the resetFlg is true, then the highest instantaneous value is +** reset back down to the current value. +** +** See also: [sqlite3_status()] and [sqlite3_stmt_status()]. +*/ +SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_db_status(sqlite3*, int op, int *pCur, int *pHiwtr, int resetFlg); + +/* +** CAPI3REF: Status Parameters for database connections {H17520} +** EXPERIMENTAL +** +** Status verbs for [sqlite3_db_status()]. +** +**
+**
SQLITE_DBSTATUS_LOOKASIDE_USED
+**
This parameter returns the number of lookaside memory slots currently +** checked out.
+**
+*/ +#define SQLITE_DBSTATUS_LOOKASIDE_USED 0 + + +/* +** CAPI3REF: Prepared Statement Status {H17550} +** EXPERIMENTAL +** +** Each prepared statement maintains various +** [SQLITE_STMTSTATUS_SORT | counters] that measure the number +** of times it has performed specific operations. These counters can +** be used to monitor the performance characteristics of the prepared +** statements. For example, if the number of table steps greatly exceeds +** the number of table searches or result rows, that would tend to indicate +** that the prepared statement is using a full table scan rather than +** an index. +** +** This interface is used to retrieve and reset counter values from +** a [prepared statement]. The first argument is the prepared statement +** object to be interrogated. The second argument +** is an integer code for a specific [SQLITE_STMTSTATUS_SORT | counter] +** to be interrogated. +** The current value of the requested counter is returned. +** If the resetFlg is true, then the counter is reset to zero after this +** interface call returns. +** +** See also: [sqlite3_status()] and [sqlite3_db_status()]. +*/ +SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_stmt_status(sqlite3_stmt*, int op,int resetFlg); + +/* +** CAPI3REF: Status Parameters for prepared statements {H17570} +** EXPERIMENTAL +** +** These preprocessor macros define integer codes that name counter +** values associated with the [sqlite3_stmt_status()] interface. +** The meanings of the various counters are as follows: +** +**
+**
SQLITE_STMTSTATUS_FULLSCAN_STEP
+**
This is the number of times that SQLite has stepped forward in +** a table as part of a full table scan. Large numbers for this counter +** may indicate opportunities for performance improvement through +** careful use of indices.
+** +**
SQLITE_STMTSTATUS_SORT
+**
This is the number of sort operations that have occurred. +** A non-zero value in this counter may indicate an opportunity to +** improvement performance through careful use of indices.
+** +**
+*/ +#define SQLITE_STMTSTATUS_FULLSCAN_STEP 1 +#define SQLITE_STMTSTATUS_SORT 2 + +/* +** CAPI3REF: Custom Page Cache Object +** EXPERIMENTAL +** +** The sqlite3_pcache type is opaque. It is implemented by +** the pluggable module. The SQLite core has no knowledge of +** its size or internal structure and never deals with the +** sqlite3_pcache object except by holding and passing pointers +** to the object. +** +** See [sqlite3_pcache_methods] for additional information. +*/ +typedef struct sqlite3_pcache sqlite3_pcache; + +/* +** CAPI3REF: Application Defined Page Cache. +** EXPERIMENTAL +** +** The [sqlite3_config]([SQLITE_CONFIG_PCACHE], ...) interface can +** register an alternative page cache implementation by passing in an +** instance of the sqlite3_pcache_methods structure. The majority of the +** heap memory used by sqlite is used by the page cache to cache data read +** from, or ready to be written to, the database file. By implementing a +** custom page cache using this API, an application can control more +** precisely the amount of memory consumed by sqlite, the way in which +** said memory is allocated and released, and the policies used to +** determine exactly which parts of a database file are cached and for +** how long. +** +** The contents of the structure are copied to an internal buffer by sqlite +** within the call to [sqlite3_config]. +** +** The xInit() method is called once for each call to [sqlite3_initialize()] +** (usually only once during the lifetime of the process). It is passed +** a copy of the sqlite3_pcache_methods.pArg value. It can be used to set +** up global structures and mutexes required by the custom page cache +** implementation. The xShutdown() method is called from within +** [sqlite3_shutdown()], if the application invokes this API. It can be used +** to clean up any outstanding resources before process shutdown, if required. +** +** The xCreate() method is used to construct a new cache instance. The +** first parameter, szPage, is the size in bytes of the pages that must +** be allocated by the cache. szPage will not be a power of two. The +** second argument, bPurgeable, is true if the cache being created will +** be used to cache database pages read from a file stored on disk, or +** false if it is used for an in-memory database. The cache implementation +** does not have to do anything special based on the value of bPurgeable, +** it is purely advisory. +** +** The xCachesize() method may be called at any time by SQLite to set the +** suggested maximum cache-size (number of pages stored by) the cache +** instance passed as the first argument. This is the value configured using +** the SQLite "[PRAGMA cache_size]" command. As with the bPurgeable parameter, +** the implementation is not required to do anything special with this +** value, it is advisory only. +** +** The xPagecount() method should return the number of pages currently +** stored in the cache supplied as an argument. +** +** The xFetch() method is used to fetch a page and return a pointer to it. +** A 'page', in this context, is a buffer of szPage bytes aligned at an +** 8-byte boundary. The page to be fetched is determined by the key. The +** mimimum key value is 1. After it has been retrieved using xFetch, the page +** is considered to be pinned. +** +** If the requested page is already in the page cache, then a pointer to +** the cached buffer should be returned with its contents intact. If the +** page is not already in the cache, then the expected behaviour of the +** cache is determined by the value of the createFlag parameter passed +** to xFetch, according to the following table: +** +** +**
createFlagExpected Behaviour +**
0NULL should be returned. No new cache entry is created. +**
1If createFlag is set to 1, this indicates that +** SQLite is holding pinned pages that can be unpinned +** by writing their contents to the database file (a +** relatively expensive operation). In this situation the +** cache implementation has two choices: it can return NULL, +** in which case SQLite will attempt to unpin one or more +** pages before re-requesting the same page, or it can +** allocate a new page and return a pointer to it. If a new +** page is allocated, then the first sizeof(void*) bytes of +** it (at least) must be zeroed before it is returned. +**
2If createFlag is set to 2, then SQLite is not holding any +** pinned pages associated with the specific cache passed +** as the first argument to xFetch() that can be unpinned. The +** cache implementation should attempt to allocate a new +** cache entry and return a pointer to it. Again, the first +** sizeof(void*) bytes of the page should be zeroed before +** it is returned. If the xFetch() method returns NULL when +** createFlag==2, SQLite assumes that a memory allocation +** failed and returns SQLITE_NOMEM to the user. +**
+** +** xUnpin() is called by SQLite with a pointer to a currently pinned page +** as its second argument. If the third parameter, discard, is non-zero, +** then the page should be evicted from the cache. In this case SQLite +** assumes that the next time the page is retrieved from the cache using +** the xFetch() method, it will be zeroed. If the discard parameter is +** zero, then the page is considered to be unpinned. The cache implementation +** may choose to reclaim (free or recycle) unpinned pages at any time. +** SQLite assumes that next time the page is retrieved from the cache +** it will either be zeroed, or contain the same data that it did when it +** was unpinned. +** +** The cache is not required to perform any reference counting. A single +** call to xUnpin() unpins the page regardless of the number of prior calls +** to xFetch(). +** +** The xRekey() method is used to change the key value associated with the +** page passed as the second argument from oldKey to newKey. If the cache +** previously contains an entry associated with newKey, it should be +** discarded. Any prior cache entry associated with newKey is guaranteed not +** to be pinned. +** +** When SQLite calls the xTruncate() method, the cache must discard all +** existing cache entries with page numbers (keys) greater than or equal +** to the value of the iLimit parameter passed to xTruncate(). If any +** of these pages are pinned, they are implicitly unpinned, meaning that +** they can be safely discarded. +** +** The xDestroy() method is used to delete a cache allocated by xCreate(). +** All resources associated with the specified cache should be freed. After +** calling the xDestroy() method, SQLite considers the [sqlite3_pcache*] +** handle invalid, and will not use it with any other sqlite3_pcache_methods +** functions. +*/ +typedef struct sqlite3_pcache_methods sqlite3_pcache_methods; +struct sqlite3_pcache_methods { + void *pArg; + int (*xInit)(void*); + void (*xShutdown)(void*); + sqlite3_pcache *(*xCreate)(int szPage, int bPurgeable); + void (*xCachesize)(sqlite3_pcache*, int nCachesize); + int (*xPagecount)(sqlite3_pcache*); + void *(*xFetch)(sqlite3_pcache*, unsigned key, int createFlag); + void (*xUnpin)(sqlite3_pcache*, void*, int discard); + void (*xRekey)(sqlite3_pcache*, void*, unsigned oldKey, unsigned newKey); + void (*xTruncate)(sqlite3_pcache*, unsigned iLimit); + void (*xDestroy)(sqlite3_pcache*); +}; + +/* +** CAPI3REF: Online Backup Object +** EXPERIMENTAL +** +** The sqlite3_backup object records state information about an ongoing +** online backup operation. The sqlite3_backup object is created by +** a call to [sqlite3_backup_init()] and is destroyed by a call to +** [sqlite3_backup_finish()]. +** +** See Also: [Using the SQLite Online Backup API] +*/ +typedef struct sqlite3_backup sqlite3_backup; + +/* +** CAPI3REF: Online Backup API. +** EXPERIMENTAL +** +** This API is used to overwrite the contents of one database with that +** of another. It is useful either for creating backups of databases or +** for copying in-memory databases to or from persistent files. +** +** See Also: [Using the SQLite Online Backup API] +** +** Exclusive access is required to the destination database for the +** duration of the operation. However the source database is only +** read-locked while it is actually being read, it is not locked +** continuously for the entire operation. Thus, the backup may be +** performed on a live database without preventing other users from +** writing to the database for an extended period of time. +** +** To perform a backup operation: +**
    +**
  1. sqlite3_backup_init() is called once to initialize the +** backup, +**
  2. sqlite3_backup_step() is called one or more times to transfer +** the data between the two databases, and finally +**
  3. sqlite3_backup_finish() is called to release all resources +** associated with the backup operation. +**
+** There should be exactly one call to sqlite3_backup_finish() for each +** successful call to sqlite3_backup_init(). +** +** sqlite3_backup_init() +** +** The first two arguments passed to [sqlite3_backup_init()] are the database +** handle associated with the destination database and the database name +** used to attach the destination database to the handle. The database name +** is "main" for the main database, "temp" for the temporary database, or +** the name specified as part of the [ATTACH] statement if the destination is +** an attached database. The third and fourth arguments passed to +** sqlite3_backup_init() identify the [database connection] +** and database name used +** to access the source database. The values passed for the source and +** destination [database connection] parameters must not be the same. +** +** If an error occurs within sqlite3_backup_init(), then NULL is returned +** and an error code and error message written into the [database connection] +** passed as the first argument. They may be retrieved using the +** [sqlite3_errcode()], [sqlite3_errmsg()], and [sqlite3_errmsg16()] functions. +** Otherwise, if successful, a pointer to an [sqlite3_backup] object is +** returned. This pointer may be used with the sqlite3_backup_step() and +** sqlite3_backup_finish() functions to perform the specified backup +** operation. +** +** sqlite3_backup_step() +** +** Function [sqlite3_backup_step()] is used to copy up to nPage pages between +** the source and destination databases, where nPage is the value of the +** second parameter passed to sqlite3_backup_step(). If nPage is a negative +** value, all remaining source pages are copied. If the required pages are +** succesfully copied, but there are still more pages to copy before the +** backup is complete, it returns [SQLITE_OK]. If no error occured and there +** are no more pages to copy, then [SQLITE_DONE] is returned. If an error +** occurs, then an SQLite error code is returned. As well as [SQLITE_OK] and +** [SQLITE_DONE], a call to sqlite3_backup_step() may return [SQLITE_READONLY], +** [SQLITE_NOMEM], [SQLITE_BUSY], [SQLITE_LOCKED], or an +** [SQLITE_IOERR_ACCESS | SQLITE_IOERR_XXX] extended error code. +** +** As well as the case where the destination database file was opened for +** read-only access, sqlite3_backup_step() may return [SQLITE_READONLY] if +** the destination is an in-memory database with a different page size +** from the source database. +** +** If sqlite3_backup_step() cannot obtain a required file-system lock, then +** the [sqlite3_busy_handler | busy-handler function] +** is invoked (if one is specified). If the +** busy-handler returns non-zero before the lock is available, then +** [SQLITE_BUSY] is returned to the caller. In this case the call to +** sqlite3_backup_step() can be retried later. If the source +** [database connection] +** is being used to write to the source database when sqlite3_backup_step() +** is called, then [SQLITE_LOCKED] is returned immediately. Again, in this +** case the call to sqlite3_backup_step() can be retried later on. If +** [SQLITE_IOERR_ACCESS | SQLITE_IOERR_XXX], [SQLITE_NOMEM], or +** [SQLITE_READONLY] is returned, then +** there is no point in retrying the call to sqlite3_backup_step(). These +** errors are considered fatal. At this point the application must accept +** that the backup operation has failed and pass the backup operation handle +** to the sqlite3_backup_finish() to release associated resources. +** +** Following the first call to sqlite3_backup_step(), an exclusive lock is +** obtained on the destination file. It is not released until either +** sqlite3_backup_finish() is called or the backup operation is complete +** and sqlite3_backup_step() returns [SQLITE_DONE]. Additionally, each time +** a call to sqlite3_backup_step() is made a [shared lock] is obtained on +** the source database file. This lock is released before the +** sqlite3_backup_step() call returns. Because the source database is not +** locked between calls to sqlite3_backup_step(), it may be modified mid-way +** through the backup procedure. If the source database is modified by an +** external process or via a database connection other than the one being +** used by the backup operation, then the backup will be transparently +** restarted by the next call to sqlite3_backup_step(). If the source +** database is modified by the using the same database connection as is used +** by the backup operation, then the backup database is transparently +** updated at the same time. +** +** sqlite3_backup_finish() +** +** Once sqlite3_backup_step() has returned [SQLITE_DONE], or when the +** application wishes to abandon the backup operation, the [sqlite3_backup] +** object should be passed to sqlite3_backup_finish(). This releases all +** resources associated with the backup operation. If sqlite3_backup_step() +** has not yet returned [SQLITE_DONE], then any active write-transaction on the +** destination database is rolled back. The [sqlite3_backup] object is invalid +** and may not be used following a call to sqlite3_backup_finish(). +** +** The value returned by sqlite3_backup_finish is [SQLITE_OK] if no error +** occurred, regardless or whether or not sqlite3_backup_step() was called +** a sufficient number of times to complete the backup operation. Or, if +** an out-of-memory condition or IO error occured during a call to +** sqlite3_backup_step() then [SQLITE_NOMEM] or an +** [SQLITE_IOERR_ACCESS | SQLITE_IOERR_XXX] error code +** is returned. In this case the error code and an error message are +** written to the destination [database connection]. +** +** A return of [SQLITE_BUSY] or [SQLITE_LOCKED] from sqlite3_backup_step() is +** not a permanent error and does not affect the return value of +** sqlite3_backup_finish(). +** +** sqlite3_backup_remaining(), sqlite3_backup_pagecount() +** +** Each call to sqlite3_backup_step() sets two values stored internally +** by an [sqlite3_backup] object. The number of pages still to be backed +** up, which may be queried by sqlite3_backup_remaining(), and the total +** number of pages in the source database file, which may be queried by +** sqlite3_backup_pagecount(). +** +** The values returned by these functions are only updated by +** sqlite3_backup_step(). If the source database is modified during a backup +** operation, then the values are not updated to account for any extra +** pages that need to be updated or the size of the source database file +** changing. +** +** Concurrent Usage of Database Handles +** +** The source [database connection] may be used by the application for other +** purposes while a backup operation is underway or being initialized. +** If SQLite is compiled and configured to support threadsafe database +** connections, then the source database connection may be used concurrently +** from within other threads. +** +** However, the application must guarantee that the destination database +** connection handle is not passed to any other API (by any thread) after +** sqlite3_backup_init() is called and before the corresponding call to +** sqlite3_backup_finish(). Unfortunately SQLite does not currently check +** for this, if the application does use the destination [database connection] +** for some other purpose during a backup operation, things may appear to +** work correctly but in fact be subtly malfunctioning. Use of the +** destination database connection while a backup is in progress might +** also cause a mutex deadlock. +** +** Furthermore, if running in [shared cache mode], the application must +** guarantee that the shared cache used by the destination database +** is not accessed while the backup is running. In practice this means +** that the application must guarantee that the file-system file being +** backed up to is not accessed by any connection within the process, +** not just the specific connection that was passed to sqlite3_backup_init(). +** +** The [sqlite3_backup] object itself is partially threadsafe. Multiple +** threads may safely make multiple concurrent calls to sqlite3_backup_step(). +** However, the sqlite3_backup_remaining() and sqlite3_backup_pagecount() +** APIs are not strictly speaking threadsafe. If they are invoked at the +** same time as another thread is invoking sqlite3_backup_step() it is +** possible that they return invalid values. +*/ +SQLITE_API sqlite3_backup *sqlite3_backup_init( + sqlite3 *pDest, /* Destination database handle */ + const char *zDestName, /* Destination database name */ + sqlite3 *pSource, /* Source database handle */ + const char *zSourceName /* Source database name */ +); +SQLITE_API int sqlite3_backup_step(sqlite3_backup *p, int nPage); +SQLITE_API int sqlite3_backup_finish(sqlite3_backup *p); +SQLITE_API int sqlite3_backup_remaining(sqlite3_backup *p); +SQLITE_API int sqlite3_backup_pagecount(sqlite3_backup *p); + +/* +** CAPI3REF: Unlock Notification +** EXPERIMENTAL +** +** When running in shared-cache mode, a database operation may fail with +** an [SQLITE_LOCKED] error if the required locks on the shared-cache or +** individual tables within the shared-cache cannot be obtained. See +** [SQLite Shared-Cache Mode] for a description of shared-cache locking. +** This API may be used to register a callback that SQLite will invoke +** when the connection currently holding the required lock relinquishes it. +** This API is only available if the library was compiled with the +** [SQLITE_ENABLE_UNLOCK_NOTIFY] C-preprocessor symbol defined. +** +** See Also: [Using the SQLite Unlock Notification Feature]. +** +** Shared-cache locks are released when a database connection concludes +** its current transaction, either by committing it or rolling it back. +** +** When a connection (known as the blocked connection) fails to obtain a +** shared-cache lock and SQLITE_LOCKED is returned to the caller, the +** identity of the database connection (the blocking connection) that +** has locked the required resource is stored internally. After an +** application receives an SQLITE_LOCKED error, it may call the +** sqlite3_unlock_notify() method with the blocked connection handle as +** the first argument to register for a callback that will be invoked +** when the blocking connections current transaction is concluded. The +** callback is invoked from within the [sqlite3_step] or [sqlite3_close] +** call that concludes the blocking connections transaction. +** +** If sqlite3_unlock_notify() is called in a multi-threaded application, +** there is a chance that the blocking connection will have already +** concluded its transaction by the time sqlite3_unlock_notify() is invoked. +** If this happens, then the specified callback is invoked immediately, +** from within the call to sqlite3_unlock_notify(). +** +** If the blocked connection is attempting to obtain a write-lock on a +** shared-cache table, and more than one other connection currently holds +** a read-lock on the same table, then SQLite arbitrarily selects one of +** the other connections to use as the blocking connection. +** +** There may be at most one unlock-notify callback registered by a +** blocked connection. If sqlite3_unlock_notify() is called when the +** blocked connection already has a registered unlock-notify callback, +** then the new callback replaces the old. If sqlite3_unlock_notify() is +** called with a NULL pointer as its second argument, then any existing +** unlock-notify callback is cancelled. The blocked connections +** unlock-notify callback may also be canceled by closing the blocked +** connection using [sqlite3_close()]. +** +** The unlock-notify callback is not reentrant. If an application invokes +** any sqlite3_xxx API functions from within an unlock-notify callback, a +** crash or deadlock may be the result. +** +** Unless deadlock is detected (see below), sqlite3_unlock_notify() always +** returns SQLITE_OK. +** +** Callback Invocation Details +** +** When an unlock-notify callback is registered, the application provides a +** single void* pointer that is passed to the callback when it is invoked. +** However, the signature of the callback function allows SQLite to pass +** it an array of void* context pointers. The first argument passed to +** an unlock-notify callback is a pointer to an array of void* pointers, +** and the second is the number of entries in the array. +** +** When a blocking connections transaction is concluded, there may be +** more than one blocked connection that has registered for an unlock-notify +** callback. If two or more such blocked connections have specified the +** same callback function, then instead of invoking the callback function +** multiple times, it is invoked once with the set of void* context pointers +** specified by the blocked connections bundled together into an array. +** This gives the application an opportunity to prioritize any actions +** related to the set of unblocked database connections. +** +** Deadlock Detection +** +** Assuming that after registering for an unlock-notify callback a +** database waits for the callback to be issued before taking any further +** action (a reasonable assumption), then using this API may cause the +** application to deadlock. For example, if connection X is waiting for +** connection Y's transaction to be concluded, and similarly connection +** Y is waiting on connection X's transaction, then neither connection +** will proceed and the system may remain deadlocked indefinitely. +** +** To avoid this scenario, the sqlite3_unlock_notify() performs deadlock +** detection. If a given call to sqlite3_unlock_notify() would put the +** system in a deadlocked state, then SQLITE_LOCKED is returned and no +** unlock-notify callback is registered. The system is said to be in +** a deadlocked state if connection A has registered for an unlock-notify +** callback on the conclusion of connection B's transaction, and connection +** B has itself registered for an unlock-notify callback when connection +** A's transaction is concluded. Indirect deadlock is also detected, so +** the system is also considered to be deadlocked if connection B has +** registered for an unlock-notify callback on the conclusion of connection +** C's transaction, where connection C is waiting on connection A. Any +** number of levels of indirection are allowed. +** +** The "DROP TABLE" Exception +** +** When a call to [sqlite3_step()] returns SQLITE_LOCKED, it is almost +** always appropriate to call sqlite3_unlock_notify(). There is however, +** one exception. When executing a "DROP TABLE" or "DROP INDEX" statement, +** SQLite checks if there are any currently executing SELECT statements +** that belong to the same connection. If there are, SQLITE_LOCKED is +** returned. In this case there is no "blocking connection", so invoking +** sqlite3_unlock_notify() results in the unlock-notify callback being +** invoked immediately. If the application then re-attempts the "DROP TABLE" +** or "DROP INDEX" query, an infinite loop might be the result. +** +** One way around this problem is to check the extended error code returned +** by an sqlite3_step() call. If there is a blocking connection, then the +** extended error code is set to SQLITE_LOCKED_SHAREDCACHE. Otherwise, in +** the special "DROP TABLE/INDEX" case, the extended error code is just +** SQLITE_LOCKED. +*/ +SQLITE_API int sqlite3_unlock_notify( + sqlite3 *pBlocked, /* Waiting connection */ + void (*xNotify)(void **apArg, int nArg), /* Callback function to invoke */ + void *pNotifyArg /* Argument to pass to xNotify */ +); + +/* +** Undo the hack that converts floating point types to integer for +** builds on processors without floating point support. +*/ +#ifdef SQLITE_OMIT_FLOATING_POINT +# undef double +#endif + +#if 0 +} /* End of the 'extern "C"' block */ +#endif +#endif + +/************** End of sqlite3.h *********************************************/ +/************** Continuing where we left off in sqliteInt.h ******************/ +/************** Include hash.h in the middle of sqliteInt.h ******************/ +/************** Begin file hash.h ********************************************/ +/* +** 2001 September 22 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +************************************************************************* +** This is the header file for the generic hash-table implemenation +** used in SQLite. +** +** $Id: hash.h,v 1.12 2008/10/10 17:41:29 drh Exp $ +*/ +#ifndef _SQLITE_HASH_H_ +#define _SQLITE_HASH_H_ + +/* Forward declarations of structures. */ +typedef struct Hash Hash; +typedef struct HashElem HashElem; + +/* A complete hash table is an instance of the following structure. +** The internals of this structure are intended to be opaque -- client +** code should not attempt to access or modify the fields of this structure +** directly. Change this structure only by using the routines below. +** However, many of the "procedures" and "functions" for modifying and +** accessing this structure are really macros, so we can't really make +** this structure opaque. +*/ +struct Hash { + unsigned int copyKey: 1; /* True if copy of key made on insert */ + unsigned int htsize : 31; /* Number of buckets in the hash table */ + unsigned int count; /* Number of entries in this table */ + HashElem *first; /* The first element of the array */ + struct _ht { /* the hash table */ + int count; /* Number of entries with this hash */ + HashElem *chain; /* Pointer to first entry with this hash */ + } *ht; +}; + +/* Each element in the hash table is an instance of the following +** structure. All elements are stored on a single doubly-linked list. +** +** Again, this structure is intended to be opaque, but it can't really +** be opaque because it is used by macros. +*/ +struct HashElem { + HashElem *next, *prev; /* Next and previous elements in the table */ + void *data; /* Data associated with this element */ + void *pKey; int nKey; /* Key associated with this element */ +}; + +/* +** Access routines. To delete, insert a NULL pointer. +*/ +SQLITE_PRIVATE void sqlite3HashInit(Hash*, int copyKey); +SQLITE_PRIVATE void *sqlite3HashInsert(Hash*, const void *pKey, int nKey, void *pData); +SQLITE_PRIVATE void *sqlite3HashFind(const Hash*, const void *pKey, int nKey); +SQLITE_PRIVATE HashElem *sqlite3HashFindElem(const Hash*, const void *pKey, int nKey); +SQLITE_PRIVATE void sqlite3HashClear(Hash*); + +/* +** Macros for looping over all elements of a hash table. The idiom is +** like this: +** +** Hash h; +** HashElem *p; +** ... +** for(p=sqliteHashFirst(&h); p; p=sqliteHashNext(p)){ +** SomeStructure *pData = sqliteHashData(p); +** // do something with pData +** } +*/ +#define sqliteHashFirst(H) ((H)->first) +#define sqliteHashNext(E) ((E)->next) +#define sqliteHashData(E) ((E)->data) +#define sqliteHashKey(E) ((E)->pKey) +#define sqliteHashKeysize(E) ((E)->nKey) + +/* +** Number of entries in a hash table +*/ +#define sqliteHashCount(H) ((H)->count) + +#endif /* _SQLITE_HASH_H_ */ + +/************** End of hash.h ************************************************/ +/************** Continuing where we left off in sqliteInt.h ******************/ +/************** Include parse.h in the middle of sqliteInt.h *****************/ +/************** Begin file parse.h *******************************************/ +#define TK_SEMI 1 +#define TK_EXPLAIN 2 +#define TK_QUERY 3 +#define TK_PLAN 4 +#define TK_BEGIN 5 +#define TK_TRANSACTION 6 +#define TK_DEFERRED 7 +#define TK_IMMEDIATE 8 +#define TK_EXCLUSIVE 9 +#define TK_COMMIT 10 +#define TK_END 11 +#define TK_ROLLBACK 12 +#define TK_SAVEPOINT 13 +#define TK_RELEASE 14 +#define TK_TO 15 +#define TK_TABLE 16 +#define TK_CREATE 17 +#define TK_IF 18 +#define TK_NOT 19 +#define TK_EXISTS 20 +#define TK_TEMP 21 +#define TK_LP 22 +#define TK_RP 23 +#define TK_AS 24 +#define TK_COMMA 25 +#define TK_ID 26 +#define TK_INDEXED 27 +#define TK_ABORT 28 +#define TK_AFTER 29 +#define TK_ANALYZE 30 +#define TK_ASC 31 +#define TK_ATTACH 32 +#define TK_BEFORE 33 +#define TK_BY 34 +#define TK_CASCADE 35 +#define TK_CAST 36 +#define TK_COLUMNKW 37 +#define TK_CONFLICT 38 +#define TK_DATABASE 39 +#define TK_DESC 40 +#define TK_DETACH 41 +#define TK_EACH 42 +#define TK_FAIL 43 +#define TK_FOR 44 +#define TK_IGNORE 45 +#define TK_INITIALLY 46 +#define TK_INSTEAD 47 +#define TK_LIKE_KW 48 +#define TK_MATCH 49 +#define TK_KEY 50 +#define TK_OF 51 +#define TK_OFFSET 52 +#define TK_PRAGMA 53 +#define TK_RAISE 54 +#define TK_REPLACE 55 +#define TK_RESTRICT 56 +#define TK_ROW 57 +#define TK_TRIGGER 58 +#define TK_VACUUM 59 +#define TK_VIEW 60 +#define TK_VIRTUAL 61 +#define TK_REINDEX 62 +#define TK_RENAME 63 +#define TK_CTIME_KW 64 +#define TK_ANY 65 +#define TK_OR 66 +#define TK_AND 67 +#define TK_IS 68 +#define TK_BETWEEN 69 +#define TK_IN 70 +#define TK_ISNULL 71 +#define TK_NOTNULL 72 +#define TK_NE 73 +#define TK_EQ 74 +#define TK_GT 75 +#define TK_LE 76 +#define TK_LT 77 +#define TK_GE 78 +#define TK_ESCAPE 79 +#define TK_BITAND 80 +#define TK_BITOR 81 +#define TK_LSHIFT 82 +#define TK_RSHIFT 83 +#define TK_PLUS 84 +#define TK_MINUS 85 +#define TK_STAR 86 +#define TK_SLASH 87 +#define TK_REM 88 +#define TK_CONCAT 89 +#define TK_COLLATE 90 +#define TK_UMINUS 91 +#define TK_UPLUS 92 +#define TK_BITNOT 93 +#define TK_STRING 94 +#define TK_JOIN_KW 95 +#define TK_CONSTRAINT 96 +#define TK_DEFAULT 97 +#define TK_NULL 98 +#define TK_PRIMARY 99 +#define TK_UNIQUE 100 +#define TK_CHECK 101 +#define TK_REFERENCES 102 +#define TK_AUTOINCR 103 +#define TK_ON 104 +#define TK_DELETE 105 +#define TK_UPDATE 106 +#define TK_INSERT 107 +#define TK_SET 108 +#define TK_DEFERRABLE 109 +#define TK_FOREIGN 110 +#define TK_DROP 111 +#define TK_UNION 112 +#define TK_ALL 113 +#define TK_EXCEPT 114 +#define TK_INTERSECT 115 +#define TK_SELECT 116 +#define TK_DISTINCT 117 +#define TK_DOT 118 +#define TK_FROM 119 +#define TK_JOIN 120 +#define TK_USING 121 +#define TK_ORDER 122 +#define TK_GROUP 123 +#define TK_HAVING 124 +#define TK_LIMIT 125 +#define TK_WHERE 126 +#define TK_INTO 127 +#define TK_VALUES 128 +#define TK_INTEGER 129 +#define TK_FLOAT 130 +#define TK_BLOB 131 +#define TK_REGISTER 132 +#define TK_VARIABLE 133 +#define TK_CASE 134 +#define TK_WHEN 135 +#define TK_THEN 136 +#define TK_ELSE 137 +#define TK_INDEX 138 +#define TK_ALTER 139 +#define TK_ADD 140 +#define TK_TO_TEXT 141 +#define TK_TO_BLOB 142 +#define TK_TO_NUMERIC 143 +#define TK_TO_INT 144 +#define TK_TO_REAL 145 +#define TK_END_OF_FILE 146 +#define TK_ILLEGAL 147 +#define TK_SPACE 148 +#define TK_UNCLOSED_STRING 149 +#define TK_FUNCTION 150 +#define TK_COLUMN 151 +#define TK_AGG_FUNCTION 152 +#define TK_AGG_COLUMN 153 +#define TK_CONST_FUNC 154 + +/************** End of parse.h ***********************************************/ +/************** Continuing where we left off in sqliteInt.h ******************/ +#include +#include +#include +#include +#include + +/* +** If compiling for a processor that lacks floating point support, +** substitute integer for floating-point +*/ +#ifdef SQLITE_OMIT_FLOATING_POINT +# define double sqlite_int64 +# define LONGDOUBLE_TYPE sqlite_int64 +# ifndef SQLITE_BIG_DBL +# define SQLITE_BIG_DBL (0x7fffffffffffffff) +# endif +# define SQLITE_OMIT_DATETIME_FUNCS 1 +# define SQLITE_OMIT_TRACE 1 +# undef SQLITE_MIXED_ENDIAN_64BIT_FLOAT +#endif +#ifndef SQLITE_BIG_DBL +# define SQLITE_BIG_DBL (1e99) +#endif + +/* +** OMIT_TEMPDB is set to 1 if SQLITE_OMIT_TEMPDB is defined, or 0 +** afterward. Having this macro allows us to cause the C compiler +** to omit code used by TEMP tables without messy #ifndef statements. +*/ +#ifdef SQLITE_OMIT_TEMPDB +#define OMIT_TEMPDB 1 +#else +#define OMIT_TEMPDB 0 +#endif + +/* +** If the following macro is set to 1, then NULL values are considered +** distinct when determining whether or not two entries are the same +** in a UNIQUE index. This is the way PostgreSQL, Oracle, DB2, MySQL, +** OCELOT, and Firebird all work. The SQL92 spec explicitly says this +** is the way things are suppose to work. +** +** If the following macro is set to 0, the NULLs are indistinct for +** a UNIQUE index. In this mode, you can only have a single NULL entry +** for a column declared UNIQUE. This is the way Informix and SQL Server +** work. +*/ +#define NULL_DISTINCT_FOR_UNIQUE 1 + +/* +** The "file format" number is an integer that is incremented whenever +** the VDBE-level file format changes. The following macros define the +** the default file format for new databases and the maximum file format +** that the library can read. +*/ +#define SQLITE_MAX_FILE_FORMAT 4 +#ifndef SQLITE_DEFAULT_FILE_FORMAT +# define SQLITE_DEFAULT_FILE_FORMAT 1 +#endif + +/* +** Provide a default value for SQLITE_TEMP_STORE in case it is not specified +** on the command-line +*/ +#ifndef SQLITE_TEMP_STORE +# define SQLITE_TEMP_STORE 1 +#endif + +/* +** GCC does not define the offsetof() macro so we'll have to do it +** ourselves. +*/ +#ifndef offsetof +#define offsetof(STRUCTURE,FIELD) ((int)((char*)&((STRUCTURE*)0)->FIELD)) +#endif + +/* +** Check to see if this machine uses EBCDIC. (Yes, believe it or +** not, there are still machines out there that use EBCDIC.) +*/ +#if 'A' == '\301' +# define SQLITE_EBCDIC 1 +#else +# define SQLITE_ASCII 1 +#endif + +/* +** Integers of known sizes. These typedefs might change for architectures +** where the sizes very. Preprocessor macros are available so that the +** types can be conveniently redefined at compile-type. Like this: +** +** cc '-DUINTPTR_TYPE=long long int' ... +*/ +#ifndef UINT32_TYPE +# ifdef HAVE_UINT32_T +# define UINT32_TYPE uint32_t +# else +# define UINT32_TYPE unsigned int +# endif +#endif +#ifndef UINT16_TYPE +# ifdef HAVE_UINT16_T +# define UINT16_TYPE uint16_t +# else +# define UINT16_TYPE unsigned short int +# endif +#endif +#ifndef INT16_TYPE +# ifdef HAVE_INT16_T +# define INT16_TYPE int16_t +# else +# define INT16_TYPE short int +# endif +#endif +#ifndef UINT8_TYPE +# ifdef HAVE_UINT8_T +# define UINT8_TYPE uint8_t +# else +# define UINT8_TYPE unsigned char +# endif +#endif +#ifndef INT8_TYPE +# ifdef HAVE_INT8_T +# define INT8_TYPE int8_t +# else +# define INT8_TYPE signed char +# endif +#endif +#ifndef LONGDOUBLE_TYPE +# define LONGDOUBLE_TYPE long double +#endif +typedef sqlite_int64 i64; /* 8-byte signed integer */ +typedef sqlite_uint64 u64; /* 8-byte unsigned integer */ +typedef UINT32_TYPE u32; /* 4-byte unsigned integer */ +typedef UINT16_TYPE u16; /* 2-byte unsigned integer */ +typedef INT16_TYPE i16; /* 2-byte signed integer */ +typedef UINT8_TYPE u8; /* 1-byte unsigned integer */ +typedef INT8_TYPE i8; /* 1-byte signed integer */ + +/* +** Macros to determine whether the machine is big or little endian, +** evaluated at runtime. +*/ +#ifdef SQLITE_AMALGAMATION +SQLITE_PRIVATE const int sqlite3one = 1; +#else +SQLITE_PRIVATE const int sqlite3one; +#endif +#if defined(i386) || defined(__i386__) || defined(_M_IX86)\ + || defined(__x86_64) || defined(__x86_64__) +# define SQLITE_BIGENDIAN 0 +# define SQLITE_LITTLEENDIAN 1 +# define SQLITE_UTF16NATIVE SQLITE_UTF16LE +#else +# define SQLITE_BIGENDIAN (*(char *)(&sqlite3one)==0) +# define SQLITE_LITTLEENDIAN (*(char *)(&sqlite3one)==1) +# define SQLITE_UTF16NATIVE (SQLITE_BIGENDIAN?SQLITE_UTF16BE:SQLITE_UTF16LE) +#endif + +/* +** Constants for the largest and smallest possible 64-bit signed integers. +** These macros are designed to work correctly on both 32-bit and 64-bit +** compilers. +*/ +#define LARGEST_INT64 (0xffffffff|(((i64)0x7fffffff)<<32)) +#define SMALLEST_INT64 (((i64)-1) - LARGEST_INT64) + +/* +** Round up a number to the next larger multiple of 8. This is used +** to force 8-byte alignment on 64-bit architectures. +*/ +#define ROUND8(x) (((x)+7)&~7) + +/* +** Round down to the nearest multiple of 8 +*/ +#define ROUNDDOWN8(x) ((x)&~7) + +/* +** An instance of the following structure is used to store the busy-handler +** callback for a given sqlite handle. +** +** The sqlite.busyHandler member of the sqlite struct contains the busy +** callback for the database handle. Each pager opened via the sqlite +** handle is passed a pointer to sqlite.busyHandler. The busy-handler +** callback is currently invoked only from within pager.c. +*/ +typedef struct BusyHandler BusyHandler; +struct BusyHandler { + int (*xFunc)(void *,int); /* The busy callback */ + void *pArg; /* First arg to busy callback */ + int nBusy; /* Incremented with each busy call */ +}; + +/* +** Name of the master database table. The master database table +** is a special table that holds the names and attributes of all +** user tables and indices. +*/ +#define MASTER_NAME "sqlite_master" +#define TEMP_MASTER_NAME "sqlite_temp_master" + +/* +** The root-page of the master database table. +*/ +#define MASTER_ROOT 1 + +/* +** The name of the schema table. +*/ +#define SCHEMA_TABLE(x) ((!OMIT_TEMPDB)&&(x==1)?TEMP_MASTER_NAME:MASTER_NAME) + +/* +** A convenience macro that returns the number of elements in +** an array. +*/ +#define ArraySize(X) ((int)(sizeof(X)/sizeof(X[0]))) + +/* +** The following value as a destructor means to use sqlite3DbFree(). +** This is an internal extension to SQLITE_STATIC and SQLITE_TRANSIENT. +*/ +#define SQLITE_DYNAMIC ((sqlite3_destructor_type)sqlite3DbFree) + +/* +** When SQLITE_OMIT_WSD is defined, it means that the target platform does +** not support Writable Static Data (WSD) such as global and static variables. +** All variables must either be on the stack or dynamically allocated from +** the heap. When WSD is unsupported, the variable declarations scattered +** throughout the SQLite code must become constants instead. The SQLITE_WSD +** macro is used for this purpose. And instead of referencing the variable +** directly, we use its constant as a key to lookup the run-time allocated +** buffer that holds real variable. The constant is also the initializer +** for the run-time allocated buffer. +** +** In the usual case where WSD is supported, the SQLITE_WSD and GLOBAL +** macros become no-ops and have zero performance impact. +*/ +#ifdef SQLITE_OMIT_WSD + #define SQLITE_WSD const + #define GLOBAL(t,v) (*(t*)sqlite3_wsd_find((void*)&(v), sizeof(v))) + #define sqlite3GlobalConfig GLOBAL(struct Sqlite3Config, sqlite3Config) +SQLITE_API int sqlite3_wsd_init(int N, int J); +SQLITE_API void *sqlite3_wsd_find(void *K, int L); +#else + #define SQLITE_WSD + #define GLOBAL(t,v) v + #define sqlite3GlobalConfig sqlite3Config +#endif + +/* +** The following macros are used to suppress compiler warnings and to +** make it clear to human readers when a function parameter is deliberately +** left unused within the body of a function. This usually happens when +** a function is called via a function pointer. For example the +** implementation of an SQL aggregate step callback may not use the +** parameter indicating the number of arguments passed to the aggregate, +** if it knows that this is enforced elsewhere. +** +** When a function parameter is not used at all within the body of a function, +** it is generally named "NotUsed" or "NotUsed2" to make things even clearer. +** However, these macros may also be used to suppress warnings related to +** parameters that may or may not be used depending on compilation options. +** For example those parameters only used in assert() statements. In these +** cases the parameters are named as per the usual conventions. +*/ +#define UNUSED_PARAMETER(x) (void)(x) +#define UNUSED_PARAMETER2(x,y) UNUSED_PARAMETER(x),UNUSED_PARAMETER(y) + +/* +** Forward references to structures +*/ +typedef struct AggInfo AggInfo; +typedef struct AuthContext AuthContext; +typedef struct Bitvec Bitvec; +typedef struct RowSet RowSet; +typedef struct CollSeq CollSeq; +typedef struct Column Column; +typedef struct Db Db; +typedef struct Schema Schema; +typedef struct Expr Expr; +typedef struct ExprList ExprList; +typedef struct FKey FKey; +typedef struct FuncDef FuncDef; +typedef struct FuncDefHash FuncDefHash; +typedef struct IdList IdList; +typedef struct Index Index; +typedef struct KeyClass KeyClass; +typedef struct KeyInfo KeyInfo; +typedef struct Lookaside Lookaside; +typedef struct LookasideSlot LookasideSlot; +typedef struct Module Module; +typedef struct NameContext NameContext; +typedef struct Parse Parse; +typedef struct Savepoint Savepoint; +typedef struct Select Select; +typedef struct SrcList SrcList; +typedef struct StrAccum StrAccum; +typedef struct Table Table; +typedef struct TableLock TableLock; +typedef struct Token Token; +typedef struct TriggerStack TriggerStack; +typedef struct TriggerStep TriggerStep; +typedef struct Trigger Trigger; +typedef struct UnpackedRecord UnpackedRecord; +typedef struct Walker Walker; +typedef struct WherePlan WherePlan; +typedef struct WhereInfo WhereInfo; +typedef struct WhereLevel WhereLevel; + +/* +** Defer sourcing vdbe.h and btree.h until after the "u8" and +** "BusyHandler" typedefs. vdbe.h also requires a few of the opaque +** pointer types (i.e. FuncDef) defined above. +*/ +/************** Include btree.h in the middle of sqliteInt.h *****************/ +/************** Begin file btree.h *******************************************/ +/* +** 2001 September 15 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +************************************************************************* +** This header file defines the interface that the sqlite B-Tree file +** subsystem. See comments in the source code for a detailed description +** of what each interface routine does. +** +** @(#) $Id: btree.h,v 1.111 2009/03/18 10:33:01 danielk1977 Exp $ +*/ +#ifndef _BTREE_H_ +#define _BTREE_H_ + +/* TODO: This definition is just included so other modules compile. It +** needs to be revisited. +*/ +#define SQLITE_N_BTREE_META 10 + +/* +** If defined as non-zero, auto-vacuum is enabled by default. Otherwise +** it must be turned on for each database using "PRAGMA auto_vacuum = 1". +*/ +#ifndef SQLITE_DEFAULT_AUTOVACUUM + #define SQLITE_DEFAULT_AUTOVACUUM 0 +#endif + +#define BTREE_AUTOVACUUM_NONE 0 /* Do not do auto-vacuum */ +#define BTREE_AUTOVACUUM_FULL 1 /* Do full auto-vacuum */ +#define BTREE_AUTOVACUUM_INCR 2 /* Incremental vacuum */ + +/* +** Forward declarations of structure +*/ +typedef struct Btree Btree; +typedef struct BtCursor BtCursor; +typedef struct BtShared BtShared; +typedef struct BtreeMutexArray BtreeMutexArray; + +/* +** This structure records all of the Btrees that need to hold +** a mutex before we enter sqlite3VdbeExec(). The Btrees are +** are placed in aBtree[] in order of aBtree[]->pBt. That way, +** we can always lock and unlock them all quickly. +*/ +struct BtreeMutexArray { + int nMutex; + Btree *aBtree[SQLITE_MAX_ATTACHED+1]; +}; + + +SQLITE_PRIVATE int sqlite3BtreeOpen( + const char *zFilename, /* Name of database file to open */ + sqlite3 *db, /* Associated database connection */ + Btree **, /* Return open Btree* here */ + int flags, /* Flags */ + int vfsFlags /* Flags passed through to VFS open */ +); + +/* The flags parameter to sqlite3BtreeOpen can be the bitwise or of the +** following values. +** +** NOTE: These values must match the corresponding PAGER_ values in +** pager.h. +*/ +#define BTREE_OMIT_JOURNAL 1 /* Do not use journal. No argument */ +#define BTREE_NO_READLOCK 2 /* Omit readlocks on readonly files */ +#define BTREE_MEMORY 4 /* In-memory DB. No argument */ +#define BTREE_READONLY 8 /* Open the database in read-only mode */ +#define BTREE_READWRITE 16 /* Open for both reading and writing */ +#define BTREE_CREATE 32 /* Create the database if it does not exist */ + +SQLITE_PRIVATE int sqlite3BtreeClose(Btree*); +SQLITE_PRIVATE int sqlite3BtreeSetCacheSize(Btree*,int); +SQLITE_PRIVATE int sqlite3BtreeSetSafetyLevel(Btree*,int,int); +SQLITE_PRIVATE int sqlite3BtreeSyncDisabled(Btree*); +SQLITE_PRIVATE int sqlite3BtreeSetPageSize(Btree*,int,int); +SQLITE_PRIVATE int sqlite3BtreeGetPageSize(Btree*); +SQLITE_PRIVATE int sqlite3BtreeMaxPageCount(Btree*,int); +SQLITE_PRIVATE int sqlite3BtreeGetReserve(Btree*); +SQLITE_PRIVATE int sqlite3BtreeSetAutoVacuum(Btree *, int); +SQLITE_PRIVATE int sqlite3BtreeGetAutoVacuum(Btree *); +SQLITE_PRIVATE int sqlite3BtreeBeginTrans(Btree*,int); +SQLITE_PRIVATE int sqlite3BtreeCommitPhaseOne(Btree*, const char *zMaster); +SQLITE_PRIVATE int sqlite3BtreeCommitPhaseTwo(Btree*); +SQLITE_PRIVATE int sqlite3BtreeCommit(Btree*); +SQLITE_PRIVATE int sqlite3BtreeRollback(Btree*); +SQLITE_PRIVATE int sqlite3BtreeBeginStmt(Btree*,int); +SQLITE_PRIVATE int sqlite3BtreeCreateTable(Btree*, int*, int flags); +SQLITE_PRIVATE int sqlite3BtreeIsInTrans(Btree*); +SQLITE_PRIVATE int sqlite3BtreeIsInReadTrans(Btree*); +SQLITE_PRIVATE int sqlite3BtreeIsInBackup(Btree*); +SQLITE_PRIVATE void *sqlite3BtreeSchema(Btree *, int, void(*)(void *)); +SQLITE_PRIVATE int sqlite3BtreeSchemaLocked(Btree *); +SQLITE_PRIVATE int sqlite3BtreeLockTable(Btree *, int, u8); +SQLITE_PRIVATE int sqlite3BtreeSavepoint(Btree *, int, int); + +SQLITE_PRIVATE const char *sqlite3BtreeGetFilename(Btree *); +SQLITE_PRIVATE const char *sqlite3BtreeGetJournalname(Btree *); +SQLITE_PRIVATE int sqlite3BtreeCopyFile(Btree *, Btree *); + +SQLITE_PRIVATE int sqlite3BtreeIncrVacuum(Btree *); + +/* The flags parameter to sqlite3BtreeCreateTable can be the bitwise OR +** of the following flags: +*/ +#define BTREE_INTKEY 1 /* Table has only 64-bit signed integer keys */ +#define BTREE_ZERODATA 2 /* Table has keys only - no data */ +#define BTREE_LEAFDATA 4 /* Data stored in leaves only. Implies INTKEY */ + +SQLITE_PRIVATE int sqlite3BtreeDropTable(Btree*, int, int*); +SQLITE_PRIVATE int sqlite3BtreeClearTable(Btree*, int, int*); +SQLITE_PRIVATE int sqlite3BtreeGetMeta(Btree*, int idx, u32 *pValue); +SQLITE_PRIVATE int sqlite3BtreeUpdateMeta(Btree*, int idx, u32 value); +SQLITE_PRIVATE void sqlite3BtreeTripAllCursors(Btree*, int); + +SQLITE_PRIVATE int sqlite3BtreeCursor( + Btree*, /* BTree containing table to open */ + int iTable, /* Index of root page */ + int wrFlag, /* 1 for writing. 0 for read-only */ + struct KeyInfo*, /* First argument to compare function */ + BtCursor *pCursor /* Space to write cursor structure */ +); +SQLITE_PRIVATE int sqlite3BtreeCursorSize(void); + +SQLITE_PRIVATE int sqlite3BtreeCloseCursor(BtCursor*); +SQLITE_PRIVATE int sqlite3BtreeMoveto( + BtCursor*, + const void *pKey, + i64 nKey, + int bias, + int *pRes +); +SQLITE_PRIVATE int sqlite3BtreeMovetoUnpacked( + BtCursor*, + UnpackedRecord *pUnKey, + i64 intKey, + int bias, + int *pRes +); +SQLITE_PRIVATE int sqlite3BtreeCursorHasMoved(BtCursor*, int*); +SQLITE_PRIVATE int sqlite3BtreeDelete(BtCursor*); +SQLITE_PRIVATE int sqlite3BtreeInsert(BtCursor*, const void *pKey, i64 nKey, + const void *pData, int nData, + int nZero, int bias); +SQLITE_PRIVATE int sqlite3BtreeFirst(BtCursor*, int *pRes); +SQLITE_PRIVATE int sqlite3BtreeLast(BtCursor*, int *pRes); +SQLITE_PRIVATE int sqlite3BtreeNext(BtCursor*, int *pRes); +SQLITE_PRIVATE int sqlite3BtreeEof(BtCursor*); +SQLITE_PRIVATE int sqlite3BtreeFlags(BtCursor*); +SQLITE_PRIVATE int sqlite3BtreePrevious(BtCursor*, int *pRes); +SQLITE_PRIVATE int sqlite3BtreeKeySize(BtCursor*, i64 *pSize); +SQLITE_PRIVATE int sqlite3BtreeKey(BtCursor*, u32 offset, u32 amt, void*); +SQLITE_PRIVATE sqlite3 *sqlite3BtreeCursorDb(const BtCursor*); +SQLITE_PRIVATE const void *sqlite3BtreeKeyFetch(BtCursor*, int *pAmt); +SQLITE_PRIVATE const void *sqlite3BtreeDataFetch(BtCursor*, int *pAmt); +SQLITE_PRIVATE int sqlite3BtreeDataSize(BtCursor*, u32 *pSize); +SQLITE_PRIVATE int sqlite3BtreeData(BtCursor*, u32 offset, u32 amt, void*); +SQLITE_PRIVATE void sqlite3BtreeSetCachedRowid(BtCursor*, sqlite3_int64); +SQLITE_PRIVATE sqlite3_int64 sqlite3BtreeGetCachedRowid(BtCursor*); + +SQLITE_PRIVATE char *sqlite3BtreeIntegrityCheck(Btree*, int *aRoot, int nRoot, int, int*); +SQLITE_PRIVATE struct Pager *sqlite3BtreePager(Btree*); + +SQLITE_PRIVATE int sqlite3BtreePutData(BtCursor*, u32 offset, u32 amt, void*); +SQLITE_PRIVATE void sqlite3BtreeCacheOverflow(BtCursor *); +SQLITE_PRIVATE void sqlite3BtreeClearCursor(BtCursor *); + +#ifndef SQLITE_OMIT_BTREECOUNT +SQLITE_PRIVATE int sqlite3BtreeCount(BtCursor *, i64 *); +#endif + +#ifdef SQLITE_TEST +SQLITE_PRIVATE int sqlite3BtreeCursorInfo(BtCursor*, int*, int); +SQLITE_PRIVATE void sqlite3BtreeCursorList(Btree*); +#endif + +/* +** If we are not using shared cache, then there is no need to +** use mutexes to access the BtShared structures. So make the +** Enter and Leave procedures no-ops. +*/ +#if !defined(SQLITE_OMIT_SHARED_CACHE) && SQLITE_THREADSAFE +SQLITE_PRIVATE void sqlite3BtreeEnter(Btree*); +SQLITE_PRIVATE void sqlite3BtreeLeave(Btree*); +#ifndef NDEBUG + /* This routine is used inside assert() statements only. */ +SQLITE_PRIVATE int sqlite3BtreeHoldsMutex(Btree*); +#endif +SQLITE_PRIVATE void sqlite3BtreeEnterCursor(BtCursor*); +SQLITE_PRIVATE void sqlite3BtreeLeaveCursor(BtCursor*); +SQLITE_PRIVATE void sqlite3BtreeEnterAll(sqlite3*); +SQLITE_PRIVATE void sqlite3BtreeLeaveAll(sqlite3*); +#ifndef NDEBUG + /* This routine is used inside assert() statements only. */ +SQLITE_PRIVATE int sqlite3BtreeHoldsAllMutexes(sqlite3*); +#endif +SQLITE_PRIVATE void sqlite3BtreeMutexArrayEnter(BtreeMutexArray*); +SQLITE_PRIVATE void sqlite3BtreeMutexArrayLeave(BtreeMutexArray*); +SQLITE_PRIVATE void sqlite3BtreeMutexArrayInsert(BtreeMutexArray*, Btree*); +#else +# define sqlite3BtreeEnter(X) +# define sqlite3BtreeLeave(X) +#ifndef NDEBUG + /* This routine is used inside assert() statements only. */ +# define sqlite3BtreeHoldsMutex(X) 1 +#endif +# define sqlite3BtreeEnterCursor(X) +# define sqlite3BtreeLeaveCursor(X) +# define sqlite3BtreeEnterAll(X) +# define sqlite3BtreeLeaveAll(X) +#ifndef NDEBUG + /* This routine is used inside assert() statements only. */ +# define sqlite3BtreeHoldsAllMutexes(X) 1 +#endif +# define sqlite3BtreeMutexArrayEnter(X) +# define sqlite3BtreeMutexArrayLeave(X) +# define sqlite3BtreeMutexArrayInsert(X,Y) +#endif + + +#endif /* _BTREE_H_ */ + +/************** End of btree.h ***********************************************/ +/************** Continuing where we left off in sqliteInt.h ******************/ +/************** Include vdbe.h in the middle of sqliteInt.h ******************/ +/************** Begin file vdbe.h ********************************************/ +/* +** 2001 September 15 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +************************************************************************* +** Header file for the Virtual DataBase Engine (VDBE) +** +** This header defines the interface to the virtual database engine +** or VDBE. The VDBE implements an abstract machine that runs a +** simple program to access and modify the underlying database. +** +** $Id: vdbe.h,v 1.140 2009/02/19 14:39:25 danielk1977 Exp $ +*/ +#ifndef _SQLITE_VDBE_H_ +#define _SQLITE_VDBE_H_ + +/* +** A single VDBE is an opaque structure named "Vdbe". Only routines +** in the source file sqliteVdbe.c are allowed to see the insides +** of this structure. +*/ +typedef struct Vdbe Vdbe; + +/* +** The names of the following types declared in vdbeInt.h are required +** for the VdbeOp definition. +*/ +typedef struct VdbeFunc VdbeFunc; +typedef struct Mem Mem; + +/* +** A single instruction of the virtual machine has an opcode +** and as many as three operands. The instruction is recorded +** as an instance of the following structure: +*/ +struct VdbeOp { + u8 opcode; /* What operation to perform */ + signed char p4type; /* One of the P4_xxx constants for p4 */ + u8 opflags; /* Not currently used */ + u8 p5; /* Fifth parameter is an unsigned character */ + int p1; /* First operand */ + int p2; /* Second parameter (often the jump destination) */ + int p3; /* The third parameter */ + union { /* forth parameter */ + int i; /* Integer value if p4type==P4_INT32 */ + void *p; /* Generic pointer */ + char *z; /* Pointer to data for string (char array) types */ + i64 *pI64; /* Used when p4type is P4_INT64 */ + double *pReal; /* Used when p4type is P4_REAL */ + FuncDef *pFunc; /* Used when p4type is P4_FUNCDEF */ + VdbeFunc *pVdbeFunc; /* Used when p4type is P4_VDBEFUNC */ + CollSeq *pColl; /* Used when p4type is P4_COLLSEQ */ + Mem *pMem; /* Used when p4type is P4_MEM */ + sqlite3_vtab *pVtab; /* Used when p4type is P4_VTAB */ + KeyInfo *pKeyInfo; /* Used when p4type is P4_KEYINFO */ + int *ai; /* Used when p4type is P4_INTARRAY */ + } p4; +#ifdef SQLITE_DEBUG + char *zComment; /* Comment to improve readability */ +#endif +#ifdef VDBE_PROFILE + int cnt; /* Number of times this instruction was executed */ + u64 cycles; /* Total time spent executing this instruction */ +#endif +}; +typedef struct VdbeOp VdbeOp; + +/* +** A smaller version of VdbeOp used for the VdbeAddOpList() function because +** it takes up less space. +*/ +struct VdbeOpList { + u8 opcode; /* What operation to perform */ + signed char p1; /* First operand */ + signed char p2; /* Second parameter (often the jump destination) */ + signed char p3; /* Third parameter */ +}; +typedef struct VdbeOpList VdbeOpList; + +/* +** Allowed values of VdbeOp.p3type +*/ +#define P4_NOTUSED 0 /* The P4 parameter is not used */ +#define P4_DYNAMIC (-1) /* Pointer to a string obtained from sqliteMalloc() */ +#define P4_STATIC (-2) /* Pointer to a static string */ +#define P4_COLLSEQ (-4) /* P4 is a pointer to a CollSeq structure */ +#define P4_FUNCDEF (-5) /* P4 is a pointer to a FuncDef structure */ +#define P4_KEYINFO (-6) /* P4 is a pointer to a KeyInfo structure */ +#define P4_VDBEFUNC (-7) /* P4 is a pointer to a VdbeFunc structure */ +#define P4_MEM (-8) /* P4 is a pointer to a Mem* structure */ +#define P4_TRANSIENT (-9) /* P4 is a pointer to a transient string */ +#define P4_VTAB (-10) /* P4 is a pointer to an sqlite3_vtab structure */ +#define P4_MPRINTF (-11) /* P4 is a string obtained from sqlite3_mprintf() */ +#define P4_REAL (-12) /* P4 is a 64-bit floating point value */ +#define P4_INT64 (-13) /* P4 is a 64-bit signed integer */ +#define P4_INT32 (-14) /* P4 is a 32-bit signed integer */ +#define P4_INTARRAY (-15) /* P4 is a vector of 32-bit integers */ + +/* When adding a P4 argument using P4_KEYINFO, a copy of the KeyInfo structure +** is made. That copy is freed when the Vdbe is finalized. But if the +** argument is P4_KEYINFO_HANDOFF, the passed in pointer is used. It still +** gets freed when the Vdbe is finalized so it still should be obtained +** from a single sqliteMalloc(). But no copy is made and the calling +** function should *not* try to free the KeyInfo. +*/ +#define P4_KEYINFO_HANDOFF (-16) +#define P4_KEYINFO_STATIC (-17) + +/* +** The Vdbe.aColName array contains 5n Mem structures, where n is the +** number of columns of data returned by the statement. +*/ +#define COLNAME_NAME 0 +#define COLNAME_DECLTYPE 1 +#define COLNAME_DATABASE 2 +#define COLNAME_TABLE 3 +#define COLNAME_COLUMN 4 +#ifdef SQLITE_ENABLE_COLUMN_METADATA +# define COLNAME_N 5 /* Number of COLNAME_xxx symbols */ +#else +# ifdef SQLITE_OMIT_DECLTYPE +# define COLNAME_N 1 /* Store only the name */ +# else +# define COLNAME_N 2 /* Store the name and decltype */ +# endif +#endif + +/* +** The following macro converts a relative address in the p2 field +** of a VdbeOp structure into a negative number so that +** sqlite3VdbeAddOpList() knows that the address is relative. Calling +** the macro again restores the address. +*/ +#define ADDR(X) (-1-(X)) + +/* +** The makefile scans the vdbe.c source file and creates the "opcodes.h" +** header file that defines a number for each opcode used by the VDBE. +*/ +/************** Include opcodes.h in the middle of vdbe.h ********************/ +/************** Begin file opcodes.h *****************************************/ +/* Automatically generated. Do not edit */ +/* See the mkopcodeh.awk script for details */ +#define OP_VNext 1 +#define OP_Affinity 2 +#define OP_Column 3 +#define OP_SetCookie 4 +#define OP_Seek 5 +#define OP_Real 130 /* same as TK_FLOAT */ +#define OP_Sequence 6 +#define OP_Savepoint 7 +#define OP_Ge 78 /* same as TK_GE */ +#define OP_RowKey 8 +#define OP_SCopy 9 +#define OP_Eq 74 /* same as TK_EQ */ +#define OP_OpenWrite 10 +#define OP_NotNull 72 /* same as TK_NOTNULL */ +#define OP_If 11 +#define OP_ToInt 144 /* same as TK_TO_INT */ +#define OP_String8 94 /* same as TK_STRING */ +#define OP_VRowid 12 +#define OP_CollSeq 13 +#define OP_OpenRead 14 +#define OP_Expire 15 +#define OP_AutoCommit 16 +#define OP_Gt 75 /* same as TK_GT */ +#define OP_Pagecount 17 +#define OP_IntegrityCk 18 +#define OP_Sort 20 +#define OP_Copy 21 +#define OP_Trace 22 +#define OP_Function 23 +#define OP_IfNeg 24 +#define OP_And 67 /* same as TK_AND */ +#define OP_Subtract 85 /* same as TK_MINUS */ +#define OP_Noop 25 +#define OP_Return 26 +#define OP_Remainder 88 /* same as TK_REM */ +#define OP_NewRowid 27 +#define OP_Multiply 86 /* same as TK_STAR */ +#define OP_Variable 28 +#define OP_String 29 +#define OP_RealAffinity 30 +#define OP_VRename 31 +#define OP_ParseSchema 32 +#define OP_VOpen 33 +#define OP_Close 34 +#define OP_CreateIndex 35 +#define OP_IsUnique 36 +#define OP_NotFound 37 +#define OP_Int64 38 +#define OP_MustBeInt 39 +#define OP_Halt 40 +#define OP_Rowid 41 +#define OP_IdxLT 42 +#define OP_AddImm 43 +#define OP_Statement 44 +#define OP_RowData 45 +#define OP_MemMax 46 +#define OP_Or 66 /* same as TK_OR */ +#define OP_NotExists 47 +#define OP_Gosub 48 +#define OP_Divide 87 /* same as TK_SLASH */ +#define OP_Integer 49 +#define OP_ToNumeric 143 /* same as TK_TO_NUMERIC*/ +#define OP_Prev 50 +#define OP_RowSetRead 51 +#define OP_Concat 89 /* same as TK_CONCAT */ +#define OP_RowSetAdd 52 +#define OP_BitAnd 80 /* same as TK_BITAND */ +#define OP_VColumn 53 +#define OP_CreateTable 54 +#define OP_Last 55 +#define OP_SeekLe 56 +#define OP_IsNull 71 /* same as TK_ISNULL */ +#define OP_IncrVacuum 57 +#define OP_IdxRowid 58 +#define OP_ShiftRight 83 /* same as TK_RSHIFT */ +#define OP_ResetCount 59 +#define OP_ContextPush 60 +#define OP_Yield 61 +#define OP_DropTrigger 62 +#define OP_DropIndex 63 +#define OP_IdxGE 64 +#define OP_IdxDelete 65 +#define OP_Vacuum 68 +#define OP_IfNot 69 +#define OP_DropTable 70 +#define OP_SeekLt 79 +#define OP_MakeRecord 90 +#define OP_ToBlob 142 /* same as TK_TO_BLOB */ +#define OP_ResultRow 91 +#define OP_Delete 92 +#define OP_AggFinal 95 +#define OP_Compare 96 +#define OP_ShiftLeft 82 /* same as TK_LSHIFT */ +#define OP_Goto 97 +#define OP_TableLock 98 +#define OP_Clear 99 +#define OP_Le 76 /* same as TK_LE */ +#define OP_VerifyCookie 100 +#define OP_AggStep 101 +#define OP_ToText 141 /* same as TK_TO_TEXT */ +#define OP_Not 19 /* same as TK_NOT */ +#define OP_ToReal 145 /* same as TK_TO_REAL */ +#define OP_SetNumColumns 102 +#define OP_Transaction 103 +#define OP_VFilter 104 +#define OP_Ne 73 /* same as TK_NE */ +#define OP_VDestroy 105 +#define OP_ContextPop 106 +#define OP_BitOr 81 /* same as TK_BITOR */ +#define OP_Next 107 +#define OP_Count 108 +#define OP_IdxInsert 109 +#define OP_Lt 77 /* same as TK_LT */ +#define OP_SeekGe 110 +#define OP_Insert 111 +#define OP_Destroy 112 +#define OP_ReadCookie 113 +#define OP_LoadAnalysis 114 +#define OP_Explain 115 +#define OP_HaltIfNull 116 +#define OP_OpenPseudo 117 +#define OP_OpenEphemeral 118 +#define OP_Null 119 +#define OP_Move 120 +#define OP_Blob 121 +#define OP_Add 84 /* same as TK_PLUS */ +#define OP_Rewind 122 +#define OP_SeekGt 123 +#define OP_VBegin 124 +#define OP_VUpdate 125 +#define OP_IfZero 126 +#define OP_BitNot 93 /* same as TK_BITNOT */ +#define OP_VCreate 127 +#define OP_Found 128 +#define OP_IfPos 129 +#define OP_NullRow 131 +#define OP_Jump 132 +#define OP_Permutation 133 + +/* The following opcode values are never used */ +#define OP_NotUsed_134 134 +#define OP_NotUsed_135 135 +#define OP_NotUsed_136 136 +#define OP_NotUsed_137 137 +#define OP_NotUsed_138 138 +#define OP_NotUsed_139 139 +#define OP_NotUsed_140 140 + + +/* Properties such as "out2" or "jump" that are specified in +** comments following the "case" for each opcode in the vdbe.c +** are encoded into bitvectors as follows: +*/ +#define OPFLG_JUMP 0x0001 /* jump: P2 holds jmp target */ +#define OPFLG_OUT2_PRERELEASE 0x0002 /* out2-prerelease: */ +#define OPFLG_IN1 0x0004 /* in1: P1 is an input */ +#define OPFLG_IN2 0x0008 /* in2: P2 is an input */ +#define OPFLG_IN3 0x0010 /* in3: P3 is an input */ +#define OPFLG_OUT3 0x0020 /* out3: P3 is an output */ +#define OPFLG_INITIALIZER {\ +/* 0 */ 0x00, 0x01, 0x00, 0x00, 0x10, 0x08, 0x02, 0x00,\ +/* 8 */ 0x00, 0x04, 0x00, 0x05, 0x02, 0x00, 0x00, 0x00,\ +/* 16 */ 0x00, 0x02, 0x00, 0x04, 0x01, 0x04, 0x00, 0x00,\ +/* 24 */ 0x05, 0x00, 0x04, 0x02, 0x00, 0x02, 0x04, 0x00,\ +/* 32 */ 0x00, 0x00, 0x00, 0x02, 0x11, 0x11, 0x02, 0x05,\ +/* 40 */ 0x00, 0x02, 0x11, 0x04, 0x00, 0x00, 0x0c, 0x11,\ +/* 48 */ 0x01, 0x02, 0x01, 0x21, 0x08, 0x00, 0x02, 0x01,\ +/* 56 */ 0x11, 0x01, 0x02, 0x00, 0x00, 0x04, 0x00, 0x00,\ +/* 64 */ 0x11, 0x00, 0x2c, 0x2c, 0x00, 0x05, 0x00, 0x05,\ +/* 72 */ 0x05, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x11,\ +/* 80 */ 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c,\ +/* 88 */ 0x2c, 0x2c, 0x00, 0x00, 0x00, 0x04, 0x02, 0x00,\ +/* 96 */ 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\ +/* 104 */ 0x01, 0x00, 0x00, 0x01, 0x02, 0x08, 0x11, 0x00,\ +/* 112 */ 0x02, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x02,\ +/* 120 */ 0x00, 0x02, 0x01, 0x11, 0x00, 0x00, 0x05, 0x00,\ +/* 128 */ 0x11, 0x05, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00,\ +/* 136 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x04, 0x04,\ +/* 144 */ 0x04, 0x04,} + +/************** End of opcodes.h *********************************************/ +/************** Continuing where we left off in vdbe.h ***********************/ + +/* +** Prototypes for the VDBE interface. See comments on the implementation +** for a description of what each of these routines does. +*/ +SQLITE_PRIVATE Vdbe *sqlite3VdbeCreate(sqlite3*); +SQLITE_PRIVATE int sqlite3VdbeAddOp0(Vdbe*,int); +SQLITE_PRIVATE int sqlite3VdbeAddOp1(Vdbe*,int,int); +SQLITE_PRIVATE int sqlite3VdbeAddOp2(Vdbe*,int,int,int); +SQLITE_PRIVATE int sqlite3VdbeAddOp3(Vdbe*,int,int,int,int); +SQLITE_PRIVATE int sqlite3VdbeAddOp4(Vdbe*,int,int,int,int,const char *zP4,int); +SQLITE_PRIVATE int sqlite3VdbeAddOpList(Vdbe*, int nOp, VdbeOpList const *aOp); +SQLITE_PRIVATE void sqlite3VdbeChangeP1(Vdbe*, int addr, int P1); +SQLITE_PRIVATE void sqlite3VdbeChangeP2(Vdbe*, int addr, int P2); +SQLITE_PRIVATE void sqlite3VdbeChangeP3(Vdbe*, int addr, int P3); +SQLITE_PRIVATE void sqlite3VdbeChangeP5(Vdbe*, u8 P5); +SQLITE_PRIVATE void sqlite3VdbeJumpHere(Vdbe*, int addr); +SQLITE_PRIVATE void sqlite3VdbeChangeToNoop(Vdbe*, int addr, int N); +SQLITE_PRIVATE void sqlite3VdbeChangeP4(Vdbe*, int addr, const char *zP4, int N); +SQLITE_PRIVATE void sqlite3VdbeUsesBtree(Vdbe*, int); +SQLITE_PRIVATE VdbeOp *sqlite3VdbeGetOp(Vdbe*, int); +SQLITE_PRIVATE int sqlite3VdbeMakeLabel(Vdbe*); +SQLITE_PRIVATE void sqlite3VdbeDelete(Vdbe*); +SQLITE_PRIVATE void sqlite3VdbeMakeReady(Vdbe*,int,int,int,int); +SQLITE_PRIVATE int sqlite3VdbeFinalize(Vdbe*); +SQLITE_PRIVATE void sqlite3VdbeResolveLabel(Vdbe*, int); +SQLITE_PRIVATE int sqlite3VdbeCurrentAddr(Vdbe*); +#ifdef SQLITE_DEBUG +SQLITE_PRIVATE void sqlite3VdbeTrace(Vdbe*,FILE*); +#endif +SQLITE_PRIVATE void sqlite3VdbeResetStepResult(Vdbe*); +SQLITE_PRIVATE int sqlite3VdbeReset(Vdbe*); +SQLITE_PRIVATE void sqlite3VdbeSetNumCols(Vdbe*,int); +SQLITE_PRIVATE int sqlite3VdbeSetColName(Vdbe*, int, int, const char *, void(*)(void*)); +SQLITE_PRIVATE void sqlite3VdbeCountChanges(Vdbe*); +SQLITE_PRIVATE sqlite3 *sqlite3VdbeDb(Vdbe*); +SQLITE_PRIVATE void sqlite3VdbeSetSql(Vdbe*, const char *z, int n, int); +SQLITE_PRIVATE void sqlite3VdbeSwap(Vdbe*,Vdbe*); + +#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT +SQLITE_PRIVATE int sqlite3VdbeReleaseMemory(int); +#endif +SQLITE_PRIVATE UnpackedRecord *sqlite3VdbeRecordUnpack(KeyInfo*,int,const void*, + UnpackedRecord*,int); +SQLITE_PRIVATE void sqlite3VdbeDeleteUnpackedRecord(UnpackedRecord*); +SQLITE_PRIVATE int sqlite3VdbeRecordCompare(int,const void*,UnpackedRecord*); + + +#ifndef NDEBUG +SQLITE_PRIVATE void sqlite3VdbeComment(Vdbe*, const char*, ...); +# define VdbeComment(X) sqlite3VdbeComment X +SQLITE_PRIVATE void sqlite3VdbeNoopComment(Vdbe*, const char*, ...); +# define VdbeNoopComment(X) sqlite3VdbeNoopComment X +#else +# define VdbeComment(X) +# define VdbeNoopComment(X) +#endif + +#endif + +/************** End of vdbe.h ************************************************/ +/************** Continuing where we left off in sqliteInt.h ******************/ +/************** Include pager.h in the middle of sqliteInt.h *****************/ +/************** Begin file pager.h *******************************************/ +/* +** 2001 September 15 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +************************************************************************* +** This header file defines the interface that the sqlite page cache +** subsystem. The page cache subsystem reads and writes a file a page +** at a time and provides a journal for rollback. +** +** @(#) $Id: pager.h,v 1.100 2009/02/03 16:51:25 danielk1977 Exp $ +*/ + +#ifndef _PAGER_H_ +#define _PAGER_H_ + +/* +** Default maximum size for persistent journal files. A negative +** value means no limit. This value may be overridden using the +** sqlite3PagerJournalSizeLimit() API. See also "PRAGMA journal_size_limit". +*/ +#ifndef SQLITE_DEFAULT_JOURNAL_SIZE_LIMIT + #define SQLITE_DEFAULT_JOURNAL_SIZE_LIMIT -1 +#endif + +/* +** The type used to represent a page number. The first page in a file +** is called page 1. 0 is used to represent "not a page". +*/ +typedef u32 Pgno; + +/* +** Each open file is managed by a separate instance of the "Pager" structure. +*/ +typedef struct Pager Pager; + +/* +** Handle type for pages. +*/ +typedef struct PgHdr DbPage; + +/* +** Page number PAGER_MJ_PGNO is never used in an SQLite database (it is +** reserved for working around a windows/posix incompatibility). It is +** used in the journal to signify that the remainder of the journal file +** is devoted to storing a master journal name - there are no more pages to +** roll back. See comments for function writeMasterJournal() in pager.c +** for details. +*/ +#define PAGER_MJ_PGNO(x) ((Pgno)((PENDING_BYTE/((x)->pageSize))+1)) + +/* +** Allowed values for the flags parameter to sqlite3PagerOpen(). +** +** NOTE: These values must match the corresponding BTREE_ values in btree.h. +*/ +#define PAGER_OMIT_JOURNAL 0x0001 /* Do not use a rollback journal */ +#define PAGER_NO_READLOCK 0x0002 /* Omit readlocks on readonly files */ + +/* +** Valid values for the second argument to sqlite3PagerLockingMode(). +*/ +#define PAGER_LOCKINGMODE_QUERY -1 +#define PAGER_LOCKINGMODE_NORMAL 0 +#define PAGER_LOCKINGMODE_EXCLUSIVE 1 + +/* +** Valid values for the second argument to sqlite3PagerJournalMode(). +*/ +#define PAGER_JOURNALMODE_QUERY -1 +#define PAGER_JOURNALMODE_DELETE 0 /* Commit by deleting journal file */ +#define PAGER_JOURNALMODE_PERSIST 1 /* Commit by zeroing journal header */ +#define PAGER_JOURNALMODE_OFF 2 /* Journal omitted. */ +#define PAGER_JOURNALMODE_TRUNCATE 3 /* Commit by truncating journal */ +#define PAGER_JOURNALMODE_MEMORY 4 /* In-memory journal file */ + +/* +** The remainder of this file contains the declarations of the functions +** that make up the Pager sub-system API. See source code comments for +** a detailed description of each routine. +*/ + +/* Open and close a Pager connection. */ +SQLITE_PRIVATE int sqlite3PagerOpen(sqlite3_vfs *, Pager **ppPager, const char*, int,int,int); +SQLITE_PRIVATE int sqlite3PagerClose(Pager *pPager); +SQLITE_PRIVATE int sqlite3PagerReadFileheader(Pager*, int, unsigned char*); + +/* Functions used to configure a Pager object. */ +SQLITE_PRIVATE void sqlite3PagerSetBusyhandler(Pager*, int(*)(void *), void *); +SQLITE_PRIVATE void sqlite3PagerSetReiniter(Pager*, void(*)(DbPage*)); +SQLITE_PRIVATE int sqlite3PagerSetPagesize(Pager*, u16*); +SQLITE_PRIVATE int sqlite3PagerMaxPageCount(Pager*, int); +SQLITE_PRIVATE void sqlite3PagerSetCachesize(Pager*, int); +SQLITE_PRIVATE void sqlite3PagerSetSafetyLevel(Pager*,int,int); +SQLITE_PRIVATE int sqlite3PagerLockingMode(Pager *, int); +SQLITE_PRIVATE int sqlite3PagerJournalMode(Pager *, int); +SQLITE_PRIVATE i64 sqlite3PagerJournalSizeLimit(Pager *, i64); +SQLITE_PRIVATE sqlite3_backup **sqlite3PagerBackupPtr(Pager*); + +/* Functions used to obtain and release page references. */ +SQLITE_PRIVATE int sqlite3PagerAcquire(Pager *pPager, Pgno pgno, DbPage **ppPage, int clrFlag); +#define sqlite3PagerGet(A,B,C) sqlite3PagerAcquire(A,B,C,0) +SQLITE_PRIVATE DbPage *sqlite3PagerLookup(Pager *pPager, Pgno pgno); +SQLITE_PRIVATE void sqlite3PagerRef(DbPage*); +SQLITE_PRIVATE void sqlite3PagerUnref(DbPage*); + +/* Operations on page references. */ +SQLITE_PRIVATE int sqlite3PagerWrite(DbPage*); +SQLITE_PRIVATE void sqlite3PagerDontWrite(DbPage*); +SQLITE_PRIVATE int sqlite3PagerMovepage(Pager*,DbPage*,Pgno,int); +SQLITE_PRIVATE int sqlite3PagerPageRefcount(DbPage*); +SQLITE_PRIVATE void *sqlite3PagerGetData(DbPage *); +SQLITE_PRIVATE void *sqlite3PagerGetExtra(DbPage *); + +/* Functions used to manage pager transactions and savepoints. */ +SQLITE_PRIVATE int sqlite3PagerPagecount(Pager*, int*); +SQLITE_PRIVATE int sqlite3PagerBegin(Pager*, int exFlag); +SQLITE_PRIVATE int sqlite3PagerCommitPhaseOne(Pager*,const char *zMaster, int); +SQLITE_PRIVATE int sqlite3PagerSync(Pager *pPager); +SQLITE_PRIVATE int sqlite3PagerCommitPhaseTwo(Pager*); +SQLITE_PRIVATE int sqlite3PagerRollback(Pager*); +SQLITE_PRIVATE int sqlite3PagerOpenSavepoint(Pager *pPager, int n); +SQLITE_PRIVATE int sqlite3PagerSavepoint(Pager *pPager, int op, int iSavepoint); + +/* Functions used to query pager state and configuration. */ +SQLITE_PRIVATE u8 sqlite3PagerIsreadonly(Pager*); +SQLITE_PRIVATE int sqlite3PagerRefcount(Pager*); +SQLITE_PRIVATE const char *sqlite3PagerFilename(Pager*); +SQLITE_PRIVATE const sqlite3_vfs *sqlite3PagerVfs(Pager*); +SQLITE_PRIVATE sqlite3_file *sqlite3PagerFile(Pager*); +SQLITE_PRIVATE const char *sqlite3PagerJournalname(Pager*); +SQLITE_PRIVATE int sqlite3PagerNosync(Pager*); +SQLITE_PRIVATE void *sqlite3PagerTempSpace(Pager*); +SQLITE_PRIVATE int sqlite3PagerIsMemdb(Pager*); + +/* Functions used to truncate the database file. */ +SQLITE_PRIVATE void sqlite3PagerTruncateImage(Pager*,Pgno); + +/* Used by encryption extensions. */ +#ifdef SQLITE_HAS_CODEC +SQLITE_PRIVATE void sqlite3PagerSetCodec(Pager*,void*(*)(void*,void*,Pgno,int),void*); +#endif + +/* Functions to support testing and debugging. */ +#if !defined(NDEBUG) || defined(SQLITE_TEST) +SQLITE_PRIVATE Pgno sqlite3PagerPagenumber(DbPage*); +SQLITE_PRIVATE int sqlite3PagerIswriteable(DbPage*); +#endif +#ifdef SQLITE_TEST +SQLITE_PRIVATE int *sqlite3PagerStats(Pager*); +SQLITE_PRIVATE void sqlite3PagerRefdump(Pager*); + void disable_simulated_io_errors(void); + void enable_simulated_io_errors(void); +#else +# define disable_simulated_io_errors() +# define enable_simulated_io_errors() +#endif + +#endif /* _PAGER_H_ */ + +/************** End of pager.h ***********************************************/ +/************** Continuing where we left off in sqliteInt.h ******************/ +/************** Include pcache.h in the middle of sqliteInt.h ****************/ +/************** Begin file pcache.h ******************************************/ +/* +** 2008 August 05 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +************************************************************************* +** This header file defines the interface that the sqlite page cache +** subsystem. +** +** @(#) $Id: pcache.h,v 1.19 2009/01/20 17:06:27 danielk1977 Exp $ +*/ + +#ifndef _PCACHE_H_ + +typedef struct PgHdr PgHdr; +typedef struct PCache PCache; + +/* +** Every page in the cache is controlled by an instance of the following +** structure. +*/ +struct PgHdr { + void *pData; /* Content of this page */ + void *pExtra; /* Extra content */ + PgHdr *pDirty; /* Transient list of dirty pages */ + Pgno pgno; /* Page number for this page */ + Pager *pPager; /* The pager this page is part of */ +#ifdef SQLITE_CHECK_PAGES + u32 pageHash; /* Hash of page content */ +#endif + u16 flags; /* PGHDR flags defined below */ + + /********************************************************************** + ** Elements above are public. All that follows is private to pcache.c + ** and should not be accessed by other modules. + */ + i16 nRef; /* Number of users of this page */ + PCache *pCache; /* Cache that owns this page */ + + PgHdr *pDirtyNext; /* Next element in list of dirty pages */ + PgHdr *pDirtyPrev; /* Previous element in list of dirty pages */ +}; + +/* Bit values for PgHdr.flags */ +#define PGHDR_DIRTY 0x002 /* Page has changed */ +#define PGHDR_NEED_SYNC 0x004 /* Fsync the rollback journal before + ** writing this page to the database */ +#define PGHDR_NEED_READ 0x008 /* Content is unread */ +#define PGHDR_REUSE_UNLIKELY 0x010 /* A hint that reuse is unlikely */ +#define PGHDR_DONT_WRITE 0x020 /* Do not write content to disk */ + +/* Initialize and shutdown the page cache subsystem */ +SQLITE_PRIVATE int sqlite3PcacheInitialize(void); +SQLITE_PRIVATE void sqlite3PcacheShutdown(void); + +/* Page cache buffer management: +** These routines implement SQLITE_CONFIG_PAGECACHE. +*/ +SQLITE_PRIVATE void sqlite3PCacheBufferSetup(void *, int sz, int n); + +/* Create a new pager cache. +** Under memory stress, invoke xStress to try to make pages clean. +** Only clean and unpinned pages can be reclaimed. +*/ +SQLITE_PRIVATE void sqlite3PcacheOpen( + int szPage, /* Size of every page */ + int szExtra, /* Extra space associated with each page */ + int bPurgeable, /* True if pages are on backing store */ + int (*xStress)(void*, PgHdr*), /* Call to try to make pages clean */ + void *pStress, /* Argument to xStress */ + PCache *pToInit /* Preallocated space for the PCache */ +); + +/* Modify the page-size after the cache has been created. */ +SQLITE_PRIVATE void sqlite3PcacheSetPageSize(PCache *, int); + +/* Return the size in bytes of a PCache object. Used to preallocate +** storage space. +*/ +SQLITE_PRIVATE int sqlite3PcacheSize(void); + +/* One release per successful fetch. Page is pinned until released. +** Reference counted. +*/ +SQLITE_PRIVATE int sqlite3PcacheFetch(PCache*, Pgno, int createFlag, PgHdr**); +SQLITE_PRIVATE void sqlite3PcacheRelease(PgHdr*); + +SQLITE_PRIVATE void sqlite3PcacheDrop(PgHdr*); /* Remove page from cache */ +SQLITE_PRIVATE void sqlite3PcacheMakeDirty(PgHdr*); /* Make sure page is marked dirty */ +SQLITE_PRIVATE void sqlite3PcacheMakeClean(PgHdr*); /* Mark a single page as clean */ +SQLITE_PRIVATE void sqlite3PcacheCleanAll(PCache*); /* Mark all dirty list pages as clean */ + +/* Change a page number. Used by incr-vacuum. */ +SQLITE_PRIVATE void sqlite3PcacheMove(PgHdr*, Pgno); + +/* Remove all pages with pgno>x. Reset the cache if x==0 */ +SQLITE_PRIVATE void sqlite3PcacheTruncate(PCache*, Pgno x); + +/* Get a list of all dirty pages in the cache, sorted by page number */ +SQLITE_PRIVATE PgHdr *sqlite3PcacheDirtyList(PCache*); + +/* Reset and close the cache object */ +SQLITE_PRIVATE void sqlite3PcacheClose(PCache*); + +/* Clear flags from pages of the page cache */ +SQLITE_PRIVATE void sqlite3PcacheClearSyncFlags(PCache *); + +/* Discard the contents of the cache */ +SQLITE_PRIVATE void sqlite3PcacheClear(PCache*); + +/* Return the total number of outstanding page references */ +SQLITE_PRIVATE int sqlite3PcacheRefCount(PCache*); + +/* Increment the reference count of an existing page */ +SQLITE_PRIVATE void sqlite3PcacheRef(PgHdr*); + +SQLITE_PRIVATE int sqlite3PcachePageRefcount(PgHdr*); + +/* Return the total number of pages stored in the cache */ +SQLITE_PRIVATE int sqlite3PcachePagecount(PCache*); + +#ifdef SQLITE_CHECK_PAGES +/* Iterate through all dirty pages currently stored in the cache. This +** interface is only available if SQLITE_CHECK_PAGES is defined when the +** library is built. +*/ +SQLITE_PRIVATE void sqlite3PcacheIterateDirty(PCache *pCache, void (*xIter)(PgHdr *)); +#endif + +/* Set and get the suggested cache-size for the specified pager-cache. +** +** If no global maximum is configured, then the system attempts to limit +** the total number of pages cached by purgeable pager-caches to the sum +** of the suggested cache-sizes. +*/ +SQLITE_PRIVATE void sqlite3PcacheSetCachesize(PCache *, int); +#ifdef SQLITE_TEST +SQLITE_PRIVATE int sqlite3PcacheGetCachesize(PCache *); +#endif + +#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT +/* Try to return memory used by the pcache module to the main memory heap */ +SQLITE_PRIVATE int sqlite3PcacheReleaseMemory(int); +#endif + +#ifdef SQLITE_TEST +SQLITE_PRIVATE void sqlite3PcacheStats(int*,int*,int*,int*); +#endif + +SQLITE_PRIVATE void sqlite3PCacheSetDefault(void); + +#endif /* _PCACHE_H_ */ + +/************** End of pcache.h **********************************************/ +/************** Continuing where we left off in sqliteInt.h ******************/ + +/************** Include os.h in the middle of sqliteInt.h ********************/ +/************** Begin file os.h **********************************************/ +/* +** 2001 September 16 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +****************************************************************************** +** +** This header file (together with is companion C source-code file +** "os.c") attempt to abstract the underlying operating system so that +** the SQLite library will work on both POSIX and windows systems. +** +** This header file is #include-ed by sqliteInt.h and thus ends up +** being included by every source file. +** +** $Id: os.h,v 1.108 2009/02/05 16:31:46 drh Exp $ +*/ +#ifndef _SQLITE_OS_H_ +#define _SQLITE_OS_H_ + +/* +** Figure out if we are dealing with Unix, Windows, or some other +** operating system. After the following block of preprocess macros, +** all of SQLITE_OS_UNIX, SQLITE_OS_WIN, SQLITE_OS_OS2, and SQLITE_OS_OTHER +** will defined to either 1 or 0. One of the four will be 1. The other +** three will be 0. +*/ +#if defined(SQLITE_OS_OTHER) +# if SQLITE_OS_OTHER==1 +# undef SQLITE_OS_UNIX +# define SQLITE_OS_UNIX 0 +# undef SQLITE_OS_WIN +# define SQLITE_OS_WIN 0 +# undef SQLITE_OS_OS2 +# define SQLITE_OS_OS2 0 +# else +# undef SQLITE_OS_OTHER +# endif +#endif +#if !defined(SQLITE_OS_UNIX) && !defined(SQLITE_OS_OTHER) +# define SQLITE_OS_OTHER 0 +# ifndef SQLITE_OS_WIN +# if defined(_WIN32) || defined(WIN32) || defined(__CYGWIN__) || defined(__MINGW32__) || defined(__BORLANDC__) +# define SQLITE_OS_WIN 1 +# define SQLITE_OS_UNIX 0 +# define SQLITE_OS_OS2 0 +# elif defined(__EMX__) || defined(_OS2) || defined(OS2) || defined(_OS2_) || defined(__OS2__) +# define SQLITE_OS_WIN 0 +# define SQLITE_OS_UNIX 0 +# define SQLITE_OS_OS2 1 +# else +# define SQLITE_OS_WIN 0 +# define SQLITE_OS_UNIX 1 +# define SQLITE_OS_OS2 0 +# endif +# else +# define SQLITE_OS_UNIX 0 +# define SQLITE_OS_OS2 0 +# endif +#else +# ifndef SQLITE_OS_WIN +# define SQLITE_OS_WIN 0 +# endif +#endif + +/* +** Determine if we are dealing with WindowsCE - which has a much +** reduced API. +*/ +#if defined(_WIN32_WCE) +# define SQLITE_OS_WINCE 1 +#else +# define SQLITE_OS_WINCE 0 +#endif + + +/* +** Define the maximum size of a temporary filename +*/ +#if SQLITE_OS_WIN +# include +# define SQLITE_TEMPNAME_SIZE (MAX_PATH+50) +#elif SQLITE_OS_OS2 +# if (__GNUC__ > 3 || __GNUC__ == 3 && __GNUC_MINOR__ >= 3) && defined(OS2_HIGH_MEMORY) +# include /* has to be included before os2.h for linking to work */ +# endif +# define INCL_DOSDATETIME +# define INCL_DOSFILEMGR +# define INCL_DOSERRORS +# define INCL_DOSMISC +# define INCL_DOSPROCESS +# define INCL_DOSMODULEMGR +# define INCL_DOSSEMAPHORES +# include +# include +# define SQLITE_TEMPNAME_SIZE (CCHMAXPATHCOMP) +#else +# define SQLITE_TEMPNAME_SIZE 200 +#endif + +/* If the SET_FULLSYNC macro is not defined above, then make it +** a no-op +*/ +#ifndef SET_FULLSYNC +# define SET_FULLSYNC(x,y) +#endif + +/* +** The default size of a disk sector +*/ +#ifndef SQLITE_DEFAULT_SECTOR_SIZE +# define SQLITE_DEFAULT_SECTOR_SIZE 512 +#endif + +/* +** Temporary files are named starting with this prefix followed by 16 random +** alphanumeric characters, and no file extension. They are stored in the +** OS's standard temporary file directory, and are deleted prior to exit. +** If sqlite is being embedded in another program, you may wish to change the +** prefix to reflect your program's name, so that if your program exits +** prematurely, old temporary files can be easily identified. This can be done +** using -DSQLITE_TEMP_FILE_PREFIX=myprefix_ on the compiler command line. +** +** 2006-10-31: The default prefix used to be "sqlite_". But then +** Mcafee started using SQLite in their anti-virus product and it +** started putting files with the "sqlite" name in the c:/temp folder. +** This annoyed many windows users. Those users would then do a +** Google search for "sqlite", find the telephone numbers of the +** developers and call to wake them up at night and complain. +** For this reason, the default name prefix is changed to be "sqlite" +** spelled backwards. So the temp files are still identified, but +** anybody smart enough to figure out the code is also likely smart +** enough to know that calling the developer will not help get rid +** of the file. +*/ +#ifndef SQLITE_TEMP_FILE_PREFIX +# define SQLITE_TEMP_FILE_PREFIX "etilqs_" +#endif + +/* +** The following values may be passed as the second argument to +** sqlite3OsLock(). The various locks exhibit the following semantics: +** +** SHARED: Any number of processes may hold a SHARED lock simultaneously. +** RESERVED: A single process may hold a RESERVED lock on a file at +** any time. Other processes may hold and obtain new SHARED locks. +** PENDING: A single process may hold a PENDING lock on a file at +** any one time. Existing SHARED locks may persist, but no new +** SHARED locks may be obtained by other processes. +** EXCLUSIVE: An EXCLUSIVE lock precludes all other locks. +** +** PENDING_LOCK may not be passed directly to sqlite3OsLock(). Instead, a +** process that requests an EXCLUSIVE lock may actually obtain a PENDING +** lock. This can be upgraded to an EXCLUSIVE lock by a subsequent call to +** sqlite3OsLock(). +*/ +#define NO_LOCK 0 +#define SHARED_LOCK 1 +#define RESERVED_LOCK 2 +#define PENDING_LOCK 3 +#define EXCLUSIVE_LOCK 4 + +/* +** File Locking Notes: (Mostly about windows but also some info for Unix) +** +** We cannot use LockFileEx() or UnlockFileEx() on Win95/98/ME because +** those functions are not available. So we use only LockFile() and +** UnlockFile(). +** +** LockFile() prevents not just writing but also reading by other processes. +** A SHARED_LOCK is obtained by locking a single randomly-chosen +** byte out of a specific range of bytes. The lock byte is obtained at +** random so two separate readers can probably access the file at the +** same time, unless they are unlucky and choose the same lock byte. +** An EXCLUSIVE_LOCK is obtained by locking all bytes in the range. +** There can only be one writer. A RESERVED_LOCK is obtained by locking +** a single byte of the file that is designated as the reserved lock byte. +** A PENDING_LOCK is obtained by locking a designated byte different from +** the RESERVED_LOCK byte. +** +** On WinNT/2K/XP systems, LockFileEx() and UnlockFileEx() are available, +** which means we can use reader/writer locks. When reader/writer locks +** are used, the lock is placed on the same range of bytes that is used +** for probabilistic locking in Win95/98/ME. Hence, the locking scheme +** will support two or more Win95 readers or two or more WinNT readers. +** But a single Win95 reader will lock out all WinNT readers and a single +** WinNT reader will lock out all other Win95 readers. +** +** The following #defines specify the range of bytes used for locking. +** SHARED_SIZE is the number of bytes available in the pool from which +** a random byte is selected for a shared lock. The pool of bytes for +** shared locks begins at SHARED_FIRST. +** +** The same locking strategy and +** byte ranges are used for Unix. This leaves open the possiblity of having +** clients on win95, winNT, and unix all talking to the same shared file +** and all locking correctly. To do so would require that samba (or whatever +** tool is being used for file sharing) implements locks correctly between +** windows and unix. I'm guessing that isn't likely to happen, but by +** using the same locking range we are at least open to the possibility. +** +** Locking in windows is manditory. For this reason, we cannot store +** actual data in the bytes used for locking. The pager never allocates +** the pages involved in locking therefore. SHARED_SIZE is selected so +** that all locks will fit on a single page even at the minimum page size. +** PENDING_BYTE defines the beginning of the locks. By default PENDING_BYTE +** is set high so that we don't have to allocate an unused page except +** for very large databases. But one should test the page skipping logic +** by setting PENDING_BYTE low and running the entire regression suite. +** +** Changing the value of PENDING_BYTE results in a subtly incompatible +** file format. Depending on how it is changed, you might not notice +** the incompatibility right away, even running a full regression test. +** The default location of PENDING_BYTE is the first byte past the +** 1GB boundary. +** +*/ +#define PENDING_BYTE sqlite3PendingByte +#define RESERVED_BYTE (PENDING_BYTE+1) +#define SHARED_FIRST (PENDING_BYTE+2) +#define SHARED_SIZE 510 + +/* +** Functions for accessing sqlite3_file methods +*/ +SQLITE_PRIVATE int sqlite3OsClose(sqlite3_file*); +SQLITE_PRIVATE int sqlite3OsRead(sqlite3_file*, void*, int amt, i64 offset); +SQLITE_PRIVATE int sqlite3OsWrite(sqlite3_file*, const void*, int amt, i64 offset); +SQLITE_PRIVATE int sqlite3OsTruncate(sqlite3_file*, i64 size); +SQLITE_PRIVATE int sqlite3OsSync(sqlite3_file*, int); +SQLITE_PRIVATE int sqlite3OsFileSize(sqlite3_file*, i64 *pSize); +SQLITE_PRIVATE int sqlite3OsLock(sqlite3_file*, int); +SQLITE_PRIVATE int sqlite3OsUnlock(sqlite3_file*, int); +SQLITE_PRIVATE int sqlite3OsCheckReservedLock(sqlite3_file *id, int *pResOut); +SQLITE_PRIVATE int sqlite3OsFileControl(sqlite3_file*,int,void*); +#define SQLITE_FCNTL_DB_UNCHANGED 0xca093fa0 +SQLITE_PRIVATE int sqlite3OsSectorSize(sqlite3_file *id); +SQLITE_PRIVATE int sqlite3OsDeviceCharacteristics(sqlite3_file *id); + +/* +** Functions for accessing sqlite3_vfs methods +*/ +SQLITE_PRIVATE int sqlite3OsOpen(sqlite3_vfs *, const char *, sqlite3_file*, int, int *); +SQLITE_PRIVATE int sqlite3OsDelete(sqlite3_vfs *, const char *, int); +SQLITE_PRIVATE int sqlite3OsAccess(sqlite3_vfs *, const char *, int, int *pResOut); +SQLITE_PRIVATE int sqlite3OsFullPathname(sqlite3_vfs *, const char *, int, char *); +#ifndef SQLITE_OMIT_LOAD_EXTENSION +SQLITE_PRIVATE void *sqlite3OsDlOpen(sqlite3_vfs *, const char *); +SQLITE_PRIVATE void sqlite3OsDlError(sqlite3_vfs *, int, char *); +SQLITE_PRIVATE void (*sqlite3OsDlSym(sqlite3_vfs *, void *, const char *))(void); +SQLITE_PRIVATE void sqlite3OsDlClose(sqlite3_vfs *, void *); +#endif /* SQLITE_OMIT_LOAD_EXTENSION */ +SQLITE_PRIVATE int sqlite3OsRandomness(sqlite3_vfs *, int, char *); +SQLITE_PRIVATE int sqlite3OsSleep(sqlite3_vfs *, int); +SQLITE_PRIVATE int sqlite3OsCurrentTime(sqlite3_vfs *, double*); + +/* +** Convenience functions for opening and closing files using +** sqlite3_malloc() to obtain space for the file-handle structure. +*/ +SQLITE_PRIVATE int sqlite3OsOpenMalloc(sqlite3_vfs *, const char *, sqlite3_file **, int,int*); +SQLITE_PRIVATE int sqlite3OsCloseFree(sqlite3_file *); + +#endif /* _SQLITE_OS_H_ */ + +/************** End of os.h **************************************************/ +/************** Continuing where we left off in sqliteInt.h ******************/ +/************** Include mutex.h in the middle of sqliteInt.h *****************/ +/************** Begin file mutex.h *******************************************/ +/* +** 2007 August 28 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +************************************************************************* +** +** This file contains the common header for all mutex implementations. +** The sqliteInt.h header #includes this file so that it is available +** to all source files. We break it out in an effort to keep the code +** better organized. +** +** NOTE: source files should *not* #include this header file directly. +** Source files should #include the sqliteInt.h file and let that file +** include this one indirectly. +** +** $Id: mutex.h,v 1.9 2008/10/07 15:25:48 drh Exp $ +*/ + + +/* +** Figure out what version of the code to use. The choices are +** +** SQLITE_MUTEX_OMIT No mutex logic. Not even stubs. The +** mutexes implemention cannot be overridden +** at start-time. +** +** SQLITE_MUTEX_NOOP For single-threaded applications. No +** mutual exclusion is provided. But this +** implementation can be overridden at +** start-time. +** +** SQLITE_MUTEX_PTHREADS For multi-threaded applications on Unix. +** +** SQLITE_MUTEX_W32 For multi-threaded applications on Win32. +** +** SQLITE_MUTEX_OS2 For multi-threaded applications on OS/2. +*/ +#if !SQLITE_THREADSAFE +# define SQLITE_MUTEX_OMIT +#endif +#if SQLITE_THREADSAFE && !defined(SQLITE_MUTEX_NOOP) +# if SQLITE_OS_UNIX +# define SQLITE_MUTEX_PTHREADS +# elif SQLITE_OS_WIN +# define SQLITE_MUTEX_W32 +# elif SQLITE_OS_OS2 +# define SQLITE_MUTEX_OS2 +# else +# define SQLITE_MUTEX_NOOP +# endif +#endif + +#ifdef SQLITE_MUTEX_OMIT +/* +** If this is a no-op implementation, implement everything as macros. +*/ +#define sqlite3_mutex_alloc(X) ((sqlite3_mutex*)8) +#define sqlite3_mutex_free(X) +#define sqlite3_mutex_enter(X) +#define sqlite3_mutex_try(X) SQLITE_OK +#define sqlite3_mutex_leave(X) +#define sqlite3_mutex_held(X) 1 +#define sqlite3_mutex_notheld(X) 1 +#define sqlite3MutexAlloc(X) ((sqlite3_mutex*)8) +#define sqlite3MutexInit() SQLITE_OK +#define sqlite3MutexEnd() +#endif /* defined(SQLITE_OMIT_MUTEX) */ + +/************** End of mutex.h ***********************************************/ +/************** Continuing where we left off in sqliteInt.h ******************/ + + +/* +** Each database file to be accessed by the system is an instance +** of the following structure. There are normally two of these structures +** in the sqlite.aDb[] array. aDb[0] is the main database file and +** aDb[1] is the database file used to hold temporary tables. Additional +** databases may be attached. +*/ +struct Db { + char *zName; /* Name of this database */ + Btree *pBt; /* The B*Tree structure for this database file */ + u8 inTrans; /* 0: not writable. 1: Transaction. 2: Checkpoint */ + u8 safety_level; /* How aggressive at syncing data to disk */ + void *pAux; /* Auxiliary data. Usually NULL */ + void (*xFreeAux)(void*); /* Routine to free pAux */ + Schema *pSchema; /* Pointer to database schema (possibly shared) */ +}; + +/* +** An instance of the following structure stores a database schema. +** +** If there are no virtual tables configured in this schema, the +** Schema.db variable is set to NULL. After the first virtual table +** has been added, it is set to point to the database connection +** used to create the connection. Once a virtual table has been +** added to the Schema structure and the Schema.db variable populated, +** only that database connection may use the Schema to prepare +** statements. +*/ +struct Schema { + int schema_cookie; /* Database schema version number for this file */ + Hash tblHash; /* All tables indexed by name */ + Hash idxHash; /* All (named) indices indexed by name */ + Hash trigHash; /* All triggers indexed by name */ + Hash aFKey; /* Foreign keys indexed by to-table */ + Table *pSeqTab; /* The sqlite_sequence table used by AUTOINCREMENT */ + u8 file_format; /* Schema format version for this file */ + u8 enc; /* Text encoding used by this database */ + u16 flags; /* Flags associated with this schema */ + int cache_size; /* Number of pages to use in the cache */ +#ifndef SQLITE_OMIT_VIRTUALTABLE + sqlite3 *db; /* "Owner" connection. See comment above */ +#endif +}; + +/* +** These macros can be used to test, set, or clear bits in the +** Db.flags field. +*/ +#define DbHasProperty(D,I,P) (((D)->aDb[I].pSchema->flags&(P))==(P)) +#define DbHasAnyProperty(D,I,P) (((D)->aDb[I].pSchema->flags&(P))!=0) +#define DbSetProperty(D,I,P) (D)->aDb[I].pSchema->flags|=(P) +#define DbClearProperty(D,I,P) (D)->aDb[I].pSchema->flags&=~(P) + +/* +** Allowed values for the DB.flags field. +** +** The DB_SchemaLoaded flag is set after the database schema has been +** read into internal hash tables. +** +** DB_UnresetViews means that one or more views have column names that +** have been filled out. If the schema changes, these column names might +** changes and so the view will need to be reset. +*/ +#define DB_SchemaLoaded 0x0001 /* The schema has been loaded */ +#define DB_UnresetViews 0x0002 /* Some views have defined column names */ +#define DB_Empty 0x0004 /* The file is empty (length 0 bytes) */ + +/* +** The number of different kinds of things that can be limited +** using the sqlite3_limit() interface. +*/ +#define SQLITE_N_LIMIT (SQLITE_LIMIT_VARIABLE_NUMBER+1) + +/* +** Lookaside malloc is a set of fixed-size buffers that can be used +** to satisfy small transient memory allocation requests for objects +** associated with a particular database connection. The use of +** lookaside malloc provides a significant performance enhancement +** (approx 10%) by avoiding numerous malloc/free requests while parsing +** SQL statements. +** +** The Lookaside structure holds configuration information about the +** lookaside malloc subsystem. Each available memory allocation in +** the lookaside subsystem is stored on a linked list of LookasideSlot +** objects. +** +** Lookaside allocations are only allowed for objects that are associated +** with a particular database connection. Hence, schema information cannot +** be stored in lookaside because in shared cache mode the schema information +** is shared by multiple database connections. Therefore, while parsing +** schema information, the Lookaside.bEnabled flag is cleared so that +** lookaside allocations are not used to construct the schema objects. +*/ +struct Lookaside { + u16 sz; /* Size of each buffer in bytes */ + u8 bEnabled; /* False to disable new lookaside allocations */ + u8 bMalloced; /* True if pStart obtained from sqlite3_malloc() */ + int nOut; /* Number of buffers currently checked out */ + int mxOut; /* Highwater mark for nOut */ + LookasideSlot *pFree; /* List of available buffers */ + void *pStart; /* First byte of available memory space */ + void *pEnd; /* First byte past end of available space */ +}; +struct LookasideSlot { + LookasideSlot *pNext; /* Next buffer in the list of free buffers */ +}; + +/* +** A hash table for function definitions. +** +** Hash each FuncDef structure into one of the FuncDefHash.a[] slots. +** Collisions are on the FuncDef.pHash chain. +*/ +struct FuncDefHash { + FuncDef *a[23]; /* Hash table for functions */ +}; + +/* +** Each database is an instance of the following structure. +** +** The sqlite.lastRowid records the last insert rowid generated by an +** insert statement. Inserts on views do not affect its value. Each +** trigger has its own context, so that lastRowid can be updated inside +** triggers as usual. The previous value will be restored once the trigger +** exits. Upon entering a before or instead of trigger, lastRowid is no +** longer (since after version 2.8.12) reset to -1. +** +** The sqlite.nChange does not count changes within triggers and keeps no +** context. It is reset at start of sqlite3_exec. +** The sqlite.lsChange represents the number of changes made by the last +** insert, update, or delete statement. It remains constant throughout the +** length of a statement and is then updated by OP_SetCounts. It keeps a +** context stack just like lastRowid so that the count of changes +** within a trigger is not seen outside the trigger. Changes to views do not +** affect the value of lsChange. +** The sqlite.csChange keeps track of the number of current changes (since +** the last statement) and is used to update sqlite_lsChange. +** +** The member variables sqlite.errCode, sqlite.zErrMsg and sqlite.zErrMsg16 +** store the most recent error code and, if applicable, string. The +** internal function sqlite3Error() is used to set these variables +** consistently. +*/ +struct sqlite3 { + sqlite3_vfs *pVfs; /* OS Interface */ + int nDb; /* Number of backends currently in use */ + Db *aDb; /* All backends */ + int flags; /* Miscellaneous flags. See below */ + int openFlags; /* Flags passed to sqlite3_vfs.xOpen() */ + int errCode; /* Most recent error code (SQLITE_*) */ + int errMask; /* & result codes with this before returning */ + u8 autoCommit; /* The auto-commit flag. */ + u8 temp_store; /* 1: file 2: memory 0: default */ + u8 mallocFailed; /* True if we have seen a malloc failure */ + u8 dfltLockMode; /* Default locking-mode for attached dbs */ + u8 dfltJournalMode; /* Default journal mode for attached dbs */ + signed char nextAutovac; /* Autovac setting after VACUUM if >=0 */ + int nextPagesize; /* Pagesize after VACUUM if >0 */ + int nTable; /* Number of tables in the database */ + CollSeq *pDfltColl; /* The default collating sequence (BINARY) */ + i64 lastRowid; /* ROWID of most recent insert (see above) */ + i64 priorNewRowid; /* Last randomly generated ROWID */ + u32 magic; /* Magic number for detect library misuse */ + int nChange; /* Value returned by sqlite3_changes() */ + int nTotalChange; /* Value returned by sqlite3_total_changes() */ + sqlite3_mutex *mutex; /* Connection mutex */ + int aLimit[SQLITE_N_LIMIT]; /* Limits */ + struct sqlite3InitInfo { /* Information used during initialization */ + int iDb; /* When back is being initialized */ + int newTnum; /* Rootpage of table being initialized */ + u8 busy; /* TRUE if currently initializing */ + } init; + int nExtension; /* Number of loaded extensions */ + void **aExtension; /* Array of shared library handles */ + struct Vdbe *pVdbe; /* List of active virtual machines */ + int activeVdbeCnt; /* Number of VDBEs currently executing */ + int writeVdbeCnt; /* Number of active VDBEs that are writing */ + void (*xTrace)(void*,const char*); /* Trace function */ + void *pTraceArg; /* Argument to the trace function */ + void (*xProfile)(void*,const char*,u64); /* Profiling function */ + void *pProfileArg; /* Argument to profile function */ + void *pCommitArg; /* Argument to xCommitCallback() */ + int (*xCommitCallback)(void*); /* Invoked at every commit. */ + void *pRollbackArg; /* Argument to xRollbackCallback() */ + void (*xRollbackCallback)(void*); /* Invoked at every commit. */ + void *pUpdateArg; + void (*xUpdateCallback)(void*,int, const char*,const char*,sqlite_int64); + void(*xCollNeeded)(void*,sqlite3*,int eTextRep,const char*); + void(*xCollNeeded16)(void*,sqlite3*,int eTextRep,const void*); + void *pCollNeededArg; + sqlite3_value *pErr; /* Most recent error message */ + char *zErrMsg; /* Most recent error message (UTF-8 encoded) */ + char *zErrMsg16; /* Most recent error message (UTF-16 encoded) */ + union { + volatile int isInterrupted; /* True if sqlite3_interrupt has been called */ + double notUsed1; /* Spacer */ + } u1; + Lookaside lookaside; /* Lookaside malloc configuration */ +#ifndef SQLITE_OMIT_AUTHORIZATION + int (*xAuth)(void*,int,const char*,const char*,const char*,const char*); + /* Access authorization function */ + void *pAuthArg; /* 1st argument to the access auth function */ +#endif +#ifndef SQLITE_OMIT_PROGRESS_CALLBACK + int (*xProgress)(void *); /* The progress callback */ + void *pProgressArg; /* Argument to the progress callback */ + int nProgressOps; /* Number of opcodes for progress callback */ +#endif +#ifndef SQLITE_OMIT_VIRTUALTABLE + Hash aModule; /* populated by sqlite3_create_module() */ + Table *pVTab; /* vtab with active Connect/Create method */ + sqlite3_vtab **aVTrans; /* Virtual tables with open transactions */ + int nVTrans; /* Allocated size of aVTrans */ +#endif + FuncDefHash aFunc; /* Hash table of connection functions */ + Hash aCollSeq; /* All collating sequences */ + BusyHandler busyHandler; /* Busy callback */ + int busyTimeout; /* Busy handler timeout, in msec */ + Db aDbStatic[2]; /* Static space for the 2 default backends */ +#ifdef SQLITE_SSE + sqlite3_stmt *pFetch; /* Used by SSE to fetch stored statements */ +#endif + Savepoint *pSavepoint; /* List of active savepoints */ + int nSavepoint; /* Number of non-transaction savepoints */ + int nStatement; /* Number of nested statement-transactions */ + u8 isTransactionSavepoint; /* True if the outermost savepoint is a TS */ + +#ifdef SQLITE_ENABLE_UNLOCK_NOTIFY + /* The following variables are all protected by the STATIC_MASTER + ** mutex, not by sqlite3.mutex. They are used by code in notify.c. + */ + sqlite3 *pBlockingConnection; /* Connection that caused SQLITE_LOCKED */ + sqlite3 *pUnlockConnection; /* Connection to watch for unlock */ + void *pUnlockArg; /* Argument to xUnlockNotify */ + void (*xUnlockNotify)(void **, int); /* Unlock notify callback */ + sqlite3 *pNextBlocked; /* Next in list of all blocked connections */ +#endif +}; + +/* +** A macro to discover the encoding of a database. +*/ +#define ENC(db) ((db)->aDb[0].pSchema->enc) + +/* +** Possible values for the sqlite.flags and or Db.flags fields. +** +** On sqlite.flags, the SQLITE_InTrans value means that we have +** executed a BEGIN. On Db.flags, SQLITE_InTrans means a statement +** transaction is active on that particular database file. +*/ +#define SQLITE_VdbeTrace 0x00000001 /* True to trace VDBE execution */ +#define SQLITE_InTrans 0x00000008 /* True if in a transaction */ +#define SQLITE_InternChanges 0x00000010 /* Uncommitted Hash table changes */ +#define SQLITE_FullColNames 0x00000020 /* Show full column names on SELECT */ +#define SQLITE_ShortColNames 0x00000040 /* Show short columns names */ +#define SQLITE_CountRows 0x00000080 /* Count rows changed by INSERT, */ + /* DELETE, or UPDATE and return */ + /* the count using a callback. */ +#define SQLITE_NullCallback 0x00000100 /* Invoke the callback once if the */ + /* result set is empty */ +#define SQLITE_SqlTrace 0x00000200 /* Debug print SQL as it executes */ +#define SQLITE_VdbeListing 0x00000400 /* Debug listings of VDBE programs */ +#define SQLITE_WriteSchema 0x00000800 /* OK to update SQLITE_MASTER */ +#define SQLITE_NoReadlock 0x00001000 /* Readlocks are omitted when + ** accessing read-only databases */ +#define SQLITE_IgnoreChecks 0x00002000 /* Do not enforce check constraints */ +#define SQLITE_ReadUncommitted 0x00004000 /* For shared-cache mode */ +#define SQLITE_LegacyFileFmt 0x00008000 /* Create new databases in format 1 */ +#define SQLITE_FullFSync 0x00010000 /* Use full fsync on the backend */ +#define SQLITE_LoadExtension 0x00020000 /* Enable load_extension */ + +#define SQLITE_RecoveryMode 0x00040000 /* Ignore schema errors */ +#define SQLITE_SharedCache 0x00080000 /* Cache sharing is enabled */ +#define SQLITE_Vtab 0x00100000 /* There exists a virtual table */ +#define SQLITE_CommitBusy 0x00200000 /* In the process of committing */ +#define SQLITE_ReverseOrder 0x00400000 /* Reverse unordered SELECTs */ + +/* +** Possible values for the sqlite.magic field. +** The numbers are obtained at random and have no special meaning, other +** than being distinct from one another. +*/ +#define SQLITE_MAGIC_OPEN 0xa029a697 /* Database is open */ +#define SQLITE_MAGIC_CLOSED 0x9f3c2d33 /* Database is closed */ +#define SQLITE_MAGIC_SICK 0x4b771290 /* Error and awaiting close */ +#define SQLITE_MAGIC_BUSY 0xf03b7906 /* Database currently in use */ +#define SQLITE_MAGIC_ERROR 0xb5357930 /* An SQLITE_MISUSE error occurred */ + +/* +** Each SQL function is defined by an instance of the following +** structure. A pointer to this structure is stored in the sqlite.aFunc +** hash table. When multiple functions have the same name, the hash table +** points to a linked list of these structures. +*/ +struct FuncDef { + i16 nArg; /* Number of arguments. -1 means unlimited */ + u8 iPrefEnc; /* Preferred text encoding (SQLITE_UTF8, 16LE, 16BE) */ + u8 flags; /* Some combination of SQLITE_FUNC_* */ + void *pUserData; /* User data parameter */ + FuncDef *pNext; /* Next function with same name */ + void (*xFunc)(sqlite3_context*,int,sqlite3_value**); /* Regular function */ + void (*xStep)(sqlite3_context*,int,sqlite3_value**); /* Aggregate step */ + void (*xFinalize)(sqlite3_context*); /* Aggregate finalizer */ + char *zName; /* SQL name of the function. */ + FuncDef *pHash; /* Next with a different name but the same hash */ +}; + +/* +** Possible values for FuncDef.flags +*/ +#define SQLITE_FUNC_LIKE 0x01 /* Candidate for the LIKE optimization */ +#define SQLITE_FUNC_CASE 0x02 /* Case-sensitive LIKE-type function */ +#define SQLITE_FUNC_EPHEM 0x04 /* Ephemeral. Delete with VDBE */ +#define SQLITE_FUNC_NEEDCOLL 0x08 /* sqlite3GetFuncCollSeq() might be called */ +#define SQLITE_FUNC_PRIVATE 0x10 /* Allowed for internal use only */ +#define SQLITE_FUNC_COUNT 0x20 /* Built-in count(*) aggregate */ + +/* +** The following three macros, FUNCTION(), LIKEFUNC() and AGGREGATE() are +** used to create the initializers for the FuncDef structures. +** +** FUNCTION(zName, nArg, iArg, bNC, xFunc) +** Used to create a scalar function definition of a function zName +** implemented by C function xFunc that accepts nArg arguments. The +** value passed as iArg is cast to a (void*) and made available +** as the user-data (sqlite3_user_data()) for the function. If +** argument bNC is true, then the FuncDef.needCollate flag is set. +** +** AGGREGATE(zName, nArg, iArg, bNC, xStep, xFinal) +** Used to create an aggregate function definition implemented by +** the C functions xStep and xFinal. The first four parameters +** are interpreted in the same way as the first 4 parameters to +** FUNCTION(). +** +** LIKEFUNC(zName, nArg, pArg, flags) +** Used to create a scalar function definition of a function zName +** that accepts nArg arguments and is implemented by a call to C +** function likeFunc. Argument pArg is cast to a (void *) and made +** available as the function user-data (sqlite3_user_data()). The +** FuncDef.flags variable is set to the value passed as the flags +** parameter. +*/ +#define FUNCTION(zName, nArg, iArg, bNC, xFunc) \ + {nArg, SQLITE_UTF8, bNC*8, SQLITE_INT_TO_PTR(iArg), 0, xFunc, 0, 0, #zName, 0} +#define STR_FUNCTION(zName, nArg, pArg, bNC, xFunc) \ + {nArg, SQLITE_UTF8, bNC*8, pArg, 0, xFunc, 0, 0, #zName, 0} +#define LIKEFUNC(zName, nArg, arg, flags) \ + {nArg, SQLITE_UTF8, flags, (void *)arg, 0, likeFunc, 0, 0, #zName, 0} +#define AGGREGATE(zName, nArg, arg, nc, xStep, xFinal) \ + {nArg, SQLITE_UTF8, nc*8, SQLITE_INT_TO_PTR(arg), 0, 0, xStep,xFinal,#zName,0} + +/* +** All current savepoints are stored in a linked list starting at +** sqlite3.pSavepoint. The first element in the list is the most recently +** opened savepoint. Savepoints are added to the list by the vdbe +** OP_Savepoint instruction. +*/ +struct Savepoint { + char *zName; /* Savepoint name (nul-terminated) */ + Savepoint *pNext; /* Parent savepoint (if any) */ +}; + +/* +** The following are used as the second parameter to sqlite3Savepoint(), +** and as the P1 argument to the OP_Savepoint instruction. +*/ +#define SAVEPOINT_BEGIN 0 +#define SAVEPOINT_RELEASE 1 +#define SAVEPOINT_ROLLBACK 2 + + +/* +** Each SQLite module (virtual table definition) is defined by an +** instance of the following structure, stored in the sqlite3.aModule +** hash table. +*/ +struct Module { + const sqlite3_module *pModule; /* Callback pointers */ + const char *zName; /* Name passed to create_module() */ + void *pAux; /* pAux passed to create_module() */ + void (*xDestroy)(void *); /* Module destructor function */ +}; + +/* +** information about each column of an SQL table is held in an instance +** of this structure. +*/ +struct Column { + char *zName; /* Name of this column */ + Expr *pDflt; /* Default value of this column */ + char *zType; /* Data type for this column */ + char *zColl; /* Collating sequence. If NULL, use the default */ + u8 notNull; /* True if there is a NOT NULL constraint */ + u8 isPrimKey; /* True if this column is part of the PRIMARY KEY */ + char affinity; /* One of the SQLITE_AFF_... values */ +#ifndef SQLITE_OMIT_VIRTUALTABLE + u8 isHidden; /* True if this column is 'hidden' */ +#endif +}; + +/* +** A "Collating Sequence" is defined by an instance of the following +** structure. Conceptually, a collating sequence consists of a name and +** a comparison routine that defines the order of that sequence. +** +** There may two separate implementations of the collation function, one +** that processes text in UTF-8 encoding (CollSeq.xCmp) and another that +** processes text encoded in UTF-16 (CollSeq.xCmp16), using the machine +** native byte order. When a collation sequence is invoked, SQLite selects +** the version that will require the least expensive encoding +** translations, if any. +** +** The CollSeq.pUser member variable is an extra parameter that passed in +** as the first argument to the UTF-8 comparison function, xCmp. +** CollSeq.pUser16 is the equivalent for the UTF-16 comparison function, +** xCmp16. +** +** If both CollSeq.xCmp and CollSeq.xCmp16 are NULL, it means that the +** collating sequence is undefined. Indices built on an undefined +** collating sequence may not be read or written. +*/ +struct CollSeq { + char *zName; /* Name of the collating sequence, UTF-8 encoded */ + u8 enc; /* Text encoding handled by xCmp() */ + u8 type; /* One of the SQLITE_COLL_... values below */ + void *pUser; /* First argument to xCmp() */ + int (*xCmp)(void*,int, const void*, int, const void*); + void (*xDel)(void*); /* Destructor for pUser */ +}; + +/* +** Allowed values of CollSeq.type: +*/ +#define SQLITE_COLL_BINARY 1 /* The default memcmp() collating sequence */ +#define SQLITE_COLL_NOCASE 2 /* The built-in NOCASE collating sequence */ +#define SQLITE_COLL_REVERSE 3 /* The built-in REVERSE collating sequence */ +#define SQLITE_COLL_USER 0 /* Any other user-defined collating sequence */ + +/* +** A sort order can be either ASC or DESC. +*/ +#define SQLITE_SO_ASC 0 /* Sort in ascending order */ +#define SQLITE_SO_DESC 1 /* Sort in ascending order */ + +/* +** Column affinity types. +** +** These used to have mnemonic name like 'i' for SQLITE_AFF_INTEGER and +** 't' for SQLITE_AFF_TEXT. But we can save a little space and improve +** the speed a little by numbering the values consecutively. +** +** But rather than start with 0 or 1, we begin with 'a'. That way, +** when multiple affinity types are concatenated into a string and +** used as the P4 operand, they will be more readable. +** +** Note also that the numeric types are grouped together so that testing +** for a numeric type is a single comparison. +*/ +#define SQLITE_AFF_TEXT 'a' +#define SQLITE_AFF_NONE 'b' +#define SQLITE_AFF_NUMERIC 'c' +#define SQLITE_AFF_INTEGER 'd' +#define SQLITE_AFF_REAL 'e' + +#define sqlite3IsNumericAffinity(X) ((X)>=SQLITE_AFF_NUMERIC) + +/* +** The SQLITE_AFF_MASK values masks off the significant bits of an +** affinity value. +*/ +#define SQLITE_AFF_MASK 0x67 + +/* +** Additional bit values that can be ORed with an affinity without +** changing the affinity. +*/ +#define SQLITE_JUMPIFNULL 0x08 /* jumps if either operand is NULL */ +#define SQLITE_STOREP2 0x10 /* Store result in reg[P2] rather than jump */ + +/* +** Each SQL table is represented in memory by an instance of the +** following structure. +** +** Table.zName is the name of the table. The case of the original +** CREATE TABLE statement is stored, but case is not significant for +** comparisons. +** +** Table.nCol is the number of columns in this table. Table.aCol is a +** pointer to an array of Column structures, one for each column. +** +** If the table has an INTEGER PRIMARY KEY, then Table.iPKey is the index of +** the column that is that key. Otherwise Table.iPKey is negative. Note +** that the datatype of the PRIMARY KEY must be INTEGER for this field to +** be set. An INTEGER PRIMARY KEY is used as the rowid for each row of +** the table. If a table has no INTEGER PRIMARY KEY, then a random rowid +** is generated for each row of the table. TF_HasPrimaryKey is set if +** the table has any PRIMARY KEY, INTEGER or otherwise. +** +** Table.tnum is the page number for the root BTree page of the table in the +** database file. If Table.iDb is the index of the database table backend +** in sqlite.aDb[]. 0 is for the main database and 1 is for the file that +** holds temporary tables and indices. If TF_Ephemeral is set +** then the table is stored in a file that is automatically deleted +** when the VDBE cursor to the table is closed. In this case Table.tnum +** refers VDBE cursor number that holds the table open, not to the root +** page number. Transient tables are used to hold the results of a +** sub-query that appears instead of a real table name in the FROM clause +** of a SELECT statement. +*/ +struct Table { + sqlite3 *dbMem; /* DB connection used for lookaside allocations. */ + char *zName; /* Name of the table or view */ + int iPKey; /* If not negative, use aCol[iPKey] as the primary key */ + int nCol; /* Number of columns in this table */ + Column *aCol; /* Information about each column */ + Index *pIndex; /* List of SQL indexes on this table. */ + int tnum; /* Root BTree node for this table (see note above) */ + Select *pSelect; /* NULL for tables. Points to definition if a view. */ + u16 nRef; /* Number of pointers to this Table */ + u8 tabFlags; /* Mask of TF_* values */ + u8 keyConf; /* What to do in case of uniqueness conflict on iPKey */ + FKey *pFKey; /* Linked list of all foreign keys in this table */ + char *zColAff; /* String defining the affinity of each column */ +#ifndef SQLITE_OMIT_CHECK + Expr *pCheck; /* The AND of all CHECK constraints */ +#endif +#ifndef SQLITE_OMIT_ALTERTABLE + int addColOffset; /* Offset in CREATE TABLE stmt to add a new column */ +#endif +#ifndef SQLITE_OMIT_VIRTUALTABLE + Module *pMod; /* Pointer to the implementation of the module */ + sqlite3_vtab *pVtab; /* Pointer to the module instance */ + int nModuleArg; /* Number of arguments to the module */ + char **azModuleArg; /* Text of all module args. [0] is module name */ +#endif + Trigger *pTrigger; /* List of triggers stored in pSchema */ + Schema *pSchema; /* Schema that contains this table */ + Table *pNextZombie; /* Next on the Parse.pZombieTab list */ +}; + +/* +** Allowed values for Tabe.tabFlags. +*/ +#define TF_Readonly 0x01 /* Read-only system table */ +#define TF_Ephemeral 0x02 /* An ephemeral table */ +#define TF_HasPrimaryKey 0x04 /* Table has a primary key */ +#define TF_Autoincrement 0x08 /* Integer primary key is autoincrement */ +#define TF_Virtual 0x10 /* Is a virtual table */ +#define TF_NeedMetadata 0x20 /* aCol[].zType and aCol[].pColl missing */ + + + +/* +** Test to see whether or not a table is a virtual table. This is +** done as a macro so that it will be optimized out when virtual +** table support is omitted from the build. +*/ +#ifndef SQLITE_OMIT_VIRTUALTABLE +# define IsVirtual(X) (((X)->tabFlags & TF_Virtual)!=0) +# define IsHiddenColumn(X) ((X)->isHidden) +#else +# define IsVirtual(X) 0 +# define IsHiddenColumn(X) 0 +#endif + +/* +** Each foreign key constraint is an instance of the following structure. +** +** A foreign key is associated with two tables. The "from" table is +** the table that contains the REFERENCES clause that creates the foreign +** key. The "to" table is the table that is named in the REFERENCES clause. +** Consider this example: +** +** CREATE TABLE ex1( +** a INTEGER PRIMARY KEY, +** b INTEGER CONSTRAINT fk1 REFERENCES ex2(x) +** ); +** +** For foreign key "fk1", the from-table is "ex1" and the to-table is "ex2". +** +** Each REFERENCES clause generates an instance of the following structure +** which is attached to the from-table. The to-table need not exist when +** the from-table is created. The existence of the to-table is not checked +** until an attempt is made to insert data into the from-table. +** +** The sqlite.aFKey hash table stores pointers to this structure +** given the name of a to-table. For each to-table, all foreign keys +** associated with that table are on a linked list using the FKey.pNextTo +** field. +*/ +struct FKey { + Table *pFrom; /* The table that contains the REFERENCES clause */ + FKey *pNextFrom; /* Next foreign key in pFrom */ + char *zTo; /* Name of table that the key points to */ + FKey *pNextTo; /* Next foreign key that points to zTo */ + int nCol; /* Number of columns in this key */ + struct sColMap { /* Mapping of columns in pFrom to columns in zTo */ + int iFrom; /* Index of column in pFrom */ + char *zCol; /* Name of column in zTo. If 0 use PRIMARY KEY */ + } *aCol; /* One entry for each of nCol column s */ + u8 isDeferred; /* True if constraint checking is deferred till COMMIT */ + u8 updateConf; /* How to resolve conflicts that occur on UPDATE */ + u8 deleteConf; /* How to resolve conflicts that occur on DELETE */ + u8 insertConf; /* How to resolve conflicts that occur on INSERT */ +}; + +/* +** SQLite supports many different ways to resolve a constraint +** error. ROLLBACK processing means that a constraint violation +** causes the operation in process to fail and for the current transaction +** to be rolled back. ABORT processing means the operation in process +** fails and any prior changes from that one operation are backed out, +** but the transaction is not rolled back. FAIL processing means that +** the operation in progress stops and returns an error code. But prior +** changes due to the same operation are not backed out and no rollback +** occurs. IGNORE means that the particular row that caused the constraint +** error is not inserted or updated. Processing continues and no error +** is returned. REPLACE means that preexisting database rows that caused +** a UNIQUE constraint violation are removed so that the new insert or +** update can proceed. Processing continues and no error is reported. +** +** RESTRICT, SETNULL, and CASCADE actions apply only to foreign keys. +** RESTRICT is the same as ABORT for IMMEDIATE foreign keys and the +** same as ROLLBACK for DEFERRED keys. SETNULL means that the foreign +** key is set to NULL. CASCADE means that a DELETE or UPDATE of the +** referenced table row is propagated into the row that holds the +** foreign key. +** +** The following symbolic values are used to record which type +** of action to take. +*/ +#define OE_None 0 /* There is no constraint to check */ +#define OE_Rollback 1 /* Fail the operation and rollback the transaction */ +#define OE_Abort 2 /* Back out changes but do no rollback transaction */ +#define OE_Fail 3 /* Stop the operation but leave all prior changes */ +#define OE_Ignore 4 /* Ignore the error. Do not do the INSERT or UPDATE */ +#define OE_Replace 5 /* Delete existing record, then do INSERT or UPDATE */ + +#define OE_Restrict 6 /* OE_Abort for IMMEDIATE, OE_Rollback for DEFERRED */ +#define OE_SetNull 7 /* Set the foreign key value to NULL */ +#define OE_SetDflt 8 /* Set the foreign key value to its default */ +#define OE_Cascade 9 /* Cascade the changes */ + +#define OE_Default 99 /* Do whatever the default action is */ + + +/* +** An instance of the following structure is passed as the first +** argument to sqlite3VdbeKeyCompare and is used to control the +** comparison of the two index keys. +*/ +struct KeyInfo { + sqlite3 *db; /* The database connection */ + u8 enc; /* Text encoding - one of the TEXT_Utf* values */ + u16 nField; /* Number of entries in aColl[] */ + u8 *aSortOrder; /* If defined an aSortOrder[i] is true, sort DESC */ + CollSeq *aColl[1]; /* Collating sequence for each term of the key */ +}; + +/* +** An instance of the following structure holds information about a +** single index record that has already been parsed out into individual +** values. +** +** A record is an object that contains one or more fields of data. +** Records are used to store the content of a table row and to store +** the key of an index. A blob encoding of a record is created by +** the OP_MakeRecord opcode of the VDBE and is disassembled by the +** OP_Column opcode. +** +** This structure holds a record that has already been disassembled +** into its constituent fields. +*/ +struct UnpackedRecord { + KeyInfo *pKeyInfo; /* Collation and sort-order information */ + u16 nField; /* Number of entries in apMem[] */ + u16 flags; /* Boolean settings. UNPACKED_... below */ + Mem *aMem; /* Values */ +}; + +/* +** Allowed values of UnpackedRecord.flags +*/ +#define UNPACKED_NEED_FREE 0x0001 /* Memory is from sqlite3Malloc() */ +#define UNPACKED_NEED_DESTROY 0x0002 /* apMem[]s should all be destroyed */ +#define UNPACKED_IGNORE_ROWID 0x0004 /* Ignore trailing rowid on key1 */ +#define UNPACKED_INCRKEY 0x0008 /* Make this key an epsilon larger */ +#define UNPACKED_PREFIX_MATCH 0x0010 /* A prefix match is considered OK */ + +/* +** Each SQL index is represented in memory by an +** instance of the following structure. +** +** The columns of the table that are to be indexed are described +** by the aiColumn[] field of this structure. For example, suppose +** we have the following table and index: +** +** CREATE TABLE Ex1(c1 int, c2 int, c3 text); +** CREATE INDEX Ex2 ON Ex1(c3,c1); +** +** In the Table structure describing Ex1, nCol==3 because there are +** three columns in the table. In the Index structure describing +** Ex2, nColumn==2 since 2 of the 3 columns of Ex1 are indexed. +** The value of aiColumn is {2, 0}. aiColumn[0]==2 because the +** first column to be indexed (c3) has an index of 2 in Ex1.aCol[]. +** The second column to be indexed (c1) has an index of 0 in +** Ex1.aCol[], hence Ex2.aiColumn[1]==0. +** +** The Index.onError field determines whether or not the indexed columns +** must be unique and what to do if they are not. When Index.onError=OE_None, +** it means this is not a unique index. Otherwise it is a unique index +** and the value of Index.onError indicate the which conflict resolution +** algorithm to employ whenever an attempt is made to insert a non-unique +** element. +*/ +struct Index { + char *zName; /* Name of this index */ + int nColumn; /* Number of columns in the table used by this index */ + int *aiColumn; /* Which columns are used by this index. 1st is 0 */ + unsigned *aiRowEst; /* Result of ANALYZE: Est. rows selected by each column */ + Table *pTable; /* The SQL table being indexed */ + int tnum; /* Page containing root of this index in database file */ + u8 onError; /* OE_Abort, OE_Ignore, OE_Replace, or OE_None */ + u8 autoIndex; /* True if is automatically created (ex: by UNIQUE) */ + char *zColAff; /* String defining the affinity of each column */ + Index *pNext; /* The next index associated with the same table */ + Schema *pSchema; /* Schema containing this index */ + u8 *aSortOrder; /* Array of size Index.nColumn. True==DESC, False==ASC */ + char **azColl; /* Array of collation sequence names for index */ +}; + +/* +** Each token coming out of the lexer is an instance of +** this structure. Tokens are also used as part of an expression. +** +** Note if Token.z==0 then Token.dyn and Token.n are undefined and +** may contain random values. Do not make any assumptions about Token.dyn +** and Token.n when Token.z==0. +*/ +struct Token { + const unsigned char *z; /* Text of the token. Not NULL-terminated! */ + unsigned dyn : 1; /* True for malloced memory, false for static */ + unsigned n : 31; /* Number of characters in this token */ +}; + +/* +** An instance of this structure contains information needed to generate +** code for a SELECT that contains aggregate functions. +** +** If Expr.op==TK_AGG_COLUMN or TK_AGG_FUNCTION then Expr.pAggInfo is a +** pointer to this structure. The Expr.iColumn field is the index in +** AggInfo.aCol[] or AggInfo.aFunc[] of information needed to generate +** code for that node. +** +** AggInfo.pGroupBy and AggInfo.aFunc.pExpr point to fields within the +** original Select structure that describes the SELECT statement. These +** fields do not need to be freed when deallocating the AggInfo structure. +*/ +struct AggInfo { + u8 directMode; /* Direct rendering mode means take data directly + ** from source tables rather than from accumulators */ + u8 useSortingIdx; /* In direct mode, reference the sorting index rather + ** than the source table */ + int sortingIdx; /* Cursor number of the sorting index */ + ExprList *pGroupBy; /* The group by clause */ + int nSortingColumn; /* Number of columns in the sorting index */ + struct AggInfo_col { /* For each column used in source tables */ + Table *pTab; /* Source table */ + int iTable; /* Cursor number of the source table */ + int iColumn; /* Column number within the source table */ + int iSorterColumn; /* Column number in the sorting index */ + int iMem; /* Memory location that acts as accumulator */ + Expr *pExpr; /* The original expression */ + } *aCol; + int nColumn; /* Number of used entries in aCol[] */ + int nColumnAlloc; /* Number of slots allocated for aCol[] */ + int nAccumulator; /* Number of columns that show through to the output. + ** Additional columns are used only as parameters to + ** aggregate functions */ + struct AggInfo_func { /* For each aggregate function */ + Expr *pExpr; /* Expression encoding the function */ + FuncDef *pFunc; /* The aggregate function implementation */ + int iMem; /* Memory location that acts as accumulator */ + int iDistinct; /* Ephemeral table used to enforce DISTINCT */ + } *aFunc; + int nFunc; /* Number of entries in aFunc[] */ + int nFuncAlloc; /* Number of slots allocated for aFunc[] */ +}; + +/* +** Each node of an expression in the parse tree is an instance +** of this structure. +** +** Expr.op is the opcode. The integer parser token codes are reused +** as opcodes here. For example, the parser defines TK_GE to be an integer +** code representing the ">=" operator. This same integer code is reused +** to represent the greater-than-or-equal-to operator in the expression +** tree. +** +** If the expression is an SQL literal (TK_INTEGER, TK_FLOAT, TK_BLOB, +** or TK_STRING), then Expr.token contains the text of the SQL literal. If +** the expression is a variable (TK_VARIABLE), then Expr.token contains the +** variable name. Finally, if the expression is an SQL function (TK_FUNCTION), +** then Expr.token contains the name of the function. +** +** Expr.pRight and Expr.pLeft are the left and right subexpressions of a +** binary operator. Either or both may be NULL. +** +** Expr.x.pList is a list of arguments if the expression is an SQL function, +** a CASE expression or an IN expression of the form " IN (, ...)". +** Expr.x.pSelect is used if the expression is a sub-select or an expression of +** the form " IN (SELECT ...)". If the EP_xIsSelect bit is set in the +** Expr.flags mask, then Expr.x.pSelect is valid. Otherwise, Expr.x.pList is +** valid. +** +** An expression of the form ID or ID.ID refers to a column in a table. +** For such expressions, Expr.op is set to TK_COLUMN and Expr.iTable is +** the integer cursor number of a VDBE cursor pointing to that table and +** Expr.iColumn is the column number for the specific column. If the +** expression is used as a result in an aggregate SELECT, then the +** value is also stored in the Expr.iAgg column in the aggregate so that +** it can be accessed after all aggregates are computed. +** +** If the expression is an unbound variable marker (a question mark +** character '?' in the original SQL) then the Expr.iTable holds the index +** number for that variable. +** +** If the expression is a subquery then Expr.iColumn holds an integer +** register number containing the result of the subquery. If the +** subquery gives a constant result, then iTable is -1. If the subquery +** gives a different answer at different times during statement processing +** then iTable is the address of a subroutine that computes the subquery. +** +** If the Expr is of type OP_Column, and the table it is selecting from +** is a disk table or the "old.*" pseudo-table, then pTab points to the +** corresponding table definition. +** +** ALLOCATION NOTES: +** +** Expr structures may be stored as part of the in-memory database schema, +** for example as part of trigger, view or table definitions. In this case, +** the amount of memory consumed by complex expressions may be significant. +** For this reason, less than sizeof(Expr) bytes may be allocated for some +** Expr structs stored as part of the in-memory database schema. +** +** If the EP_Reduced flag is set in Expr.flags, then only EXPR_REDUCEDSIZE +** bytes of space are allocated for the expression structure. This is enough +** space to store all fields up to and including the "Token span;" field. +** +** If the EP_TokenOnly flag is set in Expr.flags, then only EXPR_TOKENONLYSIZE +** bytes of space are allocated for the expression structure. This is enough +** space to store all fields up to and including the "Token token;" field. +*/ +struct Expr { + u8 op; /* Operation performed by this node */ + char affinity; /* The affinity of the column or 0 if not a column */ + VVA_ONLY(u8 vvaFlags;) /* Flags used for VV&A only. EVVA_* below. */ + u16 flags; /* Various flags. EP_* See below */ + Token token; /* An operand token */ + + /* If the EP_TokenOnly flag is set in the Expr.flags mask, then no + ** space is allocated for the fields below this point. An attempt to + ** access them will result in a segfault or malfunction. + *********************************************************************/ + + Token span; /* Complete text of the expression */ + + /* If the EP_SpanOnly flag is set in the Expr.flags mask, then no + ** space is allocated for the fields below this point. An attempt to + ** access them will result in a segfault or malfunction. + *********************************************************************/ + + Expr *pLeft; /* Left subnode */ + Expr *pRight; /* Right subnode */ + union { + ExprList *pList; /* Function arguments or in " IN ( IN ( +// +// 00 means an unconnected pin. +// <- means using a R/W allocator from the upstream filter +// <= means using a R-O allocator from an upstream filter +// || means using our own (R/W) allocator. +// -> means using a R/W allocator from a downstream filter +// (a R-O allocator from downstream is nonsense, it can't ever work). +// +// +// That makes 25 possible states. Some states are nonsense (two different +// allocators from the same place). These are just an artifact of the notation. +// <= <- Nonsense. +// <- <= Nonsense +// Some states are illegal (the output pin never accepts a R-O allocator): +// 00 <= !! Error !! +// <= <= !! Error !! +// || <= !! Error !! +// -> <= !! Error !! +// Three states appears to be inaccessible: +// -> || Inaccessible +// || -> Inaccessible +// || <- Inaccessible +// Some states only ever occur as intermediates with a pending reconnect which +// is guaranteed to finish in another state. +// -> 00 ?? unstable goes to || 00 +// 00 <- ?? unstable goes to 00 || +// -> <- ?? unstable goes to -> -> +// <- || ?? unstable goes to <- <- +// <- -> ?? unstable goes to <- <- +// And that leaves 11 possible resting states: +// 1 00 00 Nothing connected. +// 2 <- 00 Input pin connected. +// 3 <= 00 Input pin connected using R-O allocator. +// 4 || 00 Needs several state changes to get here. +// 5 00 || Output pin connected using our allocator +// 6 00 -> Downstream only connected +// 7 || || Undesirable but can be forced upon us. +// 8 <= || Copy forced. <= -> is preferable +// 9 <= -> OK - forced to copy. +// 10 <- <- Transform in place (ideal) +// 11 -> -> Transform in place (ideal) +// +// The object of the exercise is to ensure that we finish up in states +// 10 or 11 whenever possible. State 10 is only possible if the upstream +// filter has a R/W allocator (the AVI splitter notoriously +// doesn't) and state 11 is only possible if the downstream filter does +// offer an allocator. +// +// The transition table (entries marked * go via a reconnect) +// +// There are 8 possible transitions: +// A: Connect upstream to filter with R-O allocator that insists on using it. +// B: Connect upstream to filter with R-O allocator but chooses not to use it. +// C: Connect upstream to filter with R/W allocator and insists on using it. +// D: Connect upstream to filter with R/W allocator but chooses not to use it. +// E: Connect downstream to a filter that offers an allocator +// F: Connect downstream to a filter that does not offer an allocator +// G: disconnect upstream +// H: Disconnect downstream +// +// A B C D E F G H +// --------------------------------------------------------- +// 00 00 1 | 3 3 2 2 6 5 . . |1 00 00 +// <- 00 2 | . . . . *10/11 10 1 . |2 <- 00 +// <= 00 3 | . . . . *9/11 *7/8 1 . |3 <= 00 +// || 00 4 | . . . . *8 *7 1 . |4 || 00 +// 00 || 5 | 8 7 *10 7 . . . 1 |5 00 || +// 00 -> 6 | 9 11 *10 11 . . . 1 |6 00 -> +// || || 7 | . . . . . . 5 4 |7 || || +// <= || 8 | . . . . . . 5 3 |8 <= || +// <= -> 9 | . . . . . . 6 3 |9 <= -> +// <- <- 10| . . . . . . *5/6 2 |10 <- <- +// -> -> 11| . . . . . . 6 *2/3 |11 -> -> +// --------------------------------------------------------- +// A B C D E F G H +// +// All these states are accessible without requiring any filter to +// change its behaviour but not all transitions are accessible, for +// instance a transition from state 4 to anywhere other than +// state 8 requires that the upstream filter first offer a R-O allocator +// and then changes its mind and offer R/W. This is NOT allowable - it +// leads to things like the output pin getting a R/W allocator from +// upstream and then the input pin being told it can only have a R-O one. +// Note that you CAN change (say) the upstream filter for a different one, but +// only as a disconnect / connect, not as a Reconnect. (Exercise for +// the reader is to see how you get into state 4). +// +// The reconnection stuff goes as follows (some of the cases shown here as +// "no reconnect" may get one to finalise media type - an old story). +// If there is a reconnect where it says "no reconnect" here then the +// reconnection must not change the allocator choice. +// +// state 2: <- 00 transition E <- <- case C <- <- (no change) +// case D -> <- and then to -> -> +// +// state 2: <- 00 transition F <- <- (no reconnect) +// +// state 3: <= 00 transition E <= -> case A <= -> (no change) +// case B -> -> +// transition F <= || case A <= || (no change) +// case B || || +// +// state 4: || 00 transition E || || case B -> || and then all cases to -> -> +// F || || case B || || (no change) +// +// state 5: 00 || transition A <= || (no reconnect) +// B || || (no reconnect) +// C <- || all cases <- <- +// D || || (unfortunate, but upstream's choice) +// +// state 6: 00 -> transition A <= -> (no reconnect) +// B -> -> (no reconnect) +// C <- -> all cases <- <- +// D -> -> (no reconnect) +// +// state 10:<- <- transition G 00 <- case E 00 -> +// case F 00 || +// +// state 11:-> -> transition H -> 00 case A <= 00 (schizo) +// case B <= 00 +// case C <- 00 (schizo) +// case D <- 00 +// +// The Rules: +// To sort out media types: +// The input is reconnected +// if the input pin is connected and the output pin connects +// The output is reconnected +// If the output pin is connected +// and the input pin connects to a different media type +// +// To sort out allocators: +// The input is reconnected +// if the output disconnects and the input was using a downstream allocator +// The output pin calls SetAllocator to pass on a new allocator +// if the output is connected and +// if the input disconnects and the output was using an upstream allocator +// if the input acquires an allocator different from the output one +// and that new allocator is not R-O +// +// Data is copied (i.e. call getbuffer and copy the data before transforming it) +// if the two allocators are different. + + + +// CHAINS of filters: +// +// We sit between two filters (call them A and Z). We should finish up +// with the same allocator on both of our pins and that should be the +// same one that A and Z would have agreed on if we hadn't been in the +// way. Furthermore, it should not matter how many in-place transforms +// are in the way. Let B, C, D... be in-place transforms ("us"). +// Here's how it goes: +// +// 1. +// A connects to B. They agree on A's allocator. +// A-a->B +// +// 2. +// B connects to C. Same story. There is no point in a reconnect, but +// B will request an input reconnect anyway. +// A-a->B-a->C +// +// 3. +// C connects to Z. +// C insists on using A's allocator, but compromises by requesting a reconnect. +// of C's input. +// A-a->B-?->C-a->Z +// +// We now have pending reconnects on both A--->B and B--->C +// +// 4. +// The A--->B link is reconnected. +// A asks B for an allocator. B sees that it has a downstream connection so +// asks its downstream input pin i.e. C's input pin for an allocator. C sees +// that it too has a downstream connection so asks Z for an allocator. +// +// Even though Z's input pin is connected, it is being asked for an allocator. +// It could refuse, in which case the chain is done and will use A's allocator +// Alternatively, Z may supply one. A chooses either Z's or A's own one. +// B's input pin gets NotifyAllocator called to tell it the decision and it +// propagates this downstream by calling ReceiveAllocator on its output pin +// which calls NotifyAllocator on the next input pin downstream etc. +// If the choice is Z then it goes: +// A-z->B-a->C-a->Z +// A-z->B-z->C-a->Z +// A-z->B-z->C-z->Z +// +// And that's IT!! Any further (essentially spurious) reconnects peter out +// with no change in the chain. + +#include +#include +#include + + +// ================================================================= +// Implements the CTransInPlaceFilter class +// ================================================================= + +CTransInPlaceFilter::CTransInPlaceFilter + ( TCHAR *pName, + LPUNKNOWN pUnk, + REFCLSID clsid, + HRESULT *phr, + bool bModifiesData + ) + : CTransformFilter(pName, pUnk, clsid), + m_bModifiesData(bModifiesData) +{ +#ifdef PERF + RegisterPerfId(); +#endif // PERF + +} // constructor + +#ifdef UNICODE +CTransInPlaceFilter::CTransInPlaceFilter + ( CHAR *pName, + LPUNKNOWN pUnk, + REFCLSID clsid, + HRESULT *phr, + bool bModifiesData + ) + : CTransformFilter(pName, pUnk, clsid), + m_bModifiesData(bModifiesData) +{ +#ifdef PERF + RegisterPerfId(); +#endif // PERF + +} // constructor +#endif + +// return a non-addrefed CBasePin * for the user to addref if he holds onto it +// for longer than his pointer to us. We create the pins dynamically when they +// are asked for rather than in the constructor. This is because we want to +// give the derived class an oppportunity to return different pin objects + +// As soon as any pin is needed we create both (this is different from the +// usual transform filter) because enumerators, allocators etc are passed +// through from one pin to another and it becomes very painful if the other +// pin isn't there. If we fail to create either pin we ensure we fail both. + +CBasePin * +CTransInPlaceFilter::GetPin(int n) +{ + HRESULT hr = S_OK; + + // Create an input pin if not already done + + if (m_pInput == NULL) { + + m_pInput = new CTransInPlaceInputPin( NAME("TransInPlace input pin") + , this // Owner filter + , &hr // Result code + , L"Input" // Pin name + ); + + // Constructor for CTransInPlaceInputPin can't fail + ASSERT(SUCCEEDED(hr)); + } + + // Create an output pin if not already done + + if (m_pInput!=NULL && m_pOutput == NULL) { + + m_pOutput = new CTransInPlaceOutputPin( NAME("TransInPlace output pin") + , this // Owner filter + , &hr // Result code + , L"Output" // Pin name + ); + + // a failed return code should delete the object + + ASSERT(SUCCEEDED(hr)); + if (m_pOutput == NULL) { + delete m_pInput; + m_pInput = NULL; + } + } + + // Return the appropriate pin + + ASSERT (n>=0 && n<=1); + if (n == 0) { + return m_pInput; + } else if (n==1) { + return m_pOutput; + } else { + return NULL; + } + +} // GetPin + + + +// dir is the direction of our pin. +// pReceivePin is the pin we are connecting to. +HRESULT CTransInPlaceFilter::CompleteConnect(PIN_DIRECTION dir,IPin *pReceivePin) +{ + UNREFERENCED_PARAMETER(pReceivePin); + ASSERT(m_pInput); + ASSERT(m_pOutput); + + // if we are not part of a graph, then don't indirect the pointer + // this probably prevents use of the filter without a filtergraph + if (!m_pGraph) { + return VFW_E_NOT_IN_GRAPH; + } + + // Always reconnect the input to account for buffering changes + // + // Because we don't get to suggest a type on ReceiveConnection + // we need another way of making sure the right type gets used. + // + // One way would be to have our EnumMediaTypes return our output + // connection type first but more deterministic and simple is to + // call ReconnectEx passing the type we want to reconnect with + // via the base class ReconeectPin method. + + if (dir == PINDIR_OUTPUT) { + if( m_pInput->IsConnected() ) { + return ReconnectPin( m_pInput, &m_pOutput->CurrentMediaType() ); + } + return NOERROR; + } + + ASSERT(dir == PINDIR_INPUT); + + // Reconnect output if necessary + + if( m_pOutput->IsConnected() ) { + + if ( m_pInput->CurrentMediaType() + != m_pOutput->CurrentMediaType() + ) { + return ReconnectPin( m_pOutput, &m_pInput->CurrentMediaType() ); + } + } + return NOERROR; + +} // ComnpleteConnect + + +// +// DecideBufferSize +// +// Tell the output pin's allocator what size buffers we require. +// *pAlloc will be the allocator our output pin is using. +// + +HRESULT CTransInPlaceFilter::DecideBufferSize + ( IMemAllocator *pAlloc + , ALLOCATOR_PROPERTIES *pProperties + ) +{ + ALLOCATOR_PROPERTIES Request, Actual; + HRESULT hr; + + // If we are connected upstream, get his views + if (m_pInput->IsConnected()) { + // Get the input pin allocator, and get its size and count. + // we don't care about his alignment and prefix. + + hr = InputPin()->PeekAllocator()->GetProperties(&Request); + if (FAILED(hr)) { + // Input connected but with a secretive allocator - enough! + return hr; + } + } else { + // We're reduced to blind guessing. Let's guess one byte and if + // this isn't enough then when the other pin does get connected + // we can revise it. + ZeroMemory(&Request, sizeof(Request)); + Request.cBuffers = 1; + Request.cbBuffer = 1; + } + + + DbgLog((LOG_MEMORY,1,TEXT("Setting Allocator Requirements"))); + DbgLog((LOG_MEMORY,1,TEXT("Count %d, Size %d"), + Request.cBuffers, Request.cbBuffer)); + + // Pass the allocator requirements to our output side + // but do a little sanity checking first or we'll just hit + // asserts in the allocator. + + pProperties->cBuffers = Request.cBuffers; + pProperties->cbBuffer = Request.cbBuffer; + pProperties->cbAlign = Request.cbAlign; + if (pProperties->cBuffers<=0) {pProperties->cBuffers = 1; } + if (pProperties->cbBuffer<=0) {pProperties->cbBuffer = 1; } + hr = pAlloc->SetProperties(pProperties, &Actual); + + if (FAILED(hr)) { + return hr; + } + + DbgLog((LOG_MEMORY,1,TEXT("Obtained Allocator Requirements"))); + DbgLog((LOG_MEMORY,1,TEXT("Count %d, Size %d, Alignment %d"), + Actual.cBuffers, Actual.cbBuffer, Actual.cbAlign)); + + // Make sure we got the right alignment and at least the minimum required + + if ( (Request.cBuffers > Actual.cBuffers) + || (Request.cbBuffer > Actual.cbBuffer) + || (Request.cbAlign > Actual.cbAlign) + ) { + return E_FAIL; + } + return NOERROR; + +} // DecideBufferSize + +// +// Copy +// +// return a pointer to an identical copy of pSample +IMediaSample * CTransInPlaceFilter::Copy(IMediaSample *pSource) +{ + IMediaSample * pDest; + + HRESULT hr; + REFERENCE_TIME tStart, tStop; + const BOOL bTime = S_OK == pSource->GetTime( &tStart, &tStop); + + // this may block for an indeterminate amount of time + hr = OutputPin()->PeekAllocator()->GetBuffer( + &pDest + , bTime ? &tStart : NULL + , bTime ? &tStop : NULL + , m_bSampleSkipped ? AM_GBF_PREVFRAMESKIPPED : 0 + ); + + if (FAILED(hr)) { + return NULL; + } + + ASSERT(pDest); + IMediaSample2 *pSample2; + if (SUCCEEDED(pDest->QueryInterface(IID_IMediaSample2, (void **)&pSample2))) { + HRESULT hr = pSample2->SetProperties( + FIELD_OFFSET(AM_SAMPLE2_PROPERTIES, pbBuffer), + (PBYTE)m_pInput->SampleProps()); + pSample2->Release(); + if (FAILED(hr)) { + pDest->Release(); + return NULL; + } + } else { + if (bTime) { + pDest->SetTime(&tStart, &tStop); + } + + if (S_OK == pSource->IsSyncPoint()) { + pDest->SetSyncPoint(TRUE); + } + if (S_OK == pSource->IsDiscontinuity() || m_bSampleSkipped) { + pDest->SetDiscontinuity(TRUE); + } + if (S_OK == pSource->IsPreroll()) { + pDest->SetPreroll(TRUE); + } + + // Copy the media type + AM_MEDIA_TYPE *pMediaType; + if (S_OK == pSource->GetMediaType(&pMediaType)) { + pDest->SetMediaType(pMediaType); + DeleteMediaType( pMediaType ); + } + + } + + m_bSampleSkipped = FALSE; + + // Copy the sample media times + REFERENCE_TIME TimeStart, TimeEnd; + if (pSource->GetMediaTime(&TimeStart,&TimeEnd) == NOERROR) { + pDest->SetMediaTime(&TimeStart,&TimeEnd); + } + + // Copy the actual data length and the actual data. + { + const long lDataLength = pSource->GetActualDataLength(); + pDest->SetActualDataLength(lDataLength); + + // Copy the sample data + { + BYTE *pSourceBuffer, *pDestBuffer; + long lSourceSize = pSource->GetSize(); + long lDestSize = pDest->GetSize(); + + ASSERT(lDestSize >= lSourceSize && lDestSize >= lDataLength); + + pSource->GetPointer(&pSourceBuffer); + pDest->GetPointer(&pDestBuffer); + ASSERT(lDestSize == 0 || pSourceBuffer != NULL && pDestBuffer != NULL); + + CopyMemory( (PVOID) pDestBuffer, (PVOID) pSourceBuffer, lDataLength ); + } + } + + return pDest; + +} // Copy + + +// override this to customize the transform process + +HRESULT +CTransInPlaceFilter::Receive(IMediaSample *pSample) +{ + /* Check for other streams and pass them on */ + AM_SAMPLE2_PROPERTIES * const pProps = m_pInput->SampleProps(); + if (pProps->dwStreamId != AM_STREAM_MEDIA) { + return m_pOutput->Deliver(pSample); + } + HRESULT hr; + + // Start timing the TransInPlace (if PERF is defined) + MSR_START(m_idTransInPlace); + + if (UsingDifferentAllocators()) { + + // We have to copy the data. + + pSample = Copy(pSample); + + if (pSample==NULL) { + MSR_STOP(m_idTransInPlace); + return E_UNEXPECTED; + } + } + + // have the derived class transform the data + hr = Transform(pSample); + + // Stop the clock and log it (if PERF is defined) + MSR_STOP(m_idTransInPlace); + + if (FAILED(hr)) { + DbgLog((LOG_TRACE, 1, TEXT("Error from TransInPlace"))); + if (UsingDifferentAllocators()) { + pSample->Release(); + } + return hr; + } + + // the Transform() function can return S_FALSE to indicate that the + // sample should not be delivered; we only deliver the sample if it's + // really S_OK (same as NOERROR, of course.) + if (hr == NOERROR) { + hr = m_pOutput->Deliver(pSample); + } else { + // But it would be an error to return this private workaround + // to the caller ... + if (S_FALSE == hr) { + // S_FALSE returned from Transform is a PRIVATE agreement + // We should return NOERROR from Receive() in this cause because + // returning S_FALSE from Receive() means that this is the end + // of the stream and no more data should be sent. + m_bSampleSkipped = TRUE; + if (!m_bQualityChanged) { + NotifyEvent(EC_QUALITY_CHANGE,0,0); + m_bQualityChanged = TRUE; + } + hr = NOERROR; + } + } + + // release the output buffer. If the connected pin still needs it, + // it will have addrefed it itself. + if (UsingDifferentAllocators()) { + pSample->Release(); + } + + return hr; + +} // Receive + + + +// ================================================================= +// Implements the CTransInPlaceInputPin class +// ================================================================= + + +// constructor + +CTransInPlaceInputPin::CTransInPlaceInputPin + ( TCHAR *pObjectName + , CTransInPlaceFilter *pFilter + , HRESULT *phr + , LPCWSTR pName + ) + : CTransformInputPin(pObjectName, + pFilter, + phr, + pName) + , m_bReadOnly(FALSE) + , m_pTIPFilter(pFilter) +{ + DbgLog((LOG_TRACE, 2 + , TEXT("CTransInPlaceInputPin::CTransInPlaceInputPin"))); + +} // constructor + + +// ================================================================= +// Implements IMemInputPin interface +// ================================================================= + + +// If the downstream filter has one then offer that (even if our own output +// pin is not using it yet. If the upstream filter chooses it then we will +// tell our output pin to ReceiveAllocator). +// Else if our output pin is using an allocator then offer that. +// ( This could mean offering the upstream filter his own allocator, +// it could mean offerring our own +// ) or it could mean offering the one from downstream +// Else fail to offer any allocator at all. + +STDMETHODIMP CTransInPlaceInputPin::GetAllocator(IMemAllocator ** ppAllocator) +{ + CheckPointer(ppAllocator,E_POINTER); + ValidateReadWritePtr(ppAllocator,sizeof(IMemAllocator *)); + CAutoLock cObjectLock(m_pLock); + + HRESULT hr; + + if ( m_pTIPFilter->m_pOutput->IsConnected() ) { + // Store the allocator we got + hr = m_pTIPFilter->OutputPin()->ConnectedIMemInputPin() + ->GetAllocator( ppAllocator ); + if (SUCCEEDED(hr)) { + m_pTIPFilter->OutputPin()->SetAllocator( *ppAllocator ); + } + } + else { + // Help upstream filter (eg TIP filter which is having to do a copy) + // by providing a temp allocator here - we'll never use + // this allocator because when our output is connected we'll + // reconnect this pin + hr = CTransformInputPin::GetAllocator( ppAllocator ); + } + return hr; + +} // GetAllocator + + + +/* Get told which allocator the upstream output pin is actually going to use */ + + +STDMETHODIMP +CTransInPlaceInputPin::NotifyAllocator( + IMemAllocator * pAllocator, + BOOL bReadOnly) +{ + HRESULT hr = S_OK; + CheckPointer(pAllocator,E_POINTER); + ValidateReadPtr(pAllocator,sizeof(IMemAllocator)); + + CAutoLock cObjectLock(m_pLock); + + m_bReadOnly = bReadOnly; + // If we modify data then don't accept the allocator if it's + // the same as the output pin's allocator + + // If our output is not connected just accept the allocator + // We're never going to use this allocator because when our + // output pin is connected we'll reconnect this pin + if (!m_pTIPFilter->OutputPin()->IsConnected()) { + return CTransformInputPin::NotifyAllocator(pAllocator, bReadOnly); + } + + // If the allocator is read-only and we're modifying data + // and the allocator is the same as the output pin's + // then reject + if (bReadOnly && m_pTIPFilter->m_bModifiesData) { + IMemAllocator *pOutputAllocator = + m_pTIPFilter->OutputPin()->PeekAllocator(); + + // Make sure we have an output allocator + if (pOutputAllocator == NULL) { + hr = m_pTIPFilter->OutputPin()->ConnectedIMemInputPin()-> + GetAllocator(&pOutputAllocator); + if(FAILED(hr)) { + hr = CreateMemoryAllocator(&pOutputAllocator); + } + if (SUCCEEDED(hr)) { + m_pTIPFilter->OutputPin()->SetAllocator(pOutputAllocator); + pOutputAllocator->Release(); + } + } + if (pAllocator == pOutputAllocator) { + hr = E_FAIL; + } else if(SUCCEEDED(hr)) { + // Must copy so set the allocator properties on the output + ALLOCATOR_PROPERTIES Props, Actual; + hr = pAllocator->GetProperties(&Props); + if (SUCCEEDED(hr)) { + hr = pOutputAllocator->SetProperties(&Props, &Actual); + } + if (SUCCEEDED(hr)) { + if ( (Props.cBuffers > Actual.cBuffers) + || (Props.cbBuffer > Actual.cbBuffer) + || (Props.cbAlign > Actual.cbAlign) + ) { + hr = E_FAIL; + } + } + + // Set the allocator on the output pin + if (SUCCEEDED(hr)) { + hr = m_pTIPFilter->OutputPin()->ConnectedIMemInputPin() + ->NotifyAllocator( pOutputAllocator, FALSE ); + } + } + } else { + hr = m_pTIPFilter->OutputPin()->ConnectedIMemInputPin() + ->NotifyAllocator( pAllocator, bReadOnly ); + if (SUCCEEDED(hr)) { + m_pTIPFilter->OutputPin()->SetAllocator( pAllocator ); + } + } + + if (SUCCEEDED(hr)) { + + // It's possible that the old and the new are the same thing. + // AddRef before release ensures that we don't unload it. + pAllocator->AddRef(); + + if( m_pAllocator != NULL ) + m_pAllocator->Release(); + + m_pAllocator = pAllocator; // We have an allocator for the input pin + } + + return hr; + +} // NotifyAllocator + + +// EnumMediaTypes +// - pass through to our downstream filter +STDMETHODIMP CTransInPlaceInputPin::EnumMediaTypes( IEnumMediaTypes **ppEnum ) +{ + // Can only pass through if connected + if( !m_pTIPFilter->m_pOutput->IsConnected() ) + return VFW_E_NOT_CONNECTED; + + return m_pTIPFilter->m_pOutput->GetConnected()->EnumMediaTypes( ppEnum ); + +} // EnumMediaTypes + + +// CheckMediaType +// - agree to anything if not connected, +// otherwise pass through to the downstream filter. +// This assumes that the filter does not change the media type. + +HRESULT CTransInPlaceInputPin::CheckMediaType(const CMediaType *pmt ) +{ + HRESULT hr = m_pTIPFilter->CheckInputType(pmt); + if (hr!=S_OK) return hr; + + if( m_pTIPFilter->m_pOutput->IsConnected() ) + return m_pTIPFilter->m_pOutput->GetConnected()->QueryAccept( pmt ); + else + return S_OK; + +} // CheckMediaType + + +// If upstream asks us what our requirements are, we will try to ask downstream +// if that doesn't work, we'll just take the defaults. +STDMETHODIMP +CTransInPlaceInputPin::GetAllocatorRequirements(ALLOCATOR_PROPERTIES *pProps) +{ + + if( m_pTIPFilter->m_pOutput->IsConnected() ) + return m_pTIPFilter->OutputPin() + ->ConnectedIMemInputPin()->GetAllocatorRequirements( pProps ); + else + return E_NOTIMPL; + +} // GetAllocatorRequirements + + +// CTransInPlaceInputPin::CompleteConnect() calls CBaseInputPin::CompleteConnect() +// and then calls CTransInPlaceFilter::CompleteConnect(). It does this because +// CTransInPlaceFilter::CompleteConnect() can reconnect a pin and we do not +// want to reconnect a pin if CBaseInputPin::CompleteConnect() fails. +HRESULT +CTransInPlaceInputPin::CompleteConnect(IPin *pReceivePin) +{ + HRESULT hr = CBaseInputPin::CompleteConnect(pReceivePin); + if (FAILED(hr)) { + return hr; + } + + return m_pTransformFilter->CompleteConnect(PINDIR_INPUT,pReceivePin); +} // CompleteConnect + + +// ================================================================= +// Implements the CTransInPlaceOutputPin class +// ================================================================= + + +// constructor + +CTransInPlaceOutputPin::CTransInPlaceOutputPin( + TCHAR *pObjectName, + CTransInPlaceFilter *pFilter, + HRESULT * phr, + LPCWSTR pPinName) + : CTransformOutputPin( pObjectName + , pFilter + , phr + , pPinName), + m_pTIPFilter(pFilter) +{ + DbgLog(( LOG_TRACE, 2 + , TEXT("CTransInPlaceOutputPin::CTransInPlaceOutputPin"))); + +} // constructor + + +// EnumMediaTypes +// - pass through to our upstream filter +STDMETHODIMP CTransInPlaceOutputPin::EnumMediaTypes( IEnumMediaTypes **ppEnum ) +{ + // Can only pass through if connected. + if( ! m_pTIPFilter->m_pInput->IsConnected() ) + return VFW_E_NOT_CONNECTED; + + return m_pTIPFilter->m_pInput->GetConnected()->EnumMediaTypes( ppEnum ); + +} // EnumMediaTypes + + + +// CheckMediaType +// - agree to anything if not connected, +// otherwise pass through to the upstream filter. + +HRESULT CTransInPlaceOutputPin::CheckMediaType(const CMediaType *pmt ) +{ + // Don't accept any output pin type changes if we're copying + // between allocators - it's too late to change the input + // allocator size. + if (m_pTIPFilter->UsingDifferentAllocators() && !m_pFilter->IsStopped()) { + if (*pmt == m_mt) { + return S_OK; + } else { + return VFW_E_TYPE_NOT_ACCEPTED; + } + } + + // Assumes the type does not change. That's why we're calling + // CheckINPUTType here on the OUTPUT pin. + HRESULT hr = m_pTIPFilter->CheckInputType(pmt); + if (hr!=S_OK) return hr; + + if( m_pTIPFilter->m_pInput->IsConnected() ) + return m_pTIPFilter->m_pInput->GetConnected()->QueryAccept( pmt ); + else + return S_OK; + +} // CheckMediaType + + +/* Save the allocator pointer in the output pin +*/ +void +CTransInPlaceOutputPin::SetAllocator(IMemAllocator * pAllocator) +{ + pAllocator->AddRef(); + if (m_pAllocator) { + m_pAllocator->Release(); + } + m_pAllocator = pAllocator; +} // SetAllocator + + +// CTransInPlaceOutputPin::CompleteConnect() calls CBaseOutputPin::CompleteConnect() +// and then calls CTransInPlaceFilter::CompleteConnect(). It does this because +// CTransInPlaceFilter::CompleteConnect() can reconnect a pin and we do not want to +// reconnect a pin if CBaseOutputPin::CompleteConnect() fails. +// CBaseOutputPin::CompleteConnect() often fails when our output pin is being connected +// to the Video Mixing Renderer. +HRESULT +CTransInPlaceOutputPin::CompleteConnect(IPin *pReceivePin) +{ + HRESULT hr = CBaseOutputPin::CompleteConnect(pReceivePin); + if (FAILED(hr)) { + return hr; + } + + return m_pTransformFilter->CompleteConnect(PINDIR_OUTPUT,pReceivePin); +} // CompleteConnect diff --git a/ThirdParty/strmbas/transip.h b/ThirdParty/strmbas/transip.h new file mode 100644 index 0000000..4945f62 --- /dev/null +++ b/ThirdParty/strmbas/transip.h @@ -0,0 +1,250 @@ +//------------------------------------------------------------------------------ +// File: TransIP.h +// +// Desc: DirectShow base classes - defines classes from which simple +// Transform-In-Place filters may be derived. +// +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------------------------ + + +// +// The difference between this and Transfrm.h is that Transfrm copies the data. +// +// It assumes the filter has one input and one output stream, and has no +// interest in memory management, interface negotiation or anything else. +// +// Derive your class from this, and supply Transform and the media type/format +// negotiation functions. Implement that class, compile and link and +// you're done. + + +#ifndef __TRANSIP__ +#define __TRANSIP__ + +// ====================================================================== +// This is the com object that represents a simple transform filter. It +// supports IBaseFilter, IMediaFilter and two pins through nested interfaces +// ====================================================================== + +class CTransInPlaceFilter; + +// Several of the pin functions call filter functions to do the work, +// so you can often use the pin classes unaltered, just overriding the +// functions in CTransInPlaceFilter. If that's not enough and you want +// to derive your own pin class, override GetPin in the filter to supply +// your own pin classes to the filter. + +// ================================================== +// Implements the input pin +// ================================================== + +class CTransInPlaceInputPin : public CTransformInputPin +{ + +protected: + CTransInPlaceFilter * const m_pTIPFilter; // our filter + BOOL m_bReadOnly; // incoming stream is read only + +public: + + CTransInPlaceInputPin( + TCHAR *pObjectName, + CTransInPlaceFilter *pFilter, + HRESULT *phr, + LPCWSTR pName); + + // --- IMemInputPin ----- + + // Provide an enumerator for media types by getting one from downstream + STDMETHODIMP EnumMediaTypes( IEnumMediaTypes **ppEnum ); + + // Say whether media type is acceptable. + HRESULT CheckMediaType(const CMediaType* pmt); + + // Return our upstream allocator + STDMETHODIMP GetAllocator(IMemAllocator ** ppAllocator); + + // get told which allocator the upstream output pin is actually + // going to use. + STDMETHODIMP NotifyAllocator(IMemAllocator * pAllocator, + BOOL bReadOnly); + + // Allow the filter to see what allocator we have + // N.B. This does NOT AddRef + IMemAllocator * PeekAllocator() const + { return m_pAllocator; } + + // Pass this on downstream if it ever gets called. + STDMETHODIMP GetAllocatorRequirements(ALLOCATOR_PROPERTIES *pProps); + + HRESULT CompleteConnect(IPin *pReceivePin); + + inline const BOOL ReadOnly() { return m_bReadOnly ; } + +}; // CTransInPlaceInputPin + +// ================================================== +// Implements the output pin +// ================================================== + +class CTransInPlaceOutputPin : public CTransformOutputPin +{ + +protected: + // m_pFilter points to our CBaseFilter + CTransInPlaceFilter * const m_pTIPFilter; + +public: + + CTransInPlaceOutputPin( + TCHAR *pObjectName, + CTransInPlaceFilter *pFilter, + HRESULT *phr, + LPCWSTR pName); + + + // --- CBaseOutputPin ------------ + + // negotiate the allocator and its buffer size/count + // Insists on using our own allocator. (Actually the one upstream of us). + // We don't override this - instead we just agree the default + // then let the upstream filter decide for itself on reconnect + // virtual HRESULT DecideAllocator(IMemInputPin * pPin, IMemAllocator ** pAlloc); + + // Provide a media type enumerator. Get it from upstream. + STDMETHODIMP EnumMediaTypes( IEnumMediaTypes **ppEnum ); + + // Say whether media type is acceptable. + HRESULT CheckMediaType(const CMediaType* pmt); + + // This just saves the allocator being used on the output pin + // Also called by input pin's GetAllocator() + void SetAllocator(IMemAllocator * pAllocator); + + IMemInputPin * ConnectedIMemInputPin() + { return m_pInputPin; } + + // Allow the filter to see what allocator we have + // N.B. This does NOT AddRef + IMemAllocator * PeekAllocator() const + { return m_pAllocator; } + + HRESULT CompleteConnect(IPin *pReceivePin); + +}; // CTransInPlaceOutputPin + + +class AM_NOVTABLE CTransInPlaceFilter : public CTransformFilter +{ + +public: + + // map getpin/getpincount for base enum of pins to owner + // override this to return more specialised pin objects + + virtual CBasePin *GetPin(int n); + +public: + + // Set bModifiesData == false if your derived filter does + // not modify the data samples (for instance it's just copying + // them somewhere else or looking at the timestamps). + + CTransInPlaceFilter(TCHAR *, LPUNKNOWN, REFCLSID clsid, HRESULT *, + bool bModifiesData = true); +#ifdef UNICODE + CTransInPlaceFilter(CHAR *, LPUNKNOWN, REFCLSID clsid, HRESULT *, + bool bModifiesData = true); +#endif + // The following are defined to avoid undefined pure virtuals. + // Even if they are never called, they will give linkage warnings/errors + + // We override EnumMediaTypes to bypass the transform class enumerator + // which would otherwise call this. + HRESULT GetMediaType(int iPosition, CMediaType *pMediaType) + { DbgBreak("CTransInPlaceFilter::GetMediaType should never be called"); + return E_UNEXPECTED; + } + + // This is called when we actually have to provide out own allocator. + HRESULT DecideBufferSize(IMemAllocator*, ALLOCATOR_PROPERTIES *); + + // The functions which call this in CTransform are overridden in this + // class to call CheckInputType with the assumption that the type + // does not change. In Debug builds some calls will be made and + // we just ensure that they do not assert. + HRESULT CheckTransform(const CMediaType *mtIn, const CMediaType *mtOut) + { + return S_OK; + }; + + + // ================================================================= + // ----- You may want to override this ----------------------------- + // ================================================================= + + HRESULT CompleteConnect(PIN_DIRECTION dir,IPin *pReceivePin); + + // chance to customize the transform process + virtual HRESULT Receive(IMediaSample *pSample); + + // ================================================================= + // ----- You MUST override these ----------------------------------- + // ================================================================= + + virtual HRESULT Transform(IMediaSample *pSample) PURE; + + // this goes in the factory template table to create new instances + // static CCOMObject * CreateInstance(LPUNKNOWN, HRESULT *); + + +#ifdef PERF + // Override to register performance measurement with a less generic string + // You should do this to avoid confusion with other filters + virtual void RegisterPerfId() + {m_idTransInPlace = MSR_REGISTER(TEXT("TransInPlace"));} +#endif // PERF + + +// implementation details + +protected: + + IMediaSample * CTransInPlaceFilter::Copy(IMediaSample *pSource); + +#ifdef PERF + int m_idTransInPlace; // performance measuring id +#endif // PERF + bool m_bModifiesData; // Does this filter change the data? + + // these hold our input and output pins + + friend class CTransInPlaceInputPin; + friend class CTransInPlaceOutputPin; + + CTransInPlaceInputPin *InputPin() const + { + return (CTransInPlaceInputPin *)m_pInput; + }; + CTransInPlaceOutputPin *OutputPin() const + { + return (CTransInPlaceOutputPin *)m_pOutput; + }; + + // Helper to see if the input and output types match + BOOL TypesMatch() + { + return InputPin()->CurrentMediaType() == + OutputPin()->CurrentMediaType(); + } + + // Are the input and output allocators different? + BOOL UsingDifferentAllocators() const + { + return InputPin()->PeekAllocator() != OutputPin()->PeekAllocator(); + } +}; // CTransInPlaceFilter + +#endif /* __TRANSIP__ */ + diff --git a/ThirdParty/strmbas/videoctl.cpp b/ThirdParty/strmbas/videoctl.cpp new file mode 100644 index 0000000..c60c94f --- /dev/null +++ b/ThirdParty/strmbas/videoctl.cpp @@ -0,0 +1,715 @@ +//------------------------------------------------------------------------------ +// File: VideoCtl.cpp +// +// Desc: DirectShow base classes. +// +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------------------------ + + +#include +#include "ddmm.h" + +// Load a string from the resource file string table. The buffer must be at +// least STR_MAX_LENGTH bytes. The easiest way to use this is to declare a +// buffer in the property page class and use it for all string loading. It +// cannot be static as multiple property pages may be active simultaneously + +TCHAR *WINAPI StringFromResource(TCHAR *pBuffer, int iResourceID) +{ + if (LoadString(g_hInst,iResourceID,pBuffer,STR_MAX_LENGTH) == 0) { + return TEXT(""); + } + return pBuffer; +} + +#ifdef UNICODE +char *WINAPI StringFromResource(char *pBuffer, int iResourceID) +{ + if (LoadStringA(g_hInst,iResourceID,pBuffer,STR_MAX_LENGTH) == 0) { + return ""; + } + return pBuffer; +} +#endif + + + +// Property pages typically are called through their OLE interfaces. These +// use UNICODE strings regardless of how the binary is built. So when we +// load strings from the resource file we sometimes want to convert them +// to UNICODE. This method is passed the target UNICODE buffer and does a +// convert after loading the string (if built UNICODE this is not needed) +// On WinNT we can explicitly call LoadStringW which saves two conversions + +#ifndef UNICODE + +WCHAR * WINAPI WideStringFromResource(WCHAR *pBuffer, int iResourceID) +{ + *pBuffer = 0; + + if (g_amPlatform == VER_PLATFORM_WIN32_NT) { + LoadStringW(g_hInst,iResourceID,pBuffer,STR_MAX_LENGTH); + } else { + + CHAR szBuffer[STR_MAX_LENGTH]; + DWORD dwStringLength = LoadString(g_hInst,iResourceID,szBuffer,STR_MAX_LENGTH); + // if we loaded a string convert it to wide characters, ensuring + // that we also null terminate the result. + if (dwStringLength++) { + MultiByteToWideChar(CP_ACP,0,szBuffer,dwStringLength,pBuffer,STR_MAX_LENGTH); + } + } + return pBuffer; +} + +#endif + + +// Helper function to calculate the size of the dialog + +BOOL WINAPI GetDialogSize(int iResourceID, + DLGPROC pDlgProc, + LPARAM lParam, + SIZE *pResult) +{ + RECT rc; + HWND hwnd; + + // Create a temporary property page + + hwnd = CreateDialogParam(g_hInst, + MAKEINTRESOURCE(iResourceID), + GetDesktopWindow(), + pDlgProc, + lParam); + if (hwnd == NULL) { + return FALSE; + } + + GetWindowRect(hwnd, &rc); + pResult->cx = rc.right - rc.left; + pResult->cy = rc.bottom - rc.top; + + DestroyWindow(hwnd); + return TRUE; +} + + +// Class that aggregates on the IDirectDraw interface. Although DirectDraw +// has the ability in its interfaces to be aggregated they're not currently +// implemented. This makes it difficult for various parts of Quartz that want +// to aggregate these interfaces. In particular the video renderer passes out +// media samples that expose IDirectDraw and IDirectDrawSurface. The filter +// graph manager also exposes IDirectDraw as a plug in distributor. For these +// objects we provide these aggregation classes that republish the interfaces + +STDMETHODIMP CAggDirectDraw::NonDelegatingQueryInterface(REFIID riid, void **ppv) +{ + ASSERT(m_pDirectDraw); + + // Do we have this interface + + if (riid == IID_IDirectDraw) { + return GetInterface((IDirectDraw *)this,ppv); + } else { + return CUnknown::NonDelegatingQueryInterface(riid,ppv); + } +} + + +STDMETHODIMP CAggDirectDraw::Compact() +{ + ASSERT(m_pDirectDraw); + return m_pDirectDraw->Compact(); +} + + +STDMETHODIMP CAggDirectDraw::CreateClipper(DWORD dwFlags,LPDIRECTDRAWCLIPPER *lplpDDClipper,IUnknown *pUnkOuter) +{ + ASSERT(m_pDirectDraw); + return m_pDirectDraw->CreateClipper(dwFlags,lplpDDClipper,pUnkOuter); +} + + +STDMETHODIMP CAggDirectDraw::CreatePalette(DWORD dwFlags,LPPALETTEENTRY lpColorTable,LPDIRECTDRAWPALETTE *lplpDDPalette,IUnknown *pUnkOuter) +{ + ASSERT(m_pDirectDraw); + return m_pDirectDraw->CreatePalette(dwFlags,lpColorTable,lplpDDPalette,pUnkOuter); +} + + +STDMETHODIMP CAggDirectDraw::CreateSurface(LPDDSURFACEDESC lpDDSurfaceDesc,LPDIRECTDRAWSURFACE *lplpDDSurface,IUnknown *pUnkOuter) +{ + ASSERT(m_pDirectDraw); + return m_pDirectDraw->CreateSurface(lpDDSurfaceDesc,lplpDDSurface,pUnkOuter); +} + + +STDMETHODIMP CAggDirectDraw::DuplicateSurface(LPDIRECTDRAWSURFACE lpDDSurface,LPDIRECTDRAWSURFACE *lplpDupDDSurface) +{ + ASSERT(m_pDirectDraw); + return m_pDirectDraw->DuplicateSurface(lpDDSurface,lplpDupDDSurface); +} + + +STDMETHODIMP CAggDirectDraw::EnumDisplayModes(DWORD dwSurfaceDescCount,LPDDSURFACEDESC lplpDDSurfaceDescList,LPVOID lpContext,LPDDENUMMODESCALLBACK lpEnumCallback) +{ + ASSERT(m_pDirectDraw); + return m_pDirectDraw->EnumDisplayModes(dwSurfaceDescCount,lplpDDSurfaceDescList,lpContext,lpEnumCallback); +} + + +STDMETHODIMP CAggDirectDraw::EnumSurfaces(DWORD dwFlags,LPDDSURFACEDESC lpDDSD,LPVOID lpContext,LPDDENUMSURFACESCALLBACK lpEnumCallback) +{ + ASSERT(m_pDirectDraw); + return m_pDirectDraw->EnumSurfaces(dwFlags,lpDDSD,lpContext,lpEnumCallback); +} + + +STDMETHODIMP CAggDirectDraw::FlipToGDISurface() +{ + ASSERT(m_pDirectDraw); + return m_pDirectDraw->FlipToGDISurface(); +} + + +STDMETHODIMP CAggDirectDraw::GetCaps(LPDDCAPS lpDDDriverCaps,LPDDCAPS lpDDHELCaps) +{ + ASSERT(m_pDirectDraw); + return m_pDirectDraw->GetCaps(lpDDDriverCaps,lpDDHELCaps); +} + + +STDMETHODIMP CAggDirectDraw::GetDisplayMode(LPDDSURFACEDESC lpDDSurfaceDesc) +{ + ASSERT(m_pDirectDraw); + return m_pDirectDraw->GetDisplayMode(lpDDSurfaceDesc); +} + + +STDMETHODIMP CAggDirectDraw::GetFourCCCodes(LPDWORD lpNumCodes,LPDWORD lpCodes) +{ + ASSERT(m_pDirectDraw); + return m_pDirectDraw->GetFourCCCodes(lpNumCodes,lpCodes); +} + + +STDMETHODIMP CAggDirectDraw::GetGDISurface(LPDIRECTDRAWSURFACE *lplpGDIDDSurface) +{ + ASSERT(m_pDirectDraw); + return m_pDirectDraw->GetGDISurface(lplpGDIDDSurface); +} + + +STDMETHODIMP CAggDirectDraw::GetMonitorFrequency(LPDWORD lpdwFrequency) +{ + ASSERT(m_pDirectDraw); + return m_pDirectDraw->GetMonitorFrequency(lpdwFrequency); +} + + +STDMETHODIMP CAggDirectDraw::GetScanLine(LPDWORD lpdwScanLine) +{ + ASSERT(m_pDirectDraw); + return m_pDirectDraw->GetScanLine(lpdwScanLine); +} + + +STDMETHODIMP CAggDirectDraw::GetVerticalBlankStatus(LPBOOL lpblsInVB) +{ + ASSERT(m_pDirectDraw); + return m_pDirectDraw->GetVerticalBlankStatus(lpblsInVB); +} + + +STDMETHODIMP CAggDirectDraw::Initialize(GUID *lpGUID) +{ + ASSERT(m_pDirectDraw); + return m_pDirectDraw->Initialize(lpGUID); +} + + +STDMETHODIMP CAggDirectDraw::RestoreDisplayMode() +{ + ASSERT(m_pDirectDraw); + return m_pDirectDraw->RestoreDisplayMode(); +} + + +STDMETHODIMP CAggDirectDraw::SetCooperativeLevel(HWND hWnd,DWORD dwFlags) +{ + ASSERT(m_pDirectDraw); + return m_pDirectDraw->SetCooperativeLevel(hWnd,dwFlags); +} + + +STDMETHODIMP CAggDirectDraw::SetDisplayMode(DWORD dwWidth,DWORD dwHeight,DWORD dwBpp) +{ + ASSERT(m_pDirectDraw); + return m_pDirectDraw->SetDisplayMode(dwWidth,dwHeight,dwBpp); +} + + +STDMETHODIMP CAggDirectDraw::WaitForVerticalBlank(DWORD dwFlags,HANDLE hEvent) +{ + ASSERT(m_pDirectDraw); + return m_pDirectDraw->WaitForVerticalBlank(dwFlags,hEvent); +} + + +// Class that aggregates an IDirectDrawSurface interface. Although DirectDraw +// has the ability in its interfaces to be aggregated they're not currently +// implemented. This makes it difficult for various parts of Quartz that want +// to aggregate these interfaces. In particular the video renderer passes out +// media samples that expose IDirectDraw and IDirectDrawSurface. The filter +// graph manager also exposes IDirectDraw as a plug in distributor. For these +// objects we provide these aggregation classes that republish the interfaces + +STDMETHODIMP CAggDrawSurface::NonDelegatingQueryInterface(REFIID riid, void **ppv) +{ + ASSERT(m_pDirectDrawSurface); + + // Do we have this interface + + if (riid == IID_IDirectDrawSurface) { + return GetInterface((IDirectDrawSurface *)this,ppv); + } else { + return CUnknown::NonDelegatingQueryInterface(riid,ppv); + } +} + + +STDMETHODIMP CAggDrawSurface::AddAttachedSurface(LPDIRECTDRAWSURFACE lpDDSAttachedSurface) +{ + ASSERT(m_pDirectDrawSurface); + return m_pDirectDrawSurface->AddAttachedSurface(lpDDSAttachedSurface); +} + + +STDMETHODIMP CAggDrawSurface::AddOverlayDirtyRect(LPRECT lpRect) +{ + ASSERT(m_pDirectDrawSurface); + return m_pDirectDrawSurface->AddOverlayDirtyRect(lpRect); +} + + +STDMETHODIMP CAggDrawSurface::Blt(LPRECT lpDestRect,LPDIRECTDRAWSURFACE lpDDSrcSurface,LPRECT lpSrcRect,DWORD dwFlags,LPDDBLTFX lpDDBltFx) +{ + ASSERT(m_pDirectDrawSurface); + return m_pDirectDrawSurface->Blt(lpDestRect,lpDDSrcSurface,lpSrcRect,dwFlags,lpDDBltFx); +} + + +STDMETHODIMP CAggDrawSurface::BltBatch(LPDDBLTBATCH lpDDBltBatch,DWORD dwCount,DWORD dwFlags) +{ + ASSERT(m_pDirectDrawSurface); + return m_pDirectDrawSurface->BltBatch(lpDDBltBatch,dwCount,dwFlags); +} + + +STDMETHODIMP CAggDrawSurface::BltFast(DWORD dwX,DWORD dwY,LPDIRECTDRAWSURFACE lpDDSrcSurface,LPRECT lpSrcRect,DWORD dwTrans) +{ + ASSERT(m_pDirectDrawSurface); + return m_pDirectDrawSurface->BltFast(dwX,dwY,lpDDSrcSurface,lpSrcRect,dwTrans); +} + + +STDMETHODIMP CAggDrawSurface::DeleteAttachedSurface(DWORD dwFlags,LPDIRECTDRAWSURFACE lpDDSAttachedSurface) +{ + ASSERT(m_pDirectDrawSurface); + return m_pDirectDrawSurface->DeleteAttachedSurface(dwFlags,lpDDSAttachedSurface); +} + + +STDMETHODIMP CAggDrawSurface::EnumAttachedSurfaces(LPVOID lpContext,LPDDENUMSURFACESCALLBACK lpEnumSurfacesCallback) +{ + ASSERT(m_pDirectDrawSurface); + return m_pDirectDrawSurface->EnumAttachedSurfaces(lpContext,lpEnumSurfacesCallback); +} + + +STDMETHODIMP CAggDrawSurface::EnumOverlayZOrders(DWORD dwFlags,LPVOID lpContext,LPDDENUMSURFACESCALLBACK lpfnCallback) +{ + ASSERT(m_pDirectDrawSurface); + return m_pDirectDrawSurface->EnumOverlayZOrders(dwFlags,lpContext,lpfnCallback); +} + + +STDMETHODIMP CAggDrawSurface::Flip(LPDIRECTDRAWSURFACE lpDDSurfaceTargetOverride,DWORD dwFlags) +{ + ASSERT(m_pDirectDrawSurface); + return m_pDirectDrawSurface->Flip(lpDDSurfaceTargetOverride,dwFlags); +} + + +STDMETHODIMP CAggDrawSurface::GetAttachedSurface(LPDDSCAPS lpDDSCaps,LPDIRECTDRAWSURFACE *lplpDDAttachedSurface) +{ + ASSERT(m_pDirectDrawSurface); + return m_pDirectDrawSurface->GetAttachedSurface(lpDDSCaps,lplpDDAttachedSurface); +} + + +STDMETHODIMP CAggDrawSurface::GetBltStatus(DWORD dwFlags) +{ + ASSERT(m_pDirectDrawSurface); + return m_pDirectDrawSurface->GetBltStatus(dwFlags); +} + + +STDMETHODIMP CAggDrawSurface::GetCaps(LPDDSCAPS lpDDSCaps) +{ + ASSERT(m_pDirectDrawSurface); + return m_pDirectDrawSurface->GetCaps(lpDDSCaps); +} + + +STDMETHODIMP CAggDrawSurface::GetClipper(LPDIRECTDRAWCLIPPER *lplpDDClipper) +{ + ASSERT(m_pDirectDrawSurface); + return m_pDirectDrawSurface->GetClipper(lplpDDClipper); +} + + +STDMETHODIMP CAggDrawSurface::GetColorKey(DWORD dwFlags,LPDDCOLORKEY lpDDColorKey) +{ + ASSERT(m_pDirectDrawSurface); + return m_pDirectDrawSurface->GetColorKey(dwFlags,lpDDColorKey); +} + + +STDMETHODIMP CAggDrawSurface::GetDC(HDC *lphDC) +{ + ASSERT(m_pDirectDrawSurface); + return m_pDirectDrawSurface->GetDC(lphDC); +} + + +STDMETHODIMP CAggDrawSurface::GetFlipStatus(DWORD dwFlags) +{ + ASSERT(m_pDirectDrawSurface); + return m_pDirectDrawSurface->GetFlipStatus(dwFlags); +} + + +STDMETHODIMP CAggDrawSurface::GetOverlayPosition(LPLONG lpdwX,LPLONG lpdwY) +{ + ASSERT(m_pDirectDrawSurface); + return m_pDirectDrawSurface->GetOverlayPosition(lpdwX,lpdwY); +} + + +STDMETHODIMP CAggDrawSurface::GetPalette(LPDIRECTDRAWPALETTE *lplpDDPalette) +{ + ASSERT(m_pDirectDrawSurface); + return m_pDirectDrawSurface->GetPalette(lplpDDPalette); +} + + +STDMETHODIMP CAggDrawSurface::GetPixelFormat(LPDDPIXELFORMAT lpDDPixelFormat) +{ + ASSERT(m_pDirectDrawSurface); + return m_pDirectDrawSurface->GetPixelFormat(lpDDPixelFormat); +} + + +// A bit of a warning here: Our media samples in DirectShow aggregate on +// IDirectDraw and IDirectDrawSurface (ie are available through IMediaSample +// by QueryInterface). Unfortunately the underlying DirectDraw code cannot +// be aggregated so we have to use these classes. The snag is that when we +// call a different surface and pass in this interface as perhaps the source +// surface the call will fail because DirectDraw dereferences the pointer to +// get at its private data structures. Therefore we supply this workaround to give +// access to the real IDirectDraw surface. A filter can call GetSurfaceDesc +// and we will fill in the lpSurface pointer with the real underlying surface + +STDMETHODIMP CAggDrawSurface::GetSurfaceDesc(LPDDSURFACEDESC lpDDSurfaceDesc) +{ + ASSERT(m_pDirectDrawSurface); + + // First call down to the underlying DirectDraw + + HRESULT hr = m_pDirectDrawSurface->GetSurfaceDesc(lpDDSurfaceDesc); + if (FAILED(hr)) { + return hr; + } + + // Store the real DirectDrawSurface interface + lpDDSurfaceDesc->lpSurface = m_pDirectDrawSurface; + return hr; +} + + +STDMETHODIMP CAggDrawSurface::Initialize(LPDIRECTDRAW lpDD,LPDDSURFACEDESC lpDDSurfaceDesc) +{ + ASSERT(m_pDirectDrawSurface); + return m_pDirectDrawSurface->Initialize(lpDD,lpDDSurfaceDesc); +} + + +STDMETHODIMP CAggDrawSurface::IsLost() +{ + ASSERT(m_pDirectDrawSurface); + return m_pDirectDrawSurface->IsLost(); +} + + +STDMETHODIMP CAggDrawSurface::Lock(LPRECT lpDestRect,LPDDSURFACEDESC lpDDSurfaceDesc,DWORD dwFlags,HANDLE hEvent) +{ + ASSERT(m_pDirectDrawSurface); + return m_pDirectDrawSurface->Lock(lpDestRect,lpDDSurfaceDesc,dwFlags,hEvent); +} + + +STDMETHODIMP CAggDrawSurface::ReleaseDC(HDC hDC) +{ + ASSERT(m_pDirectDrawSurface); + return m_pDirectDrawSurface->ReleaseDC(hDC); +} + + +STDMETHODIMP CAggDrawSurface::Restore() +{ + ASSERT(m_pDirectDrawSurface); + return m_pDirectDrawSurface->Restore(); +} + + +STDMETHODIMP CAggDrawSurface::SetClipper(LPDIRECTDRAWCLIPPER lpDDClipper) +{ + ASSERT(m_pDirectDrawSurface); + return m_pDirectDrawSurface->SetClipper(lpDDClipper); +} + + +STDMETHODIMP CAggDrawSurface::SetColorKey(DWORD dwFlags,LPDDCOLORKEY lpDDColorKey) +{ + ASSERT(m_pDirectDrawSurface); + return m_pDirectDrawSurface->SetColorKey(dwFlags,lpDDColorKey); +} + + +STDMETHODIMP CAggDrawSurface::SetOverlayPosition(LONG dwX,LONG dwY) +{ + ASSERT(m_pDirectDrawSurface); + return m_pDirectDrawSurface->SetOverlayPosition(dwX,dwY); +} + + +STDMETHODIMP CAggDrawSurface::SetPalette(LPDIRECTDRAWPALETTE lpDDPalette) +{ + ASSERT(m_pDirectDrawSurface); + return m_pDirectDrawSurface->SetPalette(lpDDPalette); +} + + +STDMETHODIMP CAggDrawSurface::Unlock(LPVOID lpSurfaceData) +{ + ASSERT(m_pDirectDrawSurface); + return m_pDirectDrawSurface->Unlock(lpSurfaceData); +} + + +STDMETHODIMP CAggDrawSurface::UpdateOverlay(LPRECT lpSrcRect,LPDIRECTDRAWSURFACE lpDDDestSurface,LPRECT lpDestRect,DWORD dwFlags,LPDDOVERLAYFX lpDDOverlayFX) +{ + ASSERT(m_pDirectDrawSurface); + return m_pDirectDrawSurface->UpdateOverlay(lpSrcRect,lpDDDestSurface,lpDestRect,dwFlags,lpDDOverlayFX); +} + + +STDMETHODIMP CAggDrawSurface::UpdateOverlayDisplay(DWORD dwFlags) +{ + ASSERT(m_pDirectDrawSurface); + return m_pDirectDrawSurface->UpdateOverlayDisplay(dwFlags); +} + + +STDMETHODIMP CAggDrawSurface::UpdateOverlayZOrder(DWORD dwFlags,LPDIRECTDRAWSURFACE lpDDSReference) +{ + ASSERT(m_pDirectDrawSurface); + return m_pDirectDrawSurface->UpdateOverlayZOrder(dwFlags,lpDDSReference); +} + + +// DirectShow must work on multiple platforms. In particular, it also runs on +// Windows NT 3.51 which does not have DirectDraw capabilities. The filters +// cannot therefore link statically to the DirectDraw library. To make their +// lives that little bit easier we provide this class that manages loading +// and unloading the library and creating the initial IDirectDraw interface + +CLoadDirectDraw::CLoadDirectDraw() : + m_pDirectDraw(NULL), + m_hDirectDraw(NULL) +{ +} + + +// Destructor forces unload + +CLoadDirectDraw::~CLoadDirectDraw() +{ + ReleaseDirectDraw(); + + if (m_hDirectDraw) { + NOTE("Unloading library"); + FreeLibrary(m_hDirectDraw); + } +} + + +// We can't be sure that DirectDraw is always available so we can't statically +// link to the library. Therefore we load the library, get the function entry +// point addresses and call them to create the driver objects. We return S_OK +// if we manage to load DirectDraw correctly otherwise we return E_NOINTERFACE +// We initialise a DirectDraw instance by explicitely loading the library and +// calling GetProcAddress on the DirectDrawCreate entry point that it exports + +// On a multi monitor system, we can get the DirectDraw object for any +// monitor (device) with the optional szDevice parameter + +HRESULT CLoadDirectDraw::LoadDirectDraw(LPSTR szDevice) +{ + PDRAWCREATE pDrawCreate; + PDRAWENUM pDrawEnum; + LPDIRECTDRAWENUMERATEEXA pDrawEnumEx; + HRESULT hr = NOERROR; + + NOTE("Entering DoLoadDirectDraw"); + + // Is DirectDraw already loaded + + if (m_pDirectDraw) { + NOTE("Already loaded"); + ASSERT(m_hDirectDraw); + return NOERROR; + } + + // Make sure the library is available + + if(!m_hDirectDraw) + { + UINT ErrorMode = SetErrorMode(SEM_NOOPENFILEERRORBOX); + m_hDirectDraw = LoadLibrary(TEXT("DDRAW.DLL")); + SetErrorMode(ErrorMode); + + if (m_hDirectDraw == NULL) { + DbgLog((LOG_ERROR,1,TEXT("Can't load DDRAW.DLL"))); + NOTE("No library"); + return E_NOINTERFACE; + } + } + + // Get the DLL address for the creator function + + pDrawCreate = (PDRAWCREATE)GetProcAddress(m_hDirectDraw,"DirectDrawCreate"); + // force ANSI, we assume it + pDrawEnum = (PDRAWENUM)GetProcAddress(m_hDirectDraw,"DirectDrawEnumerateA"); + pDrawEnumEx = (LPDIRECTDRAWENUMERATEEXA)GetProcAddress(m_hDirectDraw, + "DirectDrawEnumerateExA"); + + // We don't NEED DirectDrawEnumerateEx, that's just for multimon stuff + if (pDrawCreate == NULL || pDrawEnum == NULL) { + DbgLog((LOG_ERROR,1,TEXT("Can't get functions: Create=%x Enum=%x"), + pDrawCreate, pDrawEnum)); + NOTE("No entry point"); + ReleaseDirectDraw(); + return E_NOINTERFACE; + } + + DbgLog((LOG_TRACE,3,TEXT("Creating DDraw for device %s"), + szDevice ? szDevice : "")); + + // Create a DirectDraw display provider for this device, using the fancy + // multimon-aware version, if it exists + if (pDrawEnumEx) + m_pDirectDraw = DirectDrawCreateFromDeviceEx(szDevice, pDrawCreate, + pDrawEnumEx); + else + m_pDirectDraw = DirectDrawCreateFromDevice(szDevice, pDrawCreate, + pDrawEnum); + + if (m_pDirectDraw == NULL) { + DbgLog((LOG_ERROR,1,TEXT("Can't create DDraw"))); + NOTE("No instance"); + ReleaseDirectDraw(); + return E_NOINTERFACE; + } + return NOERROR; +} + + +// Called to release any DirectDraw provider we previously loaded. We may be +// called at any time especially when something goes horribly wrong and when +// we need to clean up before returning so we can't guarantee that all state +// variables are consistent so free only those really allocated allocated +// This should only be called once all reference counts have been released + +void CLoadDirectDraw::ReleaseDirectDraw() +{ + NOTE("Releasing DirectDraw driver"); + + // Release any DirectDraw provider interface + + if (m_pDirectDraw) { + NOTE("Releasing instance"); + m_pDirectDraw->Release(); + m_pDirectDraw = NULL; + } + +} + + +// Return NOERROR (S_OK) if DirectDraw has been loaded by this object + +HRESULT CLoadDirectDraw::IsDirectDrawLoaded() +{ + NOTE("Entering IsDirectDrawLoaded"); + + if (m_pDirectDraw == NULL) { + NOTE("DirectDraw not loaded"); + return S_FALSE; + } + return NOERROR; +} + + +// Return the IDirectDraw interface we look after + +LPDIRECTDRAW CLoadDirectDraw::GetDirectDraw() +{ + NOTE("Entering GetDirectDraw"); + + if (m_pDirectDraw == NULL) { + NOTE("No DirectDraw"); + return NULL; + } + + NOTE("Returning DirectDraw"); + m_pDirectDraw->AddRef(); + return m_pDirectDraw; +} + + +// Are we running on Direct Draw version 1? We need to find out as +// we rely on specific bug fixes in DirectDraw 2 for fullscreen playback. To +// find out, we simply see if it supports IDirectDraw2. Only version 2 and +// higher support this. + +BOOL CLoadDirectDraw::IsDirectDrawVersion1() +{ + + if (m_pDirectDraw == NULL) + return FALSE; + + IDirectDraw2 *p = NULL; + HRESULT hr = m_pDirectDraw->QueryInterface(IID_IDirectDraw2, (void **)&p); + if (p) + p->Release(); + if (hr == NOERROR) { + DbgLog((LOG_TRACE,3,TEXT("Direct Draw Version 2 or greater"))); + return FALSE; + } else { + DbgLog((LOG_TRACE,3,TEXT("Direct Draw Version 1"))); + return TRUE; + } +} diff --git a/ThirdParty/strmbas/videoctl.h b/ThirdParty/strmbas/videoctl.h new file mode 100644 index 0000000..9e05357 --- /dev/null +++ b/ThirdParty/strmbas/videoctl.h @@ -0,0 +1,178 @@ +//------------------------------------------------------------------------------ +// File: VideoCtl.h +// +// Desc: DirectShow base classes. +// +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------------------------ + + +#ifndef __VIDEOCTL__ +#define __VIDEOCTL__ + +// These help with property page implementations. The first can be used to +// load any string from a resource file. The buffer to load into is passed +// as an input parameter. The same buffer is the return value if the string +// was found otherwise it returns TEXT(""). The GetDialogSize is passed the +// resource ID of a dialog box and returns the size of it in screen pixels + +#define STR_MAX_LENGTH 256 +TCHAR * WINAPI StringFromResource(TCHAR *pBuffer, int iResourceID); + +#ifdef UNICODE +#define WideStringFromResource StringFromResource +char* WINAPI StringFromResource(char*pBuffer, int iResourceID); +#else +WCHAR * WINAPI WideStringFromResource(WCHAR *pBuffer, int iResourceID); +#endif + + +BOOL WINAPI GetDialogSize(int iResourceID, // Dialog box resource identifier + DLGPROC pDlgProc, // Pointer to dialog procedure + LPARAM lParam, // Any user data wanted in pDlgProc + SIZE *pResult); // Returns the size of dialog box + +// Class that aggregates an IDirectDraw interface + +class CAggDirectDraw : public IDirectDraw, public CUnknown +{ +protected: + + LPDIRECTDRAW m_pDirectDraw; + +public: + + DECLARE_IUNKNOWN + STDMETHODIMP NonDelegatingQueryInterface(REFIID riid,void **ppv); + + // Constructor and destructor + + CAggDirectDraw(TCHAR *pName,LPUNKNOWN pUnk) : + CUnknown(pName,pUnk), + m_pDirectDraw(NULL) { }; + + virtual CAggDirectDraw::~CAggDirectDraw() { }; + + // Set the object we should be aggregating + void SetDirectDraw(LPDIRECTDRAW pDirectDraw) { + m_pDirectDraw = pDirectDraw; + } + + // IDirectDraw methods + + STDMETHODIMP Compact(); + STDMETHODIMP CreateClipper(DWORD dwFlags,LPDIRECTDRAWCLIPPER *lplpDDClipper,IUnknown *pUnkOuter); + STDMETHODIMP CreatePalette(DWORD dwFlags,LPPALETTEENTRY lpColorTable,LPDIRECTDRAWPALETTE *lplpDDPalette,IUnknown *pUnkOuter); + STDMETHODIMP CreateSurface(LPDDSURFACEDESC lpDDSurfaceDesc,LPDIRECTDRAWSURFACE *lplpDDSurface,IUnknown *pUnkOuter); + STDMETHODIMP DuplicateSurface(LPDIRECTDRAWSURFACE lpDDSurface,LPDIRECTDRAWSURFACE *lplpDupDDSurface); + STDMETHODIMP EnumDisplayModes(DWORD dwSurfaceDescCount,LPDDSURFACEDESC lplpDDSurfaceDescList,LPVOID lpContext,LPDDENUMMODESCALLBACK lpEnumCallback); + STDMETHODIMP EnumSurfaces(DWORD dwFlags,LPDDSURFACEDESC lpDDSD,LPVOID lpContext,LPDDENUMSURFACESCALLBACK lpEnumCallback); + STDMETHODIMP FlipToGDISurface(); + STDMETHODIMP GetCaps(LPDDCAPS lpDDDriverCaps,LPDDCAPS lpDDHELCaps); + STDMETHODIMP GetDisplayMode(LPDDSURFACEDESC lpDDSurfaceDesc); + STDMETHODIMP GetFourCCCodes(LPDWORD lpNumCodes,LPDWORD lpCodes); + STDMETHODIMP GetGDISurface(LPDIRECTDRAWSURFACE *lplpGDIDDSurface); + STDMETHODIMP GetMonitorFrequency(LPDWORD lpdwFrequency); + STDMETHODIMP GetScanLine(LPDWORD lpdwScanLine); + STDMETHODIMP GetVerticalBlankStatus(LPBOOL lpblsInVB); + STDMETHODIMP Initialize(GUID *lpGUID); + STDMETHODIMP RestoreDisplayMode(); + STDMETHODIMP SetCooperativeLevel(HWND hWnd,DWORD dwFlags); + STDMETHODIMP SetDisplayMode(DWORD dwWidth,DWORD dwHeight,DWORD dwBpp); + STDMETHODIMP WaitForVerticalBlank(DWORD dwFlags,HANDLE hEvent); +}; + + +// Class that aggregates an IDirectDrawSurface interface + +class CAggDrawSurface : public IDirectDrawSurface, public CUnknown +{ +protected: + + LPDIRECTDRAWSURFACE m_pDirectDrawSurface; + +public: + + DECLARE_IUNKNOWN + STDMETHODIMP NonDelegatingQueryInterface(REFIID riid,void **ppv); + + // Constructor and destructor + + CAggDrawSurface(TCHAR *pName,LPUNKNOWN pUnk) : + CUnknown(pName,pUnk), + m_pDirectDrawSurface(NULL) { }; + + virtual ~CAggDrawSurface() { }; + + // Set the object we should be aggregating + void SetDirectDrawSurface(LPDIRECTDRAWSURFACE pDirectDrawSurface) { + m_pDirectDrawSurface = pDirectDrawSurface; + } + + // IDirectDrawSurface methods + + STDMETHODIMP AddAttachedSurface(LPDIRECTDRAWSURFACE lpDDSAttachedSurface); + STDMETHODIMP AddOverlayDirtyRect(LPRECT lpRect); + STDMETHODIMP Blt(LPRECT lpDestRect,LPDIRECTDRAWSURFACE lpDDSrcSurface,LPRECT lpSrcRect,DWORD dwFlags,LPDDBLTFX lpDDBltFx); + STDMETHODIMP BltBatch(LPDDBLTBATCH lpDDBltBatch,DWORD dwCount,DWORD dwFlags); + STDMETHODIMP BltFast(DWORD dwX,DWORD dwY,LPDIRECTDRAWSURFACE lpDDSrcSurface,LPRECT lpSrcRect,DWORD dwTrans); + STDMETHODIMP DeleteAttachedSurface(DWORD dwFlags,LPDIRECTDRAWSURFACE lpDDSAttachedSurface); + STDMETHODIMP EnumAttachedSurfaces(LPVOID lpContext,LPDDENUMSURFACESCALLBACK lpEnumSurfacesCallback); + STDMETHODIMP EnumOverlayZOrders(DWORD dwFlags,LPVOID lpContext,LPDDENUMSURFACESCALLBACK lpfnCallback); + STDMETHODIMP Flip(LPDIRECTDRAWSURFACE lpDDSurfaceTargetOverride,DWORD dwFlags); + STDMETHODIMP GetAttachedSurface(LPDDSCAPS lpDDSCaps,LPDIRECTDRAWSURFACE *lplpDDAttachedSurface); + STDMETHODIMP GetBltStatus(DWORD dwFlags); + STDMETHODIMP GetCaps(LPDDSCAPS lpDDSCaps); + STDMETHODIMP GetClipper(LPDIRECTDRAWCLIPPER *lplpDDClipper); + STDMETHODIMP GetColorKey(DWORD dwFlags,LPDDCOLORKEY lpDDColorKey); + STDMETHODIMP GetDC(HDC *lphDC); + STDMETHODIMP GetFlipStatus(DWORD dwFlags); + STDMETHODIMP GetOverlayPosition(LPLONG lpdwX,LPLONG lpdwY); + STDMETHODIMP GetPalette(LPDIRECTDRAWPALETTE *lplpDDPalette); + STDMETHODIMP GetPixelFormat(LPDDPIXELFORMAT lpDDPixelFormat); + STDMETHODIMP GetSurfaceDesc(LPDDSURFACEDESC lpDDSurfaceDesc); + STDMETHODIMP Initialize(LPDIRECTDRAW lpDD,LPDDSURFACEDESC lpDDSurfaceDesc); + STDMETHODIMP IsLost(); + STDMETHODIMP Lock(LPRECT lpDestRect,LPDDSURFACEDESC lpDDSurfaceDesc,DWORD dwFlags,HANDLE hEvent); + STDMETHODIMP ReleaseDC(HDC hDC); + STDMETHODIMP Restore(); + STDMETHODIMP SetClipper(LPDIRECTDRAWCLIPPER lpDDClipper); + STDMETHODIMP SetColorKey(DWORD dwFlags,LPDDCOLORKEY lpDDColorKey); + STDMETHODIMP SetOverlayPosition(LONG dwX,LONG dwY); + STDMETHODIMP SetPalette(LPDIRECTDRAWPALETTE lpDDPalette); + STDMETHODIMP Unlock(LPVOID lpSurfaceData); + STDMETHODIMP UpdateOverlay(LPRECT lpSrcRect,LPDIRECTDRAWSURFACE lpDDDestSurface,LPRECT lpDestRect,DWORD dwFlags,LPDDOVERLAYFX lpDDOverlayFX); + STDMETHODIMP UpdateOverlayDisplay(DWORD dwFlags); + STDMETHODIMP UpdateOverlayZOrder(DWORD dwFlags,LPDIRECTDRAWSURFACE lpDDSReference); +}; + + +// DirectShow must work on multiple platforms. In particular, it also runs on +// Windows NT 3.51 which does not have DirectDraw capabilities. The filters +// cannot therefore link statically to the DirectDraw library. To make their +// lives that little bit easier we provide this class that manages loading +// and unloading the library and creating the initial IDirectDraw interface + +typedef DWORD (WINAPI *PGETFILEVERSIONINFOSIZE)(LPTSTR,LPDWORD); +typedef BOOL (WINAPI *PGETFILEVERSIONINFO)(LPTSTR,DWORD,DWORD,LPVOID); +typedef BOOL (WINAPI *PVERQUERYVALUE)(LPVOID,LPTSTR,LPVOID,PUINT); + +class CLoadDirectDraw +{ + LPDIRECTDRAW m_pDirectDraw; // The DirectDraw driver instance + HINSTANCE m_hDirectDraw; // Handle to the loaded library + +public: + + CLoadDirectDraw(); + ~CLoadDirectDraw(); + + HRESULT LoadDirectDraw(LPSTR szDevice); + void ReleaseDirectDraw(); + HRESULT IsDirectDrawLoaded(); + LPDIRECTDRAW GetDirectDraw(); + BOOL IsDirectDrawVersion1(); +}; + +#endif // __VIDEOCTL__ + diff --git a/ThirdParty/strmbas/vtrans.cpp b/ThirdParty/strmbas/vtrans.cpp new file mode 100644 index 0000000..bc4f2cd --- /dev/null +++ b/ThirdParty/strmbas/vtrans.cpp @@ -0,0 +1,468 @@ +//------------------------------------------------------------------------------ +// File: Vtrans.cpp +// +// Desc: DirectShow base classes. +// +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------------------------ + + +#include +#include +// #include // now in precomp file streams.h + +CVideoTransformFilter::CVideoTransformFilter + ( TCHAR *pName, LPUNKNOWN pUnk, REFCLSID clsid) + : CTransformFilter(pName, pUnk, clsid) + , m_itrLate(0) + , m_nKeyFramePeriod(0) // No QM until we see at least 2 key frames + , m_nFramesSinceKeyFrame(0) + , m_bSkipping(FALSE) + , m_tDecodeStart(0) + , m_itrAvgDecode(300000) // 30mSec - probably allows skipping + , m_bQualityChanged(FALSE) +{ +#ifdef PERF + RegisterPerfId(); +#endif // PERF +} + + +CVideoTransformFilter::~CVideoTransformFilter() +{ + // nothing to do +} + + +// Reset our quality management state + +HRESULT CVideoTransformFilter::StartStreaming() +{ + m_itrLate = 0; + m_nKeyFramePeriod = 0; // No QM until we see at least 2 key frames + m_nFramesSinceKeyFrame = 0; + m_bSkipping = FALSE; + m_tDecodeStart = 0; + m_itrAvgDecode = 300000; // 30mSec - probably allows skipping + m_bQualityChanged = FALSE; + m_bSampleSkipped = FALSE; + return NOERROR; +} + + +// Overriden to reset quality management information + +HRESULT CVideoTransformFilter::EndFlush() +{ + { + // Synchronize + CAutoLock lck(&m_csReceive); + + // Reset our stats + // + // Note - we don't want to call derived classes here, + // we only want to reset our internal variables and this + // is a convenient way to do it + CVideoTransformFilter::StartStreaming(); + } + return CTransformFilter::EndFlush(); +} + + +HRESULT CVideoTransformFilter::AbortPlayback(HRESULT hr) +{ + NotifyEvent(EC_ERRORABORT, hr, 0); + m_pOutput->DeliverEndOfStream(); + return hr; +} + + +// Receive() +// +// Accept a sample from upstream, decide whether to process it +// or drop it. If we process it then get a buffer from the +// allocator of the downstream connection, transform it into the +// new buffer and deliver it to the downstream filter. +// If we decide not to process it then we do not get a buffer. + +// Remember that although this code will notice format changes coming into +// the input pin, it will NOT change its output format if that results +// in the filter needing to make a corresponding output format change. Your +// derived filter will have to take care of that. (eg. a palette change if +// the input and output is an 8 bit format). If the input sample is discarded +// and nothing is sent out for this Receive, please remember to put the format +// change on the first output sample that you actually do send. +// If your filter will produce the same output type even when the input type +// changes, then this base class code will do everything you need. + +HRESULT CVideoTransformFilter::Receive(IMediaSample *pSample) +{ + // If the next filter downstream is the video renderer, then it may + // be able to operate in DirectDraw mode which saves copying the data + // and gives higher performance. In that case the buffer which we + // get from GetDeliveryBuffer will be a DirectDraw buffer, and + // drawing into this buffer draws directly onto the display surface. + // This means that any waiting for the correct time to draw occurs + // during GetDeliveryBuffer, and that once the buffer is given to us + // the video renderer will count it in its statistics as a frame drawn. + // This means that any decision to drop the frame must be taken before + // calling GetDeliveryBuffer. + + ASSERT(CritCheckIn(&m_csReceive)); + AM_MEDIA_TYPE *pmtOut, *pmt; +#ifdef DEBUG + FOURCCMap fccOut; +#endif + HRESULT hr; + ASSERT(pSample); + IMediaSample * pOutSample; + + // If no output pin to deliver to then no point sending us data + ASSERT (m_pOutput != NULL) ; + + // The source filter may dynamically ask us to start transforming from a + // different media type than the one we're using now. If we don't, we'll + // draw garbage. (typically, this is a palette change in the movie, + // but could be something more sinister like the compression type changing, + // or even the video size changing) + +#define rcS1 ((VIDEOINFOHEADER *)(pmt->pbFormat))->rcSource +#define rcT1 ((VIDEOINFOHEADER *)(pmt->pbFormat))->rcTarget + + pSample->GetMediaType(&pmt); + if (pmt != NULL && pmt->pbFormat != NULL) { + + // spew some debug output + ASSERT(!IsEqualGUID(pmt->majortype, GUID_NULL)); +#ifdef DEBUG + fccOut.SetFOURCC(&pmt->subtype); + LONG lCompression = HEADER(pmt->pbFormat)->biCompression; + LONG lBitCount = HEADER(pmt->pbFormat)->biBitCount; + LONG lStride = (HEADER(pmt->pbFormat)->biWidth * lBitCount + 7) / 8; + lStride = (lStride + 3) & ~3; + DbgLog((LOG_TRACE,3,TEXT("*Changing input type on the fly to"))); + DbgLog((LOG_TRACE,3,TEXT("FourCC: %lx Compression: %lx BitCount: %ld"), + fccOut.GetFOURCC(), lCompression, lBitCount)); + DbgLog((LOG_TRACE,3,TEXT("biHeight: %ld rcDst: (%ld, %ld, %ld, %ld)"), + HEADER(pmt->pbFormat)->biHeight, + rcT1.left, rcT1.top, rcT1.right, rcT1.bottom)); + DbgLog((LOG_TRACE,3,TEXT("rcSrc: (%ld, %ld, %ld, %ld) Stride: %ld"), + rcS1.left, rcS1.top, rcS1.right, rcS1.bottom, + lStride)); +#endif + + // now switch to using the new format. I am assuming that the + // derived filter will do the right thing when its media type is + // switched and streaming is restarted. + + StopStreaming(); + m_pInput->CurrentMediaType() = *pmt; + DeleteMediaType(pmt); + // if this fails, playback will stop, so signal an error + hr = StartStreaming(); + if (FAILED(hr)) { + return AbortPlayback(hr); + } + } + + // Now that we have noticed any format changes on the input sample, it's + // OK to discard it. + + if (ShouldSkipFrame(pSample)) { + MSR_NOTE(m_idSkip); + m_bSampleSkipped = TRUE; + return NOERROR; + } + + // Set up the output sample + hr = InitializeOutputSample(pSample, &pOutSample); + + if (FAILED(hr)) { + return hr; + } + + m_bSampleSkipped = FALSE; + + // The renderer may ask us to on-the-fly to start transforming to a + // different format. If we don't obey it, we'll draw garbage + +#define rcS ((VIDEOINFOHEADER *)(pmtOut->pbFormat))->rcSource +#define rcT ((VIDEOINFOHEADER *)(pmtOut->pbFormat))->rcTarget + + pOutSample->GetMediaType(&pmtOut); + if (pmtOut != NULL && pmtOut->pbFormat != NULL) { + + // spew some debug output + ASSERT(!IsEqualGUID(pmtOut->majortype, GUID_NULL)); +#ifdef DEBUG + fccOut.SetFOURCC(&pmtOut->subtype); + LONG lCompression = HEADER(pmtOut->pbFormat)->biCompression; + LONG lBitCount = HEADER(pmtOut->pbFormat)->biBitCount; + LONG lStride = (HEADER(pmtOut->pbFormat)->biWidth * lBitCount + 7) / 8; + lStride = (lStride + 3) & ~3; + DbgLog((LOG_TRACE,3,TEXT("*Changing output type on the fly to"))); + DbgLog((LOG_TRACE,3,TEXT("FourCC: %lx Compression: %lx BitCount: %ld"), + fccOut.GetFOURCC(), lCompression, lBitCount)); + DbgLog((LOG_TRACE,3,TEXT("biHeight: %ld rcDst: (%ld, %ld, %ld, %ld)"), + HEADER(pmtOut->pbFormat)->biHeight, + rcT.left, rcT.top, rcT.right, rcT.bottom)); + DbgLog((LOG_TRACE,3,TEXT("rcSrc: (%ld, %ld, %ld, %ld) Stride: %ld"), + rcS.left, rcS.top, rcS.right, rcS.bottom, + lStride)); +#endif + + // now switch to using the new format. I am assuming that the + // derived filter will do the right thing when its media type is + // switched and streaming is restarted. + + StopStreaming(); + m_pOutput->CurrentMediaType() = *pmtOut; + DeleteMediaType(pmtOut); + hr = StartStreaming(); + + if (SUCCEEDED(hr)) { + // a new format, means a new empty buffer, so wait for a keyframe + // before passing anything on to the renderer. + // !!! a keyframe may never come, so give up after 30 frames + DbgLog((LOG_TRACE,3,TEXT("Output format change means we must wait for a keyframe"))); + m_nWaitForKey = 30; + + // if this fails, playback will stop, so signal an error + } else { + + // Must release the sample before calling AbortPlayback + // because we might be holding the win16 lock or + // ddraw lock + pOutSample->Release(); + AbortPlayback(hr); + return hr; + } + } + + // After a discontinuity, we need to wait for the next key frame + if (pSample->IsDiscontinuity() == S_OK) { + DbgLog((LOG_TRACE,3,TEXT("Non-key discontinuity - wait for keyframe"))); + m_nWaitForKey = 30; + } + + // Start timing the transform (and log it if PERF is defined) + + if (SUCCEEDED(hr)) { + m_tDecodeStart = timeGetTime(); + MSR_START(m_idTransform); + + // have the derived class transform the data + hr = Transform(pSample, pOutSample); + + // Stop the clock (and log it if PERF is defined) + MSR_STOP(m_idTransform); + m_tDecodeStart = timeGetTime()-m_tDecodeStart; + m_itrAvgDecode = m_tDecodeStart*(10000/16) + 15*(m_itrAvgDecode/16); + + // Maybe we're waiting for a keyframe still? + if (m_nWaitForKey) + m_nWaitForKey--; + if (m_nWaitForKey && pSample->IsSyncPoint() == S_OK) + m_nWaitForKey = FALSE; + + // if so, then we don't want to pass this on to the renderer + if (m_nWaitForKey && hr == NOERROR) { + DbgLog((LOG_TRACE,3,TEXT("still waiting for a keyframe"))); + hr = S_FALSE; + } + } + + if (FAILED(hr)) { + DbgLog((LOG_TRACE,1,TEXT("Error from video transform"))); + } else { + // the Transform() function can return S_FALSE to indicate that the + // sample should not be delivered; we only deliver the sample if it's + // really S_OK (same as NOERROR, of course.) + // Try not to return S_FALSE to a direct draw buffer (it's wasteful) + // Try to take the decision earlier - before you get it. + + if (hr == NOERROR) { + hr = m_pOutput->Deliver(pOutSample); + } else { + // S_FALSE returned from Transform is a PRIVATE agreement + // We should return NOERROR from Receive() in this case because returning S_FALSE + // from Receive() means that this is the end of the stream and no more data should + // be sent. + if (S_FALSE == hr) { + + // We must Release() the sample before doing anything + // like calling the filter graph because having the + // sample means we may have the DirectDraw lock + // (== win16 lock on some versions) + pOutSample->Release(); + m_bSampleSkipped = TRUE; + if (!m_bQualityChanged) { + m_bQualityChanged = TRUE; + NotifyEvent(EC_QUALITY_CHANGE,0,0); + } + return NOERROR; + } + } + } + + // release the output buffer. If the connected pin still needs it, + // it will have addrefed it itself. + pOutSample->Release(); + ASSERT(CritCheckIn(&m_csReceive)); + + return hr; +} + + + +BOOL CVideoTransformFilter::ShouldSkipFrame( IMediaSample * pIn) +{ + REFERENCE_TIME trStart, trStopAt; + HRESULT hr = pIn->GetTime(&trStart, &trStopAt); + + // Don't skip frames with no timestamps + if (hr != S_OK) + return FALSE; + + int itrFrame = (int)(trStopAt - trStart); // frame duration + + if(S_OK==pIn->IsSyncPoint()) { + MSR_INTEGER(m_idFrameType, 1); + if ( m_nKeyFramePeriod < m_nFramesSinceKeyFrame ) { + // record the max + m_nKeyFramePeriod = m_nFramesSinceKeyFrame; + } + m_nFramesSinceKeyFrame = 0; + m_bSkipping = FALSE; + } else { + MSR_INTEGER(m_idFrameType, 2); + if ( m_nFramesSinceKeyFrame>m_nKeyFramePeriod + && m_nKeyFramePeriod>0 + ) { + // We haven't seen the key frame yet, but we were clearly being + // overoptimistic about how frequent they are. + m_nKeyFramePeriod = m_nFramesSinceKeyFrame; + } + } + + + // Whatever we might otherwise decide, + // if we are taking only a small fraction of the required frame time to decode + // then any quality problems are actually coming from somewhere else. + // Could be a net problem at the source for instance. In this case there's + // no point in us skipping frames here. + if (m_itrAvgDecode*4>itrFrame) { + + // Don't skip unless we are at least a whole frame late. + // (We would skip B frames if more than 1/2 frame late, but they're safe). + if ( m_itrLate > itrFrame ) { + + // Don't skip unless the anticipated key frame would be no more than + // 1 frame early. If the renderer has not been waiting (we *guess* + // it hasn't because we're late) then it will allow frames to be + // played early by up to a frame. + + // Let T = Stream time from now to anticipated next key frame + // = (frame duration) * (KeyFramePeriod - FramesSinceKeyFrame) + // So we skip if T - Late < one frame i.e. + // (duration) * (freq - FramesSince) - Late < duration + // or (duration) * (freq - FramesSince - 1) < Late + + // We don't dare skip until we have seen some key frames and have + // some idea how often they occur and they are reasonably frequent. + if (m_nKeyFramePeriod>0) { + // It would be crazy - but we could have a stream with key frames + // a very long way apart - and if they are further than about + // 3.5 minutes apart then we could get arithmetic overflow in + // reference time units. Therefore we switch to mSec at this point + int it = (itrFrame/10000) + * (m_nKeyFramePeriod-m_nFramesSinceKeyFrame - 1); + MSR_INTEGER(m_idTimeTillKey, it); + + // For debug - might want to see the details - dump them as scratch pad +#ifdef VTRANSPERF + MSR_INTEGER(0, itrFrame); + MSR_INTEGER(0, m_nFramesSinceKeyFrame); + MSR_INTEGER(0, m_nKeyFramePeriod); +#endif + if (m_itrLate/10000 > it) { + m_bSkipping = TRUE; + // Now we are committed. Once we start skipping, we + // cannot stop until we hit a key frame. + } else { +#ifdef VTRANSPERF + MSR_INTEGER(0, 777770); // not near enough to next key +#endif + } + } else { +#ifdef VTRANSPERF + MSR_INTEGER(0, 777771); // Next key not predictable +#endif + } + } else { +#ifdef VTRANSPERF + MSR_INTEGER(0, 777772); // Less than one frame late + MSR_INTEGER(0, m_itrLate); + MSR_INTEGER(0, itrFrame); +#endif + } + } else { +#ifdef VTRANSPERF + MSR_INTEGER(0, 777773); // Decode time short - not not worth skipping + MSR_INTEGER(0, m_itrAvgDecode); + MSR_INTEGER(0, itrFrame); +#endif + } + + ++m_nFramesSinceKeyFrame; + + if (m_bSkipping) { + // We will count down the lateness as we skip each frame. + // We re-assess each frame. The key frame might not arrive when expected. + // We reset m_itrLate if we get a new Quality message, but actually that's + // not likely because we're not sending frames on to the Renderer. In + // fact if we DID get another one it would mean that there's a long + // pipe between us and the renderer and we might need an altogether + // better strategy to avoid hunting! + m_itrLate = m_itrLate - itrFrame; + } + + MSR_INTEGER(m_idLate, (int)m_itrLate/10000 ); // Note how late we think we are + if (m_bSkipping) { + if (!m_bQualityChanged) { + m_bQualityChanged = TRUE; + NotifyEvent(EC_QUALITY_CHANGE,0,0); + } + } + return m_bSkipping; +} + + +HRESULT CVideoTransformFilter::AlterQuality(Quality q) +{ + // to reduce the amount of 64 bit arithmetic, m_itrLate is an int. + // +, -, >, == etc are not too bad, but * and / are painful. + if (m_itrLate>300000000) { + // Avoid overflow and silliness - more than 30 secs late is already silly + m_itrLate = 300000000; + } else { + m_itrLate = (int)q.Late; + } + // We ignore the other fields + + // We're actually not very good at handling this. In non-direct draw mode + // most of the time can be spent in the renderer which can skip any frame. + // In that case we'd rather the renderer handled things. + // Nevertheless we will keep an eye on it and if we really start getting + // a very long way behind then we will actually skip - but we'll still tell + // the renderer (or whoever is downstream) that they should handle quality. + + return E_FAIL; // Tell the renderer to do his thing. + +} + + + +// This will avoid several hundred useless warnings if compiled -W4 by MS VC++ v4 +#pragma warning(disable:4514) + diff --git a/ThirdParty/strmbas/vtrans.h b/ThirdParty/strmbas/vtrans.h new file mode 100644 index 0000000..05a8aef --- /dev/null +++ b/ThirdParty/strmbas/vtrans.h @@ -0,0 +1,143 @@ +//------------------------------------------------------------------------------ +// File: VTrans.h +// +// Desc: DirectShow base classes - defines a video transform class. +// +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------------------------ + + +// This class is derived from CTransformFilter, but is specialised to handle +// the requirements of video quality control by frame dropping. +// This is a non-in-place transform, (i.e. it copies the data) such as a decoder. + +class CVideoTransformFilter : public CTransformFilter +{ + public: + + CVideoTransformFilter(TCHAR *, LPUNKNOWN, REFCLSID clsid); + ~CVideoTransformFilter(); + HRESULT EndFlush(); + + // ================================================================= + // ----- override these bits --------------------------------------- + // ================================================================= + // The following methods are in CTransformFilter which is inherited. + // They are mentioned here for completeness + // + // These MUST be supplied in a derived class + // + // NOTE: + // virtual HRESULT Transform(IMediaSample * pIn, IMediaSample *pOut); + // virtual HRESULT CheckInputType(const CMediaType* mtIn) PURE; + // virtual HRESULT CheckTransform + // (const CMediaType* mtIn, const CMediaType* mtOut) PURE; + // static CCOMObject * CreateInstance(LPUNKNOWN, HRESULT *); + // virtual HRESULT DecideBufferSize + // (IMemAllocator * pAllocator, ALLOCATOR_PROPERTIES *pprop) PURE; + // virtual HRESULT GetMediaType(int iPosition, CMediaType *pMediaType) PURE; + // + // These MAY also be overridden + // + // virtual HRESULT StopStreaming(); + // virtual HRESULT SetMediaType(PIN_DIRECTION direction,const CMediaType *pmt); + // virtual HRESULT CheckConnect(PIN_DIRECTION dir,IPin *pPin); + // virtual HRESULT BreakConnect(PIN_DIRECTION dir); + // virtual HRESULT CompleteConnect(PIN_DIRECTION direction,IPin *pReceivePin); + // virtual HRESULT EndOfStream(void); + // virtual HRESULT BeginFlush(void); + // virtual HRESULT EndFlush(void); + // virtual HRESULT NewSegment + // (REFERENCE_TIME tStart,REFERENCE_TIME tStop,double dRate); +#ifdef PERF + + // If you override this - ensure that you register all these ids + // as well as any of your own, + virtual void RegisterPerfId() { + m_idSkip = MSR_REGISTER(TEXT("Video Transform Skip frame")); + m_idFrameType = MSR_REGISTER(TEXT("Video transform frame type")); + m_idLate = MSR_REGISTER(TEXT("Video Transform Lateness")); + m_idTimeTillKey = MSR_REGISTER(TEXT("Video Transform Estd. time to next key")); + CTransformFilter::RegisterPerfId(); + } +#endif + + protected: + + // =========== QUALITY MANAGEMENT IMPLEMENTATION ======================== + // Frames are assumed to come in three types: + // Type 1: an AVI key frame or an MPEG I frame. + // This frame can be decoded with no history. + // Dropping this frame means that no further frame can be decoded + // until the next type 1 frame. + // Type 1 frames are sync points. + // Type 2: an AVI non-key frame or an MPEG P frame. + // This frame cannot be decoded unless the previous type 1 frame was + // decoded and all type 2 frames since have been decoded. + // Dropping this frame means that no further frame can be decoded + // until the next type 1 frame. + // Type 3: An MPEG B frame. + // This frame cannot be decoded unless the previous type 1 or 2 frame + // has been decoded AND the subsequent type 1 or 2 frame has also + // been decoded. (This requires decoding the frames out of sequence). + // Dropping this frame affects no other frames. This implementation + // does not allow for these. All non-sync-point frames are treated + // as being type 2. + // + // The spacing of frames of type 1 in a file is not guaranteed. There MUST + // be a type 1 frame at (well, near) the start of the file in order to start + // decoding at all. After that there could be one every half second or so, + // there could be one at the start of each scene (aka "cut", "shot") or + // there could be no more at all. + // If there is only a single type 1 frame then NO FRAMES CAN BE DROPPED + // without losing all the rest of the movie. There is no way to tell whether + // this is the case, so we find that we are in the gambling business. + // To try to improve the odds, we record the greatest interval between type 1s + // that we have seen and we bet on things being no worse than this in the + // future. + + // You can tell if it's a type 1 frame by calling IsSyncPoint(). + // there is no architected way to test for a type 3, so you should override + // the quality management here if you have B-frames. + + int m_nKeyFramePeriod; // the largest observed interval between type 1 frames + // 1 means every frame is type 1, 2 means every other. + + int m_nFramesSinceKeyFrame; // Used to count frames since the last type 1. + // becomes the new m_nKeyFramePeriod if greater. + + BOOL m_bSkipping; // we are skipping to the next type 1 frame + +#ifdef PERF + int m_idFrameType; // MSR id Frame type. 1=Key, 2="non-key" + int m_idSkip; // MSR id skipping + int m_idLate; // MSR id lateness + int m_idTimeTillKey; // MSR id for guessed time till next key frame. +#endif + + virtual HRESULT StartStreaming(); + + HRESULT AbortPlayback(HRESULT hr); // if something bad happens + + HRESULT Receive(IMediaSample *pSample); + + HRESULT AlterQuality(Quality q); + + BOOL ShouldSkipFrame(IMediaSample * pIn); + + int m_itrLate; // lateness from last Quality message + // (this overflows at 214 secs late). + int m_tDecodeStart; // timeGetTime when decode started. + int m_itrAvgDecode; // Average decode time in reference units. + + BOOL m_bNoSkip; // debug - no skipping. + + // We send an EC_QUALITY_CHANGE notification to the app if we have to degrade. + // We send one when we start degrading, not one for every frame, this means + // we track whether we've sent one yet. + BOOL m_bQualityChanged; + + // When non-zero, don't pass anything to renderer until next keyframe + // If there are few keys, give up and eventually draw something + int m_nWaitForKey; +}; diff --git a/ThirdParty/strmbas/winctrl.cpp b/ThirdParty/strmbas/winctrl.cpp new file mode 100644 index 0000000..4e269ea --- /dev/null +++ b/ThirdParty/strmbas/winctrl.cpp @@ -0,0 +1,2050 @@ +//------------------------------------------------------------------------------ +// File: WinCtrl.cpp +// +// Desc: DirectShow base classes - implements video control interface class. +// +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------------------------ + + +#include + +// The control interface methods require us to be connected + +#define CheckConnected(pin,code) \ +{ \ + if (pin == NULL) { \ + ASSERT(!TEXT("Pin not set")); \ + } else if (pin->IsConnected() == FALSE) { \ + return (code); \ + } \ +} + +// This checks to see whether the window has a drain. An application can in +// most environments set the owner/parent of windows so that they appear in +// a compound document context (for example). In this case, the application +// would probably like to be told of any keyboard/mouse messages. Therefore +// we pass these messages on untranslated, returning TRUE if we're successful + +BOOL WINAPI PossiblyEatMessage(HWND hwndDrain, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + if (hwndDrain != NULL && !InSendMessage()) + { + switch (uMsg) + { + case WM_CHAR: + case WM_DEADCHAR: + case WM_KEYDOWN: + case WM_KEYUP: + case WM_LBUTTONDBLCLK: + case WM_LBUTTONDOWN: + case WM_LBUTTONUP: + case WM_MBUTTONDBLCLK: + case WM_MBUTTONDOWN: + case WM_MBUTTONUP: + case WM_MOUSEACTIVATE: + case WM_MOUSEMOVE: + // If we pass this on we don't get any mouse clicks + //case WM_NCHITTEST: + case WM_NCLBUTTONDBLCLK: + case WM_NCLBUTTONDOWN: + case WM_NCLBUTTONUP: + case WM_NCMBUTTONDBLCLK: + case WM_NCMBUTTONDOWN: + case WM_NCMBUTTONUP: + case WM_NCMOUSEMOVE: + case WM_NCRBUTTONDBLCLK: + case WM_NCRBUTTONDOWN: + case WM_NCRBUTTONUP: + case WM_RBUTTONDBLCLK: + case WM_RBUTTONDOWN: + case WM_RBUTTONUP: + case WM_SYSCHAR: + case WM_SYSDEADCHAR: + case WM_SYSKEYDOWN: + case WM_SYSKEYUP: + + DbgLog((LOG_TRACE, 2, TEXT("Forwarding %x to drain"))); + PostMessage(hwndDrain, uMsg, wParam, lParam); + + return TRUE; + } + } + return FALSE; +} + + +// This class implements the IVideoWindow control functions (dual interface) +// we support a large number of properties and methods designed to allow the +// client (whether it be an automation controller or a C/C++ application) to +// set and get a number of window related properties such as it's position. +// We also support some methods that duplicate the properties but provide a +// more direct and efficient mechanism as many values may be changed in one + +CBaseControlWindow::CBaseControlWindow( + CBaseFilter *pFilter, // Owning filter + CCritSec *pInterfaceLock, // Locking object + TCHAR *pName, // Object description + LPUNKNOWN pUnk, // Normal COM ownership + HRESULT *phr) : // OLE return code + + CBaseVideoWindow(pName,pUnk), + m_pInterfaceLock(pInterfaceLock), + m_hwndOwner(NULL), + m_hwndDrain(NULL), + m_bAutoShow(TRUE), + m_pFilter(pFilter), + m_bCursorHidden(FALSE), + m_pPin(NULL) +{ + ASSERT(m_pFilter); + ASSERT(m_pInterfaceLock); + ASSERT(phr); + m_BorderColour = VIDEO_COLOUR; +} + + +// Set the title caption on the base window, we don't do any field checking +// as we really don't care what title they intend to have. We can always get +// it back again later with GetWindowText. The only other complication is to +// do the necessary string conversions between ANSI and OLE Unicode strings + +STDMETHODIMP CBaseControlWindow::put_Caption(BSTR strCaption) +{ + CheckPointer(strCaption,E_POINTER); + CheckConnected(m_pPin,VFW_E_NOT_CONNECTED); +#ifdef UNICODE + SetWindowText(m_hwnd, strCaption); +#else + CHAR Caption[CAPTION]; + + WideCharToMultiByte(CP_ACP,0,strCaption,-1,Caption,CAPTION,NULL,NULL); + SetWindowText(m_hwnd, Caption); +#endif + return NOERROR; +} + + +// Get the current base window title caption, once again we do no real field +// checking. We allocate a string for the window title to be filled in with +// which ensures the interface doesn't fiddle around with getting memory. A +// BSTR is a normal C string with the length at position (-1), we use the +// WriteBSTR helper function to create the caption to try and avoid OLE32 + +STDMETHODIMP CBaseControlWindow::get_Caption(BSTR *pstrCaption) +{ + CheckPointer(pstrCaption,E_POINTER); + CheckConnected(m_pPin,VFW_E_NOT_CONNECTED); + WCHAR WideCaption[CAPTION]; + +#ifdef UNICODE + GetWindowText(m_hwnd,WideCaption,CAPTION); +#else + // Convert the ASCII caption to a UNICODE string + + TCHAR Caption[CAPTION]; + GetWindowText(m_hwnd,Caption,CAPTION); + MultiByteToWideChar(CP_ACP,0,Caption,-1,WideCaption,CAPTION); +#endif + return WriteBSTR(pstrCaption,WideCaption); +} + + +// Set the window style using GWL_EXSTYLE + +STDMETHODIMP CBaseControlWindow::put_WindowStyleEx(long WindowStyleEx) +{ + CheckConnected(m_pPin,VFW_E_NOT_CONNECTED); + + // Should we be taking off WS_EX_TOPMOST + + if (GetWindowLong(m_hwnd,GWL_EXSTYLE) & WS_EX_TOPMOST) { + if ((WindowStyleEx & WS_EX_TOPMOST) == 0) { + SendMessage(m_hwnd,m_ShowStageTop,(WPARAM) FALSE,(LPARAM) 0); + } + } + + // Likewise should we be adding WS_EX_TOPMOST + + if (WindowStyleEx & WS_EX_TOPMOST) { + SendMessage(m_hwnd,m_ShowStageTop,(WPARAM) TRUE,(LPARAM) 0); + WindowStyleEx &= (~WS_EX_TOPMOST); + if (WindowStyleEx == 0) return NOERROR; + } + return DoSetWindowStyle(WindowStyleEx,GWL_EXSTYLE); +} + + +// Gets the current GWL_EXSTYLE base window style + +STDMETHODIMP CBaseControlWindow::get_WindowStyleEx(long *pWindowStyleEx) +{ + CheckPointer(pWindowStyleEx,E_POINTER); + CheckConnected(m_pPin,VFW_E_NOT_CONNECTED); + return DoGetWindowStyle(pWindowStyleEx,GWL_EXSTYLE); +} + + +// Set the window style using GWL_STYLE + +STDMETHODIMP CBaseControlWindow::put_WindowStyle(long WindowStyle) +{ + // These styles cannot be changed dynamically + + if ((WindowStyle & WS_DISABLED) || + (WindowStyle & WS_ICONIC) || + (WindowStyle & WS_MAXIMIZE) || + (WindowStyle & WS_MINIMIZE) || + (WindowStyle & WS_HSCROLL) || + (WindowStyle & WS_VSCROLL)) { + + return E_INVALIDARG; + } + + CheckConnected(m_pPin,VFW_E_NOT_CONNECTED); + return DoSetWindowStyle(WindowStyle,GWL_STYLE); +} + + +// Get the current GWL_STYLE base window style + +STDMETHODIMP CBaseControlWindow::get_WindowStyle(long *pWindowStyle) +{ + CheckPointer(pWindowStyle,E_POINTER); + CheckConnected(m_pPin,VFW_E_NOT_CONNECTED); + return DoGetWindowStyle(pWindowStyle,GWL_STYLE); +} + + +// Change the base window style or the extended styles depending on whether +// WindowLong is GWL_STYLE or GWL_EXSTYLE. We must call SetWindowPos to have +// the window displayed in it's new style after the change which is a little +// tricky if the window is not currently visible as we realise it offscreen. +// In most cases the client will call get_WindowStyle before they call this +// and then AND and OR in extra bit settings according to the requirements + +HRESULT CBaseControlWindow::DoSetWindowStyle(long Style,long WindowLong) +{ + RECT WindowRect; + + // Get the window's visibility before setting the style + BOOL bVisible = IsWindowVisible(m_hwnd); + EXECUTE_ASSERT(GetWindowRect(m_hwnd,&WindowRect)); + + // Set the new style flags for the window + SetWindowLong(m_hwnd,WindowLong,Style); + UINT WindowFlags = SWP_SHOWWINDOW | SWP_FRAMECHANGED | SWP_NOACTIVATE; + WindowFlags |= SWP_NOZORDER | SWP_NOSIZE | SWP_NOMOVE; + + // Show the window again in the current position + + if (bVisible == TRUE) { + + SetWindowPos(m_hwnd, // Base window handle + HWND_TOP, // Just a place holder + 0,0,0,0, // Leave size and position + WindowFlags); // Just draw it again + + return NOERROR; + } + + // Move the window offscreen so the user doesn't see the changes + + MoveWindow((HWND) m_hwnd, // Base window handle + GetSystemMetrics(SM_CXSCREEN), // Current desktop width + GetSystemMetrics(SM_CYSCREEN), // Likewise it's height + WIDTH(&WindowRect), // Use the same width + HEIGHT(&WindowRect), // Keep height same to + TRUE); // May as well repaint + + // Now show the previously hidden window + + SetWindowPos(m_hwnd, // Base window handle + HWND_TOP, // Just a place holder + 0,0,0,0, // Leave size and position + WindowFlags); // Just draw it again + + ShowWindow(m_hwnd,SW_HIDE); + + if (GetParent(m_hwnd)) { + + MapWindowPoints(HWND_DESKTOP, GetParent(m_hwnd), (LPPOINT)&WindowRect, 2); + } + + MoveWindow((HWND) m_hwnd, // Base window handle + WindowRect.left, // Existing x coordinate + WindowRect.top, // Existing y coordinate + WIDTH(&WindowRect), // Use the same width + HEIGHT(&WindowRect), // Keep height same to + TRUE); // May as well repaint + + return NOERROR; +} + + +// Get the current base window style (either GWL_STYLE or GWL_EXSTYLE) + +HRESULT CBaseControlWindow::DoGetWindowStyle(long *pStyle,long WindowLong) +{ + *pStyle = GetWindowLong(m_hwnd,WindowLong); + return NOERROR; +} + + +// Change the visibility of the base window, this takes the same parameters +// as the ShowWindow Win32 API does, so the client can have the window hidden +// or shown, minimised to an icon, or maximised to play in full screen mode +// We pass the request on to the base window to actually make the change + +STDMETHODIMP CBaseControlWindow::put_WindowState(long WindowState) +{ + CheckConnected(m_pPin,VFW_E_NOT_CONNECTED); + DoShowWindow(WindowState); + return NOERROR; +} + + +// Get the current window state, this function returns a subset of the SW bit +// settings available in ShowWindow, if the window is visible then SW_SHOW is +// set, if it is hidden then the SW_HIDDEN is set, if it is either minimised +// or maximised then the SW_MINIMIZE or SW_MAXIMIZE is set respectively. The +// other SW bit settings are really set commands not readable output values + +STDMETHODIMP CBaseControlWindow::get_WindowState(long *pWindowState) +{ + CheckPointer(pWindowState,E_POINTER); + CheckConnected(m_pPin,VFW_E_NOT_CONNECTED); + ASSERT(pWindowState); + *pWindowState = FALSE; + + // Is the window visible, a window is termed visible if it is somewhere on + // the current desktop even if it is completely obscured by other windows + // so the flag is a style for each window set with the WS_VISIBLE bit + + if (IsWindowVisible(m_hwnd) == TRUE) { + + // Is the base window iconic + if (IsIconic(m_hwnd) == TRUE) { + *pWindowState |= SW_MINIMIZE; + } + + // Has the window been maximised + else if (IsZoomed(m_hwnd) == TRUE) { + *pWindowState |= SW_MAXIMIZE; + } + + // Window is normal + else { + *pWindowState |= SW_SHOW; + } + + } else { + *pWindowState |= SW_HIDE; + } + return NOERROR; +} + + +// This makes sure that any palette we realise in the base window (through a +// media type or through the overlay interface) is done in the background and +// is therefore mapped to existing device entries rather than taking it over +// as it will do when we this window gets the keyboard focus. An application +// uses this to make sure it doesn't have it's palette removed by the window + +STDMETHODIMP CBaseControlWindow::put_BackgroundPalette(long BackgroundPalette) +{ + CheckConnected(m_pPin,VFW_E_NOT_CONNECTED); + CAutoLock cWindowLock(&m_WindowLock); + + // Check this is a valid automation boolean type + + if (BackgroundPalette != OATRUE) { + if (BackgroundPalette != OAFALSE) { + return E_INVALIDARG; + } + } + + // Make sure the window realises any palette it has again + + m_bBackground = (BackgroundPalette == OATRUE ? TRUE : FALSE); + PostMessage(m_hwnd,m_RealizePalette,0,0); + PaintWindow(FALSE); + + return NOERROR; +} + + +// This returns the current background realisation setting + +STDMETHODIMP +CBaseControlWindow::get_BackgroundPalette(long *pBackgroundPalette) +{ + CheckPointer(pBackgroundPalette,E_POINTER); + CheckConnected(m_pPin,VFW_E_NOT_CONNECTED); + CAutoLock cWindowLock(&m_WindowLock); + + // Get the current background palette setting + + *pBackgroundPalette = (m_bBackground == TRUE ? OATRUE : OAFALSE); + return NOERROR; +} + + +// Change the visibility of the base window + +STDMETHODIMP CBaseControlWindow::put_Visible(long Visible) +{ + CheckConnected(m_pPin,VFW_E_NOT_CONNECTED); + + // Check this is a valid automation boolean type + + if (Visible != OATRUE) { + if (Visible != OAFALSE) { + return E_INVALIDARG; + } + } + + // Convert the boolean visibility into SW_SHOW and SW_HIDE + + INT Mode = (Visible == OATRUE ? SW_SHOWNORMAL : SW_HIDE); + DoShowWindow(Mode); + return NOERROR; +} + + +// Return OATRUE if the window is currently visible otherwise OAFALSE + +STDMETHODIMP CBaseControlWindow::get_Visible(long *pVisible) +{ + CheckPointer(pVisible,E_POINTER); + CheckConnected(m_pPin,VFW_E_NOT_CONNECTED); + + // See if the base window has a WS_VISIBLE style - this will return TRUE + // even if the window is completely obscured by other desktop windows, we + // return FALSE if the window is not showing because of earlier calls + + BOOL Mode = IsWindowVisible(m_hwnd); + *pVisible = (Mode == TRUE ? OATRUE : OAFALSE); + return NOERROR; +} + + +// Change the left position of the base window. This keeps the window width +// and height properties the same so it effectively shunts the window left or +// right accordingly - there is the Width property to change that dimension + +STDMETHODIMP CBaseControlWindow::put_Left(long Left) +{ + CheckConnected(m_pPin,VFW_E_NOT_CONNECTED); + BOOL bSuccess; + RECT WindowRect; + + // Get the current window position in a RECT + EXECUTE_ASSERT(GetWindowRect(m_hwnd,&WindowRect)); + + if (GetParent(m_hwnd)) { + + MapWindowPoints(HWND_DESKTOP, GetParent(m_hwnd), (LPPOINT)&WindowRect, 2); + } + + // Adjust the coordinates ready for SetWindowPos, the window rectangle we + // get back from GetWindowRect is in left,top,right and bottom while the + // coordinates SetWindowPos wants are left,top,width and height values + + WindowRect.bottom = WindowRect.bottom - WindowRect.top; + WindowRect.right = WindowRect.right - WindowRect.left; + UINT WindowFlags = SWP_NOZORDER | SWP_FRAMECHANGED | SWP_NOACTIVATE; + + bSuccess = SetWindowPos(m_hwnd, // Window handle + HWND_TOP, // Put it at the top + Left, // New left position + WindowRect.top, // Leave top alone + WindowRect.right, // The WIDTH (not right) + WindowRect.bottom, // The HEIGHT (not bottom) + WindowFlags); // Show window options + + if (bSuccess == FALSE) { + return E_INVALIDARG; + } + return NOERROR; +} + + +// Return the current base window left position + +STDMETHODIMP CBaseControlWindow::get_Left(long *pLeft) +{ + CheckPointer(pLeft,E_POINTER); + CheckConnected(m_pPin,VFW_E_NOT_CONNECTED); + RECT WindowRect; + + EXECUTE_ASSERT(GetWindowRect(m_hwnd,&WindowRect)); + *pLeft = WindowRect.left; + return NOERROR; +} + + +// Change the current width of the base window. This property complements the +// left position property so we must keep the left edge constant and expand or +// contract to the right, the alternative would be to change the left edge so +// keeping the right edge constant but this is maybe a little more intuitive + +STDMETHODIMP CBaseControlWindow::put_Width(long Width) +{ + CheckConnected(m_pPin,VFW_E_NOT_CONNECTED); + BOOL bSuccess; + RECT WindowRect; + + // Adjust the coordinates ready for SetWindowPos, the window rectangle we + // get back from GetWindowRect is in left,top,right and bottom while the + // coordinates SetWindowPos wants are left,top,width and height values + + EXECUTE_ASSERT(GetWindowRect(m_hwnd,&WindowRect)); + + if (GetParent(m_hwnd)) { + + MapWindowPoints(HWND_DESKTOP, GetParent(m_hwnd), (LPPOINT)&WindowRect, 2); + } + + WindowRect.bottom = WindowRect.bottom - WindowRect.top; + UINT WindowFlags = SWP_NOZORDER | SWP_FRAMECHANGED | SWP_NOACTIVATE; + + // This seems to have a bug in that calling SetWindowPos on a window with + // just the width changing causes it to ignore the width that you pass in + // and sets it to a mimimum value of 110 pixels wide (Windows NT 3.51) + + bSuccess = SetWindowPos(m_hwnd, // Window handle + HWND_TOP, // Put it at the top + WindowRect.left, // Leave left alone + WindowRect.top, // Leave top alone + Width, // New WIDTH dimension + WindowRect.bottom, // The HEIGHT (not bottom) + WindowFlags); // Show window options + + if (bSuccess == FALSE) { + return E_INVALIDARG; + } + return NOERROR; +} + + +// Return the current base window width + +STDMETHODIMP CBaseControlWindow::get_Width(long *pWidth) +{ + CheckPointer(pWidth,E_POINTER); + CheckConnected(m_pPin,VFW_E_NOT_CONNECTED); + RECT WindowRect; + + EXECUTE_ASSERT(GetWindowRect(m_hwnd,&WindowRect)); + *pWidth = WindowRect.right - WindowRect.left; + return NOERROR; +} + + +// This allows the client program to change the top position for the window in +// the same way that changing the left position does not affect the width of +// the image so changing the top position does not affect the window height + +STDMETHODIMP CBaseControlWindow::put_Top(long Top) +{ + CheckConnected(m_pPin,VFW_E_NOT_CONNECTED); + BOOL bSuccess; + RECT WindowRect; + + // Get the current window position in a RECT + EXECUTE_ASSERT(GetWindowRect(m_hwnd,&WindowRect)); + + if (GetParent(m_hwnd)) { + + MapWindowPoints(HWND_DESKTOP, GetParent(m_hwnd), (LPPOINT)&WindowRect, 2); + } + + // Adjust the coordinates ready for SetWindowPos, the window rectangle we + // get back from GetWindowRect is in left,top,right and bottom while the + // coordinates SetWindowPos wants are left,top,width and height values + + WindowRect.bottom = WindowRect.bottom - WindowRect.top; + WindowRect.right = WindowRect.right - WindowRect.left; + UINT WindowFlags = SWP_NOZORDER | SWP_FRAMECHANGED | SWP_NOACTIVATE; + + bSuccess = SetWindowPos(m_hwnd, // Window handle + HWND_TOP, // Put it at the top + WindowRect.left, // Leave left alone + Top, // New top position + WindowRect.right, // The WIDTH (not right) + WindowRect.bottom, // The HEIGHT (not bottom) + WindowFlags); // Show window flags + + if (bSuccess == FALSE) { + return E_INVALIDARG; + } + return NOERROR; +} + + +// Return the current base window top position + +STDMETHODIMP CBaseControlWindow::get_Top(long *pTop) +{ + CheckPointer(pTop,E_POINTER); + CheckConnected(m_pPin,VFW_E_NOT_CONNECTED); + RECT WindowRect; + + EXECUTE_ASSERT(GetWindowRect(m_hwnd,&WindowRect)); + *pTop = WindowRect.top; + return NOERROR; +} + + +// Change the height of the window, this complements the top property so when +// we change this we must keep the top position for the base window, as said +// before we could keep the bottom and grow upwards although this is perhaps +// a little more intuitive since we already have a top position property + +STDMETHODIMP CBaseControlWindow::put_Height(long Height) +{ + CheckConnected(m_pPin,VFW_E_NOT_CONNECTED); + BOOL bSuccess; + RECT WindowRect; + + // Adjust the coordinates ready for SetWindowPos, the window rectangle we + // get back from GetWindowRect is in left,top,right and bottom while the + // coordinates SetWindowPos wants are left,top,width and height values + + EXECUTE_ASSERT(GetWindowRect(m_hwnd,&WindowRect)); + + if (GetParent(m_hwnd)) { + + MapWindowPoints(HWND_DESKTOP, GetParent(m_hwnd), (LPPOINT)&WindowRect, 2); + } + + WindowRect.right = WindowRect.right - WindowRect.left; + UINT WindowFlags = SWP_NOZORDER | SWP_FRAMECHANGED | SWP_NOACTIVATE; + + bSuccess = SetWindowPos(m_hwnd, // Window handle + HWND_TOP, // Put it at the top + WindowRect.left, // Leave left alone + WindowRect.top, // Leave top alone + WindowRect.right, // The WIDTH (not right) + Height, // New height dimension + WindowFlags); // Show window flags + + if (bSuccess == FALSE) { + return E_INVALIDARG; + } + return NOERROR; +} + + +// Return the current base window height + +STDMETHODIMP CBaseControlWindow::get_Height(long *pHeight) +{ + CheckPointer(pHeight,E_POINTER); + CheckConnected(m_pPin,VFW_E_NOT_CONNECTED); + RECT WindowRect; + + EXECUTE_ASSERT(GetWindowRect(m_hwnd,&WindowRect)); + *pHeight = WindowRect.bottom - WindowRect.top; + return NOERROR; +} + + +// This can be called to change the owning window. Setting the owner is done +// through this function, however to make the window a true child window the +// style must also be set to WS_CHILD. After resetting the owner to NULL an +// application should also set the style to WS_OVERLAPPED | WS_CLIPCHILDREN. + +// We cannot lock the object here because the SetParent causes an interthread +// SendMessage to the owner window. If they are in GetState we will sit here +// incomplete with the critical section locked therefore blocking out source +// filter threads from accessing us. Because the source thread can't enter us +// it can't get buffers or call EndOfStream so the GetState will not complete + +STDMETHODIMP CBaseControlWindow::put_Owner(OAHWND Owner) +{ + // Check we are connected otherwise reject the call + + CheckConnected(m_pPin,VFW_E_NOT_CONNECTED); + m_hwndOwner = (HWND) Owner; + HWND hwndParent = m_hwndOwner; + + // Add or remove WS_CHILD as appropriate + + LONG Style = GetWindowLong(m_hwnd,GWL_STYLE); + if (Owner == NULL) { + Style &= (~WS_CHILD); + } else { + Style |= (WS_CHILD); + } + SetWindowLong(m_hwnd,GWL_STYLE,Style); + + // Don't call this with the filter locked + + SetParent(m_hwnd,hwndParent); + + PaintWindow(TRUE); + NOTE1("Changed parent %lx",hwndParent); + + return NOERROR; +} + + +// This complements the put_Owner to get the current owning window property +// we always return NOERROR although the returned window handle may be NULL +// to indicate no owning window (the desktop window doesn't qualify as one) +// If an application sets the owner we call SetParent, however that returns +// NULL until the WS_CHILD bit is set on, so we store the owner internally + +STDMETHODIMP CBaseControlWindow::get_Owner(OAHWND *Owner) +{ + CheckPointer(Owner,E_POINTER); + CheckConnected(m_pPin,VFW_E_NOT_CONNECTED); + *Owner = (OAHWND) m_hwndOwner; + return NOERROR; +} + + +// And renderer supporting IVideoWindow may have an HWND set who will get any +// keyboard and mouse messages we receive posted on to them. This is separate +// from setting an owning window. By separating the two, applications may get +// messages sent on even when they have set no owner (perhaps it's maximised) + +STDMETHODIMP CBaseControlWindow::put_MessageDrain(OAHWND Drain) +{ + // Check we are connected otherwise reject the call + + CheckConnected(m_pPin,VFW_E_NOT_CONNECTED); + m_hwndDrain = (HWND) Drain; + return NOERROR; +} + + +// Return the current message drain + +STDMETHODIMP CBaseControlWindow::get_MessageDrain(OAHWND *Drain) +{ + CheckPointer(Drain,E_POINTER); + CheckConnected(m_pPin,VFW_E_NOT_CONNECTED); + *Drain = (OAHWND) m_hwndDrain; + return NOERROR; +} + + +// This is called by the filter graph to inform us of a message we should know +// is being sent to our owning window. We have this because as a child window +// we do not get certain messages that are only sent to top level windows. We +// must see the palette changed/changing/query messages so that we know if we +// have the foreground palette or not. We pass the message on to our window +// using SendMessage - this will cause an interthread send message to occur + +STDMETHODIMP +CBaseControlWindow::NotifyOwnerMessage(OAHWND hwnd, // Window handle + long uMsg, // Message ID + LONG_PTR wParam, // Parameters + LONG_PTR lParam) // for message +{ + CheckConnected(m_pPin,VFW_E_NOT_CONNECTED); + + // Only interested in these Windows messages + + switch (uMsg) { + + case WM_SYSCOLORCHANGE: + case WM_PALETTECHANGED: + case WM_PALETTEISCHANGING: + case WM_QUERYNEWPALETTE: + case WM_DEVMODECHANGE: + case WM_DISPLAYCHANGE: + case WM_ACTIVATEAPP: + + // If we do not have an owner then ignore + + if (m_hwndOwner == NULL) { + return NOERROR; + } + SendMessage(m_hwnd,uMsg,(WPARAM)wParam,(LPARAM)lParam); + break; + + // do NOT fwd WM_MOVE. the parameters are the location of the parent + // window, NOT what the renderer should be looking at. But we need + // to make sure the overlay is moved with the parent window, so we + // do this. + case WM_MOVE: + PostMessage(m_hwnd,WM_PAINT,0,0); + break; + } + return NOERROR; +} + + +// Allow an application to have us set the base window in the foreground. We +// have this because it is difficult for one thread to do do this to a window +// owned by another thread. We ask the base window class to do the real work + +STDMETHODIMP CBaseControlWindow::SetWindowForeground(long Focus) +{ + // Check this is a valid automation boolean type + + if (Focus != OATRUE) { + if (Focus != OAFALSE) { + return E_INVALIDARG; + } + } + + // We shouldn't lock as this sends a message + + CheckConnected(m_pPin,VFW_E_NOT_CONNECTED); + BOOL bFocus = (Focus == OATRUE ? TRUE : FALSE); + DoSetWindowForeground(bFocus); + + return NOERROR; +} + + +// This allows a client to set the complete window size and position in one +// atomic operation. The same affect can be had by changing each dimension +// in turn through their individual properties although some flashing will +// occur as each of them gets updated (they are better set at design time) + +STDMETHODIMP +CBaseControlWindow::SetWindowPosition(long Left,long Top,long Width,long Height) +{ + CheckConnected(m_pPin,VFW_E_NOT_CONNECTED); + BOOL bSuccess; + + // Set the new size and position + UINT WindowFlags = SWP_NOZORDER | SWP_FRAMECHANGED | SWP_NOACTIVATE; + + ASSERT(IsWindow(m_hwnd)); + bSuccess = SetWindowPos(m_hwnd, // Window handle + HWND_TOP, // Put it at the top + Left, // Left position + Top, // Top position + Width, // Window width + Height, // Window height + WindowFlags); // Show window flags + ASSERT(bSuccess); +#ifdef DEBUG + DbgLog((LOG_TRACE, 1, TEXT("SWP failed error %d"), GetLastError())); +#endif + if (bSuccess == FALSE) { + return E_INVALIDARG; + } + return NOERROR; +} + + +// This complements the SetWindowPosition to return the current window place +// in device coordinates. As before the same information can be retrived by +// calling the property get functions individually but this is atomic and is +// therefore more suitable to a live environment rather than design time + +STDMETHODIMP +CBaseControlWindow::GetWindowPosition(long *pLeft,long *pTop,long *pWidth,long *pHeight) +{ + // Should check the pointers are not NULL + + CheckPointer(pLeft,E_POINTER); + CheckPointer(pTop,E_POINTER); + CheckPointer(pWidth,E_POINTER); + CheckPointer(pHeight,E_POINTER); + CheckConnected(m_pPin,VFW_E_NOT_CONNECTED); + RECT WindowRect; + + // Get the current window coordinates + + EXECUTE_ASSERT(GetWindowRect(m_hwnd,&WindowRect)); + + // Convert the RECT into left,top,width and height values + + *pLeft = WindowRect.left; + *pTop = WindowRect.top; + *pWidth = WindowRect.right - WindowRect.left; + *pHeight = WindowRect.bottom - WindowRect.top; + + return NOERROR; +} + + +// When a window is maximised or iconic calling GetWindowPosition will return +// the current window position (likewise for the properties). However if the +// restored size (ie the size we'll return to when normally shown) is needed +// then this should be used. When in a normal position (neither iconic nor +// maximised) then this returns the same coordinates as GetWindowPosition + +STDMETHODIMP +CBaseControlWindow::GetRestorePosition(long *pLeft,long *pTop,long *pWidth,long *pHeight) +{ + // Should check the pointers are not NULL + + CheckPointer(pLeft,E_POINTER); + CheckPointer(pTop,E_POINTER); + CheckPointer(pWidth,E_POINTER); + CheckPointer(pHeight,E_POINTER); + CheckConnected(m_pPin,VFW_E_NOT_CONNECTED); + + // Use GetWindowPlacement to find the restore position + + WINDOWPLACEMENT Place; + Place.length = sizeof(WINDOWPLACEMENT); + EXECUTE_ASSERT(GetWindowPlacement(m_hwnd,&Place)); + + RECT WorkArea; + + // We must take into account any task bar present + + if (SystemParametersInfo(SPI_GETWORKAREA,0,&WorkArea,FALSE) == TRUE) { + if (GetParent(m_hwnd) == NULL) { + Place.rcNormalPosition.top += WorkArea.top; + Place.rcNormalPosition.bottom += WorkArea.top; + Place.rcNormalPosition.left += WorkArea.left; + Place.rcNormalPosition.right += WorkArea.left; + } + } + + // Convert the RECT into left,top,width and height values + + *pLeft = Place.rcNormalPosition.left; + *pTop = Place.rcNormalPosition.top; + *pWidth = Place.rcNormalPosition.right - Place.rcNormalPosition.left; + *pHeight = Place.rcNormalPosition.bottom - Place.rcNormalPosition.top; + + return NOERROR; +} + + +// Return the current border colour, if we are playing something to a subset +// of the base window display there is an outside area exposed. The default +// action is to paint this colour in the Windows background colour (defined +// as value COLOR_WINDOW) We reset to this default when we're disconnected + +STDMETHODIMP CBaseControlWindow::get_BorderColor(long *Color) +{ + CheckPointer(Color,E_POINTER); + CheckConnected(m_pPin,VFW_E_NOT_CONNECTED); + *Color = (long) m_BorderColour; + return NOERROR; +} + + +// This can be called to set the current border colour + +STDMETHODIMP CBaseControlWindow::put_BorderColor(long Color) +{ + CheckConnected(m_pPin,VFW_E_NOT_CONNECTED); + + // Have the window repainted with the new border colour + + m_BorderColour = (COLORREF) Color; + PaintWindow(TRUE); + return NOERROR; +} + + +// Delegate fullscreen handling to plug in distributor + +STDMETHODIMP CBaseControlWindow::get_FullScreenMode(long *FullScreenMode) +{ + CheckConnected(m_pPin,VFW_E_NOT_CONNECTED); + CheckPointer(FullScreenMode,E_POINTER); + return E_NOTIMPL; +} + + +// Delegate fullscreen handling to plug in distributor + +STDMETHODIMP CBaseControlWindow::put_FullScreenMode(long FullScreenMode) +{ + return E_NOTIMPL; +} + + +// This sets the auto show property, this property causes the base window to +// be displayed whenever we change state. This allows an application to have +// to do nothing to have the window appear but still allow them to change the +// default behaviour if for example they want to keep it hidden for longer + +STDMETHODIMP CBaseControlWindow::put_AutoShow(long AutoShow) +{ + CheckConnected(m_pPin,VFW_E_NOT_CONNECTED); + + // Check this is a valid automation boolean type + + if (AutoShow != OATRUE) { + if (AutoShow != OAFALSE) { + return E_INVALIDARG; + } + } + + m_bAutoShow = (AutoShow == OATRUE ? TRUE : FALSE); + return NOERROR; +} + + +// This can be called to get the current auto show flag. The flag is updated +// when we connect and disconnect and through this interface all of which are +// controlled and serialised by means of the main renderer critical section + +STDMETHODIMP CBaseControlWindow::get_AutoShow(long *AutoShow) +{ + CheckPointer(AutoShow,E_POINTER); + CheckConnected(m_pPin,VFW_E_NOT_CONNECTED); + *AutoShow = (m_bAutoShow == TRUE ? OATRUE : OAFALSE); + return NOERROR; +} + + +// Return the minimum ideal image size for the current video. This may differ +// to the actual video dimensions because we may be using DirectDraw hardware +// that has specific stretching requirements. For example the Cirrus Logic +// cards have a minimum stretch factor depending on the overlay surface size + +STDMETHODIMP +CBaseControlWindow::GetMinIdealImageSize(long *pWidth,long *pHeight) +{ + CheckPointer(pWidth,E_POINTER); + CheckPointer(pHeight,E_POINTER); + CheckConnected(m_pPin,VFW_E_NOT_CONNECTED); + FILTER_STATE State; + + // Must not be stopped for this to work correctly + + m_pFilter->GetState(0,&State); + if (State == State_Stopped) { + return VFW_E_WRONG_STATE; + } + + RECT DefaultRect = GetDefaultRect(); + *pWidth = WIDTH(&DefaultRect); + *pHeight = HEIGHT(&DefaultRect); + return NOERROR; +} + + +// Return the maximum ideal image size for the current video. This may differ +// to the actual video dimensions because we may be using DirectDraw hardware +// that has specific stretching requirements. For example the Cirrus Logic +// cards have a maximum stretch factor depending on the overlay surface size + +STDMETHODIMP +CBaseControlWindow::GetMaxIdealImageSize(long *pWidth,long *pHeight) +{ + CheckPointer(pWidth,E_POINTER); + CheckPointer(pHeight,E_POINTER); + CheckConnected(m_pPin,VFW_E_NOT_CONNECTED); + FILTER_STATE State; + + // Must not be stopped for this to work correctly + + m_pFilter->GetState(0,&State); + if (State == State_Stopped) { + return VFW_E_WRONG_STATE; + } + + RECT DefaultRect = GetDefaultRect(); + *pWidth = WIDTH(&DefaultRect); + *pHeight = HEIGHT(&DefaultRect); + return NOERROR; +} + + +// Allow an application to hide the cursor on our window + +STDMETHODIMP +CBaseControlWindow::HideCursor(long HideCursor) +{ + CheckConnected(m_pPin,VFW_E_NOT_CONNECTED); + + // Check this is a valid automation boolean type + + if (HideCursor != OATRUE) { + if (HideCursor != OAFALSE) { + return E_INVALIDARG; + } + } + + m_bCursorHidden = (HideCursor == OATRUE ? TRUE : FALSE); + return NOERROR; +} + + +// Returns whether we have the cursor hidden or not + +STDMETHODIMP CBaseControlWindow::IsCursorHidden(long *CursorHidden) +{ + CheckPointer(CursorHidden,E_POINTER); + CheckConnected(m_pPin,VFW_E_NOT_CONNECTED); + *CursorHidden = (m_bCursorHidden == TRUE ? OATRUE : OAFALSE); + return NOERROR; +} + + +// This class implements the IBasicVideo control functions (dual interface) +// we support a large number of properties and methods designed to allow the +// client (whether it be an automation controller or a C/C++ application) to +// set and get a number of video related properties such as the native video +// size. We support some methods that duplicate the properties but provide a +// more direct and efficient mechanism as many values may be changed in one + +CBaseControlVideo::CBaseControlVideo( + CBaseFilter *pFilter, // Owning filter + CCritSec *pInterfaceLock, // Locking object + TCHAR *pName, // Object description + LPUNKNOWN pUnk, // Normal COM ownership + HRESULT *phr) : // OLE return code + + CBaseBasicVideo(pName,pUnk), + m_pFilter(pFilter), + m_pInterfaceLock(pInterfaceLock), + m_pPin(NULL) +{ + ASSERT(m_pFilter); + ASSERT(m_pInterfaceLock); + ASSERT(phr); +} + +// Return an approximate average time per frame + +STDMETHODIMP CBaseControlVideo::get_AvgTimePerFrame(REFTIME *pAvgTimePerFrame) +{ + CheckPointer(pAvgTimePerFrame,E_POINTER); + CheckConnected(m_pPin,VFW_E_NOT_CONNECTED); + CAutoLock cInterfaceLock(m_pInterfaceLock); + + VIDEOINFOHEADER *pVideoInfo = GetVideoFormat(); + if (pVideoInfo == NULL) + return E_OUTOFMEMORY; + COARefTime AvgTime(pVideoInfo->AvgTimePerFrame); + *pAvgTimePerFrame = (REFTIME) AvgTime; + + return NOERROR; +} + + +// Return an approximate bit rate for the video + +STDMETHODIMP CBaseControlVideo::get_BitRate(long *pBitRate) +{ + CheckPointer(pBitRate,E_POINTER); + CheckConnected(m_pPin,VFW_E_NOT_CONNECTED); + CAutoLock cInterfaceLock(m_pInterfaceLock); + + VIDEOINFOHEADER *pVideoInfo = GetVideoFormat(); + if (pVideoInfo == NULL) + return E_OUTOFMEMORY; + *pBitRate = pVideoInfo->dwBitRate; + return NOERROR; +} + + +// Return an approximate bit error rate + +STDMETHODIMP CBaseControlVideo::get_BitErrorRate(long *pBitErrorRate) +{ + CheckPointer(pBitErrorRate,E_POINTER); + CheckConnected(m_pPin,VFW_E_NOT_CONNECTED); + CAutoLock cInterfaceLock(m_pInterfaceLock); + + VIDEOINFOHEADER *pVideoInfo = GetVideoFormat(); + if (pVideoInfo == NULL) + return E_OUTOFMEMORY; + *pBitErrorRate = pVideoInfo->dwBitErrorRate; + return NOERROR; +} + + +// This returns the current video width + +STDMETHODIMP CBaseControlVideo::get_VideoWidth(long *pVideoWidth) +{ + CheckPointer(pVideoWidth,E_POINTER); + CheckConnected(m_pPin,VFW_E_NOT_CONNECTED); + CAutoLock cInterfaceLock(m_pInterfaceLock); + + VIDEOINFOHEADER *pVideoInfo = GetVideoFormat(); + if (pVideoInfo == NULL) + return E_OUTOFMEMORY; + *pVideoWidth = pVideoInfo->bmiHeader.biWidth; + return NOERROR; +} + + +// This returns the current video height + +STDMETHODIMP CBaseControlVideo::get_VideoHeight(long *pVideoHeight) +{ + CheckPointer(pVideoHeight,E_POINTER); + CheckConnected(m_pPin,VFW_E_NOT_CONNECTED); + CAutoLock cInterfaceLock(m_pInterfaceLock); + + VIDEOINFOHEADER *pVideoInfo = GetVideoFormat(); + if (pVideoInfo == NULL) + return E_OUTOFMEMORY; + *pVideoHeight = pVideoInfo->bmiHeader.biHeight; + return NOERROR; +} + + +// This returns the current palette the video is using as an array allocated +// by the user. To remain consistent we use PALETTEENTRY fields to return the +// colours in rather than RGBQUADs that multimedia decided to use. The memory +// is allocated by the user so we simple copy each in turn. We check that the +// number of entries requested and the start position offset are both valid +// If the number of entries evaluates to zero then we return an S_FALSE code + +STDMETHODIMP CBaseControlVideo::GetVideoPaletteEntries(long StartIndex, + long Entries, + long *pRetrieved, + long *pPalette) +{ + CheckPointer(pRetrieved,E_POINTER); + CheckConnected(m_pPin,VFW_E_NOT_CONNECTED); + CAutoLock cInterfaceLock(m_pInterfaceLock); + CMediaType MediaType; + + // Get the video format from the derived class + + VIDEOINFOHEADER *pVideoInfo = GetVideoFormat(); + if (pVideoInfo == NULL) + return E_OUTOFMEMORY; + BITMAPINFOHEADER *pHeader = HEADER(pVideoInfo); + + // Is the current format palettised + + if (PALETTISED(pVideoInfo) == FALSE) { + *pRetrieved = 0; + return VFW_E_NO_PALETTE_AVAILABLE; + } + + // Do they just want to know how many are available + + if (pPalette == NULL) { + *pRetrieved = pHeader->biClrUsed; + return NOERROR; + } + + // Make sure the start position is a valid offset + + if (StartIndex >= (LONG) pHeader->biClrUsed || StartIndex < 0) { + *pRetrieved = 0; + return E_INVALIDARG; + } + + // Correct the number we can retrieve + + LONG Available = (LONG) pHeader->biClrUsed - StartIndex; + *pRetrieved = max(0,min(Available,Entries)); + if (*pRetrieved == 0) { + return S_FALSE; + } + + // Copy the palette entries to the output buffer + + PALETTEENTRY *pEntries = (PALETTEENTRY *) pPalette; + RGBQUAD *pColours = COLORS(pVideoInfo) + StartIndex; + + for (LONG Count = 0;Count < *pRetrieved;Count++) { + pEntries[Count].peRed = pColours[Count].rgbRed; + pEntries[Count].peGreen = pColours[Count].rgbGreen; + pEntries[Count].peBlue = pColours[Count].rgbBlue; + pEntries[Count].peFlags = 0; + } + return NOERROR; +} + + +// This returns the current video dimensions as a method rather than a number +// of individual property get calls. For the same reasons as said before we +// cannot access the renderer media type directly as the window object thread +// may be updating it since dynamic format changes may change these values + +STDMETHODIMP CBaseControlVideo::GetVideoSize(long *pWidth,long *pHeight) +{ + CheckPointer(pWidth,E_POINTER); + CheckPointer(pHeight,E_POINTER); + CheckConnected(m_pPin,VFW_E_NOT_CONNECTED); + CAutoLock cInterfaceLock(m_pInterfaceLock); + + // Get the video format from the derived class + VIDEOINFOHEADER *pVideoInfo = GetVideoFormat(); + if (pVideoInfo == NULL) + return E_OUTOFMEMORY; + *pWidth = pVideoInfo->bmiHeader.biWidth; + *pHeight = pVideoInfo->bmiHeader.biHeight; + return NOERROR; +} + + +// Set the source video rectangle as left,top,right and bottom coordinates +// rather than left,top,width and height as per OLE automation interfaces +// Then pass the rectangle on to the window object to set the source + +STDMETHODIMP +CBaseControlVideo::SetSourcePosition(long Left,long Top,long Width,long Height) +{ + CheckConnected(m_pPin,VFW_E_NOT_CONNECTED); + CAutoLock cInterfaceLock(m_pInterfaceLock); + RECT SourceRect; + SourceRect.left = Left; + SourceRect.top = Top; + SourceRect.right = Left + Width; + SourceRect.bottom = Top + Height; + + // Check the source rectangle is valid + + HRESULT hr = CheckSourceRect(&SourceRect); + if (FAILED(hr)) { + return hr; + } + + // Now set the source rectangle + + hr = SetSourceRect(&SourceRect); + if (FAILED(hr)) { + return hr; + } + return OnUpdateRectangles(); +} + + +// Return the source rectangle in left,top,width and height rather than the +// left,top,right and bottom values that RECT uses (and which the window +// object returns through GetSourceRect) which requires a little work + +STDMETHODIMP +CBaseControlVideo::GetSourcePosition(long *pLeft,long *pTop,long *pWidth,long *pHeight) +{ + CheckPointer(pLeft,E_POINTER); + CheckPointer(pTop,E_POINTER); + CheckPointer(pWidth,E_POINTER); + CheckPointer(pHeight,E_POINTER); + CheckConnected(m_pPin,VFW_E_NOT_CONNECTED); + RECT SourceRect; + + CAutoLock cInterfaceLock(m_pInterfaceLock); + GetSourceRect(&SourceRect); + + *pLeft = SourceRect.left; + *pTop = SourceRect.top; + *pWidth = WIDTH(&SourceRect); + *pHeight = HEIGHT(&SourceRect); + + return NOERROR; +} + + +// Set the video destination as left,top,right and bottom coordinates rather +// than the left,top,width and height uses as per OLE automation interfaces +// Then pass the rectangle on to the window object to set the destination + +STDMETHODIMP +CBaseControlVideo::SetDestinationPosition(long Left,long Top,long Width,long Height) +{ + CheckConnected(m_pPin,VFW_E_NOT_CONNECTED); + CAutoLock cInterfaceLock(m_pInterfaceLock); + RECT DestinationRect; + + DestinationRect.left = Left; + DestinationRect.top = Top; + DestinationRect.right = Left + Width; + DestinationRect.bottom = Top + Height; + + // Check the target rectangle is valid + + HRESULT hr = CheckTargetRect(&DestinationRect); + if (FAILED(hr)) { + return hr; + } + + // Now set the new target rectangle + + hr = SetTargetRect(&DestinationRect); + if (FAILED(hr)) { + return hr; + } + return OnUpdateRectangles(); +} + + +// Return the destination rectangle in left,top,width and height rather than +// the left,top,right and bottom values that RECT uses (and which the window +// object returns through GetDestinationRect) which requires a little work + +STDMETHODIMP +CBaseControlVideo::GetDestinationPosition(long *pLeft,long *pTop,long *pWidth,long *pHeight) +{ + // Should check the pointers are not NULL + + CheckPointer(pLeft,E_POINTER); + CheckPointer(pTop,E_POINTER); + CheckPointer(pWidth,E_POINTER); + CheckPointer(pHeight,E_POINTER); + CheckConnected(m_pPin,VFW_E_NOT_CONNECTED); + RECT DestinationRect; + + CAutoLock cInterfaceLock(m_pInterfaceLock); + GetTargetRect(&DestinationRect); + + *pLeft = DestinationRect.left; + *pTop = DestinationRect.top; + *pWidth = WIDTH(&DestinationRect); + *pHeight = HEIGHT(&DestinationRect); + + return NOERROR; +} + + +// Set the source left position, the source rectangle we get back from the +// window object is a true rectangle in left,top,right and bottom positions +// so all we have to do is to update the left position and pass it back. We +// must keep the current width constant when we're updating this property + +STDMETHODIMP CBaseControlVideo::put_SourceLeft(long SourceLeft) +{ + CheckConnected(m_pPin,VFW_E_NOT_CONNECTED); + CAutoLock cInterfaceLock(m_pInterfaceLock); + RECT SourceRect; + GetSourceRect(&SourceRect); + SourceRect.right = SourceLeft + WIDTH(&SourceRect); + SourceRect.left = SourceLeft; + + // Check the source rectangle is valid + + HRESULT hr = CheckSourceRect(&SourceRect); + if (FAILED(hr)) { + return hr; + } + + // Now set the source rectangle + + hr = SetSourceRect(&SourceRect); + if (FAILED(hr)) { + return hr; + } + return OnUpdateRectangles(); +} + + +// Return the current left source video position + +STDMETHODIMP CBaseControlVideo::get_SourceLeft(long *pSourceLeft) +{ + CheckPointer(pSourceLeft,E_POINTER); + CheckConnected(m_pPin,VFW_E_NOT_CONNECTED); + CAutoLock cInterfaceLock(m_pInterfaceLock); + RECT SourceRect; + + GetSourceRect(&SourceRect); + *pSourceLeft = SourceRect.left; + return NOERROR; +} + + +// Set the source width, we get the current source rectangle and then update +// the right position to be the left position (thereby keeping it constant) +// plus the new source width we are passed in (it expands to the right) + +STDMETHODIMP CBaseControlVideo::put_SourceWidth(long SourceWidth) +{ + CheckConnected(m_pPin,VFW_E_NOT_CONNECTED); + CAutoLock cInterfaceLock(m_pInterfaceLock); + RECT SourceRect; + GetSourceRect(&SourceRect); + SourceRect.right = SourceRect.left + SourceWidth; + + // Check the source rectangle is valid + + HRESULT hr = CheckSourceRect(&SourceRect); + if (FAILED(hr)) { + return hr; + } + + // Now set the source rectangle + + hr = SetSourceRect(&SourceRect); + if (FAILED(hr)) { + return hr; + } + return OnUpdateRectangles(); +} + + +// Return the current source width + +STDMETHODIMP CBaseControlVideo::get_SourceWidth(long *pSourceWidth) +{ + CheckPointer(pSourceWidth,E_POINTER); + CheckConnected(m_pPin,VFW_E_NOT_CONNECTED); + CAutoLock cInterfaceLock(m_pInterfaceLock); + RECT SourceRect; + + GetSourceRect(&SourceRect); + *pSourceWidth = WIDTH(&SourceRect); + return NOERROR; +} + + +// Set the source top position - changing this property does not affect the +// current source height. So changing this shunts the source rectangle up and +// down appropriately. Changing the height complements this functionality by +// keeping the top position constant and simply changing the source height + +STDMETHODIMP CBaseControlVideo::put_SourceTop(long SourceTop) +{ + CheckConnected(m_pPin,VFW_E_NOT_CONNECTED); + CAutoLock cInterfaceLock(m_pInterfaceLock); + RECT SourceRect; + GetSourceRect(&SourceRect); + SourceRect.bottom = SourceTop + HEIGHT(&SourceRect); + SourceRect.top = SourceTop; + + // Check the source rectangle is valid + + HRESULT hr = CheckSourceRect(&SourceRect); + if (FAILED(hr)) { + return hr; + } + + // Now set the source rectangle + + hr = SetSourceRect(&SourceRect); + if (FAILED(hr)) { + return hr; + } + return OnUpdateRectangles(); +} + + +// Return the current top position + +STDMETHODIMP CBaseControlVideo::get_SourceTop(long *pSourceTop) +{ + CheckPointer(pSourceTop,E_POINTER); + CheckConnected(m_pPin,VFW_E_NOT_CONNECTED); + CAutoLock cInterfaceLock(m_pInterfaceLock); + RECT SourceRect; + + GetSourceRect(&SourceRect); + *pSourceTop = SourceRect.top; + return NOERROR; +} + + +// Set the source height + +STDMETHODIMP CBaseControlVideo::put_SourceHeight(long SourceHeight) +{ + CheckConnected(m_pPin,VFW_E_NOT_CONNECTED); + CAutoLock cInterfaceLock(m_pInterfaceLock); + RECT SourceRect; + GetSourceRect(&SourceRect); + SourceRect.bottom = SourceRect.top + SourceHeight; + + // Check the source rectangle is valid + + HRESULT hr = CheckSourceRect(&SourceRect); + if (FAILED(hr)) { + return hr; + } + + // Now set the source rectangle + + hr = SetSourceRect(&SourceRect); + if (FAILED(hr)) { + return hr; + } + return OnUpdateRectangles(); +} + + +// Return the current source height + +STDMETHODIMP CBaseControlVideo::get_SourceHeight(long *pSourceHeight) +{ + CheckPointer(pSourceHeight,E_POINTER); + CheckConnected(m_pPin,VFW_E_NOT_CONNECTED); + CAutoLock cInterfaceLock(m_pInterfaceLock); + RECT SourceRect; + + GetSourceRect(&SourceRect); + *pSourceHeight = HEIGHT(&SourceRect); + return NOERROR; +} + + +// Set the target left position, the target rectangle we get back from the +// window object is a true rectangle in left,top,right and bottom positions +// so all we have to do is to update the left position and pass it back. We +// must keep the current width constant when we're updating this property + +STDMETHODIMP CBaseControlVideo::put_DestinationLeft(long DestinationLeft) +{ + CheckConnected(m_pPin,VFW_E_NOT_CONNECTED); + CAutoLock cInterfaceLock(m_pInterfaceLock); + RECT DestinationRect; + GetTargetRect(&DestinationRect); + DestinationRect.right = DestinationLeft + WIDTH(&DestinationRect); + DestinationRect.left = DestinationLeft; + + // Check the target rectangle is valid + + HRESULT hr = CheckTargetRect(&DestinationRect); + if (FAILED(hr)) { + return hr; + } + + // Now set the new target rectangle + + hr = SetTargetRect(&DestinationRect); + if (FAILED(hr)) { + return hr; + } + return OnUpdateRectangles(); +} + + +// Return the left position for the destination rectangle + +STDMETHODIMP CBaseControlVideo::get_DestinationLeft(long *pDestinationLeft) +{ + CheckPointer(pDestinationLeft,E_POINTER); + CheckConnected(m_pPin,VFW_E_NOT_CONNECTED); + CAutoLock cInterfaceLock(m_pInterfaceLock); + RECT DestinationRect; + + GetTargetRect(&DestinationRect); + *pDestinationLeft = DestinationRect.left; + return NOERROR; +} + + +// Set the destination width + +STDMETHODIMP CBaseControlVideo::put_DestinationWidth(long DestinationWidth) +{ + CheckConnected(m_pPin,VFW_E_NOT_CONNECTED); + CAutoLock cInterfaceLock(m_pInterfaceLock); + RECT DestinationRect; + GetTargetRect(&DestinationRect); + DestinationRect.right = DestinationRect.left + DestinationWidth; + + // Check the target rectangle is valid + + HRESULT hr = CheckTargetRect(&DestinationRect); + if (FAILED(hr)) { + return hr; + } + + // Now set the new target rectangle + + hr = SetTargetRect(&DestinationRect); + if (FAILED(hr)) { + return hr; + } + return OnUpdateRectangles(); +} + + +// Return the width for the destination rectangle + +STDMETHODIMP CBaseControlVideo::get_DestinationWidth(long *pDestinationWidth) +{ + CheckPointer(pDestinationWidth,E_POINTER); + CheckConnected(m_pPin,VFW_E_NOT_CONNECTED); + CAutoLock cInterfaceLock(m_pInterfaceLock); + RECT DestinationRect; + + GetTargetRect(&DestinationRect); + *pDestinationWidth = WIDTH(&DestinationRect); + return NOERROR; +} + + +// Set the target top position - changing this property does not affect the +// current target height. So changing this shunts the target rectangle up and +// down appropriately. Changing the height complements this functionality by +// keeping the top position constant and simply changing the target height + +STDMETHODIMP CBaseControlVideo::put_DestinationTop(long DestinationTop) +{ + CheckConnected(m_pPin,VFW_E_NOT_CONNECTED); + CAutoLock cInterfaceLock(m_pInterfaceLock); + RECT DestinationRect; + GetTargetRect(&DestinationRect); + DestinationRect.bottom = DestinationTop + HEIGHT(&DestinationRect); + DestinationRect.top = DestinationTop; + + // Check the target rectangle is valid + + HRESULT hr = CheckTargetRect(&DestinationRect); + if (FAILED(hr)) { + return hr; + } + + // Now set the new target rectangle + + hr = SetTargetRect(&DestinationRect); + if (FAILED(hr)) { + return hr; + } + return OnUpdateRectangles(); +} + + +// Return the top position for the destination rectangle + +STDMETHODIMP CBaseControlVideo::get_DestinationTop(long *pDestinationTop) +{ + CheckPointer(pDestinationTop,E_POINTER); + CheckConnected(m_pPin,VFW_E_NOT_CONNECTED); + CAutoLock cInterfaceLock(m_pInterfaceLock); + RECT DestinationRect; + + GetTargetRect(&DestinationRect); + *pDestinationTop = DestinationRect.top; + return NOERROR; +} + + +// Set the destination height + +STDMETHODIMP CBaseControlVideo::put_DestinationHeight(long DestinationHeight) +{ + CheckConnected(m_pPin,VFW_E_NOT_CONNECTED); + CAutoLock cInterfaceLock(m_pInterfaceLock); + RECT DestinationRect; + GetTargetRect(&DestinationRect); + DestinationRect.bottom = DestinationRect.top + DestinationHeight; + + // Check the target rectangle is valid + + HRESULT hr = CheckTargetRect(&DestinationRect); + if (FAILED(hr)) { + return hr; + } + + // Now set the new target rectangle + + hr = SetTargetRect(&DestinationRect); + if (FAILED(hr)) { + return hr; + } + return OnUpdateRectangles(); +} + + +// Return the height for the destination rectangle + +STDMETHODIMP CBaseControlVideo::get_DestinationHeight(long *pDestinationHeight) +{ + CheckPointer(pDestinationHeight,E_POINTER); + CheckConnected(m_pPin,VFW_E_NOT_CONNECTED); + CAutoLock cInterfaceLock(m_pInterfaceLock); + RECT DestinationRect; + + GetTargetRect(&DestinationRect); + *pDestinationHeight = HEIGHT(&DestinationRect); + return NOERROR; +} + + +// Reset the source rectangle to the full video dimensions + +STDMETHODIMP CBaseControlVideo::SetDefaultSourcePosition() +{ + CheckConnected(m_pPin,VFW_E_NOT_CONNECTED); + CAutoLock cInterfaceLock(m_pInterfaceLock); + HRESULT hr = SetDefaultSourceRect(); + if (FAILED(hr)) { + return hr; + } + return OnUpdateRectangles(); +} + + +// Return S_OK if we're using the default source otherwise S_FALSE + +STDMETHODIMP CBaseControlVideo::IsUsingDefaultSource() +{ + CheckConnected(m_pPin,VFW_E_NOT_CONNECTED); + CAutoLock cInterfaceLock(m_pInterfaceLock); + return IsDefaultSourceRect(); +} + + +// Reset the video renderer to use the entire playback area + +STDMETHODIMP CBaseControlVideo::SetDefaultDestinationPosition() +{ + CheckConnected(m_pPin,VFW_E_NOT_CONNECTED); + CAutoLock cInterfaceLock(m_pInterfaceLock); + HRESULT hr = SetDefaultTargetRect(); + if (FAILED(hr)) { + return hr; + } + return OnUpdateRectangles(); +} + + +// Return S_OK if we're using the default target otherwise S_FALSE + +STDMETHODIMP CBaseControlVideo::IsUsingDefaultDestination() +{ + CheckConnected(m_pPin,VFW_E_NOT_CONNECTED); + CAutoLock cInterfaceLock(m_pInterfaceLock); + return IsDefaultTargetRect(); +} + + +// Return a copy of the current image in the video renderer + +STDMETHODIMP +CBaseControlVideo::GetCurrentImage(long *pBufferSize,long *pVideoImage) +{ + CheckPointer(pBufferSize,E_POINTER); + CheckConnected(m_pPin,VFW_E_NOT_CONNECTED); + CAutoLock cInterfaceLock(m_pInterfaceLock); + FILTER_STATE State; + + // Make sure we are in a paused state + + if (pVideoImage != NULL) { + m_pFilter->GetState(0,&State); + if (State != State_Paused) { + return VFW_E_NOT_PAUSED; + } + return GetStaticImage(pBufferSize,pVideoImage); + } + + // Just return the memory required + + VIDEOINFOHEADER *pVideoInfo = GetVideoFormat(); + if (pVideoInfo == NULL) + return E_OUTOFMEMORY; + RECT SourceRect; + GetSourceRect(&SourceRect); + return GetImageSize(pVideoInfo,pBufferSize,&SourceRect); +} + + +// An application has two ways of using GetCurrentImage, one is to pass a real +// buffer which should be filled with the current image. The other is to pass +// a NULL buffer pointer which is interpreted as asking us to return how much +// memory is required for the image. The constraints for when the latter can +// be called are much looser. To calculate the memory required we synthesize +// a VIDEOINFO that takes into account the source rectangle that's being used + +HRESULT CBaseControlVideo::GetImageSize(VIDEOINFOHEADER *pVideoInfo, + LONG *pBufferSize, + RECT *pSourceRect) +{ + NOTE("Entering GetImageSize"); + ASSERT(pSourceRect); + + // Check we have the correct input parameters + + if (pSourceRect == NULL || + pVideoInfo == NULL || + pBufferSize == NULL) { + + return E_UNEXPECTED; + } + + // Is the data format compatible + + if (pVideoInfo->bmiHeader.biCompression != BI_RGB) { + if (pVideoInfo->bmiHeader.biCompression != BI_BITFIELDS) { + return E_INVALIDARG; + } + } + + ASSERT(IsRectEmpty(pSourceRect) == FALSE); + + BITMAPINFOHEADER bih; + bih.biWidth = WIDTH(pSourceRect); + bih.biHeight = HEIGHT(pSourceRect); + bih.biBitCount = pVideoInfo->bmiHeader.biBitCount; + LONG Size = DIBSIZE(bih); + Size += GetBitmapFormatSize(HEADER(pVideoInfo)) - SIZE_PREHEADER; + *pBufferSize = Size; + + return NOERROR; +} + + +// Given an IMediaSample containing a linear buffer with an image and a type +// describing the bitmap make a rendering of the image into the output buffer +// This may be called by derived classes who render typical video images to +// handle the IBasicVideo GetCurrentImage method. The pVideoImage pointer may +// be NULL when passed to GetCurrentImage in which case GetImageSize will be +// called instead, which will just do the calculation of the memory required + +HRESULT CBaseControlVideo::CopyImage(IMediaSample *pMediaSample, + VIDEOINFOHEADER *pVideoInfo, + LONG *pBufferSize, + BYTE *pVideoImage, + RECT *pSourceRect) +{ + NOTE("Entering CopyImage"); + ASSERT(pSourceRect); + BYTE *pCurrentImage; + + // Check we have an image to copy + + if (pMediaSample == NULL || pSourceRect == NULL || + pVideoInfo == NULL || pVideoImage == NULL || + pBufferSize == NULL) { + + return E_UNEXPECTED; + } + + // Is the data format compatible + + if (pVideoInfo->bmiHeader.biCompression != BI_RGB) { + if (pVideoInfo->bmiHeader.biCompression != BI_BITFIELDS) { + return E_INVALIDARG; + } + } + + ASSERT(IsRectEmpty(pSourceRect) == FALSE); + + BITMAPINFOHEADER bih; + bih.biWidth = WIDTH(pSourceRect); + bih.biHeight = HEIGHT(pSourceRect); + bih.biBitCount = pVideoInfo->bmiHeader.biBitCount; + LONG Size = GetBitmapFormatSize(HEADER(pVideoInfo)) - SIZE_PREHEADER; + LONG Total = Size + DIBSIZE(bih); + + // Make sure we have a large enough buffer + + if (*pBufferSize < Total) { + return E_OUTOFMEMORY; + } + + // Copy the BITMAPINFO + + CopyMemory((PVOID)pVideoImage, (PVOID)&pVideoInfo->bmiHeader, Size); + ((BITMAPINFOHEADER *)pVideoImage)->biWidth = WIDTH(pSourceRect); + ((BITMAPINFOHEADER *)pVideoImage)->biHeight = HEIGHT(pSourceRect); + ((BITMAPINFOHEADER *)pVideoImage)->biSizeImage = DIBSIZE(bih); + BYTE *pImageData = pVideoImage + Size; + + // Get the pointer to it's image data + + HRESULT hr = pMediaSample->GetPointer(&pCurrentImage); + if (FAILED(hr)) { + return hr; + } + + // Now we are ready to start copying the source scan lines + + LONG ScanLine = (pVideoInfo->bmiHeader.biBitCount / 8) * WIDTH(pSourceRect); + LONG LinesToSkip = pVideoInfo->bmiHeader.biHeight; + LinesToSkip -= pSourceRect->top + HEIGHT(pSourceRect); + pCurrentImage += LinesToSkip * DIBWIDTHBYTES(pVideoInfo->bmiHeader); + pCurrentImage += pSourceRect->left * (pVideoInfo->bmiHeader.biBitCount / 8); + + // Even money on this GP faulting sometime... + + for (LONG Line = 0;Line < HEIGHT(pSourceRect);Line++) { + CopyMemory((PVOID)pImageData, (PVOID)pCurrentImage, ScanLine); + pImageData += DIBWIDTHBYTES(*(BITMAPINFOHEADER *)pVideoImage); + pCurrentImage += DIBWIDTHBYTES(pVideoInfo->bmiHeader); + } + return NOERROR; +} + + +// Called when we change media types either during connection or dynamically +// We inform the filter graph and therefore the application that the video +// size may have changed, we don't bother looking to see if it really has as +// we leave that to the application - the dimensions are the event parameters + +HRESULT CBaseControlVideo::OnVideoSizeChange() +{ + // Get the video format from the derived class + + VIDEOINFOHEADER *pVideoInfo = GetVideoFormat(); + if (pVideoInfo == NULL) + return E_OUTOFMEMORY; + WORD Width = (WORD) pVideoInfo->bmiHeader.biWidth; + WORD Height = (WORD) pVideoInfo->bmiHeader.biHeight; + + return m_pFilter->NotifyEvent(EC_VIDEO_SIZE_CHANGED, + MAKELPARAM(Width,Height), + MAKEWPARAM(0,0)); +} + + +// Set the video source rectangle. We must check the source rectangle against +// the actual video dimensions otherwise when we come to draw the pictures we +// get access violations as GDI tries to touch data outside of the image data +// Although we store the rectangle in left, top, right and bottom coordinates +// instead of left, top, width and height as OLE uses we do take into account +// that the rectangle is used up to, but not including, the right column and +// bottom row of pixels, see the Win32 documentation on RECT for more details + +HRESULT CBaseControlVideo::CheckSourceRect(RECT *pSourceRect) +{ + CheckPointer(pSourceRect,E_POINTER); + LONG Width,Height; + GetVideoSize(&Width,&Height); + + // Check the coordinates are greater than zero + // and that the rectangle is valid (leftleft >= pSourceRect->right) || + (pSourceRect->left < 0) || + (pSourceRect->top >= pSourceRect->bottom) || + (pSourceRect->top < 0)) { + + return E_INVALIDARG; + } + + // Check the coordinates are less than the extents + + if ((pSourceRect->right > Width) || + (pSourceRect->bottom > Height)) { + + return E_INVALIDARG; + } + return NOERROR; +} + + +// Check the target rectangle has some valid coordinates, which amounts to +// little more than checking the destination rectangle isn't empty. Derived +// classes may call this when they have their SetTargetRect method called to +// check the rectangle validity, we do not update the rectangles passed in +// Although we store the rectangle in left, top, right and bottom coordinates +// instead of left, top, width and height as OLE uses we do take into account +// that the rectangle is used up to, but not including, the right column and +// bottom row of pixels, see the Win32 documentation on RECT for more details + +HRESULT CBaseControlVideo::CheckTargetRect(RECT *pTargetRect) +{ + // Check the pointer is valid + + if (pTargetRect == NULL) { + return E_POINTER; + } + + // These overflow the WIDTH and HEIGHT checks + + if (pTargetRect->left > pTargetRect->right || + pTargetRect->top > pTargetRect->bottom) { + return E_INVALIDARG; + } + + // Check the rectangle has valid coordinates + + if (WIDTH(pTargetRect) <= 0 || HEIGHT(pTargetRect) <= 0) { + return E_INVALIDARG; + } + + ASSERT(IsRectEmpty(pTargetRect) == FALSE); + return NOERROR; +} + diff --git a/ThirdParty/strmbas/winctrl.h b/ThirdParty/strmbas/winctrl.h new file mode 100644 index 0000000..a7c7f5c --- /dev/null +++ b/ThirdParty/strmbas/winctrl.h @@ -0,0 +1,224 @@ +//------------------------------------------------------------------------------ +// File: WinCtrl.h +// +// Desc: DirectShow base classes - defines classes for video control +// interfaces. +// +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------------------------ + + +#ifndef __WINCTRL__ +#define __WINCTRL__ + +#define ABSOL(x) (x < 0 ? -x : x) +#define NEGAT(x) (x > 0 ? -x : x) + +// Helper +BOOL WINAPI PossiblyEatMessage(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam); + +class CBaseControlWindow : public CBaseVideoWindow, public CBaseWindow +{ +protected: + + CBaseFilter *m_pFilter; // Pointer to owning media filter + CBasePin *m_pPin; // Controls media types for connection + CCritSec *m_pInterfaceLock; // Externally defined critical section + COLORREF m_BorderColour; // Current window border colour + BOOL m_bAutoShow; // What happens when the state changes + HWND m_hwndOwner; // Owner window that we optionally have + HWND m_hwndDrain; // HWND to post any messages received + BOOL m_bCursorHidden; // Should we hide the window cursor + +public: + + // Internal methods for other objects to get information out + + HRESULT DoSetWindowStyle(long Style,long WindowLong); + HRESULT DoGetWindowStyle(long *pStyle,long WindowLong); + BOOL IsAutoShowEnabled() { return m_bAutoShow; }; + COLORREF GetBorderColour() { return m_BorderColour; }; + HWND GetOwnerWindow() { return m_hwndOwner; }; + BOOL IsCursorHidden() { return m_bCursorHidden; }; + + inline BOOL PossiblyEatMessage(UINT uMsg, WPARAM wParam, LPARAM lParam) + { + return ::PossiblyEatMessage(m_hwndDrain, uMsg, wParam, lParam); + } + + // Derived classes must call this to set the pin the filter is using + // We don't have the pin passed in to the constructor (as we do with + // the CBaseFilter object) because filters typically create the + // pins dynamically when requested in CBaseFilter::GetPin. This can + // not be called from our constructor because is is a virtual method + + void SetControlWindowPin(CBasePin *pPin) { + m_pPin = pPin; + } + +public: + + CBaseControlWindow(CBaseFilter *pFilter, // Owning media filter + CCritSec *pInterfaceLock, // Locking object + TCHAR *pName, // Object description + LPUNKNOWN pUnk, // Normal COM ownership + HRESULT *phr); // OLE return code + + // These are the properties we support + + STDMETHODIMP put_Caption(BSTR strCaption); + STDMETHODIMP get_Caption(BSTR *pstrCaption); + STDMETHODIMP put_AutoShow(long AutoShow); + STDMETHODIMP get_AutoShow(long *AutoShow); + STDMETHODIMP put_WindowStyle(long WindowStyle); + STDMETHODIMP get_WindowStyle(long *pWindowStyle); + STDMETHODIMP put_WindowStyleEx(long WindowStyleEx); + STDMETHODIMP get_WindowStyleEx(long *pWindowStyleEx); + STDMETHODIMP put_WindowState(long WindowState); + STDMETHODIMP get_WindowState(long *pWindowState); + STDMETHODIMP put_BackgroundPalette(long BackgroundPalette); + STDMETHODIMP get_BackgroundPalette(long *pBackgroundPalette); + STDMETHODIMP put_Visible(long Visible); + STDMETHODIMP get_Visible(long *pVisible); + STDMETHODIMP put_Left(long Left); + STDMETHODIMP get_Left(long *pLeft); + STDMETHODIMP put_Width(long Width); + STDMETHODIMP get_Width(long *pWidth); + STDMETHODIMP put_Top(long Top); + STDMETHODIMP get_Top(long *pTop); + STDMETHODIMP put_Height(long Height); + STDMETHODIMP get_Height(long *pHeight); + STDMETHODIMP put_Owner(OAHWND Owner); + STDMETHODIMP get_Owner(OAHWND *Owner); + STDMETHODIMP put_MessageDrain(OAHWND Drain); + STDMETHODIMP get_MessageDrain(OAHWND *Drain); + STDMETHODIMP get_BorderColor(long *Color); + STDMETHODIMP put_BorderColor(long Color); + STDMETHODIMP get_FullScreenMode(long *FullScreenMode); + STDMETHODIMP put_FullScreenMode(long FullScreenMode); + + // And these are the methods + + STDMETHODIMP SetWindowForeground(long Focus); + STDMETHODIMP NotifyOwnerMessage(OAHWND hwnd,long uMsg,LONG_PTR wParam,LONG_PTR lParam); + STDMETHODIMP GetMinIdealImageSize(long *pWidth,long *pHeight); + STDMETHODIMP GetMaxIdealImageSize(long *pWidth,long *pHeight); + STDMETHODIMP SetWindowPosition(long Left,long Top,long Width,long Height); + STDMETHODIMP GetWindowPosition(long *pLeft,long *pTop,long *pWidth,long *pHeight); + STDMETHODIMP GetRestorePosition(long *pLeft,long *pTop,long *pWidth,long *pHeight); + STDMETHODIMP HideCursor(long HideCursor); + STDMETHODIMP IsCursorHidden(long *CursorHidden); +}; + +// This class implements the IBasicVideo interface + +class CBaseControlVideo : public CBaseBasicVideo +{ +protected: + + CBaseFilter *m_pFilter; // Pointer to owning media filter + CBasePin *m_pPin; // Controls media types for connection + CCritSec *m_pInterfaceLock; // Externally defined critical section + +public: + + // Derived classes must provide these for the implementation + + virtual HRESULT IsDefaultTargetRect() PURE; + virtual HRESULT SetDefaultTargetRect() PURE; + virtual HRESULT SetTargetRect(RECT *pTargetRect) PURE; + virtual HRESULT GetTargetRect(RECT *pTargetRect) PURE; + virtual HRESULT IsDefaultSourceRect() PURE; + virtual HRESULT SetDefaultSourceRect() PURE; + virtual HRESULT SetSourceRect(RECT *pSourceRect) PURE; + virtual HRESULT GetSourceRect(RECT *pSourceRect) PURE; + virtual HRESULT GetStaticImage(long *pBufferSize,long *pDIBImage) PURE; + + // Derived classes must override this to return a VIDEOINFO representing + // the video format. We cannot call IPin ConnectionMediaType to get this + // format because various filters dynamically change the type when using + // DirectDraw such that the format shows the position of the logical + // bitmap in a frame buffer surface, so the size might be returned as + // 1024x768 pixels instead of 320x240 which is the real video dimensions + + virtual VIDEOINFOHEADER *GetVideoFormat() PURE; + + // Helper functions for creating memory renderings of a DIB image + + HRESULT GetImageSize(VIDEOINFOHEADER *pVideoInfo, + LONG *pBufferSize, + RECT *pSourceRect); + + HRESULT CopyImage(IMediaSample *pMediaSample, + VIDEOINFOHEADER *pVideoInfo, + LONG *pBufferSize, + BYTE *pVideoImage, + RECT *pSourceRect); + + // Override this if you want notifying when the rectangles change + virtual HRESULT OnUpdateRectangles() { return NOERROR; }; + virtual HRESULT OnVideoSizeChange(); + + // Derived classes must call this to set the pin the filter is using + // We don't have the pin passed in to the constructor (as we do with + // the CBaseFilter object) because filters typically create the + // pins dynamically when requested in CBaseFilter::GetPin. This can + // not be called from our constructor because is is a virtual method + + void SetControlVideoPin(CBasePin *pPin) { + m_pPin = pPin; + } + + // Helper methods for checking rectangles + virtual HRESULT CheckSourceRect(RECT *pSourceRect); + virtual HRESULT CheckTargetRect(RECT *pTargetRect); + +public: + + CBaseControlVideo(CBaseFilter *pFilter, // Owning media filter + CCritSec *pInterfaceLock, // Serialise interface + TCHAR *pName, // Object description + LPUNKNOWN pUnk, // Normal COM ownership + HRESULT *phr); // OLE return code + + // These are the properties we support + + STDMETHODIMP get_AvgTimePerFrame(REFTIME *pAvgTimePerFrame); + STDMETHODIMP get_BitRate(long *pBitRate); + STDMETHODIMP get_BitErrorRate(long *pBitErrorRate); + STDMETHODIMP get_VideoWidth(long *pVideoWidth); + STDMETHODIMP get_VideoHeight(long *pVideoHeight); + STDMETHODIMP put_SourceLeft(long SourceLeft); + STDMETHODIMP get_SourceLeft(long *pSourceLeft); + STDMETHODIMP put_SourceWidth(long SourceWidth); + STDMETHODIMP get_SourceWidth(long *pSourceWidth); + STDMETHODIMP put_SourceTop(long SourceTop); + STDMETHODIMP get_SourceTop(long *pSourceTop); + STDMETHODIMP put_SourceHeight(long SourceHeight); + STDMETHODIMP get_SourceHeight(long *pSourceHeight); + STDMETHODIMP put_DestinationLeft(long DestinationLeft); + STDMETHODIMP get_DestinationLeft(long *pDestinationLeft); + STDMETHODIMP put_DestinationWidth(long DestinationWidth); + STDMETHODIMP get_DestinationWidth(long *pDestinationWidth); + STDMETHODIMP put_DestinationTop(long DestinationTop); + STDMETHODIMP get_DestinationTop(long *pDestinationTop); + STDMETHODIMP put_DestinationHeight(long DestinationHeight); + STDMETHODIMP get_DestinationHeight(long *pDestinationHeight); + + // And these are the methods + + STDMETHODIMP GetVideoSize(long *pWidth,long *pHeight); + STDMETHODIMP SetSourcePosition(long Left,long Top,long Width,long Height); + STDMETHODIMP GetSourcePosition(long *pLeft,long *pTop,long *pWidth,long *pHeight); + STDMETHODIMP GetVideoPaletteEntries(long StartIndex,long Entries,long *pRetrieved,long *pPalette); + STDMETHODIMP SetDefaultSourcePosition(); + STDMETHODIMP IsUsingDefaultSource(); + STDMETHODIMP SetDestinationPosition(long Left,long Top,long Width,long Height); + STDMETHODIMP GetDestinationPosition(long *pLeft,long *pTop,long *pWidth,long *pHeight); + STDMETHODIMP SetDefaultDestinationPosition(); + STDMETHODIMP IsUsingDefaultDestination(); + STDMETHODIMP GetCurrentImage(long *pBufferSize,long *pVideoImage); +}; + +#endif // __WINCTRL__ + diff --git a/ThirdParty/strmbas/winutil.cpp b/ThirdParty/strmbas/winutil.cpp new file mode 100644 index 0000000..46c33ae --- /dev/null +++ b/ThirdParty/strmbas/winutil.cpp @@ -0,0 +1,2681 @@ +//------------------------------------------------------------------------------ +// File: WinUtil.cpp +// +// Desc: DirectShow base classes - implements generic window handler class. +// +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------------------------ + + +#include +#include +#include + +static UINT MsgDestroy; + +// Constructor + +CBaseWindow::CBaseWindow(BOOL bDoGetDC, bool bDoPostToDestroy) : + m_hInstance(g_hInst), + m_hwnd(NULL), + m_hdc(NULL), + m_bActivated(FALSE), + m_pClassName(NULL), + m_ClassStyles(0), + m_WindowStyles(0), + m_WindowStylesEx(0), + m_ShowStageMessage(0), + m_ShowStageTop(0), + m_MemoryDC(NULL), + m_hPalette(NULL), + m_bBackground(FALSE), +#ifdef DEBUG + m_bRealizing(FALSE), +#endif + m_bNoRealize(FALSE), + m_bDoPostToDestroy(bDoPostToDestroy) +{ + m_bDoGetDC = bDoGetDC; +} + + +// Prepare a window by spinning off a worker thread to do the creation and +// also poll the message input queue. We leave this to be called by derived +// classes because they might want to override methods like MessageLoop and +// InitialiseWindow, if we do this during construction they'll ALWAYS call +// this base class methods. We make the worker thread create the window so +// it owns it rather than the filter graph thread which is constructing us + +HRESULT CBaseWindow::PrepareWindow() +{ + if (m_hwnd) return NOERROR; + ASSERT(m_hwnd == NULL); + ASSERT(m_hdc == NULL); + + // Get the derived object's window and class styles + + m_pClassName = GetClassWindowStyles(&m_ClassStyles, + &m_WindowStyles, + &m_WindowStylesEx); + if (m_pClassName == NULL) { + return E_FAIL; + } + + // Register our special private messages + m_ShowStageMessage = RegisterWindowMessage(SHOWSTAGE); + + // RegisterWindowMessage() returns 0 if an error occurs. + if (0 == m_ShowStageMessage) { + return AmGetLastErrorToHResult(); + } + + m_ShowStageTop = RegisterWindowMessage(SHOWSTAGETOP); + if (0 == m_ShowStageTop) { + return AmGetLastErrorToHResult(); + } + + m_RealizePalette = RegisterWindowMessage(REALIZEPALETTE); + if (0 == m_RealizePalette) { + return AmGetLastErrorToHResult(); + } + + MsgDestroy = RegisterWindowMessage(TEXT("AM_DESTROY")); + if (0 == MsgDestroy) { + return AmGetLastErrorToHResult(); + } + + return DoCreateWindow(); +} + + +// Destructor just a placeholder so that we know it becomes virtual +// Derived classes MUST call DoneWithWindow in their destructors so +// that no messages arrive after the derived class constructor ends + +#ifdef DEBUG +CBaseWindow::~CBaseWindow() +{ + ASSERT(m_hwnd == NULL); + ASSERT(m_hdc == NULL); +} +#endif + + +// We use the sync worker event to have the window destroyed. All we do is +// signal the event and wait on the window thread handle. Trying to send it +// messages causes too many problems, furthermore to be on the safe side we +// just wait on the thread handle while it returns WAIT_TIMEOUT or there is +// a sent message to process on this thread. If the constructor failed to +// create the thread in the first place then the loop will get terminated + +HRESULT CBaseWindow::DoneWithWindow() +{ + if (!IsWindow(m_hwnd) || (GetWindowThreadProcessId(m_hwnd, NULL) != GetCurrentThreadId())) { + + if (IsWindow(m_hwnd)) { + + if (m_bDoPostToDestroy) { + + CAMEvent m_evDone; + + // We must post a message to destroy the window + // That way we can't be in the middle of processing a + // message posted to our window when we do go away + // Sending a message gives less synchronization. + PostMessage(m_hwnd, MsgDestroy, (WPARAM)(HANDLE)m_evDone, 0); + WaitDispatchingMessages(m_evDone, INFINITE); + } else { + SendMessage(m_hwnd, MsgDestroy, 0, 0); + } + } + + // + // This is not a leak, the window manager automatically free's + // hdc's that were got via GetDC, which is the case here. + // We set it to NULL so that we don't get any asserts later. + // + m_hdc = NULL; + + // + // We need to free this DC though because USER32 does not know + // anything about it. + // + if (m_MemoryDC) + { + EXECUTE_ASSERT(DeleteDC(m_MemoryDC)); + m_MemoryDC = NULL; + } + + // Reset the window variables + m_hwnd = NULL; + + return NOERROR; + } + const HWND hwnd = m_hwnd; + if (hwnd == NULL) { + return NOERROR; + } + + InactivateWindow(); + NOTE("Inactivated"); + + // Reset the window styles before destruction + + SetWindowLong(hwnd,GWL_STYLE,m_WindowStyles); + ASSERT(GetParent(hwnd) == NULL); + NOTE1("Reset window styles %d",m_WindowStyles); + + // UnintialiseWindow sets m_hwnd to NULL so save a copy + UninitialiseWindow(); + DbgLog((LOG_TRACE, 2, TEXT("Destroying 0x%8.8X"), hwnd)); + if (!DestroyWindow(hwnd)) { + DbgLog((LOG_TRACE, 0, TEXT("DestroyWindow %8.8X failed code %d"), + hwnd, GetLastError())); + DbgBreak(""); + } + + // Reset our state so we can be prepared again + + m_pClassName = NULL; + m_ClassStyles = 0; + m_WindowStyles = 0; + m_WindowStylesEx = 0; + m_ShowStageMessage = 0; + m_ShowStageTop = 0; + + return NOERROR; +} + + +// Called at the end to put the window in an inactive state. The pending list +// will always have been cleared by this time so event if the worker thread +// gets has been signaled and gets in to render something it will find both +// the state has been changed and that there are no available sample images +// Since we wait on the window thread to complete we don't lock the object + +HRESULT CBaseWindow::InactivateWindow() +{ + // Has the window been activated + if (m_bActivated == FALSE) { + return S_FALSE; + } + + m_bActivated = FALSE; + ShowWindow(m_hwnd,SW_HIDE); + return NOERROR; +} + + +HRESULT CBaseWindow::CompleteConnect() +{ + m_bActivated = FALSE; + return NOERROR; +} + +// This displays a normal window. We ask the base window class for default +// sizes which unless overriden will return DEFWIDTH and DEFHEIGHT. We go +// through a couple of extra hoops to get the client area the right size +// as the object specifies which accounts for the AdjustWindowRectEx calls +// We also DWORD align the left and top coordinates of the window here to +// maximise the chance of being able to use DCI/DirectDraw primary surface + +HRESULT CBaseWindow::ActivateWindow() +{ + // Has the window been sized and positioned already + + if (m_bActivated == TRUE || GetParent(m_hwnd) != NULL) { + + SetWindowPos(m_hwnd, // Our window handle + HWND_TOP, // Put it at the top + 0, 0, 0, 0, // Leave in current position + SWP_NOMOVE | // Don't change it's place + SWP_NOSIZE); // Change Z-order only + + m_bActivated = TRUE; + return S_FALSE; + } + + // Calculate the desired client rectangle + + RECT WindowRect, ClientRect = GetDefaultRect(); + GetWindowRect(m_hwnd,&WindowRect); + AdjustWindowRectEx(&ClientRect,GetWindowLong(m_hwnd,GWL_STYLE), + FALSE,GetWindowLong(m_hwnd,GWL_EXSTYLE)); + + // Align left and top edges on DWORD boundaries + + UINT WindowFlags = (SWP_NOACTIVATE | SWP_FRAMECHANGED); + WindowRect.left -= (WindowRect.left & 3); + WindowRect.top -= (WindowRect.top & 3); + + SetWindowPos(m_hwnd, // Window handle + HWND_TOP, // Put it at the top + WindowRect.left, // Align left edge + WindowRect.top, // And also top place + WIDTH(&ClientRect), // Horizontal size + HEIGHT(&ClientRect), // Vertical size + WindowFlags); // Don't show window + + m_bActivated = TRUE; + return NOERROR; +} + + +// This can be used to DWORD align the window for maximum performance + +HRESULT CBaseWindow::PerformanceAlignWindow() +{ + RECT ClientRect,WindowRect; + GetWindowRect(m_hwnd,&WindowRect); + ASSERT(m_bActivated == TRUE); + + // Don't do this if we're owned + + if (GetParent(m_hwnd)) { + return NOERROR; + } + + // Align left and top edges on DWORD boundaries + + GetClientRect(m_hwnd, &ClientRect); + MapWindowPoints(m_hwnd, HWND_DESKTOP, (LPPOINT) &ClientRect, 2); + WindowRect.left -= (ClientRect.left & 3); + WindowRect.top -= (ClientRect.top & 3); + UINT WindowFlags = (SWP_NOACTIVATE | SWP_NOSIZE); + + SetWindowPos(m_hwnd, // Window handle + HWND_TOP, // Put it at the top + WindowRect.left, // Align left edge + WindowRect.top, // And also top place + (int) 0,(int) 0, // Ignore these sizes + WindowFlags); // Don't show window + + return NOERROR; +} + + +// Install a palette into the base window - we may be called by a different +// thread to the one that owns the window. We have to be careful how we do +// the palette realisation as we could be a different thread to the window +// which would cause an inter thread send message. Therefore we realise the +// palette by sending it a special message but without the window locked + +HRESULT CBaseWindow::SetPalette(HPALETTE hPalette) +{ + // We must own the window lock during the change + { + CAutoLock cWindowLock(&m_WindowLock); + CAutoLock cPaletteLock(&m_PaletteLock); + ASSERT(hPalette); + m_hPalette = hPalette; + } + return SetPalette(); +} + + +HRESULT CBaseWindow::SetPalette() +{ + if (!m_bNoRealize) { + SendMessage(m_hwnd, m_RealizePalette, 0, 0); + return S_OK; + } else { + // Just select the palette + ASSERT(m_hdc); + ASSERT(m_MemoryDC); + + CAutoLock cPaletteLock(&m_PaletteLock); + SelectPalette(m_hdc,m_hPalette,m_bBackground); + SelectPalette(m_MemoryDC,m_hPalette,m_bBackground); + + return S_OK; + } +} + + +void CBaseWindow::UnsetPalette() +{ + CAutoLock cWindowLock(&m_WindowLock); + CAutoLock cPaletteLock(&m_PaletteLock); + + // Get a standard VGA colour palette + + HPALETTE hPalette = (HPALETTE) GetStockObject(DEFAULT_PALETTE); + ASSERT(hPalette); + + SelectPalette(GetWindowHDC(), hPalette, TRUE); + SelectPalette(GetMemoryHDC(), hPalette, TRUE); + + m_hPalette = NULL; +} + + +void CBaseWindow::LockPaletteLock() +{ + m_PaletteLock.Lock(); +} + + +void CBaseWindow::UnlockPaletteLock() +{ + m_PaletteLock.Unlock(); +} + + +// Realise our palettes in the window and device contexts + +HRESULT CBaseWindow::DoRealisePalette(BOOL bForceBackground) +{ + { + CAutoLock cPaletteLock(&m_PaletteLock); + + if (m_hPalette == NULL) { + return NOERROR; + } + + // Realize the palette on the window thread + ASSERT(m_hdc); + ASSERT(m_MemoryDC); + + SelectPalette(m_hdc,m_hPalette,m_bBackground || bForceBackground); + SelectPalette(m_MemoryDC,m_hPalette,m_bBackground); + } + + // If we grab a critical section here we can deadlock + // with the window thread because one of the side effects + // of RealizePalette is to send a WM_PALETTECHANGED message + // to every window in the system. In our handling + // of WM_PALETTECHANGED we used to grab this CS too. + // The really bad case is when our renderer calls DoRealisePalette() + // while we're in the middle of processing a palette change + // for another window. + // So don't hold the critical section while actually realising + // the palette. In any case USER is meant to manage palette + // handling - we shouldn't have to serialize everything as well + ASSERT(CritCheckOut(&m_WindowLock)); + ASSERT(CritCheckOut(&m_PaletteLock)); + + EXECUTE_ASSERT(RealizePalette(m_hdc) != GDI_ERROR); + EXECUTE_ASSERT(RealizePalette(m_MemoryDC) != GDI_ERROR); + + return (GdiFlush() == FALSE ? S_FALSE : S_OK); +} + + +// This is the global window procedure + +LRESULT CALLBACK WndProc(HWND hwnd, // Window handle + UINT uMsg, // Message ID + WPARAM wParam, // First parameter + LPARAM lParam) // Other parameter +{ + + // Get the window long that holds our window object pointer + // If it is NULL then we are initialising the window in which + // case the object pointer has been passed in the window creation + // structure. IF we get any messages before WM_NCCREATE we will + // pass them to DefWindowProc. + + CBaseWindow *pBaseWindow = (CBaseWindow *)GetWindowLongPtr(hwnd,0); + if (pBaseWindow == NULL) { + + // Get the structure pointer from the create struct. + // We can only do this for WM_NCCREATE which should be one of + // the first messages we receive. Anything before this will + // have to be passed to DefWindowProc (i.e. WM_GETMINMAXINFO) + + // If the message is WM_NCCREATE we set our pBaseWindow pointer + // and will then place it in the window structure + + // turn off WS_EX_LAYOUTRTL style for quartz windows + if (uMsg == WM_NCCREATE) { + SetWindowLong(hwnd, GWL_EXSTYLE, GetWindowLong(hwnd, GWL_EXSTYLE) & ~0x400000); + } + + if ((uMsg != WM_NCCREATE) + || (NULL == (pBaseWindow = *(CBaseWindow**) ((LPCREATESTRUCT)lParam)->lpCreateParams))) + { + return(DefWindowProc(hwnd, uMsg, wParam, lParam)); + } + + // Set the window LONG to be the object who created us +#ifdef DEBUG + SetLastError(0); // because of the way SetWindowLong works +#endif + LONG_PTR rc = SetWindowLongPtr(hwnd, (DWORD) 0, (LONG_PTR) pBaseWindow); +#ifdef DEBUG + if (0 == rc) { + // SetWindowLong MIGHT have failed. (Read the docs which admit + // that it is awkward to work out if you have had an error.) + LONG lasterror = GetLastError(); + ASSERT(0 == lasterror); + // If this is not the case we have not set the pBaseWindow pointer + // into the window structure and we will blow up. + } +#endif + + } + // See if this is the packet of death + if (uMsg == MsgDestroy && uMsg != 0) { + pBaseWindow->DoneWithWindow(); + if (pBaseWindow->m_bDoPostToDestroy) { + EXECUTE_ASSERT(SetEvent((HANDLE)wParam)); + } + return 0; + } + return pBaseWindow->OnReceiveMessage(hwnd,uMsg,wParam,lParam); +} + + +// When the window size changes we adjust our member variables that +// contain the dimensions of the client rectangle for our window so +// that we come to render an image we will know whether to stretch + +BOOL CBaseWindow::OnSize(LONG Width, LONG Height) +{ + m_Width = Width; + m_Height = Height; + return TRUE; +} + + +// This function handles the WM_CLOSE message + +BOOL CBaseWindow::OnClose() +{ + ShowWindow(m_hwnd,SW_HIDE); + return TRUE; +} + + +// This is called by the worker window thread when it receives a terminate +// message from the window object destructor to delete all the resources we +// allocated during initialisation. By the time the worker thread exits all +// processing will have been completed as the source filter disconnection +// flushes the image pending sample, therefore the GdiFlush should succeed + +HRESULT CBaseWindow::UninitialiseWindow() +{ + // Have we already cleaned up + + if (m_hwnd == NULL) { + ASSERT(m_hdc == NULL); + ASSERT(m_MemoryDC == NULL); + return NOERROR; + } + + // Release the window resources + + EXECUTE_ASSERT(GdiFlush()); + + if (m_hdc) + { + EXECUTE_ASSERT(ReleaseDC(m_hwnd,m_hdc)); + m_hdc = NULL; + } + + if (m_MemoryDC) + { + EXECUTE_ASSERT(DeleteDC(m_MemoryDC)); + m_MemoryDC = NULL; + } + + // Reset the window variables + m_hwnd = NULL; + + return NOERROR; +} + + +// This is called by the worker window thread after it has created the main +// window and it wants to initialise the rest of the owner objects window +// variables such as the device contexts. We execute this function with the +// critical section still locked. Nothing in this function must generate any +// SendMessage calls to the window because this is executing on the window +// thread so the message will never be processed and we will deadlock + +HRESULT CBaseWindow::InitialiseWindow(HWND hwnd) +{ + // Initialise the window variables + + ASSERT(IsWindow(hwnd)); + m_hwnd = hwnd; + + if (m_bDoGetDC) + { + EXECUTE_ASSERT(m_hdc = GetDC(hwnd)); + EXECUTE_ASSERT(m_MemoryDC = CreateCompatibleDC(m_hdc)); + + EXECUTE_ASSERT(SetStretchBltMode(m_hdc,COLORONCOLOR)); + EXECUTE_ASSERT(SetStretchBltMode(m_MemoryDC,COLORONCOLOR)); + } + + return NOERROR; +} + +HRESULT CBaseWindow::DoCreateWindow() +{ + WNDCLASS wndclass; // Used to register classes + BOOL bRegistered; // Is this class registered + HWND hwnd; // Handle to our window + + bRegistered = GetClassInfo(m_hInstance, // Module instance + m_pClassName, // Window class + &wndclass); // Info structure + + // if the window is to be used for drawing puposes and we are getting a DC + // for the entire lifetime of the window then changes the class style to do + // say so. If we don't set this flag then the DC comes from the cache and is + // really bad. + if (m_bDoGetDC) + { + m_ClassStyles |= CS_OWNDC; + } + + if (bRegistered == FALSE) { + + // Register the renderer window class + + wndclass.lpszClassName = m_pClassName; + wndclass.style = m_ClassStyles; + wndclass.lpfnWndProc = WndProc; + wndclass.cbClsExtra = 0; + wndclass.cbWndExtra = sizeof(CBaseWindow *); + wndclass.hInstance = m_hInstance; + wndclass.hIcon = NULL; + wndclass.hCursor = LoadCursor (NULL, IDC_ARROW); + wndclass.hbrBackground = (HBRUSH) NULL; + wndclass.lpszMenuName = NULL; + + RegisterClass(&wndclass); + } + + // Create the frame window. Pass the pBaseWindow information in the + // CreateStruct which allows our message handling loop to get hold of + // the pBaseWindow pointer. + + CBaseWindow *pBaseWindow = this; // The owner window object + hwnd = CreateWindowEx(m_WindowStylesEx, // Extended styles + m_pClassName, // Registered name + TEXT("ActiveMovie Window"), // Window title + m_WindowStyles, // Window styles + CW_USEDEFAULT, // Start x position + CW_USEDEFAULT, // Start y position + DEFWIDTH, // Window width + DEFHEIGHT, // Window height + NULL, // Parent handle + NULL, // Menu handle + m_hInstance, // Instance handle + &pBaseWindow); // Creation data + + // If we failed signal an error to the object constructor (based on the + // last Win32 error on this thread) then signal the constructor thread + // to continue, release the mutex to let others have a go and exit + + if (hwnd == NULL) { + DWORD Error = GetLastError(); + return AmHresultFromWin32(Error); + } + + // Check the window LONG is the object who created us + ASSERT(GetWindowLongPtr(hwnd, 0) == (LONG_PTR)this); + + // Initialise the window and then signal the constructor so that it can + // continue and then finally unlock the object's critical section. The + // window class is left registered even after we terminate the thread + // as we don't know when the last window has been closed. So we allow + // the operating system to free the class resources as appropriate + + InitialiseWindow(hwnd); + + DbgLog((LOG_TRACE, 2, TEXT("Created window class (%s) HWND(%8.8X)"), + m_pClassName, hwnd)); + + return S_OK; +} + + +// The base class provides some default handling and calls DefWindowProc + +LRESULT CBaseWindow::OnReceiveMessage(HWND hwnd, // Window handle + UINT uMsg, // Message ID + WPARAM wParam, // First parameter + LPARAM lParam) // Other parameter +{ + ASSERT(IsWindow(hwnd)); + + if (PossiblyEatMessage(uMsg, wParam, lParam)) + return 0; + + // This is sent by the IVideoWindow SetWindowForeground method. If the + // window is invisible we will show it and make it topmost without the + // foreground focus. If the window is visible it will also be made the + // topmost window without the foreground focus. If wParam is TRUE then + // for both cases the window will be forced into the foreground focus + + if (uMsg == m_ShowStageMessage) { + + BOOL bVisible = IsWindowVisible(hwnd); + SetWindowPos(hwnd, HWND_TOP, 0, 0, 0, 0, + SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW | + (bVisible ? SWP_NOACTIVATE : 0)); + + // Should we bring the window to the foreground + if (wParam == TRUE) { + SetForegroundWindow(hwnd); + } + return (LRESULT) 1; + } + + // When we go fullscreen we have to add the WS_EX_TOPMOST style to the + // video window so that it comes out above any task bar (this is more + // relevant to WindowsNT than Windows95). However the SetWindowPos call + // must be on the same thread as that which created the window. The + // wParam parameter can be TRUE or FALSE to set and reset the topmost + + if (uMsg == m_ShowStageTop) { + HWND HwndTop = (wParam == TRUE ? HWND_TOPMOST : HWND_NOTOPMOST); + BOOL bVisible = IsWindowVisible(hwnd); + SetWindowPos(hwnd, HwndTop, 0, 0, 0, 0, + SWP_NOMOVE | SWP_NOSIZE | + (wParam == TRUE ? SWP_SHOWWINDOW : 0) | + (bVisible ? SWP_NOACTIVATE : 0)); + return (LRESULT) 1; + } + + // New palette stuff + if (uMsg == m_RealizePalette) { + ASSERT(m_hwnd == hwnd); + return OnPaletteChange(m_hwnd,WM_QUERYNEWPALETTE); + } + + switch (uMsg) { + + // Repaint the window if the system colours change + + case WM_SYSCOLORCHANGE: + + InvalidateRect(hwnd,NULL,FALSE); + return (LRESULT) 1; + + // Somebody has changed the palette + case WM_PALETTECHANGED: + + OnPaletteChange((HWND)wParam,uMsg); + return (LRESULT) 0; + + // We are about to receive the keyboard focus so we ask GDI to realise + // our logical palette again and hopefully it will be fully installed + // without any mapping having to be done during any picture rendering + + case WM_QUERYNEWPALETTE: + ASSERT(m_hwnd == hwnd); + return OnPaletteChange(m_hwnd,uMsg); + + // do NOT fwd WM_MOVE. the parameters are the location of the parent + // window, NOT what the renderer should be looking at. But we need + // to make sure the overlay is moved with the parent window, so we + // do this. + case WM_MOVE: + if (IsWindowVisible(m_hwnd)) { + PostMessage(m_hwnd,WM_PAINT,0,0); + } + break; + + // Store the width and height as useful base class members + + case WM_SIZE: + + OnSize(LOWORD(lParam), HIWORD(lParam)); + return (LRESULT) 0; + + // Intercept the WM_CLOSE messages to hide the window + + case WM_CLOSE: + + OnClose(); + return (LRESULT) 0; + } + return DefWindowProc(hwnd,uMsg,wParam,lParam); +} + + +// This handles the Windows palette change messages - if we do realise our +// palette then we return TRUE otherwise we return FALSE. If our window is +// foreground application then we should get first choice of colours in the +// system palette entries. We get best performance when our logical palette +// includes the standard VGA colours (at the beginning and end) otherwise +// GDI may have to map from our palette to the device palette while drawing + +LRESULT CBaseWindow::OnPaletteChange(HWND hwnd,UINT Message) +{ + // First check we are not changing the palette during closedown + + if (m_hwnd == NULL || hwnd == NULL) { + return (LRESULT) 0; + } + ASSERT(!m_bRealizing); + + // Should we realise our palette again + + if ((Message == WM_QUERYNEWPALETTE || hwnd != m_hwnd)) { + // It seems that even if we're invisible that we can get asked + // to realize our palette and this can cause really ugly side-effects + // Seems like there's another bug but this masks it a least for the + // shutting down case. + if (!IsWindowVisible(m_hwnd)) { + DbgLog((LOG_TRACE, 1, TEXT("Realizing when invisible!"))); + return (LRESULT) 0; + } + + // Avoid recursion with multiple graphs in the same app +#ifdef DEBUG + m_bRealizing = TRUE; +#endif + DoRealisePalette(Message != WM_QUERYNEWPALETTE); +#ifdef DEBUG + m_bRealizing = FALSE; +#endif + + // Should we redraw the window with the new palette + if (Message == WM_PALETTECHANGED) { + InvalidateRect(m_hwnd,NULL,FALSE); + } + } + + return (LRESULT) 1; +} + + +// Determine if the window exists. + +bool CBaseWindow::WindowExists() +{ + return !!IsWindow(m_hwnd); +} + + +// Return the default window rectangle + +RECT CBaseWindow::GetDefaultRect() +{ + RECT DefaultRect = {0,0,DEFWIDTH,DEFHEIGHT}; + ASSERT(m_hwnd); + // ASSERT(m_hdc); + return DefaultRect; +} + + +// Return the current window width + +LONG CBaseWindow::GetWindowWidth() +{ + ASSERT(m_hwnd); + // ASSERT(m_hdc); + return m_Width; +} + + +// Return the current window height + +LONG CBaseWindow::GetWindowHeight() +{ + ASSERT(m_hwnd); + // ASSERT(m_hdc); + return m_Height; +} + + +// Return the window handle + +HWND CBaseWindow::GetWindowHWND() +{ + ASSERT(m_hwnd); + // ASSERT(m_hdc); + return m_hwnd; +} + + +// Return the window drawing device context + +HDC CBaseWindow::GetWindowHDC() +{ + ASSERT(m_hwnd); + ASSERT(m_hdc); + return m_hdc; +} + + +// Return the offscreen window drawing device context + +HDC CBaseWindow::GetMemoryHDC() +{ + ASSERT(m_hwnd); + ASSERT(m_MemoryDC); + return m_MemoryDC; +} + + +#ifdef DEBUG +HPALETTE CBaseWindow::GetPalette() +{ + // The palette lock should always be held when accessing + // m_hPalette. + ASSERT(CritCheckIn(&m_PaletteLock)); + return m_hPalette; +} +#endif // DEBUG + + +// This is available to clients who want to change the window visiblity. It's +// little more than an indirection to the Win32 ShowWindow although these is +// some benefit in going through here as this function may change sometime + +HRESULT CBaseWindow::DoShowWindow(LONG ShowCmd) +{ + ShowWindow(m_hwnd,ShowCmd); + return NOERROR; +} + + +// Generate a WM_PAINT message for the video window + +void CBaseWindow::PaintWindow(BOOL bErase) +{ + InvalidateRect(m_hwnd,NULL,bErase); +} + + +// Allow an application to have us set the video window in the foreground. We +// have this because it is difficult for one thread to do do this to a window +// owned by another thread. Rather than expose the message we use to execute +// the inter thread send message we provide the interface function. All we do +// is to SendMessage to the video window renderer thread with a WM_SHOWSTAGE + +void CBaseWindow::DoSetWindowForeground(BOOL bFocus) +{ + SendMessage(m_hwnd,m_ShowStageMessage,(WPARAM) bFocus,(LPARAM) 0); +} + + +// Constructor initialises the owning object pointer. Since we are a worker +// class for the main window object we have relatively few state variables to +// look after. We are given device context handles to use later on as well as +// the source and destination rectangles (but reset them here just in case) + +CDrawImage::CDrawImage(CBaseWindow *pBaseWindow) : + m_pBaseWindow(pBaseWindow), + m_hdc(NULL), + m_MemoryDC(NULL), + m_bStretch(FALSE), + m_pMediaType(NULL), + m_bUsingImageAllocator(FALSE) +{ + ASSERT(pBaseWindow); + ResetPaletteVersion(); + SetRectEmpty(&m_TargetRect); + SetRectEmpty(&m_SourceRect); + + m_perfidRenderTime = MSR_REGISTER(TEXT("Single Blt time")); +} + + +// Overlay the image time stamps on the picture. Access to this method is +// serialised by the caller. We display the sample start and end times on +// top of the video using TextOut on the device context we are handed. If +// there isn't enough room in the window for the times we don't show them + +void CDrawImage::DisplaySampleTimes(IMediaSample *pSample) +{ +#ifdef DEBUG + // + // Only allow the "annoying" time messages if the users has turned the + // logging "way up" + // + BOOL bAccept = DbgCheckModuleLevel(LOG_TRACE, 5); + if (bAccept == FALSE) { + return; + } +#endif + + TCHAR szTimes[TIMELENGTH]; // Time stamp strings + ASSERT(pSample); // Quick sanity check + RECT ClientRect; // Client window size + SIZE Size; // Size of text output + + // Get the time stamps and window size + + pSample->GetTime((REFERENCE_TIME*)&m_StartSample, (REFERENCE_TIME*)&m_EndSample); + HWND hwnd = m_pBaseWindow->GetWindowHWND(); + EXECUTE_ASSERT(GetClientRect(hwnd,&ClientRect)); + + // Format the sample time stamps + + (void)StringCchPrintf(szTimes, NUMELMS(szTimes), TEXT("%08d : %08d"), + m_StartSample.Millisecs(), + m_EndSample.Millisecs()); + + ASSERT(lstrlen(szTimes) < TIMELENGTH); + + // Put the times in the middle at the bottom of the window + + GetTextExtentPoint32(m_hdc,szTimes,lstrlen(szTimes),&Size); + INT XPos = ((ClientRect.right - ClientRect.left) - Size.cx) / 2; + INT YPos = ((ClientRect.bottom - ClientRect.top) - Size.cy) * 4 / 5; + + // Check the window is big enough to have sample times displayed + + if ((XPos > 0) && (YPos > 0)) { + TextOut(m_hdc,XPos,YPos,szTimes,lstrlen(szTimes)); + } +} + + +// This is called when the drawing code sees that the image has a down level +// palette cookie. We simply call the SetDIBColorTable Windows API with the +// palette that is found after the BITMAPINFOHEADER - we return no errors + +void CDrawImage::UpdateColourTable(HDC hdc,BITMAPINFOHEADER *pbmi) +{ + ASSERT(pbmi->biClrUsed); + RGBQUAD *pColourTable = (RGBQUAD *)(pbmi+1); + + // Set the new palette in the device context + + UINT uiReturn = SetDIBColorTable(hdc,(UINT) 0, + pbmi->biClrUsed, + pColourTable); + + // Should always succeed but check in debug builds + ASSERT(uiReturn == pbmi->biClrUsed); +} + + +// No source rectangle scaling is done by the base class + +RECT CDrawImage::ScaleSourceRect(const RECT *pSource) +{ + ASSERT(pSource); + return *pSource; +} + + +// This is called when the funky output pin uses our allocator. The samples we +// allocate are special because the memory is shared between us and GDI thus +// removing one copy when we ask for the image to be rendered. The source type +// information is in the main renderer m_mtIn field which is initialised when +// the media type is agreed in SetMediaType, the media type may be changed on +// the fly if, for example, the source filter needs to change the palette + +void CDrawImage::FastRender(IMediaSample *pMediaSample) +{ + BITMAPINFOHEADER *pbmi; // Image format data + DIBDATA *pDibData; // Stores DIB information + BYTE *pImage; // Pointer to image data + HBITMAP hOldBitmap; // Store the old bitmap + CImageSample *pSample; // Pointer to C++ object + + ASSERT(m_pMediaType); + + // From the untyped source format block get the VIDEOINFO and subsequently + // the BITMAPINFOHEADER structure. We can cast the IMediaSample interface + // to a CImageSample object so we can retrieve it's DIBSECTION details + + pbmi = HEADER(m_pMediaType->Format()); + pSample = (CImageSample *) pMediaSample; + pDibData = pSample->GetDIBData(); + hOldBitmap = (HBITMAP) SelectObject(m_MemoryDC,pDibData->hBitmap); + + // Get a pointer to the real image data + + HRESULT hr = pMediaSample->GetPointer(&pImage); + if (FAILED(hr)) { + return; + } + + // Do we need to update the colour table, we increment our palette cookie + // each time we get a dynamic format change. The sample palette cookie is + // stored in the DIBDATA structure so we try to keep the fields in sync + // By the time we get to draw the images the format change will be done + // so all we do is ask the renderer for what it's palette version is + + if (pDibData->PaletteVersion < GetPaletteVersion()) { + ASSERT(pbmi->biBitCount <= iPALETTE); + UpdateColourTable(m_MemoryDC,pbmi); + pDibData->PaletteVersion = GetPaletteVersion(); + } + + // This allows derived classes to change the source rectangle that we do + // the drawing with. For example a renderer may ask a codec to stretch + // the video from 320x240 to 640x480, in which case the source we see in + // here will still be 320x240, although the source we want to draw with + // should be scaled up to 640x480. The base class implementation of this + // method does nothing but return the same rectangle as we are passed in + + RECT SourceRect = ScaleSourceRect(&m_SourceRect); + + // Is the window the same size as the video + + if (m_bStretch == FALSE) { + + // Put the image straight into the window + + BitBlt( + (HDC) m_hdc, // Target device HDC + m_TargetRect.left, // X sink position + m_TargetRect.top, // Y sink position + m_TargetRect.right - m_TargetRect.left, // Destination width + m_TargetRect.bottom - m_TargetRect.top, // Destination height + m_MemoryDC, // Source device context + SourceRect.left, // X source position + SourceRect.top, // Y source position + SRCCOPY); // Simple copy + + } else { + + // Stretch the image when copying to the window + + StretchBlt( + (HDC) m_hdc, // Target device HDC + m_TargetRect.left, // X sink position + m_TargetRect.top, // Y sink position + m_TargetRect.right - m_TargetRect.left, // Destination width + m_TargetRect.bottom - m_TargetRect.top, // Destination height + m_MemoryDC, // Source device HDC + SourceRect.left, // X source position + SourceRect.top, // Y source position + SourceRect.right - SourceRect.left, // Source width + SourceRect.bottom - SourceRect.top, // Source height + SRCCOPY); // Simple copy + } + + // This displays the sample times over the top of the image. This used to + // draw the times into the offscreen device context however that actually + // writes the text into the image data buffer which may not be writable + + #ifdef DEBUG + DisplaySampleTimes(pMediaSample); + #endif + + // Put the old bitmap back into the device context so we don't leak + SelectObject(m_MemoryDC,hOldBitmap); +} + + +// This is called when there is a sample ready to be drawn, unfortunately the +// output pin was being rotten and didn't choose our super excellent shared +// memory DIB allocator so we have to do this slow render using boring old GDI +// SetDIBitsToDevice and StretchDIBits. The down side of using these GDI +// functions is that the image data has to be copied across from our address +// space into theirs before going to the screen (although in reality the cost +// is small because all they do is to map the buffer into their address space) + +void CDrawImage::SlowRender(IMediaSample *pMediaSample) +{ + // Get the BITMAPINFOHEADER for the connection + + ASSERT(m_pMediaType); + BITMAPINFOHEADER *pbmi = HEADER(m_pMediaType->Format()); + BYTE *pImage; + + // Get the image data buffer + + HRESULT hr = pMediaSample->GetPointer(&pImage); + if (FAILED(hr)) { + return; + } + + // This allows derived classes to change the source rectangle that we do + // the drawing with. For example a renderer may ask a codec to stretch + // the video from 320x240 to 640x480, in which case the source we see in + // here will still be 320x240, although the source we want to draw with + // should be scaled up to 640x480. The base class implementation of this + // method does nothing but return the same rectangle as we are passed in + + RECT SourceRect = ScaleSourceRect(&m_SourceRect); + + LONG lAdjustedSourceTop = SourceRect.top; + // if the origin of bitmap is bottom-left, adjust soruce_rect_top + // to be the bottom-left corner instead of the top-left. + if (pbmi->biHeight > 0) { + lAdjustedSourceTop = pbmi->biHeight - SourceRect.bottom; + } + // Is the window the same size as the video + + if (m_bStretch == FALSE) { + + // Put the image straight into the window + + SetDIBitsToDevice( + (HDC) m_hdc, // Target device HDC + m_TargetRect.left, // X sink position + m_TargetRect.top, // Y sink position + m_TargetRect.right - m_TargetRect.left, // Destination width + m_TargetRect.bottom - m_TargetRect.top, // Destination height + SourceRect.left, // X source position + lAdjustedSourceTop, // Adjusted Y source position + (UINT) 0, // Start scan line + pbmi->biHeight, // Scan lines present + pImage, // Image data + (BITMAPINFO *) pbmi, // DIB header + DIB_RGB_COLORS); // Type of palette + + } else { + + // Stretch the image when copying to the window + + StretchDIBits( + (HDC) m_hdc, // Target device HDC + m_TargetRect.left, // X sink position + m_TargetRect.top, // Y sink position + m_TargetRect.right - m_TargetRect.left, // Destination width + m_TargetRect.bottom - m_TargetRect.top, // Destination height + SourceRect.left, // X source position + lAdjustedSourceTop, // Adjusted Y source position + SourceRect.right - SourceRect.left, // Source width + SourceRect.bottom - SourceRect.top, // Source height + pImage, // Image data + (BITMAPINFO *) pbmi, // DIB header + DIB_RGB_COLORS, // Type of palette + SRCCOPY); // Simple image copy + } + + // This shows the sample reference times over the top of the image which + // looks a little flickery. I tried using GdiSetBatchLimit and GdiFlush to + // control the screen updates but it doesn't quite work as expected and + // only partially reduces the flicker. I also tried using a memory context + // and combining the two in that before doing a final BitBlt operation to + // the screen, unfortunately this has considerable performance penalties + // and also means that this code is not executed when compiled retail + + #ifdef DEBUG + DisplaySampleTimes(pMediaSample); + #endif +} + + +// This is called with an IMediaSample interface on the image to be drawn. We +// decide on the drawing mechanism based on who's allocator we are using. We +// may be called when the window wants an image painted by WM_PAINT messages +// We can't realise the palette here because we have the renderer lock, any +// call to realise may cause an interthread send message to the window thread +// which may in turn be waiting to get the renderer lock before servicing it + +BOOL CDrawImage::DrawImage(IMediaSample *pMediaSample) +{ + ASSERT(m_hdc); + ASSERT(m_MemoryDC); + NotifyStartDraw(); + + // If the output pin used our allocator then the samples passed are in + // fact CVideoSample objects that contain CreateDIBSection data that we + // use to do faster image rendering, they may optionally also contain a + // DirectDraw surface pointer in which case we do not do the drawing + + if (m_bUsingImageAllocator == FALSE) { + SlowRender(pMediaSample); + EXECUTE_ASSERT(GdiFlush()); + NotifyEndDraw(); + return TRUE; + } + + // This is a DIBSECTION buffer + + FastRender(pMediaSample); + EXECUTE_ASSERT(GdiFlush()); + NotifyEndDraw(); + return TRUE; +} + + +BOOL CDrawImage::DrawVideoImageHere( + HDC hdc, + IMediaSample *pMediaSample, + LPRECT lprcSrc, + LPRECT lprcDst + ) +{ + ASSERT(m_pMediaType); + BITMAPINFOHEADER *pbmi = HEADER(m_pMediaType->Format()); + BYTE *pImage; + + // Get the image data buffer + + HRESULT hr = pMediaSample->GetPointer(&pImage); + if (FAILED(hr)) { + return FALSE; + } + + RECT SourceRect; + RECT TargetRect; + + if (lprcSrc) { + SourceRect = *lprcSrc; + } + else SourceRect = ScaleSourceRect(&m_SourceRect); + + if (lprcDst) { + TargetRect = *lprcDst; + } + else TargetRect = m_TargetRect; + + LONG lAdjustedSourceTop = SourceRect.top; + // if the origin of bitmap is bottom-left, adjust soruce_rect_top + // to be the bottom-left corner instead of the top-left. + if (pbmi->biHeight > 0) { + lAdjustedSourceTop = pbmi->biHeight - SourceRect.bottom; + } + + + // Stretch the image when copying to the DC + + BOOL bRet = (0 != StretchDIBits(hdc, + TargetRect.left, + TargetRect.top, + TargetRect.right - TargetRect.left, + TargetRect.bottom - TargetRect.top, + SourceRect.left, + lAdjustedSourceTop, + SourceRect.right - SourceRect.left, + SourceRect.bottom - SourceRect.top, + pImage, + (BITMAPINFO *)pbmi, + DIB_RGB_COLORS, + SRCCOPY)); + return bRet; +} + + +// This is called by the owning window object after it has created the window +// and it's drawing contexts. We are constructed with the base window we'll +// be drawing into so when given the notification we retrive the device HDCs +// to draw with. We cannot call these in our constructor as they are virtual + +void CDrawImage::SetDrawContext() +{ + m_MemoryDC = m_pBaseWindow->GetMemoryHDC(); + m_hdc = m_pBaseWindow->GetWindowHDC(); +} + + +// This is called to set the target rectangle in the video window, it will be +// called whenever a WM_SIZE message is retrieved from the message queue. We +// simply store the rectangle and use it later when we do the drawing calls + +void CDrawImage::SetTargetRect(RECT *pTargetRect) +{ + ASSERT(pTargetRect); + m_TargetRect = *pTargetRect; + SetStretchMode(); +} + + +// Return the current target rectangle + +void CDrawImage::GetTargetRect(RECT *pTargetRect) +{ + ASSERT(pTargetRect); + *pTargetRect = m_TargetRect; +} + + +// This is called when we want to change the section of the image to draw. We +// use this information in the drawing operation calls later on. We must also +// see if the source and destination rectangles have the same dimensions. If +// not we must stretch during the drawing rather than a direct pixel copy + +void CDrawImage::SetSourceRect(RECT *pSourceRect) +{ + ASSERT(pSourceRect); + m_SourceRect = *pSourceRect; + SetStretchMode(); +} + + +// Return the current source rectangle + +void CDrawImage::GetSourceRect(RECT *pSourceRect) +{ + ASSERT(pSourceRect); + *pSourceRect = m_SourceRect; +} + + +// This is called when either the source or destination rectanges change so we +// can update the stretch flag. If the rectangles don't match we stretch the +// video during the drawing otherwise we call the fast pixel copy functions +// NOTE the source and/or the destination rectangle may be completely empty + +void CDrawImage::SetStretchMode() +{ + // Calculate the overall rectangle dimensions + + LONG SourceWidth = m_SourceRect.right - m_SourceRect.left; + LONG SinkWidth = m_TargetRect.right - m_TargetRect.left; + LONG SourceHeight = m_SourceRect.bottom - m_SourceRect.top; + LONG SinkHeight = m_TargetRect.bottom - m_TargetRect.top; + + m_bStretch = TRUE; + if (SourceWidth == SinkWidth) { + if (SourceHeight == SinkHeight) { + m_bStretch = FALSE; + } + } +} + + +// Tell us whose allocator we are using. This should be called with TRUE if +// the filter agrees to use an allocator based around the CImageAllocator +// SDK base class - whose image buffers are made through CreateDIBSection. +// Otherwise this should be called with FALSE and we will draw the images +// using SetDIBitsToDevice and StretchDIBitsToDevice. None of these calls +// can handle buffers which have non zero strides (like DirectDraw uses) + +void CDrawImage::NotifyAllocator(BOOL bUsingImageAllocator) +{ + m_bUsingImageAllocator = bUsingImageAllocator; +} + + +// Are we using the image DIBSECTION allocator + +BOOL CDrawImage::UsingImageAllocator() +{ + return m_bUsingImageAllocator; +} + + +// We need the media type of the connection so that we can get the BITMAPINFO +// from it. We use that in the calls to draw the image such as StretchDIBits +// and also when updating the colour table held in shared memory DIBSECTIONs + +void CDrawImage::NotifyMediaType(CMediaType *pMediaType) +{ + m_pMediaType = pMediaType; +} + + +// We store in this object a cookie maintaining the current palette version. +// Each time a palettised format is changed we increment this value so that +// when we come to draw the images we look at the colour table value they +// have and if less than the current we know to update it. This version is +// only needed and indeed used when working with shared memory DIBSECTIONs + +LONG CDrawImage::GetPaletteVersion() +{ + return m_PaletteVersion; +} + + +// Resets the current palette version number + +void CDrawImage::ResetPaletteVersion() +{ + m_PaletteVersion = PALETTE_VERSION; +} + + +// Increment the current palette version + +void CDrawImage::IncrementPaletteVersion() +{ + m_PaletteVersion++; +} + + +// Constructor must initialise the base allocator. Each sample we create has a +// palette version cookie on board. When the source filter changes the palette +// during streaming the window object increments an internal cookie counter it +// keeps as well. When it comes to render the samples it looks at the cookie +// values and if they don't match then it knows to update the sample's colour +// table. However we always create samples with a cookie of PALETTE_VERSION +// If there have been multiple format changes and we disconnect and reconnect +// thereby causing the samples to be reallocated we will create them with a +// cookie much lower than the current version, this isn't a problem since it +// will be seen by the window object and the versions will then be updated + +CImageAllocator::CImageAllocator(CBaseFilter *pFilter, + TCHAR *pName, + HRESULT *phr) : + CBaseAllocator(pName,NULL,phr,TRUE,TRUE), + m_pFilter(pFilter) +{ + ASSERT(phr); + ASSERT(pFilter); +} + + +// Check our DIB buffers have been released + +#ifdef DEBUG +CImageAllocator::~CImageAllocator() +{ + ASSERT(m_bCommitted == FALSE); +} +#endif + + +// Called from destructor and also from base class to free resources. We work +// our way through the list of media samples deleting the DIBSECTION created +// for each. All samples should be back in our list so there is no chance a +// filter is still using one to write on the display or hold on a pending list + +void CImageAllocator::Free() +{ + ASSERT(m_lAllocated == m_lFree.GetCount()); + EXECUTE_ASSERT(GdiFlush()); + CImageSample *pSample; + DIBDATA *pDibData; + + while (m_lFree.GetCount() != 0) { + pSample = (CImageSample *) m_lFree.RemoveHead(); + pDibData = pSample->GetDIBData(); + EXECUTE_ASSERT(DeleteObject(pDibData->hBitmap)); + EXECUTE_ASSERT(CloseHandle(pDibData->hMapping)); + delete pSample; + } + + m_lAllocated = 0; +} + + +// Prepare the allocator by checking all the input parameters + +STDMETHODIMP CImageAllocator::CheckSizes(ALLOCATOR_PROPERTIES *pRequest) +{ + // Check we have a valid connection + + if (m_pMediaType == NULL) { + return VFW_E_NOT_CONNECTED; + } + + // NOTE We always create a DIB section with the source format type which + // may contain a source palette. When we do the BitBlt drawing operation + // the target display device may contain a different palette (we may not + // have the focus) in which case GDI will do after the palette mapping + + VIDEOINFOHEADER *pVideoInfo = (VIDEOINFOHEADER *) m_pMediaType->Format(); + + // When we call CreateDIBSection it implicitly maps only enough memory + // for the image as defined by thee BITMAPINFOHEADER. If the user asks + // for an image smaller than this then we reject the call, if they ask + // for an image larger than this then we return what they can have + + if ((DWORD) pRequest->cbBuffer < pVideoInfo->bmiHeader.biSizeImage) { + return E_INVALIDARG; + } + + // Reject buffer prefixes + + if (pRequest->cbPrefix > 0) { + return E_INVALIDARG; + } + + pRequest->cbBuffer = pVideoInfo->bmiHeader.biSizeImage; + return NOERROR; +} + + +// Agree the number of media sample buffers and their sizes. The base class +// this allocator is derived from allows samples to be aligned only on byte +// boundaries NOTE the buffers are not allocated until the Commit call + +STDMETHODIMP CImageAllocator::SetProperties( + ALLOCATOR_PROPERTIES * pRequest, + ALLOCATOR_PROPERTIES * pActual) +{ + ALLOCATOR_PROPERTIES Adjusted = *pRequest; + + // Check the parameters fit with the current connection + + HRESULT hr = CheckSizes(&Adjusted); + if (FAILED(hr)) { + return hr; + } + return CBaseAllocator::SetProperties(&Adjusted, pActual); +} + + +// Commit the memory by allocating the agreed number of media samples. For +// each sample we are committed to creating we have a CImageSample object +// that we use to manage it's resources. This is initialised with a DIBDATA +// structure that contains amongst other things the GDI DIBSECTION handle +// We will access the renderer media type during this so we must have locked +// (to prevent the format changing for example). The class overrides Commit +// and Decommit to do this locking (base class Commit in turn calls Alloc) + +HRESULT CImageAllocator::Alloc(void) +{ + ASSERT(m_pMediaType); + CImageSample *pSample; + DIBDATA DibData; + + // Check the base allocator says it's ok to continue + + HRESULT hr = CBaseAllocator::Alloc(); + if (FAILED(hr)) { + return hr; + } + + // We create a new memory mapped object although we don't map it into our + // address space because GDI does that in CreateDIBSection. It is possible + // that we run out of resources before creating all the samples in which + // case the available sample list is left with those already created + + ASSERT(m_lAllocated == 0); + while (m_lAllocated < m_lCount) { + + // Create and initialise a shared memory GDI buffer + + HRESULT hr = CreateDIB(m_lSize,DibData); + if (FAILED(hr)) { + return hr; + } + + // Create the sample object and pass it the DIBDATA + + pSample = CreateImageSample(DibData.pBase,m_lSize); + if (pSample == NULL) { + EXECUTE_ASSERT(DeleteObject(DibData.hBitmap)); + EXECUTE_ASSERT(CloseHandle(DibData.hMapping)); + return E_OUTOFMEMORY; + } + + // Add the completed sample to the available list + + pSample->SetDIBData(&DibData); + m_lFree.Add(pSample); + m_lAllocated++; + } + return NOERROR; +} + + +// We have a virtual method that allocates the samples so that a derived class +// may override it and allocate more specialised sample objects. So long as it +// derives its samples from CImageSample then all this code will still work ok + +CImageSample *CImageAllocator::CreateImageSample(LPBYTE pData,LONG Length) +{ + HRESULT hr = NOERROR; + CImageSample *pSample; + + // Allocate the new sample and check the return codes + + pSample = new CImageSample((CBaseAllocator *) this, // Base class + NAME("Video sample"), // DEBUG name + (HRESULT *) &hr, // Return code + (LPBYTE) pData, // DIB address + (LONG) Length); // Size of DIB + + if (pSample == NULL || FAILED(hr)) { + delete pSample; + return NULL; + } + return pSample; +} + + +// This function allocates a shared memory block for use by the source filter +// generating DIBs for us to render. The memory block is created in shared +// memory so that GDI doesn't have to copy the memory when we do a BitBlt + +HRESULT CImageAllocator::CreateDIB(LONG InSize,DIBDATA &DibData) +{ + BITMAPINFO *pbmi; // Format information for pin + BYTE *pBase; // Pointer to the actual image + HANDLE hMapping; // Handle to mapped object + HBITMAP hBitmap; // DIB section bitmap handle + + // Create a file mapping object and map into our address space + + hMapping = CreateFileMapping(hMEMORY, // Use system page file + NULL, // No security attributes + PAGE_READWRITE, // Full access to memory + (DWORD) 0, // Less than 4Gb in size + InSize, // Size of buffer + NULL); // No name to section + if (hMapping == NULL) { + DWORD Error = GetLastError(); + return MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, Error); + } + + // NOTE We always create a DIB section with the source format type which + // may contain a source palette. When we do the BitBlt drawing operation + // the target display device may contain a different palette (we may not + // have the focus) in which case GDI will do after the palette mapping + + pbmi = (BITMAPINFO *) HEADER(m_pMediaType->Format()); + if (m_pMediaType == NULL) { + DbgBreak("Invalid media type"); + } + + hBitmap = CreateDIBSection((HDC) NULL, // NO device context + pbmi, // Format information + DIB_RGB_COLORS, // Use the palette + (VOID **) &pBase, // Pointer to image data + hMapping, // Mapped memory handle + (DWORD) 0); // Offset into memory + + if (hBitmap == NULL || pBase == NULL) { + EXECUTE_ASSERT(CloseHandle(hMapping)); + DWORD Error = GetLastError(); + return MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, Error); + } + + // Initialise the DIB information structure + + DibData.hBitmap = hBitmap; + DibData.hMapping = hMapping; + DibData.pBase = pBase; + DibData.PaletteVersion = PALETTE_VERSION; + GetObject(hBitmap,sizeof(DIBSECTION),(VOID *)&DibData.DibSection); + + return NOERROR; +} + + +// We use the media type during the DIBSECTION creation + +void CImageAllocator::NotifyMediaType(CMediaType *pMediaType) +{ + m_pMediaType = pMediaType; +} + + +// Overriden to increment the owning object's reference count + +STDMETHODIMP_(ULONG) CImageAllocator::NonDelegatingAddRef() +{ + return m_pFilter->AddRef(); +} + + +// Overriden to decrement the owning object's reference count + +STDMETHODIMP_(ULONG) CImageAllocator::NonDelegatingRelease() +{ + return m_pFilter->Release(); +} + + +// If you derive a class from CMediaSample that has to transport specialised +// member variables and entry points then there are three alternate solutions +// The first is to create a memory buffer larger than actually required by the +// sample and store your information either at the beginning of it or at the +// end, the former being moderately safer allowing for misbehaving transform +// filters. You then adjust the buffer address when you create the base media +// sample. This has the disadvantage of breaking up the memory allocated to +// the samples into separate blocks. The second solution is to implement a +// class derived from CMediaSample and support additional interface(s) that +// convey your private data. This means defining a custom interface. The final +// alternative is to create a class that inherits from CMediaSample and adds +// the private data structures, when you get an IMediaSample in your Receive() +// call check to see if your allocator is being used, and if it is then cast +// the IMediaSample into one of your objects. Additional checks can be made +// to ensure the sample's this pointer is known to be one of your own objects + +CImageSample::CImageSample(CBaseAllocator *pAllocator, + TCHAR *pName, + HRESULT *phr, + LPBYTE pBuffer, + LONG length) : + CMediaSample(pName,pAllocator,phr,pBuffer,length), + m_bInit(FALSE) +{ + ASSERT(pAllocator); + ASSERT(pBuffer); +} + + +// Set the shared memory DIB information + +void CImageSample::SetDIBData(DIBDATA *pDibData) +{ + ASSERT(pDibData); + m_DibData = *pDibData; + m_bInit = TRUE; +} + + +// Retrieve the shared memory DIB data + +DIBDATA *CImageSample::GetDIBData() +{ + ASSERT(m_bInit == TRUE); + return &m_DibData; +} + + +// This class handles the creation of a palette. It is fairly specialist and +// is intended to simplify palette management for video renderer filters. It +// is for this reason that the constructor requires three other objects with +// which it interacts, namely a base media filter, a base window and a base +// drawing object although the base window or the draw object may be NULL to +// ignore that part of us. We try not to create and install palettes unless +// absolutely necessary as they typically require WM_PALETTECHANGED messages +// to be sent to every window thread in the system which is very expensive + +CImagePalette::CImagePalette(CBaseFilter *pBaseFilter, + CBaseWindow *pBaseWindow, + CDrawImage *pDrawImage) : + m_pBaseWindow(pBaseWindow), + m_pFilter(pBaseFilter), + m_pDrawImage(pDrawImage), + m_hPalette(NULL) +{ + ASSERT(m_pFilter); +} + + +// Destructor + +#ifdef DEBUG +CImagePalette::~CImagePalette() +{ + ASSERT(m_hPalette == NULL); +} +#endif + + +// We allow dynamic format changes of the palette but rather than change the +// palette every time we call this to work out whether an update is required. +// If the original type didn't use a palette and the new one does (or vica +// versa) then we return TRUE. If neither formats use a palette we'll return +// FALSE. If both formats use a palette we compare their colours and return +// FALSE if they match. This therefore short circuits palette creation unless +// absolutely necessary since installing palettes is an expensive operation + +BOOL CImagePalette::ShouldUpdate(const VIDEOINFOHEADER *pNewInfo, + const VIDEOINFOHEADER *pOldInfo) +{ + // We may not have a current format yet + + if (pOldInfo == NULL) { + return TRUE; + } + + // Do both formats not require a palette + + if (ContainsPalette(pNewInfo) == FALSE) { + if (ContainsPalette(pOldInfo) == FALSE) { + return FALSE; + } + } + + // Compare the colours to see if they match + + DWORD VideoEntries = pNewInfo->bmiHeader.biClrUsed; + if (ContainsPalette(pNewInfo) == TRUE) + if (ContainsPalette(pOldInfo) == TRUE) + if (pOldInfo->bmiHeader.biClrUsed == VideoEntries) + if (pOldInfo->bmiHeader.biClrUsed > 0) + if (memcmp((PVOID) GetBitmapPalette(pNewInfo), + (PVOID) GetBitmapPalette(pOldInfo), + VideoEntries * sizeof(RGBQUAD)) == 0) { + + return FALSE; + } + return TRUE; +} + + +// This is normally called when the input pin type is set to install a palette +// We will typically be called from two different places. The first is when we +// have negotiated a palettised media type after connection, the other is when +// we receive a new type during processing with an updated palette in which +// case we must remove and release the resources held by the current palette + +// We can be passed an optional device name if we wish to prepare a palette +// for a specific monitor on a multi monitor system + +HRESULT CImagePalette::PreparePalette(const CMediaType *pmtNew, + const CMediaType *pmtOld, + LPSTR szDevice) +{ + const VIDEOINFOHEADER *pNewInfo = (VIDEOINFOHEADER *) pmtNew->Format(); + const VIDEOINFOHEADER *pOldInfo = (VIDEOINFOHEADER *) pmtOld->Format(); + ASSERT(pNewInfo); + + // This is an performance optimisation, when we get a media type we check + // to see if the format requires a palette change. If either we need one + // when previously we didn't or vica versa then this returns TRUE, if we + // previously needed a palette and we do now it compares their colours + + if (ShouldUpdate(pNewInfo,pOldInfo) == FALSE) { + NOTE("No update needed"); + return S_FALSE; + } + + // We must notify the filter graph that the application may have changed + // the palette although in practice we don't bother checking to see if it + // is really different. If it tries to get the palette either the window + // or renderer lock will ensure it doesn't get in until we are finished + + RemovePalette(); + m_pFilter->NotifyEvent(EC_PALETTE_CHANGED,0,0); + + // Do we need a palette for the new format + + if (ContainsPalette(pNewInfo) == FALSE) { + NOTE("New has no palette"); + return S_FALSE; + } + + if (m_pBaseWindow) { + m_pBaseWindow->LockPaletteLock(); + } + + // If we're changing the palette on the fly then we increment our palette + // cookie which is compared against the cookie also stored in all of our + // DIBSECTION media samples. If they don't match when we come to draw it + // then we know the sample is out of date and we'll update it's palette + + NOTE("Making new colour palette"); + m_hPalette = MakePalette(pNewInfo, szDevice); + ASSERT(m_hPalette != NULL); + + if (m_pBaseWindow) { + m_pBaseWindow->UnlockPaletteLock(); + } + + // The window in which the new palette is to be realised may be a NULL + // pointer to signal that no window is in use, if so we don't call it + // Some filters just want to use this object to create/manage palettes + + if (m_pBaseWindow) m_pBaseWindow->SetPalette(m_hPalette); + + // This is the only time where we need access to the draw object to say + // to it that a new palette will be arriving on a sample real soon. The + // constructor may take a NULL pointer in which case we don't call this + + if (m_pDrawImage) m_pDrawImage->IncrementPaletteVersion(); + return NOERROR; +} + + +// Helper function to copy a palette out of any kind of VIDEOINFO (ie it may +// be YUV or true colour) into a palettised VIDEOINFO. We use this changing +// palettes on DirectDraw samples as a source filter can attach a palette to +// any buffer (eg YUV) and hand it back. We make a new palette out of that +// format and then copy the palette colours into the current connection type + +HRESULT CImagePalette::CopyPalette(const CMediaType *pSrc,CMediaType *pDest) +{ + // Reset the destination palette before starting + + VIDEOINFOHEADER *pDestInfo = (VIDEOINFOHEADER *) pDest->Format(); + pDestInfo->bmiHeader.biClrUsed = 0; + pDestInfo->bmiHeader.biClrImportant = 0; + + // Does the destination have a palette + + if (PALETTISED(pDestInfo) == FALSE) { + NOTE("No destination palette"); + return S_FALSE; + } + + // Does the source contain a palette + + const VIDEOINFOHEADER *pSrcInfo = (VIDEOINFOHEADER *) pSrc->Format(); + if (ContainsPalette(pSrcInfo) == FALSE) { + NOTE("No source palette"); + return S_FALSE; + } + + // The number of colours may be zero filled + + DWORD PaletteEntries = pSrcInfo->bmiHeader.biClrUsed; + if (PaletteEntries == 0) { + DWORD Maximum = (1 << pSrcInfo->bmiHeader.biBitCount); + NOTE1("Setting maximum colours (%d)",Maximum); + PaletteEntries = Maximum; + } + + // Make sure the destination has enough room for the palette + + ASSERT(pSrcInfo->bmiHeader.biClrUsed <= iPALETTE_COLORS); + ASSERT(pSrcInfo->bmiHeader.biClrImportant <= PaletteEntries); + ASSERT(COLORS(pDestInfo) == GetBitmapPalette(pDestInfo)); + pDestInfo->bmiHeader.biClrUsed = PaletteEntries; + pDestInfo->bmiHeader.biClrImportant = pSrcInfo->bmiHeader.biClrImportant; + ULONG BitmapSize = GetBitmapFormatSize(HEADER(pSrcInfo)); + + if (pDest->FormatLength() < BitmapSize) { + NOTE("Reallocating destination"); + pDest->ReallocFormatBuffer(BitmapSize); + } + + // Now copy the palette colours across + + CopyMemory((PVOID) COLORS(pDestInfo), + (PVOID) GetBitmapPalette(pSrcInfo), + PaletteEntries * sizeof(RGBQUAD)); + + return NOERROR; +} + + +// This is normally called when the palette is changed (typically during a +// dynamic format change) to remove any palette we previously installed. We +// replace it (if necessary) in the video window with a standard VGA palette +// that should always be available even if this is a true colour display + +HRESULT CImagePalette::RemovePalette() +{ + if (m_pBaseWindow) { + m_pBaseWindow->LockPaletteLock(); + } + + // Do we have a palette to remove + + if (m_hPalette != NULL) { + + if (m_pBaseWindow) { + // Make sure that the window's palette handle matches + // our palette handle. + ASSERT(m_hPalette == m_pBaseWindow->GetPalette()); + + m_pBaseWindow->UnsetPalette(); + } + + EXECUTE_ASSERT(DeleteObject(m_hPalette)); + m_hPalette = NULL; + } + + if (m_pBaseWindow) { + m_pBaseWindow->UnlockPaletteLock(); + } + + return NOERROR; +} + + +// Called to create a palette for the object, the data structure used by GDI +// to describe a palette is a LOGPALETTE, this includes a variable number of +// PALETTEENTRY fields which are the colours, we have to convert the RGBQUAD +// colour fields we are handed in a BITMAPINFO from the media type into these +// This handles extraction of palettes from true colour and YUV media formats + +// We can be passed an optional device name if we wish to prepare a palette +// for a specific monitor on a multi monitor system + +HPALETTE CImagePalette::MakePalette(const VIDEOINFOHEADER *pVideoInfo, LPSTR szDevice) +{ + ASSERT(ContainsPalette(pVideoInfo) == TRUE); + ASSERT(pVideoInfo->bmiHeader.biClrUsed <= iPALETTE_COLORS); + BITMAPINFOHEADER *pHeader = HEADER(pVideoInfo); + + const RGBQUAD *pColours; // Pointer to the palette + LOGPALETTE *lp; // Used to create a palette + HPALETTE hPalette; // Logical palette object + + lp = (LOGPALETTE *) new BYTE[sizeof(LOGPALETTE) + SIZE_PALETTE]; + if (lp == NULL) { + return NULL; + } + + // Unfortunately for some hare brained reason a GDI palette entry (a + // PALETTEENTRY structure) is different to a palette entry from a DIB + // format (a RGBQUAD structure) so we have to do the field conversion + // The VIDEOINFO containing the palette may be a true colour type so + // we use GetBitmapPalette to skip over any bit fields if they exist + + lp->palVersion = PALVERSION; + lp->palNumEntries = (USHORT) pHeader->biClrUsed; + if (lp->palNumEntries == 0) lp->palNumEntries = (1 << pHeader->biBitCount); + pColours = GetBitmapPalette(pVideoInfo); + + for (DWORD dwCount = 0;dwCount < lp->palNumEntries;dwCount++) { + lp->palPalEntry[dwCount].peRed = pColours[dwCount].rgbRed; + lp->palPalEntry[dwCount].peGreen = pColours[dwCount].rgbGreen; + lp->palPalEntry[dwCount].peBlue = pColours[dwCount].rgbBlue; + lp->palPalEntry[dwCount].peFlags = 0; + } + + MakeIdentityPalette(lp->palPalEntry, lp->palNumEntries, szDevice); + + // Create a logical palette + + hPalette = CreatePalette(lp); + ASSERT(hPalette != NULL); + delete[] lp; + return hPalette; +} + + +// GDI does a fair job of compressing the palette entries you give it, so for +// example if you have five entries with an RGB colour (0,0,0) it will remove +// all but one of them. When you subsequently draw an image it will map from +// your logical palette to the compressed device palette. This function looks +// to see if it is trying to be an identity palette and if so sets the flags +// field in the PALETTEENTRYs so they remain expanded to boost performance + +// We can be passed an optional device name if we wish to prepare a palette +// for a specific monitor on a multi monitor system + +HRESULT CImagePalette::MakeIdentityPalette(PALETTEENTRY *pEntry,INT iColours, LPSTR szDevice) +{ + PALETTEENTRY SystemEntries[10]; // System palette entries + BOOL bIdentityPalette = TRUE; // Is an identity palette + ASSERT(iColours <= iPALETTE_COLORS); // Should have a palette + const int PalLoCount = 10; // First ten reserved colours + const int PalHiStart = 246; // Last VGA palette entries + + // Does this have the full colour range + + if (iColours < 10) { + return S_FALSE; + } + + // Apparently some displays have odd numbers of system colours + + // Get a DC on the right monitor - it's ugly, but this is the way you have + // to do it + HDC hdc; + if (szDevice == NULL || lstrcmpiA(szDevice, "DISPLAY") == 0) + hdc = CreateDCA("DISPLAY", NULL, NULL, NULL); + else + hdc = CreateDCA(NULL, szDevice, NULL, NULL); + if (NULL == hdc) { + return E_OUTOFMEMORY; + } + INT Reserved = GetDeviceCaps(hdc,NUMRESERVED); + if (Reserved != 20) { + DeleteDC(hdc); + return S_FALSE; + } + + // Compare our palette against the first ten system entries. The reason I + // don't do a memory compare between our two arrays of colours is because + // I am not sure what will be in the flags fields for the system entries + + UINT Result = GetSystemPaletteEntries(hdc,0,PalLoCount,SystemEntries); + UINT Count = 0; + for (Count = 0;Count < Result;Count++) { + if (SystemEntries[Count].peRed != pEntry[Count].peRed || + SystemEntries[Count].peGreen != pEntry[Count].peGreen || + SystemEntries[Count].peBlue != pEntry[Count].peBlue) { + bIdentityPalette = FALSE; + } + } + + // And likewise compare against the last ten entries + + Result = GetSystemPaletteEntries(hdc,PalHiStart,PalLoCount,SystemEntries); + for (Count = 0;Count < Result;Count++) { + if (INT(Count) + PalHiStart < iColours) { + if (SystemEntries[Count].peRed != pEntry[PalHiStart + Count].peRed || + SystemEntries[Count].peGreen != pEntry[PalHiStart + Count].peGreen || + SystemEntries[Count].peBlue != pEntry[PalHiStart + Count].peBlue) { + bIdentityPalette = FALSE; + } + } + } + + // If not an identity palette then return S_FALSE + + DeleteDC(hdc); + if (bIdentityPalette == FALSE) { + return S_FALSE; + } + + // Set the non VGA entries so that GDI doesn't map them + + for (Count = PalLoCount;INT(Count) < min(PalHiStart,iColours);Count++) { + pEntry[Count].peFlags = PC_NOCOLLAPSE; + } + return NOERROR; +} + + +// Constructor initialises the VIDEOINFO we keep storing the current display +// format. The format can be changed at any time, to reset the format held +// by us call the RefreshDisplayType directly (it's a public method). Since +// more than one thread will typically call us (ie window threads resetting +// the type and source threads in the type checking methods) we have a lock + +CImageDisplay::CImageDisplay() +{ + RefreshDisplayType(NULL); +} + + + +// This initialises the format we hold which contains the display device type +// We do a conversion on the display device type in here so that when we start +// type checking input formats we can assume that certain fields have been set +// correctly, an example is when we make the 16 bit mask fields explicit. This +// is normally called when we receive WM_DEVMODECHANGED device change messages + +// The optional szDeviceName parameter tells us which monitor we are interested +// in for a multi monitor system + +HRESULT CImageDisplay::RefreshDisplayType(LPSTR szDeviceName) +{ + CAutoLock cDisplayLock(this); + + // Set the preferred format type + + ZeroMemory((PVOID)&m_Display,sizeof(VIDEOINFOHEADER)+sizeof(TRUECOLORINFO)); + m_Display.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + m_Display.bmiHeader.biBitCount = FALSE; + + // Get the bit depth of a device compatible bitmap + + // get caps of whichever monitor they are interested in (multi monitor) + HDC hdcDisplay; + // it's ugly, but this is the way you have to do it + if (szDeviceName == NULL || lstrcmpiA(szDeviceName, "DISPLAY") == 0) + hdcDisplay = CreateDCA("DISPLAY", NULL, NULL, NULL); + else + hdcDisplay = CreateDCA(NULL, szDeviceName, NULL, NULL); + if (hdcDisplay == NULL) { + ASSERT(FALSE); + DbgLog((LOG_ERROR,1,TEXT("ACK! Can't get a DC for %hs"), + szDeviceName ? szDeviceName : "")); + return E_FAIL; + } else { + DbgLog((LOG_TRACE,3,TEXT("Created a DC for %s"), + szDeviceName ? szDeviceName : "")); + } + HBITMAP hbm = CreateCompatibleBitmap(hdcDisplay,1,1); + if ( hbm ) + { + GetDIBits(hdcDisplay,hbm,0,1,NULL,(BITMAPINFO *)&m_Display.bmiHeader,DIB_RGB_COLORS); + + // This call will get the colour table or the proper bitfields + GetDIBits(hdcDisplay,hbm,0,1,NULL,(BITMAPINFO *)&m_Display.bmiHeader,DIB_RGB_COLORS); + DeleteObject(hbm); + } + DeleteDC(hdcDisplay); + + // Complete the display type initialisation + + ASSERT(CheckHeaderValidity(&m_Display)); + UpdateFormat(&m_Display); + DbgLog((LOG_TRACE,3,TEXT("New DISPLAY bit depth =%d"), + m_Display.bmiHeader.biBitCount)); + return NOERROR; +} + + +// We assume throughout this code that any bitfields masks are allowed no +// more than eight bits to store a colour component. This checks that the +// bit count assumption is enforced and also makes sure that all the bits +// set are contiguous. We return a boolean TRUE if the field checks out ok + +BOOL CImageDisplay::CheckBitFields(const VIDEOINFO *pInput) +{ + DWORD *pBitFields = (DWORD *) BITMASKS(pInput); + + for (INT iColour = iRED;iColour <= iBLUE;iColour++) { + + // First of all work out how many bits are set + + DWORD SetBits = CountSetBits(pBitFields[iColour]); + if (SetBits > iMAXBITS || SetBits == 0) { + NOTE1("Bit fields for component %d invalid",iColour); + return FALSE; + } + + // Next work out the number of zero bits prefix + DWORD PrefixBits = CountPrefixBits(pBitFields[iColour]); + + // This is going to see if all the bits set are contiguous (as they + // should be). We know how much to shift them right by from the + // count of prefix bits. The number of bits set defines a mask, we + // invert this (ones complement) and AND it with the shifted bit + // fields. If the result is NON zero then there are bit(s) sticking + // out the left hand end which means they are not contiguous + + DWORD TestField = pBitFields[iColour] >> PrefixBits; + DWORD Mask = ULONG_MAX << SetBits; + if (TestField & Mask) { + NOTE1("Bit fields for component %d not contiguous",iColour); + return FALSE; + } + } + return TRUE; +} + + +// This counts the number of bits set in the input field + +DWORD CImageDisplay::CountSetBits(DWORD Field) +{ + // This is a relatively well known bit counting algorithm + + DWORD Count = 0; + DWORD init = Field; + + // Until the input is exhausted, count the number of bits + + while (init) { + init = init & (init - 1); // Turn off the bottommost bit + Count++; + } + return Count; +} + + +// This counts the number of zero bits upto the first one set NOTE the input +// field should have been previously checked to ensure there is at least one +// set although if we don't find one set we return the impossible value 32 + +DWORD CImageDisplay::CountPrefixBits(DWORD Field) +{ + DWORD Mask = 1; + DWORD Count = 0; + + while (TRUE) { + if (Field & Mask) { + return Count; + } + Count++; + + ASSERT(Mask != 0x80000000); + if (Mask == 0x80000000) { + return Count; + } + Mask <<= 1; + } +} + + +// This is called to check the BITMAPINFOHEADER for the input type. There are +// many implicit dependancies between the fields in a header structure which +// if we validate now make for easier manipulation in subsequent handling. We +// also check that the BITMAPINFOHEADER matches it's specification such that +// fields likes the number of planes is one, that it's structure size is set +// correctly and that the bitmap dimensions have not been set as negative + +BOOL CImageDisplay::CheckHeaderValidity(const VIDEOINFO *pInput) +{ + // Check the bitmap width and height are not negative. + + if (pInput->bmiHeader.biWidth <= 0 || + pInput->bmiHeader.biHeight <= 0) { + NOTE("Invalid bitmap dimensions"); + return FALSE; + } + + // Check the compression is either BI_RGB or BI_BITFIELDS + + if (pInput->bmiHeader.biCompression != BI_RGB) { + if (pInput->bmiHeader.biCompression != BI_BITFIELDS) { + NOTE("Invalid compression format"); + return FALSE; + } + } + + // If BI_BITFIELDS compression format check the colour depth + + if (pInput->bmiHeader.biCompression == BI_BITFIELDS) { + if (pInput->bmiHeader.biBitCount != 16) { + if (pInput->bmiHeader.biBitCount != 32) { + NOTE("BI_BITFIELDS not 16/32 bit depth"); + return FALSE; + } + } + } + + // Check the assumptions about the layout of the bit fields + + if (pInput->bmiHeader.biCompression == BI_BITFIELDS) { + if (CheckBitFields(pInput) == FALSE) { + NOTE("Bit fields are not valid"); + return FALSE; + } + } + + // Are the number of planes equal to one + + if (pInput->bmiHeader.biPlanes != 1) { + NOTE("Number of planes not one"); + return FALSE; + } + + // Check the image size is consistent (it can be zero) + + if (pInput->bmiHeader.biSizeImage != GetBitmapSize(&pInput->bmiHeader)) { + if (pInput->bmiHeader.biSizeImage) { + NOTE("Image size incorrectly set"); + return FALSE; + } + } + + // Check the size of the structure + + if (pInput->bmiHeader.biSize != sizeof(BITMAPINFOHEADER)) { + NOTE("Size of BITMAPINFOHEADER wrong"); + return FALSE; + } + return CheckPaletteHeader(pInput); +} + + +// This runs a few simple tests against the palette fields in the input to +// see if it looks vaguely correct. The tests look at the number of palette +// colours present, the number considered important and the biCompression +// field which should always be BI_RGB as no other formats are meaningful + +BOOL CImageDisplay::CheckPaletteHeader(const VIDEOINFO *pInput) +{ + // The checks here are for palettised videos only + + if (PALETTISED(pInput) == FALSE) { + if (pInput->bmiHeader.biClrUsed) { + NOTE("Invalid palette entries"); + return FALSE; + } + return TRUE; + } + + // Compression type of BI_BITFIELDS is meaningless for palette video + + if (pInput->bmiHeader.biCompression != BI_RGB) { + NOTE("Palettised video must be BI_RGB"); + return FALSE; + } + + // Check the number of palette colours is correct + + if (pInput->bmiHeader.biClrUsed > PALETTE_ENTRIES(pInput)) { + NOTE("Too many colours in palette"); + return FALSE; + } + + // The number of important colours shouldn't exceed the number used + + if (pInput->bmiHeader.biClrImportant > pInput->bmiHeader.biClrUsed) { + NOTE("Too many important colours"); + return FALSE; + } + return TRUE; +} + + +// Return the format of the video display + +const VIDEOINFO *CImageDisplay::GetDisplayFormat() +{ + return &m_Display; +} + + +// Return TRUE if the display uses a palette + +BOOL CImageDisplay::IsPalettised() +{ + return PALETTISED(&m_Display); +} + + +// Return the bit depth of the current display setting + +WORD CImageDisplay::GetDisplayDepth() +{ + return m_Display.bmiHeader.biBitCount; +} + + +// Initialise the optional fields in a VIDEOINFO. These are mainly to do with +// the source and destination rectangles and palette information such as the +// number of colours present. It simplifies our code just a little if we don't +// have to keep checking for all the different valid permutations in a header +// every time we want to do anything with it (an example would be creating a +// palette). We set the base class media type before calling this function so +// that the media types between the pins match after a connection is made + +HRESULT CImageDisplay::UpdateFormat(VIDEOINFO *pVideoInfo) +{ + ASSERT(pVideoInfo); + + BITMAPINFOHEADER *pbmi = HEADER(pVideoInfo); + SetRectEmpty(&pVideoInfo->rcSource); + SetRectEmpty(&pVideoInfo->rcTarget); + + // Set the number of colours explicitly + + if (PALETTISED(pVideoInfo)) { + if (pVideoInfo->bmiHeader.biClrUsed == 0) { + pVideoInfo->bmiHeader.biClrUsed = PALETTE_ENTRIES(pVideoInfo); + } + } + + // The number of important colours shouldn't exceed the number used, on + // some displays the number of important colours is not initialised when + // retrieving the display type so we set the colours used correctly + + if (pVideoInfo->bmiHeader.biClrImportant > pVideoInfo->bmiHeader.biClrUsed) { + pVideoInfo->bmiHeader.biClrImportant = PALETTE_ENTRIES(pVideoInfo); + } + + // Change the image size field to be explicit + + if (pVideoInfo->bmiHeader.biSizeImage == 0) { + pVideoInfo->bmiHeader.biSizeImage = GetBitmapSize(&pVideoInfo->bmiHeader); + } + return NOERROR; +} + + +// Lots of video rendering filters want code to check proposed formats are ok +// This checks the VIDEOINFO we are passed as a media type. If the media type +// is a valid media type then we return NOERROR otherwise E_INVALIDARG. Note +// however we only accept formats that can be easily displayed in the display +// so if we are on a 16 bit device we will not accept 24 bit images. The one +// complexity is that most displays draw 8 bit palettised images efficiently +// Also if the input format is less colour bits per pixel then we also accept + +HRESULT CImageDisplay::CheckVideoType(const VIDEOINFO *pInput) +{ + // First of all check the VIDEOINFOHEADER looks correct + + if (CheckHeaderValidity(pInput) == FALSE) { + return E_INVALIDARG; + } + + // Virtually all devices support palettised images efficiently + + if (m_Display.bmiHeader.biBitCount == pInput->bmiHeader.biBitCount) { + if (PALETTISED(pInput) == TRUE) { + ASSERT(PALETTISED(&m_Display) == TRUE); + NOTE("(Video) Type connection ACCEPTED"); + return NOERROR; + } + } + + + // Is the display depth greater than the input format + + if (m_Display.bmiHeader.biBitCount > pInput->bmiHeader.biBitCount) { + NOTE("(Video) Mismatch agreed"); + return NOERROR; + } + + // Is the display depth less than the input format + + if (m_Display.bmiHeader.biBitCount < pInput->bmiHeader.biBitCount) { + NOTE("(Video) Format mismatch"); + return E_INVALIDARG; + } + + + // Both input and display formats are either BI_RGB or BI_BITFIELDS + + ASSERT(m_Display.bmiHeader.biBitCount == pInput->bmiHeader.biBitCount); + ASSERT(PALETTISED(pInput) == FALSE); + ASSERT(PALETTISED(&m_Display) == FALSE); + + // BI_RGB 16 bit representation is implicitly RGB555, and likewise BI_RGB + // 24 bit representation is RGB888. So we initialise a pointer to the bit + // fields they really mean and check against the display device format + // This is only going to be called when both formats are equal bits pixel + + const DWORD *pInputMask = GetBitMasks(pInput); + const DWORD *pDisplayMask = GetBitMasks((VIDEOINFO *)&m_Display); + + if (pInputMask[iRED] != pDisplayMask[iRED] || + pInputMask[iGREEN] != pDisplayMask[iGREEN] || + pInputMask[iBLUE] != pDisplayMask[iBLUE]) { + + NOTE("(Video) Bit field mismatch"); + return E_INVALIDARG; + } + + NOTE("(Video) Type connection ACCEPTED"); + return NOERROR; +} + + +// Return the bit masks for the true colour VIDEOINFO provided + +const DWORD *CImageDisplay::GetBitMasks(const VIDEOINFO *pVideoInfo) +{ + static const DWORD FailMasks[] = {0,0,0}; + + if (pVideoInfo->bmiHeader.biCompression == BI_BITFIELDS) { + return BITMASKS(pVideoInfo); + } + + ASSERT(pVideoInfo->bmiHeader.biCompression == BI_RGB); + + switch (pVideoInfo->bmiHeader.biBitCount) { + case 16: return bits555; + case 24: return bits888; + case 32: return bits888; + default: return FailMasks; + } +} + + +// Check to see if we can support media type pmtIn as proposed by the output +// pin - We first check that the major media type is video and also identify +// the media sub type. Then we thoroughly check the VIDEOINFO type provided +// As well as the contained VIDEOINFO being correct the major type must be +// video, the subtype a recognised video format and the type GUID correct + +HRESULT CImageDisplay::CheckMediaType(const CMediaType *pmtIn) +{ + // Does this have a VIDEOINFOHEADER format block + + const GUID *pFormatType = pmtIn->FormatType(); + if (*pFormatType != FORMAT_VideoInfo) { + NOTE("Format GUID not a VIDEOINFOHEADER"); + return E_INVALIDARG; + } + ASSERT(pmtIn->Format()); + + // Check the format looks reasonably ok + + ULONG Length = pmtIn->FormatLength(); + if (Length < SIZE_VIDEOHEADER) { + NOTE("Format smaller than a VIDEOHEADER"); + return E_FAIL; + } + + VIDEOINFO *pInput = (VIDEOINFO *) pmtIn->Format(); + + // Check the major type is MEDIATYPE_Video + + const GUID *pMajorType = pmtIn->Type(); + if (*pMajorType != MEDIATYPE_Video) { + NOTE("Major type not MEDIATYPE_Video"); + return E_INVALIDARG; + } + + // Check we can identify the media subtype + + const GUID *pSubType = pmtIn->Subtype(); + if (GetBitCount(pSubType) == USHRT_MAX) { + NOTE("Invalid video media subtype"); + return E_INVALIDARG; + } + return CheckVideoType(pInput); +} + + +// Given a video format described by a VIDEOINFO structure we return the mask +// that is used to obtain the range of acceptable colours for this type, for +// example, the mask for a 24 bit true colour format is 0xFF in all cases. A +// 16 bit 5:6:5 display format uses 0xF8, 0xFC and 0xF8, therefore given any +// RGB triplets we can AND them with these fields to find one that is valid + +BOOL CImageDisplay::GetColourMask(DWORD *pMaskRed, + DWORD *pMaskGreen, + DWORD *pMaskBlue) +{ + CAutoLock cDisplayLock(this); + *pMaskRed = 0xFF; + *pMaskGreen = 0xFF; + *pMaskBlue = 0xFF; + + // If this format is palettised then it doesn't have bit fields + + if (m_Display.bmiHeader.biBitCount < 16) { + return FALSE; + } + + // If this is a 24 bit true colour display then it can handle all the + // possible colour component ranges described by a byte. It is never + // allowed for a 24 bit colour depth image to have BI_BITFIELDS set + + if (m_Display.bmiHeader.biBitCount == 24) { + ASSERT(m_Display.bmiHeader.biCompression == BI_RGB); + return TRUE; + } + + // Calculate the mask based on the format's bit fields + + const DWORD *pBitFields = (DWORD *) GetBitMasks((VIDEOINFO *)&m_Display); + DWORD *pOutputMask[] = { pMaskRed, pMaskGreen, pMaskBlue }; + + // We know from earlier testing that there are no more than iMAXBITS + // bits set in the mask and that they are all contiguous. All that + // therefore remains is to shift them into the correct position + + for (INT iColour = iRED;iColour <= iBLUE;iColour++) { + + // This works out how many bits there are and where they live + + DWORD PrefixBits = CountPrefixBits(pBitFields[iColour]); + DWORD SetBits = CountSetBits(pBitFields[iColour]); + + // The first shift moves the bit field so that it is right justified + // in the DWORD, after which we then shift it back left which then + // puts the leading bit in the bytes most significant bit position + + *(pOutputMask[iColour]) = pBitFields[iColour] >> PrefixBits; + *(pOutputMask[iColour]) <<= (iMAXBITS - SetBits); + } + return TRUE; +} + + +/* Helper to convert to VIDEOINFOHEADER2 +*/ +STDAPI ConvertVideoInfoToVideoInfo2(AM_MEDIA_TYPE *pmt) +{ + ASSERT(pmt->formattype == FORMAT_VideoInfo); + VIDEOINFO *pVideoInfo = (VIDEOINFO *)pmt->pbFormat; + PVOID pvNew = CoTaskMemAlloc(pmt->cbFormat + sizeof(VIDEOINFOHEADER2) - + sizeof(VIDEOINFOHEADER)); + if (pvNew == NULL) { + return E_OUTOFMEMORY; + } + CopyMemory(pvNew, pmt->pbFormat, FIELD_OFFSET(VIDEOINFOHEADER, bmiHeader)); + ZeroMemory((PBYTE)pvNew + FIELD_OFFSET(VIDEOINFOHEADER, bmiHeader), + sizeof(VIDEOINFOHEADER2) - sizeof(VIDEOINFOHEADER)); + CopyMemory((PBYTE)pvNew + FIELD_OFFSET(VIDEOINFOHEADER2, bmiHeader), + pmt->pbFormat + FIELD_OFFSET(VIDEOINFOHEADER, bmiHeader), + pmt->cbFormat - FIELD_OFFSET(VIDEOINFOHEADER, bmiHeader)); + VIDEOINFOHEADER2 *pVideoInfo2 = (VIDEOINFOHEADER2 *)pvNew; + pVideoInfo2->dwPictAspectRatioX = (DWORD)pVideoInfo2->bmiHeader.biWidth; + pVideoInfo2->dwPictAspectRatioY = (DWORD)pVideoInfo2->bmiHeader.biHeight; + pmt->formattype = FORMAT_VideoInfo2; + CoTaskMemFree(pmt->pbFormat); + pmt->pbFormat = (PBYTE)pvNew; + pmt->cbFormat += sizeof(VIDEOINFOHEADER2) - sizeof(VIDEOINFOHEADER); + return S_OK; +} diff --git a/ThirdParty/strmbas/winutil.h b/ThirdParty/strmbas/winutil.h new file mode 100644 index 0000000..50f7bf3 --- /dev/null +++ b/ThirdParty/strmbas/winutil.h @@ -0,0 +1,413 @@ +//------------------------------------------------------------------------------ +// File: WinUtil.h +// +// Desc: DirectShow base classes - defines generic handler classes. +// +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------------------------ + + +// Make sure that you call PrepareWindow to initialise the window after +// the object has been constructed. It is a separate method so that +// derived classes can override useful methods like MessageLoop. Also +// any derived class must call DoneWithWindow in its destructor. If it +// doesn't a message may be retrieved and call a derived class member +// function while a thread is executing the base class destructor code + +#ifndef __WINUTIL__ +#define __WINUTIL__ + +const int DEFWIDTH = 320; // Initial window width +const int DEFHEIGHT = 240; // Initial window height +const int CAPTION = 256; // Maximum length of caption +const int TIMELENGTH = 50; // Maximum length of times +const int PROFILESTR = 128; // Normal profile string +const WORD PALVERSION = 0x300; // GDI palette version +const LONG PALETTE_VERSION = (LONG) 1; // Initial palette version +const COLORREF VIDEO_COLOUR = 0; // Defaults to black background +const HANDLE hMEMORY = (HANDLE) (-1); // Says to open as memory file + +#define WIDTH(x) ((*(x)).right - (*(x)).left) +#define HEIGHT(x) ((*(x)).bottom - (*(x)).top) +#define SHOWSTAGE TEXT("WM_SHOWSTAGE") +#define SHOWSTAGETOP TEXT("WM_SHOWSTAGETOP") +#define REALIZEPALETTE TEXT("WM_REALIZEPALETTE") + +class AM_NOVTABLE CBaseWindow +{ +protected: + + HINSTANCE m_hInstance; // Global module instance handle + HWND m_hwnd; // Handle for our window + HDC m_hdc; // Device context for the window + LONG m_Width; // Client window width + LONG m_Height; // Client window height + BOOL m_bActivated; // Has the window been activated + LPTSTR m_pClassName; // Static string holding class name + DWORD m_ClassStyles; // Passed in to our constructor + DWORD m_WindowStyles; // Likewise the initial window styles + DWORD m_WindowStylesEx; // And the extended window styles + UINT m_ShowStageMessage; // Have the window shown with focus + UINT m_ShowStageTop; // Makes the window WS_EX_TOPMOST + UINT m_RealizePalette; // Makes us realize our new palette + HDC m_MemoryDC; // Used for fast BitBlt operations + HPALETTE m_hPalette; // Handle to any palette we may have + BYTE m_bNoRealize; // Don't realize palette now + BYTE m_bBackground; // Should we realise in background + BYTE m_bRealizing; // already realizing the palette + CCritSec m_WindowLock; // Serialise window object access + BOOL m_bDoGetDC; // Should this window get a DC + bool m_bDoPostToDestroy; // Use PostMessage to destroy + CCritSec m_PaletteLock; // This lock protects m_hPalette. + // It should be held anytime the + // program use the value of m_hPalette. + + // Maps windows message procedure into C++ methods + friend LRESULT CALLBACK WndProc(HWND hwnd, // Window handle + UINT uMsg, // Message ID + WPARAM wParam, // First parameter + LPARAM lParam); // Other parameter + + virtual LRESULT OnPaletteChange(HWND hwnd, UINT Message); + +public: + + CBaseWindow(BOOL bDoGetDC = TRUE, bool bPostToDestroy = false); + +#ifdef DEBUG + virtual ~CBaseWindow(); +#endif + + virtual HRESULT DoneWithWindow(); + virtual HRESULT PrepareWindow(); + virtual HRESULT InactivateWindow(); + virtual HRESULT ActivateWindow(); + virtual BOOL OnSize(LONG Width, LONG Height); + virtual BOOL OnClose(); + virtual RECT GetDefaultRect(); + virtual HRESULT UninitialiseWindow(); + virtual HRESULT InitialiseWindow(HWND hwnd); + + HRESULT CompleteConnect(); + HRESULT DoCreateWindow(); + + HRESULT PerformanceAlignWindow(); + HRESULT DoShowWindow(LONG ShowCmd); + void PaintWindow(BOOL bErase); + void DoSetWindowForeground(BOOL bFocus); + virtual HRESULT SetPalette(HPALETTE hPalette); + void SetRealize(BOOL bRealize) + { + m_bNoRealize = !bRealize; + } + + // Jump over to the window thread to set the current palette + HRESULT SetPalette(); + void UnsetPalette(void); + virtual HRESULT DoRealisePalette(BOOL bForceBackground = FALSE); + + void LockPaletteLock(); + void UnlockPaletteLock(); + + virtual BOOL PossiblyEatMessage(UINT uMsg, WPARAM wParam, LPARAM lParam) + { return FALSE; }; + + // Access our window information + + bool WindowExists(); + LONG GetWindowWidth(); + LONG GetWindowHeight(); + HWND GetWindowHWND(); + HDC GetMemoryHDC(); + HDC GetWindowHDC(); + + #ifdef DEBUG + HPALETTE GetPalette(); + #endif // DEBUG + + // This is the window procedure the derived object should override + + virtual LRESULT OnReceiveMessage(HWND hwnd, // Window handle + UINT uMsg, // Message ID + WPARAM wParam, // First parameter + LPARAM lParam); // Other parameter + + // Must be overriden to return class and window styles + + virtual LPTSTR GetClassWindowStyles( + DWORD *pClassStyles, // Class styles + DWORD *pWindowStyles, // Window styles + DWORD *pWindowStylesEx) PURE; // Extended styles +}; + + +// This helper class is entirely subservient to the owning CBaseWindow object +// All this object does is to split out the actual drawing operation from the +// main object (because it was becoming too large). We have a number of entry +// points to set things like the draw device contexts, to implement the actual +// drawing and to set the destination rectangle in the client window. We have +// no critical section locking in this class because we are used exclusively +// by the owning window object which looks after serialising calls into us + +// If you want to use this class make sure you call NotifyAllocator once the +// allocate has been agreed, also call NotifyMediaType with a pointer to a +// NON stack based CMediaType once that has been set (we keep a pointer to +// the original rather than taking a copy). When the palette changes call +// IncrementPaletteVersion (easiest thing to do is to also call this method +// in the SetMediaType method most filters implement). Finally before you +// start rendering anything call SetDrawContext so that we can get the HDCs +// for drawing from the CBaseWindow object we are given during construction + +class CDrawImage +{ +protected: + + CBaseWindow *m_pBaseWindow; // Owning video window object + CRefTime m_StartSample; // Start time for the current sample + CRefTime m_EndSample; // And likewise it's end sample time + HDC m_hdc; // Main window device context + HDC m_MemoryDC; // Offscreen draw device context + RECT m_TargetRect; // Target destination rectangle + RECT m_SourceRect; // Source image rectangle + BOOL m_bStretch; // Do we have to stretch the images + BOOL m_bUsingImageAllocator; // Are the samples shared DIBSECTIONs + CMediaType *m_pMediaType; // Pointer to the current format + int m_perfidRenderTime; // Time taken to render an image + LONG m_PaletteVersion; // Current palette version cookie + + // Draw the video images in the window + + void SlowRender(IMediaSample *pMediaSample); + void FastRender(IMediaSample *pMediaSample); + void DisplaySampleTimes(IMediaSample *pSample); + void UpdateColourTable(HDC hdc,BITMAPINFOHEADER *pbmi); + void SetStretchMode(); + +public: + + // Used to control the image drawing + + CDrawImage(CBaseWindow *pBaseWindow); + BOOL DrawImage(IMediaSample *pMediaSample); + BOOL DrawVideoImageHere(HDC hdc, IMediaSample *pMediaSample, + LPRECT lprcSrc, LPRECT lprcDst); + void SetDrawContext(); + void SetTargetRect(RECT *pTargetRect); + void SetSourceRect(RECT *pSourceRect); + void GetTargetRect(RECT *pTargetRect); + void GetSourceRect(RECT *pSourceRect); + virtual RECT ScaleSourceRect(const RECT *pSource); + + // Handle updating palettes as they change + + LONG GetPaletteVersion(); + void ResetPaletteVersion(); + void IncrementPaletteVersion(); + + // Tell us media types and allocator assignments + + void NotifyAllocator(BOOL bUsingImageAllocator); + void NotifyMediaType(CMediaType *pMediaType); + BOOL UsingImageAllocator(); + + // Called when we are about to draw an image + + void NotifyStartDraw() { + MSR_START(m_perfidRenderTime); + }; + + // Called when we complete an image rendering + + void NotifyEndDraw() { + MSR_STOP(m_perfidRenderTime); + }; +}; + + +// This is the structure used to keep information about each GDI DIB. All the +// samples we create from our allocator will have a DIBSECTION allocated to +// them. When we receive the sample we know we can BitBlt straight to an HDC + +typedef struct tagDIBDATA { + + LONG PaletteVersion; // Current palette version in use + DIBSECTION DibSection; // Details of DIB section allocated + HBITMAP hBitmap; // Handle to bitmap for drawing + HANDLE hMapping; // Handle to shared memory block + BYTE *pBase; // Pointer to base memory address + +} DIBDATA; + + +// This class inherits from CMediaSample and uses all of it's methods but it +// overrides the constructor to initialise itself with the DIBDATA structure +// When we come to render an IMediaSample we will know if we are using our own +// allocator, and if we are, we can cast the IMediaSample to a pointer to one +// of these are retrieve the DIB section information and hence the HBITMAP + +class CImageSample : public CMediaSample +{ +protected: + + DIBDATA m_DibData; // Information about the DIBSECTION + BOOL m_bInit; // Is the DIB information setup + +public: + + // Constructor + + CImageSample(CBaseAllocator *pAllocator, + TCHAR *pName, + HRESULT *phr, + LPBYTE pBuffer, + LONG length); + + // Maintain the DIB/DirectDraw state + + void SetDIBData(DIBDATA *pDibData); + DIBDATA *GetDIBData(); +}; + + +// This is an allocator based on the abstract CBaseAllocator base class that +// allocates sample buffers in shared memory. The number and size of these +// are determined when the output pin calls Prepare on us. The shared memory +// blocks are used in subsequent calls to GDI CreateDIBSection, once that +// has been done the output pin can fill the buffers with data which will +// then be handed to GDI through BitBlt calls and thereby remove one copy + +class CImageAllocator : public CBaseAllocator +{ +protected: + + CBaseFilter *m_pFilter; // Delegate reference counts to + CMediaType *m_pMediaType; // Pointer to the current format + + // Used to create and delete samples + + HRESULT Alloc(); + void Free(); + + // Manage the shared DIBSECTION and DCI/DirectDraw buffers + + HRESULT CreateDIB(LONG InSize,DIBDATA &DibData); + STDMETHODIMP CheckSizes(ALLOCATOR_PROPERTIES *pRequest); + virtual CImageSample *CreateImageSample(LPBYTE pData,LONG Length); + +public: + + // Constructor and destructor + + CImageAllocator(CBaseFilter *pFilter,TCHAR *pName,HRESULT *phr); +#ifdef DEBUG + ~CImageAllocator(); +#endif + + STDMETHODIMP_(ULONG) NonDelegatingAddRef(); + STDMETHODIMP_(ULONG) NonDelegatingRelease(); + void NotifyMediaType(CMediaType *pMediaType); + + // Agree the number of buffers to be used and their size + + STDMETHODIMP SetProperties( + ALLOCATOR_PROPERTIES *pRequest, + ALLOCATOR_PROPERTIES *pActual); +}; + + +// This class is a fairly specialised helper class for image renderers that +// have to create and manage palettes. The CBaseWindow class looks after +// realising palettes once they have been installed. This class can be used +// to create the palette handles from a media format (which must contain a +// VIDEOINFO structure in the format block). We try to make the palette an +// identity palette to maximise performance and also only change palettes +// if actually required to (we compare palette colours before updating). +// All the methods are virtual so that they can be overriden if so required + +class CImagePalette +{ +protected: + + CBaseWindow *m_pBaseWindow; // Window to realise palette in + CBaseFilter *m_pFilter; // Media filter to send events + CDrawImage *m_pDrawImage; // Object who will be drawing + HPALETTE m_hPalette; // The palette handle we own + +public: + + CImagePalette(CBaseFilter *pBaseFilter, + CBaseWindow *pBaseWindow, + CDrawImage *pDrawImage); + +#ifdef DEBUG + virtual ~CImagePalette(); +#endif + + static HPALETTE MakePalette(const VIDEOINFOHEADER *pVideoInfo, LPSTR szDevice); + HRESULT RemovePalette(); + static HRESULT MakeIdentityPalette(PALETTEENTRY *pEntry,INT iColours, LPSTR szDevice); + HRESULT CopyPalette(const CMediaType *pSrc,CMediaType *pDest); + BOOL ShouldUpdate(const VIDEOINFOHEADER *pNewInfo,const VIDEOINFOHEADER *pOldInfo); + HRESULT PreparePalette(const CMediaType *pmtNew,const CMediaType *pmtOld,LPSTR szDevice); + + BOOL DrawVideoImageHere(HDC hdc, IMediaSample *pMediaSample, LPRECT lprcSrc, LPRECT lprcDst) + { + return m_pDrawImage->DrawVideoImageHere(hdc, pMediaSample, lprcSrc,lprcDst); + } +}; + + +// Another helper class really for video based renderers. Most such renderers +// need to know what the display format is to some degree or another. This +// class initialises itself with the display format. The format can be asked +// for through GetDisplayFormat and various other accessor functions. If a +// filter detects a display format change (perhaps it gets a WM_DEVMODECHANGE +// message then it can call RefreshDisplayType to reset that format). Also +// many video renderers will want to check formats as they are proposed by +// source filters. This class provides methods to check formats and only +// accept those video formats that can be efficiently drawn using GDI calls + +class CImageDisplay : public CCritSec +{ +protected: + + // This holds the display format; biSize should not be too big, so we can + // safely use the VIDEOINFO structure + VIDEOINFO m_Display; + + static DWORD CountSetBits(const DWORD Field); + static DWORD CountPrefixBits(const DWORD Field); + static BOOL CheckBitFields(const VIDEOINFO *pInput); + +public: + + // Constructor and destructor + + CImageDisplay(); + + // Used to manage BITMAPINFOHEADERs and the display format + + const VIDEOINFO *GetDisplayFormat(); + HRESULT RefreshDisplayType(LPSTR szDeviceName); + static BOOL CheckHeaderValidity(const VIDEOINFO *pInput); + static BOOL CheckPaletteHeader(const VIDEOINFO *pInput); + BOOL IsPalettised(); + WORD GetDisplayDepth(); + + // Provide simple video format type checking + + HRESULT CheckMediaType(const CMediaType *pmtIn); + HRESULT CheckVideoType(const VIDEOINFO *pInput); + HRESULT UpdateFormat(VIDEOINFO *pVideoInfo); + const DWORD *GetBitMasks(const VIDEOINFO *pVideoInfo); + + BOOL GetColourMask(DWORD *pMaskRed, + DWORD *pMaskGreen, + DWORD *pMaskBlue); +}; + +// Convert a FORMAT_VideoInfo to FORMAT_VideoInfo2 +STDAPI ConvertVideoInfoToVideoInfo2(AM_MEDIA_TYPE *pmt); + +#endif // __WINUTIL__ + diff --git a/ThirdParty/strmbas/wxdebug.cpp b/ThirdParty/strmbas/wxdebug.cpp new file mode 100644 index 0000000..0be7347 --- /dev/null +++ b/ThirdParty/strmbas/wxdebug.cpp @@ -0,0 +1,1420 @@ +//------------------------------------------------------------------------------ +// File: WXDebug.cpp +// +// Desc: DirectShow base classes - implements ActiveX system debugging +// facilities. +// +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------------------------ + + +#define _WINDLL + +#include +#include +#include + +#ifdef DEBUG +#ifdef UNICODE +#ifndef _UNICODE +#define _UNICODE +#endif // _UNICODE +#endif // UNICODE +#endif // DEBUG + +#ifdef DEBUG + +// The Win32 wsprintf() function writes a maximum of 1024 characters to it's output buffer. +// See the documentation for wsprintf()'s lpOut parameter for more information. +const INT iDEBUGINFO = 1024; // Used to format strings + +/* For every module and executable we store a debugging level for each of + the five categories (eg LOG_ERROR and LOG_TIMING). This makes it easy + to isolate and debug individual modules without seeing everybody elses + spurious debug output. The keys are stored in the registry under the + HKEY_LOCAL_MACHINE\SOFTWARE\Debug\\ key values + NOTE these must be in the same order as their enumeration definition */ + +TCHAR *pKeyNames[] = { + TEXT("TIMING"), // Timing and performance measurements + TEXT("TRACE"), // General step point call tracing + TEXT("MEMORY"), // Memory and object allocation/destruction + TEXT("LOCKING"), // Locking/unlocking of critical sections + TEXT("ERROR"), // Debug error notification + TEXT("CUSTOM1"), + TEXT("CUSTOM2"), + TEXT("CUSTOM3"), + TEXT("CUSTOM4"), + TEXT("CUSTOM5") + }; + +const TCHAR CAutoTrace::_szEntering[] = TEXT("->: %s"); +const TCHAR CAutoTrace::_szLeaving[] = TEXT("<-: %s"); + +const INT iMAXLEVELS = NUMELMS(pKeyNames); // Maximum debug categories + +HINSTANCE m_hInst; // Module instance handle +TCHAR m_ModuleName[iDEBUGINFO]; // Cut down module name +DWORD m_Levels[iMAXLEVELS]; // Debug level per category +CRITICAL_SECTION m_CSDebug; // Controls access to list +DWORD m_dwNextCookie; // Next active object ID +ObjectDesc *pListHead = NULL; // First active object +DWORD m_dwObjectCount; // Active object count +BOOL m_bInit = FALSE; // Have we been initialised +HANDLE m_hOutput = INVALID_HANDLE_VALUE; // Optional output written here +DWORD dwWaitTimeout = INFINITE; // Default timeout value +DWORD dwTimeOffset; // Time of first DbgLog call +bool g_fUseKASSERT = false; // don't create messagebox +bool g_fDbgInDllEntryPoint = false; +bool g_fAutoRefreshLevels = false; + +const TCHAR *pBaseKey = TEXT("SOFTWARE\\Debug"); +const TCHAR *pGlobalKey = TEXT("GLOBAL"); +static CHAR *pUnknownName = "UNKNOWN"; + +TCHAR *TimeoutName = TEXT("TIMEOUT"); + +/* This sets the instance handle that the debug library uses to find + the module's file name from the Win32 GetModuleFileName function */ + +void WINAPI DbgInitialise(HINSTANCE hInst) +{ + InitializeCriticalSection(&m_CSDebug); + m_bInit = TRUE; + + m_hInst = hInst; + DbgInitModuleName(); + if (GetProfileInt(m_ModuleName, TEXT("BreakOnLoad"), 0)) + DebugBreak(); + DbgInitModuleSettings(false); + DbgInitGlobalSettings(true); + dwTimeOffset = timeGetTime(); +} + + +/* This is called to clear up any resources the debug library uses - at the + moment we delete our critical section and the object list. The values we + retrieve from the registry are all done during initialisation but we don't + go looking for update notifications while we are running, if the values + are changed then the application has to be restarted to pick them up */ + +void WINAPI DbgTerminate() +{ + if (m_hOutput != INVALID_HANDLE_VALUE) { + EXECUTE_ASSERT(CloseHandle(m_hOutput)); + m_hOutput = INVALID_HANDLE_VALUE; + } + DeleteCriticalSection(&m_CSDebug); + m_bInit = FALSE; +} + + +/* This is called by DbgInitLogLevels to read the debug settings + for each logging category for this module from the registry */ + +void WINAPI DbgInitKeyLevels(HKEY hKey, bool fTakeMax) +{ + LONG lReturn; // Create key return value + LONG lKeyPos; // Current key category + DWORD dwKeySize; // Size of the key value + DWORD dwKeyType; // Receives it's type + DWORD dwKeyValue; // This fields value + + /* Try and read a value for each key position in turn */ + for (lKeyPos = 0;lKeyPos < iMAXLEVELS;lKeyPos++) { + + dwKeySize = sizeof(DWORD); + lReturn = RegQueryValueEx( + hKey, // Handle to an open key + pKeyNames[lKeyPos], // Subkey name derivation + NULL, // Reserved field + &dwKeyType, // Returns the field type + (LPBYTE) &dwKeyValue, // Returns the field's value + &dwKeySize ); // Number of bytes transferred + + /* If either the key was not available or it was not a DWORD value + then we ensure only the high priority debug logging is output + but we try and update the field to a zero filled DWORD value */ + + if (lReturn != ERROR_SUCCESS || dwKeyType != REG_DWORD) { + + dwKeyValue = 0; + lReturn = RegSetValueEx( + hKey, // Handle of an open key + pKeyNames[lKeyPos], // Address of subkey name + (DWORD) 0, // Reserved field + REG_DWORD, // Type of the key field + (PBYTE) &dwKeyValue, // Value for the field + sizeof(DWORD)); // Size of the field buffer + + if (lReturn != ERROR_SUCCESS) { + DbgLog((LOG_ERROR,0,TEXT("Could not create subkey %s"),pKeyNames[lKeyPos])); + dwKeyValue = 0; + } + } + if(fTakeMax) + { + m_Levels[lKeyPos] = max(dwKeyValue,m_Levels[lKeyPos]); + } + else + { + if((m_Levels[lKeyPos] & LOG_FORCIBLY_SET) == 0) { + m_Levels[lKeyPos] = dwKeyValue; + } + } + } + + /* Read the timeout value for catching hangs */ + dwKeySize = sizeof(DWORD); + lReturn = RegQueryValueEx( + hKey, // Handle to an open key + TimeoutName, // Subkey name derivation + NULL, // Reserved field + &dwKeyType, // Returns the field type + (LPBYTE) &dwWaitTimeout, // Returns the field's value + &dwKeySize ); // Number of bytes transferred + + /* If either the key was not available or it was not a DWORD value + then we ensure only the high priority debug logging is output + but we try and update the field to a zero filled DWORD value */ + + if (lReturn != ERROR_SUCCESS || dwKeyType != REG_DWORD) { + + dwWaitTimeout = INFINITE; + lReturn = RegSetValueEx( + hKey, // Handle of an open key + TimeoutName, // Address of subkey name + (DWORD) 0, // Reserved field + REG_DWORD, // Type of the key field + (PBYTE) &dwWaitTimeout, // Value for the field + sizeof(DWORD)); // Size of the field buffer + + if (lReturn != ERROR_SUCCESS) { + DbgLog((LOG_ERROR,0,TEXT("Could not create subkey %s"),TimeoutName)); + dwWaitTimeout = INFINITE; + } + } +} + +void WINAPI DbgOutString(LPCTSTR psz) +{ + if (m_hOutput != INVALID_HANDLE_VALUE) { + UINT cb = lstrlen(psz); + DWORD dw; +#ifdef UNICODE + CHAR szDest[2048]; + WideCharToMultiByte(CP_ACP, 0, psz, -1, szDest, NUMELMS(szDest), 0, 0); + WriteFile (m_hOutput, szDest, cb, &dw, NULL); +#else + WriteFile (m_hOutput, psz, cb, &dw, NULL); +#endif + } else { + OutputDebugString (psz); + } +} + +/* Called by DbgInitGlobalSettings to setup alternate logging destinations + */ + +void WINAPI DbgInitLogTo ( + HKEY hKey) +{ + LONG lReturn; + DWORD dwKeyType; + DWORD dwKeySize; + TCHAR szFile[MAX_PATH] = {0}; + static const TCHAR cszKey[] = TEXT("LogToFile"); + + dwKeySize = MAX_PATH; + lReturn = RegQueryValueEx( + hKey, // Handle to an open key + cszKey, // Subkey name derivation + NULL, // Reserved field + &dwKeyType, // Returns the field type + (LPBYTE) szFile, // Returns the field's value + &dwKeySize); // Number of bytes transferred + + // create an empty key if it does not already exist + // + if (lReturn != ERROR_SUCCESS || dwKeyType != REG_SZ) + { + dwKeySize = sizeof(TCHAR); + lReturn = RegSetValueEx( + hKey, // Handle of an open key + cszKey, // Address of subkey name + (DWORD) 0, // Reserved field + REG_SZ, // Type of the key field + (PBYTE)szFile, // Value for the field + dwKeySize); // Size of the field buffer + } + + // if an output-to was specified. try to open it. + // + if (m_hOutput != INVALID_HANDLE_VALUE) { + EXECUTE_ASSERT(CloseHandle (m_hOutput)); + m_hOutput = INVALID_HANDLE_VALUE; + } + if (szFile[0] != 0) + { + if (!lstrcmpi(szFile, TEXT("Console"))) { + m_hOutput = GetStdHandle (STD_OUTPUT_HANDLE); + if (m_hOutput == INVALID_HANDLE_VALUE) { + AllocConsole (); + m_hOutput = GetStdHandle (STD_OUTPUT_HANDLE); + } + SetConsoleTitle (TEXT("ActiveX Debug Output")); + } else if (szFile[0] && + lstrcmpi(szFile, TEXT("Debug")) && + lstrcmpi(szFile, TEXT("Debugger")) && + lstrcmpi(szFile, TEXT("Deb"))) + { + m_hOutput = CreateFile(szFile, GENERIC_WRITE, + FILE_SHARE_READ, + NULL, OPEN_ALWAYS, + FILE_ATTRIBUTE_NORMAL, + NULL); + if (INVALID_HANDLE_VALUE != m_hOutput) + { + static const TCHAR cszBar[] = TEXT("\r\n\r\n=====DbgInitialize()=====\r\n\r\n"); + SetFilePointer (m_hOutput, 0, NULL, FILE_END); + DbgOutString (cszBar); + } + } + } +} + + + +/* This is called by DbgInitLogLevels to read the global debug settings for + each logging category for this module from the registry. Normally each + module has it's own values set for it's different debug categories but + setting the global SOFTWARE\Debug\Global applies them to ALL modules */ + +void WINAPI DbgInitGlobalSettings(bool fTakeMax) +{ + LONG lReturn; // Create key return value + TCHAR szInfo[iDEBUGINFO]; // Constructs key names + HKEY hGlobalKey; // Global override key + + /* Construct the global base key name */ + (void)StringCchPrintf(szInfo,NUMELMS(szInfo),TEXT("%s\\%s"),pBaseKey,pGlobalKey); + + /* Create or open the key for this module */ + lReturn = RegCreateKeyEx(HKEY_LOCAL_MACHINE, // Handle of an open key + szInfo, // Address of subkey name + (DWORD) 0, // Reserved value + NULL, // Address of class name + (DWORD) 0, // Special options flags + KEY_ALL_ACCESS, // Desired security access + NULL, // Key security descriptor + &hGlobalKey, // Opened handle buffer + NULL); // What really happened + + if (lReturn != ERROR_SUCCESS) { + DbgLog((LOG_ERROR,0,TEXT("Could not access GLOBAL module key"))); + return; + } + + DbgInitKeyLevels(hGlobalKey, fTakeMax); + RegCloseKey(hGlobalKey); +} + + +/* This sets the debugging log levels for the different categories. We start + by opening (or creating if not already available) the SOFTWARE\Debug key + that all these settings live under. We then look at the global values + set under SOFTWARE\Debug\Global which apply on top of the individual + module settings. We then load the individual module registry settings */ + +void WINAPI DbgInitModuleSettings(bool fTakeMax) +{ + LONG lReturn; // Create key return value + TCHAR szInfo[iDEBUGINFO]; // Constructs key names + HKEY hModuleKey; // Module key handle + + /* Construct the base key name */ + (void)StringCchPrintf(szInfo,NUMELMS(szInfo), TEXT("%s\\%s"),pBaseKey,m_ModuleName); + + /* Create or open the key for this module */ + lReturn = RegCreateKeyEx(HKEY_LOCAL_MACHINE, // Handle of an open key + szInfo, // Address of subkey name + (DWORD) 0, // Reserved value + NULL, // Address of class name + (DWORD) 0, // Special options flags + KEY_ALL_ACCESS, // Desired security access + NULL, // Key security descriptor + &hModuleKey, // Opened handle buffer + NULL); // What really happened + + if (lReturn != ERROR_SUCCESS) { + DbgLog((LOG_ERROR,0,TEXT("Could not access module key"))); + return; + } + + DbgInitLogTo(hModuleKey); + DbgInitKeyLevels(hModuleKey, fTakeMax); + RegCloseKey(hModuleKey); +} + + +/* Initialise the module file name */ + +void WINAPI DbgInitModuleName() +{ + TCHAR FullName[iDEBUGINFO]; // Load the full path and module name + TCHAR *pName; // Searches from the end for a backslash + + GetModuleFileName(m_hInst,FullName,iDEBUGINFO); + pName = _tcsrchr(FullName,'\\'); + if (pName == NULL) { + pName = FullName; + } else { + pName++; + } + (void)StringCchCopy(m_ModuleName,NUMELMS(m_ModuleName), pName); +} + +struct MsgBoxMsg +{ + HWND hwnd; + TCHAR *szTitle; + TCHAR *szMessage; + DWORD dwFlags; + INT iResult; +}; + +// +// create a thread to call MessageBox(). calling MessageBox() on +// random threads at bad times can confuse the host (eg IE). +// +DWORD WINAPI MsgBoxThread( + LPVOID lpParameter // thread data + ) +{ + MsgBoxMsg *pmsg = (MsgBoxMsg *)lpParameter; + pmsg->iResult = MessageBox( + pmsg->hwnd, + pmsg->szTitle, + pmsg->szMessage, + pmsg->dwFlags); + + return 0; +} + +INT MessageBoxOtherThread( + HWND hwnd, + TCHAR *szTitle, + TCHAR *szMessage, + DWORD dwFlags) +{ + if(g_fDbgInDllEntryPoint) + { + // can't wait on another thread because we have the loader + // lock held in the dll entry point. + return MessageBox(hwnd, szTitle, szMessage, dwFlags); + } + else + { + MsgBoxMsg msg = {hwnd, szTitle, szMessage, dwFlags, 0}; + DWORD dwid; + HANDLE hThread = CreateThread( + 0, // security + 0, // stack size + MsgBoxThread, + (void *)&msg, // arg + 0, // flags + &dwid); + if(hThread) + { + WaitForSingleObject(hThread, INFINITE); + CloseHandle(hThread); + return msg.iResult; + } + + // break into debugger on failure. + return IDCANCEL; + } +} + +/* Displays a message box if the condition evaluated to FALSE */ + +void WINAPI DbgAssert(const TCHAR *pCondition,const TCHAR *pFileName,INT iLine) +{ + if(g_fUseKASSERT) + { + DbgKernelAssert(pCondition, pFileName, iLine); + } + else + { + + TCHAR szInfo[iDEBUGINFO]; + + (void)StringCchPrintf(szInfo, NUMELMS(szInfo), TEXT("%s \nAt line %d of %s\nContinue? (Cancel to debug)"), + pCondition, iLine, pFileName); + + INT MsgId = MessageBoxOtherThread(NULL,szInfo,TEXT("ASSERT Failed"), + MB_SYSTEMMODAL | + MB_ICONHAND | + MB_YESNOCANCEL | + MB_SETFOREGROUND); + switch (MsgId) + { + case IDNO: /* Kill the application */ + + FatalAppExit(FALSE, TEXT("Application terminated")); + break; + + case IDCANCEL: /* Break into the debugger */ + + DebugBreak(); + break; + + case IDYES: /* Ignore assertion continue execution */ + break; + } + } +} + +/* Displays a message box at a break point */ + +void WINAPI DbgBreakPoint(const TCHAR *pCondition,const TCHAR *pFileName,INT iLine) +{ + if(g_fUseKASSERT) + { + DbgKernelAssert(pCondition, pFileName, iLine); + } + else + { + TCHAR szInfo[iDEBUGINFO]; + + (void)StringCchPrintf(szInfo, NUMELMS(szInfo), TEXT("%s \nAt line %d of %s\nContinue? (Cancel to debug)"), + pCondition, iLine, pFileName); + + INT MsgId = MessageBoxOtherThread(NULL,szInfo,TEXT("Hard coded break point"), + MB_SYSTEMMODAL | + MB_ICONHAND | + MB_YESNOCANCEL | + MB_SETFOREGROUND); + switch (MsgId) + { + case IDNO: /* Kill the application */ + + FatalAppExit(FALSE, TEXT("Application terminated")); + break; + + case IDCANCEL: /* Break into the debugger */ + + DebugBreak(); + break; + + case IDYES: /* Ignore break point continue execution */ + break; + } + } +} + +void WINAPI DbgBreakPoint(const TCHAR *pFileName,INT iLine,const TCHAR* szFormatString,...) +{ + // A debug break point message can have at most 2000 characters if + // ANSI or UNICODE characters are being used. A debug break point message + // can have between 1000 and 2000 double byte characters in it. If a + // particular message needs more characters, then the value of this constant + // should be increased. + const DWORD MAX_BREAK_POINT_MESSAGE_SIZE = 2000; + + TCHAR szBreakPointMessage[MAX_BREAK_POINT_MESSAGE_SIZE]; + + const DWORD MAX_CHARS_IN_BREAK_POINT_MESSAGE = sizeof(szBreakPointMessage) / sizeof(TCHAR); + + va_list va; + va_start( va, szFormatString ); + + HRESULT hr = StringCchVPrintf( szBreakPointMessage, MAX_CHARS_IN_BREAK_POINT_MESSAGE, szFormatString, va ); + + va_end(va); + + if( S_OK != hr ) { + DbgBreak( "ERROR in DbgBreakPoint(). The variable length debug message could not be displayed because _vsnprintf() failed." ); + return; + } + + ::DbgBreakPoint( szBreakPointMessage, pFileName, iLine ); +} + + +/* When we initialised the library we stored in the m_Levels array the current + debug output level for this module for each of the five categories. When + some debug logging is sent to us it can be sent with a combination of the + categories (if it is applicable to many for example) in which case we map + the type's categories into their current debug levels and see if any of + them can be accepted. The function looks at each bit position in turn from + the input type field and then compares it's debug level with the modules. + + A level of 0 means that output is always sent to the debugger. This is + due to producing output if the input level is <= m_Levels. +*/ + + +BOOL WINAPI DbgCheckModuleLevel(DWORD Type,DWORD Level) +{ + if(g_fAutoRefreshLevels) + { + // re-read the registry every second. We cannot use RegNotify() to + // notice registry changes because it's not available on win9x. + static int g_dwLastRefresh = 0; + DWORD dwTime = timeGetTime(); + if(dwTime - g_dwLastRefresh > 1000) { + g_dwLastRefresh = dwTime; + + // there's a race condition: multiple threads could update the + // values. plus read and write not synchronized. no harm + // though. + DbgInitModuleSettings(false); + } + } + + + DWORD Mask = 0x01; + + // If no valid bits are set return FALSE + if ((Type & ((1<m_szName = szObjectName; + pObject->m_wszName = wszObjectName; + pObject->m_dwCookie = ++m_dwNextCookie; + pObject->m_pNext = pListHead; + + pListHead = pObject; + m_dwObjectCount++; + + DWORD ObjectCookie = pObject->m_dwCookie; + ASSERT(ObjectCookie); + + if(wszObjectName) { + DbgLog((LOG_MEMORY,2,TEXT("Object created %d (%ls) %d Active"), + pObject->m_dwCookie, wszObjectName, m_dwObjectCount)); + } else { + DbgLog((LOG_MEMORY,2,TEXT("Object created %d (%hs) %d Active"), + pObject->m_dwCookie, szObjectName, m_dwObjectCount)); + } + + LeaveCriticalSection(&m_CSDebug); + return ObjectCookie; +} + + +/* This is called by the CBaseObject destructor when an object is about to be + destroyed, we are passed the cookie we returned during construction that + identifies this object. We scan the object list for a matching cookie and + remove the object if successful. We also update the active object count */ + +BOOL WINAPI DbgRegisterObjectDestruction(DWORD dwCookie) +{ + /* Grab the list critical section */ + EnterCriticalSection(&m_CSDebug); + + ObjectDesc *pObject = pListHead; + ObjectDesc *pPrevious = NULL; + + /* Scan the object list looking for a cookie match */ + + while (pObject) { + if (pObject->m_dwCookie == dwCookie) { + break; + } + pPrevious = pObject; + pObject = pObject->m_pNext; + } + + if (pObject == NULL) { + DbgBreak("Apparently destroying a bogus object"); + LeaveCriticalSection(&m_CSDebug); + return FALSE; + } + + /* Is the object at the head of the list */ + + if (pPrevious == NULL) { + pListHead = pObject->m_pNext; + } else { + pPrevious->m_pNext = pObject->m_pNext; + } + + /* Delete the object and update the housekeeping information */ + + m_dwObjectCount--; + + if(pObject->m_wszName) { + DbgLog((LOG_MEMORY,2,TEXT("Object destroyed %d (%ls) %d Active"), + pObject->m_dwCookie, pObject->m_wszName, m_dwObjectCount)); + } else { + DbgLog((LOG_MEMORY,2,TEXT("Object destroyed %d (%hs) %d Active"), + pObject->m_dwCookie, pObject->m_szName, m_dwObjectCount)); + } + + delete pObject; + LeaveCriticalSection(&m_CSDebug); + return TRUE; +} + + +/* This runs through the active object list displaying their details */ + +void WINAPI DbgDumpObjectRegister() +{ + TCHAR szInfo[iDEBUGINFO]; + + /* Grab the list critical section */ + + EnterCriticalSection(&m_CSDebug); + ObjectDesc *pObject = pListHead; + + /* Scan the object list displaying the name and cookie */ + + DbgLog((LOG_MEMORY,2,TEXT(""))); + DbgLog((LOG_MEMORY,2,TEXT(" ID Object Description"))); + DbgLog((LOG_MEMORY,2,TEXT(""))); + + while (pObject) { + if(pObject->m_wszName) { + #ifdef UNICODE + LPCTSTR FORMAT_STRING = TEXT("%5d (%8x) %30s"); + #else + LPCTSTR FORMAT_STRING = TEXT("%5d (%8x) %30S"); + #endif + + (void)StringCchPrintf(szInfo,NUMELMS(szInfo), FORMAT_STRING, pObject->m_dwCookie, &pObject, pObject->m_wszName); + + } else { + #ifdef UNICODE + LPCTSTR FORMAT_STRING = TEXT("%5d (%8x) %30S"); + #else + LPCTSTR FORMAT_STRING = TEXT("%5d (%8x) %30s"); + #endif + + (void)StringCchPrintf(szInfo,NUMELMS(szInfo),FORMAT_STRING,pObject->m_dwCookie, &pObject, pObject->m_szName); + } + DbgLog((LOG_MEMORY,2,szInfo)); + pObject = pObject->m_pNext; + } + + (void)StringCchPrintf(szInfo,NUMELMS(szInfo),TEXT("Total object count %5d"),m_dwObjectCount); + DbgLog((LOG_MEMORY,2,TEXT(""))); + DbgLog((LOG_MEMORY,1,szInfo)); + LeaveCriticalSection(&m_CSDebug); +} + +/* Debug infinite wait stuff */ +DWORD WINAPI DbgWaitForSingleObject(HANDLE h) +{ + DWORD dwWaitResult; + do { + dwWaitResult = WaitForSingleObject(h, dwWaitTimeout); + ASSERT(dwWaitResult == WAIT_OBJECT_0); + } while (dwWaitResult == WAIT_TIMEOUT); + return dwWaitResult; +} +DWORD WINAPI DbgWaitForMultipleObjects(DWORD nCount, + CONST HANDLE *lpHandles, + BOOL bWaitAll) +{ + DWORD dwWaitResult; + do { + dwWaitResult = WaitForMultipleObjects(nCount, + lpHandles, + bWaitAll, + dwWaitTimeout); + ASSERT((DWORD)(dwWaitResult - WAIT_OBJECT_0) < MAXIMUM_WAIT_OBJECTS); + } while (dwWaitResult == WAIT_TIMEOUT); + return dwWaitResult; +} + +void WINAPI DbgSetWaitTimeout(DWORD dwTimeout) +{ + dwWaitTimeout = dwTimeout; +} + +#endif /* DEBUG */ + +#ifdef _OBJBASE_H_ + + /* Stuff for printing out our GUID names */ + + GUID_STRING_ENTRY g_GuidNames[] = { + #define OUR_GUID_ENTRY(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \ + { #name, { l, w1, w2, { b1, b2, b3, b4, b5, b6, b7, b8 } } }, + #include + }; + + CGuidNameList GuidNames; + int g_cGuidNames = sizeof(g_GuidNames) / sizeof(g_GuidNames[0]); + + char *CGuidNameList::operator [] (const GUID &guid) + { + for (int i = 0; i < g_cGuidNames; i++) { + if (g_GuidNames[i].guid == guid) { + return g_GuidNames[i].szName; + } + } + if (guid == GUID_NULL) { + return "GUID_NULL"; + } + + // !!! add something to print FOURCC guids? + + // shouldn't this print the hex CLSID? + return "Unknown GUID Name"; + } + +#endif /* _OBJBASE_H_ */ + +/* CDisp class - display our data types */ + +// clashes with REFERENCE_TIME +CDisp::CDisp(LONGLONG ll, int Format) +{ + // note: this could be combined with CDisp(LONGLONG) by + // introducing a default format of CDISP_REFTIME + LARGE_INTEGER li; + li.QuadPart = ll; + switch (Format) { + case CDISP_DEC: + { + TCHAR temp[20]; + int pos=20; + temp[--pos] = 0; + int digit; + // always output at least one digit + do { + // Get the rightmost digit - we only need the low word + digit = li.LowPart % 10; + li.QuadPart /= 10; + temp[--pos] = (TCHAR) digit+L'0'; + } while (li.QuadPart); + (void)StringCchPrintf(m_String, NUMELMS(m_String), TEXT("%s"), temp+pos); + break; + } + case CDISP_HEX: + default: + (void)StringCchPrintf(m_String, NUMELMS(m_String), TEXT("0x%X%8.8X"), li.HighPart, li.LowPart); + } +}; + +CDisp::CDisp(REFCLSID clsid) +{ + WCHAR strClass[CHARS_IN_GUID+1]; + StringFromGUID2(clsid, strClass, sizeof(strClass) / sizeof(strClass[0])); + ASSERT(sizeof(m_String)/sizeof(m_String[0]) >= CHARS_IN_GUID+1); + #ifdef UNICODE + (void)StringCchPrintf(m_String, NUMELMS(m_String), TEXT("%s"), strClass); + #else + (void)StringCchPrintf(m_String, NUMELMS(m_String), TEXT("%S"), strClass); + #endif +}; + +#ifdef __STREAMS__ +/* Display stuff */ +CDisp::CDisp(CRefTime llTime) +{ + LPTSTR lpsz = m_String; + size_t len = NUMELMS(m_String); + LONGLONG llDiv; + if (llTime < 0) { + llTime = -llTime; + (void)StringCchPrintf(lpsz, len, TEXT("-")); + size_t t = lstrlen(lpsz); + lpsz += t; + len -= t; + } + llDiv = (LONGLONG)24 * 3600 * 10000000; + if (llTime >= llDiv) { + (void)StringCchPrintf(lpsz, len, TEXT("%d days "), (LONG)(llTime / llDiv)); + size_t t = lstrlen(lpsz); + lpsz += t; + len -= t; + llTime = llTime % llDiv; + } + llDiv = (LONGLONG)3600 * 10000000; + if (llTime >= llDiv) { + (void)StringCchPrintf(lpsz, len, TEXT("%d hrs "), (LONG)(llTime / llDiv)); + size_t t = lstrlen(lpsz); + lpsz += t; + len -= t; + llTime = llTime % llDiv; + } + llDiv = (LONGLONG)60 * 10000000; + if (llTime >= llDiv) { + (void)StringCchPrintf(lpsz, len, TEXT("%d mins "), (LONG)(llTime / llDiv)); + size_t t = lstrlen(lpsz); + lpsz += t; + len -= t; + llTime = llTime % llDiv; + } + (void)StringCchPrintf(lpsz, len, TEXT("%d.%3.3d sec"), + (LONG)llTime / 10000000, + (LONG)((llTime % 10000000) / 10000)); +}; + +#endif // __STREAMS__ + + +/* Display pin */ +CDisp::CDisp(IPin *pPin) +{ + PIN_INFO pi; + TCHAR str[MAX_PIN_NAME]; + CLSID clsid; + + if (pPin) { + pPin->QueryPinInfo(&pi); + pi.pFilter->GetClassID(&clsid); + QueryPinInfoReleaseFilter(pi); + #ifndef UNICODE + WideCharToMultiByte(GetACP(), 0, pi.achName, lstrlenW(pi.achName) + 1, + str, MAX_PIN_NAME, NULL, NULL); + #else + (void)StringCchCopy(str, NUMELMS(str), pi.achName); + #endif + } else { + (void)StringCchCopy(str, NUMELMS(str), TEXT("NULL IPin")); + } + + size_t len = lstrlen(str)+64; + m_pString = (PTCHAR) new TCHAR[len]; + if (!m_pString) { + return; + } + + #ifdef UNICODE + LPCTSTR FORMAT_STRING = TEXT("%S(%s)"); + #else + LPCTSTR FORMAT_STRING = TEXT("%s(%s)"); + #endif + + (void)StringCchPrintf(m_pString, len, FORMAT_STRING, GuidNames[clsid], str); +} + +/* Display filter or pin */ +CDisp::CDisp(IUnknown *pUnk) +{ + IBaseFilter *pf; + HRESULT hr = pUnk->QueryInterface(IID_IBaseFilter, (void **)&pf); + if(SUCCEEDED(hr)) + { + FILTER_INFO fi; + hr = pf->QueryFilterInfo(&fi); + if(SUCCEEDED(hr)) + { + QueryFilterInfoReleaseGraph(fi); + + size_t len = lstrlenW(fi.achName) + 1; + m_pString = new TCHAR[len]; + if(m_pString) + { + #ifdef UNICODE + LPCTSTR FORMAT_STRING = TEXT("%s"); + #else + LPCTSTR FORMAT_STRING = TEXT("%S"); + #endif + + (void)StringCchPrintf(m_pString, len, FORMAT_STRING, fi.achName); + } + } + + pf->Release(); + + return; + } + + IPin *pp; + hr = pUnk->QueryInterface(IID_IPin, (void **)&pp); + if(SUCCEEDED(hr)) + { + CDisp::CDisp(pp); + pp->Release(); + return; + } +} + + +CDisp::~CDisp() +{ +} + +CDispBasic::~CDispBasic() +{ + if (m_pString != m_String) { + delete [] m_pString; + } +} + +CDisp::CDisp(double d) +{ +#ifdef DEBUG + (void)StringCchPrintf(m_String, NUMELMS(m_String), TEXT("%.16g"), d); +#else + (void)StringCchPrintf(m_String, NUMELMS(m_String), TEXT("%d.%03d"), (int) d, (int) ((d - (int) d) * 1000)); +#endif +} + + +/* If built for debug this will display the media type details. We convert the + major and subtypes into strings and also ask the base classes for a string + description of the subtype, so MEDIASUBTYPE_RGB565 becomes RGB 565 16 bit + We also display the fields in the BITMAPINFOHEADER structure, this should + succeed as we do not accept input types unless the format is big enough */ + +#ifdef DEBUG +void WINAPI DisplayType(LPTSTR label, const AM_MEDIA_TYPE *pmtIn) +{ + + /* Dump the GUID types and a short description */ + + DbgLog((LOG_TRACE,5,TEXT(""))); + DbgLog((LOG_TRACE,2,TEXT("%s M type %hs S type %hs"), label, + GuidNames[pmtIn->majortype], + GuidNames[pmtIn->subtype])); + DbgLog((LOG_TRACE,5,TEXT("Subtype description %s"),GetSubtypeName(&pmtIn->subtype))); + + /* Dump the generic media types */ + + if (pmtIn->bTemporalCompression) { + DbgLog((LOG_TRACE,5,TEXT("Temporally compressed"))); + } else { + DbgLog((LOG_TRACE,5,TEXT("Not temporally compressed"))); + } + + if (pmtIn->bFixedSizeSamples) { + DbgLog((LOG_TRACE,5,TEXT("Sample size %d"),pmtIn->lSampleSize)); + } else { + DbgLog((LOG_TRACE,5,TEXT("Variable size samples"))); + } + + if (pmtIn->formattype == FORMAT_VideoInfo) { + /* Dump the contents of the BITMAPINFOHEADER structure */ + BITMAPINFOHEADER *pbmi = HEADER(pmtIn->pbFormat); + VIDEOINFOHEADER *pVideoInfo = (VIDEOINFOHEADER *)pmtIn->pbFormat; + + DbgLog((LOG_TRACE,5,TEXT("Source rectangle (Left %d Top %d Right %d Bottom %d)"), + pVideoInfo->rcSource.left, + pVideoInfo->rcSource.top, + pVideoInfo->rcSource.right, + pVideoInfo->rcSource.bottom)); + + DbgLog((LOG_TRACE,5,TEXT("Target rectangle (Left %d Top %d Right %d Bottom %d)"), + pVideoInfo->rcTarget.left, + pVideoInfo->rcTarget.top, + pVideoInfo->rcTarget.right, + pVideoInfo->rcTarget.bottom)); + + DbgLog((LOG_TRACE,5,TEXT("Size of BITMAPINFO structure %d"),pbmi->biSize)); + if (pbmi->biCompression < 256) { + DbgLog((LOG_TRACE,2,TEXT("%dx%dx%d bit (%d)"), + pbmi->biWidth, pbmi->biHeight, + pbmi->biBitCount, pbmi->biCompression)); + } else { + DbgLog((LOG_TRACE,2,TEXT("%dx%dx%d bit '%4.4hs'"), + pbmi->biWidth, pbmi->biHeight, + pbmi->biBitCount, &pbmi->biCompression)); + } + + DbgLog((LOG_TRACE,2,TEXT("Image size %d"),pbmi->biSizeImage)); + DbgLog((LOG_TRACE,5,TEXT("Planes %d"),pbmi->biPlanes)); + DbgLog((LOG_TRACE,5,TEXT("X Pels per metre %d"),pbmi->biXPelsPerMeter)); + DbgLog((LOG_TRACE,5,TEXT("Y Pels per metre %d"),pbmi->biYPelsPerMeter)); + DbgLog((LOG_TRACE,5,TEXT("Colours used %d"),pbmi->biClrUsed)); + + } else if (pmtIn->majortype == MEDIATYPE_Audio) { + DbgLog((LOG_TRACE,2,TEXT(" Format type %hs"), + GuidNames[pmtIn->formattype])); + DbgLog((LOG_TRACE,2,TEXT(" Subtype %hs"), + GuidNames[pmtIn->subtype])); + + if ((pmtIn->subtype != MEDIASUBTYPE_MPEG1Packet) + && (pmtIn->cbFormat >= sizeof(PCMWAVEFORMAT))) + { + /* Dump the contents of the WAVEFORMATEX type-specific format structure */ + + WAVEFORMATEX *pwfx = (WAVEFORMATEX *) pmtIn->pbFormat; + DbgLog((LOG_TRACE,2,TEXT("wFormatTag %u"), pwfx->wFormatTag)); + DbgLog((LOG_TRACE,2,TEXT("nChannels %u"), pwfx->nChannels)); + DbgLog((LOG_TRACE,2,TEXT("nSamplesPerSec %lu"), pwfx->nSamplesPerSec)); + DbgLog((LOG_TRACE,2,TEXT("nAvgBytesPerSec %lu"), pwfx->nAvgBytesPerSec)); + DbgLog((LOG_TRACE,2,TEXT("nBlockAlign %u"), pwfx->nBlockAlign)); + DbgLog((LOG_TRACE,2,TEXT("wBitsPerSample %u"), pwfx->wBitsPerSample)); + + /* PCM uses a WAVEFORMAT and does not have the extra size field */ + + if (pmtIn->cbFormat >= sizeof(WAVEFORMATEX)) { + DbgLog((LOG_TRACE,2,TEXT("cbSize %u"), pwfx->cbSize)); + } + } else { + } + + } else { + DbgLog((LOG_TRACE,2,TEXT(" Format type %hs"), + GuidNames[pmtIn->formattype])); + // !!!! should add code to dump wave format, others + } +} + + +void WINAPI DumpGraph(IFilterGraph *pGraph, DWORD dwLevel) +{ + if( !pGraph ) + { + return; + } + + IEnumFilters *pFilters; + + DbgLog((LOG_TRACE,dwLevel,TEXT("DumpGraph [%x]"), pGraph)); + + if (FAILED(pGraph->EnumFilters(&pFilters))) { + DbgLog((LOG_TRACE,dwLevel,TEXT("EnumFilters failed!"))); + } + + IBaseFilter *pFilter; + ULONG n; + while (pFilters->Next(1, &pFilter, &n) == S_OK) { + FILTER_INFO info; + + if (FAILED(pFilter->QueryFilterInfo(&info))) { + DbgLog((LOG_TRACE,dwLevel,TEXT(" Filter [%x] -- failed QueryFilterInfo"), pFilter)); + } else { + QueryFilterInfoReleaseGraph(info); + + // !!! should QueryVendorInfo here! + + DbgLog((LOG_TRACE,dwLevel,TEXT(" Filter [%x] '%ls'"), pFilter, info.achName)); + + IEnumPins *pins; + + if (FAILED(pFilter->EnumPins(&pins))) { + DbgLog((LOG_TRACE,dwLevel,TEXT("EnumPins failed!"))); + } else { + + IPin *pPin; + while (pins->Next(1, &pPin, &n) == S_OK) { + PIN_INFO info; + + if (FAILED(pPin->QueryPinInfo(&info))) { + DbgLog((LOG_TRACE,dwLevel,TEXT(" Pin [%x] -- failed QueryPinInfo"), pPin)); + } else { + QueryPinInfoReleaseFilter(info); + + IPin *pPinConnected = NULL; + + HRESULT hr = pPin->ConnectedTo(&pPinConnected); + + if (pPinConnected) { + DbgLog((LOG_TRACE,dwLevel,TEXT(" Pin [%x] '%ls' [%sput]") + TEXT(" Connected to pin [%x]"), + pPin, info.achName, + info.dir == PINDIR_INPUT ? TEXT("In") : TEXT("Out"), + pPinConnected)); + + pPinConnected->Release(); + + // perhaps we should really dump the type both ways as a sanity + // check? + if (info.dir == PINDIR_OUTPUT) { + AM_MEDIA_TYPE mt; + + hr = pPin->ConnectionMediaType(&mt); + + if (SUCCEEDED(hr)) { + DisplayType(TEXT("Connection type"), &mt); + + FreeMediaType(mt); + } + } + } else { + DbgLog((LOG_TRACE,dwLevel, + TEXT(" Pin [%x] '%ls' [%sput]"), + pPin, info.achName, + info.dir == PINDIR_INPUT ? TEXT("In") : TEXT("Out"))); + + } + } + + pPin->Release(); + + } + + pins->Release(); + } + + } + + pFilter->Release(); + } + + pFilters->Release(); + +} + +#endif + diff --git a/ThirdParty/strmbas/wxdebug.h b/ThirdParty/strmbas/wxdebug.h new file mode 100644 index 0000000..bc98967 --- /dev/null +++ b/ThirdParty/strmbas/wxdebug.h @@ -0,0 +1,393 @@ +//------------------------------------------------------------------------------ +// File: WXDebug.h +// +// Desc: DirectShow base classes - provides debugging facilities. +// +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------------------------ + + +#ifndef __WXDEBUG1__ +#define __WXDEBUG1__ + +// This library provides fairly straight forward debugging functionality, this +// is split into two main sections. The first is assertion handling, there are +// three types of assertions provided here. The most commonly used one is the +// ASSERT(condition) macro which will pop up a message box including the file +// and line number if the condition evaluates to FALSE. Then there is the +// EXECUTE_ASSERT macro which is the same as ASSERT except the condition will +// still be executed in NON debug builds. The final type of assertion is the +// KASSERT macro which is more suitable for pure (perhaps kernel) filters as +// the condition is printed onto the debugger rather than in a message box. +// +// The other part of the debug module facilties is general purpose logging. +// This is accessed by calling DbgLog(). The function takes a type and level +// field which define the type of informational string you are presenting and +// it's relative importance. The type field can be a combination (one or more) +// of LOG_TIMING, LOG_TRACE, LOG_MEMORY, LOG_LOCKING and LOG_ERROR. The level +// is a DWORD value where zero defines highest important. Use of zero as the +// debug logging level is to be encouraged ONLY for major errors or events as +// they will ALWAYS be displayed on the debugger. Other debug output has it's +// level matched against the current debug output level stored in the registry +// for this module and if less than the current setting it will be displayed. +// +// Each module or executable has it's own debug output level for each of the +// five types. These are read in when the DbgInitialise function is called +// for DLLs linking to STRMBASE.LIB this is done automatically when the DLL +// is loaded, executables must call it explicitely with the module instance +// handle given to them through the WINMAIN entry point. An executable must +// also call DbgTerminate when they have finished to clean up the resources +// the debug library uses, once again this is done automatically for DLLs + +// These are the five different categories of logging information + +enum { LOG_TIMING = 0x01, // Timing and performance measurements + LOG_TRACE = 0x02, // General step point call tracing + LOG_MEMORY = 0x04, // Memory and object allocation/destruction + LOG_LOCKING = 0x08, // Locking/unlocking of critical sections + LOG_ERROR = 0x10, // Debug error notification + LOG_CUSTOM1 = 0x20, + LOG_CUSTOM2 = 0x40, + LOG_CUSTOM3 = 0x80, + LOG_CUSTOM4 = 0x100, + LOG_CUSTOM5 = 0x200, +}; + +#define LOG_FORCIBLY_SET 0x80000000 + +enum { CDISP_HEX = 0x01, + CDISP_DEC = 0x02}; + +// For each object created derived from CBaseObject (in debug builds) we +// create a descriptor that holds it's name (statically allocated memory) +// and a cookie we assign it. We keep a list of all the active objects +// we have registered so that we can dump a list of remaining objects + +typedef struct tag_ObjectDesc { + const CHAR *m_szName; + const WCHAR *m_wszName; + DWORD m_dwCookie; + tag_ObjectDesc *m_pNext; +} ObjectDesc; + +#define DLLIMPORT __declspec(dllimport) +#define DLLEXPORT __declspec(dllexport) + +#ifdef DEBUG + + #define NAME(x) TEXT(x) + + // These are used internally by the debug library (PRIVATE) + + void WINAPI DbgInitKeyLevels(HKEY hKey, bool fTakeMax); + void WINAPI DbgInitGlobalSettings(bool fTakeMax); + void WINAPI DbgInitModuleSettings(bool fTakeMax); + void WINAPI DbgInitModuleName(); + DWORD WINAPI DbgRegisterObjectCreation( + const CHAR *szObjectName, const WCHAR *wszObjectName); + + BOOL WINAPI DbgRegisterObjectDestruction(DWORD dwCookie); + + // These are the PUBLIC entry points + + BOOL WINAPI DbgCheckModuleLevel(DWORD Type,DWORD Level); + void WINAPI DbgSetModuleLevel(DWORD Type,DWORD Level); + void WINAPI DbgSetAutoRefreshLevels(bool fAuto); + + // Initialise the library with the module handle + + void WINAPI DbgInitialise(HINSTANCE hInst); + void WINAPI DbgTerminate(); + + void WINAPI DbgDumpObjectRegister(); + + // Display error and logging to the user + + void WINAPI DbgAssert(const TCHAR *pCondition,const TCHAR *pFileName,INT iLine); + void WINAPI DbgBreakPoint(const TCHAR *pCondition,const TCHAR *pFileName,INT iLine); + void WINAPI DbgBreakPoint(const TCHAR *pFileName,INT iLine,const TCHAR* szFormatString,...); + + void WINAPI DbgKernelAssert(const TCHAR *pCondition,const TCHAR *pFileName,INT iLine); + void WINAPI DbgLogInfo(DWORD Type,DWORD Level,const TCHAR *pFormat,...); +#ifdef UNICODE + void WINAPI DbgLogInfo(DWORD Type,DWORD Level,const CHAR *pFormat,...); + void WINAPI DbgAssert(const CHAR *pCondition,const CHAR *pFileName,INT iLine); + void WINAPI DbgBreakPoint(const CHAR *pCondition,const CHAR *pFileName,INT iLine); + void WINAPI DbgKernelAssert(const CHAR *pCondition,const CHAR *pFileName,INT iLine); +#endif + void WINAPI DbgOutString(LPCTSTR psz); + + // Debug infinite wait stuff + DWORD WINAPI DbgWaitForSingleObject(HANDLE h); + DWORD WINAPI DbgWaitForMultipleObjects(DWORD nCount, + CONST HANDLE *lpHandles, + BOOL bWaitAll); + void WINAPI DbgSetWaitTimeout(DWORD dwTimeout); + +#ifdef __strmif_h__ + // Display a media type: Terse at level 2, verbose at level 5 + void WINAPI DisplayType(LPTSTR label, const AM_MEDIA_TYPE *pmtIn); + + // Dump lots of information about a filter graph + void WINAPI DumpGraph(IFilterGraph *pGraph, DWORD dwLevel); +#endif + + #define KASSERT(_x_) if (!(_x_)) \ + DbgKernelAssert(TEXT(#_x_),TEXT(__FILE__),__LINE__) + + // Break on the debugger without putting up a message box + // message goes to debugger instead + + #define KDbgBreak(_x_) \ + DbgKernelAssert(TEXT(#_x_),TEXT(__FILE__),__LINE__) + + // We chose a common name for our ASSERT macro, MFC also uses this name + // So long as the implementation evaluates the condition and handles it + // then we will be ok. Rather than override the behaviour expected we + // will leave whatever first defines ASSERT as the handler (i.e. MFC) + #ifndef ASSERT + #define ASSERT(_x_) if (!(_x_)) \ + DbgAssert(TEXT(#_x_),TEXT(__FILE__),__LINE__) + #endif + + #define DbgAssertAligned( _ptr_, _alignment_ ) ASSERT( ((DWORD_PTR) (_ptr_)) % (_alignment_) == 0) + + // Put up a message box informing the user of a halt + // condition in the program + + #define DbgBreak(_x_) \ + DbgBreakPoint(TEXT(#_x_),TEXT(__FILE__),__LINE__) + + #define EXECUTE_ASSERT(_x_) ASSERT(_x_) + #define DbgLog(_x_) DbgLogInfo _x_ + // MFC style trace macros + + #define NOTE(_x_) DbgLog((LOG_TRACE,5,TEXT(_x_))) + #define NOTE1(_x_,a) DbgLog((LOG_TRACE,5,TEXT(_x_),a)) + #define NOTE2(_x_,a,b) DbgLog((LOG_TRACE,5,TEXT(_x_),a,b)) + #define NOTE3(_x_,a,b,c) DbgLog((LOG_TRACE,5,TEXT(_x_),a,b,c)) + #define NOTE4(_x_,a,b,c,d) DbgLog((LOG_TRACE,5,TEXT(_x_),a,b,c,d)) + #define NOTE5(_x_,a,b,c,d,e) DbgLog((LOG_TRACE,5,TEXT(_x_),a,b,c,d,e)) + +#else + + // Retail builds make public debug functions inert - WARNING the source + // files do not define or build any of the entry points in debug builds + // (public entry points compile to nothing) so if you go trying to call + // any of the private entry points in your source they won't compile + + #define NAME(_x_) ((TCHAR *) NULL) + + #define DbgInitialise(hInst) + #define DbgTerminate() + #define DbgLog(_x_) 0 + #define DbgOutString(psz) + #define DbgAssertAligned( _ptr_, _alignment_ ) 0 + + #define DbgRegisterObjectCreation(pObjectName) + #define DbgRegisterObjectDestruction(dwCookie) + #define DbgDumpObjectRegister() + + #define DbgCheckModuleLevel(Type,Level) + #define DbgSetModuleLevel(Type,Level) + #define DbgSetAutoRefreshLevels(fAuto) + + #define DbgWaitForSingleObject(h) WaitForSingleObject(h, INFINITE) + #define DbgWaitForMultipleObjects(nCount, lpHandles, bWaitAll) \ + WaitForMultipleObjects(nCount, lpHandles, bWaitAll, INFINITE) + #define DbgSetWaitTimeout(dwTimeout) + + #define KDbgBreak(_x_) + #define DbgBreak(_x_) + + #define KASSERT(_x_) ((void)0) + #ifndef ASSERT + #define ASSERT(_x_) ((void)0) + #endif + #define EXECUTE_ASSERT(_x_) ((void)(_x_)) + + // MFC style trace macros + + #define NOTE(_x_) ((void)0) + #define NOTE1(_x_,a) ((void)0) + #define NOTE2(_x_,a,b) ((void)0) + #define NOTE3(_x_,a,b,c) ((void)0) + #define NOTE4(_x_,a,b,c,d) ((void)0) + #define NOTE5(_x_,a,b,c,d,e) ((void)0) + + #define DisplayType(label, pmtIn) ((void)0) + #define DumpGraph(pGraph, label) ((void)0) +#endif + + +// Checks a pointer which should be non NULL - can be used as follows. + +#define CheckPointer(p,ret) {if((p)==NULL) return (ret);} + +// HRESULT Foo(VOID *pBar) +// { +// CheckPointer(pBar,E_INVALIDARG) +// } +// +// Or if the function returns a boolean +// +// BOOL Foo(VOID *pBar) +// { +// CheckPointer(pBar,FALSE) +// } + +// These validate pointers when symbol VFWROBUST is defined +// This will normally be defined in debug not retail builds + +#ifdef DEBUG + #define VFWROBUST +#endif + +#ifdef VFWROBUST + + #define ValidateReadPtr(p,cb) \ + {if(IsBadReadPtr((PVOID)p,cb) == TRUE) \ + DbgBreak("Invalid read pointer");} + + #define ValidateWritePtr(p,cb) \ + {if(IsBadWritePtr((PVOID)p,cb) == TRUE) \ + DbgBreak("Invalid write pointer");} + + #define ValidateReadWritePtr(p,cb) \ + {ValidateReadPtr(p,cb) ValidateWritePtr(p,cb)} + + #define ValidateStringPtr(p) \ + {if(IsBadStringPtr((LPCTSTR)p,INFINITE) == TRUE) \ + DbgBreak("Invalid string pointer");} + + #define ValidateStringPtrA(p) \ + {if(IsBadStringPtrA((LPCSTR)p,INFINITE) == TRUE) \ + DbgBreak("Invalid ANSI string pointer");} + + #define ValidateStringPtrW(p) \ + {if(IsBadStringPtrW((LPCWSTR)p,INFINITE) == TRUE) \ + DbgBreak("Invalid UNICODE string pointer");} + +#else + #define ValidateReadPtr(p,cb) 0 + #define ValidateWritePtr(p,cb) 0 + #define ValidateReadWritePtr(p,cb) 0 + #define ValidateStringPtr(p) 0 + #define ValidateStringPtrA(p) 0 + #define ValidateStringPtrW(p) 0 +#endif + + +#ifdef _OBJBASE_H_ + + // Outputting GUID names. If you want to include the name + // associated with a GUID (eg CLSID_...) then + // + // GuidNames[yourGUID] + // + // Returns the name defined in uuids.h as a string + + typedef struct { + CHAR *szName; + GUID guid; + } GUID_STRING_ENTRY; + + class CGuidNameList { + public: + CHAR *operator [] (const GUID& guid); + }; + + extern CGuidNameList GuidNames; + +#endif + +#ifndef REMIND + // REMIND macro - generates warning as reminder to complete coding + // (eg) usage: + // + // #pragma message (REMIND("Add automation support")) + + + #define QUOTE(x) #x + #define QQUOTE(y) QUOTE(y) + #define REMIND(str) __FILE__ "(" QQUOTE(__LINE__) ") : " str +#endif + +// Method to display objects in a useful format +// +// eg If you want to display a LONGLONG ll in a debug string do (eg) +// +// DbgLog((LOG_TRACE, n, TEXT("Value is %s"), (LPCTSTR)CDisp(ll, CDISP_HEX))); + + +class CDispBasic +{ +public: + CDispBasic() { m_pString = m_String; }; + ~CDispBasic(); +protected: + PTCHAR m_pString; // normally points to m_String... unless too much data + TCHAR m_String[50]; +}; +class CDisp : public CDispBasic +{ +public: + CDisp(LONGLONG ll, int Format = CDISP_HEX); // Display a LONGLONG in CDISP_HEX or CDISP_DEC form + CDisp(REFCLSID clsid); // Display a GUID + CDisp(double d); // Display a floating point number +#ifdef __strmif_h__ +#ifdef __STREAMS__ + CDisp(CRefTime t); // Display a Reference Time +#endif + CDisp(IPin *pPin); // Display a pin as {filter clsid}(pin name) + CDisp(IUnknown *pUnk); // Display a filter or pin +#endif // __strmif_h__ + ~CDisp(); + + // Implement cast to (LPCTSTR) as parameter to logger + operator LPCTSTR() + { + return (LPCTSTR)m_pString; + }; +}; + + +#if defined(DEBUG) +class CAutoTrace +{ +private: + const TCHAR* _szBlkName; + const int _level; + static const TCHAR _szEntering[]; + static const TCHAR _szLeaving[]; +public: + CAutoTrace(const TCHAR* szBlkName, const int level = 15) + : _szBlkName(szBlkName), _level(level) + {DbgLog((LOG_TRACE, _level, _szEntering, _szBlkName));} + + ~CAutoTrace() + {DbgLog((LOG_TRACE, _level, _szLeaving, _szBlkName));} +}; + +#if defined (__FUNCTION__) + +#define AMTRACEFN() CAutoTrace __trace(TEXT(__FUNCTION__)) +#define AMTRACE(_x_) CAutoTrace __trace(TEXT(__FUNCTION__)) + +#else + +#define AMTRACE(_x_) CAutoTrace __trace _x_ +#define AMTRACEFN() + +#endif + +#else + +#define AMTRACE(_x_) +#define AMTRACEFN() + +#endif + +#endif // __WXDEBUG__ + + diff --git a/ThirdParty/strmbas/wxlist.cpp b/ThirdParty/strmbas/wxlist.cpp new file mode 100644 index 0000000..9657f09 --- /dev/null +++ b/ThirdParty/strmbas/wxlist.cpp @@ -0,0 +1,885 @@ +//------------------------------------------------------------------------------ +// File: WXList.cpp +// +// Desc: DirectShow base classes - implements a non-MFC based generic list +// template class. +// +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------------------------ + + +/* A generic list of pointers to objects. + Objectives: avoid using MFC libraries in ndm kernel mode and + provide a really useful list type. + + The class is thread safe in that separate threads may add and + delete items in the list concurrently although the application + must ensure that constructor and destructor access is suitably + synchronised. + + The list name must not conflict with MFC classes as an + application may use both + + The nodes form a doubly linked, NULL terminated chain with an anchor + block (the list object per se) holding pointers to the first and last + nodes and a count of the nodes. + There is a node cache to reduce the allocation and freeing overhead. + It optionally (determined at construction time) has an Event which is + set whenever the list becomes non-empty and reset whenever it becomes + empty. + It optionally (determined at construction time) has a Critical Section + which is entered during the important part of each operation. (About + all you can do outside it is some parameter checking). + + The node cache is a repository of nodes that are NOT in the list to speed + up storage allocation. Each list has its own cache to reduce locking and + serialising. The list accesses are serialised anyway for a given list - a + common cache would mean that we would have to separately serialise access + of all lists within the cache. Because the cache only stores nodes that are + not in the list, releasing the cache does not release any list nodes. This + means that list nodes can be copied or rechained from one list to another + without danger of creating a dangling reference if the original cache goes + away. + + Questionable design decisions: + 1. Retaining the warts for compatibility + 2. Keeping an element count -i.e. counting whenever we do anything + instead of only when we want the count. + 3. Making the chain pointers NULL terminated. If the list object + itself looks just like a node and the list is kept as a ring then + it reduces the number of special cases. All inserts look the same. +*/ + + +#include + +/* set cursor to the position of each element of list in turn */ +#define INTERNALTRAVERSELIST(list, cursor) \ +for ( cursor = (list).GetHeadPositionI() \ + ; cursor!=NULL \ + ; cursor = (list).Next(cursor) \ + ) + + +/* set cursor to the position of each element of list in turn + in reverse order +*/ +#define INTERNALREVERSETRAVERSELIST(list, cursor) \ +for ( cursor = (list).GetTailPositionI() \ + ; cursor!=NULL \ + ; cursor = (list).Prev(cursor) \ + ) + +/* Constructor calls a separate initialisation function that + creates a node cache, optionally creates a lock object + and optionally creates a signaling object. + + By default we create a locking object, a DEFAULTCACHE sized + cache but no event object so the list cannot be used in calls + to WaitForSingleObject +*/ +CBaseList::CBaseList(TCHAR *pName, // Descriptive list name + INT iItems) : // Node cache size +#ifdef DEBUG + CBaseObject(pName), +#endif + m_pFirst(NULL), + m_pLast(NULL), + m_Count(0), + m_Cache(iItems) +{ +} // constructor + +CBaseList::CBaseList(TCHAR *pName) : // Descriptive list name +#ifdef DEBUG + CBaseObject(pName), +#endif + m_pFirst(NULL), + m_pLast(NULL), + m_Count(0), + m_Cache(DEFAULTCACHE) +{ +} // constructor + +#ifdef UNICODE +CBaseList::CBaseList(CHAR *pName, // Descriptive list name + INT iItems) : // Node cache size +#ifdef DEBUG + CBaseObject(pName), +#endif + m_pFirst(NULL), + m_pLast(NULL), + m_Count(0), + m_Cache(iItems) +{ +} // constructor + +CBaseList::CBaseList(CHAR *pName) : // Descriptive list name +#ifdef DEBUG + CBaseObject(pName), +#endif + m_pFirst(NULL), + m_pLast(NULL), + m_Count(0), + m_Cache(DEFAULTCACHE) +{ +} // constructor + +#endif + +/* The destructor enumerates all the node objects in the list and + in the cache deleting each in turn. We do not do any processing + on the objects that the list holds (i.e. points to) so if they + represent interfaces for example the creator of the list should + ensure that each of them is released before deleting us +*/ +CBaseList::~CBaseList() +{ + /* Delete all our list nodes */ + + RemoveAll(); + +} // destructor + +/* Remove all the nodes from the list but don't do anything + with the objects that each node looks after (this is the + responsibility of the creator). + Aa a last act we reset the signalling event + (if available) to indicate to clients that the list + does not have any entries in it. +*/ +void CBaseList::RemoveAll() +{ + /* Free up all the CNode objects NOTE we don't bother putting the + deleted nodes into the cache as this method is only really called + in serious times of change such as when we are being deleted at + which point the cache will be deleted anway */ + + CNode *pn = m_pFirst; + while (pn) { + CNode *op = pn; + pn = pn->Next(); + delete op; + } + + /* Reset the object count and the list pointers */ + + m_Count = 0; + m_pFirst = m_pLast = NULL; + +} // RemoveAll + + + +/* Return a position enumerator for the entire list. + A position enumerator is a pointer to a node object cast to a + transparent type so all we do is return the head/tail node + pointer in the list. + WARNING because the position is a pointer to a node there is + an implicit assumption for users a the list class that after + deleting an object from the list that any other position + enumerators that you have may be invalid (since the node + may be gone). +*/ +POSITION CBaseList::GetHeadPositionI() const +{ + return (POSITION) m_pFirst; +} // GetHeadPosition + + + +POSITION CBaseList::GetTailPositionI() const +{ + return (POSITION) m_pLast; +} // GetTailPosition + + + +/* Get the number of objects in the list, + Get the lock before accessing the count. + Locking may not be entirely necessary but it has the side effect + of making sure that all operations are complete before we get it. + So for example if a list is being added to this list then that + will have completed in full before we continue rather than seeing + an intermediate albeit valid state +*/ +int CBaseList::GetCountI() const +{ + return m_Count; +} // GetCount + + + +/* Return the object at rp, update rp to the next object from + the list or NULL if you have moved over the last object. + You may still call this function once we return NULL but + we will continue to return a NULL position value +*/ +void *CBaseList::GetNextI(POSITION& rp) const +{ + /* have we reached the end of the list */ + + if (rp == NULL) { + return NULL; + } + + /* Lock the object before continuing */ + + void *pObject; + + /* Copy the original position then step on */ + + CNode *pn = (CNode *) rp; + ASSERT(pn != NULL); + rp = (POSITION) pn->Next(); + + /* Get the object at the original position from the list */ + + pObject = pn->GetData(); + // ASSERT(pObject != NULL); // NULL pointers in the list are allowed. + return pObject; +} //GetNext + + + +/* Return the object at p. + Asking for the object at NULL ASSERTs then returns NULL + The object is NOT locked. The list is not being changed + in any way. If another thread is busy deleting the object + then locking would only result in a change from one bad + behaviour to another. +*/ +void *CBaseList::GetI(POSITION p) const +{ + if (p == NULL) { + return NULL; + } + + CNode * pn = (CNode *) p; + void *pObject = pn->GetData(); + // ASSERT(pObject != NULL); // NULL pointers in the list are allowed. + return pObject; +} //Get + + + +/* Return the first position in the list which holds the given pointer. + Return NULL if it's not found. +*/ +POSITION CBaseList::FindI( void * pObj) const +{ + POSITION pn; + INTERNALTRAVERSELIST(*this, pn){ + if (GetI(pn)==pObj) { + return pn; + } + } + return NULL; +} // Find + + + +/* Remove the first node in the list (deletes the pointer to its object + from the list, does not free the object itself). + Return the pointer to its object or NULL if empty +*/ +void *CBaseList::RemoveHeadI() +{ + /* All we do is get the head position and ask for that to be deleted. + We could special case this since some of the code path checking + in Remove() is redundant as we know there is no previous + node for example but it seems to gain little over the + added complexity + */ + + return RemoveI((POSITION)m_pFirst); +} // RemoveHead + + + +/* Remove the last node in the list (deletes the pointer to its object + from the list, does not free the object itself). + Return the pointer to its object or NULL if empty +*/ +void *CBaseList::RemoveTailI() +{ + /* All we do is get the tail position and ask for that to be deleted. + We could special case this since some of the code path checking + in Remove() is redundant as we know there is no previous + node for example but it seems to gain little over the + added complexity + */ + + return RemoveI((POSITION)m_pLast); +} // RemoveTail + + + +/* Remove the pointer to the object in this position from the list. + Deal with all the chain pointers + Return a pointer to the object removed from the list. + The node object that is freed as a result + of this operation is added to the node cache where + it can be used again. + Remove(NULL) is a harmless no-op - but probably is a wart. +*/ +void *CBaseList::RemoveI(POSITION pos) +{ + /* Lock the critical section before continuing */ + + // ASSERT (pos!=NULL); // Removing NULL is to be harmless! + if (pos==NULL) return NULL; + + + CNode *pCurrent = (CNode *) pos; + ASSERT(pCurrent != NULL); + + /* Update the previous node */ + + CNode *pNode = pCurrent->Prev(); + if (pNode == NULL) { + m_pFirst = pCurrent->Next(); + } else { + pNode->SetNext(pCurrent->Next()); + } + + /* Update the following node */ + + pNode = pCurrent->Next(); + if (pNode == NULL) { + m_pLast = pCurrent->Prev(); + } else { + pNode->SetPrev(pCurrent->Prev()); + } + + /* Get the object this node was looking after */ + + void *pObject = pCurrent->GetData(); + + // ASSERT(pObject != NULL); // NULL pointers in the list are allowed. + + /* Try and add the node object to the cache - + a NULL return code from the cache means we ran out of room. + The cache size is fixed by a constructor argument when the + list is created and defaults to DEFAULTCACHE. + This means that the cache will have room for this many + node objects. So if you have a list of media samples + and you know there will never be more than five active at + any given time of them for example then override the default + constructor + */ + + m_Cache.AddToCache(pCurrent); + + /* If the list is empty then reset the list event */ + + --m_Count; + ASSERT(m_Count >= 0); + return pObject; +} // Remove + + + +/* Add this object to the tail end of our list + Return the new tail position. +*/ + +POSITION CBaseList::AddTailI(void *pObject) +{ + /* Lock the critical section before continuing */ + + CNode *pNode; + // ASSERT(pObject); // NULL pointers in the list are allowed. + + /* If there is a node objects in the cache then use + that otherwise we will have to create a new one */ + + pNode = (CNode *) m_Cache.RemoveFromCache(); + if (pNode == NULL) { + pNode = new CNode; + } + + /* Check we have a valid object */ + + if (pNode == NULL) { + return NULL; + } + + /* Initialise all the CNode object + just in case it came from the cache + */ + + pNode->SetData(pObject); + pNode->SetNext(NULL); + pNode->SetPrev(m_pLast); + + if (m_pLast == NULL) { + m_pFirst = pNode; + } else { + m_pLast->SetNext(pNode); + } + + /* Set the new last node pointer and also increment the number + of list entries, the critical section is unlocked when we + exit the function + */ + + m_pLast = pNode; + ++m_Count; + + return (POSITION) pNode; +} // AddTail(object) + + + +/* Add this object to the head end of our list + Return the new head position. +*/ +POSITION CBaseList::AddHeadI(void *pObject) +{ + CNode *pNode; + // ASSERT(pObject); // NULL pointers in the list are allowed. + + /* If there is a node objects in the cache then use + that otherwise we will have to create a new one */ + + pNode = (CNode *) m_Cache.RemoveFromCache(); + if (pNode == NULL) { + pNode = new CNode; + } + + /* Check we have a valid object */ + + if (pNode == NULL) { + return NULL; + } + + /* Initialise all the CNode object + just in case it came from the cache + */ + + pNode->SetData(pObject); + + /* chain it in (set four pointers) */ + pNode->SetPrev(NULL); + pNode->SetNext(m_pFirst); + + if (m_pFirst == NULL) { + m_pLast = pNode; + } else { + m_pFirst->SetPrev(pNode); + } + m_pFirst = pNode; + + ++m_Count; + + return (POSITION) pNode; +} // AddHead(object) + + + +/* Add all the elements in *pList to the tail of this list. + Return TRUE if it all worked, FALSE if it didn't. + If it fails some elements may have been added. +*/ +BOOL CBaseList::AddTail(CBaseList *pList) +{ + /* lock the object before starting then enumerate + each entry in the source list and add them one by one to + our list (while still holding the object lock) + Lock the other list too. + */ + POSITION pos = pList->GetHeadPositionI(); + + while (pos) { + if (NULL == AddTailI(pList->GetNextI(pos))) { + return FALSE; + } + } + return TRUE; +} // AddTail(list) + + + +/* Add all the elements in *pList to the head of this list. + Return TRUE if it all worked, FALSE if it didn't. + If it fails some elements may have been added. +*/ +BOOL CBaseList::AddHead(CBaseList *pList) +{ + /* lock the object before starting then enumerate + each entry in the source list and add them one by one to + our list (while still holding the object lock) + Lock the other list too. + + To avoid reversing the list, traverse it backwards. + */ + + POSITION pos; + + INTERNALREVERSETRAVERSELIST(*pList, pos) { + if (NULL== AddHeadI(pList->GetI(pos))){ + return FALSE; + } + } + return TRUE; +} // AddHead(list) + + + +/* Add the object after position p + p is still valid after the operation. + AddAfter(NULL,x) adds x to the start - same as AddHead + Return the position of the new object, NULL if it failed +*/ +POSITION CBaseList::AddAfterI(POSITION pos, void * pObj) +{ + if (pos==NULL) + return AddHeadI(pObj); + + /* As someone else might be furkling with the list - + Lock the critical section before continuing + */ + CNode *pAfter = (CNode *) pos; + ASSERT(pAfter != NULL); + if (pAfter==m_pLast) + return AddTailI(pObj); + + /* set pnode to point to a new node, preferably from the cache */ + + CNode *pNode = (CNode *) m_Cache.RemoveFromCache(); + if (pNode == NULL) { + pNode = new CNode; + } + + /* Check we have a valid object */ + + if (pNode == NULL) { + return NULL; + } + + /* Initialise all the CNode object + just in case it came from the cache + */ + + pNode->SetData(pObj); + + /* It is to be added to the middle of the list - there is a before + and after node. Chain it after pAfter, before pBefore. + */ + CNode * pBefore = pAfter->Next(); + ASSERT(pBefore != NULL); + + /* chain it in (set four pointers) */ + pNode->SetPrev(pAfter); + pNode->SetNext(pBefore); + pBefore->SetPrev(pNode); + pAfter->SetNext(pNode); + + ++m_Count; + + return (POSITION) pNode; + +} // AddAfter(object) + + + +BOOL CBaseList::AddAfter(POSITION p, CBaseList *pList) +{ + POSITION pos; + INTERNALTRAVERSELIST(*pList, pos) { + /* p follows along the elements being added */ + p = AddAfterI(p, pList->GetI(pos)); + if (p==NULL) return FALSE; + } + return TRUE; +} // AddAfter(list) + + + +/* Mirror images: + Add the element or list after position p. + p is still valid after the operation. + AddBefore(NULL,x) adds x to the end - same as AddTail +*/ +POSITION CBaseList::AddBeforeI(POSITION pos, void * pObj) +{ + if (pos==NULL) + return AddTailI(pObj); + + /* set pnode to point to a new node, preferably from the cache */ + + CNode *pBefore = (CNode *) pos; + ASSERT(pBefore != NULL); + if (pBefore==m_pFirst) + return AddHeadI(pObj); + + CNode * pNode = (CNode *) m_Cache.RemoveFromCache(); + if (pNode == NULL) { + pNode = new CNode; + } + + /* Check we have a valid object */ + + if (pNode == NULL) { + return NULL; + } + + /* Initialise all the CNode object + just in case it came from the cache + */ + + pNode->SetData(pObj); + + /* It is to be added to the middle of the list - there is a before + and after node. Chain it after pAfter, before pBefore. + */ + + CNode * pAfter = pBefore->Prev(); + ASSERT(pAfter != NULL); + + /* chain it in (set four pointers) */ + pNode->SetPrev(pAfter); + pNode->SetNext(pBefore); + pBefore->SetPrev(pNode); + pAfter->SetNext(pNode); + + ++m_Count; + + return (POSITION) pNode; + +} // Addbefore(object) + + + +BOOL CBaseList::AddBefore(POSITION p, CBaseList *pList) +{ + POSITION pos; + INTERNALREVERSETRAVERSELIST(*pList, pos) { + /* p follows along the elements being added */ + p = AddBeforeI(p, pList->GetI(pos)); + if (p==NULL) return FALSE; + } + return TRUE; +} // AddBefore(list) + + + +/* Split *this after position p in *this + Retain as *this the tail portion of the original *this + Add the head portion to the tail end of *pList + Return TRUE if it all worked, FALSE if it didn't. + + e.g. + foo->MoveToTail(foo->GetHeadPosition(), bar); + moves one element from the head of foo to the tail of bar + foo->MoveToTail(NULL, bar); + is a no-op + foo->MoveToTail(foo->GetTailPosition, bar); + concatenates foo onto the end of bar and empties foo. + + A better, except excessively long name might be + MoveElementsFromHeadThroughPositionToOtherTail +*/ +BOOL CBaseList::MoveToTail + (POSITION pos, CBaseList *pList) +{ + /* Algorithm: + Note that the elements (including their order) in the concatenation + of *pList to the head of *this is invariant. + 1. Count elements to be moved + 2. Join *pList onto the head of this to make one long chain + 3. Set first/Last pointers in *this and *pList + 4. Break the chain at the new place + 5. Adjust counts + 6. Set/Reset any events + */ + + if (pos==NULL) return TRUE; // no-op. Eliminates special cases later. + + + /* Make cMove the number of nodes to move */ + CNode * p = (CNode *)pos; + int cMove = 0; // number of nodes to move + while(p!=NULL) { + p = p->Prev(); + ++cMove; + } + + + /* Join the two chains together */ + if (pList->m_pLast!=NULL) + pList->m_pLast->SetNext(m_pFirst); + if (m_pFirst!=NULL) + m_pFirst->SetPrev(pList->m_pLast); + + + /* set first and last pointers */ + p = (CNode *)pos; + + if (pList->m_pFirst==NULL) + pList->m_pFirst = m_pFirst; + m_pFirst = p->Next(); + if (m_pFirst==NULL) + m_pLast = NULL; + pList->m_pLast = p; + + + /* Break the chain after p to create the new pieces */ + if (m_pFirst!=NULL) + m_pFirst->SetPrev(NULL); + p->SetNext(NULL); + + + /* Adjust the counts */ + m_Count -= cMove; + pList->m_Count += cMove; + + return TRUE; + +} // MoveToTail + + + +/* Mirror image of MoveToTail: + Split *this before position p in *this. + Retain in *this the head portion of the original *this + Add the tail portion to the start (i.e. head) of *pList + Return TRUE if it all worked, FALSE if it didn't. + + e.g. + foo->MoveToHead(foo->GetTailPosition(), bar); + moves one element from the tail of foo to the head of bar + foo->MoveToHead(NULL, bar); + is a no-op + foo->MoveToHead(foo->GetHeadPosition, bar); + concatenates foo onto the start of bar and empties foo. +*/ +BOOL CBaseList::MoveToHead + (POSITION pos, CBaseList *pList) +{ + + /* See the comments on the algorithm in MoveToTail */ + + if (pos==NULL) return TRUE; // no-op. Eliminates special cases later. + + /* Make cMove the number of nodes to move */ + CNode * p = (CNode *)pos; + int cMove = 0; // number of nodes to move + while(p!=NULL) { + p = p->Next(); + ++cMove; + } + + + /* Join the two chains together */ + if (pList->m_pFirst!=NULL) + pList->m_pFirst->SetPrev(m_pLast); + if (m_pLast!=NULL) + m_pLast->SetNext(pList->m_pFirst); + + + /* set first and last pointers */ + p = (CNode *)pos; + + + if (pList->m_pLast==NULL) + pList->m_pLast = m_pLast; + + m_pLast = p->Prev(); + if (m_pLast==NULL) + m_pFirst = NULL; + pList->m_pFirst = p; + + + /* Break the chain after p to create the new pieces */ + if (m_pLast!=NULL) + m_pLast->SetNext(NULL); + p->SetPrev(NULL); + + + /* Adjust the counts */ + m_Count -= cMove; + pList->m_Count += cMove; + + return TRUE; + +} // MoveToHead + + + +/* Reverse the order of the [pointers to] objects in *this +*/ +void CBaseList::Reverse() +{ + /* algorithm: + The obvious booby trap is that you flip pointers around and lose + addressability to the node that you are going to process next. + The easy way to avoid this is do do one chain at a time. + + Run along the forward chain, + For each node, set the reverse pointer to the one ahead of us. + The reverse chain is now a copy of the old forward chain, including + the NULL termination. + + Run along the reverse chain (i.e. old forward chain again) + For each node set the forward pointer of the node ahead to point back + to the one we're standing on. + The first node needs special treatment, + it's new forward pointer is NULL. + Finally set the First/Last pointers + + */ + CNode * p; + + // Yes we COULD use a traverse, but it would look funny! + p = m_pFirst; + while (p!=NULL) { + CNode * q; + q = p->Next(); + p->SetNext(p->Prev()); + p->SetPrev(q); + p = q; + } + + p = m_pFirst; + m_pFirst = m_pLast; + m_pLast = p; + + +#if 0 // old version + + if (m_pFirst==NULL) return; // empty list + if (m_pFirst->Next()==NULL) return; // single node list + + + /* run along forward chain */ + for ( p = m_pFirst + ; p!=NULL + ; p = p->Next() + ){ + p->SetPrev(p->Next()); + } + + + /* special case first element */ + m_pFirst->SetNext(NULL); // fix the old first element + + + /* run along new reverse chain i.e. old forward chain again */ + for ( p = m_pFirst // start at the old first element + ; p->Prev()!=NULL // while there's a node still to be set + ; p = p->Prev() // work in the same direction as before + ){ + p->Prev()->SetNext(p); + } + + + /* fix forward and reverse pointers + - the triple XOR swap would work but all the casts look hideous */ + p = m_pFirst; + m_pFirst = m_pLast; + m_pLast = p; +#endif + +} // Reverse diff --git a/ThirdParty/strmbas/wxlist.h b/ThirdParty/strmbas/wxlist.h new file mode 100644 index 0000000..1e360f7 --- /dev/null +++ b/ThirdParty/strmbas/wxlist.h @@ -0,0 +1,545 @@ +//------------------------------------------------------------------------------ +// File: WXList.h +// +// Desc: DirectShow base classes - defines a non-MFC generic template list +// class. +// +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------------------------ + + +/* A generic list of pointers to objects. + No storage management or copying is done on the objects pointed to. + Objectives: avoid using MFC libraries in ndm kernel mode and + provide a really useful list type. + + The class is thread safe in that separate threads may add and + delete items in the list concurrently although the application + must ensure that constructor and destructor access is suitably + synchronised. An application can cause deadlock with operations + which use two lists by simultaneously calling + list1->Operation(list2) and list2->Operation(list1). So don't! + + The names must not conflict with MFC classes as an application + may use both. + */ + +#ifndef __WXLIST__ +#define __WXLIST__ + + /* A POSITION represents (in some fashion that's opaque) a cursor + on the list that can be set to identify any element. NULL is + a valid value and several operations regard NULL as the position + "one step off the end of the list". (In an n element list there + are n+1 places to insert and NULL is that "n+1-th" value). + The POSITION of an element in the list is only invalidated if + that element is deleted. Move operations may mean that what + was a valid POSITION in one list is now a valid POSITION in + a different list. + + Some operations which at first sight are illegal are allowed as + harmless no-ops. For instance RemoveHead is legal on an empty + list and it returns NULL. This allows an atomic way to test if + there is an element there, and if so, get it. The two operations + AddTail and RemoveHead thus implement a MONITOR (See Hoare's paper). + + Single element operations return POSITIONs, non-NULL means it worked. + whole list operations return a BOOL. TRUE means it all worked. + + This definition is the same as the POSITION type for MFCs, so we must + avoid defining it twice. + */ +#ifndef __AFX_H__ +struct __POSITION { int unused; }; +typedef __POSITION* POSITION; +#endif + +const int DEFAULTCACHE = 10; /* Default node object cache size */ + +/* A class representing one node in a list. + Each node knows a pointer to it's adjacent nodes and also a pointer + to the object that it looks after. + All of these pointers can be retrieved or set through member functions. +*/ +class CBaseList +#ifdef DEBUG + : public CBaseObject +#endif +{ + /* Making these classes inherit from CBaseObject does nothing + functionally but it allows us to check there are no memory + leaks in debug builds. + */ + +public: + +#ifdef DEBUG + class CNode : public CBaseObject { +#else + class CNode { +#endif + + CNode *m_pPrev; /* Previous node in the list */ + CNode *m_pNext; /* Next node in the list */ + void *m_pObject; /* Pointer to the object */ + + public: + + /* Constructor - initialise the object's pointers */ + CNode() +#ifdef DEBUG + : CBaseObject(NAME("List node")) +#endif + { + }; + + + /* Return the previous node before this one */ + CNode *Prev() const { return m_pPrev; }; + + + /* Return the next node after this one */ + CNode *Next() const { return m_pNext; }; + + + /* Set the previous node before this one */ + void SetPrev(CNode *p) { m_pPrev = p; }; + + + /* Set the next node after this one */ + void SetNext(CNode *p) { m_pNext = p; }; + + + /* Get the pointer to the object for this node */ + void *GetData() const { return m_pObject; }; + + + /* Set the pointer to the object for this node */ + void SetData(void *p) { m_pObject = p; }; + }; + + class CNodeCache + { + public: + CNodeCache(INT iCacheSize) : m_iCacheSize(iCacheSize), + m_pHead(NULL), + m_iUsed(0) + {}; + ~CNodeCache() { + CNode *pNode = m_pHead; + while (pNode) { + CNode *pCurrent = pNode; + pNode = pNode->Next(); + delete pCurrent; + } + }; + void AddToCache(CNode *pNode) + { + if (m_iUsed < m_iCacheSize) { + pNode->SetNext(m_pHead); + m_pHead = pNode; + m_iUsed++; + } else { + delete pNode; + } + }; + CNode *RemoveFromCache() + { + CNode *pNode = m_pHead; + if (pNode != NULL) { + m_pHead = pNode->Next(); + m_iUsed--; + ASSERT(m_iUsed >= 0); + } else { + ASSERT(m_iUsed == 0); + } + return pNode; + }; + private: + INT m_iCacheSize; + INT m_iUsed; + CNode *m_pHead; + }; + +protected: + + CNode* m_pFirst; /* Pointer to first node in the list */ + CNode* m_pLast; /* Pointer to the last node in the list */ + LONG m_Count; /* Number of nodes currently in the list */ + +private: + + CNodeCache m_Cache; /* Cache of unused node pointers */ + +private: + + /* These override the default copy constructor and assignment + operator for all list classes. They are in the private class + declaration section so that anybody trying to pass a list + object by value will generate a compile time error of + "cannot access the private member function". If these were + not here then the compiler will create default constructors + and assignment operators which when executed first take a + copy of all member variables and then during destruction + delete them all. This must not be done for any heap + allocated data. + */ + CBaseList(const CBaseList &refList); + CBaseList &operator=(const CBaseList &refList); + +public: + + CBaseList(TCHAR *pName, + INT iItems); + + CBaseList(TCHAR *pName); +#ifdef UNICODE + CBaseList(CHAR *pName, + INT iItems); + + CBaseList(CHAR *pName); +#endif + ~CBaseList(); + + /* Remove all the nodes from *this i.e. make the list empty */ + void RemoveAll(); + + + /* Return a cursor which identifies the first element of *this */ + POSITION GetHeadPositionI() const; + + + /* Return a cursor which identifies the last element of *this */ + POSITION GetTailPositionI() const; + + + /* Return the number of objects in *this */ + int GetCountI() const; + +protected: + /* Return the pointer to the object at rp, + Update rp to the next node in *this + but make it NULL if it was at the end of *this. + This is a wart retained for backwards compatibility. + GetPrev is not implemented. + Use Next, Prev and Get separately. + */ + void *GetNextI(POSITION& rp) const; + + + /* Return a pointer to the object at p + Asking for the object at NULL will return NULL harmlessly. + */ + void *GetI(POSITION p) const; + +public: + /* return the next / prev position in *this + return NULL when going past the end/start. + Next(NULL) is same as GetHeadPosition() + Prev(NULL) is same as GetTailPosition() + An n element list therefore behaves like a n+1 element + cycle with NULL at the start/end. + + !!WARNING!! - This handling of NULL is DIFFERENT from GetNext. + + Some reasons are: + 1. For a list of n items there are n+1 positions to insert + These are conveniently encoded as the n POSITIONs and NULL. + 2. If you are keeping a list sorted (fairly common) and you + search forward for an element to insert before and don't + find it you finish up with NULL as the element before which + to insert. You then want that NULL to be a valid POSITION + so that you can insert before it and you want that insertion + point to mean the (n+1)-th one that doesn't have a POSITION. + (symmetrically if you are working backwards through the list). + 3. It simplifies the algebra which the methods generate. + e.g. AddBefore(p,x) is identical to AddAfter(Prev(p),x) + in ALL cases. All the other arguments probably are reflections + of the algebraic point. + */ + POSITION Next(POSITION pos) const + { + if (pos == NULL) { + return (POSITION) m_pFirst; + } + CNode *pn = (CNode *) pos; + return (POSITION) pn->Next(); + } //Next + + // See Next + POSITION Prev(POSITION pos) const + { + if (pos == NULL) { + return (POSITION) m_pLast; + } + CNode *pn = (CNode *) pos; + return (POSITION) pn->Prev(); + } //Prev + + + /* Return the first position in *this which holds the given + pointer. Return NULL if the pointer was not not found. + */ +protected: + POSITION FindI( void * pObj) const; + + /* Remove the first node in *this (deletes the pointer to its + object from the list, does not free the object itself). + Return the pointer to its object. + If *this was already empty it will harmlessly return NULL. + */ + void *RemoveHeadI(); + + + /* Remove the last node in *this (deletes the pointer to its + object from the list, does not free the object itself). + Return the pointer to its object. + If *this was already empty it will harmlessly return NULL. + */ + void *RemoveTailI(); + + + /* Remove the node identified by p from the list (deletes the pointer + to its object from the list, does not free the object itself). + Asking to Remove the object at NULL will harmlessly return NULL. + Return the pointer to the object removed. + */ + void *RemoveI(POSITION p); + + /* Add single object *pObj to become a new last element of the list. + Return the new tail position, NULL if it fails. + If you are adding a COM objects, you might want AddRef it first. + Other existing POSITIONs in *this are still valid + */ + POSITION AddTailI(void * pObj); +public: + + + /* Add all the elements in *pList to the tail of *this. + This duplicates all the nodes in *pList (i.e. duplicates + all its pointers to objects). It does not duplicate the objects. + If you are adding a list of pointers to a COM object into the list + it's a good idea to AddRef them all it when you AddTail it. + Return TRUE if it all worked, FALSE if it didn't. + If it fails some elements may have been added. + Existing POSITIONs in *this are still valid + + If you actually want to MOVE the elements, use MoveToTail instead. + */ + BOOL AddTail(CBaseList *pList); + + + /* Mirror images of AddHead: */ + + /* Add single object to become a new first element of the list. + Return the new head position, NULL if it fails. + Existing POSITIONs in *this are still valid + */ +protected: + POSITION AddHeadI(void * pObj); +public: + + /* Add all the elements in *pList to the head of *this. + Same warnings apply as for AddTail. + Return TRUE if it all worked, FALSE if it didn't. + If it fails some of the objects may have been added. + + If you actually want to MOVE the elements, use MoveToHead instead. + */ + BOOL AddHead(CBaseList *pList); + + + /* Add the object *pObj to *this after position p in *this. + AddAfter(NULL,x) adds x to the start - equivalent to AddHead + Return the position of the object added, NULL if it failed. + Existing POSITIONs in *this are undisturbed, including p. + */ +protected: + POSITION AddAfterI(POSITION p, void * pObj); +public: + + /* Add the list *pList to *this after position p in *this + AddAfter(NULL,x) adds x to the start - equivalent to AddHead + Return TRUE if it all worked, FALSE if it didn't. + If it fails, some of the objects may be added + Existing POSITIONs in *this are undisturbed, including p. + */ + BOOL AddAfter(POSITION p, CBaseList *pList); + + + /* Mirror images: + Add the object *pObj to this-List after position p in *this. + AddBefore(NULL,x) adds x to the end - equivalent to AddTail + Return the position of the new object, NULL if it fails + Existing POSITIONs in *this are undisturbed, including p. + */ + protected: + POSITION AddBeforeI(POSITION p, void * pObj); + public: + + /* Add the list *pList to *this before position p in *this + AddAfter(NULL,x) adds x to the start - equivalent to AddHead + Return TRUE if it all worked, FALSE if it didn't. + If it fails, some of the objects may be added + Existing POSITIONs in *this are undisturbed, including p. + */ + BOOL AddBefore(POSITION p, CBaseList *pList); + + + /* Note that AddAfter(p,x) is equivalent to AddBefore(Next(p),x) + even in cases where p is NULL or Next(p) is NULL. + Similarly for mirror images etc. + This may make it easier to argue about programs. + */ + + + + /* The following operations do not copy any elements. + They move existing blocks of elements around by switching pointers. + They are fairly efficient for long lists as for short lists. + (Alas, the Count slows things down). + + They split the list into two parts. + One part remains as the original list, the other part + is appended to the second list. There are eight possible + variations: + Split the list {after/before} a given element + keep the {head/tail} portion in the original list + append the rest to the {head/tail} of the new list. + + Since After is strictly equivalent to Before Next + we are not in serious need of the Before/After variants. + That leaves only four. + + If you are processing a list left to right and dumping + the bits that you have processed into another list as + you go, the Tail/Tail variant gives the most natural result. + If you are processing in reverse order, Head/Head is best. + + By using NULL positions and empty lists judiciously either + of the other two can be built up in two operations. + + The definition of NULL (see Next/Prev etc) means that + degenerate cases include + "move all elements to new list" + "Split a list into two lists" + "Concatenate two lists" + (and quite a few no-ops) + + !!WARNING!! The type checking won't buy you much if you get list + positions muddled up - e.g. use a POSITION that's in a different + list and see what a mess you get! + */ + + /* Split *this after position p in *this + Retain as *this the tail portion of the original *this + Add the head portion to the tail end of *pList + Return TRUE if it all worked, FALSE if it didn't. + + e.g. + foo->MoveToTail(foo->GetHeadPosition(), bar); + moves one element from the head of foo to the tail of bar + foo->MoveToTail(NULL, bar); + is a no-op, returns NULL + foo->MoveToTail(foo->GetTailPosition, bar); + concatenates foo onto the end of bar and empties foo. + + A better, except excessively long name might be + MoveElementsFromHeadThroughPositionToOtherTail + */ + BOOL MoveToTail(POSITION pos, CBaseList *pList); + + + /* Mirror image: + Split *this before position p in *this. + Retain in *this the head portion of the original *this + Add the tail portion to the start (i.e. head) of *pList + + e.g. + foo->MoveToHead(foo->GetTailPosition(), bar); + moves one element from the tail of foo to the head of bar + foo->MoveToHead(NULL, bar); + is a no-op, returns NULL + foo->MoveToHead(foo->GetHeadPosition, bar); + concatenates foo onto the start of bar and empties foo. + */ + BOOL MoveToHead(POSITION pos, CBaseList *pList); + + + /* Reverse the order of the [pointers to] objects in *this + */ + void Reverse(); + + + /* set cursor to the position of each element of list in turn */ + #define TRAVERSELIST(list, cursor) \ + for ( cursor = (list).GetHeadPosition() \ + ; cursor!=NULL \ + ; cursor = (list).Next(cursor) \ + ) + + + /* set cursor to the position of each element of list in turn + in reverse order + */ + #define REVERSETRAVERSELIST(list, cursor) \ + for ( cursor = (list).GetTailPosition() \ + ; cursor!=NULL \ + ; cursor = (list).Prev(cursor) \ + ) + +}; // end of class declaration + +template class CGenericList : public CBaseList +{ +public: + CGenericList(TCHAR *pName, + INT iItems, + BOOL bLock = TRUE, + BOOL bAlert = FALSE) : + CBaseList(pName, iItems) { + UNREFERENCED_PARAMETER(bAlert); + UNREFERENCED_PARAMETER(bLock); + }; + CGenericList(TCHAR *pName) : + CBaseList(pName) { + }; + + POSITION GetHeadPosition() const { return (POSITION)m_pFirst; } + POSITION GetTailPosition() const { return (POSITION)m_pLast; } + int GetCount() const { return m_Count; } + + OBJECT *GetNext(POSITION& rp) const { return (OBJECT *) GetNextI(rp); } + + OBJECT *Get(POSITION p) const { return (OBJECT *) GetI(p); } + OBJECT *GetHead() const { return Get(GetHeadPosition()); } + + OBJECT *RemoveHead() { return (OBJECT *) RemoveHeadI(); } + + OBJECT *RemoveTail() { return (OBJECT *) RemoveTailI(); } + + OBJECT *Remove(POSITION p) { return (OBJECT *) RemoveI(p); } + POSITION AddBefore(POSITION p, OBJECT * pObj) { return AddBeforeI(p, pObj); } + POSITION AddAfter(POSITION p, OBJECT * pObj) { return AddAfterI(p, pObj); } + POSITION AddHead(OBJECT * pObj) { return AddHeadI(pObj); } + POSITION AddTail(OBJECT * pObj) { return AddTailI(pObj); } + BOOL AddTail(CGenericList *pList) + { return CBaseList::AddTail((CBaseList *) pList); } + BOOL AddHead(CGenericList *pList) + { return CBaseList::AddHead((CBaseList *) pList); } + BOOL AddAfter(POSITION p, CGenericList *pList) + { return CBaseList::AddAfter(p, (CBaseList *) pList); }; + BOOL AddBefore(POSITION p, CGenericList *pList) + { return CBaseList::AddBefore(p, (CBaseList *) pList); }; + POSITION Find( OBJECT * pObj) const { return FindI(pObj); } +}; // end of class declaration + + + +/* These define the standard list types */ + +typedef CGenericList CBaseObjectList; +typedef CGenericList CBaseInterfaceList; + +#endif /* __WXLIST__ */ + diff --git a/ThirdParty/strmbas/wxutil.cpp b/ThirdParty/strmbas/wxutil.cpp new file mode 100644 index 0000000..6665c5b --- /dev/null +++ b/ThirdParty/strmbas/wxutil.cpp @@ -0,0 +1,1256 @@ +//------------------------------------------------------------------------------ +// File: WXUtil.cpp +// +// Desc: DirectShow base classes - implements helper classes for building +// multimedia filters. +// +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------------------------ + + +#include + +// +// Declare function from largeint.h we need so that PPC can build +// + +// +// Enlarged integer divide - 64-bits / 32-bits > 32-bits +// + +#ifndef _X86_ + +#define LLtoU64(x) (*(unsigned __int64*)(void*)(&(x))) + +__inline +ULONG +WINAPI +EnlargedUnsignedDivide ( + IN ULARGE_INTEGER Dividend, + IN ULONG Divisor, + IN PULONG Remainder + ) +{ + // return remainder if necessary + if (Remainder != NULL) + *Remainder = (ULONG)(LLtoU64(Dividend) % Divisor); + return (ULONG)(LLtoU64(Dividend) / Divisor); +} + +#else +__inline +ULONG +WINAPI +EnlargedUnsignedDivide ( + IN ULARGE_INTEGER Dividend, + IN ULONG Divisor, + IN PULONG Remainder + ) +{ + ULONG ulResult; + _asm { + mov eax,Dividend.LowPart + mov edx,Dividend.HighPart + mov ecx,Remainder + div Divisor + or ecx,ecx + jz short label + mov [ecx],edx +label: + mov ulResult,eax + } + return ulResult; +} +#endif + +// --- CAMEvent ----------------------- +CAMEvent::CAMEvent(BOOL fManualReset) +{ + m_hEvent = CreateEvent(NULL, fManualReset, FALSE, NULL); +} + +CAMEvent::~CAMEvent() +{ + if (m_hEvent) { + EXECUTE_ASSERT(CloseHandle(m_hEvent)); + } +} + + +// --- CAMMsgEvent ----------------------- +// One routine. The rest is handled in CAMEvent + +BOOL CAMMsgEvent::WaitMsg(DWORD dwTimeout) +{ + // wait for the event to be signalled, or for the + // timeout (in MS) to expire. allow SENT messages + // to be processed while we wait + DWORD dwWait; + DWORD dwStartTime; + + // set the waiting period. + DWORD dwWaitTime = dwTimeout; + + // the timeout will eventually run down as we iterate + // processing messages. grab the start time so that + // we can calculate elapsed times. + if (dwWaitTime != INFINITE) { + dwStartTime = timeGetTime(); + } + + do { + dwWait = MsgWaitForMultipleObjects(1,&m_hEvent,FALSE, dwWaitTime, QS_SENDMESSAGE); + if (dwWait == WAIT_OBJECT_0 + 1) { + MSG Message; + PeekMessage(&Message,NULL,0,0,PM_NOREMOVE); + + // If we have an explicit length of time to wait calculate + // the next wake up point - which might be now. + // If dwTimeout is INFINITE, it stays INFINITE + if (dwWaitTime != INFINITE) { + + DWORD dwElapsed = timeGetTime()-dwStartTime; + + dwWaitTime = + (dwElapsed >= dwTimeout) + ? 0 // wake up with WAIT_TIMEOUT + : dwTimeout-dwElapsed; + } + } + } while (dwWait == WAIT_OBJECT_0 + 1); + + // return TRUE if we woke on the event handle, + // FALSE if we timed out. + return (dwWait == WAIT_OBJECT_0); +} + +// --- CAMThread ---------------------- + + +CAMThread::CAMThread() + : m_EventSend(TRUE) // must be manual-reset for CheckRequest() +{ + m_hThread = NULL; +} + +CAMThread::~CAMThread() { + Close(); +} + + +// when the thread starts, it calls this function. We unwrap the 'this' +//pointer and call ThreadProc. +DWORD WINAPI +CAMThread::InitialThreadProc(LPVOID pv) +{ + HRESULT hrCoInit = CAMThread::CoInitializeHelper(); + if(FAILED(hrCoInit)) { + DbgLog((LOG_ERROR, 1, TEXT("CoInitializeEx failed."))); + } + + CAMThread * pThread = (CAMThread *) pv; + + HRESULT hr = pThread->ThreadProc(); + + if(SUCCEEDED(hrCoInit)) { + CoUninitialize(); + } + + return hr; +} + +BOOL +CAMThread::Create() +{ + DWORD threadid; + + CAutoLock lock(&m_AccessLock); + + if (ThreadExists()) { + return FALSE; + } + + m_hThread = CreateThread( + NULL, + 0, + CAMThread::InitialThreadProc, + this, + 0, + &threadid); + + if (!m_hThread) { + return FALSE; + } + + return TRUE; +} + +DWORD +CAMThread::CallWorker(DWORD dwParam) +{ + // lock access to the worker thread for scope of this object + CAutoLock lock(&m_AccessLock); + + if (!ThreadExists()) { + return (DWORD) E_FAIL; + } + + // set the parameter + m_dwParam = dwParam; + + // signal the worker thread + m_EventSend.Set(); + + // wait for the completion to be signalled + m_EventComplete.Wait(); + + // done - this is the thread's return value + return m_dwReturnVal; +} + +// Wait for a request from the client +DWORD +CAMThread::GetRequest() +{ + m_EventSend.Wait(); + return m_dwParam; +} + +// is there a request? +BOOL +CAMThread::CheckRequest(DWORD * pParam) +{ + if (!m_EventSend.Check()) { + return FALSE; + } else { + if (pParam) { + *pParam = m_dwParam; + } + return TRUE; + } +} + +// reply to the request +void +CAMThread::Reply(DWORD dw) +{ + m_dwReturnVal = dw; + + // The request is now complete so CheckRequest should fail from + // now on + // + // This event should be reset BEFORE we signal the client or + // the client may Set it before we reset it and we'll then + // reset it (!) + + m_EventSend.Reset(); + + // Tell the client we're finished + + m_EventComplete.Set(); +} + +HRESULT CAMThread::CoInitializeHelper() +{ + // call CoInitializeEx and tell OLE not to create a window (this + // thread probably won't dispatch messages and will hang on + // broadcast msgs o/w). + // + // If CoInitEx is not available, threads that don't call CoCreate + // aren't affected. Threads that do will have to handle the + // failure. Perhaps we should fall back to CoInitialize and risk + // hanging? + // + + // older versions of ole32.dll don't have CoInitializeEx + + HRESULT hr = E_FAIL; + HINSTANCE hOle = GetModuleHandle(TEXT("ole32.dll")); + if(hOle) + { + typedef HRESULT (STDAPICALLTYPE *PCoInitializeEx)( + LPVOID pvReserved, DWORD dwCoInit); + PCoInitializeEx pCoInitializeEx = + (PCoInitializeEx)(GetProcAddress(hOle, "CoInitializeEx")); + if(pCoInitializeEx) + { + hr = (*pCoInitializeEx)(0, COINIT_DISABLE_OLE1DDE ); + } + } + else + { + // caller must load ole32.dll + DbgBreak("couldn't locate ole32.dll"); + } + + return hr; +} + + +// destructor for CMsgThread - cleans up any messages left in the +// queue when the thread exited +CMsgThread::~CMsgThread() +{ + if (m_hThread != NULL) { + WaitForSingleObject(m_hThread, INFINITE); + EXECUTE_ASSERT(CloseHandle(m_hThread)); + } + + POSITION pos = m_ThreadQueue.GetHeadPosition(); + while (pos) { + CMsg * pMsg = m_ThreadQueue.GetNext(pos); + delete pMsg; + } + m_ThreadQueue.RemoveAll(); + + if (m_hSem != NULL) { + EXECUTE_ASSERT(CloseHandle(m_hSem)); + } +} + +BOOL +CMsgThread::CreateThread( + ) +{ + m_hSem = CreateSemaphore(NULL, 0, 0x7FFFFFFF, NULL); + if (m_hSem == NULL) { + return FALSE; + } + + m_hThread = ::CreateThread(NULL, 0, DefaultThreadProc, + (LPVOID)this, 0, &m_ThreadId); + return m_hThread != NULL; +} + + +// This is the threads message pump. Here we get and dispatch messages to +// clients thread proc until the client refuses to process a message. +// The client returns a non-zero value to stop the message pump, this +// value becomes the threads exit code. + +DWORD WINAPI +CMsgThread::DefaultThreadProc( + LPVOID lpParam + ) +{ + CMsgThread *lpThis = (CMsgThread *)lpParam; + CMsg msg; + LRESULT lResult; + + // !!! + CoInitialize(NULL); + + // allow a derived class to handle thread startup + lpThis->OnThreadInit(); + + do { + lpThis->GetThreadMsg(&msg); + lResult = lpThis->ThreadMessageProc(msg.uMsg,msg.dwFlags, + msg.lpParam, msg.pEvent); + } while (lResult == 0L); + + // !!! + CoUninitialize(); + + return (DWORD)lResult; +} + + +// Block until the next message is placed on the list m_ThreadQueue. +// copies the message to the message pointed to by *pmsg +void +CMsgThread::GetThreadMsg(CMsg *msg) +{ + CMsg * pmsg = NULL; + + // keep trying until a message appears + while (TRUE) { + { + CAutoLock lck(&m_Lock); + pmsg = m_ThreadQueue.RemoveHead(); + if (pmsg == NULL) { + m_lWaiting++; + } else { + break; + } + } + // the semaphore will be signalled when it is non-empty + WaitForSingleObject(m_hSem, INFINITE); + } + // copy fields to caller's CMsg + *msg = *pmsg; + + // this CMsg was allocated by the 'new' in PutThreadMsg + delete pmsg; + +} + + +// NOTE: as we need to use the same binaries on Win95 as on NT this code should +// be compiled WITHOUT unicode being defined. Otherwise we will not pick up +// these internal routines and the binary will not run on Win95. + +#ifndef UNICODE +// Windows 95 doesn't implement this, so we provide an implementation. +// LPWSTR +// WINAPI +// lstrcpyWInternal( +// LPWSTR lpString1, +// LPCWSTR lpString2 +// ) +// { +// LPWSTR lpReturn = lpString1; +// while (*lpString1++ = *lpString2++); +// +// return lpReturn; +// } + +// Windows 95 doesn't implement this, so we provide an implementation. +LPWSTR +WINAPI +lstrcpynWInternal( + LPWSTR lpString1, + LPCWSTR lpString2, + int iMaxLength + ) +{ + ASSERT(iMaxLength); + LPWSTR lpReturn = lpString1; + if (iMaxLength) { + while (--iMaxLength && (*lpString1++ = *lpString2++)); + + // If we ran out of room (which will be the case if + // iMaxLength is now 0) we still need to terminate the + // string. + if (!iMaxLength) *lpString1 = L'\0'; + } + return lpReturn; +} + +int +WINAPI +lstrcmpWInternal( + LPCWSTR lpString1, + LPCWSTR lpString2 + ) +{ + do { + WCHAR c1 = *lpString1; + WCHAR c2 = *lpString2; + if (c1 != c2) + return (int) c1 - (int) c2; + } while (*lpString1++ && *lpString2++); + return 0; +} + + +int +WINAPI +lstrcmpiWInternal( + LPCWSTR lpString1, + LPCWSTR lpString2 + ) +{ + do { + WCHAR c1 = *lpString1; + WCHAR c2 = *lpString2; + if (c1 >= L'A' && c1 <= L'Z') + c1 -= (WCHAR) (L'A' - L'a'); + if (c2 >= L'A' && c2 <= L'Z') + c2 -= (WCHAR) (L'A' - L'a'); + + if (c1 != c2) + return (int) c1 - (int) c2; + } while (*lpString1++ && *lpString2++); + + return 0; +} + + +int +WINAPI +lstrlenWInternal( + LPCWSTR lpString + ) +{ + int i = -1; + while (*(lpString+(++i))) + ; + return i; +} + + +// int WINAPIV wsprintfWInternal(LPWSTR wszOut, LPCWSTR pszFmt, ...) +// { +// char fmt[256]; // !!! +// char ach[256]; // !!! +// int i; +// +// va_list va; +// va_start(va, pszFmt); +// WideCharToMultiByte(GetACP(), 0, pszFmt, -1, fmt, 256, NULL, NULL); +// (void)StringCchVPrintf(ach, NUMELMS(ach), fmt, va); +// i = lstrlenA(ach); +// va_end(va); +// +// MultiByteToWideChar(CP_ACP, 0, ach, -1, wszOut, i+1); +// +// return i; +// } +#else + +// need to provide the implementations in unicode for non-unicode +// builds linking with the unicode strmbase.lib +//LPWSTR WINAPI lstrcpyWInternal( +// LPWSTR lpString1, +// LPCWSTR lpString2 +// ) +//{ +// return lstrcpyW(lpString1, lpString2); +//} + +LPWSTR WINAPI lstrcpynWInternal( + LPWSTR lpString1, + LPCWSTR lpString2, + int iMaxLength + ) +{ + return lstrcpynW(lpString1, lpString2, iMaxLength); +} + +int WINAPI lstrcmpWInternal( + LPCWSTR lpString1, + LPCWSTR lpString2 + ) +{ + return lstrcmpW(lpString1, lpString2); +} + + +int WINAPI lstrcmpiWInternal( + LPCWSTR lpString1, + LPCWSTR lpString2 + ) +{ + return lstrcmpiW(lpString1, lpString2); +} + + +int WINAPI lstrlenWInternal( + LPCWSTR lpString + ) +{ + return lstrlenW(lpString); +} + + +//int WINAPIV wsprintfWInternal( +// LPWSTR wszOut, LPCWSTR pszFmt, ...) +//{ +// va_list va; +// va_start(va, pszFmt); +// int i = wvsprintfW(wszOut, pszFmt, va); +// va_end(va); +// return i; +//} +#endif + + +// Helper function - convert int to WSTR +void WINAPI IntToWstr(int i, LPWSTR wstr, size_t len) +{ +#ifdef UNICODE + (void)StringCchPrintf(wstr, len, L"%d", i); +#else + TCHAR temp[32]; + (void)StringCchPrintf(temp, NUMELMS(temp), "%d", i); + MultiByteToWideChar(CP_ACP, 0, temp, -1, wstr, int(len) ); +#endif +} // IntToWstr + + +#if 0 +void * memchrInternal(const void *pv, int c, size_t sz) +{ + BYTE *pb = (BYTE *) pv; + while (sz--) { + if (*pb == c) + return (void *) pb; + pb++; + } + return NULL; +} +#endif + + +#define MEMORY_ALIGNMENT 4 +#define MEMORY_ALIGNMENT_LOG2 2 +#define MEMORY_ALIGNMENT_MASK MEMORY_ALIGNMENT - 1 + +void * __stdcall memmoveInternal(void * dst, const void * src, size_t count) +{ + void * ret = dst; + +#ifdef _X86_ + if (dst <= src || (char *)dst >= ((char *)src + count)) { + + /* + * Non-Overlapping Buffers + * copy from lower addresses to higher addresses + */ + _asm { + mov esi,src + mov edi,dst + mov ecx,count + cld + mov edx,ecx + and edx,MEMORY_ALIGNMENT_MASK + shr ecx,MEMORY_ALIGNMENT_LOG2 + rep movsd + or ecx,edx + jz memmove_done + rep movsb +memmove_done: + } + } + else { + + /* + * Overlapping Buffers + * copy from higher addresses to lower addresses + */ + _asm { + mov esi,src + mov edi,dst + mov ecx,count + std + add esi,ecx + add edi,ecx + dec esi + dec edi + rep movsb + cld + } + } +#else + MoveMemory(dst, src, count); +#endif + + return ret; +} + +/* Arithmetic functions to help with time format conversions +*/ + +#ifdef _M_ALPHA +// work around bug in version 12.00.8385 of the alpha compiler where +// UInt32x32To64 sign-extends its arguments (?) +#undef UInt32x32To64 +#define UInt32x32To64(a, b) (((ULONGLONG)((ULONG)(a)) & 0xffffffff) * ((ULONGLONG)((ULONG)(b)) & 0xffffffff)) +#endif + +/* Compute (a * b + d) / c */ +LONGLONG WINAPI llMulDiv(LONGLONG a, LONGLONG b, LONGLONG c, LONGLONG d) +{ + /* Compute the absolute values to avoid signed arithmetic problems */ + ULARGE_INTEGER ua, ub; + DWORDLONG uc; + + ua.QuadPart = (DWORDLONG)(a >= 0 ? a : -a); + ub.QuadPart = (DWORDLONG)(b >= 0 ? b : -b); + uc = (DWORDLONG)(c >= 0 ? c : -c); + BOOL bSign = (a < 0) ^ (b < 0); + + /* Do long multiplication */ + ULARGE_INTEGER p[2]; + p[0].QuadPart = UInt32x32To64(ua.LowPart, ub.LowPart); + + /* This next computation cannot overflow into p[1].HighPart because + the max number we can compute here is: + + (2 ** 32 - 1) * (2 ** 32 - 1) + // ua.LowPart * ub.LowPart + (2 ** 32) * (2 ** 31) * (2 ** 32 - 1) * 2 // x.LowPart * y.HighPart * 2 + + == 2 ** 96 - 2 ** 64 + (2 ** 64 - 2 ** 33 + 1) + == 2 ** 96 - 2 ** 33 + 1 + < 2 ** 96 + */ + + ULARGE_INTEGER x; + x.QuadPart = UInt32x32To64(ua.LowPart, ub.HighPart) + + UInt32x32To64(ua.HighPart, ub.LowPart) + + p[0].HighPart; + p[0].HighPart = x.LowPart; + p[1].QuadPart = UInt32x32To64(ua.HighPart, ub.HighPart) + x.HighPart; + + if (d != 0) { + ULARGE_INTEGER ud[2]; + if (bSign) { + ud[0].QuadPart = (DWORDLONG)(-d); + if (d > 0) { + /* -d < 0 */ + ud[1].QuadPart = (DWORDLONG)(LONGLONG)-1; + } else { + ud[1].QuadPart = (DWORDLONG)0; + } + } else { + ud[0].QuadPart = (DWORDLONG)d; + if (d < 0) { + ud[1].QuadPart = (DWORDLONG)(LONGLONG)-1; + } else { + ud[1].QuadPart = (DWORDLONG)0; + } + } + /* Now do extended addition */ + ULARGE_INTEGER uliTotal; + + /* Add ls DWORDs */ + uliTotal.QuadPart = (DWORDLONG)ud[0].LowPart + p[0].LowPart; + p[0].LowPart = uliTotal.LowPart; + + /* Propagate carry */ + uliTotal.LowPart = uliTotal.HighPart; + uliTotal.HighPart = 0; + + /* Add 2nd most ls DWORDs */ + uliTotal.QuadPart += (DWORDLONG)ud[0].HighPart + p[0].HighPart; + p[0].HighPart = uliTotal.LowPart; + + /* Propagate carry */ + uliTotal.LowPart = uliTotal.HighPart; + uliTotal.HighPart = 0; + + /* Add MS DWORDLONGs - no carry expected */ + p[1].QuadPart += ud[1].QuadPart + uliTotal.QuadPart; + + /* Now see if we got a sign change from the addition */ + if ((LONG)p[1].HighPart < 0) { + bSign = !bSign; + + /* Negate the current value (ugh!) */ + p[0].QuadPart = ~p[0].QuadPart; + p[1].QuadPart = ~p[1].QuadPart; + p[0].QuadPart += 1; + p[1].QuadPart += (p[0].QuadPart == 0); + } + } + + /* Now for the division */ + if (c < 0) { + bSign = !bSign; + } + + + /* This will catch c == 0 and overflow */ + if (uc <= p[1].QuadPart) { + return bSign ? (LONGLONG)0x8000000000000000 : + (LONGLONG)0x7FFFFFFFFFFFFFFF; + } + + DWORDLONG ullResult; + + /* Do the division */ + /* If the dividend is a DWORD_LONG use the compiler */ + if (p[1].QuadPart == 0) { + ullResult = p[0].QuadPart / uc; + return bSign ? -(LONGLONG)ullResult : (LONGLONG)ullResult; + } + + /* If the divisor is a DWORD then its simpler */ + ULARGE_INTEGER ulic; + ulic.QuadPart = uc; + if (ulic.HighPart == 0) { + ULARGE_INTEGER uliDividend; + ULARGE_INTEGER uliResult; + DWORD dwDivisor = (DWORD)uc; + // ASSERT(p[1].HighPart == 0 && p[1].LowPart < dwDivisor); + uliDividend.HighPart = p[1].LowPart; + uliDividend.LowPart = p[0].HighPart; +#ifndef USE_LARGEINT + uliResult.HighPart = (DWORD)(uliDividend.QuadPart / dwDivisor); + p[0].HighPart = (DWORD)(uliDividend.QuadPart % dwDivisor); + uliResult.LowPart = 0; + uliResult.QuadPart = p[0].QuadPart / dwDivisor + uliResult.QuadPart; +#else + /* NOTE - this routine will take exceptions if + the result does not fit in a DWORD + */ + if (uliDividend.QuadPart >= (DWORDLONG)dwDivisor) { + uliResult.HighPart = EnlargedUnsignedDivide( + uliDividend, + dwDivisor, + &p[0].HighPart); + } else { + uliResult.HighPart = 0; + } + uliResult.LowPart = EnlargedUnsignedDivide( + p[0], + dwDivisor, + NULL); +#endif + return bSign ? -(LONGLONG)uliResult.QuadPart : + (LONGLONG)uliResult.QuadPart; + } + + + ullResult = 0; + + /* OK - do long division */ + for (int i = 0; i < 64; i++) { + ullResult <<= 1; + + /* Shift 128 bit p left 1 */ + p[1].QuadPart <<= 1; + if ((p[0].HighPart & 0x80000000) != 0) { + p[1].LowPart++; + } + p[0].QuadPart <<= 1; + + /* Compare */ + if (uc <= p[1].QuadPart) { + p[1].QuadPart -= uc; + ullResult += 1; + } + } + + return bSign ? - (LONGLONG)ullResult : (LONGLONG)ullResult; +} + +LONGLONG WINAPI Int64x32Div32(LONGLONG a, LONG b, LONG c, LONG d) +{ + ULARGE_INTEGER ua; + DWORD ub; + DWORD uc; + + /* Compute the absolute values to avoid signed arithmetic problems */ + ua.QuadPart = (DWORDLONG)(a >= 0 ? a : -a); + ub = (DWORD)(b >= 0 ? b : -b); + uc = (DWORD)(c >= 0 ? c : -c); + BOOL bSign = (a < 0) ^ (b < 0); + + /* Do long multiplication */ + ULARGE_INTEGER p0; + DWORD p1; + p0.QuadPart = UInt32x32To64(ua.LowPart, ub); + + if (ua.HighPart != 0) { + ULARGE_INTEGER x; + x.QuadPart = UInt32x32To64(ua.HighPart, ub) + p0.HighPart; + p0.HighPart = x.LowPart; + p1 = x.HighPart; + } else { + p1 = 0; + } + + if (d != 0) { + ULARGE_INTEGER ud0; + DWORD ud1; + + if (bSign) { + // + // Cast d to LONGLONG first otherwise -0x80000000 sign extends + // incorrectly + // + ud0.QuadPart = (DWORDLONG)(-(LONGLONG)d); + if (d > 0) { + /* -d < 0 */ + ud1 = (DWORD)-1; + } else { + ud1 = (DWORD)0; + } + } else { + ud0.QuadPart = (DWORDLONG)d; + if (d < 0) { + ud1 = (DWORD)-1; + } else { + ud1 = (DWORD)0; + } + } + /* Now do extended addition */ + ULARGE_INTEGER uliTotal; + + /* Add ls DWORDs */ + uliTotal.QuadPart = (DWORDLONG)ud0.LowPart + p0.LowPart; + p0.LowPart = uliTotal.LowPart; + + /* Propagate carry */ + uliTotal.LowPart = uliTotal.HighPart; + uliTotal.HighPart = 0; + + /* Add 2nd most ls DWORDs */ + uliTotal.QuadPart += (DWORDLONG)ud0.HighPart + p0.HighPart; + p0.HighPart = uliTotal.LowPart; + + /* Add MS DWORDLONGs - no carry expected */ + p1 += ud1 + uliTotal.HighPart; + + /* Now see if we got a sign change from the addition */ + if ((LONG)p1 < 0) { + bSign = !bSign; + + /* Negate the current value (ugh!) */ + p0.QuadPart = ~p0.QuadPart; + p1 = ~p1; + p0.QuadPart += 1; + p1 += (p0.QuadPart == 0); + } + } + + /* Now for the division */ + if (c < 0) { + bSign = !bSign; + } + + + /* This will catch c == 0 and overflow */ + if (uc <= p1) { + return bSign ? (LONGLONG)0x8000000000000000 : + (LONGLONG)0x7FFFFFFFFFFFFFFF; + } + + /* Do the division */ + + /* If the divisor is a DWORD then its simpler */ + ULARGE_INTEGER uliDividend; + ULARGE_INTEGER uliResult; + DWORD dwDivisor = uc; + uliDividend.HighPart = p1; + uliDividend.LowPart = p0.HighPart; + /* NOTE - this routine will take exceptions if + the result does not fit in a DWORD + */ + if (uliDividend.QuadPart >= (DWORDLONG)dwDivisor) { + uliResult.HighPart = EnlargedUnsignedDivide( + uliDividend, + dwDivisor, + &p0.HighPart); + } else { + uliResult.HighPart = 0; + } + uliResult.LowPart = EnlargedUnsignedDivide( + p0, + dwDivisor, + NULL); + return bSign ? -(LONGLONG)uliResult.QuadPart : + (LONGLONG)uliResult.QuadPart; +} + +#ifdef _DEBUG +/******************************Public*Routine******************************\ +* Debug CCritSec helpers +* +* We provide debug versions of the Constructor, destructor, Lock and Unlock +* routines. The debug code tracks who owns each critical section by +* maintaining a depth count. +* +* History: +* +\**************************************************************************/ + +CCritSec::CCritSec() +{ + InitializeCriticalSection(&m_CritSec); + m_currentOwner = m_lockCount = 0; + m_fTrace = FALSE; +} + +CCritSec::~CCritSec() +{ + DeleteCriticalSection(&m_CritSec); +} + +void CCritSec::Lock() +{ + UINT tracelevel=3; + DWORD us = GetCurrentThreadId(); + DWORD currentOwner = m_currentOwner; + if (currentOwner && (currentOwner != us)) { + // already owned, but not by us + if (m_fTrace) { + DbgLog((LOG_LOCKING, 2, TEXT("Thread %d about to wait for lock %x owned by %d"), + GetCurrentThreadId(), &m_CritSec, currentOwner)); + tracelevel=2; + // if we saw the message about waiting for the critical + // section we ensure we see the message when we get the + // critical section + } + } + EnterCriticalSection(&m_CritSec); + if (0 == m_lockCount++) { + // we now own it for the first time. Set owner information + m_currentOwner = us; + + if (m_fTrace) { + DbgLog((LOG_LOCKING, tracelevel, TEXT("Thread %d now owns lock %x"), m_currentOwner, &m_CritSec)); + } + } +} + +void CCritSec::Unlock() { + if (0 == --m_lockCount) { + // about to be unowned + if (m_fTrace) { + DbgLog((LOG_LOCKING, 3, TEXT("Thread %d releasing lock %x"), m_currentOwner, &m_CritSec)); + } + + m_currentOwner = 0; + } + LeaveCriticalSection(&m_CritSec); +} + +void WINAPI DbgLockTrace(CCritSec * pcCrit, BOOL fTrace) +{ + pcCrit->m_fTrace = fTrace; +} + +BOOL WINAPI CritCheckIn(CCritSec * pcCrit) +{ + return (GetCurrentThreadId() == pcCrit->m_currentOwner); +} + +BOOL WINAPI CritCheckIn(const CCritSec * pcCrit) +{ + return (GetCurrentThreadId() == pcCrit->m_currentOwner); +} + +BOOL WINAPI CritCheckOut(CCritSec * pcCrit) +{ + return (GetCurrentThreadId() != pcCrit->m_currentOwner); +} + +BOOL WINAPI CritCheckOut(const CCritSec * pcCrit) +{ + return (GetCurrentThreadId() != pcCrit->m_currentOwner); +} +#else +CCritSec::CCritSec() { + InitializeCriticalSection(&m_CritSec); +}; + +CCritSec::~CCritSec() { + DeleteCriticalSection(&m_CritSec); +}; + +void CCritSec::Lock() { + EnterCriticalSection(&m_CritSec); +}; + +void CCritSec::Unlock() { + LeaveCriticalSection(&m_CritSec); +}; +#endif + + +STDAPI WriteBSTR(BSTR *pstrDest, LPCWSTR szSrc) +{ + *pstrDest = SysAllocString( szSrc ); + if( !(*pstrDest) ) return E_OUTOFMEMORY; + return NOERROR; +} + + +STDAPI FreeBSTR(BSTR* pstr) +{ + if( *pstr == NULL ) return S_FALSE; + SysFreeString( *pstr ); + return NOERROR; +} + + +// Return a wide string - allocating memory for it +// Returns: +// S_OK - no error +// E_POINTER - ppszReturn == NULL +// E_OUTOFMEMORY - can't allocate memory for returned string +STDAPI AMGetWideString(LPCWSTR psz, LPWSTR *ppszReturn) +{ + CheckPointer(ppszReturn, E_POINTER); + ValidateReadWritePtr(ppszReturn, sizeof(LPWSTR)); + DWORD nameLen = sizeof(WCHAR) * (lstrlenW(psz)+1); + *ppszReturn = (LPWSTR)CoTaskMemAlloc(nameLen); + if (*ppszReturn == NULL) { + return E_OUTOFMEMORY; + } + CopyMemory(*ppszReturn, psz, nameLen); + return NOERROR; +} + +// Waits for the HANDLE hObject. While waiting messages sent +// to windows on our thread by SendMessage will be processed. +// Using this function to do waits and mutual exclusion +// avoids some deadlocks in objects with windows. +// Return codes are the same as for WaitForSingleObject +DWORD WINAPI WaitDispatchingMessages( + HANDLE hObject, + DWORD dwWait, + HWND hwnd, + UINT uMsg, + HANDLE hEvent) +{ + BOOL bPeeked = FALSE; + DWORD dwResult; + DWORD dwStart; + DWORD dwThreadPriority; + + static UINT uMsgId = 0; + + HANDLE hObjects[2] = { hObject, hEvent }; + if (dwWait != INFINITE && dwWait != 0) { + dwStart = GetTickCount(); + } + for (; ; ) { + DWORD nCount = NULL != hEvent ? 2 : 1; + + // Minimize the chance of actually dispatching any messages + // by seeing if we can lock immediately. + dwResult = WaitForMultipleObjects(nCount, hObjects, FALSE, 0); + if (dwResult < WAIT_OBJECT_0 + nCount) { + break; + } + + DWORD dwTimeOut = dwWait; + if (dwTimeOut > 10) { + dwTimeOut = 10; + } + dwResult = MsgWaitForMultipleObjects( + nCount, + hObjects, + FALSE, + dwTimeOut, + hwnd == NULL ? QS_SENDMESSAGE : + QS_SENDMESSAGE + QS_POSTMESSAGE); + if (dwResult == WAIT_OBJECT_0 + nCount || + dwResult == WAIT_TIMEOUT && dwTimeOut != dwWait) { + MSG msg; + if (hwnd != NULL) { + while (PeekMessage(&msg, hwnd, uMsg, uMsg, PM_REMOVE)) { + DispatchMessage(&msg); + } + } + // Do this anyway - the previous peek doesn't flush out the + // messages + PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE); + + if (dwWait != INFINITE && dwWait != 0) { + DWORD dwNow = GetTickCount(); + + // Working with differences handles wrap-around + DWORD dwDiff = dwNow - dwStart; + if (dwDiff > dwWait) { + dwWait = 0; + } else { + dwWait -= dwDiff; + } + dwStart = dwNow; + } + if (!bPeeked) { + // Raise our priority to prevent our message queue + // building up + dwThreadPriority = GetThreadPriority(GetCurrentThread()); + if (dwThreadPriority < THREAD_PRIORITY_HIGHEST) { + SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_HIGHEST); + } + bPeeked = TRUE; + } + } else { + break; + } + } + if (bPeeked) { + SetThreadPriority(GetCurrentThread(), dwThreadPriority); + if (HIWORD(GetQueueStatus(QS_POSTMESSAGE)) & QS_POSTMESSAGE) { + if (uMsgId == 0) { + uMsgId = RegisterWindowMessage(TEXT("AMUnblock")); + } + if (uMsgId != 0) { + MSG msg; + // Remove old ones + while (PeekMessage(&msg, (HWND)-1, uMsgId, uMsgId, PM_REMOVE)) { + } + } + PostThreadMessage(GetCurrentThreadId(), uMsgId, 0, 0); + } + } + return dwResult; +} + +HRESULT AmGetLastErrorToHResult() +{ + DWORD dwLastError = GetLastError(); + if(dwLastError != 0) + { + return HRESULT_FROM_WIN32(dwLastError); + } + else + { + return E_FAIL; + } +} + +IUnknown* QzAtlComPtrAssign(IUnknown** pp, IUnknown* lp) +{ + if (lp != NULL) + lp->AddRef(); + if (*pp) + (*pp)->Release(); + *pp = lp; + return lp; +} + +/****************************************************************************** + +CompatibleTimeSetEvent + + CompatibleTimeSetEvent() sets the TIME_KILL_SYNCHRONOUS flag before calling +timeSetEvent() if the current operating system supports it. TIME_KILL_SYNCHRONOUS +is supported on Windows XP and later operating systems. + +Parameters: +- The same parameters as timeSetEvent(). See timeSetEvent()'s documentation in +the Platform SDK for more information. + +Return Value: +- The same return value as timeSetEvent(). See timeSetEvent()'s documentation in +the Platform SDK for more information. + +******************************************************************************/ +MMRESULT CompatibleTimeSetEvent( UINT uDelay, UINT uResolution, LPTIMECALLBACK lpTimeProc, DWORD_PTR dwUser, UINT fuEvent ) +{ + #if WINVER >= 0x0501 + { + static bool fCheckedVersion = false; + static bool fTimeKillSynchronousFlagAvailable = false; + + if( !fCheckedVersion ) { + fTimeKillSynchronousFlagAvailable = TimeKillSynchronousFlagAvailable(); + fCheckedVersion = true; + } + + if( fTimeKillSynchronousFlagAvailable ) { + fuEvent = fuEvent | TIME_KILL_SYNCHRONOUS; + } + } + #endif // WINVER >= 0x0501 + + return timeSetEvent( uDelay, uResolution, lpTimeProc, dwUser, fuEvent ); +} + +bool TimeKillSynchronousFlagAvailable( void ) +{ + OSVERSIONINFO osverinfo; + + osverinfo.dwOSVersionInfoSize = sizeof(osverinfo); + + if( GetVersionEx( &osverinfo ) ) { + + // Windows XP's major version is 5 and its' minor version is 1. + // timeSetEvent() started supporting the TIME_KILL_SYNCHRONOUS flag + // in Windows XP. + if( (osverinfo.dwMajorVersion > 5) || + ( (osverinfo.dwMajorVersion == 5) && (osverinfo.dwMinorVersion >= 1) ) ) { + return true; + } + } + + return false; +} diff --git a/ThirdParty/strmbas/wxutil.h b/ThirdParty/strmbas/wxutil.h new file mode 100644 index 0000000..e58d2f7 --- /dev/null +++ b/ThirdParty/strmbas/wxutil.h @@ -0,0 +1,532 @@ +//------------------------------------------------------------------------------ +// File: WXUtil.h +// +// Desc: DirectShow base classes - defines helper classes and functions for +// building multimedia filters. +// +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------------------------ + + +#ifndef __WXUTIL__ +#define __WXUTIL__ + +// eliminate spurious "statement has no effect" warnings. +#pragma warning(disable: 4705) + +// wrapper for whatever critical section we have +class CCritSec { + + // make copy constructor and assignment operator inaccessible + + CCritSec(const CCritSec &refCritSec); + CCritSec &operator=(const CCritSec &refCritSec); + + CRITICAL_SECTION m_CritSec; + +#ifdef _DEBUG +public: + DWORD m_currentOwner; + DWORD m_lockCount; + BOOL m_fTrace; // Trace this one +public: + CCritSec(); + ~CCritSec(); + void Lock(); + void Unlock(); +#else + +public: + CCritSec(); + + ~CCritSec(); + + void Lock(); + + void Unlock(); +#endif +}; + +// +// To make deadlocks easier to track it is useful to insert in the +// code an assertion that says whether we own a critical section or +// not. We make the routines that do the checking globals to avoid +// having different numbers of member functions in the debug and +// retail class implementations of CCritSec. In addition we provide +// a routine that allows usage of specific critical sections to be +// traced. This is NOT on by default - there are far too many. +// + +#ifdef _DEBUG + BOOL WINAPI CritCheckIn(CCritSec * pcCrit); + BOOL WINAPI CritCheckIn(const CCritSec * pcCrit); + BOOL WINAPI CritCheckOut(CCritSec * pcCrit); + BOOL WINAPI CritCheckOut(const CCritSec * pcCrit); + void WINAPI DbgLockTrace(CCritSec * pcCrit, BOOL fTrace); +#else + #define CritCheckIn(x) TRUE + #define CritCheckOut(x) TRUE + #define DbgLockTrace(pc, fT) +#endif + + +// locks a critical section, and unlocks it automatically +// when the lock goes out of scope +class CAutoLock { + + // make copy constructor and assignment operator inaccessible + + CAutoLock(const CAutoLock &refAutoLock); + CAutoLock &operator=(const CAutoLock &refAutoLock); + +protected: + CCritSec * m_pLock; + +public: + CAutoLock(CCritSec * plock) + { + m_pLock = plock; + m_pLock->Lock(); + }; + + ~CAutoLock() { + m_pLock->Unlock(); + }; +}; + + + +// wrapper for event objects +class CAMEvent +{ + + // make copy constructor and assignment operator inaccessible + + CAMEvent(const CAMEvent &refEvent); + CAMEvent &operator=(const CAMEvent &refEvent); + +protected: + HANDLE m_hEvent; +public: + CAMEvent(BOOL fManualReset = FALSE); + ~CAMEvent(); + + // Cast to HANDLE - we don't support this as an lvalue + operator HANDLE () const { return m_hEvent; }; + + void Set() {EXECUTE_ASSERT(SetEvent(m_hEvent));}; + BOOL Wait(DWORD dwTimeout = INFINITE) { + return (WaitForSingleObject(m_hEvent, dwTimeout) == WAIT_OBJECT_0); + }; + void Reset() { ResetEvent(m_hEvent); }; + BOOL Check() { return Wait(0); }; +}; + + +// wrapper for event objects that do message processing +// This adds ONE method to the CAMEvent object to allow sent +// messages to be processed while waiting + +class CAMMsgEvent : public CAMEvent +{ + +public: + + // Allow SEND messages to be processed while waiting + BOOL WaitMsg(DWORD dwTimeout = INFINITE); +}; + +// old name supported for the time being +#define CTimeoutEvent CAMEvent + +// support for a worker thread + +// simple thread class supports creation of worker thread, synchronization +// and communication. Can be derived to simplify parameter passing +class AM_NOVTABLE CAMThread { + + // make copy constructor and assignment operator inaccessible + + CAMThread(const CAMThread &refThread); + CAMThread &operator=(const CAMThread &refThread); + + CAMEvent m_EventSend; + CAMEvent m_EventComplete; + + DWORD m_dwParam; + DWORD m_dwReturnVal; + +protected: + HANDLE m_hThread; + + // thread will run this function on startup + // must be supplied by derived class + virtual DWORD ThreadProc() = 0; + +public: + CAMThread(); + virtual ~CAMThread(); + + CCritSec m_AccessLock; // locks access by client threads + CCritSec m_WorkerLock; // locks access to shared objects + + // thread initially runs this. param is actually 'this'. function + // just gets this and calls ThreadProc + static DWORD WINAPI InitialThreadProc(LPVOID pv); + + // start thread running - error if already running + BOOL Create(); + + // signal the thread, and block for a response + // + DWORD CallWorker(DWORD); + + // accessor thread calls this when done with thread (having told thread + // to exit) + void Close() { + #pragma warning( push ) + // C4312: 'type cast' : conversion from 'LONG' to 'PVOID' of greater size + // + // This code works correctly on 32-bit and 64-bit systems. + #pragma warning( disable : 4312 ) + HANDLE hThread = (HANDLE)InterlockedExchangePointer(&m_hThread, 0); + #pragma warning( pop ) + + if (hThread) { + WaitForSingleObject(hThread, INFINITE); + CloseHandle(hThread); + } + }; + + // ThreadExists + // Return TRUE if the thread exists. FALSE otherwise + BOOL ThreadExists(void) const + { + if (m_hThread == 0) { + return FALSE; + } else { + return TRUE; + } + } + + // wait for the next request + DWORD GetRequest(); + + // is there a request? + BOOL CheckRequest(DWORD * pParam); + + // reply to the request + void Reply(DWORD); + + // If you want to do WaitForMultipleObjects you'll need to include + // this handle in your wait list or you won't be responsive + HANDLE GetRequestHandle() const { return m_EventSend; }; + + // Find out what the request was + DWORD GetRequestParam() const { return m_dwParam; }; + + // call CoInitializeEx (COINIT_DISABLE_OLE1DDE) if + // available. S_FALSE means it's not available. + static HRESULT CoInitializeHelper(); +}; + + +// CQueue +// +// Implements a simple Queue ADT. The queue contains a finite number of +// objects, access to which is controlled by a semaphore. The semaphore +// is created with an initial count (N). Each time an object is added +// a call to WaitForSingleObject is made on the semaphore's handle. When +// this function returns a slot has been reserved in the queue for the new +// object. If no slots are available the function blocks until one becomes +// available. Each time an object is removed from the queue ReleaseSemaphore +// is called on the semaphore's handle, thus freeing a slot in the queue. +// If no objects are present in the queue the function blocks until an +// object has been added. + +#define DEFAULT_QUEUESIZE 2 + +template class CQueue { +private: + HANDLE hSemPut; // Semaphore controlling queue "putting" + HANDLE hSemGet; // Semaphore controlling queue "getting" + CRITICAL_SECTION CritSect; // Thread seriallization + int nMax; // Max objects allowed in queue + int iNextPut; // Array index of next "PutMsg" + int iNextGet; // Array index of next "GetMsg" + T *QueueObjects; // Array of objects (ptr's to void) + + void Initialize(int n) { + iNextPut = iNextGet = 0; + nMax = n; + InitializeCriticalSection(&CritSect); + hSemPut = CreateSemaphore(NULL, n, n, NULL); + hSemGet = CreateSemaphore(NULL, 0, n, NULL); + QueueObjects = new T[n]; + } + + +public: + CQueue(int n) { + Initialize(n); + } + + CQueue() { + Initialize(DEFAULT_QUEUESIZE); + } + + ~CQueue() { + delete [] QueueObjects; + DeleteCriticalSection(&CritSect); + CloseHandle(hSemPut); + CloseHandle(hSemGet); + } + + T GetQueueObject() { + int iSlot; + T Object; + LONG lPrevious; + + // Wait for someone to put something on our queue, returns straight + // away is there is already an object on the queue. + // + WaitForSingleObject(hSemGet, INFINITE); + + EnterCriticalSection(&CritSect); + iSlot = iNextGet++ % nMax; + Object = QueueObjects[iSlot]; + LeaveCriticalSection(&CritSect); + + // Release anyone waiting to put an object onto our queue as there + // is now space available in the queue. + // + ReleaseSemaphore(hSemPut, 1L, &lPrevious); + return Object; + } + + void PutQueueObject(T Object) { + int iSlot; + LONG lPrevious; + + // Wait for someone to get something from our queue, returns straight + // away is there is already an empty slot on the queue. + // + WaitForSingleObject(hSemPut, INFINITE); + + EnterCriticalSection(&CritSect); + iSlot = iNextPut++ % nMax; + QueueObjects[iSlot] = Object; + LeaveCriticalSection(&CritSect); + + // Release anyone waiting to remove an object from our queue as there + // is now an object available to be removed. + // + ReleaseSemaphore(hSemGet, 1L, &lPrevious); + } +}; + +// miscellaneous string conversion functions +// NOTE: as we need to use the same binaries on Win95 as on NT this code should +// be compiled WITHOUT unicode being defined. Otherwise we will not pick up +// these internal routines and the binary will not run on Win95. + +// int WINAPIV wsprintfWInternal(LPWSTR, LPCWSTR, ...); + +//LPWSTR +//WINAPI +//lstrcpyWInternal( +// LPWSTR lpString1, +// LPCWSTR lpString2 +// ); +LPWSTR +WINAPI +lstrcpynWInternal( + LPWSTR lpString1, + LPCWSTR lpString2, + int iMaxLength + ); +int +WINAPI +lstrcmpWInternal( + LPCWSTR lpString1, + LPCWSTR lpString2 + ); +int +WINAPI +lstrcmpiWInternal( + LPCWSTR lpString1, + LPCWSTR lpString2 + ); +int +WINAPI +lstrlenWInternal( + LPCWSTR lpString + ); + +#ifndef UNICODE +#define wsprintfW wsprintfWInternal +#define lstrcpyW lstrcpyWInternal +#define lstrcpynW lstrcpynWInternal +#define lstrcmpW lstrcmpWInternal +#define lstrcmpiW lstrcmpiWInternal +#define lstrlenW lstrlenWInternal +#endif + +extern "C" +void * __stdcall memmoveInternal(void *, const void *, size_t); + +inline void * __cdecl memchrInternal(const void *buf, int chr, size_t cnt) +{ +#ifdef _X86_ + void *pRet = NULL; + + _asm { + cld // make sure we get the direction right + mov ecx, cnt // num of bytes to scan + mov edi, buf // pointer byte stream + mov eax, chr // byte to scan for + repne scasb // look for the byte in the byte stream + jnz exit_memchr // Z flag set if byte found + dec edi // scasb always increments edi even when it + // finds the required byte + mov pRet, edi +exit_memchr: + } + return pRet; + +#else + while ( cnt && (*(unsigned char *)buf != (unsigned char)chr) ) { + buf = (unsigned char *)buf + 1; + cnt--; + } + + return(cnt ? (void *)buf : NULL); +#endif +} + +void WINAPI IntToWstr(int i, LPWSTR wstr, size_t len); + +#define WstrToInt(sz) _wtoi(sz) +#define atoiW(sz) _wtoi(sz) +#define atoiA(sz) atoi(sz) + +// These are available to help managing bitmap VIDEOINFOHEADER media structures + +extern const DWORD bits555[3]; +extern const DWORD bits565[3]; +extern const DWORD bits888[3]; + +// These help convert between VIDEOINFOHEADER and BITMAPINFO structures + +STDAPI_(const GUID) GetTrueColorType(const BITMAPINFOHEADER *pbmiHeader); +STDAPI_(const GUID) GetBitmapSubtype(const BITMAPINFOHEADER *pbmiHeader); +STDAPI_(WORD) GetBitCount(const GUID *pSubtype); + +// strmbase.lib implements this for compatibility with people who +// managed to link to this directly. we don't want to advertise it. +// +// STDAPI_(/* T */ CHAR *) GetSubtypeName(const GUID *pSubtype); + +STDAPI_(CHAR *) GetSubtypeNameA(const GUID *pSubtype); +STDAPI_(WCHAR *) GetSubtypeNameW(const GUID *pSubtype); + +#ifdef UNICODE +#define GetSubtypeName GetSubtypeNameW +#else +#define GetSubtypeName GetSubtypeNameA +#endif + +STDAPI_(LONG) GetBitmapFormatSize(const BITMAPINFOHEADER *pHeader); +STDAPI_(DWORD) GetBitmapSize(const BITMAPINFOHEADER *pHeader); +STDAPI_(BOOL) ContainsPalette(const VIDEOINFOHEADER *pVideoInfo); +STDAPI_(const RGBQUAD *) GetBitmapPalette(const VIDEOINFOHEADER *pVideoInfo); + + +// Compares two interfaces and returns TRUE if they are on the same object +BOOL WINAPI IsEqualObject(IUnknown *pFirst, IUnknown *pSecond); + +// This is for comparing pins +#define EqualPins(pPin1, pPin2) IsEqualObject(pPin1, pPin2) + + +// Arithmetic helper functions + +// Compute (a * b + rnd) / c +LONGLONG WINAPI llMulDiv(LONGLONG a, LONGLONG b, LONGLONG c, LONGLONG rnd); +LONGLONG WINAPI Int64x32Div32(LONGLONG a, LONG b, LONG c, LONG rnd); + + +// Avoids us dyna-linking to SysAllocString to copy BSTR strings +STDAPI WriteBSTR(BSTR * pstrDest, LPCWSTR szSrc); +STDAPI FreeBSTR(BSTR* pstr); + +// Return a wide string - allocating memory for it +// Returns: +// S_OK - no error +// E_POINTER - ppszReturn == NULL +// E_OUTOFMEMORY - can't allocate memory for returned string +STDAPI AMGetWideString(LPCWSTR pszString, LPWSTR *ppszReturn); + +// Special wait for objects owning windows +DWORD WINAPI WaitDispatchingMessages( + HANDLE hObject, + DWORD dwWait, + HWND hwnd = NULL, + UINT uMsg = 0, + HANDLE hEvent = NULL); + +// HRESULT_FROM_WIN32 converts ERROR_SUCCESS to a success code, but in +// our use of HRESULT_FROM_WIN32, it typically means a function failed +// to call SetLastError(), and we still want a failure code. +// +#define AmHresultFromWin32(x) (MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, x)) + +// call GetLastError and return an HRESULT value that will fail the +// SUCCEEDED() macro. +HRESULT AmGetLastErrorToHResult(void); + +// duplicate of ATL's CComPtr to avoid linker conflicts. + +IUnknown* QzAtlComPtrAssign(IUnknown** pp, IUnknown* lp); + +template +class QzCComPtr +{ +public: + typedef T _PtrClass; + QzCComPtr() {p=NULL;} + QzCComPtr(T* lp) + { + if ((p = lp) != NULL) + p->AddRef(); + } + QzCComPtr(const QzCComPtr& lp) + { + if ((p = lp.p) != NULL) + p->AddRef(); + } + ~QzCComPtr() {if (p) p->Release();} + void Release() {if (p) p->Release(); p=NULL;} + operator T*() {return (T*)p;} + T& operator*() {ASSERT(p!=NULL); return *p; } + //The assert on operator& usually indicates a bug. If this is really + //what is needed, however, take the address of the p member explicitly. + T** operator&() { ASSERT(p==NULL); return &p; } + T* operator->() { ASSERT(p!=NULL); return p; } + T* operator=(T* lp){return (T*)QzAtlComPtrAssign((IUnknown**)&p, lp);} + T* operator=(const QzCComPtr& lp) + { + return (T*)QzAtlComPtrAssign((IUnknown**)&p, lp.p); + } +#if _MSC_VER>1020 + bool operator!(){return (p == NULL);} +#else + BOOL operator!(){return (p == NULL) ? TRUE : FALSE;} +#endif + T* p; +}; + +MMRESULT CompatibleTimeSetEvent( UINT uDelay, UINT uResolution, LPTIMECALLBACK lpTimeProc, DWORD_PTR dwUser, UINT fuEvent ); +bool TimeKillSynchronousFlagAvailable( void ); + +#endif /* __WXUTIL__ */ diff --git a/ThirdParty/videoInput/CMakeLists.txt b/ThirdParty/videoInput/CMakeLists.txt new file mode 100644 index 0000000..b3e0122 --- /dev/null +++ b/ThirdParty/videoInput/CMakeLists.txt @@ -0,0 +1,26 @@ +# +# Stuff the average programmer needs to change +# +set(SRCS + videoInput.cpp) +set(HFILES + videoInput.h) +set(INCLUDE_DIRECTORIES ${BASE_INCLUDE_DIRECTORIES}) +set(LIBRARY_NAME videoInput) +if(WIN32) + set(INCLUDE_DIRECTORIES ${INCLUDE_DIRECTORIES} ${QEDIT_PATH}) + set(PREPROCESSOR_DEFINITIONS ${PREPROCESSOR_DEFINITIONS};/D_LIB;/D_WIN32_WINNT=0x0501) +endif(WIN32) +set(SRCS ${SRCS} ${HFILES}) + +add_library(${LIBRARY_NAME} STATIC ${SRCS}) + +target_compile_definitions(${LIBRARY_NAME} PRIVATE ${PREPROCESSOR_DEFINITIONS}) + +target_include_directories(${LIBRARY_NAME} PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR} + ${BASE_INCLUDE_DIRECTORIES} + "${CMAKE_CURRENT_SOURCE_DIR}/../strmbas" +) + +target_link_libraries(${LIBRARY_NAME} PUBLIC strmbas) \ No newline at end of file diff --git a/ThirdParty/videoInput/Win/videoInput.vcxproj b/ThirdParty/videoInput/Win/videoInput.vcxproj new file mode 100644 index 0000000..d682eeb --- /dev/null +++ b/ThirdParty/videoInput/Win/videoInput.vcxproj @@ -0,0 +1,133 @@ + + + + + Debug + x64 + + + Release + x64 + + + + {74EFD4AB-32C0-397D-AB90-A265D0CA656D} + 10.0.16299.0 + Win32Proj + x64 + videoInput + NoUpgrade + + + + StaticLibrary + Unicode + v141 + + + StaticLibrary + Unicode + v141 + + + + + + + + + + <_ProjectFileVersion>10.0.20506.1 + F:\IT-Dim\trunk\ThirdParty\videoInput\Win\Debug\ + videoInput.dir\Debug\ + videoInput + .lib + F:\IT-Dim\trunk\ThirdParty\videoInput\Win\Release\ + videoInput.dir\Release\ + videoInput + .lib + + + + F:\IT-Dim\wxWidgets\lib\vc_x64_dll\mswu;F:\IT-Dim\wxWidgets\include;F:\IT-Dim\trunk\ThirdParty\build\..\..\include;F:\IT-Dim\trunk\ThirdParty\build\..\..\ThirdParty\GLEW\include;F:\IT-Dim\trunk\ThirdParty\build\..\..\Include;F:\IT-Dim\trunk\ThirdParty\build\..\..\ThirdParty\strmbas;%(AdditionalIncludeDirectories) + Debug/ + EnableFastChecks + CompileAsCpp + ProgramDatabase + 4996 + Sync + Disabled + true + Disabled + NotUsing + MultiThreadedDebugDLL + true + Level3 + WIN32;_WINDOWS;__WXDEBUG__=1;WXUSINGDLL;UNICODE;_UNICODE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;_LIB;_WIN32_WINNT=0x0501;CMAKE_INTDIR="Debug";%(PreprocessorDefinitions) + $(IntDir) + + + WIN32;_DEBUG;_WINDOWS;__WXDEBUG__=1;WXUSINGDLL;UNICODE;_UNICODE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;_LIB;_WIN32_WINNT=0x0501;CMAKE_INTDIR=\"Debug\";%(PreprocessorDefinitions) + F:\IT-Dim\wxWidgets\lib\vc_x64_dll\mswu;F:\IT-Dim\wxWidgets\include;F:\IT-Dim\trunk\ThirdParty\build\..\..\include;F:\IT-Dim\trunk\ThirdParty\build\..\..\ThirdParty\GLEW\include;F:\IT-Dim\trunk\ThirdParty\build\..\..\Include;F:\IT-Dim\trunk\ThirdParty\build\..\..\ThirdParty\strmbas;%(AdditionalIncludeDirectories) + + + F:\IT-Dim\wxWidgets\lib\vc_x64_dll\mswu;F:\IT-Dim\wxWidgets\include;F:\IT-Dim\trunk\ThirdParty\build\..\..\include;F:\IT-Dim\trunk\ThirdParty\build\..\..\ThirdParty\GLEW\include;F:\IT-Dim\trunk\ThirdParty\build\..\..\Include;F:\IT-Dim\trunk\ThirdParty\build\..\..\ThirdParty\strmbas;%(AdditionalIncludeDirectories) + $(ProjectDir)/$(IntDir) + %(Filename).h + %(Filename).tlb + %(Filename)_i.c + %(Filename)_p.c + + + %(AdditionalOptions) /machine:x64 + + + + + F:\IT-Dim\wxWidgets\lib\vc_x64_dll\mswu;F:\IT-Dim\wxWidgets\include;F:\IT-Dim\trunk\ThirdParty\build\..\..\include;F:\IT-Dim\trunk\ThirdParty\build\..\..\ThirdParty\GLEW\include;F:\IT-Dim\trunk\ThirdParty\build\..\..\Include;F:\IT-Dim\trunk\ThirdParty\build\..\..\ThirdParty\strmbas;%(AdditionalIncludeDirectories) + Release/ + CompileAsCpp + 4996 + Sync + AnySuitable + true + MaxSpeed + NotUsing + MultiThreadedDLL + true + Level3 + WIN32;_WINDOWS;NDEBUG;WXUSINGDLL;UNICODE;_UNICODE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;__WXDEBUG__=1;_LIB;_WIN32_WINNT=0x0501;CMAKE_INTDIR="Release";%(PreprocessorDefinitions) + $(IntDir) + + + + + WIN32;_WINDOWS;NDEBUG;WXUSINGDLL;UNICODE;_UNICODE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;__WXDEBUG__=1;_LIB;_WIN32_WINNT=0x0501;CMAKE_INTDIR=\"Release\";%(PreprocessorDefinitions) + F:\IT-Dim\wxWidgets\lib\vc_x64_dll\mswu;F:\IT-Dim\wxWidgets\include;F:\IT-Dim\trunk\ThirdParty\build\..\..\include;F:\IT-Dim\trunk\ThirdParty\build\..\..\ThirdParty\GLEW\include;F:\IT-Dim\trunk\ThirdParty\build\..\..\Include;F:\IT-Dim\trunk\ThirdParty\build\..\..\ThirdParty\strmbas;%(AdditionalIncludeDirectories) + + + F:\IT-Dim\wxWidgets\lib\vc_x64_dll\mswu;F:\IT-Dim\wxWidgets\include;F:\IT-Dim\trunk\ThirdParty\build\..\..\include;F:\IT-Dim\trunk\ThirdParty\build\..\..\ThirdParty\GLEW\include;F:\IT-Dim\trunk\ThirdParty\build\..\..\Include;F:\IT-Dim\trunk\ThirdParty\build\..\..\ThirdParty\strmbas;%(AdditionalIncludeDirectories) + $(ProjectDir)/$(IntDir) + %(Filename).h + %(Filename).tlb + %(Filename)_i.c + %(Filename)_p.c + + + %(AdditionalOptions) /machine:x64 + + + + + + + + + {C08529ED-6D79-37D3-ADE8-50F312522BA0} + strmbas + + + + + + \ No newline at end of file diff --git a/ThirdParty/videoInput/Win/videoInput.vcxproj.filters b/ThirdParty/videoInput/Win/videoInput.vcxproj.filters new file mode 100644 index 0000000..4200fe3 --- /dev/null +++ b/ThirdParty/videoInput/Win/videoInput.vcxproj.filters @@ -0,0 +1,21 @@ + + + + + Source Files + + + + + Header Files + + + + + {51D244FA-C21C-3174-8217-FDB3769212C0} + + + {A07A887B-0B8A-3ED7-BD7F-43C26975F88D} + + + diff --git a/ThirdParty/videoInput/videoInput.cpp b/ThirdParty/videoInput/videoInput.cpp new file mode 100644 index 0000000..9038c41 --- /dev/null +++ b/ThirdParty/videoInput/videoInput.cpp @@ -0,0 +1,2340 @@ +//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +//THE SOFTWARE. + +#define DEBUG 1 +#define _DEBUG 1 + +#include "videoInput.h" +#include "tchar.h" + +//Include Directshow stuff here so we don't worry about needing all the h files. +#include "DShow.h" +#include "streams.h" +#pragma include_alias( "dxtrans.h", "qedit.h" ) +#define __IDxtCompositor_INTERFACE_DEFINED__ +#define __IDxtAlphaSetter_INTERFACE_DEFINED__ +#define __IDxtJpeg_INTERFACE_DEFINED__ +#define __IDxtKey_INTERFACE_DEFINED__ +#include "qedit.h" +#include "vector" +#include "Aviriff.h" +#include "Windows.h" + +//for threading +#include + +/////////////////////////// HANDY FUNCTIONS ///////////////////////////// + +void MyFreeMediaType(AM_MEDIA_TYPE& mt){ + if (mt.cbFormat != 0) + { + CoTaskMemFree((PVOID)mt.pbFormat); + mt.cbFormat = 0; + mt.pbFormat = NULL; + } + if (mt.pUnk != NULL) + { + // Unecessary because pUnk should not be used, but safest. + mt.pUnk->Release(); + mt.pUnk = NULL; + } +} + +void MyDeleteMediaType(AM_MEDIA_TYPE *pmt) +{ + if (pmt != NULL) + { + MyFreeMediaType(*pmt); + CoTaskMemFree(pmt); + } +} + +////////////////////////////// CALLBACK //////////////////////////////// + +//Callback class +class SampleGrabberCallback : public ISampleGrabberCB{ +public: + + //------------------------------------------------ + SampleGrabberCallback(){ + InitializeCriticalSection(&critSection); + freezeCheck = 0; + + + bufferSetup = false; + newFrame = false; + latestBufferLength = 0; + + hEvent = CreateEvent(NULL, true, false, NULL); + } + + + //------------------------------------------------ + ~SampleGrabberCallback(){ + ptrBuffer = NULL; + DeleteCriticalSection(&critSection); + CloseHandle(hEvent); + if(bufferSetup){ + delete pixels; + } + } + + + //------------------------------------------------ + bool setupBuffer(int numBytesIn){ + if(bufferSetup){ + return false; + }else{ + numBytes = numBytesIn; + pixels = new unsigned char[numBytes]; + bufferSetup = true; + newFrame = false; + latestBufferLength = 0; + } + return true; + } + + + //------------------------------------------------ + STDMETHODIMP_(ULONG) AddRef() { return 1; } + STDMETHODIMP_(ULONG) Release() { return 2; } + + + //------------------------------------------------ + STDMETHODIMP QueryInterface(REFIID riid, void **ppvObject){ + *ppvObject = static_cast(this); + return S_OK; + } + + + //This method is meant to have less overhead + //------------------------------------------------ + STDMETHODIMP SampleCB(double Time, IMediaSample *pSample){ + if(WaitForSingleObject(hEvent, 0) == WAIT_OBJECT_0) return S_OK; + + HRESULT hr = pSample->GetPointer(&ptrBuffer); + + if(hr == S_OK){ + latestBufferLength = pSample->GetActualDataLength(); + if(latestBufferLength == numBytes){ + EnterCriticalSection(&critSection); + memcpy(pixels, ptrBuffer, latestBufferLength); + newFrame = true; + freezeCheck = 1; + LeaveCriticalSection(&critSection); + SetEvent(hEvent); + }else{ + printf("ERROR: SampleCB() - buffer sizes do not match\n"); + } + } + + return S_OK; + } + + + //This method is meant to have more overhead + STDMETHODIMP BufferCB(double Time, BYTE *pBuffer, long BufferLen){ + return E_NOTIMPL; + } + + int freezeCheck; + + int latestBufferLength; + int numBytes; + bool newFrame; + bool bufferSetup; + unsigned char * pixels; + unsigned char * ptrBuffer; + CRITICAL_SECTION critSection; + HANDLE hEvent; +}; + + +////////////////////////////// VIDEO DEVICE //////////////////////////////// + +// ---------------------------------------------------------------------- +// Should this class also be the callback? +// +// ---------------------------------------------------------------------- + +videoDevice::videoDevice(){ + + pCaptureGraph = NULL; // Capture graph builder object + pGraph = NULL; // Graph builder object + pControl = NULL; // Media control object + pVideoInputFilter = NULL; // Video Capture filter + pGrabber = NULL; // Grabs frame + pDestFilter = NULL; // Null Renderer Filter + pGrabberF = NULL; // Grabber Filter + pMediaEvent = NULL; + streamConf = NULL; + pAmMediaType = NULL; + + //This is our callback class that processes the frame. + sgCallback = new SampleGrabberCallback(); + sgCallback->newFrame = false; + + //Default values for capture type + videoType = MEDIASUBTYPE_RGB24; + connection = PhysConn_Video_Composite; + storeConn = 0; + + videoSize = 0; + width = 0; + height = 0; + tryWidth = 0; + tryHeight = 0; + nFramesForReconnect= 10000; + nFramesRunning = 0; + myID = -1; + + tryDiffSize = false; + useCrossbar = false; + readyToCapture = false; + sizeSet = false; + setupStarted = false; + specificFormat = false; + autoReconnect = false; + requestedFrameTime = -1; + + memset(wDeviceName, 0, sizeof(WCHAR) * 255); + memset(nDeviceName, 0, sizeof(char) * 255); + +} + + +// ---------------------------------------------------------------------- +// The only place we are doing new +// +// ---------------------------------------------------------------------- + +void videoDevice::setSize(int w, int h){ + if(sizeSet){ + if(verbose)printf("SETUP: Error device size should not be set more than once \n"); + } + else + { + width = w; + height = h; + videoSize = w*h*3; + sizeSet = true; + pixels = new unsigned char[videoSize]; + pBuffer = new char[videoSize]; + + memset(pixels, 0 , videoSize); + sgCallback->setupBuffer(videoSize); + + } +} + + +// ---------------------------------------------------------------------- +// Borrowed from the SDK, use it to take apart the graph from +// the capture device downstream to the null renderer +// ---------------------------------------------------------------------- + +void videoDevice::NukeDownstream(IBaseFilter *pBF){ + IPin *pP, *pTo; + ULONG u; + IEnumPins *pins = NULL; + PIN_INFO pininfo; + HRESULT hr = pBF->EnumPins(&pins); + pins->Reset(); + while (hr == NOERROR) + { + hr = pins->Next(1, &pP, &u); + if (hr == S_OK && pP) + { + pP->ConnectedTo(&pTo); + if (pTo) + { + hr = pTo->QueryPinInfo(&pininfo); + if (hr == NOERROR) + { + if (pininfo.dir == PINDIR_INPUT) + { + NukeDownstream(pininfo.pFilter); + pGraph->Disconnect(pTo); + pGraph->Disconnect(pP); + pGraph->RemoveFilter(pininfo.pFilter); + } + pininfo.pFilter->Release(); + pininfo.pFilter = NULL; + } + pTo->Release(); + } + pP->Release(); + } + } + if (pins) pins->Release(); +} + + +// ---------------------------------------------------------------------- +// Also from SDK +// ---------------------------------------------------------------------- + +void videoDevice::destroyGraph(){ + HRESULT hr = NULL; + int FuncRetval=0; + int NumFilters=0; + + int i = 0; + while (hr == NOERROR) + { + IEnumFilters * pEnum = 0; + ULONG cFetched; + + // We must get the enumerator again every time because removing a filter from the graph + // invalidates the enumerator. We always get only the first filter from each enumerator. + hr = pGraph->EnumFilters(&pEnum); + if (FAILED(hr)) { if(verbose)printf("SETUP: pGraph->EnumFilters() failed. \n"); return; } + + IBaseFilter * pFilter = NULL; + if (pEnum->Next(1, &pFilter, &cFetched) == S_OK) + { + FILTER_INFO FilterInfo={0}; + hr = pFilter->QueryFilterInfo(&FilterInfo); + FilterInfo.pGraph->Release(); + + int count = 0; + char buffer[255]; + memset(buffer, 0, 255 * sizeof(char)); + + while( FilterInfo.achName[count] != 0x00 ) + { + buffer[count] = FilterInfo.achName[count]; + count++; + } + + if(verbose)printf("SETUP: removing filter %s...\n", buffer); + hr = pGraph->RemoveFilter(pFilter); + if (FAILED(hr)) { if(verbose)printf("SETUP: pGraph->RemoveFilter() failed. \n"); return; } + if(verbose)printf("SETUP: filter removed %s \n",buffer); + + pFilter->Release(); + pFilter = NULL; + } + else break; + pEnum->Release(); + pEnum = NULL; + i++; + } + + return; +} + + +// ---------------------------------------------------------------------- +// Our deconstructor, attempts to tear down graph and release filters etc +// Does checking to make sure it only is freeing if it needs to +// Probably could be a lot cleaner! :) +// ---------------------------------------------------------------------- + +videoDevice::~videoDevice(){ + + if(setupStarted){ if(verbose)printf("\nSETUP: Disconnecting device %i\n", myID); } + else{ + if(sgCallback){ + sgCallback->Release(); + delete sgCallback; + } + return; + } + + HRESULT HR = NULL; + + //Stop the callback and free it + if( (sgCallback) && (pGrabber) ) + { + pGrabber->SetCallback(NULL, 1); + if(verbose)printf("SETUP: freeing Grabber Callback\n"); + sgCallback->Release(); + + //delete our pixels + if(sizeSet){ + delete[] pixels; + delete[] pBuffer; + } + + delete sgCallback; + } + + //Check to see if the graph is running, if so stop it. + if( (pControl) ) + { + HR = pControl->Pause(); + if (FAILED(HR)) if(verbose)printf("ERROR - Could not pause pControl\n"); + + HR = pControl->Stop(); + if (FAILED(HR)) if(verbose)printf("ERROR - Could not stop pControl\n"); + } + + //Disconnect filters from capture device + if( (pVideoInputFilter) )NukeDownstream(pVideoInputFilter); + + //Release and zero pointers to our filters etc + if( (pDestFilter) ){ if(verbose)printf("SETUP: freeing Renderer \n"); + (pDestFilter)->Release(); + (pDestFilter) = 0; + } + if( (pVideoInputFilter) ){ if(verbose)printf("SETUP: freeing Capture Source \n"); + (pVideoInputFilter)->Release(); + (pVideoInputFilter) = 0; + } + if( (pGrabberF) ){ if(verbose)printf("SETUP: freeing Grabber Filter \n"); + (pGrabberF)->Release(); + (pGrabberF) = 0; + } + if( (pGrabber) ){ if(verbose)printf("SETUP: freeing Grabber \n"); + (pGrabber)->Release(); + (pGrabber) = 0; + } + if( (pControl) ){ if(verbose)printf("SETUP: freeing Control \n"); + (pControl)->Release(); + (pControl) = 0; + } + if( (pMediaEvent) ){ if(verbose)printf("SETUP: freeing Media Event \n"); + (pMediaEvent)->Release(); + (pMediaEvent) = 0; + } + if( (streamConf) ){ if(verbose)printf("SETUP: freeing Stream \n"); + (streamConf)->Release(); + (streamConf) = 0; + } + + if( (pAmMediaType) ){ if(verbose)printf("SETUP: freeing Media Type \n"); + MyDeleteMediaType(pAmMediaType); + } + + if((pMediaEvent)){ + if(verbose)printf("SETUP: freeing Media Event \n"); + (pMediaEvent)->Release(); + (pMediaEvent) = 0; + } + + //Destroy the graph + if( (pGraph) )destroyGraph(); + + //Release and zero our capture graph and our main graph + if( (pCaptureGraph) ){ if(verbose)printf("SETUP: freeing Capture Graph \n"); + (pCaptureGraph)->Release(); + (pCaptureGraph) = 0; + } + if( (pGraph) ){ if(verbose)printf("SETUP: freeing Main Graph \n"); + (pGraph)->Release(); + (pGraph) = 0; + } + + //delete our pointers + delete pDestFilter; + delete pVideoInputFilter; + delete pGrabberF; + delete pGrabber; + delete pControl; + delete streamConf; + delete pMediaEvent; + delete pCaptureGraph; + delete pGraph; + + if(verbose)printf("SETUP: Device %i disconnected and freed\n\n",myID); +} + + +////////////////////////////// VIDEO INPUT //////////////////////////////// +//////////////////////////// PUBLIC METHODS /////////////////////////////// + + +// ---------------------------------------------------------------------- +// Constructor - creates instances of videoDevice and adds the various +// media subtypes to check. +// ---------------------------------------------------------------------- + +videoInput::videoInput(){ + //start com + comInit(); + + devicesFound = 0; + callbackSetCount = 0; + bCallback = true; + + //setup a max no of device objects + for(int i=0; i= VI_MAX_CAMERAS || VDList[deviceNumber]->readyToCapture) return; + + if( idealFramerate > 0 ){ + VDList[deviceNumber]->requestedFrameTime = (unsigned long)(10000000 / idealFramerate); + } +} + + +// ---------------------------------------------------------------------- +// Set the requested framerate - no guarantee you will get this +// +// ---------------------------------------------------------------------- + +void videoInput::setAutoReconnectOnFreeze(int deviceNumber, bool doReconnect, int numMissedFramesBeforeReconnect){ + if(deviceNumber >= VI_MAX_CAMERAS) return; + + VDList[deviceNumber]->autoReconnect = doReconnect; + VDList[deviceNumber]->nFramesForReconnect = numMissedFramesBeforeReconnect; + +} + + +// ---------------------------------------------------------------------- +// Setup a device with the default settings +// +// ---------------------------------------------------------------------- + +bool videoInput::setupDevice(int deviceNumber){ + if(deviceNumber >= VI_MAX_CAMERAS || VDList[deviceNumber]->readyToCapture) return false; + + if(setup(deviceNumber))return true; + return false; +} + + +// ---------------------------------------------------------------------- +// Setup a device with the default size but specify input type +// +// ---------------------------------------------------------------------- + +bool videoInput::setupDevice(int deviceNumber, int connection){ + if(deviceNumber >= VI_MAX_CAMERAS || VDList[deviceNumber]->readyToCapture) return false; + + setPhyCon(deviceNumber, connection); + if(setup(deviceNumber))return true; + return false; +} + + +// ---------------------------------------------------------------------- +// Setup a device with the default connection but specify size +// +// ---------------------------------------------------------------------- + +bool videoInput::setupDevice(int deviceNumber, int w, int h){ + if(deviceNumber >= VI_MAX_CAMERAS || VDList[deviceNumber]->readyToCapture) return false; + + setAttemptCaptureSize(deviceNumber,w,h); + if(setup(deviceNumber))return true; + return false; +} + + +// ---------------------------------------------------------------------- +// Setup a device with specific size and connection +// +// ---------------------------------------------------------------------- + +bool videoInput::setupDevice(int deviceNumber, int w, int h, int connection){ + if(deviceNumber >= VI_MAX_CAMERAS || VDList[deviceNumber]->readyToCapture) return false; + + setAttemptCaptureSize(deviceNumber,w,h); + setPhyCon(deviceNumber, connection); + if(setup(deviceNumber))return true; + return false; +} + + +// ---------------------------------------------------------------------- +// Setup the default video format of the device +// Must be called after setup! +// See #define formats in header file (eg VI_NTSC_M ) +// +// ---------------------------------------------------------------------- + +bool videoInput::setFormat(int deviceNumber, int format){ + if(deviceNumber >= VI_MAX_CAMERAS || !VDList[deviceNumber]->readyToCapture) return false; + + bool returnVal = false; + + if(format >= 0 && format < VI_NUM_FORMATS){ + VDList[deviceNumber]->formatType = formatTypes[format]; + VDList[deviceNumber]->specificFormat = true; + + if(VDList[deviceNumber]->specificFormat){ + + HRESULT hr = getDevice(&VDList[deviceNumber]->pVideoInputFilter, deviceNumber, VDList[deviceNumber]->wDeviceName, VDList[deviceNumber]->nDeviceName); + if(hr != S_OK){ + return false; + } + + IAMAnalogVideoDecoder *pVideoDec = NULL; + hr = VDList[deviceNumber]->pCaptureGraph->FindInterface(NULL, &MEDIATYPE_Video, VDList[deviceNumber]->pVideoInputFilter, IID_IAMAnalogVideoDecoder, (void **)&pVideoDec); + + //in case the settings window some how freed them first + if(VDList[deviceNumber]->pVideoInputFilter)VDList[deviceNumber]->pVideoInputFilter->Release(); + if(VDList[deviceNumber]->pVideoInputFilter)VDList[deviceNumber]->pVideoInputFilter = NULL; + + if(FAILED(hr)){ + printf("SETUP: couldn't set requested format\n"); + }else{ + long lValue = 0; + hr = pVideoDec->get_AvailableTVFormats(&lValue); + if( SUCCEEDED(hr) && (lValue & VDList[deviceNumber]->formatType) ) + { + hr = pVideoDec->put_TVFormat(VDList[deviceNumber]->formatType); + if( FAILED(hr) ){ + printf("SETUP: couldn't set requested format\n"); + }else{ + returnVal = true; + } + } + + pVideoDec->Release(); + pVideoDec = NULL; + } + } + } + + return returnVal; +} + +// ---------------------------------------------------------------------- +// Our static function for returning device names - thanks Peter! +// Must call listDevices first. +// +// ---------------------------------------------------------------------- +char videoInput::deviceNames[VI_MAX_CAMERAS][255]={{0}}; + +char * videoInput::getDeviceName(int deviceID){ + if( deviceID >= VI_MAX_CAMERAS ){ + return NULL; + } + return deviceNames[deviceID]; +} + + +// ---------------------------------------------------------------------- +// Our static function for finding num devices available etc +// +// ---------------------------------------------------------------------- + +int videoInput::listDevices(bool silent){ + + //COM Library Intialization + comInit(); + + if(!silent)printf("\nVIDEOINPUT SPY MODE!\n\n"); + + + ICreateDevEnum *pDevEnum = NULL; + IEnumMoniker *pEnum = NULL; + int deviceCounter = 0; + + HRESULT hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL, + CLSCTX_INPROC_SERVER, IID_ICreateDevEnum, + reinterpret_cast(&pDevEnum)); + + + if (SUCCEEDED(hr)) + { + // Create an enumerator for the video capture category. + hr = pDevEnum->CreateClassEnumerator( + CLSID_VideoInputDeviceCategory, + &pEnum, 0); + + if(hr == S_OK){ + + if(!silent)printf("SETUP: Looking For Capture Devices\n"); + IMoniker *pMoniker = NULL; + + while (pEnum->Next(1, &pMoniker, NULL) == S_OK){ + + IPropertyBag *pPropBag; + hr = pMoniker->BindToStorage(0, 0, IID_IPropertyBag, + (void**)(&pPropBag)); + + if (FAILED(hr)){ + pMoniker->Release(); + continue; // Skip this one, maybe the next one will work. + } + + + // Find the description or friendly name. + VARIANT varName; + VariantInit(&varName); + hr = pPropBag->Read(L"Description", &varName, 0); + + if (FAILED(hr)) hr = pPropBag->Read(L"FriendlyName", &varName, 0); + + if (SUCCEEDED(hr)){ + + hr = pPropBag->Read(L"FriendlyName", &varName, 0); + + int count = 0; + int maxLen = sizeof(deviceNames[0])/sizeof(deviceNames[0][0]) - 2; + while( varName.bstrVal[count] != 0x00 && count < maxLen) { + deviceNames[deviceCounter][count] = varName.bstrVal[count]; + count++; + } + deviceNames[deviceCounter][count] = 0; + + if(!silent)printf("SETUP: %i) %s \n",deviceCounter, deviceNames[deviceCounter]); + } + + pPropBag->Release(); + pPropBag = NULL; + + pMoniker->Release(); + pMoniker = NULL; + + deviceCounter++; + } + + pDevEnum->Release(); + pDevEnum = NULL; + + pEnum->Release(); + pEnum = NULL; + } + + if(!silent)printf("SETUP: %i Device(s) found\n\n", deviceCounter); + } + + comUnInit(); + + return deviceCounter; +} + + +// ---------------------------------------------------------------------- +// +// +// ---------------------------------------------------------------------- + +int videoInput::getWidth(int id){ + + if(isDeviceSetup(id)) + { + return VDList[id] ->width; + } + + return 0; + +} + + +// ---------------------------------------------------------------------- +// +// +// ---------------------------------------------------------------------- + +int videoInput::getHeight(int id){ + + if(isDeviceSetup(id)) + { + return VDList[id] ->height; + } + + return 0; + +} + + +// ---------------------------------------------------------------------- +// +// +// ---------------------------------------------------------------------- + +int videoInput::getSize(int id){ + + if(isDeviceSetup(id)) + { + return VDList[id] ->videoSize; + } + + return 0; + +} + + +// ---------------------------------------------------------------------- +// Uses a supplied buffer +// ---------------------------------------------------------------------- + +bool videoInput::getPixels(int id, unsigned char * dstBuffer, bool flipRedAndBlue, bool flipImage){ + + bool success = false; + + if(isDeviceSetup(id)){ + if(bCallback){ + //callback capture + + DWORD result = WaitForSingleObject(VDList[id]->sgCallback->hEvent, 1000); + if( result != WAIT_OBJECT_0) return false; + + //double paranoia - mutexing with both event and critical section + EnterCriticalSection(&VDList[id]->sgCallback->critSection); + + unsigned char * src = VDList[id]->sgCallback->pixels; + unsigned char * dst = dstBuffer; + int height = VDList[id]->height; + int width = VDList[id]->width; + + processPixels(src, dst, width, height, flipRedAndBlue, flipImage); + VDList[id]->sgCallback->newFrame = false; + + LeaveCriticalSection(&VDList[id]->sgCallback->critSection); + + ResetEvent(VDList[id]->sgCallback->hEvent); + + success = true; + + } + else{ + //regular capture method + long bufferSize = VDList[id]->videoSize; + HRESULT hr = VDList[id]->pGrabber->GetCurrentBuffer(&bufferSize, (long *)VDList[id]->pBuffer); + if(hr==S_OK){ + int numBytes = VDList[id]->videoSize; + if (numBytes == bufferSize){ + + unsigned char * src = (unsigned char * )VDList[id]->pBuffer; + unsigned char * dst = dstBuffer; + int height = VDList[id]->height; + int width = VDList[id]->width; + + processPixels(src, dst, width, height, flipRedAndBlue, flipImage); + success = true; + }else{ + if(verbose)printf("ERROR: GetPixels() - bufferSizes do not match!\n"); + } + }else{ + if(verbose)printf("ERROR: GetPixels() - Unable to grab frame for device %i\n", id); + } + } + } + + return success; +} + + +// ---------------------------------------------------------------------- +// Returns a buffer +// ---------------------------------------------------------------------- +unsigned char * videoInput::getPixels(int id, bool flipRedAndBlue, bool flipImage){ + + if(isDeviceSetup(id)){ + getPixels(id, VDList[id]->pixels, flipRedAndBlue, flipImage); + } + + return VDList[id]->pixels; +} + + + +// ---------------------------------------------------------------------- +// +// +// ---------------------------------------------------------------------- +bool videoInput::isFrameNew(int id){ + if(!isDeviceSetup(id)) return false; + if(!bCallback)return true; + + bool result = false; + bool freeze = false; + + //again super paranoia! + EnterCriticalSection(&VDList[id]->sgCallback->critSection); + result = VDList[id]->sgCallback->newFrame; + + //we need to give it some time at the begining to start up so lets check after 400 frames + if(VDList[id]->nFramesRunning > 400 && VDList[id]->sgCallback->freezeCheck > VDList[id]->nFramesForReconnect ){ + freeze = true; + } + + //we increment the freezeCheck var here - the callback resets it to 1 + //so as long as the callback is running this var should never get too high. + //if the callback is not running then this number will get high and trigger the freeze action below + VDList[id]->sgCallback->freezeCheck++; + LeaveCriticalSection(&VDList[id]->sgCallback->critSection); + + VDList[id]->nFramesRunning++; + + if(freeze && VDList[id]->autoReconnect){ + if(verbose)printf("ERROR: Device seems frozen - attempting to reconnect\n"); + if( !restartDevice(VDList[id]->myID) ){ + if(verbose)printf("ERROR: Unable to reconnect to device\n"); + }else{ + if(verbose)printf("SUCCESS: Able to reconnect to device\n"); + } + } + + return result; +} + + +// ---------------------------------------------------------------------- +// +// +// ---------------------------------------------------------------------- + +bool videoInput::isDeviceSetup(int id){ + + if(idreadyToCapture)return true; + else return false; + +} + + +// ---------------------------------------------------------------------- +// Gives us a little pop up window to adjust settings +// We do this in a seperate thread now! +// ---------------------------------------------------------------------- + + +void __cdecl videoInput::basicThread(void * objPtr){ + + //get a reference to the video device + //not a copy as we need to free the filter + videoDevice * vd = *( (videoDevice **)(objPtr) ); + ShowFilterPropertyPages(vd->pVideoInputFilter); + + //now we free the filter and make sure it set to NULL + if(vd->pVideoInputFilter)vd->pVideoInputFilter->Release(); + if(vd->pVideoInputFilter)vd->pVideoInputFilter = NULL; + + return; +} + +void videoInput::showSettingsWindow(int id){ + + if(isDeviceSetup(id)){ + + HANDLE myTempThread; + + //we reconnect to the device as we have freed our reference to it + //why have we freed our reference? because there seemed to be an issue + //with some mpeg devices if we didn't + HRESULT hr = getDevice(&VDList[id]->pVideoInputFilter, id, VDList[id]->wDeviceName, VDList[id]->nDeviceName); + if(hr == S_OK){ + myTempThread = (HANDLE)_beginthread(basicThread, 0, (void *)&VDList[id]); + } + } +} + + +// Set a video signal setting using IAMVideoProcAmp +bool videoInput::getVideoSettingFilter(int deviceID, long Property, long &min, long &max, long &SteppingDelta, long ¤tValue, long &flags, long &defaultValue){ + if( !isDeviceSetup(deviceID) )return false; + + HRESULT hr; + bool isSuccessful = false; + + videoDevice * VD = VDList[deviceID]; + + hr = getDevice(&VD->pVideoInputFilter, deviceID, VD->wDeviceName, VD->nDeviceName); + if (FAILED(hr)){ + printf("setVideoSetting - getDevice Error\n"); + return false; + } + + IAMVideoProcAmp *pAMVideoProcAmp = NULL; + + hr = VD->pVideoInputFilter->QueryInterface(IID_IAMVideoProcAmp, (void**)&pAMVideoProcAmp); + if(FAILED(hr)){ + printf("setVideoSetting - QueryInterface Error\n"); + if(VD->pVideoInputFilter)VD->pVideoInputFilter->Release(); + if(VD->pVideoInputFilter)VD->pVideoInputFilter = NULL; + return false; + } + + if (verbose) printf("Setting video setting %ld.\n", Property); + + pAMVideoProcAmp->GetRange(Property, &min, &max, &SteppingDelta, &defaultValue, &flags); + if (verbose) printf("Range for video setting %ld: Min:%ld Max:%ld SteppingDelta:%ld Default:%ld Flags:%ld\n", Property, min, max, SteppingDelta, defaultValue, flags); + pAMVideoProcAmp->Get(Property, ¤tValue, &flags); + + if(pAMVideoProcAmp)pAMVideoProcAmp->Release(); + if(VD->pVideoInputFilter)VD->pVideoInputFilter->Release(); + if(VD->pVideoInputFilter)VD->pVideoInputFilter = NULL; + + return true; + +} + + +// Set a video signal setting using IAMVideoProcAmp +bool videoInput::setVideoSettingFilterPct(int deviceID, long Property, float pctValue, long Flags){ + if( !isDeviceSetup(deviceID) )return false; + + long min, max, currentValue, flags, defaultValue, stepAmnt; + + if( !getVideoSettingFilter(deviceID, Property, min, max, stepAmnt, currentValue, flags, defaultValue) )return false; + + if(pctValue > 1.0)pctValue = 1.0; + else if(pctValue < 0)pctValue = 0.0; + + float range = (float)max - (float)min; + if(range <= 0)return false; + if(stepAmnt == 0) return false; + + long value = (long)( (float)min + range * pctValue ); + long rasterValue = value; + + //if the range is the stepAmnt then it is just a switch + //so we either set the value to low or high + if( range == stepAmnt ){ + if( pctValue < 0.5)rasterValue = min; + else rasterValue = max; + }else{ + //we need to rasterize the value to the stepping amnt + long mod = value % stepAmnt; + float halfStep = (float)stepAmnt * 0.5; + if( mod < halfStep ) rasterValue -= mod; + else rasterValue += stepAmnt - mod; + printf("RASTER - pctValue is %f - value is %i - step is %i - mod is %i - rasterValue is %i\n", pctValue, value, stepAmnt, mod, rasterValue); + } + + return setVideoSettingFilter(deviceID, Property, rasterValue, Flags, false); +} + + +// Set a video signal setting using IAMVideoProcAmp +bool videoInput::setVideoSettingFilter(int deviceID, long Property, long lValue, long Flags, bool useDefaultValue){ + if( !isDeviceSetup(deviceID) )return false; + + HRESULT hr; + bool isSuccessful = false; + + videoDevice * VD = VDList[deviceID]; + + hr = getDevice(&VD->pVideoInputFilter, deviceID, VD->wDeviceName, VD->nDeviceName); + if (FAILED(hr)){ + printf("setVideoSetting - getDevice Error\n"); + return false; + } + + IAMVideoProcAmp *pAMVideoProcAmp = NULL; + + hr = VD->pVideoInputFilter->QueryInterface(IID_IAMVideoProcAmp, (void**)&pAMVideoProcAmp); + if(FAILED(hr)){ + printf("setVideoSetting - QueryInterface Error\n"); + if(VD->pVideoInputFilter)VD->pVideoInputFilter->Release(); + if(VD->pVideoInputFilter)VD->pVideoInputFilter = NULL; + return false; + } + + if (verbose) printf("Setting video setting %ld.\n", Property); + long CurrVal, Min, Max, SteppingDelta, Default, CapsFlags, AvailableCapsFlags = 0; + + + pAMVideoProcAmp->GetRange(Property, &Min, &Max, &SteppingDelta, &Default, &AvailableCapsFlags); + if (verbose) printf("Range for video setting %ld: Min:%ld Max:%ld SteppingDelta:%ld Default:%ld Flags:%ld\n", Property, Min, Max, SteppingDelta, Default, AvailableCapsFlags); + pAMVideoProcAmp->Get(Property, &CurrVal, &CapsFlags); + + if (verbose) printf("Current value: %ld Flags %ld (%s)\n", CurrVal, CapsFlags, (CapsFlags == 1 ? "Auto" : (CapsFlags == 2 ? "Manual" : "Unknown"))); + + if (useDefaultValue) { + pAMVideoProcAmp->Set(Property, Default, VideoProcAmp_Flags_Auto); + } + else{ + // Perhaps add a check that lValue and Flags are within the range aquired from GetRange above + pAMVideoProcAmp->Set(Property, lValue, Flags); + } + + if(pAMVideoProcAmp)pAMVideoProcAmp->Release(); + if(VD->pVideoInputFilter)VD->pVideoInputFilter->Release(); + if(VD->pVideoInputFilter)VD->pVideoInputFilter = NULL; + + return true; + +} + + +bool videoInput::setVideoSettingCameraPct(int deviceID, long Property, float pctValue, long Flags){ + if( !isDeviceSetup(deviceID) )return false; + + long min, max, currentValue, flags, defaultValue, stepAmnt; + + if( !getVideoSettingCamera(deviceID, Property, min, max, stepAmnt, currentValue, flags, defaultValue) )return false; + + if(pctValue > 1.0)pctValue = 1.0; + else if(pctValue < 0)pctValue = 0.0; + + float range = (float)max - (float)min; + if(range <= 0)return false; + if(stepAmnt == 0) return false; + + long value = (long)( (float)min + range * pctValue ); + long rasterValue = value; + + //if the range is the stepAmnt then it is just a switch + //so we either set the value to low or high + if( range == stepAmnt ){ + if( pctValue < 0.5)rasterValue = min; + else rasterValue = max; + }else{ + //we need to rasterize the value to the stepping amnt + long mod = value % stepAmnt; + float halfStep = (float)stepAmnt * 0.5; + if( mod < halfStep ) rasterValue -= mod; + else rasterValue += stepAmnt - mod; + printf("RASTER - pctValue is %f - value is %i - step is %i - mod is %i - rasterValue is %i\n", pctValue, value, stepAmnt, mod, rasterValue); + } + + return setVideoSettingCamera(deviceID, Property, rasterValue, Flags, false); +} + + +bool videoInput::setVideoSettingCamera(int deviceID, long Property, long lValue, long Flags, bool useDefaultValue){ + IAMCameraControl *pIAMCameraControl; + if(isDeviceSetup(deviceID)) + { + HRESULT hr; + hr = getDevice(&VDList[deviceID]->pVideoInputFilter, deviceID, VDList[deviceID]->wDeviceName, VDList[deviceID]->nDeviceName); + + if (verbose) printf("Setting video setting %ld.\n", Property); + hr = VDList[deviceID]->pVideoInputFilter->QueryInterface(IID_IAMCameraControl, (void**)&pIAMCameraControl); + if (FAILED(hr)) { + printf("Error\n"); + return false; + } + else + { + long CurrVal, Min, Max, SteppingDelta, Default, CapsFlags, AvailableCapsFlags; + pIAMCameraControl->GetRange(Property, &Min, &Max, &SteppingDelta, &Default, &AvailableCapsFlags); + if (verbose) printf("Range for video setting %ld: Min:%ld Max:%ld SteppingDelta:%ld Default:%ld Flags:%ld\n", Property, Min, Max, SteppingDelta, Default, AvailableCapsFlags); + pIAMCameraControl->Get(Property, &CurrVal, &CapsFlags); + if (verbose) printf("Current value: %ld Flags %ld (%s)\n", CurrVal, CapsFlags, (CapsFlags == 1 ? "Auto" : (CapsFlags == 2 ? "Manual" : "Unknown"))); + if (useDefaultValue) { + pIAMCameraControl->Set(Property, Default, CameraControl_Flags_Auto); + } + else + { + // Perhaps add a check that lValue and Flags are within the range aquired from GetRange above + pIAMCameraControl->Set(Property, lValue, Flags); + } + pIAMCameraControl->Release(); + return true; + } + } + return false; +} + + + +bool videoInput::getVideoSettingCamera(int deviceID, long Property, long &min, long &max, long &SteppingDelta, long ¤tValue, long &flags, long &defaultValue){ + if( !isDeviceSetup(deviceID) )return false; + + HRESULT hr; + bool isSuccessful = false; + + videoDevice * VD = VDList[deviceID]; + + hr = getDevice(&VD->pVideoInputFilter, deviceID, VD->wDeviceName, VD->nDeviceName); + if (FAILED(hr)){ + printf("setVideoSetting - getDevice Error\n"); + return false; + } + + IAMCameraControl *pIAMCameraControl = NULL; + + hr = VD->pVideoInputFilter->QueryInterface(IID_IAMCameraControl, (void**)&pIAMCameraControl); + if(FAILED(hr)){ + printf("setVideoSetting - QueryInterface Error\n"); + if(VD->pVideoInputFilter)VD->pVideoInputFilter->Release(); + if(VD->pVideoInputFilter)VD->pVideoInputFilter = NULL; + return false; + } + + if (verbose) printf("Setting video setting %ld.\n", Property); + + pIAMCameraControl->GetRange(Property, &min, &max, &SteppingDelta, &defaultValue, &flags); + if (verbose) printf("Range for video setting %ld: Min:%ld Max:%ld SteppingDelta:%ld Default:%ld Flags:%ld\n", Property, min, max, SteppingDelta, defaultValue, flags); + pIAMCameraControl->Get(Property, ¤tValue, &flags); + + if(pIAMCameraControl)pIAMCameraControl->Release(); + if(VD->pVideoInputFilter)VD->pVideoInputFilter->Release(); + if(VD->pVideoInputFilter)VD->pVideoInputFilter = NULL; + + return true; + +} + + +// ---------------------------------------------------------------------- +// Shutsdown the device, deletes the object and creates a new object +// so it is ready to be setup again +// ---------------------------------------------------------------------- + +void videoInput::stopDevice(int id){ + if(id < VI_MAX_CAMERAS) + { + delete VDList[id]; + VDList[id] = new videoDevice(); + } + +} + +// ---------------------------------------------------------------------- +// Restarts the device with the same settings it was using +// +// ---------------------------------------------------------------------- + +bool videoInput::restartDevice(int id){ + if(isDeviceSetup(id)) + { + int conn = VDList[id]->storeConn; + int tmpW = VDList[id]->width; + int tmpH = VDList[id]->height; + + bool bFormat = VDList[id]->specificFormat; + long format = VDList[id]->formatType; + + int nReconnect = VDList[id]->nFramesForReconnect; + bool bReconnect = VDList[id]->autoReconnect; + + unsigned long avgFrameTime = VDList[id]->requestedFrameTime; + + stopDevice(id); + + //set our fps if needed + if( avgFrameTime != -1){ + VDList[id]->requestedFrameTime = avgFrameTime; + } + + if( setupDevice(id, tmpW, tmpH, conn) ){ + //reapply the format - ntsc / pal etc + if( bFormat ){ + setFormat(id, format); + } + if( bReconnect ){ + setAutoReconnectOnFreeze(id, true, nReconnect); + } + return true; + } + } + return false; +} + +// ---------------------------------------------------------------------- +// Shuts down all devices, deletes objects and unitializes com if needed +// +// ---------------------------------------------------------------------- +videoInput::~videoInput(){ + + for(int i = 0; i < VI_MAX_CAMERAS; i++) + { + delete VDList[i]; + } + //Unitialize com + comUnInit(); +} + + +////////////////////////////// VIDEO INPUT //////////////////////////////// +//////////////////////////// PRIVATE METHODS ////////////////////////////// + +// ---------------------------------------------------------------------- +// We only should init com if it hasn't been done so by our apps thread +// Use a static counter to keep track of other times it has been inited +// (do we need to worry about multithreaded apps?) +// ---------------------------------------------------------------------- + +bool videoInput::comInit(){ + HRESULT hr = NULL; + + //no need for us to start com more than once + if(comInitCount == 0 ){ + + // Initialize the COM library. + //CoInitializeEx so videoInput can run in another thread + #ifdef VI_COM_MULTI_THREADED + hr = CoInitializeEx(NULL,COINIT_MULTITHREADED); + #else + hr = CoInitialize(NULL); + #endif + //this is the only case where there might be a problem + //if another library has started com as single threaded + //and we need it multi-threaded - send warning but don't fail + if( hr == RPC_E_CHANGED_MODE){ + if(verbose)printf("SETUP - COM already setup - threaded VI might not be possible\n"); + } + } + + comInitCount++; + return true; +} + + +// ---------------------------------------------------------------------- +// Same as above but to unitialize com, decreases counter and frees com +// if no one else is using it +// ---------------------------------------------------------------------- + +bool videoInput::comUnInit(){ + if(comInitCount > 0)comInitCount--; //decrease the count of instances using com + + if(comInitCount == 0){ + CoUninitialize(); //if there are no instances left - uninitialize com + return true; + } + + return false; +} + + +// ---------------------------------------------------------------------- +// This is the size we ask for - we might not get it though :) +// +// ---------------------------------------------------------------------- + +void videoInput::setAttemptCaptureSize(int id, int w, int h){ + + VDList[id]->tryWidth = w; + VDList[id]->tryHeight = h; + VDList[id]->tryDiffSize = true; + +} + + +// ---------------------------------------------------------------------- +// Set the connection type +// (maybe move to private?) +// ---------------------------------------------------------------------- + +void videoInput::setPhyCon(int id, int conn){ + + switch(conn){ + + case 0: + VDList[id]->connection = PhysConn_Video_Composite; + break; + case 1: + VDList[id]->connection = PhysConn_Video_SVideo; + break; + case 2: + VDList[id]->connection = PhysConn_Video_Tuner; + break; + case 3: + VDList[id]->connection = PhysConn_Video_USB; + break; + case 4: + VDList[id]->connection = PhysConn_Video_1394; + break; + default: + return; //if it is not these types don't set crossbar + break; + } + + VDList[id]->storeConn = conn; + VDList[id]->useCrossbar = true; +} + + +// ---------------------------------------------------------------------- +// Check that we are not trying to setup a non-existant device +// Then start the graph building! +// ---------------------------------------------------------------------- + +bool videoInput::setup(int deviceNumber){ + devicesFound = getDeviceCount(); + + if(deviceNumber>devicesFound-1) + { + if(verbose)printf("SETUP: device[%i] not found - you have %i devices available\n", deviceNumber, devicesFound); + if(devicesFound>=0) if(verbose)printf("SETUP: this means that the last device you can use is device[%i] \n", devicesFound-1); + return false; + } + + if(VDList[deviceNumber]->readyToCapture) + { + if(verbose)printf("SETUP: can't setup, device %i is currently being used\n",VDList[deviceNumber]->myID); + return false; + } + + HRESULT hr = start(deviceNumber, VDList[deviceNumber]); + if(hr == S_OK)return true; + else return false; +} + + +// ---------------------------------------------------------------------- +// Does both vertical buffer flipping and bgr to rgb swapping +// You have any combination of those. +// ---------------------------------------------------------------------- + +void videoInput::processPixels(unsigned char * src, unsigned char * dst, int width, int height, bool bRGB, bool bFlip){ + + int widthInBytes = width * 3; + int numBytes = widthInBytes * height; + + if(!bRGB){ + + int x = 0; + int y = 0; + + if(bFlip){ + for(int y = 0; y < height; y++){ + memcpy(dst + (y * widthInBytes), src + ( (height -y -1) * widthInBytes), widthInBytes); + } + + }else{ + memcpy(dst, src, numBytes); + } + }else{ + if(bFlip){ + + int x = 0; + int y = (height - 1) * widthInBytes; + src += y; + + for(int i = 0; i < numBytes; i+=3){ + if(x >= width){ + x = 0; + src -= widthInBytes*2; + } + + *dst = *(src+2); + dst++; + + *dst = *(src+1); + dst++; + + *dst = *src; + dst++; + + src+=3; + x++; + } + } + else{ + for(int i = 0; i < numBytes; i+=3){ + *dst = *(src+2); + dst++; + + *dst = *(src+1); + dst++; + + *dst = *src; + dst++; + + src+=3; + } + } + } +} + + +//------------------------------------------------------------------------------------------ +void videoInput::getMediaSubtypeAsString(GUID type, char * typeAsString){ + + char tmpStr[8]; + if( type == MEDIASUBTYPE_RGB24) sprintf(tmpStr, "RGB24"); + else if(type == MEDIASUBTYPE_RGB32) sprintf(tmpStr, "RGB32"); + else if(type == MEDIASUBTYPE_RGB555)sprintf(tmpStr, "RGB555"); + else if(type == MEDIASUBTYPE_RGB565)sprintf(tmpStr, "RGB565"); + else if(type == MEDIASUBTYPE_YUY2) sprintf(tmpStr, "YUY2"); + else if(type == MEDIASUBTYPE_YVYU) sprintf(tmpStr, "YVYU"); + else if(type == MEDIASUBTYPE_YUYV) sprintf(tmpStr, "YUYV"); + else if(type == MEDIASUBTYPE_IYUV) sprintf(tmpStr, "IYUV"); + else if(type == MEDIASUBTYPE_UYVY) sprintf(tmpStr, "UYVY"); + else if(type == MEDIASUBTYPE_YV12) sprintf(tmpStr, "YV12"); + else if(type == MEDIASUBTYPE_YVU9) sprintf(tmpStr, "YVU9"); + else if(type == MEDIASUBTYPE_Y411) sprintf(tmpStr, "Y411"); + else if(type == MEDIASUBTYPE_Y41P) sprintf(tmpStr, "Y41P"); + else if(type == MEDIASUBTYPE_Y211) sprintf(tmpStr, "Y211"); + else if(type == MEDIASUBTYPE_AYUV) sprintf(tmpStr, "AYUV"); + else if(type == MEDIASUBTYPE_Y800) sprintf(tmpStr, "Y800"); + else if(type == MEDIASUBTYPE_Y8) sprintf(tmpStr, "Y8"); + else if(type == MEDIASUBTYPE_GREY) sprintf(tmpStr, "GREY"); + else sprintf(tmpStr, "OTHER"); + + memcpy(typeAsString, tmpStr, sizeof(char)*8); +} + +//------------------------------------------------------------------------------------------- +static void findClosestSizeAndSubtype(videoDevice * VD, int widthIn, int heightIn, int &widthOut, int &heightOut, GUID & mediatypeOut){ + HRESULT hr; + + //find perfect match or closest size + int nearW = 9999999; + int nearH = 9999999; + bool foundClosestMatch = true; + + int iCount = 0; + int iSize = 0; + hr = VD->streamConf->GetNumberOfCapabilities(&iCount, &iSize); + + if (iSize == sizeof(VIDEO_STREAM_CONFIG_CAPS)) + { + //For each format type RGB24 YUV2 etc + for (int iFormat = 0; iFormat < iCount; iFormat++) + { + VIDEO_STREAM_CONFIG_CAPS scc; + AM_MEDIA_TYPE *pmtConfig; + hr = VD->streamConf->GetStreamCaps(iFormat, &pmtConfig, (BYTE*)&scc); + + if (SUCCEEDED(hr)){ + + //his is how many diff sizes are available for the format + int stepX = scc.OutputGranularityX; + int stepY = scc.OutputGranularityY; + + int tempW = 999999; + int tempH = 999999; + + //Don't want to get stuck in a loop + if(stepX < 1 || stepY < 1) continue; + + //if(verbose)printf("min is %i %i max is %i %i - res is %i %i \n", scc.MinOutputSize.cx, scc.MinOutputSize.cy, scc.MaxOutputSize.cx, scc.MaxOutputSize.cy, stepX, stepY); + //if(verbose)printf("min frame duration is %i max duration is %i\n", scc.MinFrameInterval, scc.MaxFrameInterval); + + bool exactMatch = false; + bool exactMatchX = false; + bool exactMatchY = false; + + for(int x = scc.MinOutputSize.cx; x <= scc.MaxOutputSize.cx; x+= stepX){ + //If we find an exact match + if( widthIn == x ){ + exactMatchX = true; + tempW = x; + } + //Otherwise lets find the closest match based on width + else if( abs(widthIn-x) < abs(widthIn-tempW) ){ + tempW = x; + } + } + + for(int y = scc.MinOutputSize.cy; y <= scc.MaxOutputSize.cy; y+= stepY){ + //If we find an exact match + if( heightIn == y){ + exactMatchY = true; + tempH = y; + } + //Otherwise lets find the closest match based on height + else if( abs(heightIn-y) < abs(heightIn-tempH) ){ + tempH = y; + } + } + + //see if we have an exact match! + if(exactMatchX && exactMatchY){ + foundClosestMatch = false; + exactMatch = true; + + widthOut = widthIn; + heightOut = heightIn; + mediatypeOut = pmtConfig->subtype; + } + + //otherwise lets see if this filters closest size is the closest + //available. the closest size is determined by the sum difference + //of the widths and heights + else if( abs(widthIn - tempW) + abs(heightIn - tempH) < abs(widthIn - nearW) + abs(heightIn - nearH) ) + { + nearW = tempW; + nearH = tempH; + + widthOut = nearW; + heightOut = nearH; + mediatypeOut = pmtConfig->subtype; + } + + MyDeleteMediaType(pmtConfig); + + //If we have found an exact match no need to search anymore + if(exactMatch)break; + } + } + } + +} + + +//--------------------------------------------------------------------------------------------------- +static bool setSizeAndSubtype(videoDevice * VD, int attemptWidth, int attemptHeight, GUID mediatype){ + VIDEOINFOHEADER *pVih = reinterpret_cast(VD->pAmMediaType->pbFormat); + + //store current size + int tmpWidth = HEADER(pVih)->biWidth; + int tmpHeight = HEADER(pVih)->biHeight; + AM_MEDIA_TYPE * tmpType = NULL; + + HRESULT hr = VD->streamConf->GetFormat(&tmpType); + if(hr != S_OK)return false; + + //set new size: + //width and height + HEADER(pVih)->biWidth = attemptWidth; + HEADER(pVih)->biHeight = attemptHeight; + + VD->pAmMediaType->formattype = FORMAT_VideoInfo; + VD->pAmMediaType->majortype = MEDIATYPE_Video; + VD->pAmMediaType->subtype = mediatype; + + //buffer size + VD->pAmMediaType->lSampleSize = attemptWidth*attemptHeight*3; + + //set fps if requested + if( VD->requestedFrameTime != -1){ + pVih->AvgTimePerFrame = VD->requestedFrameTime; + } + + //okay lets try new size + hr = VD->streamConf->SetFormat(VD->pAmMediaType); + if(hr == S_OK){ + if( tmpType != NULL )MyDeleteMediaType(tmpType); + return true; + }else{ + VD->streamConf->SetFormat(tmpType); + if( tmpType != NULL )MyDeleteMediaType(tmpType); + } + + return false; +} + +// ---------------------------------------------------------------------- +// Where all the work happens! +// Attempts to build a graph for the specified device +// ---------------------------------------------------------------------- + +int videoInput::start(int deviceID, videoDevice *VD){ + + HRESULT hr = NULL; + VD->myID = deviceID; + VD->setupStarted = true; + CAPTURE_MODE = PIN_CATEGORY_CAPTURE; //Don't worry - it ends up being preview (which is faster) + callbackSetCount = 1; //make sure callback method is not changed after setup called + + if(verbose)printf("SETUP: Setting up device %i\n",deviceID); + + // CREATE THE GRAPH BUILDER // + // Create the filter graph manager and query for interfaces. + hr = CoCreateInstance(CLSID_CaptureGraphBuilder2, NULL, CLSCTX_INPROC_SERVER, IID_ICaptureGraphBuilder2, (void **)&VD->pCaptureGraph); + if (FAILED(hr)) // FAILED is a macro that tests the return value + { + if(verbose)printf("ERROR - Could not create the Filter Graph Manager\n"); + return hr; + } + + //FITLER GRAPH MANAGER// + // Create the Filter Graph Manager. + hr = CoCreateInstance(CLSID_FilterGraph, 0, CLSCTX_INPROC_SERVER,IID_IGraphBuilder, (void**)&VD->pGraph); + if (FAILED(hr)) + { + if(verbose)printf("ERROR - Could not add the graph builder!\n"); + stopDevice(deviceID); + return hr; + } + + //SET THE FILTERGRAPH// + hr = VD->pCaptureGraph->SetFiltergraph(VD->pGraph); + if (FAILED(hr)) + { + if(verbose)printf("ERROR - Could not set filtergraph\n"); + stopDevice(deviceID); + return hr; + } + + //MEDIA CONTROL (START/STOPS STREAM)// + // Using QueryInterface on the graph builder, + // Get the Media Control object. + hr = VD->pGraph->QueryInterface(IID_IMediaControl, (void **)&VD->pControl); + if (FAILED(hr)) + { + if(verbose)printf("ERROR - Could not create the Media Control object\n"); + stopDevice(deviceID); + return hr; + } + + + //FIND VIDEO DEVICE AND ADD TO GRAPH// + //gets the device specified by the second argument. + hr = getDevice(&VD->pVideoInputFilter, deviceID, VD->wDeviceName, VD->nDeviceName); + + if (SUCCEEDED(hr)){ + if(verbose)printf("SETUP: %s\n", VD->nDeviceName); + hr = VD->pGraph->AddFilter(VD->pVideoInputFilter, VD->wDeviceName); + }else{ + if(verbose)printf("ERROR - Could not find specified video device\n"); + stopDevice(deviceID); + return hr; + } + + //LOOK FOR PREVIEW PIN IF THERE IS NONE THEN WE USE CAPTURE PIN AND THEN SMART TEE TO PREVIEW + IAMStreamConfig *streamConfTest = NULL; + hr = VD->pCaptureGraph->FindInterface(&PIN_CATEGORY_PREVIEW, &MEDIATYPE_Video, VD->pVideoInputFilter, IID_IAMStreamConfig, (void **)&streamConfTest); + if(FAILED(hr)){ + if(verbose)printf("SETUP: Couldn't find preview pin using SmartTee\n"); + }else{ + CAPTURE_MODE = PIN_CATEGORY_PREVIEW; + streamConfTest->Release(); + streamConfTest = NULL; + } + + //CROSSBAR (SELECT PHYSICAL INPUT TYPE)// + //my own function that checks to see if the device can support a crossbar and if so it routes it. + //webcams tend not to have a crossbar so this function will also detect a webcams and not apply the crossbar + if(VD->useCrossbar) + { + if(verbose)printf("SETUP: Checking crossbar\n"); + routeCrossbar(&VD->pCaptureGraph, &VD->pVideoInputFilter, VD->connection, CAPTURE_MODE); + } + + + //we do this because webcams don't have a preview mode + hr = VD->pCaptureGraph->FindInterface(&CAPTURE_MODE, &MEDIATYPE_Video, VD->pVideoInputFilter, IID_IAMStreamConfig, (void **)&VD->streamConf); + if(FAILED(hr)){ + if(verbose)printf("ERROR: Couldn't config the stream!\n"); + stopDevice(deviceID); + return hr; + } + + //NOW LETS DEAL WITH GETTING THE RIGHT SIZE + hr = VD->streamConf->GetFormat(&VD->pAmMediaType); + if(FAILED(hr)){ + if(verbose)printf("ERROR: Couldn't getFormat for pAmMediaType!\n"); + stopDevice(deviceID); + return hr; + } + + VIDEOINFOHEADER *pVih = reinterpret_cast(VD->pAmMediaType->pbFormat); + int currentWidth = HEADER(pVih)->biWidth; + int currentHeight = HEADER(pVih)->biHeight; + + bool customSize = VD->tryDiffSize; + bool foundSize = false; + + if(customSize){ + if(verbose) printf("SETUP: Default Format is set to %i by %i \n", currentWidth, currentHeight); + + char guidStr[8]; + for(int i = 0; i < VI_NUM_TYPES; i++){ + + getMediaSubtypeAsString(mediaSubtypes[i], guidStr); + + if(verbose)printf("SETUP: trying format %s @ %i by %i\n", guidStr, VD->tryWidth, VD->tryHeight); + if( setSizeAndSubtype(VD, VD->tryWidth, VD->tryHeight, mediaSubtypes[i]) ){ + VD->setSize(VD->tryWidth, VD->tryHeight); + foundSize = true; + break; + } + } + + //if we didn't find the requested size - lets try and find the closest matching size + if( foundSize == false ){ + if( verbose )printf("SETUP: couldn't find requested size - searching for closest matching size\n"); + + int closestWidth = -1; + int closestHeight = -1; + GUID newMediaSubtype; + + findClosestSizeAndSubtype(VD, VD->tryWidth, VD->tryHeight, closestWidth, closestHeight, newMediaSubtype); + + if( closestWidth != -1 && closestHeight != -1){ + getMediaSubtypeAsString(newMediaSubtype, guidStr); + + if(verbose)printf("SETUP: closest supported size is %s @ %i %i\n", guidStr, closestWidth, closestHeight); + if( setSizeAndSubtype(VD, closestWidth, closestHeight, newMediaSubtype) ){ + VD->setSize(closestWidth, closestHeight); + foundSize = true; + } + } + } + } + + //if we didn't specify a custom size or if we did but couldn't find it lets setup with the default settings + if(customSize == false || foundSize == false){ + if( VD->requestedFrameTime != -1 ){ + pVih->AvgTimePerFrame = VD->requestedFrameTime; + hr = VD->streamConf->SetFormat(VD->pAmMediaType); + } + VD->setSize(currentWidth, currentHeight); + } + + //SAMPLE GRABBER (ALLOWS US TO GRAB THE BUFFER)// + // Create the Sample Grabber. + hr = CoCreateInstance(CLSID_SampleGrabber, NULL, CLSCTX_INPROC_SERVER,IID_IBaseFilter, (void**)&VD->pGrabberF); + if (FAILED(hr)){ + if(verbose)printf("Could not Create Sample Grabber - CoCreateInstance()\n"); + stopDevice(deviceID); + return hr; + } + + hr = VD->pGraph->AddFilter(VD->pGrabberF, L"Sample Grabber"); + if (FAILED(hr)){ + if(verbose)printf("Could not add Sample Grabber - AddFilter()\n"); + stopDevice(deviceID); + return hr; + } + + hr = VD->pGrabberF->QueryInterface(IID_ISampleGrabber, (void**)&VD->pGrabber); + if (FAILED(hr)){ + if(verbose)printf("ERROR: Could not query SampleGrabber\n"); + stopDevice(deviceID); + return hr; + } + + + //Set Params - One Shot should be false unless you want to capture just one buffer + hr = VD->pGrabber->SetOneShot(FALSE); + if(bCallback){ + hr = VD->pGrabber->SetBufferSamples(FALSE); + }else{ + hr = VD->pGrabber->SetBufferSamples(TRUE); + } + + if(bCallback){ + //Tell the grabber to use our callback function - 0 is for SampleCB and 1 for BufferCB + //We use SampleCB + hr = VD->pGrabber->SetCallback(VD->sgCallback, 0); + if (FAILED(hr)){ + if(verbose)printf("ERROR: problem setting callback\n"); + stopDevice(deviceID); + return hr; + }else{ + if(verbose)printf("SETUP: Capture callback set\n"); + } + } + + //MEDIA CONVERSION + //Get video properties from the stream's mediatype and apply to the grabber (otherwise we don't get an RGB image) + //zero the media type - lets try this :) - maybe this works? + AM_MEDIA_TYPE mt; + ZeroMemory(&mt,sizeof(AM_MEDIA_TYPE)); + + mt.majortype = MEDIATYPE_Video; + mt.subtype = MEDIASUBTYPE_RGB24; + mt.formattype = FORMAT_VideoInfo; + + //VD->pAmMediaType->subtype = VD->videoType; + hr = VD->pGrabber->SetMediaType(&mt); + + //lets try freeing our stream conf here too + //this will fail if the device is already running + if(VD->streamConf){ + VD->streamConf->Release(); + VD->streamConf = NULL; + }else{ + if(verbose)printf("ERROR: connecting device - prehaps it is already being used?\n"); + stopDevice(deviceID); + return S_FALSE; + } + + + //NULL RENDERER// + //used to give the video stream somewhere to go to. + hr = CoCreateInstance(CLSID_NullRenderer, NULL, CLSCTX_INPROC_SERVER, IID_IBaseFilter, (void**)(&VD->pDestFilter)); + if (FAILED(hr)){ + if(verbose)printf("ERROR: Could not create filter - NullRenderer\n"); + stopDevice(deviceID); + return hr; + } + + hr = VD->pGraph->AddFilter(VD->pDestFilter, L"NullRenderer"); + if (FAILED(hr)){ + if(verbose)printf("ERROR: Could not add filter - NullRenderer\n"); + stopDevice(deviceID); + return hr; + } + + //RENDER STREAM// + //This is where the stream gets put together. + hr = VD->pCaptureGraph->RenderStream(&PIN_CATEGORY_PREVIEW, &MEDIATYPE_Video, VD->pVideoInputFilter, VD->pGrabberF, VD->pDestFilter); + + if (FAILED(hr)){ + if(verbose)printf("ERROR: Could not connect pins - RenderStream()\n"); + stopDevice(deviceID); + return hr; + } + + + //EXP - lets try setting the sync source to null - and make it run as fast as possible + { + IMediaFilter *pMediaFilter = 0; + hr = VD->pGraph->QueryInterface(IID_IMediaFilter, (void**)&pMediaFilter); + if (FAILED(hr)){ + if(verbose)printf("ERROR: Could not get IID_IMediaFilter interface\n"); + }else{ + pMediaFilter->SetSyncSource(NULL); + pMediaFilter->Release(); + } + } + + + //LETS RUN THE STREAM! + hr = VD->pControl->Run(); + + if (FAILED(hr)){ + if(verbose)printf("ERROR: Could not start graph\n"); + stopDevice(deviceID); + return hr; + } + + + //MAKE SURE THE DEVICE IS SENDING VIDEO BEFORE WE FINISH + if(!bCallback){ + + long bufferSize = VD->videoSize; + + while( hr != S_OK){ + hr = VD->pGrabber->GetCurrentBuffer(&bufferSize, (long *)VD->pBuffer); + Sleep(10); + } + + } + + if(verbose)printf("SETUP: Device is setup and ready to capture.\n\n"); + VD->readyToCapture = true; + + //Release filters - seen someone else do this + //looks like it solved the freezes + + //if we release this then we don't have access to the settings + //we release our video input filter but then reconnect with it + //each time we need to use it + VD->pVideoInputFilter->Release(); + VD->pVideoInputFilter = NULL; + + VD->pGrabberF->Release(); + VD->pGrabberF = NULL; + + VD->pDestFilter->Release(); + VD->pDestFilter = NULL; + + return S_OK; +} + + +// ---------------------------------------------------------------------- +// Returns number of good devices +// +// ---------------------------------------------------------------------- + +int videoInput::getDeviceCount(){ + + + ICreateDevEnum *pDevEnum = NULL; + IEnumMoniker *pEnum = NULL; + int deviceCounter = 0; + + HRESULT hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL, + CLSCTX_INPROC_SERVER, IID_ICreateDevEnum, + reinterpret_cast(&pDevEnum)); + + + if (SUCCEEDED(hr)) + { + // Create an enumerator for the video capture category. + hr = pDevEnum->CreateClassEnumerator( + CLSID_VideoInputDeviceCategory, + &pEnum, 0); + + if(hr == S_OK){ + IMoniker *pMoniker = NULL; + while (pEnum->Next(1, &pMoniker, NULL) == S_OK){ + + IPropertyBag *pPropBag; + hr = pMoniker->BindToStorage(0, 0, IID_IPropertyBag, + (void**)(&pPropBag)); + + if (FAILED(hr)){ + pMoniker->Release(); + continue; // Skip this one, maybe the next one will work. + } + + pPropBag->Release(); + pPropBag = NULL; + + pMoniker->Release(); + pMoniker = NULL; + + deviceCounter++; + } + + pEnum->Release(); + pEnum = NULL; + } + + pDevEnum->Release(); + pDevEnum = NULL; + } + return deviceCounter; +} + + +// ---------------------------------------------------------------------- +// Do we need this? +// +// Enumerate all of the video input devices +// Return the filter with a matching friendly name +// ---------------------------------------------------------------------- + +HRESULT videoInput::getDevice(IBaseFilter** gottaFilter, int deviceId, WCHAR * wDeviceName, char * nDeviceName){ + BOOL done = false; + int deviceCounter = 0; + + // Create the System Device Enumerator. + ICreateDevEnum *pSysDevEnum = NULL; + HRESULT hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC_SERVER, IID_ICreateDevEnum, (void **)&pSysDevEnum); + if (FAILED(hr)) + { + return hr; + } + + // Obtain a class enumerator for the video input category. + IEnumMoniker *pEnumCat = NULL; + hr = pSysDevEnum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory, &pEnumCat, 0); + + if (hr == S_OK) + { + // Enumerate the monikers. + IMoniker *pMoniker = NULL; + ULONG cFetched; + while ((pEnumCat->Next(1, &pMoniker, &cFetched) == S_OK) && (!done)) + { + if(deviceCounter == deviceId) + { + // Bind the first moniker to an object + IPropertyBag *pPropBag; + hr = pMoniker->BindToStorage(0, 0, IID_IPropertyBag, (void **)&pPropBag); + if (SUCCEEDED(hr)) + { + // To retrieve the filter's friendly name, do the following: + VARIANT varName; + VariantInit(&varName); + hr = pPropBag->Read(L"FriendlyName", &varName, 0); + if (SUCCEEDED(hr)) + { + + //copy the name to nDeviceName & wDeviceName + int count = 0; + while( varName.bstrVal[count] != 0x00 ) { + wDeviceName[count] = varName.bstrVal[count]; + nDeviceName[count] = (char)varName.bstrVal[count]; + count++; + } + + // We found it, so send it back to the caller + hr = pMoniker->BindToObject(NULL, NULL, IID_IBaseFilter, (void**)gottaFilter); + done = true; + } + VariantClear(&varName); + pPropBag->Release(); + pPropBag = NULL; + pMoniker->Release(); + pMoniker = NULL; + } + } + deviceCounter++; + } + pEnumCat->Release(); + pEnumCat = NULL; + } + pSysDevEnum->Release(); + pSysDevEnum = NULL; + + if (done) { + return hr; // found it, return native error + } else { + return VFW_E_NOT_FOUND; // didn't find it error + } +} + + +// ---------------------------------------------------------------------- +// Show the property pages for a filter +// This is stolen from the DX9 SDK +// ---------------------------------------------------------------------- + +HRESULT videoInput::ShowFilterPropertyPages(IBaseFilter *pFilter){ + + ISpecifyPropertyPages *pProp; + HRESULT hr = pFilter->QueryInterface(IID_ISpecifyPropertyPages, (void **)&pProp); + if (SUCCEEDED(hr)) + { + // Get the filter's name and IUnknown pointer. + FILTER_INFO FilterInfo; + hr = pFilter->QueryFilterInfo(&FilterInfo); + IUnknown *pFilterUnk; + pFilter->QueryInterface(IID_IUnknown, (void **)&pFilterUnk); + + // Show the page. + CAUUID caGUID; + pProp->GetPages(&caGUID); + pProp->Release(); + OleCreatePropertyFrame( + NULL, // Parent window + 0, 0, // Reserved + FilterInfo.achName, // Caption for the dialog box + 1, // Number of objects (just the filter) + &pFilterUnk, // Array of object pointers. + caGUID.cElems, // Number of property pages + caGUID.pElems, // Array of property page CLSIDs + 0, // Locale identifier + 0, NULL // Reserved + ); + + // Clean up. + if(pFilterUnk)pFilterUnk->Release(); + if(FilterInfo.pGraph)FilterInfo.pGraph->Release(); + CoTaskMemFree(caGUID.pElems); + } + return hr; +} + + +// ---------------------------------------------------------------------- +// This code was also brazenly stolen from the DX9 SDK +// Pass it a file name in wszPath, and it will save the filter graph to that file. +// ---------------------------------------------------------------------- + +HRESULT videoInput::SaveGraphFile(IGraphBuilder *pGraph, WCHAR *wszPath) { + const WCHAR wszStreamName[] = L"ActiveMovieGraph"; + HRESULT hr; + IStorage *pStorage = NULL; + + // First, create a document file which will hold the GRF file + hr = StgCreateDocfile( + wszPath, + STGM_CREATE | STGM_TRANSACTED | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, + 0, &pStorage); + if(FAILED(hr)) + { + return hr; + } + + // Next, create a stream to store. + IStream *pStream; + hr = pStorage->CreateStream( + wszStreamName, + STGM_WRITE | STGM_CREATE | STGM_SHARE_EXCLUSIVE, + 0, 0, &pStream); + if (FAILED(hr)) + { + pStorage->Release(); + return hr; + } + + // The IPersistStream converts a stream into a persistent object. + IPersistStream *pPersist = NULL; + pGraph->QueryInterface(IID_IPersistStream, reinterpret_cast(&pPersist)); + hr = pPersist->Save(pStream, TRUE); + pStream->Release(); + pPersist->Release(); + if (SUCCEEDED(hr)) + { + hr = pStorage->Commit(STGC_DEFAULT); + } + pStorage->Release(); + return hr; +} + + +// ---------------------------------------------------------------------- +// For changing the input types +// +// ---------------------------------------------------------------------- + +HRESULT videoInput::routeCrossbar(ICaptureGraphBuilder2 **ppBuild, IBaseFilter **pVidInFilter, int conType, GUID captureMode){ + + //create local ICaptureGraphBuilder2 + ICaptureGraphBuilder2 *pBuild = NULL; + pBuild = *ppBuild; + + //create local IBaseFilter + IBaseFilter *pVidFilter = NULL; + pVidFilter = * pVidInFilter; + + // Search upstream for a crossbar. + IAMCrossbar *pXBar1 = NULL; + HRESULT hr = pBuild->FindInterface(&LOOK_UPSTREAM_ONLY, NULL, pVidFilter, + IID_IAMCrossbar, (void**)&pXBar1); + if (SUCCEEDED(hr)) + { + + bool foundDevice = false; + + if(verbose)printf("SETUP: You are not a webcam! Setting Crossbar\n"); + pXBar1->Release(); + + IAMCrossbar *Crossbar; + hr = pBuild->FindInterface(&captureMode, &MEDIATYPE_Interleaved, pVidFilter, IID_IAMCrossbar, (void **)&Crossbar); + + if(hr != NOERROR){ + hr = pBuild->FindInterface(&captureMode, &MEDIATYPE_Video, pVidFilter, IID_IAMCrossbar, (void **)&Crossbar); + } + + LONG lInpin, lOutpin; + hr = Crossbar->get_PinCounts(&lOutpin , &lInpin); + + BOOL IPin=TRUE; LONG pIndex=0 , pRIndex=0 , pType=0; + + while( pIndex < lInpin) + { + hr = Crossbar->get_CrossbarPinInfo( IPin , pIndex , &pRIndex , &pType); + + if( pType == conType){ + if(verbose)printf("SETUP: Found Physical Interface"); + + switch(conType){ + + case PhysConn_Video_Composite: + if(verbose)printf(" - Composite\n"); + break; + case PhysConn_Video_SVideo: + if(verbose)printf(" - S-Video\n"); + break; + case PhysConn_Video_Tuner: + if(verbose)printf(" - Tuner\n"); + break; + case PhysConn_Video_USB: + if(verbose)printf(" - USB\n"); + break; + case PhysConn_Video_1394: + if(verbose)printf(" - Firewire\n"); + break; + } + + foundDevice = true; + break; + } + pIndex++; + + } + + if(foundDevice){ + BOOL OPin=FALSE; LONG pOIndex=0 , pORIndex=0 , pOType=0; + while( pOIndex < lOutpin) + { + hr = Crossbar->get_CrossbarPinInfo( OPin , pOIndex , &pORIndex , &pOType); + if( pOType == PhysConn_Video_VideoDecoder) + break; + } + Crossbar->Route(pOIndex,pIndex); + }else{ + if(verbose)printf("SETUP: Didn't find specified Physical Connection type. Using Defualt. \n"); + } + + //we only free the crossbar when we close or restart the device + //we were getting a crash otherwise + //if(Crossbar)Crossbar->Release(); + //if(Crossbar)Crossbar = NULL; + + if(pXBar1)pXBar1->Release(); + if(pXBar1)pXBar1 = NULL; + + }else{ + if(verbose)printf("SETUP: You are a webcam or snazzy firewire cam! No Crossbar needed\n"); + return hr; + } + + return hr; +} + diff --git a/ThirdParty/videoInput/videoInput.h b/ThirdParty/videoInput/videoInput.h new file mode 100644 index 0000000..0284141 --- /dev/null +++ b/ThirdParty/videoInput/videoInput.h @@ -0,0 +1,385 @@ +#ifndef _VIDEOINPUT +#define _VIDEOINPUT + +//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +//THE SOFTWARE. + +////////////////////////////////////////////////////////// +//Written by Theodore Watson - theo.watson@gmail.com // +//Do whatever you want with this code but if you find // +//a bug or make an improvement I would love to know! // +// // +//Warning This code is experimental // +//use at your own risk :) // +////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////// +/* Shoutouts + +Thanks to: + + Dillip Kumar Kara for crossbar code. + Zachary Lieberman for getting me into this stuff + and for being so generous with time and code. + The guys at Potion Design for helping me with VC++ + Josh Fisher for being a serious C++ nerd :) + Golan Levin for helping me debug the strangest + and slowest bug in the world! + + And all the people using this library who send in + bugs, suggestions and improvements who keep me working on + the next version - yeah thanks a lot ;) + +*/ +///////////////////////////////////////////////////////// + + + +#include +#include +#include +#include +#include + +//this is for TryEnterCriticalSection +#ifndef _WIN32_WINNT + # define _WIN32_WINNT 0x400 +#endif +#include + + +//Example Usage +/* + //create a videoInput object + videoInput VI; + + //Prints out a list of available devices and returns num of devices found + int numDevices = VI.listDevices(); + + int device1 = 0; //this could be any deviceID that shows up in listDevices + int device2 = 1; //this could be any deviceID that shows up in listDevices + + //if you want to capture at a different frame rate (default is 30) + //specify it here, you are not guaranteed to get this fps though. + //VI.setIdealFramerate(dev, 60); + + //setup the first device - there are a number of options: + + VI.setupDevice(device1); //setup the first device with the default settings + //VI.setupDevice(device1, VI_COMPOSITE); //or setup device with specific connection type + //VI.setupDevice(device1, 320, 240); //or setup device with specified video size + //VI.setupDevice(device1, 320, 240, VI_COMPOSITE); //or setup device with video size and connection type + + //VI.setFormat(device1, VI_NTSC_M); //if your card doesn't remember what format it should be + //call this with the appropriate format listed above + //NOTE: must be called after setupDevice! + + //optionally setup a second (or third, fourth ...) device - same options as above + VI.setupDevice(device2); + + //As requested width and height can not always be accomodated + //make sure to check the size once the device is setup + + int width = VI.getWidth(device1); + int height = VI.getHeight(device1); + int size = VI.getSize(device1); + + unsigned char * yourBuffer1 = new unsigned char[size]; + unsigned char * yourBuffer2 = new unsigned char[size]; + + //to get the data from the device first check if the data is new + if(VI.isFrameNew(device1)){ + VI.getPixels(device1, yourBuffer1, false, false); //fills pixels as a BGR (for openCV) unsigned char array - no flipping + VI.getPixels(device1, yourBuffer2, true, true); //fills pixels as a RGB (for openGL) unsigned char array - flipping! + } + + //same applies to device2 etc + + //to get a settings dialog for the device + VI.showSettingsWindow(device1); + + + //Shut down devices properly + VI.stopDevice(device1); + VI.stopDevice(device2); +*/ + + +////////////////////////////////////// VARS AND DEFS ////////////////////////////////// + + +//STUFF YOU CAN CHANGE + +//change for verbose debug info +static bool verbose = true; + +//if you need VI to use multi threaded com +//#define VI_COM_MULTI_THREADED + +//STUFF YOU DON'T CHANGE + +//videoInput defines +#define VI_VERSION 0.1995 +#define VI_MAX_CAMERAS 20 +#define VI_NUM_TYPES 18 //DON'T TOUCH +#define VI_NUM_FORMATS 18 //DON'T TOUCH + +//defines for setPhyCon - tuner is not as well supported as composite and s-video +#define VI_COMPOSITE 0 +#define VI_S_VIDEO 1 +#define VI_TUNER 2 +#define VI_USB 3 +#define VI_1394 4 + +//defines for formats +#define VI_NTSC_M 0 +#define VI_PAL_B 1 +#define VI_PAL_D 2 +#define VI_PAL_G 3 +#define VI_PAL_H 4 +#define VI_PAL_I 5 +#define VI_PAL_M 6 +#define VI_PAL_N 7 +#define VI_PAL_NC 8 +#define VI_SECAM_B 9 +#define VI_SECAM_D 10 +#define VI_SECAM_G 11 +#define VI_SECAM_H 12 +#define VI_SECAM_K 13 +#define VI_SECAM_K1 14 +#define VI_SECAM_L 15 +#define VI_NTSC_M_J 16 +#define VI_NTSC_433 17 + + +//allows us to directShow classes here with the includes in the cpp +struct ICaptureGraphBuilder2; +struct IGraphBuilder; +struct IBaseFilter; +struct IAMCrossbar; +struct IMediaControl; +struct ISampleGrabber; +struct IMediaEventEx; +struct IAMStreamConfig; +struct _AMMediaType; +class SampleGrabberCallback; +typedef _AMMediaType AM_MEDIA_TYPE; + +//keeps track of how many instances of VI are being used +//don't touch +static int comInitCount = 0; + + +//////////////////////////////////////// VIDEO DEVICE /////////////////////////////////// + +class videoDevice{ + + + public: + + videoDevice(); + void setSize(int w, int h); + void NukeDownstream(IBaseFilter *pBF); + void destroyGraph(); + ~videoDevice(); + + int videoSize; + int width; + int height; + int tryWidth; + int tryHeight; + + ICaptureGraphBuilder2 *pCaptureGraph; // Capture graph builder object + IGraphBuilder *pGraph; // Graph builder object + IMediaControl *pControl; // Media control object + IBaseFilter *pVideoInputFilter; // Video Capture filter + IBaseFilter *pGrabberF; + IBaseFilter * pDestFilter; + IAMStreamConfig *streamConf; + ISampleGrabber * pGrabber; // Grabs frame + AM_MEDIA_TYPE * pAmMediaType; + + IMediaEventEx * pMediaEvent; + + GUID videoType; + long formatType; + + SampleGrabberCallback * sgCallback; + + bool tryDiffSize; + bool useCrossbar; + bool readyToCapture; + bool sizeSet; + bool setupStarted; + bool specificFormat; + bool autoReconnect; + int nFramesForReconnect; + unsigned long nFramesRunning; + int connection; + int storeConn; + int myID; + long requestedFrameTime; //ie fps + + char nDeviceName[255]; + WCHAR wDeviceName[255]; + + unsigned char * pixels; + char * pBuffer; + +}; + + + + +////////////////////////////////////// VIDEO INPUT ///////////////////////////////////// + + + +class videoInput{ + + public: + videoInput(); + ~videoInput(); + + //turns off console messages - default is to print messages + static void setVerbose(bool _verbose); + + //Functions in rough order they should be used. + static int listDevices(bool silent = false); + + //needs to be called after listDevices - otherwise returns NULL + static char * getDeviceName(int deviceID); + + //choose to use callback based capture - or single threaded + void setUseCallback(bool useCallback); + + //call before setupDevice + //directshow will try and get the closest possible framerate to what is requested + void setIdealFramerate(int deviceID, int idealFramerate); + + //some devices will stop delivering frames after a while - this method gives you the option to try and reconnect + //to a device if videoInput detects that a device has stopped delivering frames. + //you MUST CALL isFrameNew every app loop for this to have any effect + void setAutoReconnectOnFreeze(int deviceNumber, bool doReconnect, int numMissedFramesBeforeReconnect); + + //Choose one of these four to setup your device + bool setupDevice(int deviceID); + bool setupDevice(int deviceID, int w, int h); + + //These two are only for capture cards + //USB and Firewire cameras souldn't specify connection + bool setupDevice(int deviceID, int connection); + bool setupDevice(int deviceID, int w, int h, int connection); + + //If you need to you can set your NTSC/PAL/SECAM + //preference here. if it is available it will be used. + //see #defines above for available formats - eg VI_NTSC_M or VI_PAL_B + //should be called after setupDevice + //can be called multiple times + bool setFormat(int deviceNumber, int format); + + //Tells you when a new frame has arrived - you should call this if you have specified setAutoReconnectOnFreeze to true + bool isFrameNew(int deviceID); + + bool isDeviceSetup(int deviceID); + + //Returns the pixels - flipRedAndBlue toggles RGB/BGR flipping - and you can flip the image too + unsigned char * getPixels(int deviceID, bool flipRedAndBlue = true, bool flipImage = false); + + //Or pass in a buffer for getPixels to fill returns true if successful. + bool getPixels(int id, unsigned char * pixels, bool flipRedAndBlue = true, bool flipImage = false); + + //Launches a pop up settings window + //For some reason in GLUT you have to call it twice each time. + void showSettingsWindow(int deviceID); + + //Manual control over settings thanks..... + //These are experimental for now. + bool setVideoSettingFilter(int deviceID, long Property, long lValue, long Flags = NULL, bool useDefaultValue = false); + bool setVideoSettingFilterPct(int deviceID, long Property, float pctValue, long Flags = NULL); + bool getVideoSettingFilter(int deviceID, long Property, long &min, long &max, long &SteppingDelta, long ¤tValue, long &flags, long &defaultValue); + + bool setVideoSettingCamera(int deviceID, long Property, long lValue, long Flags = NULL, bool useDefaultValue = false); + bool setVideoSettingCameraPct(int deviceID, long Property, float pctValue, long Flags = NULL); + bool getVideoSettingCamera(int deviceID, long Property, long &min, long &max, long &SteppingDelta, long ¤tValue, long &flags, long &defaultValue); + + //bool setVideoSettingCam(int deviceID, long Property, long lValue, long Flags = NULL, bool useDefaultValue = false); + + //get width, height and number of pixels + int getWidth(int deviceID); + int getHeight(int deviceID); + int getSize(int deviceID); + + //completely stops and frees a device + void stopDevice(int deviceID); + + //as above but then sets it up with same settings + bool restartDevice(int deviceID); + + //number of devices available + int devicesFound; + + long propBrightness; + long propContrast; + long propHue; + long propSaturation; + long propSharpness; + long propGamma; + long propColorEnable; + long propWhiteBalance; + long propBacklightCompensation; + long propGain; + + long propPan; + long propTilt; + long propRoll; + long propZoom; + long propExposure; + long propIris; + long propFocus; + + + private: + void setPhyCon(int deviceID, int conn); + void setAttemptCaptureSize(int deviceID, int w, int h); + bool setup(int deviceID); + void processPixels(unsigned char * src, unsigned char * dst, int width, int height, bool bRGB, bool bFlip); + int start(int deviceID, videoDevice * VD); + int getDeviceCount(); + void getMediaSubtypeAsString(GUID type, char * typeAsString); + + HRESULT getDevice(IBaseFilter **pSrcFilter, int deviceID, WCHAR * wDeviceName, char * nDeviceName); + static HRESULT ShowFilterPropertyPages(IBaseFilter *pFilter); + HRESULT SaveGraphFile(IGraphBuilder *pGraph, WCHAR *wszPath); + HRESULT routeCrossbar(ICaptureGraphBuilder2 **ppBuild, IBaseFilter **pVidInFilter, int conType, GUID captureMode); + + //don't touch + static bool comInit(); + static bool comUnInit(); + + int connection; + int callbackSetCount; + bool bCallback; + + GUID CAPTURE_MODE; + + //Extra video subtypes + GUID MEDIASUBTYPE_Y800; + GUID MEDIASUBTYPE_Y8; + GUID MEDIASUBTYPE_GREY; + + videoDevice * VDList[VI_MAX_CAMERAS]; + GUID mediaSubtypes[VI_NUM_TYPES]; + long formatTypes[VI_NUM_FORMATS]; + + static void __cdecl basicThread(void * objPtr); + + static char deviceNames[VI_MAX_CAMERAS][255]; + +}; + + #endif diff --git a/ThirdParty/websocket/include/base64.h b/ThirdParty/websocket/include/base64.h new file mode 100644 index 0000000..65d5db8 --- /dev/null +++ b/ThirdParty/websocket/include/base64.h @@ -0,0 +1,4 @@ +#include + +std::string base64_encode(unsigned char const* , unsigned int len); +std::string base64_decode(std::string const& s); diff --git a/ThirdParty/websocket/include/connection.h b/ThirdParty/websocket/include/connection.h new file mode 100644 index 0000000..e37188e --- /dev/null +++ b/ThirdParty/websocket/include/connection.h @@ -0,0 +1,120 @@ +/** + * + * filename: connection.h + * summary: websocket connection + * author: caosiyang + * email: csy3228@gmail.com + * + */ +#ifndef CONNECTION_H +#define CONNECTION_H + +#include "websocket.h" +extern "C" { + #include + #include +} +#include +using namespace std; + + +typedef void(*websocket_cb)(void*); + + +typedef struct { + websocket_cb cb; + void *cbarg; +} ws_cb_unit; + + +typedef struct websocket_connection { + struct bufferevent *bev; + string ws_req_str; + string ws_resp_str; + enum Step step; + uint64_t ntoread; + frame_t *frame; //current frame + ws_cb_unit handshake_cb_unit; + ws_cb_unit frame_recv_cb_unit; + ws_cb_unit write_cb_unit; + ws_cb_unit close_cb_unit; + ws_cb_unit ping_cb_unit; +} ws_conn_t; + + +//callback type +enum CBTYPE { + HANDSHAKE, + FRAME_RECV, + WRITE, + CLOSE, + PING +}; + + +// +// following functions are for library-users +// +//create a websocket connection +ws_conn_t *ws_conn_new(); + + +//destroy a websocket connection +void ws_conn_free(ws_conn_t *conn); + + +//set callback +//MUST set: frame_read_cb, write_cb, close_cb +void ws_conn_setcb(ws_conn_t *conn, enum CBTYPE cbtype, websocket_cb cb, void *cbarg); + + +//websocket serve start +void ws_serve_start(ws_conn_t *conn); + + +//websocket serve exit +void ws_serve_exit(ws_conn_t *conn); + + +//send a frame +int32_t send_a_frame(ws_conn_t *conn, const frame_buffer_t *fb); + + + + +// +// following functions are for internal +// +//accept the websocket request +void accept_websocket_request(ws_conn_t *conn); + + +//respond the websocket request +void respond_websocket_request(ws_conn_t *conn); + + +//receive a frame +void frame_recv_loop(ws_conn_t *conn); + + +//request read callback +void request_read_cb(struct bufferevent *bev, void *ctx); + + +//response write callback +void response_write_cb(struct bufferevent *bev, void *ctx); + + +//frame read callback +void frame_read_cb(struct bufferevent *bev, void *ctx); + + +//websocket write callback +void write_cb(struct bufferevent *bev, void *ctx); + + +//connection close callback +void close_cb(struct bufferevent *bev, short what, void *ctx); + + +#endif diff --git a/ThirdParty/websocket/include/frame.h b/ThirdParty/websocket/include/frame.h new file mode 100644 index 0000000..85dfb64 --- /dev/null +++ b/ThirdParty/websocket/include/frame.h @@ -0,0 +1,68 @@ +/** + * + * filename: frame.h + * summary: + * author: caosiyang + * email: csy3228@gmail.com + * + */ +#ifndef FRAME_H +#define FRAME_H + +#include "tools.h" +#include +using namespace std; + + +//frame buffer +typedef struct FrameBuffer { + char *data; + uint64_t len; +} frame_buffer_t; + + +//frame +typedef struct Frame { + uint8_t fin; + uint8_t opcode; + uint8_t mask; + uint64_t payload_len; + unsigned char masking_key[4]; + char *payload_data; +} frame_t; + + +frame_t *frame_new(); + + +void frame_free(frame_t *frame); + + +bool is_frame_valid(const frame_t *frame); + + +#if 0 +int32_t frame_set(frame_t *frame, + uint8_t fin, + uint8_t opcode, + uint64_t payload_len, + const char *payload_data); +#endif + + +frame_buffer_t *frame_buffer_new(uint8_t fin, + uint8_t opcode, + uint64_t payload_len, + const char *payload_data); + + +frame_buffer_t *frame_buffer_new(const frame_t *frame); + + +void frame_buffer_free(frame_buffer_t *fb); + + +void print_frame_info(const frame_buffer_t *fb); + + +#endif diff --git a/ThirdParty/websocket/include/sha1.h b/ThirdParty/websocket/include/sha1.h new file mode 100644 index 0000000..834622a --- /dev/null +++ b/ThirdParty/websocket/include/sha1.h @@ -0,0 +1,53 @@ +/* sha1.h + +Copyright (c) 2005 Michael D. Leonhard + +http://tamale.net/ + +Copyright (c) 2005 Michael D. Leonhard + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#ifndef SHA1_HEADER +typedef unsigned int Uint32; + +class SHA1 +{ + private: + // fields + Uint32 H0, H1, H2, H3, H4; + unsigned char bytes[64]; + int unprocessedBytes; + Uint32 size; + void process(); + public: + SHA1(); + ~SHA1(); + void addBytes( const char* data, int num ); + unsigned char* getDigest(); + // utility methods + static Uint32 lrot( Uint32 x, int bits ); + static void storeBigEndianUint32( unsigned char* byte, Uint32 num ); + static void hexPrinter( unsigned char* c, int l ); +}; + +#define SHA1_HEADER +#endif + diff --git a/ThirdParty/websocket/include/stdint.h b/ThirdParty/websocket/include/stdint.h new file mode 100644 index 0000000..e032ff1 --- /dev/null +++ b/ThirdParty/websocket/include/stdint.h @@ -0,0 +1,232 @@ +// ISO C9x compliant stdint.h for Microsoft Visual Studio +// Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG14/N1124 +// +// Copyright (c) 2006-2008 Alexander Chemeris +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// 3. The name of the author may be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED +// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +/////////////////////////////////////////////////////////////////////////////// + +#ifndef _MSC_VER // [ +#error "Use this header only with Microsoft Visual C++ compilers!" +#endif // _MSC_VER ] + +#ifndef _MSC_STDINT_H_ // [ +#define _MSC_STDINT_H_ + +#if _MSC_VER > 1000 +#pragma once +#endif + +#include + +// For Visual Studio 6 in C++ mode wrap include with 'extern "C++" {}' +// or compiler give many errors like this: +// error C2733: second C linkage of overloaded function 'wmemchr' not allowed +#if (_MSC_VER < 1300) && defined(__cplusplus) + extern "C++" { +#endif +# include +#if (_MSC_VER < 1300) && defined(__cplusplus) + } +#endif + +// Define _W64 macros to mark types changing their size, like intptr_t. +#ifndef _W64 +# if !defined(__midl) && (defined(_X86_) || defined(_M_IX86)) && _MSC_VER >= 1300 +# define _W64 __w64 +# else +# define _W64 +# endif +#endif + + +// 7.18.1 Integer types + +// 7.18.1.1 Exact-width integer types +typedef __int8 int8_t; +typedef __int16 int16_t; +typedef __int32 int32_t; +typedef __int64 int64_t; +typedef unsigned __int8 uint8_t; +typedef unsigned __int16 uint16_t; +typedef unsigned __int32 uint32_t; +typedef unsigned __int64 uint64_t; + +// 7.18.1.2 Minimum-width integer types +typedef int8_t int_least8_t; +typedef int16_t int_least16_t; +typedef int32_t int_least32_t; +typedef int64_t int_least64_t; +typedef uint8_t uint_least8_t; +typedef uint16_t uint_least16_t; +typedef uint32_t uint_least32_t; +typedef uint64_t uint_least64_t; + +// 7.18.1.3 Fastest minimum-width integer types +typedef int8_t int_fast8_t; +typedef int16_t int_fast16_t; +typedef int32_t int_fast32_t; +typedef int64_t int_fast64_t; +typedef uint8_t uint_fast8_t; +typedef uint16_t uint_fast16_t; +typedef uint32_t uint_fast32_t; +typedef uint64_t uint_fast64_t; + +// 7.18.1.4 Integer types capable of holding object pointers +#ifdef _WIN64 // [ + typedef __int64 intptr_t; + typedef unsigned __int64 uintptr_t; +#else // _WIN64 ][ + typedef _W64 int intptr_t; + typedef _W64 unsigned int uintptr_t; +#endif // _WIN64 ] + +// 7.18.1.5 Greatest-width integer types +typedef int64_t intmax_t; +typedef uint64_t uintmax_t; + + +// 7.18.2 Limits of specified-width integer types + +#if !defined(__cplusplus) || defined(__STDC_LIMIT_MACROS) // [ See footnote 220 at page 257 and footnote 221 at page 259 + +// 7.18.2.1 Limits of exact-width integer types +#define INT8_MIN ((int8_t)_I8_MIN) +#define INT8_MAX _I8_MAX +#define INT16_MIN ((int16_t)_I16_MIN) +#define INT16_MAX _I16_MAX +#define INT32_MIN ((int32_t)_I32_MIN) +#define INT32_MAX _I32_MAX +#define INT64_MIN ((int64_t)_I64_MIN) +#define INT64_MAX _I64_MAX +#define UINT8_MAX _UI8_MAX +#define UINT16_MAX _UI16_MAX +#define UINT32_MAX _UI32_MAX +#define UINT64_MAX _UI64_MAX + +// 7.18.2.2 Limits of minimum-width integer types +#define INT_LEAST8_MIN INT8_MIN +#define INT_LEAST8_MAX INT8_MAX +#define INT_LEAST16_MIN INT16_MIN +#define INT_LEAST16_MAX INT16_MAX +#define INT_LEAST32_MIN INT32_MIN +#define INT_LEAST32_MAX INT32_MAX +#define INT_LEAST64_MIN INT64_MIN +#define INT_LEAST64_MAX INT64_MAX +#define UINT_LEAST8_MAX UINT8_MAX +#define UINT_LEAST16_MAX UINT16_MAX +#define UINT_LEAST32_MAX UINT32_MAX +#define UINT_LEAST64_MAX UINT64_MAX + +// 7.18.2.3 Limits of fastest minimum-width integer types +#define INT_FAST8_MIN INT8_MIN +#define INT_FAST8_MAX INT8_MAX +#define INT_FAST16_MIN INT16_MIN +#define INT_FAST16_MAX INT16_MAX +#define INT_FAST32_MIN INT32_MIN +#define INT_FAST32_MAX INT32_MAX +#define INT_FAST64_MIN INT64_MIN +#define INT_FAST64_MAX INT64_MAX +#define UINT_FAST8_MAX UINT8_MAX +#define UINT_FAST16_MAX UINT16_MAX +#define UINT_FAST32_MAX UINT32_MAX +#define UINT_FAST64_MAX UINT64_MAX + +// 7.18.2.4 Limits of integer types capable of holding object pointers +#ifdef _WIN64 // [ +# define INTPTR_MIN INT64_MIN +# define INTPTR_MAX INT64_MAX +# define UINTPTR_MAX UINT64_MAX +#else // _WIN64 ][ +# define INTPTR_MIN INT32_MIN +# define INTPTR_MAX INT32_MAX +# define UINTPTR_MAX UINT32_MAX +#endif // _WIN64 ] + +// 7.18.2.5 Limits of greatest-width integer types +#define INTMAX_MIN INT64_MIN +#define INTMAX_MAX INT64_MAX +#define UINTMAX_MAX UINT64_MAX + +// 7.18.3 Limits of other integer types + +#ifdef _WIN64 // [ +# define PTRDIFF_MIN _I64_MIN +# define PTRDIFF_MAX _I64_MAX +#else // _WIN64 ][ +# define PTRDIFF_MIN _I32_MIN +# define PTRDIFF_MAX _I32_MAX +#endif // _WIN64 ] + +#define SIG_ATOMIC_MIN INT_MIN +#define SIG_ATOMIC_MAX INT_MAX + +#ifndef SIZE_MAX // [ +# ifdef _WIN64 // [ +# define SIZE_MAX _UI64_MAX +# else // _WIN64 ][ +# define SIZE_MAX _UI32_MAX +# endif // _WIN64 ] +#endif // SIZE_MAX ] + +// WCHAR_MIN and WCHAR_MAX are also defined in +#ifndef WCHAR_MIN // [ +# define WCHAR_MIN 0 +#endif // WCHAR_MIN ] +#ifndef WCHAR_MAX // [ +# define WCHAR_MAX _UI16_MAX +#endif // WCHAR_MAX ] + +#define WINT_MIN 0 +#define WINT_MAX _UI16_MAX + +#endif // __STDC_LIMIT_MACROS ] + + +// 7.18.4 Limits of other integer types + +#if !defined(__cplusplus) || defined(__STDC_CONSTANT_MACROS) // [ See footnote 224 at page 260 + +// 7.18.4.1 Macros for minimum-width integer constants + +#define INT8_C(val) val##i8 +#define INT16_C(val) val##i16 +#define INT32_C(val) val##i32 +#define INT64_C(val) val##i64 + +#define UINT8_C(val) val##ui8 +#define UINT16_C(val) val##ui16 +#define UINT32_C(val) val##ui32 +#define UINT64_C(val) val##ui64 + +// 7.18.4.2 Macros for greatest-width integer constants +#define INTMAX_C INT64_C +#define UINTMAX_C UINT64_C + +#endif // __STDC_CONSTANT_MACROS ] + + +#endif // _MSC_STDINT_H_ ] diff --git a/ThirdParty/websocket/include/tools.h b/ThirdParty/websocket/include/tools.h new file mode 100644 index 0000000..442110d --- /dev/null +++ b/ThirdParty/websocket/include/tools.h @@ -0,0 +1,51 @@ +/** + * + * filename: tools.h + * summary: + * author: caosiyang + * email: csy3228@gmail.com + * + */ +#ifndef TOOLS_H +#define TOOLS_H + +#include "stdint.h" + +#include +using namespace std; + + +#define LOG(format, args) fprintf(stdout, format"\n", ##args) + + +inline uint16_t myhtons(uint16_t n) { + return ((n & 0xff00) >> 8) | ((n & 0x00ff) << 8); +} + + +inline uint16_t myntohs(uint16_t n) { + return ((n & 0xff00) >> 8) | ((n & 0x00ff) << 8); +} + + +inline uint32_t myhtonl(uint32_t n) { + return ((n & 0xff000000) >> 24) | ((n & 0x00ff0000) >> 8) | ((n & 0x0000ff00) << 8) | ((n & 0x000000ff) << 24); +} + + +inline uint32_t myntohl(uint32_t n) { + return ((n & 0xff000000) >> 24) | ((n & 0x00ff0000) >> 8) | ((n & 0x0000ff00) << 8) | ((n & 0x000000ff) << 24); +} + + +inline uint64_t myhtonll(uint64_t n) { + return (uint64_t)myhtonl(n >> 32) | ((uint64_t)myhtonl(n) << 32); +} + + +inline uint64_t myntohll(uint64_t n) { + return (uint64_t)myhtonl(n >> 32) | ((uint64_t)myhtonl(n) << 32); +} + + +#endif diff --git a/ThirdParty/websocket/include/websocket.h b/ThirdParty/websocket/include/websocket.h new file mode 100644 index 0000000..203a949 --- /dev/null +++ b/ThirdParty/websocket/include/websocket.h @@ -0,0 +1,85 @@ +/** + * + * filename: websocket.h + * summary: + * author: caosiyang + * email: csy3228@gmail.com + * + */ +#ifndef WEBSOCKET_H +#define WEBSOCKET_H + +#include "tools.h" +#include "frame.h" +#include "sha1.h" +#include "base64.h" +#include +#include +#include +using namespace std; + + +//WebSocket request +typedef struct WebsocketRequest { + string req; + string connection; + string upgrade; + string host; + string origin; + string cookie; + string sec_websocket_key; + string sec_websocket_version; +} ws_req_t; + + +//WebSocket response +typedef struct WebsocketResponse { + string resp; + string date; + string connection; + string server; + string upgrade; + string access_control_allow_origin; + string access_control_allow_credentials; + string sec_websocket_accept; + string access_control_allow_headers; +} ws_resp_t; + + +//steps of receiving a frame +enum Step { + ZERO, //before websocket handshake + ONE, //0-2 bytes, fin, opcode, mask, payload length + TWO, //extended payload length + THREE, //masking-key + FOUR, //payload data + UNKNOWN +}; + + +//parse websocket request +int32_t parse_websocket_request(const char *src, ws_req_t *ws_req); + + +//print websocket request +void print_websocket_request(const ws_req_t *ws_req); + + +//generate websocket response +string generate_websocket_response(const ws_req_t *ws_req); + + +//generate websocket key +string generate_key(const string &key); + + +//parse fin, opcode, mask, payload len +int32_t parse_frame_header(const char *buf, frame_t *frame); + + +//unmask payload-data +int32_t unmask_payload_data(frame_t *frame); +int32_t unmask_payload_data(const char *masking_key, char *payload_data, uint32_t payload_len); + + +#endif diff --git a/ThirdParty/websocket/src/base64.cpp b/ThirdParty/websocket/src/base64.cpp new file mode 100644 index 0000000..aa25878 --- /dev/null +++ b/ThirdParty/websocket/src/base64.cpp @@ -0,0 +1,123 @@ +/* + base64.cpp and base64.h + + Copyright (C) 2004-2008 René Nyffenegger + + This source code is provided 'as-is', without any express or implied + warranty. In no event will the author be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this source code must not be misrepresented; you must not + claim that you wrote the original source code. If you use this source code + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original source code. + + 3. This notice may not be removed or altered from any source distribution. + + René Nyffenegger rene.nyffenegger@adp-gmbh.ch + +*/ + +#include "base64.h" +#include + +static const std::string base64_chars = +"ABCDEFGHIJKLMNOPQRSTUVWXYZ" +"abcdefghijklmnopqrstuvwxyz" +"0123456789+/"; + + +static inline bool is_base64(unsigned char c) { + return (isalnum(c) || (c == '+') || (c == '/')); +} + +std::string base64_encode(unsigned char const* bytes_to_encode, unsigned int in_len) { + std::string ret; + int i = 0; + int j = 0; + unsigned char char_array_3[3]; + unsigned char char_array_4[4]; + + while (in_len--) { + char_array_3[i++] = *(bytes_to_encode++); + if (i == 3) { + char_array_4[0] = (char_array_3[0] & 0xfc) >> 2; + char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4); + char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6); + char_array_4[3] = char_array_3[2] & 0x3f; + + for(i = 0; (i <4) ; i++) + ret += base64_chars[char_array_4[i]]; + i = 0; + } + } + + if (i) + { + for(j = i; j < 3; j++) + char_array_3[j] = '\0'; + + char_array_4[0] = (char_array_3[0] & 0xfc) >> 2; + char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4); + char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6); + char_array_4[3] = char_array_3[2] & 0x3f; + + for (j = 0; (j < i + 1); j++) + ret += base64_chars[char_array_4[j]]; + + while((i++ < 3)) + ret += '='; + + } + + return ret; + +} + +std::string base64_decode(std::string const& encoded_string) { + int in_len = encoded_string.size(); + int i = 0; + int j = 0; + int in_ = 0; + unsigned char char_array_4[4], char_array_3[3]; + std::string ret; + + while (in_len-- && ( encoded_string[in_] != '=') && is_base64(encoded_string[in_])) { + char_array_4[i++] = encoded_string[in_]; in_++; + if (i ==4) { + for (i = 0; i <4; i++) + char_array_4[i] = base64_chars.find(char_array_4[i]); + + 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[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3]; + + for (i = 0; (i < 3); i++) + ret += char_array_3[i]; + i = 0; + } + } + + if (i) { + for (j = i; j <4; j++) + char_array_4[j] = 0; + + for (j = 0; j <4; j++) + char_array_4[j] = base64_chars.find(char_array_4[j]); + + 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[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3]; + + for (j = 0; (j < i - 1); j++) ret += char_array_3[j]; + } + + return ret; +} diff --git a/ThirdParty/websocket/src/connection.cpp b/ThirdParty/websocket/src/connection.cpp new file mode 100644 index 0000000..7ff8722 --- /dev/null +++ b/ThirdParty/websocket/src/connection.cpp @@ -0,0 +1,391 @@ +#include "connection.h" + + +//create a websocket connection +ws_conn_t *ws_conn_new() { + ws_conn_t *conn = new (nothrow) ws_conn_t; + if (conn) { + conn->bev = NULL; + conn->ws_req_str = ""; + conn->ws_resp_str = ""; + conn->step = ZERO; + conn->ntoread = 0; + conn->frame = frame_new(); + conn->handshake_cb_unit.cb = NULL; + conn->handshake_cb_unit.cbarg = NULL; + conn->frame_recv_cb_unit.cb = NULL; + conn->frame_recv_cb_unit.cbarg = NULL; + conn->write_cb_unit.cb = NULL; + conn->write_cb_unit.cbarg = NULL; + conn->close_cb_unit.cb = NULL; + conn->close_cb_unit.cbarg = NULL; + conn->ping_cb_unit.cb = NULL; + conn->ping_cb_unit.cbarg = NULL; + } + return conn; +} + + +//destroy a websocket connection +void ws_conn_free(ws_conn_t *conn) { + if (conn) { + if (conn->frame) { + frame_free(conn->frame); + conn->frame = NULL; + } + delete conn; + } +} + + +//websocket serve start +void ws_serve_start(ws_conn_t *conn) { + if (conn && conn->bev) { + accept_websocket_request(conn); + } else { + ws_serve_exit(conn); + } +} + + +//websocket serve exit +void ws_serve_exit(ws_conn_t *conn) { + if (conn) { + if (conn->close_cb_unit.cb) { + websocket_cb cb = conn->close_cb_unit.cb; + void *cbarg = conn->close_cb_unit.cbarg; + cb(cbarg); + } + } +} + + +//accept the websocket request +void accept_websocket_request(ws_conn_t *conn) { + if (conn && conn->bev) { + //read websocket request + bufferevent_setcb(conn->bev, request_read_cb, response_write_cb, close_cb, conn); + bufferevent_setwatermark(conn->bev, EV_READ, 1, 1); + bufferevent_setwatermark(conn->bev, EV_WRITE, 0, 0); + bufferevent_enable(conn->bev, EV_READ); + } else { + ws_serve_exit(conn); + } +} + + +//respond the websocket request +void respond_websocket_request(ws_conn_t *conn) { + if (conn && conn->bev) { + ws_req_t ws_req; + parse_websocket_request(conn->ws_req_str.c_str(), &ws_req); //parse request + //TODO + //check if it is a websocket request + //if (!valid) { + // ws_serve_exit(conn); + // return; + //} + conn->ws_resp_str = generate_websocket_response(&ws_req); //generate response + if (!conn->ws_resp_str.empty()) { + bufferevent_write(conn->bev, conn->ws_resp_str.c_str(), conn->ws_resp_str.length()); + } else { + ws_serve_exit(conn); + } + } else { + ws_serve_exit(conn); + } +} + + +//request read callback +void request_read_cb(struct bufferevent *bev, void *ctx) { + ws_conn_t *conn = (ws_conn_t*)ctx; + if (conn && conn->bev) { + char c; + bufferevent_read(bev, &c, 1); + conn->ws_req_str += c; + size_t n = conn->ws_req_str.size(); + //TODO + //for security + //if (n > MAX_WS_REQ_LEN) { + // ws_serve_exit(); + //} + + //receive request completely + if (n >= 4 && conn->ws_req_str.substr(n - 4) == "\r\n\r\n") { + bufferevent_disable(conn->bev, EV_READ); //stop reading before a valid handshake + respond_websocket_request(conn); //send websocket response + } + } else { + ws_serve_exit(conn); + } +} + + +//response write callback +void response_write_cb(struct bufferevent *bev, void *ctx) { + ws_conn_t *conn = (ws_conn_t*)ctx; + if (conn && conn->bev) { + if (conn->handshake_cb_unit.cb) { + websocket_cb cb = conn->handshake_cb_unit.cb; + void *cbarg = conn->handshake_cb_unit.cbarg; + cb(cbarg); + } + LOG("%s", conn->ws_req_str.c_str()); + LOG("%s", conn->ws_resp_str.c_str()); + + frame_recv_loop(conn); //frame receive loop + } else { + ws_serve_exit(conn); + } +} + + +//send a frame +inline int32_t send_a_frame(ws_conn_t *conn, const frame_buffer_t *fb) { + return bufferevent_write(conn->bev, fb->data, fb->len) == fb->len ? 0 : -1; +} + + +void frame_recv_loop(ws_conn_t *conn) { + if (conn && conn->bev) { + conn->step = ONE; + conn->ntoread = 2; + bufferevent_setcb(conn->bev, frame_read_cb, write_cb, close_cb, conn); + bufferevent_setwatermark(conn->bev, EV_READ, conn->ntoread, conn->ntoread); + bufferevent_setwatermark(conn->bev, EV_WRITE, 0, 0); + bufferevent_enable(conn->bev, EV_READ); + } else { + ws_serve_exit(conn); + } +} + + +void frame_read_cb(struct bufferevent *bev, void *ctx) { + ws_conn_t *conn = (ws_conn_t*)ctx; + if (!conn || !conn->bev) { + ws_serve_exit(conn); + return; + } + + switch (conn->step) { + case ONE: + { + LOG("---- STEP 1 ----", ""); + char * tmp = new char[conn->ntoread]; + bufferevent_read(bev, tmp, conn->ntoread); + //parse header + if (parse_frame_header(tmp, conn->frame) == 0) { + LOG("FIN = %lu", conn->frame->fin); + LOG("OPCODE = %lu", conn->frame->opcode); + LOG("MASK = %lu", conn->frame->mask); + LOG("PAYLOAD_LEN = %lu", conn->frame->payload_len); + //payload_len is [0, 127] + if (conn->frame->payload_len <= 125) { + conn->step = THREE; + conn->ntoread = 4; + bufferevent_setwatermark(bev, EV_READ, conn->ntoread, conn->ntoread); + } else if (conn->frame->payload_len == 126) { + conn->step = TWO; + conn->ntoread = 2; + bufferevent_setwatermark(bev, EV_READ, conn->ntoread, conn->ntoread); + } else if (conn->frame->payload_len == 127) { + conn->step = TWO; + conn->ntoread = 8; + bufferevent_setwatermark(bev, EV_READ, conn->ntoread, conn->ntoread); + } + } + //TODO + //validate frame header + if (!is_frame_valid(conn->frame)) { + return; + } + delete[] tmp; + break; + } + + case TWO: + { + LOG("---- STEP 2 ----", ""); + char * tmp = new char[conn->ntoread]; + bufferevent_read(bev, tmp, conn->ntoread); + if (conn->frame->payload_len == 126) { + conn->frame->payload_len = ntohs(*(uint16_t*)tmp); + LOG("PAYLOAD_LEN = %lu", conn->frame->payload_len); + } else if (conn->frame->payload_len == 127) { + conn->frame->payload_len = myntohll(*(uint64_t*)tmp); + LOG("PAYLOAD_LEN = %llu", conn->frame->payload_len); + } + conn->step = THREE; + conn->ntoread = 4; + bufferevent_setwatermark(bev, EV_READ, conn->ntoread, conn->ntoread); + delete[] tmp; + break; + } + + case THREE: + { + LOG("---- STEP 3 ----", ""); + char * tmp = new char[conn->ntoread]; + bufferevent_read(bev, tmp, conn->ntoread); + memcpy(conn->frame->masking_key, tmp, conn->ntoread); + if (conn->frame->payload_len > 0) { + conn->step = FOUR; + conn->ntoread = conn->frame->payload_len; + bufferevent_setwatermark(bev, EV_READ, conn->ntoread, conn->ntoread); + } else if (conn->frame->payload_len == 0) { + /*recv a whole frame*/ + if (conn->frame->mask == 0) { + //recv an unmasked frame + } + if (conn->frame->fin == 1 && conn->frame->opcode == 0x8) { + //0x8 denotes a connection close + frame_buffer_t *fb = frame_buffer_new(1, 8, 0, NULL); + send_a_frame(conn, fb); + LOG("send a close frame", ""); + frame_buffer_free(fb); + +#if 0 + if (conn->conn_close_cb_unit.cb) { + websocket_cb cb = conn->conn_close_cb_unit.cb; + void *cbarg = conn->conn_close_cb_unit.cbarg; + cb(cbarg); + } else { + //bufferevent_disable(conn->bev, EV_READ | EV_WRITE); + } +#endif + break; + } else if (conn->frame->fin == 1 && conn->frame->opcode == 0x9) { + //0x9 denotes a ping + //TODO + //make a pong + } else { + //execute custom operation + if (conn->frame_recv_cb_unit.cb) { + websocket_cb cb = conn->frame_recv_cb_unit.cb; + void *cbarg = conn->frame_recv_cb_unit.cbarg; + cb(cbarg); + } + } + + conn->step = ONE; + conn->ntoread = 2; + bufferevent_setwatermark(bev, EV_READ, conn->ntoread, conn->ntoread); + } + delete[] tmp; + break; + } + + case FOUR: + { + LOG("---- STEP 4 ----", ""); + if (conn->frame->payload_len > 0) { + if (conn->frame->payload_data) { + delete[] conn->frame->payload_data; + conn->frame->payload_data = NULL; + } + conn->frame->payload_data = new char[conn->frame->payload_len]; + bufferevent_read(bev, conn->frame->payload_data, conn->frame->payload_len); + unmask_payload_data(conn->frame); + } + + + /*recv a whole frame*/ + if (conn->frame->fin == 1 && conn->frame->opcode == 0x8) { + //0x8 denotes a connection close + frame_buffer_t *fb = frame_buffer_new(1, 8, 0, NULL); + send_a_frame(conn, fb); + LOG("send a close frame", ""); + frame_buffer_free(fb); + +#if 0 + if (conn->conn_close_cb_unit.cb) { + websocket_cb cb = conn->conn_close_cb_unit.cb; + void *cbarg = conn->conn_close_cb_unit.cbarg; + cb(cbarg); + } else { + //bufferevent_disable(conn->bev, EV_READ | EV_WRITE); + } +#endif + break; + } else if (conn->frame->fin == 1 && conn->frame->opcode == 0x9) { + //0x9 denotes a ping + //TODO + //make a pong + } else { + //execute custom operation + if (conn->frame_recv_cb_unit.cb) { + websocket_cb cb = conn->frame_recv_cb_unit.cb; + void *cbarg = conn->frame_recv_cb_unit.cbarg; + cb(cbarg); + } + } + + + if (conn->frame->opcode == 0x1) { //0x1 denotes a text frame + } + if (conn->frame->opcode == 0x2) { //0x1 denotes a binary frame + } + + + conn->step = ONE; + conn->ntoread = 2; + bufferevent_setwatermark(bev, EV_READ, conn->ntoread, conn->ntoread); + break; + } + + default: + LOG("---- STEP UNKNOWN ----", ""); + LOG("exit", ""); + exit(-1); + break; + } + +} + + +void ws_conn_setcb(ws_conn_t *conn, enum CBTYPE cbtype, websocket_cb cb, void *cbarg) { + if (conn) { + switch (cbtype) { + case HANDSHAKE: + conn->handshake_cb_unit.cb = cb; + conn->handshake_cb_unit.cbarg = cbarg; + break; + case FRAME_RECV: + conn->frame_recv_cb_unit.cb = cb; + conn->frame_recv_cb_unit.cbarg = cbarg; + break; + case WRITE: + conn->write_cb_unit.cb = cb; + conn->write_cb_unit.cbarg = cbarg; + break; + case CLOSE: + conn->close_cb_unit.cb = cb; + conn->close_cb_unit.cbarg = cbarg; + break; + case PING: + conn->ping_cb_unit.cb = cb; + conn->ping_cb_unit.cbarg = cbarg; + break; + default: + break; + } + } +} + + +void write_cb(struct bufferevent *bev, void *ctx) { + ws_conn_t *conn = (ws_conn_t*)ctx; + if (conn) { + if (conn->write_cb_unit.cb) { + websocket_cb cb = conn->write_cb_unit.cb; + void *cbarg = conn->write_cb_unit.cbarg; + cb(cbarg); + } + } +} + + +void close_cb(struct bufferevent *bev, short what, void *ctx) { + ws_serve_exit((ws_conn_t*)ctx); +} diff --git a/ThirdParty/websocket/src/frame.cpp b/ThirdParty/websocket/src/frame.cpp new file mode 100644 index 0000000..e7aab88 --- /dev/null +++ b/ThirdParty/websocket/src/frame.cpp @@ -0,0 +1,211 @@ +#include "frame.h" + + +frame_t *frame_new() { + frame_t *frame = new (nothrow) frame_t; + if (frame) { + memset(frame, 0, sizeof(frame_t)); + } + return frame; +} + + +void frame_free(frame_t *frame) { + if (frame) { + if (frame->payload_data) { + delete[] frame->payload_data; + } + delete frame; + } +} + + +bool is_frame_valid(const frame_t *frame) { + if (frame && frame->fin <= 1 && frame->opcode <= 0xf && frame->mask == 1) { + return true; + } + return false; +} + + +#if 0 +int32_t frame_set(frame_t *frame, uint8_t fin, uint8_t opcode, uint64_t payload_len, const char *payload_data) { + if (!frame || fin > 1 || opcode > 0xf) { + return -1; + } + + frame->fin = fin; + frame->opcode = opcode; + frame->mask = 0; + if (payload_data && payload_len > 0) { + frame->payload_len = payload_len; + frame->payload_data = new char[payload_len]; + memcpy(frame->payload_data, payload_data, payload_len); + } else { + frame->payload_len = 0; + frame->payload_data = NULL; + } + return 0; +} +#endif + + +frame_buffer_t *frame_buffer_new(uint8_t fin, uint8_t opcode, uint64_t payload_len, const char *payload_data) { + if (fin > 1 || opcode > 0xf) { + return NULL; + } + + uint8_t mask = 0; //must not mask at server endpoint + char masking_key[4] = {0}; //no need at server endpoint + + char *p = NULL; //buffer + uint64_t len = 0; //buffer length + + unsigned char c1 = 0x00; + unsigned char c2 = 0x00; + c1 = c1 | (fin << 7); //set fin + c1 = c1 | opcode; //set opcode + c2 = c2 | (mask << 7); //set mask + + if (!payload_data || payload_len == 0) { + if (mask == 0) { + p = new char[2]; + *p = c1; + *(p + 1) = c2; + len = 2; + } else { + p = new char[2 + 4]; + *p = c1; + *(p + 1) = c2; + memcpy(p + 2, masking_key, 4); + len = 2 + 4; + } + } else if (payload_data && payload_len <= 125) { + if (mask == 0) { + p = new char[2 + payload_len]; + *p = c1; + *(p + 1) = c2 + payload_len; + memcpy(p + 2, payload_data, payload_len); + len = 2 + payload_len; + } else { + p = new char[2 + 4 + payload_len]; + *p = c1; + *(p + 1) = c2 + payload_len; + memcpy(p + 2, masking_key, 4); + memcpy(p + 6, payload_data, payload_len); + len = 2 + 4 + payload_len; + } + } else if (payload_data && payload_len >= 126 && payload_len <= 65535) { + if (mask == 0) { + p = new char[4 + payload_len]; + *p = c1; + *(p + 1) = c2 + 126; + uint16_t tmplen = myhtons((uint16_t)payload_len); + memcpy(p + 2, &tmplen, 2); + memcpy(p + 4, payload_data, payload_len); + len = 4 + payload_len; + } else { + p = new char[4 + 4 + payload_len]; + *p = c1; + *(p + 1) = c2 + 126; + uint16_t tmplen = myhtons((uint16_t)payload_len); + memcpy(p + 2, &tmplen, 2); + memcpy(p + 4, masking_key, 4); + memcpy(p + 8, payload_data, payload_len); + len = 4 + 4 + payload_len; + } + } else if (payload_data && payload_len >= 65536) { + if (mask == 0) { + p = new char[10 + payload_len]; + *p = c1; + *(p + 1) = c2 + 127; + uint64_t tmplen = myhtonll(payload_len); + memcpy(p + 2, &tmplen, 8); + memcpy(p + 10, payload_data, payload_len); + len = 10 + payload_len; + } else { + p = new char[10 + 4 + payload_len]; + *p = c1; + *(p + 1) = c2 + 127; + uint64_t tmplen = myhtonll(payload_len); + memcpy(p + 2, &tmplen, 8); + memcpy(p + 10, masking_key, 4); + memcpy(p + 14, payload_data, payload_len); + len = 10 + 4 + payload_len; + } + } + + frame_buffer_t *fb = NULL; + if (p && len > 0) { + fb = new (nothrow) frame_buffer_t; + if (fb) { + fb->data = p; + fb->len = len; + } + } + return fb; +} + + +frame_buffer_t *frame_buffer_new(const frame_t *frame) { + if (!frame || frame->fin > 1 || frame->opcode > 0xf || frame->mask > 1) { + return NULL; + } + frame_buffer_t *fb = frame_buffer_new(frame->fin, frame->opcode, frame->payload_len, frame->payload_data); + return fb; +} + + +void frame_buffer_free(frame_buffer_t *fb) { + if (fb) { + if (fb->data) { + delete[] fb->data; + } + delete fb; + } +} + + +void print_frame_info(const frame_buffer_t *fb) { + if (!fb || !fb->data) { + return; + } + LOG("--------------------", ""); + char *p = fb->data; + uint8_t fin = 0, opcode = 0, mask = 0; + uint64_t payload_len = 0; + fin = (*(uint8_t*)p) >> 7; + opcode = (*(uint8_t*)p) & 0x0f; + mask = (*(uint8_t*)(p + 1)) >> 7; + payload_len = (*(uint8_t*)(p + 1)) & 0x7f; + char *tmp = NULL; + if (payload_len == 0) { + } else if (payload_len <= 125) { + tmp = new char[payload_len + 1]; + tmp[payload_len] = 0; + memcpy(tmp, p + 2, payload_len); + } else if (payload_len == 126) { + LOG("126", ""); + payload_len = myntohs(*(uint16_t*)(p + 2)); + tmp = new char[payload_len + 1]; + tmp[payload_len] = 0; + memcpy(tmp, p + 2 + 2, payload_len); + } else if (payload_len == 127) { + LOG("127", ""); + payload_len = myntohll(*(uint64_t*)(p + 2)); + tmp = new char[payload_len + 1]; + tmp[payload_len] = 0; + memcpy(tmp, p + 2 + 8, payload_len); + } + LOG("fin = %lu", fin); + LOG("opcode = %lu", opcode); + LOG("mask = %lu", mask); + LOG("payload_len = %lu", payload_len); + if (tmp) { + LOG("payload = \n%s", tmp); + delete[] tmp; + } else { + LOG("payload = NULL", ""); + } + LOG("--------------------", ""); +} diff --git a/ThirdParty/websocket/src/sha1.cpp b/ThirdParty/websocket/src/sha1.cpp new file mode 100644 index 0000000..3676a43 --- /dev/null +++ b/ThirdParty/websocket/src/sha1.cpp @@ -0,0 +1,209 @@ +/* sha1.cpp + +Copyright (c) 2005 Michael D. Leonhard + +http://tamale.net/ + +Copyright (c) 2005 Michael D. Leonhard + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#include +#include +#include +#include + +#include "sha1.h" + +// print out memory in hexadecimal +void SHA1::hexPrinter( unsigned char* c, int l ) +{ + assert( c ); + assert( l > 0 ); + while( l > 0 ) + { + printf( " %02x", *c ); + l--; + c++; + } +} + +// circular left bit rotation. MSB wraps around to LSB +Uint32 SHA1::lrot( Uint32 x, int bits ) +{ + return (x<>(32 - bits)); +}; + +// Save a 32-bit unsigned integer to memory, in big-endian order +void SHA1::storeBigEndianUint32( unsigned char* byte, Uint32 num ) +{ + assert( byte ); + byte[0] = (unsigned char)(num>>24); + byte[1] = (unsigned char)(num>>16); + byte[2] = (unsigned char)(num>>8); + byte[3] = (unsigned char)num; +} + + +// Constructor ******************************************************* +SHA1::SHA1() +{ + // make sure that the data type is the right size + assert( sizeof( Uint32 ) * 5 == 20 ); + + // initialize + H0 = 0x67452301; + H1 = 0xefcdab89; + H2 = 0x98badcfe; + H3 = 0x10325476; + H4 = 0xc3d2e1f0; + unprocessedBytes = 0; + size = 0; +} + +// Destructor ******************************************************** +SHA1::~SHA1() +{ + // erase data + H0 = H1 = H2 = H3 = H4 = 0; + for( int c = 0; c < 64; c++ ) bytes[c] = 0; + unprocessedBytes = size = 0; +} + +// process *********************************************************** +void SHA1::process() +{ + assert( unprocessedBytes == 64 ); + //printf( "process: " ); hexPrinter( bytes, 64 ); printf( "\n" ); + int t; + Uint32 a, b, c, d, e, K, f, W[80]; + // starting values + a = H0; + b = H1; + c = H2; + d = H3; + e = H4; + // copy and expand the message block + for( t = 0; t < 16; t++ ) W[t] = (bytes[t*4] << 24) + +(bytes[t*4 + 1] << 16) + +(bytes[t*4 + 2] << 8) + + bytes[t*4 + 3]; + for(; t< 80; t++ ) W[t] = lrot( W[t-3]^W[t-8]^W[t-14]^W[t-16], 1 ); + + /* main loop */ + Uint32 temp; + for( t = 0; t < 80; t++ ) + { + if( t < 20 ) { + K = 0x5a827999; + f = (b & c) | ((b ^ 0xFFFFFFFF) & d);//TODO: try using ~ + } else if( t < 40 ) { + K = 0x6ed9eba1; + f = b ^ c ^ d; + } else if( t < 60 ) { + K = 0x8f1bbcdc; + f = (b & c) | (b & d) | (c & d); + } else { + K = 0xca62c1d6; + f = b ^ c ^ d; + } + temp = lrot(a,5) + f + e + W[t] + K; + e = d; + d = c; + c = lrot(b,30); + b = a; + a = temp; + //printf( "t=%d %08x %08x %08x %08x %08x\n",t,a,b,c,d,e ); + } + /* add variables */ + H0 += a; + H1 += b; + H2 += c; + H3 += d; + H4 += e; + //printf( "Current: %08x %08x %08x %08x %08x\n",H0,H1,H2,H3,H4 ); + /* all bytes have been processed */ + unprocessedBytes = 0; +} + +// addBytes ********************************************************** +void SHA1::addBytes( const char* data, int num ) +{ + assert( data ); + assert( num > 0 ); + // add these bytes to the running total + size += num; + // repeat until all data is processed + while( num > 0 ) + { + // number of bytes required to complete block + int needed = 64 - unprocessedBytes; + assert( needed > 0 ); + // number of bytes to copy (use smaller of two) + int toCopy = (num < needed) ? num : needed; + // Copy the bytes + memcpy( bytes + unprocessedBytes, data, toCopy ); + // Bytes have been copied + num -= toCopy; + data += toCopy; + unprocessedBytes += toCopy; + + // there is a full block + if( unprocessedBytes == 64 ) process(); + } +} + +// digest ************************************************************ +unsigned char* SHA1::getDigest() +{ + // save the message size + Uint32 totalBitsL = size << 3; + Uint32 totalBitsH = size >> 29; + // add 0x80 to the message + addBytes( "\x80", 1 ); + + unsigned char footer[64] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + // block has no room for 8-byte filesize, so finish it + if( unprocessedBytes > 56 ) + addBytes( (char*)footer, 64 - unprocessedBytes); + assert( unprocessedBytes <= 56 ); + // how many zeros do we need + int neededZeros = 56 - unprocessedBytes; + // store file size (in bits) in big-endian format + storeBigEndianUint32( footer + neededZeros , totalBitsH ); + storeBigEndianUint32( footer + neededZeros + 4, totalBitsL ); + // finish the final block + addBytes( (char*)footer, neededZeros + 8 ); + // allocate memory for the digest bytes + unsigned char* digest = (unsigned char*)malloc( 20 ); + // copy the digest bytes + storeBigEndianUint32( digest, H0 ); + storeBigEndianUint32( digest + 4, H1 ); + storeBigEndianUint32( digest + 8, H2 ); + storeBigEndianUint32( digest + 12, H3 ); + storeBigEndianUint32( digest + 16, H4 ); + // return the digest + return digest; +} + diff --git a/ThirdParty/websocket/src/websocket.cpp b/ThirdParty/websocket/src/websocket.cpp new file mode 100644 index 0000000..699d9af --- /dev/null +++ b/ThirdParty/websocket/src/websocket.cpp @@ -0,0 +1,153 @@ +#include "websocket.h" + +int32_t parse_websocket_request(const char *s_req, ws_req_t *ws_req) { + if (!s_req || !ws_req) { + return -1; + } + int len = strlen(s_req); + char * tmp = new char[len + 1]; + tmp[len] = 0; + memcpy(tmp, s_req, len); + + char *delim = "\r\n"; + char *p = NULL, *q = NULL; + + p = strtok(tmp, delim); + if (p) { + //printf("%s\n", p); + ws_req->req = p; + while (p = strtok(NULL, delim)) { + //printf("%s\n", p); + if ((q = strstr(p, ":")) != NULL) { + *q = '\0'; + if (_stricmp(p, "Connection") == 0) { + while (*++q == ' '); + ws_req->connection = q; + } + if (_stricmp(p, "Upgrade") == 0) { + while (*++q == ' '); + ws_req->upgrade = q; + } + if (_stricmp(p, "Host") == 0) { + while (*++q == ' '); + ws_req->host = q + 1; + } + if (_stricmp(p, "Origin") == 0) { + while (*++q == ' '); + ws_req->origin = q; + } + if (_stricmp(p, "Cookie") == 0) { + while (*++q == ' '); + ws_req->cookie = q; + } + if (_stricmp(p, "Sec-WebSocket-Key") == 0) { + while (*++q == ' '); + ws_req->sec_websocket_key = q; + } + if (_stricmp(p, "Sec-WebSocket-Version") == 0) { + while (*++q == ' '); + ws_req->sec_websocket_version = q; + } + } + } + } + delete[] tmp; + return 0; +} + + +void print_websocket_request(const ws_req_t *ws_req) { + if (ws_req) { + if (!ws_req->req.empty()) { + fprintf(stdout, "%s\r\n", ws_req->req.c_str()); + } + if (!ws_req->connection.empty()) { + fprintf(stdout, "Connection: %s\r\n", ws_req->connection.c_str()); + } + if (!ws_req->upgrade.empty()) { + fprintf(stdout, "Upgrade: %s\r\n", ws_req->upgrade.c_str()); + } + if (!ws_req->host.empty()) { + fprintf(stdout, "Host: %s\r\n", ws_req->host.c_str()); + } + if (!ws_req->origin.empty()) { + fprintf(stdout, "Origin: %s\r\n", ws_req->origin.c_str()); + } + if (!ws_req->cookie.empty()) { + fprintf(stdout, "Cookie: %s\r\n", ws_req->cookie.c_str()); + } + if (!ws_req->sec_websocket_key.empty()) { + fprintf(stdout, "Sec-WebSocket-Key: %s\r\n", ws_req->sec_websocket_key.c_str()); + } + if (!ws_req->sec_websocket_version.empty()) { + fprintf(stdout, "Sec-WebSocket-Version: %s\r\n", ws_req->sec_websocket_version.c_str()); + } + fprintf(stdout, "\r\n"); + } +} + + +string generate_websocket_response(const ws_req_t *ws_req) { + string resp; + if (ws_req) { + resp += "HTTP/1.1 101 WebSocket Protocol HandShake\r\n"; + resp += "Connection: Upgrade\r\n"; + resp += "Upgrade: WebSocket\r\n"; + resp += "Server: WebChat Demo Server\r\n"; + resp += "Sec-WebSocket-Accept: " + generate_key(ws_req->sec_websocket_key) + "\r\n"; + resp += "\r\n"; + } + return resp; +} + + +string generate_key(const string &key) { + //sha-1 + string tmp = key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; + unsigned char md[20] = {0}; + SHA1 sha; + sha.addBytes(tmp.c_str(), tmp.length()); + + //base64 encode + unsigned char * digest = sha.getDigest(); + string res = base64_encode(digest, 20); + delete [] digest; + + return res; +} + + +int32_t parse_frame_header(const char *buf, frame_t *frame) { + if (!buf || !frame) { + return -1; + } + unsigned char c1 = *buf; + unsigned char c2 = *(buf + 1); + frame->fin = (c1 >> 7) & 0xff; + frame->opcode = c1 & 0x0f; + frame->mask = (c2 >> 7) & 0xff; + frame->payload_len = c2 & 0x7f; + return 0; +} + + +int32_t unmask_payload_data(frame_t *frame) { + if (frame && frame->payload_data && frame->payload_len > 0) { + for (int32_t i = 0; i < frame->payload_len; ++i) { + *(frame->payload_data + i) = *(frame->payload_data + i) ^ *(frame->masking_key + i % 4); + } + return 0; + } + return -1; +} + + +int32_t unmask_payload_data(const char *masking_key, char *payload_data, uint32_t payload_len) { + if (!masking_key || !payload_data || payload_len == 0) { + return -1; + } + for (int32_t i = 0; i < payload_len; ++i) { + *(payload_data + i) = *(payload_data + i) ^ *(masking_key + i % 4); + } + return 0; +} diff --git a/ThirdParty/wxJSON/include/wx/json_defs.h b/ThirdParty/wxJSON/include/wx/json_defs.h new file mode 100644 index 0000000..1576f3c --- /dev/null +++ b/ThirdParty/wxJSON/include/wx/json_defs.h @@ -0,0 +1,212 @@ +///////////////////////////////////////////////////////////////////////////// +// Name: json_defs.h +// Purpose: shared build defines +// Author: Luciano Cattani +// Created: 2007/10/20 +// RCS-ID: $Id: json_defs.h,v 1.6 2008/03/12 10:48:19 luccat Exp $ +// Copyright: (c) 2007 Luciano Cattani +// Licence: wxWidgets licence +///////////////////////////////////////////////////////////////////////////// + + +#ifndef _WX_JSON_DEFS_H_ +#define _WX_JSON_DEFS_H_ + +// Defines for component version. +// The following symbols should be updated for each new component release +// since some kind of tests, like those of AM_WXCODE_CHECKFOR_COMPONENT_VERSION() +// for "configure" scripts under unix, use them. +#define wxJSON_MAJOR 1 +#define wxJSON_MINOR 2 +#define wxJSON_RELEASE 1 + +// For non-Unix systems (i.e. when building without a configure script), +// users of this component can use the following macro to check if the +// current version is at least major.minor.release +#define wxCHECK_JSON_VERSION(major,minor,release) \ + (wxJSON_MAJOR > (major) || \ + (wxJSON_MAJOR == (major) && wxJSON_MINOR > (minor)) || \ + (wxJSON_MAJOR == (major) && wxJSON_MINOR == (minor) && wxJSON_RELEASE >= (release))) + + +// Defines for shared builds. +// Simple reference for using these macros and for writin components +// which support shared builds: +// +// 1) use the WXDLLIMPEXP_MYCOMP in each class declaration: +// class WXDLLIMPEXP_MYCOMP myCompClass { [...] }; +// +// 2) use the WXDLLIMPEXP_MYCOMP in the declaration of each global function: +// WXDLLIMPEXP_MYCOMP int myGlobalFunc(); +// +// 3) use the WXDLLIMPEXP_DATA_MYCOMP() in the declaration of each global +// variable: +// WXDLLIMPEXP_DATA_MYCOMP(int) myGlobalIntVar; +// +#ifdef WXMAKINGDLL_JSON + #define WXDLLIMPEXP_JSON WXEXPORT + #define WXDLLIMPEXP_DATA_JSON(type) WXEXPORT type +#elif defined(WXUSINGDLL) + #define WXDLLIMPEXP_JSON WXIMPORT + #define WXDLLIMPEXP_DATA_JSON(type) WXIMPORT type +#else // not making nor using DLL + #define WXDLLIMPEXP_JSON + #define WXDLLIMPEXP_DATA_JSON(type) type +#endif + +// the __PRETTY_FUNCTION__ macro expands to the full class's +// member name in the GNU GCC. +// For other compilers we use the standard __wxFUNCTION__ macro +#if !defined( __GNUC__ ) + #define __PRETTY_FUNCTION__ __WXFUNCTION__ +#endif + + + +// define wxJSON_USE_UNICODE if wxWidgets was built with +// unicode support +#if defined( wxJSON_USE_UNICODE ) + #undef wxJSON_USE_UNICODE +#endif +// do not modify the following lines +#if wxUSE_UNICODE == 1 + #define wxJSON_USE_UNICODE +#endif + +// the following macro, if defined, cause the wxJSONValue to store +// pointers to C-strings as pointers to statically allocated +// C-strings. By default this macro is not defined +// #define wxJSON_USE_CSTRING + + +// the following macro, if defined, cause the wxJSONvalue and its +// referenced data structure to store and increment a static +// progressive counter in the ctor. +// this is only usefull for debugging purposes +// #define WXJSON_USE_VALUE_COUNTER + + +// the following macro is used by wxJSON internally and you should not +// modify it. If the platform seems to support 64-bits integers, +// the following lines define the 'wxJSON_64BIT_INT' macro +#if defined( wxLongLong_t ) +#define wxJSON_64BIT_INT +#endif + + +// +// the following macro, if defined, cause the wxJSON library to +// always use 32-bits integers also when the platform seems to +// have native 64-bits support: by default the macro if not defined +// +// #define wxJSON_NO_64BIT_INT +// +#if defined( wxJSON_NO_64BIT_INT ) && defined( wxJSON_64BIT_INT ) +#undef wxJSON_64BIT_INT +#endif + +// +// it seems that some compilers do not define 'long long int' limits +// constants. For example, this is the output of the Borland BCC 5.5 +// compiler when I tried to compile wxJSON with 64-bits integer support: +// Error E2451 ..\src\jsonreader.cpp 1737: Undefined symbol 'LLONG_MAX' +// in function wxJSONReader::Strtoll(const wxString &,__int64 *) +// *** 1 errors in Compile *** +// so, if the constants are not defined, I define them by myself +#if !defined( LLONG_MAX ) + #define LLONG_MAX 9223372036854775807 +#endif + +#if !defined( ULLONG_MAX ) + #define ULLONG_MAX 18446744073709551615 +#endif + +#if !defined( LLONG_MIN ) + #define LLONG_MIN -9223372036854775808 +#endif + + + +// the same applies for all other integer constants +#if !defined( INT_MIN ) + #define INT_MIN -32768 +#endif +#if !defined( INT_MAX ) + #define INT_MAX 32767 +#endif +#if !defined( UINT_MAX ) + #define UINT_MAX 65535 +#endif +#if !defined( LONG_MIN ) + #define LONG_MIN -2147483648 +#endif +#if !defined( LONG_MAX ) + #define LONG_MAX 2147483647 +#endif +#if !defined( ULONG_MAX ) + #define ULONG_MAX 4294967295 +#endif +#if !defined( SHORT_MAX ) + #define SHORT_MAX 32767 +#endif +#if !defined( SHORT_MIN ) + #define SHORT_MIN -32768 +#endif +#if !defined( USHORT_MAX ) + #define USHORT_MAX 65535 +#endif + + + +// +// define the wxJSON_ASSERT() macro to expand to wxASSERT() +// unless the wxJSON_NOABORT_ASSERT is defined +// #define wxJSON_NOABORT_ASSERT +#if defined( wxJSON_NOABORT_ASSERT ) + #define wxJSON_ASSERT( cond ) +#else + #define wxJSON_ASSERT( cond ) wxASSERT( cond ); +#endif + + +// +// the following macros are used by the wxJSONWriter::WriteStringValues() +// when the wxJSONWRITER_SPLIT_STRING flag is set +#define wxJSONWRITER_LAST_COL 50 +#define wxJSONWRITER_SPLIT_COL 75 +#define wxJSONWRITER_MIN_LENGTH 15 +#define wxJSONWRITER_TAB_LENGTH 4 + + +// +// some compilers (i.e. MSVC++) defines their own 'snprintf' function +// so if it is not defined, define it in the following lines +// please note that we cannot use the wxWidget's counterpart 'wxSnprintf' +// because the latter uses 'wxChar' but wxJSON only use 'char' +#if !defined(snprintf) && defined(_MSC_VER) +#define snprintf _snprintf +#endif + + +// +// check if wxWidgets is compiled using --enable-stl in which case +// we have to use different aproaches when declaring the array and +// key/value containers (see the docs: wxJSON internals: array and hash_map +#undef wxJSON_USE_STL +#if defined( wxUSE_STL ) && wxUSE_STL == 1 +#define wxJSON_USE_STL +#endif + +// +// defines the MIN and MAX macro for numeric arguments +// note that the safest way to define such functions is using templates +#ifndef MIN +#define MIN(a,b) a < b ? a : b +#endif +#ifndef MAX +#define MAX(a,b) a > b ? a : b +#endif + +#endif // _WX_JSON_DEFS_H_ + + diff --git a/ThirdParty/wxJSON/include/wx/jsonreader.h b/ThirdParty/wxJSON/include/wx/jsonreader.h new file mode 100644 index 0000000..dd10b8c --- /dev/null +++ b/ThirdParty/wxJSON/include/wx/jsonreader.h @@ -0,0 +1,150 @@ +///////////////////////////////////////////////////////////////////////////// +// Name: jsonreader.h +// Purpose: the parser of JSON text +// Author: Luciano Cattani +// Created: 2007/09/15 +// RCS-ID: $Id: jsonreader.h,v 1.3 2008/03/03 19:05:45 luccat Exp $ +// Copyright: (c) 2007 Luciano Cattani +// Licence: wxWidgets licence +///////////////////////////////////////////////////////////////////////////// + +#if !defined( _WX_JSONREADER_H ) +#define _WX_JSONREADER_H + +#ifdef __GNUG__ + #pragma interface "jsonreader.h" +#endif + +// For compilers that support precompilation, includes "wx/wx.h". +#include "wx/wxprec.h" + +#ifdef __BORLANDC__ + #pragma hdrstop +#endif + +// for all others, include the necessary headers (this file is usually all you +// need because it includes almost all "standard" wxWidgets headers) +#ifndef WX_PRECOMP + #include + #include + #include +#endif + + +#include "json_defs.h" +#include "jsonval.h" + +// The flags of the parser +enum { + wxJSONREADER_STRICT = 0, + wxJSONREADER_ALLOW_COMMENTS = 1, + wxJSONREADER_STORE_COMMENTS = 2, + wxJSONREADER_CASE = 4, + wxJSONREADER_MISSING = 8, + wxJSONREADER_MULTISTRING = 16, + wxJSONREADER_COMMENTS_AFTER = 32, + wxJSONREADER_NOUTF8_STREAM = 64, + wxJSONREADER_MEMORYBUFF = 128, + + wxJSONREADER_TOLERANT = wxJSONREADER_ALLOW_COMMENTS | wxJSONREADER_CASE | + wxJSONREADER_MISSING | wxJSONREADER_MULTISTRING, + wxJSONREADER_COMMENTS_BEFORE = wxJSONREADER_ALLOW_COMMENTS | wxJSONREADER_STORE_COMMENTS +}; + + +class WXDLLIMPEXP_JSON wxJSONReader +{ +public: + wxJSONReader( int flags = wxJSONREADER_TOLERANT, int maxErrors = 30 ); + virtual ~wxJSONReader(); + + int Parse( const wxString& doc, wxJSONValue* val ); + int Parse( wxInputStream& doc, wxJSONValue* val ); + + int GetDepth() const; + int GetErrorCount() const; + int GetWarningCount() const; + const wxArrayString& GetErrors() const; + const wxArrayString& GetWarnings() const; + + static int UTF8NumBytes( char ch ); + +#if defined( wxJSON_64BIT_INT ) + static bool Strtoll( const wxString& str, wxInt64* i64 ); + static bool Strtoull( const wxString& str, wxUint64* ui64 ); + static bool DoStrto_ll( const wxString& str, wxUint64* ui64, wxChar* sign ); +#endif + +protected: + + int DoRead( wxInputStream& doc, wxJSONValue& val ); + void AddError( const wxString& descr ); + void AddError( const wxString& fmt, const wxString& str ); + void AddError( const wxString& fmt, wxChar ch ); + void AddWarning( int type, const wxString& descr ); + int GetStart( wxInputStream& is ); + int ReadChar( wxInputStream& is ); + int PeekChar( wxInputStream& is ); + void StoreValue( int ch, const wxString& key, wxJSONValue& value, wxJSONValue& parent ); + int SkipWhiteSpace( wxInputStream& is ); + int SkipComment( wxInputStream& is ); + void StoreComment( const wxJSONValue* parent ); + int ReadString( wxInputStream& is, wxJSONValue& val ); + int ReadToken( wxInputStream& is, int ch, wxString& s ); + int ReadValue( wxInputStream& is, int ch, wxJSONValue& val ); + int ReadUES( wxInputStream& is, char* uesBuffer ); + int AppendUES( wxMemoryBuffer& utf8Buff, const char* uesBuffer ); + int NumBytes( char ch ); + int ConvertCharByChar( wxString& s, const wxMemoryBuffer& utf8Buffer ); + int ReadMemoryBuff( wxInputStream& is, wxJSONValue& val ); + + //! Flag that control the parser behaviour, + int m_flags; + + //! Maximum number of errors stored in the error's array + int m_maxErrors; + + //! The current line number (start at 1). + int m_lineNo; + + //! The current column number (start at 1). + int m_colNo; + + //! The current level of object/array annidation (start at ZERO). + int m_level; + + //! The depth level of the read JSON text + int m_depth; + + //! The pointer to the value object that is being read. + wxJSONValue* m_current; + + //! The pointer to the value object that was last stored. + wxJSONValue* m_lastStored; + + //! The pointer to the value object that will be read. + wxJSONValue* m_next; + + //! The comment string read by SkipComment(). + wxString m_comment; + + //! The starting line of the comment string. + int m_commentLine; + + //! The array of error messages. + wxArrayString m_errors; + + //! The array of warning messages. + wxArrayString m_warnings; + + //! The character read by the PeekChar() function (-1 none) + int m_peekChar; + + //! ANSI: do not convert UTF-8 strings + bool m_noUtf8; +}; + + +#endif // not defined _WX_JSONREADER_H + + diff --git a/ThirdParty/wxJSON/include/wx/jsonval.h b/ThirdParty/wxJSON/include/wx/jsonval.h new file mode 100644 index 0000000..7eb5341 --- /dev/null +++ b/ThirdParty/wxJSON/include/wx/jsonval.h @@ -0,0 +1,441 @@ +///////////////////////////////////////////////////////////////////////////// +// Name: jsonval.h +// Purpose: the wxJSONValue class: it holds a JSON value +// Author: Luciano Cattani +// Created: 2007/09/15 +// RCS-ID: $Id: jsonval.h,v 1.4 2008/01/10 21:27:15 luccat Exp $ +// Copyright: (c) 2007 Luciano Cattani +// Licence: wxWidgets licence +///////////////////////////////////////////////////////////////////////////// + +#if !defined( _WX_JSONVAL_H ) +#define _WX_JSONVAL_H + +#ifdef __GNUG__ + #pragma interface "jsonval.h" +#endif + +// For compilers that support precompilation, includes "wx/wx.h". +#include "wx/wxprec.h" + +#ifdef __BORLANDC__ + #pragma hdrstop +#endif + +// for all others, include the necessary headers (this file is usually all you +// need because it includes almost all "standard" wxWidgets headers) +#ifndef WX_PRECOMP + #include + #include + #include + #include +#endif + + +#include "json_defs.h" + +// forward declarations +class WXDLLIMPEXP_JSON wxJSONReader; +class WXDLLIMPEXP_JSON wxJSONRefData; + +//#if defined( wxJSON_USE_STL ) +// // if compiling on MinGW we use the STL-style declaration of wxWidget's +// // container classes +// class WXDLLIMPEXP_JSON wxJSONValue; +// WX_DECLARE_OBJARRAY( wxJSONValue, wxJSONInternalArray ); +// WX_DECLARE_STRING_HASH_MAP( wxJSONValue, wxJSONInternalMap ); +//#else + class WXDLLIMPEXP_JSON wxJSONInternalMap; + class WXDLLIMPEXP_JSON wxJSONInternalArray; +//#endif + + +//! The type of the value held by the wxJSONRefData class +enum wxJSONType { + wxJSONTYPE_INVALID = 0, /*!< the object is not uninitialized */ + wxJSONTYPE_NULL, /*!< the object contains a NULL value */ + wxJSONTYPE_INT, /*!< the object contains an integer */ + wxJSONTYPE_UINT, /*!< the object contains an unsigned integer */ + wxJSONTYPE_DOUBLE, /*!< the object contains a double */ + wxJSONTYPE_STRING, /*!< the object contains a wxString object */ + wxJSONTYPE_CSTRING, /*!< the object contains a static C-string */ + wxJSONTYPE_BOOL, /*!< the object contains a boolean */ + wxJSONTYPE_ARRAY, /*!< the object contains an array of values */ + wxJSONTYPE_OBJECT, /*!< the object contains a map of keys/values */ + wxJSONTYPE_LONG, /*!< the object contains a 32-bit integer */ + wxJSONTYPE_INT64, /*!< the object contains a 64-bit integer */ + wxJSONTYPE_ULONG, /*!< the object contains an unsigned 32-bit integer */ + wxJSONTYPE_UINT64, /*!< the object contains an unsigned 64-bit integer */ + wxJSONTYPE_SHORT, /*!< the object contains a 16-bit integer */ + wxJSONTYPE_USHORT, /*!< the object contains a 16-bit unsigned integer */ + wxJSONTYPE_MEMORYBUFF /*!< the object contains a binary memory buffer */ +}; + +// the comment position: every value only has one comment position +// althrough comments may be splitted into several lines +enum { + wxJSONVALUE_COMMENT_DEFAULT = 0, + wxJSONVALUE_COMMENT_BEFORE, + wxJSONVALUE_COMMENT_AFTER, + wxJSONVALUE_COMMENT_INLINE, +}; + +/*********************************************************************** + + class wxJSONValue + +***********************************************************************/ + + +// class WXDLLIMPEXP_JSON wxJSONValue : public wxObject +class WXDLLIMPEXP_JSON wxJSONValue +{ + friend class wxJSONReader; + +public: + + // ctors and dtor + wxJSONValue(); + wxJSONValue( wxJSONType type ); + wxJSONValue( int i ); + wxJSONValue( unsigned int i ); + wxJSONValue( short i ); + wxJSONValue( unsigned short i ); + wxJSONValue( long int i ); + wxJSONValue( unsigned long int i ); +#if defined( wxJSON_64BIT_INT) + wxJSONValue( wxInt64 i ); + wxJSONValue( wxUint64 ui ); +#endif + wxJSONValue( bool b ); + wxJSONValue( double d ); + wxJSONValue( const wxChar* str ); // assume static ASCIIZ strings + wxJSONValue( const wxString& str ); + wxJSONValue( const wxMemoryBuffer& buff ); + wxJSONValue( const void* buff, size_t len ); + wxJSONValue( const wxJSONValue& other ); + virtual ~wxJSONValue(); + + // functions for retrieving the value type + wxJSONType GetType() const; + bool IsValid() const; + bool IsNull() const; + bool IsInt() const; + bool IsUInt() const; + bool IsShort() const; + bool IsUShort() const; + bool IsLong() const; + bool IsULong() const; +#if defined( wxJSON_64BIT_INT) + bool IsInt32() const; + bool IsInt64() const; + bool IsUInt32() const; + bool IsUInt64() const; +#endif + bool IsBool() const; + bool IsDouble() const; + bool IsString() const; + bool IsCString() const; + bool IsArray() const; + bool IsObject() const; + bool IsMemoryBuff() const; + + // function for retireving the value as ... + int AsInt() const; + unsigned int AsUInt() const; + short AsShort() const; + unsigned short AsUShort() const; + long int AsLong() const; + unsigned long AsULong() const; + bool AsInt( int& i ) const; + bool AsUInt( unsigned int& ui ) const; + bool AsShort( short int& s ) const; + bool AsUShort( unsigned short& us ) const; + bool AsLong( long int& l ) const; + bool AsULong( unsigned long& ul ) const; +#if defined( wxJSON_64BIT_INT) + wxInt32 AsInt32() const; + wxUint32 AsUInt32() const; + wxInt64 AsInt64() const; + wxUint64 AsUInt64() const; + bool AsInt32( wxInt32& i32 ) const; + bool AsUInt32( wxUint32& ui32 ) const; + bool AsInt64( wxInt64& i64 ) const; + bool AsUInt64( wxUint64& ui64 ) const; +#endif + bool AsBool() const; + double AsDouble() const; + wxString AsString() const; + const wxChar* AsCString() const; + bool AsBool( bool& b ) const; + bool AsDouble( double& d ) const; + bool AsString( wxString& str ) const; + bool AsCString( wxChar* ch ) const; + wxMemoryBuffer AsMemoryBuff() const; + bool AsMemoryBuff( wxMemoryBuffer& buff ) const; + + const wxJSONInternalMap* AsMap() const; + const wxJSONInternalArray* AsArray() const; + + // get members names, size and other info + bool HasMember( unsigned index ) const; + bool HasMember( const wxString& key ) const; + int Size() const; + wxArrayString GetMemberNames() const; + + // appending items, resizing and deleting items + wxJSONValue& Append( const wxJSONValue& value ); + wxJSONValue& Append( bool b ); + wxJSONValue& Append( int i ); + wxJSONValue& Append( unsigned int ui ); + wxJSONValue& Append( short int i ); + wxJSONValue& Append( unsigned short int ui ); + wxJSONValue& Append( long int l ); + wxJSONValue& Append( unsigned long int ul ); +#if defined( wxJSON_64BIT_INT ) + wxJSONValue& Append( wxInt64 i ); + wxJSONValue& Append( wxUint64 ui ); +#endif + wxJSONValue& Append( double d ); + wxJSONValue& Append( const wxChar* str ); + wxJSONValue& Append( const wxString& str ); + wxJSONValue& Append( const wxMemoryBuffer& buff ); + wxJSONValue& Append( const void* buff, size_t len ); + bool Remove( int index ); + bool Remove( const wxString& key ); + void Clear(); + bool Cat( const wxChar* str ); + bool Cat( const wxString& str ); + bool Cat( const wxMemoryBuffer& buff ); + + // retrieve an item + wxJSONValue& Item( unsigned index ); + wxJSONValue& Item( const wxString& key ); + wxJSONValue ItemAt( unsigned index ) const; + wxJSONValue ItemAt( const wxString& key ) const; + + wxJSONValue& operator [] ( unsigned index ); + wxJSONValue& operator [] ( const wxString& key ); + + wxJSONValue& operator = ( int i ); + wxJSONValue& operator = ( unsigned int ui ); + wxJSONValue& operator = ( short int i ); + wxJSONValue& operator = ( unsigned short int ui ); + wxJSONValue& operator = ( long int l ); + wxJSONValue& operator = ( unsigned long int ul ); +#if defined( wxJSON_64BIT_INT ) + wxJSONValue& operator = ( wxInt64 i ); + wxJSONValue& operator = ( wxUint64 ui ); +#endif + wxJSONValue& operator = ( bool b ); + wxJSONValue& operator = ( double d ); + wxJSONValue& operator = ( const wxChar* str ); + wxJSONValue& operator = ( const wxString& str ); + wxJSONValue& operator = ( const wxMemoryBuffer& buff ); + // wxJSONValue& operator = ( const void* buff, size_t len ); cannot be declared + wxJSONValue& operator = ( const wxJSONValue& value ); + + // get the value or a default value + wxJSONValue Get( const wxString& key, const wxJSONValue& defaultValue ) const; + + // comparison function + bool IsSameAs( const wxJSONValue& other ) const; + + // comment-related functions + int AddComment( const wxString& str, int position = wxJSONVALUE_COMMENT_DEFAULT ); + int AddComment( const wxArrayString& comments, int position = wxJSONVALUE_COMMENT_DEFAULT ); + wxString GetComment( int idx = -1 ) const; + int GetCommentPos() const; + int GetCommentCount() const; + void ClearComments(); + const wxArrayString& GetCommentArray() const; + + // debugging functions + wxString GetInfo() const; + wxString Dump( bool deep = false, int mode = 0 ) const; + + //misc functions + wxJSONRefData* GetRefData() const; + wxJSONRefData* SetType( wxJSONType type ); + int GetLineNo() const; + void SetLineNo( int num ); + + // public static functions: mainly used for debugging + static wxString TypeToString( wxJSONType type ); + static wxString MemoryBuffToString( const wxMemoryBuffer& buff, size_t len = -1 ); + static wxString MemoryBuffToString( const void* buff, size_t len, size_t actualLen = -1 ); + static int CompareMemoryBuff( const wxMemoryBuffer& buff1, const wxMemoryBuffer& buff2 ); + static int CompareMemoryBuff( const wxMemoryBuffer& buff1, const void* buff2 ); + static wxMemoryBuffer ArrayToMemoryBuff( const wxJSONValue& value ); + +protected: + wxJSONValue* Find( unsigned index ) const; + wxJSONValue* Find( const wxString& key ) const; + void DeepCopy( const wxJSONValue& other ); + + wxJSONRefData* Init( wxJSONType type ); + wxJSONRefData* COW(); + + // overidden from wxObject + virtual wxJSONRefData* CloneRefData(const wxJSONRefData *data) const; + virtual wxJSONRefData* CreateRefData() const; + + void SetRefData(wxJSONRefData* data); + void Ref(const wxJSONValue& clone); + void UnRef(); + void UnShare(); + void AllocExclusive(); + + //! the referenced data + wxJSONRefData* m_refData; + + + // used for debugging purposes: only in debug builds. +#if defined( WXJSON_USE_VALUE_COUNTER ) + int m_progr; + static int sm_progr; +#endif +}; + + +//#if !defined( wxJSON_USE_STL ) + // if using wxWidget's implementation of container classes we declare + // the OBJARRAY are HASH_MAP _after_ the wxJSONValue is fully known + WX_DECLARE_OBJARRAY( wxJSONValue, wxJSONInternalArray ); + WX_DECLARE_STRING_HASH_MAP_WITH_DECL( wxJSONValue, wxJSONInternalMap, class WXDLLIMPEXP_JSON ); +//#endif + + +/*********************************************************************** + + class wxJSONRefData + +***********************************************************************/ + + + + +//! The actual value held by the wxJSONValue class (internal use) +/*! + Note that this structure is a \b union as in versions prior to 0.4.x + The union just stores primitive types and not complex types which are + stored in separate data members of the wxJSONRefData structure. + + This organization give us more flexibility when retrieving compatible + types such as ints unsigned ints, long and so on. + To know more about the internal structure of the wxJSONValue class + see \ref pg_json_internals. +*/ +union wxJSONValueHolder { + int m_valInt; + unsigned int m_valUInt; + short int m_valShort; + unsigned short m_valUShort; + long int m_valLong; + unsigned long m_valULong; + double m_valDouble; + const wxChar* m_valCString; + bool m_valBool; +#if defined( wxJSON_64BIT_INT ) + wxInt64 m_valInt64; + wxUint64 m_valUInt64; +#endif + }; + +// +// access to the (unsigned) integer value is done through +// the VAL_INT macro which expands to the 'long' integer +// data member of the 'long long' integer if 64-bits integer +// support is enabled +#if defined( wxJSON_64BIT_INT ) + #define VAL_INT m_valInt64 + #define VAL_UINT m_valUInt64 +#else + #define VAL_INT m_valLong + #define VAL_UINT m_valULong +#endif + + + +// class WXDLLIMPEXP_JSON wxJSONRefData : public wxObjectRefData +class WXDLLIMPEXP_JSON wxJSONRefData +{ + // friend class wxJSONReader; + friend class wxJSONValue; + friend class wxJSONWriter; + +public: + + wxJSONRefData(); + virtual ~wxJSONRefData(); + + int GetRefCount() const; + + // there is no need to define copy ctor + + //! the references count + int m_refCount; + + //! The actual type of the value held by this object. + wxJSONType m_type; + + //! The JSON value held by this object. + /*! + This data member contains the JSON data types defined by the + JSON syntax with the exception of the complex objects. + This data member is an union of the primitive types + so that it is simplier to cast them in other compatible types. + */ + wxJSONValueHolder m_value; + + //! The JSON string value. + wxString m_valString; + + //! The JSON array value. + wxJSONInternalArray m_valArray; + + //! The JSON object value. + wxJSONInternalMap m_valMap; + + //! The position of the comment line(s), if any. + /*! + The data member contains one of the following constants: + \li \c wxJSONVALUE_COMMENT_BEFORE + \li \c wxJSONVALUE_COMMENT_AFTER + \li \c wxJSONVALUE_COMMENT_INLINE + */ + int m_commentPos; + + //! The array of comment lines; may be empty. + wxArrayString m_comments; + + //! The line number when this value was read + /*! + This data member is used by the wxJSONReader class and it is + used to store the line number of the JSON text document where + the value appeared. This value is compared to the line number + of a comment line in order to obtain the value which a + comment refersto. + */ + int m_lineNo; + + //! The pointer to the memory buffer object + /*! + Note that despite using reference counting, the \b wxMemoryBuffer is not a + \e copy-on-write structure so the wxJSON library uses some tricks in order to + avoid the side effects of copying / assigning wxMemoryBuffer objects + */ + wxMemoryBuffer* m_memBuff; + + // used for debugging purposes: only in debug builds. +#if defined( WXJSON_USE_VALUE_COUNTER ) + int m_progr; + static int sm_progr; +#endif +}; + + + +#endif // not defined _WX_JSONVAL_H + + diff --git a/ThirdParty/wxJSON/include/wx/jsonwriter.h b/ThirdParty/wxJSON/include/wx/jsonwriter.h new file mode 100644 index 0000000..5705f91 --- /dev/null +++ b/ThirdParty/wxJSON/include/wx/jsonwriter.h @@ -0,0 +1,119 @@ +///////////////////////////////////////////////////////////////////////////// +// Name: jsonwriter.h +// Purpose: the generator of JSON text from a JSON value +// Author: Luciano Cattani +// Created: 2007/09/15 +// RCS-ID: $Id: jsonwriter.h,v 1.4 2008/03/03 19:05:45 luccat Exp $ +// Copyright: (c) 2007 Luciano Cattani +// Licence: wxWidgets licence +///////////////////////////////////////////////////////////////////////////// + +#if !defined( _WX_JSONWRITER_H ) +#define _WX_JSONWRITER_H + +#ifdef __GNUG__ + #pragma interface "jsonwriter.h" +#endif + +// For compilers that support precompilation, includes "wx/wx.h". +#include "wx/wxprec.h" + +#ifdef __BORLANDC__ + #pragma hdrstop +#endif + +// for all others, include the necessary headers (this file is usually all you +// need because it includes almost all "standard" wxWidgets headers) +#ifndef WX_PRECOMP + #include + #include +#endif + +#include "json_defs.h" +#include "jsonval.h" + +enum { + wxJSONWRITER_NONE = 0, + wxJSONWRITER_STYLED = 1, + wxJSONWRITER_WRITE_COMMENTS = 2, + wxJSONWRITER_COMMENTS_BEFORE = 4, + wxJSONWRITER_COMMENTS_AFTER = 8, + wxJSONWRITER_SPLIT_STRING = 16, + wxJSONWRITER_NO_LINEFEEDS = 32, + wxJSONWRITER_ESCAPE_SOLIDUS = 64, + wxJSONWRITER_MULTILINE_STRING = 128, + wxJSONWRITER_RECOGNIZE_UNSIGNED = 256, + wxJSONWRITER_TAB_INDENT = 512, + wxJSONWRITER_NO_INDENTATION = 1024, + wxJSONWRITER_NOUTF8_STREAM = 2048, + wxJSONWRITER_MEMORYBUFF = 4096 +}; + +// class declaration + +class WXDLLIMPEXP_JSON wxJSONWriter +{ +public: + wxJSONWriter( int style = wxJSONWRITER_STYLED, int indent = 0, int step = 3 ); + ~wxJSONWriter(); + + void Write( const wxJSONValue& value, wxString& str ); + void Write( const wxJSONValue& value, wxOutputStream& os ); + void SetDoubleFmtString( const char* fmt ); + +protected: + + int DoWrite( wxOutputStream& os, const wxJSONValue& value, const wxString* key, bool comma ); + int WriteIndent( wxOutputStream& os ); + int WriteIndent( wxOutputStream& os, int num ); + bool IsSpace( wxChar ch ); + bool IsPunctuation( wxChar ch ); + + int WriteString( wxOutputStream& os, const wxString& str ); + int WriteStringValue( wxOutputStream& os, const wxString& str ); + int WriteNullValue( wxOutputStream& os ); + int WriteIntValue( wxOutputStream& os, const wxJSONValue& v ); + int WriteUIntValue( wxOutputStream& os, const wxJSONValue& v ); + int WriteBoolValue( wxOutputStream& os, const wxJSONValue& v ); + int WriteDoubleValue( wxOutputStream& os, const wxJSONValue& v ); + int WriteMemoryBuff( wxOutputStream& os, const wxMemoryBuffer& buff ); + + int WriteInvalid( wxOutputStream& os ); + int WriteSeparator( wxOutputStream& os ); + + int WriteKey( wxOutputStream& os, const wxString& key ); + int WriteComment( wxOutputStream& os, const wxJSONValue& value, bool indent ); + + int WriteError( const wxString& err ); + +private: + //! The style flag is a combination of wxJSONWRITER_(something) constants. + int m_style; + + //! The initial indentation value, in number of spaces. + int m_indent; + + //! The indentation increment, in number of spaces. + int m_step; + + //! JSON value objects can be nested; this is the level of annidation (used internally). + int m_level; + + // The line number when printing JSON text output (not yet used) + int m_lineNo; + + // The column number when printing JSON text output + int m_colNo; + + // Flag used in ANSI mode that controls UTF-8 conversion + bool m_noUtf8; + + // The format string for printing doubles + char* m_fmt; +}; + + +#endif // not defined _WX_JSONWRITER_H + + + diff --git a/ThirdParty/wxJSON/src/jsonreader.cpp b/ThirdParty/wxJSON/src/jsonreader.cpp new file mode 100644 index 0000000..88c1008 --- /dev/null +++ b/ThirdParty/wxJSON/src/jsonreader.cpp @@ -0,0 +1,2132 @@ +///////////////////////////////////////////////////////////////////////////// +// Name: jsonreader.cpp +// Purpose: the wxJSONReader class: a JSON text parser +// Author: Luciano Cattani +// Created: 2007/10/14 +// RCS-ID: $Id: jsonreader.cpp,v 1.12 2008/03/12 10:48:19 luccat Exp $ +// Copyright: (c) 2007 Luciano Cattani +// Licence: wxWidgets licence +///////////////////////////////////////////////////////////////////////////// + +#ifdef __GNUG__ + #pragma implementation "jsonreader.cpp" +#endif + +#include + +#include +#include +#include +#include + + + +/*! \class wxJSONReader + \brief The JSON parser + + The class is a JSON parser which reads a JSON formatted text and stores + values in the \c wxJSONValue structure. + The ctor accepts two parameters: the \e style flag, which controls how + much error-tolerant should the parser be and an integer which is + the maximum number of errors and warnings that have to be reported + (the default is 30). + + If the JSON text document does not contain an open/close JSON character the + function returns an \b invalid value object; in other words, the + wxJSONValue::IsValid() function returns FALSE. + This is the case of a document that is empty or contains only + whitespaces or comments. + If the document contains a starting object/array character immediatly + followed by a closing object/array character + (i.e.: \c {} ) then the function returns an \b empty array or object + JSON value. + This is a valid JSON object of type wxJSONTYPE_OBJECT or wxJSONTYPE_ARRAY + whose wxJSONValue::Size() function returns ZERO. + + \par JSON text + + The wxJSON parser just skips all characters read from the + input JSON text until the start-object '{' or start-array '[' characters + are encontered (see the GetStart() function). + This means that the JSON input text may contain anything + before the first start-object/array character except these two chars themselves + unless they are included in a C/C++ comment. + Comment lines that apear before the first start array/object character, + are non ignored if the parser is constructed with the wxJSONREADER_STORE_COMMENT + flag: they are added to the comment's array of the root JSON value. + + Note that the parsing process stops when the internal DoRead() function + returns. Because that function is recursive, the top-level close-object + '}' or close-array ']' character cause the top-level DoRead() function + to return thus stopping the parsing process regardless the EOF condition. + This means that the JSON input text may contain anything \b after + the top-level close-object/array character. + Here are some examples: + + Returns a wxJSONTYPE_INVALID value (invalid JSON value) + \code + // this text does not contain an open array/object character + \endcode + + Returns a wxJSONTYPE_OBJECT value of Size() = 0 + \code + { + } + \endcode + + Returns a wxJSONTYPE_ARRAY value of Size() = 0 + \code + [ + ] + \endcode + + Text before and after the top-level open/close characters is ignored. + \code + This non-JSON text does not cause the parser to report errors or warnings + { + } + This non-JSON text does not cause the parser to report errors or warnings + \endcode + + + \par Extensions + + The wxJSON parser recognizes all JSON text plus some extensions + that are not part of the JSON syntax but that many other JSON + implementations do recognize. + If the input text contains the following non-JSON text, the parser + reports the situation as \e warnings and not as \e errors unless + the parser object was constructed with the wxJSONREADER_STRICT + flag. In the latter case the wxJSON parser is not tolerant. + + \li C/C++ comments: the parser recognizes C and C++ comments. + Comments can optionally be stored in the value they refer + to and can also be written back to the JSON text document. + To know more about comment storage see \ref wxjson_comments + + \li case tolerance: JSON syntax states that the literals \c null, + \c true and \c false must be lowercase; the wxJSON parser + also recognizes mixed case literals such as, for example, + \b Null or \b FaLSe. A \e warning is emitted. + + \li wrong or missing closing character: wxJSON parser is tolerant + about the object / array closing character. When an open-array + character '[' is encontered, the parser expects the + corresponding close-array character ']'. If the character + encontered is a close-object char '}' a warning is reported. + A warning is also reported if the character is missing when + the end-of-file is reached. + + \li multi-line strings: this feature allows a JSON string type to be + splitted in two or more lines as in the standard C/C++ + languages. The drawback is that this feature is error-prone + and you have to use it with care. + For more info about this topic read \ref wxjson_tutorial_style_split + + Note that you can control how much error-tolerant should the parser be + and also you can specify how many and what extensions are recognized. + See the constructor's parameters for more details. + + \par Unicode vs ANSI + + The parser can read JSON text from two very different kind of objects: + + \li a string object (\b wxString) + \li a stream object (\b wxInputStream) + + When the input is from a string object, the character represented in the + string is platform- and mode- dependant; in other words, characters are + represented differently: in ANSI builds they depend on the charset in use + and in Unicode builds they depend on the platform (UCS-2 on win32, UCS-4 + or UTF-8 on GNU/Linux). + + When the input is from a stream object, the only recognized encoding format + is UTF-8 for both ANSI and Unicode builds. + + \par Example: + + \code + wxJSONValue value; + wxJSONReader reader; + + // open a text file that contains the UTF-8 encoded JSON text + wxFFileInputStream jsonText( _T("filename.utf8"), _T("r")); + + // read the file + int numErrors = reader.Parse( jsonText, &value ); + + if ( numErrors > 0 ) { + ::MessageBox( _T("Error reading the input file")); + } + \endcode + + Starting from version 1.1.0 the wxJSON reader and the writer has changed in + their internal organization. + To know more about ANSI and Unicode mode read \ref wxjson_tutorial_unicode. +*/ + + + +// if you have the debug build of wxWidgets and wxJSON you can see +// trace messages by setting the: +// WXTRACE=traceReader StoreComment +// environment variable +static const wxChar* traceMask = _T("traceReader"); +static const wxChar* storeTraceMask = _T("StoreComment"); + + +//! Ctor +/*! + Construct a JSON parser object with the given parameters. + + JSON parser objects should always be constructed on the stack but + it does not hurt to have a global JSON parser. + + \param flags this paramter controls how much error-tolerant should the + parser be + + \param maxErrors the maximum number of errors (and warnings, too) that are + reported by the parser. When the number of errors reaches this limit, + the parser stops to read the JSON input text and no other error is + reported. + + The \c flag parameter is the combination of ZERO or more of the + following constants OR'ed toghether: + + \li wxJSONREADER_ALLOW_COMMENTS: C/C++ comments are recognized by the + parser; a warning is reported by the parser + \li wxJSONREADER_STORE_COMMENTS: C/C++ comments, if recognized, are + stored in the value they refer to and can be rewritten back to + the JSON text + \li wxJSONREADER_CASE: the parser recognizes mixed-case literal strings + \li wxJSONREADER_MISSING: the parser allows missing or wrong close-object + and close-array characters + \li wxJSONREADER_MULTISTRING: strings may be splitted in two or more + lines + \li wxJSONREADER_COMMENTS_AFTER: if STORE_COMMENTS if defined, the parser + assumes that comment lines apear \b before the value they + refer to unless this constant is specified. In the latter case, + comments apear \b after the value they refer to. + \li wxJSONREADER_NOUTF8_STREAM: suppress UTF-8 conversion when reading a + string value from a stream: the reader assumes that the input stream + is encoded in ANSI format and not in UTF-8; only meaningfull in ANSI + builds, this flag is simply ignored in Unicode builds. + + You can also use the following shortcuts to specify some predefined + flag's combinations: + + \li wxJSONREADER_STRICT: all wxJSON extensions are reported as errors, this + is the same as specifying a ZERO value as \c flags. + \li wxJSONREADER_TOLERANT: this is the same as ALLOW_COMMENTS | CASE | + MISSING | MULTISTRING; all wxJSON extensions are turned on but comments + are not stored in the value objects. + + \par Example: + + The following code fragment construct a JSON parser, turns on all + wxJSON extensions and also stores C/C++ comments in the value object + they refer to. The parser assumes that the comments apear before the + value: + + \code + wxJSONReader reader( wxJSONREADER_TOLERANT | wxJSONREADER_STORE_COMMENTS ); + wxJSONValue root; + int numErrors = reader.Parse( jsonText, &root ); + \endcode +*/ +wxJSONReader::wxJSONReader( int flags, int maxErrors ) +{ + m_flags = flags; + m_maxErrors = maxErrors; + m_noUtf8 = false; +#if !defined( wxJSON_USE_UNICODE ) + // in ANSI builds we can suppress UTF-8 conversion for both the writer and the reader + if ( m_flags & wxJSONREADER_NOUTF8_STREAM ) { + m_noUtf8 = true; + } +#endif + +} + +//! Dtor - does nothing +wxJSONReader::~wxJSONReader() +{ +} + +//! Parse the JSON document. +/*! + The two overloaded versions of the \c Parse() function read a + JSON text stored in a wxString object or in a wxInputStream + object. + + If \c val is a NULL pointer, the function does not store the + values: it can be used as a JSON checker in order to check the + syntax of the document. + Returns the number of \b errors found in the document. + If the returned value is ZERO and the parser was constructed + with the \c wxJSONREADER_STRICT flag, then the parsed document + is \e well-formed and it only contains valid JSON text. + + If the \c wxJSONREADER_TOLERANT flag was used in the parser's + constructor, then a return value of ZERO + does not mean that the document is \e well-formed because it may + contain comments and other extensions that are not fatal for the + wxJSON parser but other parsers may fail to recognize. + You can use the \c GetWarningCount() function to know how many + wxJSON extensions are present in the JSON input text. + + Note that the JSON value object \c val is not cleared by this + function unless its type is of the wrong type. + In other words, if \c val is of type wxJSONTYPE_ARRAY and it already + contains 10 elements and the input document starts with a + '[' (open-array char) then the elements read from the document are + \b appended to the existing ones. + + On the other hand, if the text document starts with a '{' (open-object) char + then this function must change the type of the \c val object to + \c wxJSONTYPE_OBJECT and the old content of 10 array elements will be lost. + + \par Different input types + + The real parsing process in done using UTF-8 streams. If the input is + from a \b wxString object, the Parse function first converts the input string + in a temporary \b wxMemoryInputStream which contains the UTF-8 conversion + of the string itself. + Next, the overloaded Parse function is called. + + @param doc the JSON text that has to be parsed + @param val the wxJSONValue object that contains the parsed text; if NULL the + parser do not store anything but errors and warnings are reported + @return the total number of errors encontered +*/ +int +wxJSONReader:: Parse( const wxString& doc, wxJSONValue* val ) +{ +#if !defined( wxJSON_USE_UNICODE ) + // in ANSI builds input from a string never use UTF-8 conversion + bool noUtf8_bak = m_noUtf8; // save the current setting + m_noUtf8 = true; +#endif + + // convert the string to a UTF-8 / ANSI memory stream and calls overloaded Parse() + char* readBuff = 0; + wxCharBuffer utf8CB = doc.ToUTF8(); // the UTF-8 buffer +#if !defined( wxJSON_USE_UNICODE ) + wxCharBuffer ansiCB( doc.c_str()); // the ANSI buffer + if ( m_noUtf8 ) { + readBuff = ansiCB.data(); + } + else { + readBuff = utf8CB.data(); + } +#else + readBuff = utf8CB.data(); +#endif + + // now construct the temporary memory input stream + size_t len = strlen( readBuff ); + wxMemoryInputStream is( readBuff, len ); + + int numErr = Parse( is, val ); +#if !defined( wxJSON_USE_UNICODE ) + m_noUtf8 = noUtf8_bak; +#endif + return numErr; +} + +//! \overload Parse( const wxString&, wxJSONValue* ) +int +wxJSONReader::Parse( wxInputStream& is, wxJSONValue* val ) +{ + // if val == 0 the 'temp' JSON value will be passed to DoRead() + wxJSONValue temp; + m_level = 0; + m_depth = 0; + m_lineNo = 1; + m_colNo = 1; + m_peekChar = -1; + m_errors.clear(); + m_warnings.clear(); + + // if a wxJSONValue is not passed to the Parse function + // we set the temparary object created on the stack + // I know this will slow down the validation of input + if ( val == 0 ) { + val = &temp; + } + wxASSERT( val ); + + // set the wxJSONValue object's pointers for comment storage + m_next = val; + m_next->SetLineNo( -1 ); + m_lastStored = 0; + m_current = 0; + + int ch = GetStart( is ); + switch ( ch ) { + case '{' : + val->SetType( wxJSONTYPE_OBJECT ); + break; + case '[' : + val->SetType( wxJSONTYPE_ARRAY ); + break; + default : + AddError( _T("Cannot find a start object/array character" )); + return m_errors.size(); + break; + } + + // returning from DoRead() could be for EOF or for + // the closing array-object character + // if -1 is returned, it is as an error because the lack + // of close-object/array characters + // note that the missing close-chars error messages are + // added by the DoRead() function + ch = DoRead( is, *val ); + return m_errors.size(); +} + + +//! Returns the start of the document +/*! + This is the first function called by the Parse() function and it searches + the input stream for the starting character of a JSON text and returns it. + JSON text start with '{' or '['. + If the two starting characters are inside a C/C++ comment, they + are ignored. + Returns the JSON-text start character or -1 on EOF. + + @param is the input stream that contains the JSON text + @return -1 on errors or EOF; one of '{' or '[' +*/ +int +wxJSONReader::GetStart( wxInputStream& is ) +{ + int ch = 0; + do { + switch ( ch ) { + case 0 : + ch = ReadChar( is ); + break; + case '{' : + return ch; + break; + case '[' : + return ch; + break; + case '/' : + ch = SkipComment( is ); + StoreComment( 0 ); + break; + default : + ch = ReadChar( is ); + break; + } + } while ( ch >= 0 ); + return ch; +} + +//! Return a reference to the error message's array. +const wxArrayString& +wxJSONReader::GetErrors() const +{ + return m_errors; +} + +//! Return a reference to the warning message's array. +const wxArrayString& +wxJSONReader::GetWarnings() const +{ + return m_warnings; +} + +//! Return the depth of the JSON input text +/*! + The function returns the number of times the recursive \c DoRead function was + called in the parsing process thus returning the maximum depth of the JSON + input text. +*/ +int +wxJSONReader::GetDepth() const +{ + return m_depth; +} + + + +//! Return the size of the error message's array. +int +wxJSONReader::GetErrorCount() const +{ + return m_errors.size(); +} + +//! Return the size of the warning message's array. +int +wxJSONReader::GetWarningCount() const +{ + return m_warnings.size(); +} + + +//! Read a character from the input JSON document. +/*! + The function returns the next byte from the UTF-8 stream as an INT. + In case of errors or EOF, the function returns -1. + The function also updates the \c m_lineNo and \c m_colNo data + members and converts all CR+LF sequence in LF. + + This function only returns one byte UTF-8 (one code unit) + at a time and not Unicode code points. + The only reason for this function is to process line and column + numbers. + + @param is the input stream that contains the JSON text + @return the next char (one single byte) in the input stream or -1 on error or EOF +*/ +int +wxJSONReader::ReadChar( wxInputStream& is ) +{ + if ( is.Eof()) { + return -1; + } + + unsigned char ch = is.GetC(); + size_t last = is.LastRead(); // returns ZERO if EOF + if ( last == 0 ) { + return -1; + } + + // the function also converts CR in LF. only LF is returned + // in the case of CR+LF + int nextChar; + + if ( ch == '\r' ) { + m_colNo = 1; + nextChar = PeekChar( is ); + if ( nextChar == -1 ) { + return -1; + } + else if ( nextChar == '\n' ) { + ch = is.GetC(); + } + } + if ( ch == '\n' ) { + ++m_lineNo; + m_colNo = 1; + } + else { + ++m_colNo; + } + return (int) ch; +} + + +//! Peek a character from the input JSON document +/*! + This function just calls the \b Peek() function on the stream + and returns it. + + @param is the input stream that contains the JSON text + @return the next char (one single byte) in the input stream or -1 on error or EOF +*/ +int +wxJSONReader::PeekChar( wxInputStream& is ) +{ + int ch = -1; unsigned char c; + if ( !is.Eof()) { + c = is.Peek(); + ch = c; + } + return ch; +} + + +//! Reads the JSON text document (internal use) +/*! + This is a recursive function that is called by \c Parse() + and by the \c DoRead() function itself when a new object / + array character is encontered. + The function returns when a EOF condition is encontered or + when the corresponding close-object / close-array char is encontered. + The function also increments the \c m_level + data member when it is entered and decrements it on return. + It also sets \c m_depth equal to \c m_level if \c m_depth is + less than \c m_level. + + The function is the heart of the wxJSON parser class but it is + also very easy to understand because JSON syntax is very + easy. + + Returns the last close-object/array character read or -1 on EOF. + + @param is the input stream that contains the JSON text + @param parent the JSON value object that is the parent of all subobjects + read by the function until the next close-object/array (for + the top-level \c DoRead function \c parent is the root JSON object) + @return one of close-array or close-object char or -1 on error or EOF +*/ +int +wxJSONReader::DoRead( wxInputStream& is, wxJSONValue& parent ) +{ + ++m_level; + if ( m_depth < m_level ) { + m_depth = m_level; + } + + // 'value' is the wxJSONValue structure that has to be + // read. Data read from the JSON text input is stored + // in the following object. + wxJSONValue value( wxJSONTYPE_INVALID ); + + // sets the pointers to the current, next and last-stored objects + // in order to determine the value to which a comment refers to + m_next = &value; + m_current = &parent; + m_current->SetLineNo( m_lineNo ); + m_lastStored = 0; + + // the 'key' string is stored from 'value' when a ':' is encontered + wxString key; + + // the character read: -1=EOF, 0=to be read + int ch=0; + + do { // we read until ch < 0 + switch ( ch ) { + case 0 : + ch = ReadChar( is ); + break; + case ' ' : + case '\t' : + case '\n' : + case '\r' : + ch = SkipWhiteSpace( is ); + break; + case -1 : // the EOF + break; + case '/' : + ch = SkipComment( is ); + StoreComment( &parent ); + break; + + case '{' : + if ( parent.IsObject() ) { + if ( key.empty() ) { + AddError( _T("\'{\' is not allowed here (\'name\' is missing") ); + } + if ( value.IsValid() ) { + AddError( _T("\'{\' cannot follow a \'value\'") ); + } + } + else if ( parent.IsArray() ) { + if ( value.IsValid() ) { + AddError( _T("\'{\' cannot follow a \'value\' in JSON array") ); + } + } + else { + wxJSON_ASSERT( 0 ); // always fails + } + + // the openobject char cause the DoRead() to be called recursively + value.SetType( wxJSONTYPE_OBJECT ); + ch = DoRead( is, value ); + break; + + case '}' : + if ( !parent.IsObject() ) { + AddWarning( wxJSONREADER_MISSING, + _T("Trying to close an array using the \'}\' (close-object) char" )); + } + // close-object: store the current value, if any + StoreValue( ch, key, value, parent ); + m_current = &parent; + m_next = 0; + m_current->SetLineNo( m_lineNo ); + ch = ReadChar( is ); + return ch; + break; + + case '[' : + if ( parent.IsObject() ) { + if ( key.empty() ) { + AddError( _T("\'[\' is not allowed here (\'name\' is missing") ); + } + if ( value.IsValid() ) { + AddError( _T("\'[\' cannot follow a \'value\' text") ); + } + } + else if ( parent.IsArray()) { + if ( value.IsValid() ) { + AddError( _T("\'[\' cannot follow a \'value\'") ); + } + } + else { + wxJSON_ASSERT( 0 ); // always fails + } + // open-array cause the DoRead() to be called recursively + value.SetType( wxJSONTYPE_ARRAY ); + ch = DoRead( is, value ); + break; + + case ']' : + if ( !parent.IsArray() ) { + // wrong close-array char (should be close-object) + AddWarning( wxJSONREADER_MISSING, + _T("Trying to close an object using the \']\' (close-array) char" )); + } + StoreValue( ch, key, value, parent ); + m_current = &parent; + m_next = 0; + m_current->SetLineNo( m_lineNo ); + return 0; // returning ZERO for reading the next char + break; + + case ',' : + // store the value, if any + StoreValue( ch, key, value, parent ); + key.clear(); + ch = ReadChar( is ); + break; + + case '\"' : + ch = ReadString( is, value ); // read a JSON string type + m_current = &value; + m_next = 0; + break; + + case '\'' : + ch = ReadMemoryBuff( is, value ); // read a memory buffer type + m_current = &value; + m_next = 0; + break; + + case ':' : // key / value separator + m_current = &value; + m_current->SetLineNo( m_lineNo ); + m_next = 0; + if ( !parent.IsObject() ) { + AddError( _T( "\':\' can only used in object's values" )); + } + else if ( !value.IsString() ) { + AddError( _T( "\':\' follows a value which is not of type \'string\'" )); + } + else if ( !key.empty() ) { + AddError( _T( "\':\' not allowed where a \'name\' string was already available" )); + } + else { + // the string in 'value' is set as the 'key' + key = value.AsString(); + value.SetType( wxJSONTYPE_INVALID ); + } + ch = ReadChar( is ); + break; + + default : + // no special char: it is a literal or a number + // errors are checked in the 'ReadValue()' function. + m_current = &value; + m_current->SetLineNo( m_lineNo ); + m_next = 0; + ch = ReadValue( is, ch, value ); + break; + } // end switch + } while ( ch >= 0 ); + + // the DoRead() should return when the close-object/array char is encontered + // if we are here, the EOF condition was encontered so one or more close-something + // characters are missing + if ( parent.IsArray() ) { + AddWarning( wxJSONREADER_MISSING, _T("\']\' missing at end of file")); + } + else if ( parent.IsObject() ) { + AddWarning( wxJSONREADER_MISSING, _T("\'}\' missing at end of file")); + } + else { + wxJSON_ASSERT( 0 ); + } + + // we store the value, as there is a missing close-object/array char + StoreValue( ch, key, value, parent ); + + --m_level; + return ch; +} + +//! Store a value in the parent object. +/*! + The function is called by \c DoRead() when a the comma + or a close-object/array character is encontered and stores the current + value read by the parser in the parent object. + The function checks that \c value is not invalid and that \c key is + not an empty string if \c parent is an object. + + \param ch the character read: a comma or close objecty/array char + \param key the \b key string: must be empty if \c parent is an array + \param value the current JSON value to be stored in \c parent + \param parent the JSON value that is the parent of \c value. + \return none +*/ +void +wxJSONReader::StoreValue( int ch, const wxString& key, wxJSONValue& value, wxJSONValue& parent ) +{ + // if 'ch' == } or ] than value AND key may be empty when a open object/array + // is immediatly followed by a close object/array + // + // if 'ch' == , (comma) value AND key (for TypeMap) cannot be empty + // + wxLogTrace( traceMask, _T("(%s) ch=%d char=%c"), __PRETTY_FUNCTION__, ch, (char) ch); + wxLogTrace( traceMask, _T("(%s) value=%s"), __PRETTY_FUNCTION__, value.AsString().c_str()); + + m_current = 0; + m_next = &value; + m_lastStored = 0; + m_next->SetLineNo( -1 ); + + if ( !value.IsValid() && key.empty() ) { + // OK, if the char read is a close-object or close-array + if ( ch == '}' || ch == ']' ) { + m_lastStored = 0; + wxLogTrace( traceMask, _T("(%s) key and value are empty, returning"), + __PRETTY_FUNCTION__); + } + else { + AddError( _T("key or value is missing for JSON value")); + } + } + else { + // key or value are not empty + if ( parent.IsObject() ) { + if ( !value.IsValid() ) { + AddError( _T("cannot store the value: \'value\' is missing for JSON object type")); + } + else if ( key.empty() ) { + AddError( _T("cannot store the value: \'key\' is missing for JSON object type")); + } + else { + // OK, adding the value to parent key/value map + wxLogTrace( traceMask, _T("(%s) adding value to key:%s"), + __PRETTY_FUNCTION__, key.c_str()); + parent[key] = value; + m_lastStored = &(parent[key]); + m_lastStored->SetLineNo( m_lineNo ); + } + } + else if ( parent.IsArray() ) { + if ( !value.IsValid() ) { + AddError( _T("cannot store the item: \'value\' is missing for JSON array type")); + } + if ( !key.empty() ) { + AddError( _T("cannot store the item: \'key\' (\'%s\') is not permitted in JSON array type"), key); + } + wxLogTrace( traceMask, _T("(%s) appending value to parent array"), + __PRETTY_FUNCTION__ ); + parent.Append( value ); + const wxJSONInternalArray* arr = parent.AsArray(); + wxJSON_ASSERT( arr ); + m_lastStored = &(arr->Last()); + m_lastStored->SetLineNo( m_lineNo ); + } + else { + wxJSON_ASSERT( 0 ); // should never happen + } + } + value.SetType( wxJSONTYPE_INVALID ); + value.ClearComments(); +} + +//! Add a error message to the error's array +/*! + The overloaded versions of this function add an error message to the + error's array stored in \c m_errors. + The error message is formatted as follows: + + \code + Error: line xxx, col xxx - + \endcode + + The \c msg parameter is the description of the error; line's and column's + number are automatically added by the functions. + The \c fmt parameter is a format string that has the same syntax as the \b printf + function. + Note that it is the user's responsability to provide a format string suitable + with the arguments: another string or a character. +*/ +void +wxJSONReader::AddError( const wxString& msg ) +{ + wxString err; + err.Printf( _T("Error: line %d, col %d - %s"), m_lineNo, m_colNo, msg.c_str() ); + + wxLogTrace( traceMask, _T("(%s) %s"), __PRETTY_FUNCTION__, err.c_str()); + + if ( (int) m_errors.size() < m_maxErrors ) { + m_errors.Add( err ); + } + else if ( (int) m_errors.size() == m_maxErrors ) { + m_errors.Add( _T("ERROR: too many error messages - ignoring further errors")); + } + // else if ( m_errors > m_maxErrors ) do nothing, thus ignore the error message +} + +//! \overload AddError( const wxString& ) +void +wxJSONReader::AddError( const wxString& fmt, const wxString& str ) +{ + wxString s; + s.Printf( fmt.c_str(), str.c_str() ); + AddError( s ); +} + +//! \overload AddError( const wxString& ) +void +wxJSONReader::AddError( const wxString& fmt, wxChar c ) +{ + wxString s; + s.Printf( fmt.c_str(), c ); + AddError( s ); +} + +//! Add a warning message to the warning's array +/*! + The warning description is as follows: + \code + Warning: line xxx, col xxx - + \endcode + + Warning messages are generated by the parser when the JSON + text that has been read is not well-formed but the + error is not fatal and the parser recognizes the text + as an extension to the JSON standard (see the parser's ctor + for more info about wxJSON extensions). + + Note that the parser has to be constructed with a flag that + indicates if each individual wxJSON extension is on. + If the warning message is related to an extension that is not + enabled in the parser's \c m_flag data member, this function + calls AddError() and the warning message becomes an error + message. + The \c type parameter is one of the same constants that + specify the parser's extensions. + If type is ZERO than the function always adds a warning +*/ +void +wxJSONReader::AddWarning( int type, const wxString& msg ) +{ + // if 'type' AND 'm_flags' == 1 than the extension is + // ON. Otherwise it is OFF anf the function calls AddError() + if ( type != 0 ) { + if ( ( type & m_flags ) == 0 ) { + AddError( msg ); + return; + } + } + + wxString err; + err.Printf( _T( "Warning: line %d, col %d - %s"), m_lineNo, m_colNo, msg.c_str() ); + + wxLogTrace( traceMask, _T("(%s) %s"), __PRETTY_FUNCTION__, err.c_str()); + if ( (int) m_warnings.size() < m_maxErrors ) { + m_warnings.Add( err ); + } + else if ( (int) m_warnings.size() == m_maxErrors ) { + m_warnings.Add( _T("Error: too many warning messages - ignoring further warnings")); + } + // else do nothing, thus ignore the warning message +} + +//! Skip all whitespaces. +/*! + The function reads characters from the input text + and returns the first non-whitespace character read or -1 + if EOF. + Note that the function does not rely on the \b isspace function + of the C library but checks the space constants: space, TAB and + LF. +*/ +int +wxJSONReader::SkipWhiteSpace( wxInputStream& is ) +{ + // just read one byte at a time and check for whitespaces + int ch; + do { + ch = ReadChar( is ); + if ( ch < 0 ) { + break; + } + } + while ( ch == ' ' || ch == '\n' || ch == '\t' ); + wxLogTrace( traceMask, _T("(%s) end whitespaces line=%d col=%d"), + __PRETTY_FUNCTION__, m_lineNo, m_colNo ); + return ch; +} + +//! Skip a comment +/*! + The function is called by DoRead() when a '/' (slash) character + is read from the input stream assuming that a C/C++ comment is starting. + Returns the first character that follows the comment or + -1 on EOF. + The function also adds a warning message because comments are not + valid JSON text. + The function also stores the comment, if any, in the \c m_comment data + member: it can be used by the DoRead() function if comments have to be + stored in the value they refer to. +*/ +int +wxJSONReader::SkipComment( wxInputStream& is ) +{ + static const wxChar* warn = + _T("Comments may be tolerated in JSON text but they are not part of JSON syntax"); + + // if it is a comment, then a warning is added to the array + // otherwise it is an error: values cannot start with a '/' + // read the char next to the first slash + int ch = ReadChar( is ); + if ( ch < 0 ) { + return -1; + } + + wxLogTrace( storeTraceMask, _T("(%s) start comment line=%d col=%d"), + __PRETTY_FUNCTION__, m_lineNo, m_colNo ); + + // the temporary UTF-8/ANSI buffer that holds the comment string. This will be + // converted to a wxString object using wxString::FromUTF8() or From8BitData() + wxMemoryBuffer utf8Buff; + unsigned char c; + + if ( ch == '/' ) { // C++ comment, read until end-of-line + // C++ comment strings are in UTF-8 format. we store all + // UTF-8 code units until the first LF or CR+LF + AddWarning( wxJSONREADER_ALLOW_COMMENTS, warn ); + m_commentLine = m_lineNo; + utf8Buff.AppendData( "//", 2 ); + + while ( ch >= 0 ) { + if ( ch == '\n' ) { + break; + } + if ( ch == '\r' ) { + ch = PeekChar( is ); + if ( ch == '\n' ) { + ch = ReadChar( is ); + } + break; + } + else { + // store the char in the UTF8 temporary buffer + c = (unsigned char) ch; + utf8Buff.AppendByte( c ); + } + ch = ReadChar( is ); + } + // now convert the temporary UTF-8 buffer + m_comment = wxString::FromUTF8( (const char*) utf8Buff.GetData(), + utf8Buff.GetDataLen()); + } + + // check if a C-style comment + else if ( ch == '*' ) { // C-style comment + AddWarning(wxJSONREADER_ALLOW_COMMENTS, warn ); + m_commentLine = m_lineNo; + utf8Buff.AppendData( "/*", 2 ); + while ( ch >= 0 ) { + // check the END-COMMENT chars ('*/') + if ( ch == '*' ) { + ch = PeekChar( is ); + if ( ch == '/' ) { + ch = ReadChar( is ); // read the '/' char + ch = ReadChar( is ); // read the next char that will be returned + utf8Buff.AppendData( "*/", 2 ); + break; + } + } + // store the char in the UTF8 temporary buffer + c = (unsigned char) ch; + utf8Buff.AppendByte( c ); + ch = ReadChar( is ); + } + // now convert the temporary buffer in a wxString object + if ( m_noUtf8 ) { + m_comment = wxString::From8BitData( (const char*) utf8Buff.GetData(), + utf8Buff.GetDataLen()); + } + else { + m_comment = wxString::FromUTF8( (const char*) utf8Buff.GetData(), + utf8Buff.GetDataLen()); + } + } + + else { // it is not a comment, return the character next the first '/' + AddError( _T( "Strange '/' (did you want to insert a comment?)")); + // we read until end-of-line OR end of C-style comment OR EOF + // because a '/' should be a start comment + while ( ch >= 0 ) { + ch = ReadChar( is ); + if ( ch == '*' && PeekChar( is ) == '/' ) { + break; + } + if ( ch == '\n' ) { + break; + } + } + // read the next char that will be returned + ch = ReadChar( is ); + } + wxLogTrace( traceMask, _T("(%s) end comment line=%d col=%d"), + __PRETTY_FUNCTION__, m_lineNo, m_colNo ); + wxLogTrace( storeTraceMask, _T("(%s) end comment line=%d col=%d"), + __PRETTY_FUNCTION__, m_lineNo, m_colNo ); + wxLogTrace( storeTraceMask, _T("(%s) comment=%s"), + __PRETTY_FUNCTION__, m_comment.c_str()); + return ch; +} + +//! Read a string value +/*! + The function reads a string value from input stream and it is + called by the \c DoRead() function when it enconters the + double quote characters. + The function read all bytes up to the next double quotes + (unless it is escaped) and stores them in a temporary UTF-8 + memory buffer. + Also, the function processes the escaped characters defined + in the JSON syntax. + + Next, the function tries to convert the UTF-8 buffer to a + \b wxString object using the \b wxString::FromUTF8 function. + Depending on the build mode, we can have the following: + \li in Unicode the function always succeeds, provided that the + buffer contains valid UTF-8 code units. + + \li in ANSI builds the conversion may fail because of the presence of + unrepresentable characters in the current locale. In this case, + the default behaviour is to perform a char-by-char conversion; every + char that cannot be represented in the current locale is stored as + \e unicode \e escaped \e sequence + + \li in ANSI builds, if the reader is constructed with the wxJSONREADER_NOUTF8_STREAM + then no conversion takes place and the UTF-8 temporary buffer is simply + \b copied to the \b wxString object + + The string is, finally, stored in the provided wxJSONValue argument + provided that it is empty or it contains a string value. + This is because the parser class recognizes multi-line strings + like the following one: + \code + [ + "This is a very long string value which is splitted into more" + "than one line because it is more human readable" + ] + \endcode + Because of the lack of the value separator (,) the parser + assumes that the string was splitted into several double-quoted + strings. + If the value does not contain a string then an error is + reported. + Splitted strings cause the parser to report a warning. +*/ +int +wxJSONReader::ReadString( wxInputStream& is, wxJSONValue& val ) +{ + // the char last read is the opening qoutes (") + + wxMemoryBuffer utf8Buff; + char ues[8]; // stores a Unicode Escaped Esquence: \uXXXX + + int ch = 0; + while ( ch >= 0 ) { + ch = ReadChar( is ); + unsigned char c = (unsigned char) ch; + if ( ch == '\\' ) { // an escape sequence + ch = ReadChar( is ); + switch ( ch ) { + case -1 : // EOF + break; + case 't' : + utf8Buff.AppendByte( '\t' ); + break; + case 'n' : + utf8Buff.AppendByte( '\n' ); + break; + case 'b' : + utf8Buff.AppendByte( '\b' ); + break; + case 'r' : + utf8Buff.AppendByte( '\r' ); + break; + case '\"' : + utf8Buff.AppendByte( '\"' ); + break; + case '\\' : + utf8Buff.AppendByte( '\\' ); + break; + case '/' : + utf8Buff.AppendByte( '/' ); + break; + case 'f' : + utf8Buff.AppendByte( '\f' ); + break; + case 'u' : + ch = ReadUES( is, ues ); + if ( ch < 0 ) { // if EOF, returns + return ch; + } + // append the escaped character to the UTF8 buffer + AppendUES( utf8Buff, ues ); + // many thanks to Bryan Ashby who discovered this bug + continue; + // break; + default : + AddError( _T( "Unknow escaped character \'\\%c\'"), ch ); + } + } + else { + // we have read a non-escaped character so we have to append it to + // the temporary UTF-8 buffer until the next quote char + if ( ch == '\"' ) { + break; + } + utf8Buff.AppendByte( c ); + } + } + + // if UTF-8 conversion is disabled (ANSI builds only) we just copy the + // bit data to a wxString object + wxString s; + if ( m_noUtf8 ) { + s = wxString::From8BitData( (const char*) utf8Buff.GetData(), utf8Buff.GetDataLen()); + } + else { + // perform UTF-8 conversion + // first we check that the UTF-8 buffer is correct, i.e. it contains valid + // UTF-8 code points. + // this works in both ANSI and Unicode builds. + size_t convLen = wxConvUTF8.ToWChar( 0, // wchar_t destination + 0, // size_t destLenght + (const char*) utf8Buff.GetData(), // char_t source + utf8Buff.GetDataLen()); // size_t sourceLenght + + if ( convLen == wxCONV_FAILED ) { + AddError( _T( "String value: the UTF-8 stream is invalid")); + s.append( _T( "")); + } + else { +#if defined( wxJSON_USE_UNICODE ) + // in Unicode just convert to wxString + s = wxString::FromUTF8( (const char*) utf8Buff.GetData(), utf8Buff.GetDataLen()); +#else + // in ANSI, the conversion may fail and an empty string is returned + // in this case, the reader do a char-by-char conversion storing + // unicode escaped sequences of unrepresentable characters + s = wxString::FromUTF8( (const char*) utf8Buff.GetData(), utf8Buff.GetDataLen()); + if ( s.IsEmpty() ) { + int r = ConvertCharByChar( s, utf8Buff ); // return number of escaped sequences + if ( r > 0 ) { + AddWarning( 0, _T( "The string value contains unrepresentable Unicode characters")); + } + } +#endif + } + } + wxLogTrace( traceMask, _T("(%s) line=%d col=%d"), + __PRETTY_FUNCTION__, m_lineNo, m_colNo ); + wxLogTrace( traceMask, _T("(%s) string read=%s"), + __PRETTY_FUNCTION__, s.c_str() ); + wxLogTrace( traceMask, _T("(%s) value=%s"), + __PRETTY_FUNCTION__, val.AsString().c_str() ); + + // now assign the string to the JSON-value 'value' + // must check that: + // 'value' is empty + // 'value' is a string; concatenate it but emit warning + if ( !val.IsValid() ) { + wxLogTrace( traceMask, _T("(%s) assigning the string to value"), __PRETTY_FUNCTION__ ); + val = s ; + } + else if ( val.IsString() ) { + AddWarning( wxJSONREADER_MULTISTRING, + _T("Multiline strings are not allowed by JSON syntax") ); + wxLogTrace( traceMask, _T("(%s) concatenate the string to value"), __PRETTY_FUNCTION__ ); + val.Cat( s ); + } + else { + AddError( _T( "String value \'%s\' cannot follow another value"), s ); + } + + // store the input text's line number when the string was stored in 'val' + val.SetLineNo( m_lineNo ); + + // read the next char after the closing quotes and returns it + if ( ch >= 0 ) { + ch = ReadChar( is ); + } + return ch; +} + +//! Reads a token string +/*! + This function is called by the ReadValue() when the + first character encontered is not a special char + and it is not a double-quote. + The only possible type is a literal or a number which + all lies in the US-ASCII charset so their UTF-8 encodeing + is the same as US-ASCII. + The function simply reads one byte at a time from the stream + and appends them to a \b wxString object. + Returns the next character read. + + A token cannot include \e unicode \e escaped \e sequences + so this function does not try to interpret such sequences. + + @param is the input stream + @param ch the character read by DoRead + @param s the string object that contains the token read + @return -1 in case of errors or EOF +*/ +int +wxJSONReader::ReadToken( wxInputStream& is, int ch, wxString& s ) +{ + int nextCh = ch; + while ( nextCh >= 0 ) { + switch ( nextCh ) { + case ' ' : + case ',' : + case ':' : + case '[' : + case ']' : + case '{' : + case '}' : + case '\t' : + case '\n' : + case '\r' : + case '\b' : + wxLogTrace( traceMask, _T("(%s) line=%d col=%d"), + __PRETTY_FUNCTION__, m_lineNo, m_colNo ); + wxLogTrace( traceMask, _T("(%s) token read=%s"), + __PRETTY_FUNCTION__, s.c_str() ); + return nextCh; + break; + default : + s.Append( (unsigned char) nextCh, 1 ); + break; + } + // read the next character + nextCh = ReadChar( is ); + } + wxLogTrace( traceMask, _T("(%s) EOF on line=%d col=%d"), + __PRETTY_FUNCTION__, m_lineNo, m_colNo ); + wxLogTrace( traceMask, _T("(%s) EOF - token read=%s"), + __PRETTY_FUNCTION__, s.c_str() ); + return nextCh; +} + +//! Read a value from input stream +/*! + The function is called by DoRead() when it enconters a char that is + not a special char nor a double-quote. + It assumes that the string is a numeric value or a literal + boolean value and stores it in the wxJSONValue object \c val. + + The function also checks that \c val is of type wxJSONTYPE_INVALID otherwise + an error is reported becasue a value cannot follow another value: + maybe a (,) or (:) is missing. + + If the literal starts with a digit, a plus or minus sign, the function + tries to interpret it as a number. The following are tried by the function, + in this order: + + \li if the literal starts with a digit: signed integer, then unsigned integer + and finally double conversion is tried + \li if the literal starts with a minus sign: signed integer, then double + conversion is tried + \li if the literal starts with plus sign: unsigned integer + then double conversion is tried + + Returns the next character or -1 on EOF. +*/ +int +wxJSONReader::ReadValue( wxInputStream& is, int ch, wxJSONValue& val ) +{ + wxString s; + int nextCh = ReadToken( is, ch, s ); + wxLogTrace( traceMask, _T("(%s) value=%s"), + __PRETTY_FUNCTION__, val.AsString().c_str() ); + + if ( val.IsValid() ) { + AddError( _T( "Value \'%s\' cannot follow a value: \',\' or \':\' missing?"), s ); + return nextCh; + } + + // variables used for converting numeric values + bool r; double d; +#if defined( wxJSON_64BIT_INT ) + wxInt64 i64; + wxUint64 ui64; +#else + unsigned long int ul; long int l; +#endif + + // first try the literal strings lowercase and nocase + if ( s == _T("null") ) { + val.SetType( wxJSONTYPE_NULL ); + wxLogTrace( traceMask, _T("(%s) value = NULL"), __PRETTY_FUNCTION__ ); + return nextCh; + } + else if ( s.CmpNoCase( _T( "null" )) == 0 ) { + wxLogTrace( traceMask, _T("(%s) value = NULL"), __PRETTY_FUNCTION__ ); + AddWarning( wxJSONREADER_CASE, _T( "the \'null\' literal must be lowercase" )); + val.SetType( wxJSONTYPE_NULL ); + return nextCh; + } + else if ( s == _T("true") ) { + wxLogTrace( traceMask, _T("(%s) value = TRUE"), __PRETTY_FUNCTION__ ); + val = true; + return nextCh; + } + else if ( s.CmpNoCase( _T( "true" )) == 0 ) { + wxLogTrace( traceMask, _T("(%s) value = TRUE"), __PRETTY_FUNCTION__ ); + AddWarning( wxJSONREADER_CASE, _T( "the \'true\' literal must be lowercase" )); + val = true; + return nextCh; + } + else if ( s == _T("false") ) { + wxLogTrace( traceMask, _T("(%s) value = FALSE"), __PRETTY_FUNCTION__ ); + val = false; + return nextCh; + } + else if ( s.CmpNoCase( _T( "false" )) == 0 ) { + wxLogTrace( traceMask, _T("(%s) value = FALSE"), __PRETTY_FUNCTION__ ); + AddWarning( wxJSONREADER_CASE, _T( "the \'false\' literal must be lowercase" )); + val = false; + return nextCh; + } + + + // try to convert to a number if the token starts with a digit, a plus or a minus + // sign. The function first states what type of conversion are tested: + // 1. first signed integer (not if 'ch' == '+') + // 2. unsigned integer (not if 'ch' == '-') + // 3. finally double + bool tSigned = true, tUnsigned = true, tDouble = true; + switch ( ch ) { + case '0' : + case '1' : + case '2' : + case '3' : + case '4' : + case '5' : + case '6' : + case '7' : + case '8' : + case '9' : + // first try a signed integer, then a unsigned integer, then a double + break; + + case '+' : + // the plus sign forces a unsigned integer + tSigned = false; + break; + + case '-' : + // try signed and double + tUnsigned = false; + break; + default : + AddError( _T( "Literal \'%s\' is incorrect (did you forget quotes?)"), s ); + return nextCh; + } + + if ( tSigned ) { + #if defined( wxJSON_64BIT_INT) + r = Strtoll( s, &i64 ); + wxLogTrace( traceMask, _T("(%s) convert to wxInt64 result=%d"), + __PRETTY_FUNCTION__, r ); + if ( r ) { + // store the value + val = i64; + return nextCh; + } + #else + r = s.ToLong( &l ); + wxLogTrace( traceMask, _T("(%s) convert to int result=%d"), + __PRETTY_FUNCTION__, r ); + if ( r ) { + // store the value + val = (int) l; + return nextCh; + } + #endif + } + + if ( tUnsigned ) { + #if defined( wxJSON_64BIT_INT) + r = Strtoull( s, &ui64 ); + wxLogTrace( traceMask, _T("(%s) convert to wxUint64 result=%d"), + __PRETTY_FUNCTION__, r ); + if ( r ) { + // store the value + val = ui64; + return nextCh; + } + #else + r = s.ToULong( &ul ); + wxLogTrace( traceMask, _T("(%s) convert to int result=%d"), + __PRETTY_FUNCTION__, r ); + if ( r ) { + // store the value + val = (unsigned int) ul; + return nextCh; + } + #endif + } + + if ( tDouble ) { + r = s.ToDouble( &d ); + wxLogTrace( traceMask, _T("(%s) convert to double result=%d"), + __PRETTY_FUNCTION__, r ); + if ( r ) { + // store the value + val = d; + return nextCh; + } + } + + + // the value is not syntactically correct + AddError( _T( "Literal \'%s\' is incorrect (did you forget quotes?)"), s ); + return nextCh; + return nextCh; +} + + +//! Read a 4-hex-digit unicode character. +/*! + The function is called by ReadString() when the \b \\u sequence is + encontered; the sequence introduces a control character in the form: + \code + \uXXXX + \endcode + where XXXX is a four-digit hex code.. + The function reads four chars from the input UTF8 stream by calling ReadChar() + four times: if EOF is encontered before reading four chars, -1 is + also returned and no sequence interpretation is performed. + The function stores the 4 hexadecimal digits in the \c uesBuffer parameter. + + Returns the character after the hex sequence or -1 if EOF. + + \b NOTICE: although the JSON syntax states that only control characters + are represented in this way, the wxJSON library reads and recognizes all + unicode characters in the BMP. +*/ +int +wxJSONReader::ReadUES( wxInputStream& is, char* uesBuffer ) +{ + int ch; + for ( int i = 0; i < 4; i++ ) { + ch = ReadChar( is ); + if ( ch < 0 ) { + return ch; + } + uesBuffer[i] = (unsigned char) ch; + } + uesBuffer[4] = 0; // makes a ASCIIZ string + + return 0; +} + + +//! The function appends a Unice Escaped Sequence to the temporary UTF8 buffer +/*! + This function is called by \c ReadString() when a \e unicode \e escaped + \e sequence is read from the input text as for example: + + \code + \u0001 + \endcode + + which represents a control character. + The \c uesBuffer parameter contains the 4 hexadecimal digits that are + read from \c ReadUES. + + The function tries to convert the 4 hex digits in a \b wchar_t character + which is appended to the memory buffer \c utf8Buff after converting it + to UTF-8. + + If the conversion from hexadecimal fails, the function does not + store the character in the UTF-8 buffer and an error is reported. + The function is the same in ANSI and Unicode. + Returns -1 if the buffer does not contain valid hex digits. + sequence. On success returns ZERO. + + @param utf8Buff the UTF-8 buffer to which the control char is written + @param uesBuffer the four-hex-digits read from the input text + @return ZERO on success, -1 if the four-hex-digit buffer cannot be converted +*/ +int +wxJSONReader::AppendUES( wxMemoryBuffer& utf8Buff, const char* uesBuffer ) +{ + unsigned long l; + int r = sscanf( uesBuffer, "%lx", &l ); // r is the assigned items + if ( r != 1 ) { + AddError( _T( "Invalid Unicode Escaped Sequence")); + return -1; + } + wxLogTrace( traceMask, _T("(%s) unicode sequence=%s code=%ld"), + __PRETTY_FUNCTION__, uesBuffer, l ); + + wchar_t ch = (wchar_t) l; + char buffer[16]; + size_t len = wxConvUTF8.FromWChar( buffer, 10, &ch, 1 ); + + // seems that the wxMBConv classes always appends a NULL byte to + // the converted buffer + //if ( len > 1 ) { + // len = len - 1; + //} + utf8Buff.AppendData( buffer, len ); + + // sould never fail + wxASSERT( len != wxCONV_FAILED ); + return 0; +} + +//! Store the comment string in the value it refers to. +/*! + The function searches a suitable value object for storing the + comment line that was read by the parser and temporarly + stored in \c m_comment. + The function searches the three values pointed to by: + \li \c m_next + \li \c m_current + \li \c m_lastStored + + The value that the comment refers to is: + + \li if the comment is on the same line as one of the values, the comment + refer to that value and it is stored as \b inline. + \li otherwise, if the comment flag is wxJSONREADER_COMMENTS_BEFORE, the comment lines + are stored in the value pointed to by \c m_next + \li otherwise, if the comment flag is wxJSONREADER_COMMENTS_AFTER, the comment lines + are stored in the value pointed to by \c m_current or m_latStored + + Note that the comment line is only stored if the wxJSONREADER_STORE_COMMENTS + flag was used when the parser object was constructed; otherwise, the + function does nothing and immediatly returns. + Also note that if the comment line has to be stored but the + function cannot find a suitable value to add the comment line to, + an error is reported (note: not a warning but an error). +*/ +void +wxJSONReader::StoreComment( const wxJSONValue* parent ) +{ + wxLogTrace( storeTraceMask, _T("(%s) m_comment=%s"), __PRETTY_FUNCTION__, m_comment.c_str()); + wxLogTrace( storeTraceMask, _T("(%s) m_flags=%d m_commentLine=%d"), + __PRETTY_FUNCTION__, m_flags, m_commentLine ); + wxLogTrace( storeTraceMask, _T("(%s) m_current=%p"), __PRETTY_FUNCTION__, m_current ); + wxLogTrace( storeTraceMask, _T("(%s) m_next=%p"), __PRETTY_FUNCTION__, m_next ); + wxLogTrace( storeTraceMask, _T("(%s) m_lastStored=%p"), __PRETTY_FUNCTION__, m_lastStored ); + + // first check if the 'store comment' bit is on + if ( (m_flags & wxJSONREADER_STORE_COMMENTS) == 0 ) { + m_comment.clear(); + return; + } + + // check if the comment is on the same line of one of the + // 'current', 'next' or 'lastStored' value + if ( m_current != 0 ) { + wxLogTrace( storeTraceMask, _T("(%s) m_current->lineNo=%d"), + __PRETTY_FUNCTION__, m_current->GetLineNo() ); + if ( m_current->GetLineNo() == m_commentLine ) { + wxLogTrace( storeTraceMask, _T("(%s) comment added to \'m_current\' INLINE"), + __PRETTY_FUNCTION__ ); + m_current->AddComment( m_comment, wxJSONVALUE_COMMENT_INLINE ); + m_comment.clear(); + return; + } + } + if ( m_next != 0 ) { + wxLogTrace( storeTraceMask, _T("(%s) m_next->lineNo=%d"), + __PRETTY_FUNCTION__, m_next->GetLineNo() ); + if ( m_next->GetLineNo() == m_commentLine ) { + wxLogTrace( storeTraceMask, _T("(%s) comment added to \'m_next\' INLINE"), + __PRETTY_FUNCTION__ ); + m_next->AddComment( m_comment, wxJSONVALUE_COMMENT_INLINE ); + m_comment.clear(); + return; + } + } + if ( m_lastStored != 0 ) { + wxLogTrace( storeTraceMask, _T("(%s) m_lastStored->lineNo=%d"), + __PRETTY_FUNCTION__, m_lastStored->GetLineNo() ); + if ( m_lastStored->GetLineNo() == m_commentLine ) { + wxLogTrace( storeTraceMask, _T("(%s) comment added to \'m_lastStored\' INLINE"), + __PRETTY_FUNCTION__ ); + m_lastStored->AddComment( m_comment, wxJSONVALUE_COMMENT_INLINE ); + m_comment.clear(); + return; + } + } + + // if comment is BEFORE, store the comment in the 'm_next' + // or 'm_current' value + // if comment is AFTER, store the comment in the 'm_lastStored' + // or 'm_current' value + + if ( m_flags & wxJSONREADER_COMMENTS_AFTER ) { // comment AFTER + if ( m_current ) { + if ( m_current == parent || !m_current->IsValid()) { + AddError( _T("Cannot find a value for storing the comment (flag AFTER)")); + } + else { + wxLogTrace( storeTraceMask, _T("(%s) comment added to m_current (AFTER)"), + __PRETTY_FUNCTION__ ); + m_current->AddComment( m_comment, wxJSONVALUE_COMMENT_AFTER ); + } + } + else if ( m_lastStored ) { + wxLogTrace( storeTraceMask, _T("(%s) comment added to m_lastStored (AFTER)"), + __PRETTY_FUNCTION__ ); + m_lastStored->AddComment( m_comment, wxJSONVALUE_COMMENT_AFTER ); + } + else { + wxLogTrace( storeTraceMask, + _T("(%s) cannot find a value for storing the AFTER comment"), __PRETTY_FUNCTION__ ); + AddError(_T("Cannot find a value for storing the comment (flag AFTER)")); + } + } + else { // comment BEFORE can only be added to the 'next' value + if ( m_next ) { + wxLogTrace( storeTraceMask, _T("(%s) comment added to m_next (BEFORE)"), + __PRETTY_FUNCTION__ ); + m_next->AddComment( m_comment, wxJSONVALUE_COMMENT_BEFORE ); + } + else { + // cannot find a value for storing the comment + AddError(_T("Cannot find a value for storing the comment (flag BEFORE)")); + } + } + m_comment.clear(); +} + + +//! Return the number of bytes that make a character in stream input +/*! + This function returns the number of bytes that represent a unicode + code point in various encoding. + For example, if the input stream is UTF-32 the function returns 4. + Because the only recognized format for streams is UTF-8 the function + just calls UTF8NumBytes() and returns. + The function is, actually, not used at all. + +*/ +int +wxJSONReader::NumBytes( char ch ) +{ + int n = UTF8NumBytes( ch ); + return n; +} + +//! Compute the number of bytes that makes a UTF-8 encoded wide character. +/*! + The function counts the number of '1' bit in the character \c ch and + returns it. + The UTF-8 encoding specifies the number of bytes needed by a wide character + by coding it in the first byte. See below. + + Note that if the character does not contain a valid UTF-8 encoding + the function returns -1. + +\code + UCS-4 range (hex.) UTF-8 octet sequence (binary) + ------------------- ----------------------------- + 0000 0000-0000 007F 0xxxxxxx + 0000 0080-0000 07FF 110xxxxx 10xxxxxx + 0000 0800-0000 FFFF 1110xxxx 10xxxxxx 10xxxxxx + 0001 0000-001F FFFF 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx + 0020 0000-03FF FFFF 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx + 0400 0000-7FFF FFFF 1111110x 10xxxxxx ... 10xxxxxx +\endcode +*/ +int +wxJSONReader::UTF8NumBytes( char ch ) +{ + int num = 0; // the counter of '1' bits + for ( int i = 0; i < 8; i++ ) { + if ( (ch & 0x80) == 0 ) { + break; + } + ++num; + ch = ch << 1; + } + + // note that if the char contains more than six '1' bits it is not + // a valid UTF-8 encoded character + if ( num > 6 ) { + num = -1; + } + else if ( num == 0 ) { + num = 1; + } + return num; +} + +//! Convert a UTF-8 memory buffer one char at a time +/*! + This function is used in ANSI mode when input from a stream is in UTF-8 + format and the UTF-8 buffer read cannot be converted to the locale + wxString object. + The function performs a char-by-char conversion of the buffer and appends + every representable character to the string \c s. + Characters that cannot be represented are stored as \e unicode \e escaped + \e sequences in the form: + \code + \uXXXX + \endcode + where XXXX is a for-hex-digits Unicode code point. + The function returns the number of characters that cannot be represented + in the current locale. +*/ +int +wxJSONReader::ConvertCharByChar( wxString& s, const wxMemoryBuffer& utf8Buffer ) +{ + size_t len = utf8Buffer.GetDataLen(); + char* buff = (char*) utf8Buffer.GetData(); + char* buffEnd = buff + len; + + int result = 0; + char temp[16]; // the UTF-8 code-point + + while ( buff < buffEnd ) { + temp[0] = *buff; // the first UTF-8 code-unit + // compute the number of code-untis that make one UTF-8 code-point + int numBytes = NumBytes( *buff ); + ++buff; + for ( int i = 1; i < numBytes; i++ ) { + if ( buff >= buffEnd ) { + break; + } + temp[i] = *buff; // the first UTF-8 code-unit + ++buff; + } + //if ( buff >= buffEnd ) { + // break; + //} + // now convert 'temp' to a wide-character + wchar_t dst[10]; + size_t outLength = wxConvUTF8.ToWChar( dst, 10, temp, numBytes ); + + // now convert the wide char to a locale dependent character + // len = wxConvLocal.FromWChar( temp, 16, dst, outLength ); + // len = wxConviso8859_1.FromWChar( temp, 16, dst, outLength ); + len = wxConvLibc.FromWChar( temp, 16, dst, outLength ); + if ( len == wxCONV_FAILED ) { + ++result; + wxString t; + t.Printf( _T( "\\u%04X"), (int) dst[0] ); + s.Append( t ); + } + else { + s.Append( temp[0], 1 ); + } + } // end while + return result; +} + +//! Read a memory buffer type +/*! + This function is called by DoRead() when the single-quote character is + encontered which starts a \e memory \e buffer type. + This type is a \b wxJSON extension so the function emits a warning + when such a type encontered. + If the reader is constructed without the \c wxJSONREADER_MEMORYBUFF flag + then the warning becomes an error. + To know more about this JSON syntax extension read \ref wxjson_tutorial_memorybuff + + @param is the input stream + @param val the JSON value that will hold the memory buffer value + @return the last char read or -1 in case of EOF +*/ + +//union byte +//{ +// unsigned char c[2]; +// short int b; +//}; + +int +wxJSONReader::ReadMemoryBuff( wxInputStream& is, wxJSONValue& val ) +{ + static const wxChar* membuffError = _T("the \'memory buffer\' type contains %d invalid digits" ); + + AddWarning( wxJSONREADER_MEMORYBUFF, _T( "the \'memory buffer\' type is not valid JSON text" )); + + wxMemoryBuffer buff; + int ch = 0; int errors = 0; + unsigned char byte = 0; + while ( ch >= 0 ) { + ch = ReadChar( is ); + if ( ch < 0 ) { + break; + } + if ( ch == '\'' ) { + break; + } + // the conversion is done two chars at a time + unsigned char c1 = (unsigned char) ch; + ch = ReadChar( is ); + if ( ch < 0 ) { + break; + } + unsigned char c2 = (unsigned char) ch; + c1 -= '0'; + c2 -= '0'; + if ( c1 > 9 ) { + c1 -= 7; + } + if ( c2 > 9 ) { + c2 -= 7; + } + if ( c1 > 15 ) { + ++errors; + } + else if ( c2 > 15 ) { + ++errors; + } + else { + byte = (c1 * 16) + c2; + buff.AppendByte( byte ); + } + } // end while + + if ( errors > 0 ) { + wxString err; + err.Printf( membuffError, errors ); + AddError( err ); + } + + + // now assign the memory buffer object to the JSON-value 'value' + // must check that: + // 'value' is invalid OR + // 'value' is a memory buffer; concatenate it + if ( !val.IsValid() ) { + wxLogTrace( traceMask, _T("(%s) assigning the memory buffer to value"), __PRETTY_FUNCTION__ ); + val = buff ; + } + else if ( val.IsMemoryBuff() ) { + wxLogTrace( traceMask, _T("(%s) concatenate memory buffer to value"), __PRETTY_FUNCTION__ ); + val.Cat( buff ); + } + else { + AddError( _T( "Memory buffer value cannot follow another value") ); + } + + // store the input text's line number when the string was stored in 'val' + val.SetLineNo( m_lineNo ); + + // read the next char after the closing quotes and returns it + if ( ch >= 0 ) { + ch = ReadChar( is ); + } + return ch; +} + + + + +#if defined( wxJSON_64BIT_INT ) +//! Converts a decimal string to a 64-bit signed integer +/*! + This function implements a simple variant + of the \b strtoll C-library function. + I needed this implementation because the wxString::To(U)LongLong + function does not work on my system: + + \li GNU/Linux Fedora Core 6 + \li GCC version 4.1.1 + \li libc.so.6 + + The wxWidgets library (actually I have installed version 2.8.7) + relies on \b strtoll in order to do the conversion from a string + to a long long integer but, in fact, it does not work because + the 'wxHAS_STRTOLL' macro is not defined on my system. + The problem only affects the Unicode builds while it seems + that the wxString::To(U)LongLong function works in ANSI builds. + + Note that this implementation is not a complete substitute of the + strtoll function because it only converts decimal strings (only base + 10 is implemented). + + @param str the string that contains the decimal literal + @param i64 the pointer to long long which holds the converted value + + @return TRUE if the conversion succeeds +*/ +bool +wxJSONReader::Strtoll( const wxString& str, wxInt64* i64 ) +{ + wxChar sign = ' '; + wxUint64 ui64; + bool r = DoStrto_ll( str, &ui64, &sign ); + + // check overflow for signed long long + switch ( sign ) { + case '-' : + if ( ui64 > (wxUint64) LLONG_MAX + 1 ) { + r = false; + } + else { + *i64 = (wxInt64) (ui64 * -1); + } + break; + + // case '+' : + default : + if ( ui64 > LLONG_MAX ) { + r = false; + } + else { + *i64 = (wxInt64) ui64; + } + break; + } + return r; +} + + +//! Converts a decimal string to a 64-bit unsigned integer. +/*! + Similar to \c Strtoll but for unsigned integers +*/ +bool +wxJSONReader::Strtoull( const wxString& str, wxUint64* ui64 ) +{ + wxChar sign = ' '; + bool r = DoStrto_ll( str, ui64, &sign ); + if ( sign == '-' ) { + r = false; + } + return r; +} + +//! Perform the actual conversion from a string to a 64-bit integer +/*! + This function is called internally by the \c Strtoll and \c Strtoull functions + and it does the actual conversion. + The function is also able to check numeric overflow. + + @param str the string that has to be converted + @param ui64 the pointer to a unsigned long long that holds the converted value + @param sign the pointer to a wxChar character that will get the sign of the literal string, if any + @return TRUE if the conversion succeeds +*/ +bool +wxJSONReader::DoStrto_ll( const wxString& str, wxUint64* ui64, wxChar* sign ) +{ + // the conversion is done by multiplying the individual digits + // in reverse order to the corresponding power of 10 + // + // 10's power: 987654321.9876543210 + // + // LLONG_MAX: 9223372036854775807 + // LLONG_MIN: -9223372036854775808 + // ULLONG_MAX: 18446744073709551615 + // + // the function does not take into account the sign: only a + // unsigned long long int is returned + + int maxDigits = 20; // 20 + 1 (for the sign) + + wxUint64 power10[] = { + wxULL(1), + wxULL(10), + wxULL(100), + wxULL(1000), + wxULL(10000), + wxULL(100000), + wxULL(1000000), + wxULL(10000000), + wxULL(100000000), + wxULL(1000000000), + wxULL(10000000000), + wxULL(100000000000), + wxULL(1000000000000), + wxULL(10000000000000), + wxULL(100000000000000), + wxULL(1000000000000000), + wxULL(10000000000000000), + wxULL(100000000000000000), + wxULL(1000000000000000000), + wxULL(10000000000000000000) + }; + + + wxUint64 temp1 = wxULL(0); // the temporary converted integer + + int strLen = str.length(); + if ( strLen == 0 ) { + // an empty string is converted to a ZERO value: the function succeeds + *ui64 = wxLL(0); + return true; + } + + int index = 0; + wxChar ch = str[0]; + if ( ch == '+' || ch == '-' ) { + *sign = ch; + ++index; + ++maxDigits; + } + + if ( strLen > maxDigits ) { + return false; + } + + // check the overflow: check the string length and the individual digits + // of the string; the overflow is checked for unsigned long long + if ( strLen == maxDigits ) { + wxString uLongMax( _T("18446744073709551615")); + int j = 0; + for ( int i = index; i < strLen - 1; i++ ) { + ch = str[i]; + if ( ch < '0' || ch > '9' ) { + return false; + } + if ( ch > uLongMax[j] ) { + return false; + } + if ( ch < uLongMax[j] ) { + break; + } + ++j; + } + } + + // get the digits in the reverse order and multiply them by the + // corresponding power of 10 + int exponent = 0; + for ( int i = strLen - 1; i >= index; i-- ) { + wxChar ch = str[i]; + if ( ch < '0' || ch > '9' ) { + return false; + } + ch = ch - '0'; + // compute the new temporary value + temp1 += ch * power10[exponent]; + ++exponent; + } + *ui64 = temp1; + return true; +} + +#endif // defined( wxJSON_64BIT_INT ) + +/* +{ +} +*/ + + + diff --git a/ThirdParty/wxJSON/src/jsonval.cpp b/ThirdParty/wxJSON/src/jsonval.cpp new file mode 100644 index 0000000..85f053b --- /dev/null +++ b/ThirdParty/wxJSON/src/jsonval.cpp @@ -0,0 +1,3561 @@ +///////////////////////////////////////////////////////////////////////////// +// Name: jsonval.cpp +// Purpose: the wxJSON class that holds a JSON value +// Author: Luciano Cattani +// Created: 2007/10/01 +// RCS-ID: $Id: jsonval.cpp,v 1.12 2008/03/06 10:25:18 luccat Exp $ +// Copyright: (c) 2007 Luciano Cattani +// Licence: wxWidgets licence +///////////////////////////////////////////////////////////////////////////// + +#ifdef __GNUG__ + #pragma implementation "jsonval.cpp" +#endif + + +// For compilers that support precompilation, includes "wx.h". +#include "wx/wxprec.h" + +#ifdef __BORLANDC__ +#pragma hdrstop +#endif + +#include +#include +#include + +#include + + +WX_DEFINE_OBJARRAY( wxJSONInternalArray ); + + +// the trace mask used in wxLogTrace() function +// static const wxChar* traceMask = _T("jsonval"); +static const wxChar* traceMask = _T("jsonval"); +static const wxChar* compareTraceMask = _T("sameas"); +static const wxChar* cowTraceMask = _T("traceCOW" ); + + + +/******************************************************************* + + class wxJSONRefData + +*******************************************************************/ + + +/*! \class wxJSONRefData + \brief The reference counted JSON value data (internal use). + + Starting from version 0.4, the JSON value class use the reference + counting tecnique (also know as \e copy-on-write) described in the + \b wxWidgets documentation in order to speed up processing. + The class is used internally by the wxJSONValue class which does + all processing. + To know more about COW see \ref json_internals_cow +*/ + +#if defined( WXJSON_USE_VALUE_COUNTER ) + // The progressive counter (used for debugging only) + int wxJSONRefData::sm_progr = 1; +#endif + +//! Constructor. +wxJSONRefData::wxJSONRefData() +{ + m_lineNo = -1; + m_refCount = 1; + m_memBuff = 0; + +#if defined( WXJSON_USE_VALUE_COUNTER ) + m_progr = sm_progr; + ++sm_progr; + wxLogTrace( traceMask, _T("(%s) JSON refData ctor progr=%d"), + __PRETTY_FUNCTION__, m_progr); +#endif +} + +// Dtor +wxJSONRefData::~wxJSONRefData() +{ + if ( m_memBuff ) { + delete m_memBuff; + } +} + +// Return the number of objects that reference this data. +int +wxJSONRefData::GetRefCount() const +{ + return m_refCount; +} + + +/******************************************************************* + + class wxJSONValue + +*******************************************************************/ + + +/*! \class wxJSONValue + \brief The JSON value class implementation. + +This class holds a JSON value which may be of variuos types (see the +wxJSONType constants for a description of the types). +To know more about the internal representation of JSON values see +\ref pg_json_internals. + +Starting from version 0.5 the wxJSON library supports 64-bits integers on +platforms that have native support for very large integers. +Note that the integer type is still stored as a generic wxJSONTYPE_(U)INT +constant regardless the size of the value but the JSON value class defines +functions in order to let the user know if an integer value fits in 16, 32 +or 64 bit integer. +To know more about 64-bits integer support see \ref json_internals_integer + +Storing values in a JSON value object of this class is very simple. +The following is an example: +\code + wxJSONValue v( _T( "A string")); // store a string value in the object + wxString s = v.AsString(); // get the string value + + v = 12; // now 'v' contains an integer value + int i = v.AsInt(); // get the integer +\endcode + + \par The C-string JSON value object + + The wxJSONValue(const wxChar*) ctor allows you to create a JSON value + object that contains a string value which is stored as a + \e pointer-to-static-string. + Beware that this ctor DOES NOT copy the string: it only stores the + pointer in a data member and the pointed-to buffer is not deleted + by the dtor. + If the string is not static you have to use the wxJSONValue(const wxString&) + constructor. + + Also note that this does NOT mean that the value stored in this JSON + object cannot change: you can assign whatever other value you want, + an integer, a double or an array of values. + What I intended is that the pointed-to string must exist for the lifetime + of the wxJSONValue object. + The following code is perfectly legal: + \code + wxJSONvalue aString( "this is a static string" ); + aString = 10; + \endcode + To know more about this topic see \ref json_internals_cstring. + + Starting from version 1.3 the class can hold binary memory buffers + as an extension to the JSON syntax. Memory buffers are stored as + \b wxMemoryBuffer objects which contain binary data. The class + uses reference counting for the copy and assignment operation but + it is not a \e copy-on-write structure. + To know more about memory buffers read \ref wxjson_tutorial_memorybuff + + \sa the \ref wxjson_tutorial. +*/ + + +#if defined( WXJSON_USE_VALUE_COUNTER ) + // The progressive counter (used for debugging only) + int wxJSONValue::sm_progr = 1; +#endif + +//! Constructors. +/*! + The overloaded constructors allow the user to construct a JSON value + object that holds the specified value and type of value. + The default ctor construct a valid JSON object that constains a \b null + value. + + If you want to create an \b invalid JSON value object you have to use the + \c wxJSONValue( wxJSONTYPE_INVALID ) ctor. + Note that this object is not a valid JSON value - to know more about this + topic see the SetType() function. + + To create an empty array or key/value map use the following: + \code + wxJSONvalue v1( wxJSONTYPE_ARRAY ); + wxJSONvalue v2( wxJSONTYPE_OBJECT ); + \endcode +*/ +wxJSONValue::wxJSONValue() +{ + m_refData = 0; + Init( wxJSONTYPE_NULL ); +} + +//! Initialize the JSON value class. +/*! + The function is called by the ctors and allocates a new instance of + the wxJSONRefData class and sets the type of the JSON value. + Note that only the type is set, not the value. + Also note that this function may be called from other memberfunctions + if the \c m_refData data member is NULL. +*/ +wxJSONRefData* +wxJSONValue::Init( wxJSONType type ) +{ + wxJSONRefData* data = GetRefData(); + if ( data != 0 ) { + UnRef(); + } + + // we allocate a new instance of the referenced data + data = new wxJSONRefData(); + wxJSON_ASSERT( data ); + + // in release builds we do not have ASSERT so we check 'data' before + // using it + if ( data ) { + data->m_type = type; + data->m_commentPos = wxJSONVALUE_COMMENT_BEFORE; + } + SetRefData( data ); + +#if defined( WXJSON_USE_VALUE_COUNTER ) + m_progr = sm_progr; + ++sm_progr; + wxLogTrace( cowTraceMask, _T("(%s) Init a new object progr=%d"), + __PRETTY_FUNCTION__, m_progr ); +#endif + return data; +} + + +//! \overload wxJSONValue() +wxJSONValue::wxJSONValue( wxJSONType type ) +{ + m_refData = 0; + Init( type ); +} + +//! \overload wxJSONValue() +wxJSONValue::wxJSONValue( int i ) +{ + m_refData = 0; + wxJSONRefData* data = Init( wxJSONTYPE_INT ); + wxJSON_ASSERT( data ); + if ( data != 0 ) { + // the 'VAL_INT' macro expands to 'm_valLong' or 'm_valInt64' depending + // on 64-bits integer support being enabled on not + data->m_value.VAL_INT = i; + } +} + + +//! \overload wxJSONValue() +wxJSONValue::wxJSONValue( unsigned int ui ) +{ + m_refData = 0; + wxJSONRefData* data = Init( wxJSONTYPE_UINT ); + wxJSON_ASSERT( data ); + if ( data != 0 ) { + // the 'VAL_UINT' macro expands to 'm_valULong' or 'm_valUInt64' depending + // on 64-bits integer support being enabled on not + data->m_value.VAL_UINT = ui; + } +} + +//! \overload wxJSONValue() +wxJSONValue::wxJSONValue( short int i ) +{ + m_refData = 0; + wxJSONRefData* data = Init( wxJSONTYPE_INT ); + wxJSON_ASSERT( data ); + if ( data != 0 ) { + // the 'VAL_INT' macro expands to 'm_valLong' or 'm_valInt64' depending + // on 64-bits integer support being enabled on not + data->m_value.VAL_INT = i; + } +} + + +//! \overload wxJSONValue() +wxJSONValue::wxJSONValue( unsigned short ui ) +{ + m_refData = 0; + wxJSONRefData* data = Init( wxJSONTYPE_UINT ); + wxJSON_ASSERT( data ); + if ( data != 0 ) { + // the 'VAL_UINT' macro expands to 'm_valULong' or 'm_valUInt64' depending + // on 64-bits integer support being enabled on not + data->m_value.VAL_UINT = ui; + } +} + +//! \overload wxJSONValue() +wxJSONValue::wxJSONValue( bool b ) +{ + m_refData = 0; + wxJSONRefData* data = Init( wxJSONTYPE_BOOL ); + wxJSON_ASSERT( data ); + if ( data != 0 ) { + data->m_value.m_valBool = b; + } +} + +//! \overload wxJSONValue() +wxJSONValue::wxJSONValue( double d ) +{ + m_refData = 0; + wxJSONRefData* data = Init( wxJSONTYPE_DOUBLE ); + wxJSON_ASSERT( data ); + if ( data != 0 ) { + data->m_value.m_valDouble = d; + } +} + +//! \overload wxJSONValue() +wxJSONValue::wxJSONValue( const wxChar* str ) +{ + m_refData = 0; + wxJSONRefData* data = Init( wxJSONTYPE_CSTRING ); + wxJSON_ASSERT( data ); + if ( data != 0 ) { + #if !defined( WXJSON_USE_CSTRING ) + data->m_type = wxJSONTYPE_STRING; + data->m_valString.assign( str ); + #else + data->m_value.m_valCString = str; + #endif + } +} + +//! \overload wxJSONValue() +wxJSONValue::wxJSONValue( const wxString& str ) +{ + m_refData = 0; + wxJSONRefData* data = Init( wxJSONTYPE_STRING ); + wxJSON_ASSERT( data ); + if ( data != 0 ) { + data->m_valString.assign( str ); + } +} + +//! \overload wxJSONValue() +wxJSONValue::wxJSONValue( long int l ) +{ + m_refData = 0; + wxJSONRefData* data = Init( wxJSONTYPE_INT ); + wxJSON_ASSERT( data ); + if ( data != 0 ) { + data->m_value.VAL_INT = l; + } +} + +//! \overload wxJSONValue() +wxJSONValue::wxJSONValue( unsigned long int ul ) +{ + m_refData = 0; + wxJSONRefData* data = Init( wxJSONTYPE_UINT ); + wxJSON_ASSERT( data ); + if ( data != 0 ) { + data->m_value.VAL_UINT = ul; + } +} + +//! Construct a JSON value object of type \e memory \e buffer +/*! + Note that this ctor makes a deep copy of \c buff so changes made + to the original buffer does not reflect to the buffer stored in this + JSON value. +*/ +wxJSONValue::wxJSONValue( const wxMemoryBuffer& buff ) +{ + m_refData = 0; + wxJSONRefData* data = Init( wxJSONTYPE_MEMORYBUFF ); + wxJSON_ASSERT( data ); + if ( data != 0 ) { + data->m_memBuff = new wxMemoryBuffer(); + const void* ptr = buff.GetData(); + size_t buffLen = buff.GetDataLen(); + if ( buffLen > 0 ) { + data->m_memBuff->AppendData( ptr, buffLen ); + } + } +} + +//! Construct a JSON value object of type \e memory \e buffer +/*! + Note that this ctor makes a deep copy of \c buff so changes made + to the original buffer does not reflect to the buffer stored in this + JSON value. +*/ +wxJSONValue::wxJSONValue( const void* buff, size_t len ) +{ + m_refData = 0; + wxJSONRefData* data = Init( wxJSONTYPE_MEMORYBUFF ); + wxJSON_ASSERT( data ); + if ( data != 0 && len > 0 ) { + data->m_memBuff = new wxMemoryBuffer(); + data->m_memBuff->AppendData( buff, len ); + } +} + +//! Copy constructor +/*! + The function copies the content of \c other in this + object. + Note that the JSON value object is not really copied; + the function calls Ref() in order to increment + the reference count of the \c wxJSONRefData structure. +*/ +wxJSONValue::wxJSONValue( const wxJSONValue& other ) +{ + m_refData = 0; + Ref( other ); + + // the progressive counter of the ctor is not copied from + // the other wxJSONValue object: only data is shared, the + // progressive counter is not shared because this object + // is a copy of 'other' and it has its own progressive +#if defined( WXJSON_USE_VALUE_COUNTER ) + m_progr = sm_progr; + ++sm_progr; + wxLogTrace( cowTraceMask, _T("(%s) Copy ctor - progr=%d other progr=%d"), + __PRETTY_FUNCTION__, m_progr, other.m_progr ); +#endif +} + + +//! Dtor - calls UnRef(). +wxJSONValue::~wxJSONValue() +{ + UnRef(); +} + + +// functions for retreiving the value type: they are all 'const' + + +//! Return the type of the value stored in the object. +/*! + This function is the only one that does not ASSERT that the + \c m_refData data member is not NULL. + In fact, if the JSON value object does not contain a pointer + to a wxJSONRefData structure, the function returns the + wxJSONTYPE_INVALID constant which represent an invalid JSON value object. + Also note that the pointer to the referenced data structure + should NEVER be NULL. + + \par Integer types + + Integers are stored internally in a \b signed/unsigned \b long \b int + or, on platforms that support 64-bits integers, in a + \b wx(U)Int64 data type. + When constructed, it is assigned a generic integer type that only + depends on the sign: wxJSON_(U)INT regardless the size of the + stored value. + + This function can be used to know the actual size requirement + of the stored value and how it can be retrieved. The value + returned by this function is: + + - for signed integers: + - \b wxJSONTYPE_SHORT if the value is between SHORT_MIN and SHORT_MAX + - \b wxJSONTYPE_LONG if the value is between LONG_MIN and LONG_MAX + and greater than SHORT_MAX and less than SHORT_MIN + - \b wxJSONTYPE_INT64 if the value is greater than LONG_MAX and + less than LONG_MIN + + - for unsigned integers: + - \b wxJSONTYPE_USHORT if the value is between 0 and USHORT_MAX + - \b wxJSONTYPE_ULONG if the value is between 0 and ULONG_MAX + and greater than USHORT_MAX + - \b wxJSONTYPE_UINT64 if the value is greater than ULONG_MAX + + Note that this function never returns the wxJSONTYPE_(U)INT constant + because the \b int data type may have the same width as SHORT or LONG + depending on the platform. + This does not mean that you cannot use \b int as the return value: if + you use \b wxWidgets to develop application in only one platform, you + can use \b int because you know the size of the data type. + Otherwise, if is preferable to always use \b long instead of \b int. + + Also note that the class defines the \c IsInt() memberfunction which + works fine regardless the actual width of the \b int data type. + This function returns TRUE if the stored value fits in a \b int data + type whatever its size is on the current platform (16 or 32-bits). + + \sa SetType IsInt +*/ +wxJSONType +wxJSONValue::GetType() const +{ + wxJSONRefData* data = GetRefData(); + wxJSONType type = wxJSONTYPE_INVALID; + if ( data ) { + type = data->m_type; + + // for integers and unsigned ints check the storage requirements + // note that ints are stored as 'long' or as 'long long' + switch ( type ) { + case wxJSONTYPE_INT : + // check if the integer fits in a SHORT INT + if ( data->m_value.VAL_INT >= SHORT_MIN && + data->m_value.VAL_INT <= SHORT_MAX ) { + type = wxJSONTYPE_SHORT; + } + // check if the value fits in LONG INT + else if ( data->m_value.VAL_INT >= LONG_MIN + && data->m_value.VAL_INT <= LONG_MAX ) { + type = wxJSONTYPE_LONG; + } + else { + type = wxJSONTYPE_INT64; + } + break; + + case wxJSONTYPE_UINT : + if ( data->m_value.VAL_UINT <= USHORT_MAX ) { + type = wxJSONTYPE_USHORT; + } + else if ( data->m_value.VAL_UINT <= ULONG_MAX ) { + type = wxJSONTYPE_ULONG; + } + else { + type = wxJSONTYPE_UINT64; + } + break; + + default : + break; + } + } + return type; +} + + +//! Return TRUE if the type of the value is wxJSONTYPE_NULL. +bool +wxJSONValue::IsNull() const +{ + wxJSONType type = GetType(); + bool r = false; + if ( type == wxJSONTYPE_NULL ) { + r = true; + } + return r; +} + + +//! Return TRUE if the value stored is valid +/*! + The function returns TRUE if the wxJSONValue object was correctly + initialized - that is it contains a valid value. + A JSON object is valid if its type is not equal to wxJSONTYPE_INVALID. + Please note that the default ctor of wxJSONValue constructs a \b valid + JSON object of type \b null. + To create an invalid object you have to use; + \code + wxJSONValue v( wxJSONTYPE_INVALID ); + \endcode +*/ +bool +wxJSONValue::IsValid() const +{ + wxJSONType type = GetType(); + bool r = false; + if ( type != wxJSONTYPE_INVALID ) { + r = true; + } + return r; +} + +//! Return TRUE if the type of the value stored is integer. +/*! + This function returns TRUE if the stored value is of + type signed integer and the numeric value fits in a + \b int data type. + In other words, the function returns TRUE if the \c wxJSONRefData::m_type + data member is of type \c wxJSONTYPE_INT and: + + \code + INT_MIN <= m_value <= INT_MAX + \endcode + + Note that if you are developing cross-platform applications you should never + use \b int as the integer data type but \b long for 32-bits integers and + \b short for 16-bits integers. + This is because the \b int data type may have different width on different + platforms. + Regardless the widht of the data type (16 or 32 bits), the function returns + the correct result because it relies on the INT_MAX and INT_MIN macros. + + \sa \ref json_internals_integer +*/ +bool +wxJSONValue::IsInt() const +{ + wxJSONType type = GetType(); + bool r = false; + // if the type is SHORT the value fits into an INT, too + if ( type == wxJSONTYPE_SHORT ) { + r = true; + } + else if ( type == wxJSONTYPE_LONG ) { + // in case of LONG, check if the bit width is the same + if ( INT_MAX == LONG_MAX ) { + r = true; + } + } + return r; +} + +//! Return TRUE if the type of the value stored is 16-bit integer. +/*! + This function returns TRUE if the stored value is of + type signed integer and the numeric value fits in a + \b short \b int data type (16-bit integer). + In other words, the function returns TRUE if the \c wxJSONRefData::m_type + data member is of type \c wxJSONTYPE_INT and: + + \code + SHORT_MIN <= m_value <= SHORT_MAX + \endcode + + \sa \ref json_internals_integer +*/ +bool +wxJSONValue::IsShort() const +{ + wxJSONType type = GetType(); + bool r = false; + if ( type == wxJSONTYPE_SHORT ) { + r = true; + } + return r; +} + +//! Return TRUE if the type of the value stored is a unsigned int. +/*! + This function returns TRUE if the stored value is of + type unsigned integer and the numeric value fits int a + \b int data type. + In other words, the function returns TRUE if the \c wxJSONRefData::m_type + data member is of type \c wxJSONTYPE_UINT and: + + \code + 0 <= m_value <= UINT_MAX + \endcode + + Note that if you are developing cross-platform applications you should never + use \b unsigned \b int as the integer data type but \b unsigned \b long for + 32-bits integers and \b unsigned \b short for 16-bits integers. + This is because the \b unsigned \b int data type may have different width + on different platforms. + Regardless the widht of the data type (16 or 32 bits), the function returns + the correct result because it relies on the UINT_MAX macro. + + + \sa \ref json_internals_integer +*/ +bool +wxJSONValue::IsUInt() const +{ + wxJSONType type = GetType(); + bool r = false; + if ( type == wxJSONTYPE_USHORT ) { + r = true; + } + else if ( type == wxJSONTYPE_ULONG ) { + if ( INT_MAX == LONG_MAX ) { + r = true; + } + } + return r; +} + +//! Return TRUE if the type of the value stored is a unsigned short. +/*! + This function returns TRUE if the stored value is of + type unsigned integer and the numeric value fits in a + \b unsigned \b short \b int data type. + In other words, the function returns TRUE if the \c wxJSONRefData::m_type + data member is of type \c wxJSONTYPE_UINT and: + + \code + 0 <= m_value <= USHORT_MAX + \endcode + + \sa \ref json_internals_integer +*/ +bool +wxJSONValue::IsUShort() const +{ + wxJSONType type = GetType(); + bool r = false; + if ( type == wxJSONTYPE_USHORT ) { + r = true; + } + return r; +} + + +//! Return TRUE if the stored value is an integer which fits in a long int +/*! + This function returns TRUE if the stored value is of + type signed LONG integer and the numeric value fits int a + \b long \b int data type. + In other words, the function returns TRUE if the \c wxJSONRefData::m_type + data member is of type \c wxJSONTYPE_INT and: + + \code + LONG_MIN <= m_value <= LONG_MAX + \endcode + + \sa \ref json_internals_integer +*/ +bool +wxJSONValue::IsLong() const +{ + wxJSONType type = GetType(); + bool r = false; + if ( type == wxJSONTYPE_LONG || type == wxJSONTYPE_SHORT ) { + r = true; + } + return r; +} + +//! Return TRUE if the stored value is an integer which fits in a unsigned long int +/*! + This function returns TRUE if the stored value is of + type unsigned LONG integer and the numeric value fits int a + \b unsigned \b long \b int data type. + In other words, the function returns TRUE if the \c wxJSONRefData::m_type + data member is of type \c wxJSONTYPE_UINT and: + + \code + 0 <= m_value <= ULONG_MAX + \endcode + + \sa \ref json_internals_integer +*/ +bool +wxJSONValue::IsULong() const +{ + wxJSONType type = GetType(); + bool r = false; + if ( type == wxJSONTYPE_ULONG || type == wxJSONTYPE_USHORT ) { + r = true; + } + return r; +} + + + +//! Return TRUE if the type of the value stored is a boolean. +bool +wxJSONValue::IsBool() const +{ + wxJSONType type = GetType(); + bool r = false; + if ( type == wxJSONTYPE_BOOL ) { + r = true; + } + return r; +} + +//! Return TRUE if the type of the value stored is a double. +bool +wxJSONValue::IsDouble() const +{ + wxJSONType type = GetType(); + bool r = false; + if ( type == wxJSONTYPE_DOUBLE ) { + r = true; + } + return r; +} + +//! Return TRUE if the type of the value stored is a wxString object. +bool +wxJSONValue::IsString() const +{ + wxJSONType type = GetType(); + bool r = false; + if ( type == wxJSONTYPE_STRING ) { + r = true; + } + return r; +} + +//! Return TRUE if the type of the value stored is a pointer to a static C string. +/*! + This function returns TRUE if, and only if the stored value is a + pointer to a static C-string and the C-string storage is enabled in + the wxJSON library. + By default, C-string storage is not enabled in the library so this + function always returns FALSE. + To know more about C-strings read \ref json_internals_cstring +*/ +bool +wxJSONValue::IsCString() const +{ + wxJSONType type = GetType(); + bool r = false; + if ( type == wxJSONTYPE_CSTRING ) { + r = true; + } + return r; +} + +//! Return TRUE if the type of the value stored is an array type. +bool +wxJSONValue::IsArray() const +{ + wxJSONType type = GetType(); + bool r = false; + if ( type == wxJSONTYPE_ARRAY ) { + r = true; + } + return r; +} + +//! Return TRUE if the type of this value is a key/value map. +bool +wxJSONValue::IsObject() const +{ + wxJSONType type = GetType(); + bool r = false; + if ( type == wxJSONTYPE_OBJECT ) { + r = true; + } + return r; +} + +//! Return TRUE if the type of this value is a binary memory buffer. +bool +wxJSONValue::IsMemoryBuff() const +{ + wxJSONType type = GetType(); + bool r = false; + if ( type == wxJSONTYPE_MEMORYBUFF ) { + r = true; + } + return r; +} + + + +// get the stored value; all these functions are 'const' + +//! Return the stored value as an integer. +/*! + The function returns the stored value as an integer. + Note that the function does not check that the type of the + value is actually an integer and it just returns the content + of the wxJSONValueHolder union. + However, in debug builds, the function ASSERTs that the + type of the stored value \c IsInt(). + + \sa \ref json_internals_integer + \sa \ref wxjson_tutorial_get +*/ +int +wxJSONValue::AsInt() const +{ + wxJSONRefData* data = GetRefData(); + wxJSON_ASSERT( data ); + int i = (int) data->m_value.VAL_INT; + + wxJSON_ASSERT( IsInt()); + return i; +} + +//! Return the stored value as a boolean. +/*! + The function returns the stored value as a boolean. + Note that the function does not check that the type of the + value is actually a boolean and it just returns the content + of the wxJSONValueHolder union. + However, in debug builds, the function ASSERTs that the + type of the stored value is wxJSONTYPE_BOOL. + + \sa \ref wxjson_tutorial_get +*/ +bool +wxJSONValue::AsBool() const +{ + wxJSONRefData* data = GetRefData(); + wxJSON_ASSERT( data ); + wxJSON_ASSERT( data->m_type == wxJSONTYPE_BOOL ); + return data->m_value.m_valBool; +} + +//! Return the stored value as a double. +/*! + The function returns the stored value as a double. + Note that the function does not check that the type of the + value is actually a double and it just returns the content + of the wxJSONValueHolder union as if it was a double. + However, in debug builds, the function ASSERTs that the + type of the stored value \c IsDouble(). + + \sa \ref wxjson_tutorial_get +*/ +double +wxJSONValue::AsDouble() const +{ + wxJSONRefData* data = GetRefData(); + wxJSON_ASSERT( data ); + double d = data->m_value.m_valDouble; + wxJSON_ASSERT( IsDouble()); + return d; +} + + +//! Return the stored value as a wxWidget's string. +/*! + The function returns a string representation of the value + stored in the JSON object. + All value types are converted to a string by this function + and returned as a string: + + \li For integer the string is the string representation of + the numerical value in decimal notation; the function uses the + \b wxString::Printf() function for the conversion + + \li for doubles, the value is converted to a string using the + \b wxString::Printf("%.10g") function; the format string specifies + a precision of ten decimal digits and suppress trailing ZEROes + + \li for booleans the string returned is: \b true or \b false. + + \li if the value is a NULL value the \b null literal string is returned. + + \li if the value is of type wxJSONTYPE_INVALID, the literal string \b <invalid> + is returned. Note that this is NOT a valid JSON text. + + \li if the value is of type wxJSONTYPE_MEMORYBUFF the string returned contains the + hexadecimal digits of the first 5 bytes preceeded by the length of the buffer, + enclosed in parenthesis + + If the value is an array or map, the returned string is the number of + elements is the array/object enclosed in the JSON special characters that + identifies the array/object. Example: + + \code + [0] // an empty array + {12} // an object of 12 elements + \endcode + + \sa \ref wxjson_tutorial_get +*/ +wxString +wxJSONValue::AsString() const +{ + wxJSONRefData* data = GetRefData(); + wxJSON_ASSERT( data ); + wxString s; + int size = Size(); + switch ( data->m_type ) { + case wxJSONTYPE_STRING : + s.assign( data->m_valString); + break; + case wxJSONTYPE_CSTRING : + s.assign( data->m_value.m_valCString); + break; + case wxJSONTYPE_INT : + #if defined( wxJSON_64BIT_INT ) + s.Printf( _T("%") wxT(wxLongLongFmtSpec) _T("i"), + data->m_value.m_valInt64 ); + #else + s.Printf( _T("%ld"), data->m_value.m_valLong ); + #endif + break; + case wxJSONTYPE_UINT : + #if defined( wxJSON_64BIT_INT ) + s.Printf( _T("%") wxT(wxLongLongFmtSpec) _T("u"), + data->m_value.m_valUInt64 ); + #else + s.Printf( _T("%lu"), data->m_value.m_valULong ); + #endif + break; + case wxJSONTYPE_DOUBLE : + s.Printf( _T("%.10g"), data->m_value.m_valDouble ); + break; + case wxJSONTYPE_BOOL : + s.assign( ( data->m_value.m_valBool ? + _T("true") : _T("false") )); + break; + case wxJSONTYPE_NULL : + s.assign( _T( "null")); + break; + case wxJSONTYPE_INVALID : + s.assign( _T( "")); + break; + case wxJSONTYPE_ARRAY : + s.Printf( _T("[%d]"), size ); + break; + case wxJSONTYPE_OBJECT : + s.Printf( _T("{%d}"), size ); + break; + case wxJSONTYPE_MEMORYBUFF : + s = MemoryBuffToString( *(data->m_memBuff), 5 ); + break; + default : + s.assign( _T( "wxJSONValue::AsString(): Unknown JSON type \'")); + s.append( TypeToString( data->m_type )); + s.append( _T( "\'" )); + wxFAIL_MSG( s ); + break; + } + return s; +} + +//! Return the stored value as a pointer to a static C string. +/*! + If the type of the value is stored as a C-string data type the + function just returns that pointer. + If the stored value is a wxString object, the function returns the + pointer returned by the \b wxString::c_str() function. + If the stored value is of all other JSON types, the functions returns a NULL pointer. + + Note that in versions prior to 0.5, the + function returned a NULL pointer also if the value is a \c wxString object. + + \sa \ref json_internals_cstring + \sa \ref wxjson_tutorial_get + +*/ +const wxChar* +wxJSONValue::AsCString() const +{ + const wxChar* s = 0; + wxJSONRefData* data = GetRefData(); + wxJSON_ASSERT( data ); + switch ( data->m_type ) { + case wxJSONTYPE_CSTRING : + s = data->m_value.m_valCString; + break; + case wxJSONTYPE_STRING : + s = data->m_valString.c_str(); + break; + default : + break; + } + return s; +} + + +//! Return the stored value as a unsigned int. +/*! + The function returns the stored value as a unsigned integer. + Note that the function does not check that the type of the + value is actually a unsigned integer and it just returns the content + of the wxJSONValueHolder union. + However, in debug builds, the function ASSERTs that the + type of the stored value is wxJSONTYPE_UINT. + + \sa \ref json_internals_integer + \sa \ref wxjson_tutorial_get +*/ +unsigned int +wxJSONValue::AsUInt() const +{ + wxJSONRefData* data = GetRefData(); + wxJSON_ASSERT( data ); + unsigned int ui = (unsigned) data->m_value.VAL_UINT; + + wxJSON_ASSERT( IsUInt()); + return ui; +} + + +//! Returns the value as a long integer +/*! + The function returns the stored value as a long integer. + Note that the function does not check that the type of the + value is actually a long integer and it just returns the content + of the wxJSONValueHolder union. + However, in debug builds, the function ASSERTs that the + type of the stored value \c IsLong(). + + \sa \ref json_internals_integer + \sa \ref wxjson_tutorial_get +*/ +long int +wxJSONValue::AsLong() const +{ + long int l; + wxJSONRefData* data = GetRefData(); + wxJSON_ASSERT( data ); + l = (long) data->m_value.VAL_INT; + + wxJSON_ASSERT( IsLong()); + return l; +} + +//! Returns the value as a unsigned long integer +/*! + The function returns the stored value as a unsigned long integer. + Note that the function does not check that the type of the + value is actually a unsigned long integer and it just returns the content + of the wxJSONValueHolder union. + However, in debug builds, the function ASSERTs that the + type of the stored value \c IsLong(). + + \sa \ref json_internals_integer + \sa \ref wxjson_tutorial_get +*/ +unsigned long int +wxJSONValue::AsULong() const +{ + wxJSONRefData* data = GetRefData(); + wxJSON_ASSERT( data ); + unsigned long int ul = (unsigned long) data->m_value.VAL_UINT; + + wxJSON_ASSERT( IsULong()); // expands only in debug builds + return ul; +} + + +//! Returns the value as a short integer +/*! + The function returns the stored value as a short integer. + Note that the function does not check that the type of the + value is actually a short integer and it just returns the content + of the wxJSONValueHolder union. + However, in debug builds, the function ASSERTs that the + type of the stored value \c IsShort(). + + \sa \ref json_internals_integer + \sa \ref wxjson_tutorial_get +*/ +short int +wxJSONValue::AsShort() const +{ + short int i; + wxJSONRefData* data = GetRefData(); + wxJSON_ASSERT( data ); + i = (short) data->m_value.VAL_INT; + + wxJSON_ASSERT( IsShort()); + return i; +} + +//! Returns the value as a unsigned short integer +/*! + The function returns the stored value as a unsigned short integer. + Note that the function does not check that the type of the + value is actually a unsigned short and it just returns the content + of the wxJSONValueHolder union. + However, in debug builds, the function ASSERTs that the + type of the stored value \c IsUShort(). + + \sa \ref json_internals_integer + \sa \ref wxjson_tutorial_get +*/ +unsigned short +wxJSONValue::AsUShort() const +{ + unsigned short ui; + wxJSONRefData* data = GetRefData(); + wxJSON_ASSERT( data ); + ui = (unsigned short) data->m_value.VAL_UINT; + + wxJSON_ASSERT( IsUShort()); + return ui; +} + + + +//! Stores the value of this object in the provided argument +/*! + The functions of the form \c AsXxxxxx(T&) are the same as the \c AsXxxxxxx() + but store the value in the provided argument and return TRUE if the value of + this object is of the correct type. + By using these functions you can get the value and test if the JSON value is + of the expected type in only one step. + For example: + \code + int i; wxJSONValue v(10); + if ( !v.AsInt( i )) { + cout << "Error: value is not of the expected type"; + } + \endcode + This is the same as: + \code + int i; wxJSONValue v(10); + if ( v.IsInt() { + i = v.AsInt(); + } + else { + cout << "Error: value is not of the expected type"; + } + \endcode + Thanks to \b catalin who has suggested this new feature. +*/ +bool +wxJSONValue::AsInt( int& i ) const +{ + bool r = false; + if ( IsInt() ) { + i = AsInt(); + r = true; + } + return r; +} + +bool +wxJSONValue::AsUInt( unsigned int& ui ) const +{ + bool r = false; + if ( IsUInt() ) { + ui = AsUInt(); + r = true; + } + return r; +} + +bool +wxJSONValue::AsShort( short int& s ) const +{ + bool r = false; + if ( IsShort() ) { + s = AsShort(); + r = true; + } + return r; +} + +bool +wxJSONValue::AsUShort( unsigned short& us ) const +{ + bool r = false; + if ( IsUShort() ) { + us = AsUShort(); + r = true; + } + return r; +} + +bool +wxJSONValue::AsLong( long int& l ) const +{ + bool r = false; + if ( IsLong() ) { + l = AsLong(); + r = true; + } + return r; +} + +bool +wxJSONValue::AsULong( unsigned long& ul ) const +{ + bool r = false; + if ( IsULong() ) { + ul = AsULong(); + r = true; + } + return r; +} + + +bool +wxJSONValue::AsBool( bool& b ) const +{ + bool r = false; + if ( IsBool() ) { + b = AsBool(); + r = true; + } + return r; +} + +bool +wxJSONValue::AsDouble( double& d ) const +{ + bool r = false; + if ( IsDouble() ) { + d = AsDouble(); + r = true; + } + return r; +} + +//! Return this string value in the provided argument +/*! + This function is different from \c AsString because the latter always returns + a string also when this object does not contain a string. In that case, a string + representation of this value is returned. + This function, instead, returns TRUE only if this object contains a string, that is + only if \c IsString() returns TRUE. + Also note that the string value is only stored in \c str if this object actually + contains a \b string or \b c-string value. + \c str will never contain a string representation of other types. +*/ +bool +wxJSONValue::AsString( wxString& str ) const +{ + bool r = IsString(); + if ( r ) { + str = AsString(); + } + return r; +} + +bool +wxJSONValue::AsCString( wxChar* ch ) const +{ + bool r = IsCString(); + if ( r ) { + ch = (wxChar*) AsCString(); + } + return r; +} + +//! Returns the value as a memory buffer +/*! + The function returns the \e memory \e buffer object stored in + this JSON object. + Note that as of wxWidgets 2.8 and 2.9 the \b wxMemoryBuffer object uses + reference counting when copying the actual buffer but the class itself + is not a \e copy-on-write structure so changes made to one buffer affects + all other copies made from it. + This means that if you make a change to the returned copy of the memory + buffer, the change affects also the memory buffer stored in this JSON value. + + If this JSON object does not contain a \e wxJSONTYPE_MEMORYBUFF type + the function returns an empty memory buffer object. + An empty memory buffer is also returned if this JSON + type contains a valid, empty memory buffer. + You have to use the IsMemoryBuff() function to known the type of the + JSON value contained in this object, or the overloaded version of + this function. +*/ +wxMemoryBuffer +wxJSONValue::AsMemoryBuff() const +{ + wxJSONRefData* data = GetRefData(); + wxJSON_ASSERT( data ); + wxMemoryBuffer buff; + if ( data->m_memBuff ) { + buff = *(data->m_memBuff); + } + + wxJSON_ASSERT( IsMemoryBuff()); + return buff; +} + + +//! Returns the value as a memory buffer +/*! + The function returns the \e memory \e buffer object stored in + this JSON object. + Note that as of wxWidgets 2.8 and 2.9 the \b wxMemoryBuffer object uses + reference counting when copying the actual buffer but the class itself + is not a \e copy-on-write structure so changes made to one buffer affects + all other copies made from it. + This means that if you make a change to the returned copy of the memory + buffer, the change affects also the memory buffer stored in this JSON value. + + If this JSON object does not contain a \e wxJSONTYPE_MEMORYBUFF type + the function returns an empty memory buffer object. + An empty memory buffer is also returned if this JSON + type contains a valid, empty memory buffer. + You have to use the IsMemoryBuff() function to known the type of the + JSON value contained in this object, or the overloaded version of + this function. +*/ +bool +wxJSONValue::AsMemoryBuff( wxMemoryBuffer& buff ) const +{ + bool r = IsMemoryBuff(); + if ( r ) { + buff = AsMemoryBuff(); + } + return r; +} + + +// internal use + +//! Return the stored value as a map object. +/*! + This function is for testing and debugging purposes and you shold never use it. + To retreive values from an array or map JSON object use the \c Item() or ItemAt() + memberfunctions or the subscript operator. + If the stored value is not a map type, returns a NULL pointer. +*/ +const wxJSONInternalMap* +wxJSONValue::AsMap() const +{ + wxJSONRefData* data = GetRefData(); + wxJSON_ASSERT( data ); + + const wxJSONInternalMap* v = 0; + if ( data->m_type == wxJSONTYPE_OBJECT ) { + v = &( data->m_valMap ); + } + return v; +} + +//! Return the stored value as an array object. +/*! + This function is for testing and debugging purposes and you shold never use it. + To retreive values from an array or map JSON object use the \c Item() or ItemAt() + memberfunctions or the subscript operator. + If the stored value is not an array type, returns a NULL pointer. +*/ +const wxJSONInternalArray* +wxJSONValue::AsArray() const +{ + wxJSONRefData* data = GetRefData(); + wxJSON_ASSERT( data ); + + const wxJSONInternalArray* v = 0; + if ( data->m_type == wxJSONTYPE_ARRAY ) { + v = &( data->m_valArray ); + } + return v; +} + +// retrieve the members and other info + + +//! Return TRUE if the object contains an element at the specified index. +/*! + If the stoerd value is not an array or a map, the function returns FALSE. +*/ +bool +wxJSONValue::HasMember( unsigned index ) const +{ + bool r = false; + int size = Size(); + if ( index < (unsigned) size ) { + r = true; + } + return r; +} + +//! Return TRUE if the object contains an element at the specified key. +/*! + If the stored value is not a key/map map, the function returns FALSE. +*/ +bool +wxJSONValue::HasMember( const wxString& key ) const +{ + bool r = false; + wxJSONRefData* data = GetRefData(); + wxJSON_ASSERT( data ); + + if ( data && data->m_type == wxJSONTYPE_OBJECT ) { + wxJSONInternalMap::iterator it = data->m_valMap.find( key ); + if ( it != data->m_valMap.end() ) { + r = true; + } + } + return r; +} + +//! Return the size of the array or map stored in this value. +/*! + Note that both the array and the key/value map may have a size of + ZERO elements. + If the stored value is not an array nor a key/value hashmap, the + function returns -1. +*/ +int +wxJSONValue::Size() const +{ + wxJSONRefData* data = GetRefData(); + wxJSON_ASSERT( data ); + + int size = -1; + if ( data->m_type == wxJSONTYPE_ARRAY ) { + size = (int) data->m_valArray.GetCount(); + } + if ( data->m_type == wxJSONTYPE_OBJECT ) { + size = (int) data->m_valMap.size(); + } + return size; +} + +//! Return the array of keys of this JSON object. +/*! + If the stored value is a key/value map, the function returns an + array of strings containing the \e key of all elements. + Note that the returned array may be empty if the map has ZERO + elements. + An empty string array is also returned if the stored value is + not a key/value map. + Also note that in debug builds, the function wxJSON_ASSERTs that the + type of the stored object is wxJSONTYPE_OBJECT. +*/ +wxArrayString +wxJSONValue::GetMemberNames() const +{ + wxJSONRefData* data = GetRefData(); + wxJSON_ASSERT( data ); + wxJSON_ASSERT( data->m_type == wxJSONTYPE_OBJECT ); + + wxArrayString arr; + if ( data->m_type == wxJSONTYPE_OBJECT ) { + wxJSONInternalMap::iterator it; + for ( it = data->m_valMap.begin(); it != data->m_valMap.end(); it++ ) { + arr.Add( it->first ); + } + } + return arr; +} + + +// appending items, resizing and deleting items +// NOTE: these functions are not 'const' so we have to call +// the COW() function before accessing data + +//! Append the specified value in the array. +/*! + The function appends the value specified in the parameter to the array + contained in this object. + If this object does not contain an array type, the actual content is + deleted, a new array type is created and the JSON value \c value is + appended to the newly created array. + Returns a reference to the appended object. +*/ +wxJSONValue& +wxJSONValue::Append( const wxJSONValue& value ) +{ + wxJSONRefData* data = COW(); + wxJSON_ASSERT( data ); + if ( data->m_type != wxJSONTYPE_ARRAY ) { + // we have to change the type of the actual object to the array type + SetType( wxJSONTYPE_ARRAY ); + } + // we add the wxJSONValue object to the wxObjArray: note that the + // array makes a copy of the JSON-value object by calling its + // copy ctor thus using reference count + data->m_valArray.Add( value ); + wxJSONValue& v = data->m_valArray.Last(); + return v; +} + + +//! \overload Append( const wxJSONValue& ) +wxJSONValue& +wxJSONValue::Append( int i ) +{ + wxJSONValue v( i ); + wxJSONValue& r = Append( v ); + return r; +} + +//! \overload Append( const wxJSONValue& ) +wxJSONValue& +wxJSONValue::Append( short int i ) +{ + wxJSONValue v( i ); + wxJSONValue& r = Append( v ); + return r; +} + +//! \overload Append( const wxJSONValue& ) +wxJSONValue& +wxJSONValue::Append( long int l ) +{ + wxJSONValue v( l ); + wxJSONValue& r = Append( v ); + return r; +} + +//! \overload Append( const wxJSONValue& ) +wxJSONValue& +wxJSONValue::Append( bool b ) +{ + wxJSONValue v( b ); + wxJSONValue& r = Append( v ); + return r; +} + +//! \overload Append( const wxJSONValue& ) +wxJSONValue& +wxJSONValue::Append( unsigned int ui ) +{ + wxJSONValue v( ui ); + wxJSONValue& r = Append( v ); + return r; +} + +//! \overload Append( const wxJSONValue& ) +wxJSONValue& +wxJSONValue::Append( unsigned short ui ) +{ + wxJSONValue v( ui ); + wxJSONValue& r = Append( v ); + return r; +} + +//! \overload Append( const wxJSONValue& ) +wxJSONValue& +wxJSONValue::Append( unsigned long ul ) +{ + wxJSONValue v( ul ); + wxJSONValue& r = Append( v ); + return r; +} + +//! \overload Append( const wxJSONValue& ) +wxJSONValue& +wxJSONValue::Append( double d ) +{ + wxJSONValue v( d ); + wxJSONValue& r = Append( v ); + return r; +} + +//! \overload Append( const wxJSONValue& ) +wxJSONValue& +wxJSONValue::Append( const wxChar* str ) +{ + wxJSONValue v( str ); + wxJSONValue& r = Append( v ); + return r; +} + +//! \overload Append( const wxJSONValue& ) +wxJSONValue& +wxJSONValue::Append( const wxString& str ) +{ + wxJSONValue v( str ); + wxJSONValue& r = Append( v ); + return r; +} + +//! \overload Append( const wxJSONValue& ) +wxJSONValue& +wxJSONValue::Append( const wxMemoryBuffer& buff ) +{ + wxJSONValue v( buff ); + wxJSONValue& r = Append( v ); + return r; +} + +//! \overload Append( const wxJSONValue& ) +wxJSONValue& +wxJSONValue::Append( const void* buff, size_t len ) +{ + wxJSONValue v( buff, len ); + wxJSONValue& r = Append( v ); + return r; +} + + +//! Concatenate a string to this string object. +/*! + The function concatenates \c str to the string contained + in this object and returns TRUE if the operation is succefull. + If the value stored in this value is not a string object + the function does nothing and returns FALSE. + Note that in order to be successfull, the value must contain + a \b wxString object and not a pointer to C-string. +*/ +bool +wxJSONValue::Cat( const wxString& str ) +{ + wxJSONRefData* data = GetRefData(); + wxJSON_ASSERT( data ); + + bool r = false; + if ( data->m_type == wxJSONTYPE_STRING ) { + wxJSONRefData* data = COW(); + wxJSON_ASSERT( data ); + data->m_valString.append( str ); + r = true; + } + return r; +} + +//! Concatenate a memory buffer to this memory buffer object. +/*! + The function concatenates \c buff to the \b wxMemoryBuffer object contained + in this object and returns TRUE if the operation is succefull. + If the value stored in this value is not a memory buffer object + the function does nothing and returns FALSE. +*/ +bool +wxJSONValue::Cat( const wxMemoryBuffer& buff ) +{ + wxJSONRefData* data = GetRefData(); + wxJSON_ASSERT( data ); + + bool r = false; + if ( data->m_type == wxJSONTYPE_MEMORYBUFF ) { + wxJSONRefData* data = COW(); + wxJSON_ASSERT( data ); + data->m_memBuff->AppendData( buff.GetData(), buff.GetDataLen()); + r = true; + } + return r; +} + + +//! \overload Cat( const wxString& ) +bool +wxJSONValue::Cat( const wxChar* str ) +{ + wxJSONRefData* data = GetRefData(); + wxJSON_ASSERT( data ); + + bool r = false; + if ( data->m_type == wxJSONTYPE_STRING ) { + wxJSONRefData* data = COW(); + wxJSON_ASSERT( data ); + data->m_valString.append( str ); + r = true; + } + return r; +} + + +//! Remove the item at the specified index or key. +/*! + The function removes the item at index \c index or at the specified + key in the array or map. + If this object does not contain an array (for a index parameter) or a map + (for a key parameter), the function does nothing and returns FALSE. + If the element does not exist, FALSE is returned. +*/ +bool +wxJSONValue::Remove( int index ) +{ + wxJSONRefData* data = COW(); + wxJSON_ASSERT( data ); + + bool r = false; + if ( data->m_type == wxJSONTYPE_ARRAY ) { + data->m_valArray.RemoveAt( index ); + r = true; + } + return r; +} + + +//! \overload Remove( int ) +bool +wxJSONValue::Remove( const wxString& key ) +{ + wxJSONRefData* data = COW(); + wxJSON_ASSERT( data ); + + bool r = false; + if ( data->m_type == wxJSONTYPE_OBJECT ) { + wxJSONInternalMap::size_type count = data->m_valMap.erase( key ); + if ( count > 0 ) { + r = true; + } + } + return r; +} + + +//! Clear the object value. +/*! + This function causes the object to be empty. + The function simply calls UnRef() making this object to become + invalid and set its type to wxJSONTYPE_INVALID. +*/ +void +wxJSONValue::Clear() +{ + UnRef(); + SetType( wxJSONTYPE_INVALID ); +} + +// retrieve an item + +//! Return the item at the specified index. +/*! + The function returns a reference to the object at the specified + index. + If the element does not exist, the array is enlarged to \c index + 1 + elements and a reference to the last element will be returned. + New elements will contain NULL values. + If this object does not contain an array, the old value is + replaced by an array object which will be enlarged to the needed + dimension. +*/ +wxJSONValue& +wxJSONValue::Item( unsigned index ) +{ + wxJSONRefData* data = COW(); + wxJSON_ASSERT( data ); + + if ( data->m_type != wxJSONTYPE_ARRAY ) { + data = SetType( wxJSONTYPE_ARRAY ); + } + int size = Size(); + wxJSON_ASSERT( size >= 0 ); + // if the desired element does not yet exist, we create as many + // elements as needed; the new values will be 'null' values + if ( index >= (unsigned) size ) { + wxJSONValue v( wxJSONTYPE_NULL); + int missing = index - size + 1; + data->m_valArray.Add( v, missing ); + } + return data->m_valArray.Item( index ); +} + +//! Return the item at the specified key. +/*! + The function returns a reference to the object in the map + that has the specified key. + If \c key does not exist, a new NULL value is created with + the provided key and a reference to it is returned. + If this object does not contain a map, the old value is + replaced by a map object. +*/ +wxJSONValue& +wxJSONValue::Item( const wxString& key ) +{ + wxLogTrace( traceMask, _T("(%s) searched key=\'%s\'"), __PRETTY_FUNCTION__, key.c_str()); + wxLogTrace( traceMask, _T("(%s) actual object: %s"), __PRETTY_FUNCTION__, GetInfo().c_str()); + + wxJSONRefData* data = COW(); + wxJSON_ASSERT( data ); + + if ( data->m_type != wxJSONTYPE_OBJECT ) { + // deletes the contained value; + data = SetType( wxJSONTYPE_OBJECT ); + return data->m_valMap[key]; + } + wxLogTrace( traceMask, _T("(%s) searching key \'%s' in the actual object"), + __PRETTY_FUNCTION__, key.c_str() ); + return data->m_valMap[key]; +} + + +//! Return the item at the specified index. +/*! + The function returns a copy of the object at the specified + index. + If the element does not exist, the function returns an \b invalid value. +*/ +wxJSONValue +wxJSONValue::ItemAt( unsigned index ) const +{ + wxJSONRefData* data = GetRefData(); + wxJSON_ASSERT( data ); + + wxJSONValue v( wxJSONTYPE_INVALID ); + if ( data->m_type == wxJSONTYPE_ARRAY ) { + int size = Size(); + wxJSON_ASSERT( size >= 0 ); + if ( index < (unsigned) size ) { + v = data->m_valArray.Item( index ); + } + } + return v; +} + +//! Return the item at the specified key. +/*! + The function returns a copy of the object in the map + that has the specified key. + If \c key does not exist, an \b invalid value is returned. +*/ +wxJSONValue +wxJSONValue::ItemAt( const wxString& key ) const +{ + wxLogTrace( traceMask, _T("(%s) searched key=\'%s\'"), __PRETTY_FUNCTION__, key.c_str()); + wxLogTrace( traceMask, _T("(%s) actual object: %s"), __PRETTY_FUNCTION__, GetInfo().c_str()); + + wxJSONRefData* data = GetRefData(); + wxJSON_ASSERT( data ); + + wxJSONValue v( wxJSONTYPE_INVALID ); + if ( data->m_type == wxJSONTYPE_OBJECT ) { + wxJSONInternalMap::const_iterator it = data->m_valMap.find( key ); + if ( it != data->m_valMap.end() ) { + v = it->second; + } + } + return v; +} + + +//! Return the item at the specified index. +/*! + The function returns a reference to the object at the specified + index. + If the element does not exist, the array is enlarged to \c index + 1 + elements and a reference to the last element will be returned. + New elements will contain NULL values. + If this object does not contain an array, the old value is + replaced by an array object. +*/ +wxJSONValue& +wxJSONValue::operator [] ( unsigned index ) +{ + wxJSONValue& v = Item( index ); + return v; +} + +//! Return the item at the specified key. +/*! + The function returns a reference to the object in the map + that has the specified key. + If \c key does not exist, a new NULL value is created with + the provided key and a reference to it is returned. + If this object does not contain a map, the old value is + replaced by a map object. +*/ +wxJSONValue& +wxJSONValue::operator [] ( const wxString& key ) +{ + wxJSONValue& v = Item( key ); + return v; +} + +// +// assignment operators +// note that reference counting is only used if the original +// value is a wxJSONValue object +// in all other cases, the operator= function deletes the old +// content and assigns the new one + + +//! Assign the specified value to this object replacing the old value. +/*! + The assignment operator assigns to this object the value specified in the + right operand of the assignment operator. + Note that the old value is deleted but not the other data members + in the wxJSONRefData structure. + This is particularly usefull for the parser class which stores + comment lines in a temporary wxJSONvalue object that is of type + wxJSONTYPE_INVALID. + As comment lines may apear before the value they refer to, comments + are stored in a value that is not yet being read. + when the value is read, it is assigned to the temporary JSON value + object without deleting the comment lines. +*/ +wxJSONValue& +wxJSONValue::operator = ( int i ) +{ + wxJSONRefData* data = SetType( wxJSONTYPE_INT ); + data->m_value.VAL_INT = i; + return *this; +} + + +//! \overload operator = (int) +wxJSONValue& +wxJSONValue::operator = ( bool b ) +{ + wxJSONRefData* data = SetType( wxJSONTYPE_BOOL ); + data->m_value.m_valBool = b; + return *this; +} + +//! \overload operator = (int) +wxJSONValue& +wxJSONValue::operator = ( unsigned int ui ) +{ + wxJSONRefData* data = SetType( wxJSONTYPE_UINT ); + data->m_value.VAL_UINT = ui; + return *this; +} + +//! \overload operator = (int) +wxJSONValue& +wxJSONValue::operator = ( long l ) +{ + wxJSONRefData* data = SetType( wxJSONTYPE_INT ); + data->m_value.VAL_INT = l; + return *this; +} + +//! \overload operator = (int) +wxJSONValue& +wxJSONValue::operator = ( unsigned long ul ) +{ + wxJSONRefData* data = SetType( wxJSONTYPE_UINT ); + data->m_value.VAL_UINT = ul; + return *this; +} + + +//! \overload operator = (int) +wxJSONValue& +wxJSONValue::operator = ( short i ) +{ + wxJSONRefData* data = SetType( wxJSONTYPE_INT ); + data->m_value.VAL_INT = i; + return *this; +} + + +//! \overload operator = (int) +wxJSONValue& +wxJSONValue::operator = ( unsigned short ui ) +{ + wxJSONRefData* data = SetType( wxJSONTYPE_UINT ); + data->m_value.VAL_UINT = ui; + return *this; +} + +//! \overload operator = (int) +wxJSONValue& +wxJSONValue::operator = ( double d ) +{ + wxJSONRefData* data = SetType( wxJSONTYPE_DOUBLE ); + data->m_value.m_valDouble = d; + return *this; +} + + +//! \overload operator = (int) +wxJSONValue& +wxJSONValue::operator = ( const wxChar* str ) +{ + wxJSONRefData* data = SetType( wxJSONTYPE_CSTRING ); + data->m_value.m_valCString = str; +#if !defined( WXJSON_USE_CSTRING ) + data->m_type = wxJSONTYPE_STRING; + data->m_valString.assign( str ); +#endif + return *this; +} + +//! \overload operator = (int) +wxJSONValue& +wxJSONValue::operator = ( const wxString& str ) +{ + wxJSONRefData* data = SetType( wxJSONTYPE_STRING ); + data->m_valString.assign( str ); + return *this; +} + + +//! Assigns to this object a memory buffer type +/*! + As with the ctor, this function makes a deep copy of the + memory buffer \c buff so changes made to the original buffer + does not reflect to the memory buffer stored in this JSON value. +*/ +wxJSONValue& +wxJSONValue::operator = ( const wxMemoryBuffer& buff ) +{ + wxJSONRefData* data = SetType( wxJSONTYPE_MEMORYBUFF ); + data->m_memBuff = new wxMemoryBuffer(); + const void* ptr = buff.GetData(); + size_t len = buff.GetDataLen(); + if ( data->m_memBuff && len ) { + data->m_memBuff->AppendData( ptr, len ); + } + return *this; +} + + +//! Assignment operator using reference counting. +/*! + Unlike all other assignment operators, this one makes a + swallow copy of the other JSON value object. + The function calls \c Ref() to get a shared referenced + data. + \sa \ref json_internals_cow +*/ +wxJSONValue& +wxJSONValue::operator = ( const wxJSONValue& other ) +{ + Ref( other ); + return *this; +} + + +// finding elements + + +//! Return a value or a default value. +/*! + This function returns a copy of the value object for the specified key. + If the key is not found, a copy of \c defaultValue is returned. + Note that the returned values are not real copy of the \c key or the + default values because \e copy-on-write is used by this class. + However, you have to treat them as real copies; in other words, if you + change the values of the returned object your changes does not reflect + in the otiginal value. + Example: + \code + wxJSONValue defaultValue( 0 ); + wxJSONvalue v1; + v1["key"] = 100; // 'v1["key"]' contains the integer 100 + + // 'v2' contains 100 but it is a swallow copy of 'v1["key"]' + wxJSONValue v2 = v1.Get( "key", defaultValue ); + + // 'v1["key"]' still contains 100 + v2 = 200; + + // if you want your change to be reflected in the 'v1' object + // you have to assign it + v1["key"] = v2; + \endcode +*/ +wxJSONValue +wxJSONValue::Get( const wxString& key, const wxJSONValue& defaultValue ) const +{ + // NOTE: this function does many wxJSONValue copies. + // so implementing COW is a good thing + + // this is the first copy (the default value) + wxJSONValue v( defaultValue ); + + wxJSONRefData* data = GetRefData(); + wxJSON_ASSERT( data ); + if ( data->m_type == wxJSONTYPE_OBJECT ) { + wxJSONInternalMap::iterator it = data->m_valMap.find( key ); + if ( it != data->m_valMap.end() ) { + v = it->second; + } + } + return v; +} + + +// protected functions + +//! Find an element +/*! + The function returns a pointer to the element at index \c index + or a NULL pointer if \c index does not exist. + A NULL pointer is also returned if the object does not contain an + array nor a key/value map. +*/ +wxJSONValue* +wxJSONValue::Find( unsigned index ) const +{ + wxJSONRefData* data = GetRefData(); + wxJSON_ASSERT( data ); + + wxJSONValue* vp = 0; + + if ( data->m_type == wxJSONTYPE_ARRAY ) { + size_t size = data->m_valArray.GetCount(); + if ( index < size ) { + vp = &(data->m_valArray.Item( index )); + } + } + return vp; +} + +//! Find an element +/*! + The function returns a pointer to the element with key \c key + or a NULL pointer if \c key does not exist. + A NULL pointer is also returned if the object does not contain a + key/value map. +*/ +wxJSONValue* +wxJSONValue::Find( const wxString& key ) const +{ + wxJSONRefData* data = GetRefData(); + wxJSON_ASSERT( data ); + + wxJSONValue* vp = 0; + + if ( data->m_type == wxJSONTYPE_OBJECT ) { + wxJSONInternalMap::iterator it = data->m_valMap.find( key ); + if ( it != data->m_valMap.end() ) { + vp = &(it->second); + } + } + return vp; +} + + + +//! Return a string description of the type +/*! + This static function is only usefull for debugging purposes and + should not be used by users of this class. + It simply returns a string representation of the JSON value + type stored in a object. + For example, if \c type is wxJSONTYPE_INT the function returns the + string "wxJSONTYPE_INT". + If \c type is out of range, an empty string is returned (should + never happen). +*/ +wxString +wxJSONValue::TypeToString( wxJSONType type ) +{ + static const wxChar* str[] = { + _T( "wxJSONTYPE_INVALID" ), // 0 + _T( "wxJSONTYPE_NULL" ), // 1 + _T( "wxJSONTYPE_INT" ), // 2 + _T( "wxJSONTYPE_UINT" ), // 3 + _T( "wxJSONTYPE_DOUBLE" ), // 4 + _T( "wxJSONTYPE_STRING" ), // 5 + _T( "wxJSONTYPE_CSTRING" ), // 6 + _T( "wxJSONTYPE_BOOL" ), // 7 + _T( "wxJSONTYPE_ARRAY" ), // 8 + _T( "wxJSONTYPE_OBJECT" ), // 9 + _T( "wxJSONTYPE_LONG" ), // 10 + _T( "wxJSONTYPE_INT64" ), // 11 + _T( "wxJSONTYPE_ULONG" ), // 12 + _T( "wxJSONTYPE_UINT64" ), // 13 + _T( "wxJSONTYPE_SHORT" ), // 14 + _T( "wxJSONTYPE_USHORT" ), // 15 + _T( "wxJSONTYPE_MEMORYBUFF" ), // 16 + }; + + wxString s; + int idx = (int) type; + if ( idx >= 0 && idx < 17 ) { + s = str[idx]; + } + return s; +} + +//! Returns informations about the object +/*! + The function is only usefull for debugging purposes and will probably + be dropped in future versions. + Returns a string that contains info about the object such as: + + \li the type of the object + \li the size + \li the progressive counter + \li the pointer to referenced data + \li the progressive counter of referenced data + \li the number of share of referenced data + +The \c deep parameter is used to specify if the function will be called +recursively in order to dump sub-items. If the parameter is TRUE than a +deep dump is executed. + +The \c indent is the initial indentation: it is incremented by 3 every +time the Dump() function is called recursively. +*/ +wxString +wxJSONValue::Dump( bool deep, int indent ) const +{ + wxJSONRefData* data = GetRefData(); + wxJSON_ASSERT( data ); + + wxJSONType type = GetType(); + + wxString s; + if ( indent > 0 ) { + s.append( indent, ' ' ); + } + + wxString s1; + wxString s2; +#if defined( WXJSON_USE_VALUE_COUNTER ) + s1.Printf( _T("Object: Progr=%d Type=%s Size=%d comments=%d\n"), + m_progr, + TypeToString( type ).c_str(), + Size(), + data->m_comments.GetCount() ); + s2.Printf(_T(" : RefData=%p Progr=%d Num shares=%d\n"), + data, data->m_progr, data->GetRefCount() ); +#else + s1.Printf( _T("Object: Type=%s Size=%d comments=%d\n"), + TypeToString( type ).c_str(), + Size(), + data->m_comments.GetCount() ); + s2.Printf(_T(" : RefData=%p Num shares=%d\n"), + data, data->GetRefCount() ); +#endif + s.append( s1 ); + if ( indent > 0 ) { + s.append( indent, ' ' ); + } + s.append( s2 ); + + wxString sub; + + // if we have to do a deep dump, we call the Dump() function for + // every sub-item + if ( deep ) { + indent += 3; + const wxJSONInternalMap* map; + int size;; + wxJSONInternalMap::const_iterator it; + switch ( type ) { + case wxJSONTYPE_OBJECT : + map = AsMap(); + size = Size(); + for ( it = map->begin(); it != map->end(); ++it ) { + const wxJSONValue& v = it->second; + sub = v.Dump( true, indent ); + s.append( sub ); + } + break; + case wxJSONTYPE_ARRAY : + size = Size(); + for ( int i = 0; i < size; i++ ) { + const wxJSONValue* v = Find( i ); + wxJSON_ASSERT( v ); + sub = v->Dump( true, indent ); + s.append( sub ); + } + break; + default : + break; + } + } + return s; +} + +//! Returns informations about the object +/*! + The function is only usefull for debugging purposes and will probably + be dropped in future versions. + You should not rely on this function to exist in future versions. +*/ +wxString +wxJSONValue::GetInfo() const +{ + wxJSONRefData* data = GetRefData(); + wxJSON_ASSERT( data ); + + wxString s; +#if defined( WXJSON_USE_VALUE_CONTER ) + s.Printf( _T("Object: Progr=%d Type=%s Size=%d comments=%d\n"), + (int)data->m_progr, + wxJSONValue::TypeToString( data->m_type ).c_str(), + (int)Size(), + (int)data->m_comments.GetCount() ); +#else + s.Printf( _T("Object: Type=%s Size=%d comments=%d\n"), + wxJSONValue::TypeToString( data->m_type ).c_str(), + (int)Size(), (int)data->m_comments.GetCount() ); +#endif + if ( data->m_type == wxJSONTYPE_OBJECT ) { + wxArrayString arr = GetMemberNames(); + for ( unsigned int i = 0; i < arr.size(); i++ ) { + s.append( _T(" Member name: ")); + s.append( arr[i] ); + s.append( _T("\n") ); + } + } + return s; +} + +//! The comparison function +/*! + This function returns TRUE if this object looks like \c other. + Note that this class does not define a comparison operator + (the classical \b operator== function) because the notion + of \b equal for JSON values objects is not applicable. + The comment strings array are not compared: JSON value objects + are \b the \b same if they contains the same values, regardless the + comment's strings. + + Note that the function does not return the element that cause the + comparison to return FALSE. There is not a good structure to + tell this information. + If you need it for debugging purposes, you have to turn on the + \b sameas tracing feature by setting the WXTRACE environment + variable (you need a debug version of the application): + + \code + export WXTRACE=sameas // for unix systems that use bash + \endcode + + Note that if the two JSON value objects share the same referenced + data, the function immediatly returns TRUE without doing a deep + comparison which is, sure, useless. + For further info see \ref json_internals_compare. +*/ +bool +wxJSONValue::IsSameAs( const wxJSONValue& other ) const +{ + // this is a recursive function: it calls itself + // for every 'value' object in an array or map + bool r = false; + + // some variables used in the switch statement + int size; + wxJSONInternalMap::const_iterator it; + + // get the referenced data for the two objects + wxJSONRefData* data = GetRefData(); + wxJSONRefData* otherData = other.GetRefData(); + + if ( data == otherData ) { + wxLogTrace( compareTraceMask, _T("(%s) objects share the same referenced data - r=TRUE"), + __PRETTY_FUNCTION__ ); + return true; + } + + + // if the type does not match the function compares the values if + // they are of compatible types such as INT, UINT and DOUBLE + if ( data->m_type != otherData->m_type ) { + // if the types are not compatible, returns false + // otherwise compares the compatible types: INT, UINT and DOUBLE + double val; + switch ( data->m_type ) { + case wxJSONTYPE_INT : + if ( otherData->m_type == wxJSONTYPE_UINT ) { + // compare the bits and returns true if value is between 0 and LLONG_MAX + if ( (data->m_value.VAL_UINT <= LLONG_MAX ) && + (data->m_value.VAL_UINT == otherData->m_value.VAL_UINT)) + { + r = true; + } + } + else if ( otherData->m_type == wxJSONTYPE_DOUBLE ) { + val = data->m_value.VAL_INT; + if ( val == otherData->m_value.m_valDouble ) { + r = true; + } + } + else { + r = false; + } + break; + case wxJSONTYPE_UINT : + if ( otherData->m_type == wxJSONTYPE_INT ) { + // compare the bits and returns true if value is between 0 and LLONG_MAX + if ( (data->m_value.VAL_UINT <= LLONG_MAX ) && + (data->m_value.VAL_UINT == otherData->m_value.VAL_UINT)) + { + r = true; + } + } + else if ( otherData->m_type == wxJSONTYPE_DOUBLE ) { + val = data->m_value.VAL_UINT; + if ( val == otherData->m_value.m_valDouble ) { + r = true; + } + } + else { + r = false; + } + break; + case wxJSONTYPE_DOUBLE : + if ( otherData->m_type == wxJSONTYPE_INT ) { + val = otherData->m_value.VAL_INT; + if ( val == data->m_value.m_valDouble ) { + r = true; + } + } + else if ( otherData->m_type == wxJSONTYPE_UINT ) { + val = otherData->m_value.VAL_UINT; + if ( val == data->m_value.m_valDouble ) { + r = true; + } + } + else { + r = false; + } + break; + default: + r = false; + break; + } + return r; + } + + // the two objects have the same 'm_type' + + // for comparing wxJSONTYPE_CSTRING we use two temporary wxString + // objects: this is to avoid using strcmp() and wcscmp() which + // may not be available on all platforms + wxString s1, s2; + r = true; + int r1; + + switch ( data->m_type ) { + case wxJSONTYPE_INVALID : + case wxJSONTYPE_NULL : + // there is no need to compare the values + break; + case wxJSONTYPE_INT : + if ( data->m_value.VAL_INT != otherData->m_value.VAL_INT ) { + r = false; + } + break; + case wxJSONTYPE_UINT : + if ( data->m_value.VAL_UINT != otherData->m_value.VAL_UINT ) { + r = false; + } + break; + case wxJSONTYPE_DOUBLE : + if ( data->m_value.m_valDouble != otherData->m_value.m_valDouble ) { + r = false; + } + break; + case wxJSONTYPE_CSTRING : + s1 = wxString( data->m_value.m_valCString ); + s2 = wxString( otherData->m_value.m_valCString ); + if ( s1 != s2 ) { + r = false; + } + break; + case wxJSONTYPE_BOOL : + if ( data->m_value.m_valBool != otherData->m_value.m_valBool ) { + r = false; + } + break; + case wxJSONTYPE_STRING : + if ( data->m_valString != otherData->m_valString ) { + r = false; + } + break; + case wxJSONTYPE_MEMORYBUFF : + // we cannot simply use the operator ==; we need a deep comparison + r1 = CompareMemoryBuff( *(data->m_memBuff), *(otherData->m_memBuff)); + if ( r1 != 0 ) { + r = false; + } + break; + case wxJSONTYPE_ARRAY : + size = Size(); + wxLogTrace( compareTraceMask, _T("(%s) Comparing an array object - size=%d"), + __PRETTY_FUNCTION__, size ); + + if ( size != other.Size() ) { + wxLogTrace( compareTraceMask, _T("(%s) Sizes does not match"), + __PRETTY_FUNCTION__ ); + return false; + } + // compares every element in this object with the element of + // the same index in the 'other' object + for ( int i = 0; i < size; i++ ) { + wxLogTrace( compareTraceMask, _T("(%s) Comparing array element=%d"), + __PRETTY_FUNCTION__, i ); + wxJSONValue v1 = ItemAt( i ); + wxJSONValue v2 = other.ItemAt( i ); + + if ( !v1.IsSameAs( v2 )) { + return false; + } + } + break; + case wxJSONTYPE_OBJECT : + size = Size(); + wxLogTrace( compareTraceMask, _T("(%s) Comparing a map obejct - size=%d"), + __PRETTY_FUNCTION__, size ); + + if ( size != other.Size() ) { + wxLogTrace( compareTraceMask, _T("(%s) Comparison failed - sizes does not match"), + __PRETTY_FUNCTION__ ); + return false; + } + // for every key calls itself on the value found in + // the other object. if 'key' does no exist, returns FALSE + for ( it = data->m_valMap.begin(); it != data->m_valMap.end(); it++ ) { + wxString key = it->first; + wxLogTrace( compareTraceMask, _T("(%s) Comparing map object - key=%s"), + __PRETTY_FUNCTION__, key.c_str() ); + wxJSONValue otherVal = other.ItemAt( key ); + bool isSame = it->second.IsSameAs( otherVal ); + if ( !isSame ) { + wxLogTrace( compareTraceMask, _T("(%s) Comparison failed for the last object"), + __PRETTY_FUNCTION__ ); + return false; + } + } + break; + default : + // should never happen + wxFAIL_MSG( _T("wxJSONValue::IsSameAs() unexpected wxJSONType")); + break; + } + return r; +} + +//! Add a comment to this JSON value object. +/*! + The function adds a comment string to this JSON value object and returns + the total number of comment strings belonging to this value. + Note that the comment string must be a valid C/C++ comment because the + wxJSONWriter does not modify it. + In other words, a C++ comment string must start with '//' and must end with + a new-line character. If the final LF char is missing, the + automatically adds it. + You can also add C-style comments which must be enclosed in the usual + C-comment characters. + For C-style comments, the function does not try to append the final comment + characters but allows trailing whitespaces and new-line chars. + The \c position parameter is one of: + + \li wxJSONVALUE_COMMENT_BEFORE: the comment will be written before the value + \li wxJSONVALUE_COMMENT_INLINE: the comment will be written on the same line + \li wxJSONVALUE_COMMENT_AFTER: the comment will be written after the value + \li wxJSONVALUE_COMMENT_DEFAULT: the old value of comment's position is not + changed; if no comments were added to the value object this is the + same as wxJSONVALUE_COMMENT_BEFORE. + + To know more about comment's storage see \ref json_comment_add + +*/ +int +wxJSONValue::AddComment( const wxString& str, int position ) +{ + wxJSONRefData* data = COW(); + wxJSON_ASSERT( data ); + + wxLogTrace( traceMask, _T("(%s) comment=%s"), __PRETTY_FUNCTION__, str.c_str() ); + int r = -1; + int len = str.length(); + if ( len < 2 ) { + wxLogTrace( traceMask, _T(" error: len < 2") ); + return -1; + } + if ( str[0] != '/' ) { + wxLogTrace( traceMask, _T(" error: does not start with\'/\'") ); + return -1; + } + if ( str[1] == '/' ) { // a C++ comment: check that it ends with '\n' + wxLogTrace( traceMask, _T(" C++ comment" )); + if ( str.GetChar(len - 1) != '\n' ) { + wxString temp( str ); + temp.append( 1, '\n' ); + data->m_comments.Add( temp ); + wxLogTrace( traceMask, _T(" C++ comment: LF added") ); + } + else { + data->m_comments.Add( str ); + } + r = data->m_comments.size(); + } + else if ( str[1] == '*' ) { // a C-style comment: check that it ends with '*/' + wxLogTrace( traceMask, _T(" C-style comment") ); + int lastPos = len - 1; + wxChar ch = str.GetChar( lastPos ); + // skip leading whitespaces + while ( ch == ' ' || ch == '\n' || ch == '\t' ) { + --lastPos; + ch = str.GetChar( lastPos ); + } + if ( str.GetChar( lastPos ) == '/' && str.GetChar( lastPos - 1 ) == '*' ) { + data->m_comments.Add( str ); + r = data->m_comments.size(); + } + } + else { + wxLogTrace( traceMask, _T(" error: is not a valid comment string") ); + r = -1; + } + // if the comment was stored, store the position + if ( r >= 0 && position != wxJSONVALUE_COMMENT_DEFAULT ) { + data->m_commentPos = position; + } + return r; +} + +//! Add one or more comments to this JSON value object. +/*! + The function adds the strings contained in \c comments to the comment's + string array of this value object by calling the AddComment( const wxString&,int) + function for every string in the \c comment array. + Returns the number of strings correctly added. +*/ +int +wxJSONValue::AddComment( const wxArrayString& comments, int position ) +{ + int siz = comments.GetCount(); int r = 0; + for ( int i = 0; i < siz; i++ ) { + int r2 = AddComment( comments[i], position ); + if ( r2 >= 0 ) { + ++r; + } + } + return r; +} + +//! Return a comment string. +/*! + The function returns the comment string at index \c idx. + If \c idx is out of range, an empty string is returned. + If \c idx is equal to -1, then the function returns a string + that contains all comment's strings stored in the array. +*/ +wxString +wxJSONValue::GetComment( int idx ) const +{ + wxJSONRefData* data = GetRefData(); + wxJSON_ASSERT( data ); + + wxString s; + int size = data->m_comments.GetCount(); + if ( idx < 0 ) { + for ( int i = 0; i < size; i++ ) { + s.append( data->m_comments[i] ); + } + } + else if ( idx < size ) { + s = data->m_comments[idx]; + } + return s; +} + +//! Return the number of comment strings. +int +wxJSONValue::GetCommentCount() const +{ + wxJSONRefData* data = GetRefData(); + wxJSON_ASSERT( data ); + + int d = data->m_comments.GetCount(); + wxLogTrace( traceMask, _T("(%s) comment count=%d"), __PRETTY_FUNCTION__, d ); + return d; +} + +//! Return the comment position. +int +wxJSONValue::GetCommentPos() const +{ + wxJSONRefData* data = GetRefData(); + wxJSON_ASSERT( data ); + return data->m_commentPos; +} + +//! Get the comment string's array. +const wxArrayString& +wxJSONValue::GetCommentArray() const +{ + wxJSONRefData* data = GetRefData(); + wxJSON_ASSERT( data ); + + return data->m_comments; +} + +//! Clear all comment strings +void +wxJSONValue::ClearComments() +{ + wxJSONRefData* data = COW(); + wxJSON_ASSERT( data ); + + data->m_comments.clear(); +} + + +//! Set the type of the stored value. +/*! + The function sets the type of the stored value as specified in + the provided argument. + If the actual type is equal to \c type, nothing happens and this + JSON value object retains the original type and value. + If the type differs, however, the original type and value are + lost. + + The function just sets the type of the object and not the + value itself. + If the object does not have a data structure it is allocated + using the CreateRefData() function unless the type to be set + is wxJSONTYPE_INVALID. In this case and if a data structure is + not yet allocated, it is not allocated. + + If the object already contains a data structure it is not deleted + but the type is changed in the original data structure. + Complex values in the old structure are cleared. + The \c type argument can be one of the following: + + \li wxJSONTYPE_INVALID: an empty (not initialized) JSON value + \li wxJSONTYPE_NULL: a NULL value + \li wxJSONTYPE_INT: an integer value + \li wxJSONTYPE_UINT: an unsigned integer + \li wxJSONTYPE_DOUBLE: a double precision number + \li wxJSONTYPE_BOOL: a boolean + \li wxJSONTYPE_CSTRING: a C string + \li wxJSONTYPE_STRING: a wxString object + \li wxJSONTYPE_ARRAY: an array of wxJSONValue objects + \li wxJSONTYPE_OBJECT: a hashmap of key/value pairs where \e value is a wxJSONValue object + \li wxJSONTYPE_LONG: a 32-bits integer value + \li wxJSONTYPE_ULONG: an unsigned 32-bits integer + \li wxJSONTYPE_INT64: a 64-bits integer value + \li wxJSONTYPE_UINT64: an unsigned 64-bits integer + \li wxJSONTYPE_SHORT: a signed short integer + \li wxJSONTYPE_USHORT: an unsigned short integer + \li wxJSONTYPE_MEMORYBUFF: a binary memory buffer + + The integer storage depends on the platform: for platforms that support 64-bits + integers, integers are always stored as 64-bits integers. + On platforms that do not support 64-bits integers, ints are stored as \b long \b int. + To know more about the internal representation of integers, read + \ref json_internals_integer. + + Note that there is no need to set a type for the object in order to assign + a value to it. + In other words, if you want to construct a JSON value which holds an integer + value of 10, just use the specific constructor: + \code + wxJSONValue value( 10 ); + \endcode + which sets the integer type and also the numeric value. + Moreover, there is no need to set the type for none of the handled types, + not only for primitive types but for complex types, too. + For example, if you want to construct an array of JSON values, just use + the default ctor and call the Append() member function which will append the + first element to the array and will set the array type: + \code + wxJSONValue value; + value.Append( "a string" ); + \endcode + \sa GetType +*/ +wxJSONRefData* +wxJSONValue::SetType( wxJSONType type ) +{ + wxJSONRefData* data = GetRefData(); + wxJSONType oldType = GetType(); + + // check that type is within the allowed range + wxJSON_ASSERT((type >= wxJSONTYPE_INVALID) && (type <= wxJSONTYPE_MEMORYBUFF)); + if ( (type < wxJSONTYPE_INVALID) || (type > wxJSONTYPE_MEMORYBUFF) ) { + type = wxJSONTYPE_INVALID; + } + + // the function unshares the referenced data but does not delete the + // structure. This is because the wxJSON reader stores comments + // that apear before the value in a temporary value of type wxJSONTYPE_INVALID + // which is invalid and, next, it stores the JSON value in the same + // wxJSONValue object. + // If we would delete the structure using 'Unref()' we loose the + // comments + data = COW(); + + // do nothing if the actual type is the same as 'type' + if ( type == oldType ) { + return data; + } + + // change the type of the referened structure + // NOTE: integer types are always stored as the generic integer types + if ( type == wxJSONTYPE_LONG || type == wxJSONTYPE_INT64 || type == wxJSONTYPE_SHORT ) { + type = wxJSONTYPE_INT; + } + if ( type == wxJSONTYPE_ULONG || type == wxJSONTYPE_UINT64 || type == wxJSONTYPE_USHORT ) { + type = wxJSONTYPE_UINT; + } + + wxJSON_ASSERT( data ); + data->m_type = type; + + // clears complex objects of the old type + switch ( oldType ) { + case wxJSONTYPE_STRING: + data->m_valString.clear(); + break; + case wxJSONTYPE_ARRAY: + data->m_valArray.Clear(); + break; + case wxJSONTYPE_OBJECT: + data->m_valMap.clear(); + break; + case wxJSONTYPE_MEMORYBUFF: + // we first have to delete the actual memory buffer, if any + if ( data->m_memBuff ) { + delete data->m_memBuff; + data->m_memBuff = 0; + } + break; + default : + // there is not need to clear primitive types + break; + } + + // if the WXJSON_USE_CSTRING macro is not defined, the class forces + // C-string to be stored as wxString objects +#if !defined( WXJSON_USE_CSTRING ) + if ( data->m_type == wxJSONTYPE_CSTRING ) { + data->m_type = wxJSONTYPE_STRING; + } +#endif + return data; +} + +//! Return the line number of this JSON value object +/*! + The line number of a JSON value object is set to -1 when the + object is constructed. + The line number is set by the parser class, wxJSONReader, when + a JSON text is read from a stream or a string. + it is used when reading a comment line: comment lines that apear + on the same line as a value are considered \b inline comments of + the value. +*/ +int +wxJSONValue::GetLineNo() const +{ + // return ZERO if there is not a referenced data structure + int n = 0; + wxJSONRefData* data = GetRefData(); + if ( data != 0 ) { + n = data->m_lineNo; + } + return n; +} + +//! Set the line number of this JSON value object. +void +wxJSONValue::SetLineNo( int num ) +{ + wxJSONRefData* data = COW(); + wxJSON_ASSERT( data ); + data->m_lineNo = num; +} + +//! Set the pointer to the referenced data. +void +wxJSONValue::SetRefData(wxJSONRefData* data) +{ + m_refData = data; +} + +//! Increments the referenced data counter. +void +wxJSONValue::Ref(const wxJSONValue& clone) +{ + // nothing to be done + if (m_refData == clone.m_refData) + return; + + // delete reference to old data + UnRef(); + + // reference new data + if ( clone.m_refData ) { + m_refData = clone.m_refData; + ++(m_refData->m_refCount); + } +} + +//! Unreferences the shared data +/*! + The function decrements the number of shares in wxJSONRefData::m_refCount + and if it is ZERO, deletes the referenced data. + It is called by the destructor and by the copy-on-write functions. +*/ +void +wxJSONValue::UnRef() +{ + if ( m_refData ) { + wxASSERT_MSG( m_refData->m_refCount > 0, _T("invalid ref data count") ); + + if ( --m_refData->m_refCount == 0 ) { + delete m_refData; + m_refData = NULL; + } + } +} + +//! Makes an exclusive copy of shared data +void +wxJSONValue::UnShare() +{ + AllocExclusive(); +} + + +//! Do a deep copy of the other object. +/*! + This function allocates a new ref-data structure and copies it + from the object \c other. +*/ +void +wxJSONValue::DeepCopy( const wxJSONValue& other ) +{ + UnRef(); + wxJSONRefData* data = CloneRefData( other.m_refData ); + SetRefData( data ); +} + +//! Return the pointer to the referenced data structure. +wxJSONRefData* +wxJSONValue::GetRefData() const +{ + wxJSONRefData* data = m_refData; + return data; +} + + +//! Make a copy of the referenced data. +/*! + The function allocates a new instance of the wxJSONRefData + structure, copies the content of \c other and returns the pointer + to the newly created structure. + This function is called by the wxObject::UnRef() function + when a non-const member function is called on multiple + referenced data. +*/ +wxJSONRefData* +wxJSONValue::CloneRefData( const wxJSONRefData* otherData ) const +{ + wxJSON_ASSERT( otherData ); + + // make a static cast to pointer-to-wxJSONRefData + const wxJSONRefData* other = otherData; + + // allocate a new instance of wxJSONRefData using the default + // ctor; we cannot use the copy ctor of a wxJSONRefData + wxJSONRefData* data = new wxJSONRefData(); + + // copy the referenced data structure's data members + data->m_type = other->m_type; + data->m_value = other->m_value; + data->m_commentPos = other->m_commentPos; + data->m_comments = other->m_comments; + data->m_lineNo = other->m_lineNo; + data->m_valString = other->m_valString; + data->m_valArray = other->m_valArray; + data->m_valMap = other->m_valMap; + + // if the data contains a wxMemoryBuffer object, then we have + // to make a deep copy of the buffer by allocating a new one because + // wxMemoryBuffer is not a copy-on-write structure + if ( other->m_memBuff ) { + data->m_memBuff = new wxMemoryBuffer(); + const void* ptr = data->m_memBuff->GetData(); + size_t len = data->m_memBuff->GetDataLen(); + if ( data->m_memBuff && len ) { + data->m_memBuff->AppendData( ptr, len ); + } + } + + wxLogTrace( cowTraceMask, _T("(%s) CloneRefData() PROGR: other=%d data=%d"), + __PRETTY_FUNCTION__, other->GetRefCount(), data->GetRefCount() ); + + return data; +} + +//! Create a new data structure +/*! + The function allocates a new instance of the wxJSONRefData + structure and returns its pointer. + The type of the JSON value is set to wxJSONTYPE_INVALID (= + a not initialized value). +*/ +wxJSONRefData* +wxJSONValue::CreateRefData() const +{ + wxJSONRefData* data = new wxJSONRefData(); + data->m_type = wxJSONTYPE_INVALID; + return data; +} + + + +//! Make sure the referenced data is unique +/*! + This function is called by all non-const member functions and makes + sure that the referenced data is unique by calling \b UnShare() + If the referenced data is shared acrosss other wxJSONValue instances, + the \c UnShare() function makes a private copy of the shared data. +*/ +wxJSONRefData* +wxJSONValue::COW() +{ + wxJSONRefData* data = GetRefData(); + wxLogTrace( cowTraceMask, _T("(%s) COW() START data=%p data->m_count=%d"), + __PRETTY_FUNCTION__, data, data->GetRefCount()); + UnShare(); + data = GetRefData(); + wxLogTrace( cowTraceMask, _T("(%s) COW() END data=%p data->m_count=%d"), + __PRETTY_FUNCTION__, data, data->GetRefCount()); + return GetRefData(); +} + +//! Makes a private copy of the referenced data +void +wxJSONValue::AllocExclusive() +{ + if ( !m_refData ) { + m_refData = CreateRefData(); + } + else if ( m_refData->GetRefCount() > 1 ) { + // note that ref is not going to be destroyed in this case + const wxJSONRefData* ref = m_refData; + UnRef(); + + // ... so we can still access it + m_refData = CloneRefData(ref); + } + //else: ref count is 1, we are exclusive owners of m_refData anyhow + + wxASSERT_MSG( m_refData && m_refData->GetRefCount() == 1, + _T("wxObject::AllocExclusive() failed.") ); +} + +//! Convert memory buffer object to a string representation. +/*/ + The fucntion returns a string representation of the data contained in the + memory buffer object \c buff. + The string is conposed of two hexadecimal digits for every byte contained + in the memory buffer; bytes are separated by a space character. + The string starts with the actual lenght of the data enclosed in parenthesis. + The string will contain \c len bytes if \c len is less than the length + of the actual data in \c buff. + Note that the (len) printed in the output referes to the length of the buffer + which may be greater than the length that has to be printed. + + \b Example: + This is an example of printing a memory buffer object that contains 10 bytes: + \code + 0x80974653 (10) 00 01 02 03 04 05 06 07 08 09 + \endcode +*/ +wxString +wxJSONValue::MemoryBuffToString( const wxMemoryBuffer& buff, size_t len ) +{ + size_t buffLen = buff.GetDataLen(); + void* ptr = buff.GetData(); + wxString s = MemoryBuffToString( ptr, MIN( buffLen, len ), buffLen ); + return s; +} + + +//! Convert a binary memory buffer to a string representation. +/*/ + The function returns a string representation of the data contained in the + binary memory buffer pointed to by \c buff for \c len bytes. + The string is composed of two hexadecimal digits for every byte contained + in the memory buffer; bytes are separated by a space character. + The string starts with pointer to binary data followed by the lenght of the + data enclosed in parenthesis. + + \b Example: + This is an example of printing ten bytes from a memory buffer: + \code + 0x80974653 (10) 00 01 02 03 04 05 06 07 08 09 + \endcode + + @param buff the pointer to the memory buffer data + @len the length of the data that has to be printed + @actualLen the real lenght of the memory buffer that has to be printed + just afetr the pointer; may be greater than \c len. If this parameter + is -1 then it is equal to \c len +*/ +wxString +wxJSONValue::MemoryBuffToString( const void* buff, size_t len, size_t actualLen ) +{ + wxString s; + size_t buffLen = actualLen; + if (buffLen == (size_t) -1 ) { + buffLen = len; + } + s.Printf( _T("%p (%u) "), buff, buffLen ); + unsigned char* ptr = (unsigned char*) buff; + for ( unsigned int i = 0; i < len; i++ ) { + unsigned char c = *ptr; + ++ptr; + // now convert the character + char c1 = c / 16; + char c2 = c % 16; + c1 += '0'; + c2 += '0'; + if ( c1 > '9' ) { + c1 += 7; + } + if ( c2 > '9' ) { + c2 += 7; + } + s.Append( c1, 1 ); + s.Append( c2, 1 ); + s.Append( ' ', 1 ); // a space separates the bytes + } + return s; +} + +//! Compares two memory buffer objects +/*! + The function is the counterpart of the comparison operator == for two wxMemoryBuffer + objects. + You may noticed that the wxMemoryBuffer class does not define comparison operators so + if you write a code snippset like the following: + \code + wxMemoryBuffer b1; + wxMemoryBuffer b2; + b1.AppendData( "1234567890", 10 ); + b2.AppendData( "1234567890", 10 ); + bool r = b1 == b2; + \endcode + + you may expect that \b r is TRUE, because both objects contain the same data. + This is not true. The result you get is FALSE because the default comparison operator + is used, which just compares the data members of the class. + The data member is the pointer to the allocated memory that contains the data and + they are not equal. + This function uses the (fast) \b memcmp function to compare the actual data + contained in the nenory buffer objects thus doing a deep comparison. + The function returns the return value of \b memcmp: + + the memcmp() function returns an integer less than, equal to, or + greater than zero if the first n bytes of \c buff1 is found, respectively, to + be less than, to match, or be greater than the first n bytes of \c buff2. +*/ +int +wxJSONValue::CompareMemoryBuff( const wxMemoryBuffer& buff1, const wxMemoryBuffer& buff2 ) +{ + int r; + size_t buff1Len = buff1.GetDataLen(); + size_t buff2Len = buff2.GetDataLen(); + if ( buff1Len > buff2Len ) { + r = 1; + } + else if ( buff1Len < buff2Len ) { + r = -1; + } + else { + r = memcmp( buff1.GetData(), buff2.GetData(), buff1Len ); + } + return r; +} + +//! Compares a memory buffer object and a memory buffer +/*! + The function compares the data contained in a memory buffer object with a + memory buffer. + This function uses the (fast) \b memcmp function to compare the actual data + contained in the nenory buffer object thus doing a deep comparison. + The function returns the return value of \b memcmp: + + The memcmp() function returns an integer less than, equal to, or + greater than zero if the first n bytes of \c buff1 is found, respectively, to + be less than, to match, or be greater than the first n bytes of \c buff2. +*/ +int +wxJSONValue::CompareMemoryBuff( const wxMemoryBuffer& buff1, const void* buff2 ) +{ + int r; + size_t buff1Len = buff1.GetDataLen(); + r = memcmp( buff1.GetData(), buff2, buff1Len ); + return r; +} + + +//! Converts an array of INTs to a memory buffer +/*! + This static function converts an array of INTs stored in a wxJSONvalue object + into a memory buffer object. + The wxJSONvalue object passed as parameter must be of type ARRAY and must contain + INT types whose values are between 0 and 255. + + Every element of the array si converted to a BYTE value and appended to the returned + wxMemoryBuffer object. The following rules apply in the conversion: + \li if \c value is not an ARRAY type, an empty memory buffer is returned + \li if the \c value array contains elements of type other than INT, those + elements are ignored + \li if the \c value array contains elements of type INT which value is outside the + range 0..255, those elements are ignored + \li if the \c value array contains only ignored elements an empty wxMemoryBuffer + object is returned. + + This function can be used to get a memory buffer object from valid JSON text. + Please note that the wxJSONReader cannot know which array of INTs represent a binary + memory buffer unless you use the \b wxJSON \e memory \e buffer extension in the writer and + in the reader. +*/ +wxMemoryBuffer +wxJSONValue::ArrayToMemoryBuff( const wxJSONValue& value ) +{ + wxMemoryBuffer buff; + if ( value.IsArray() ) { + int len = value.Size(); + for ( int i = 0; i < len; i++ ) { + short int byte; unsigned char c; + // we do not use opertaor [] because it is not const + // bool r = value[i].AsShort( byte ); + bool r = value.ItemAt(i).AsShort( byte ); + if ( r && ( byte >= 0 && byte <= 255 ) ) { + c = (unsigned char) byte; + buff.AppendByte( c ); + } + } + } + return buff; +} + + +/************************************************************************* + + 64-bits integer support + +*************************************************************************/ + +#if defined( wxJSON_64BIT_INT) + + +//! \overload wxJSONValue() +wxJSONValue::wxJSONValue( wxInt64 i ) +{ + m_refData = 0; + wxJSONRefData* data = Init( wxJSONTYPE_INT ); + wxJSON_ASSERT( data ); + if ( data != 0 ) { + data->m_value.VAL_INT = i; + } +} + +//! \overload wxJSONValue() +wxJSONValue::wxJSONValue( wxUint64 ui ) +{ + m_refData = 0; + wxJSONRefData* data = Init( wxJSONTYPE_UINT ); + wxJSON_ASSERT( data ); + if ( data != 0 ) { + data->m_value.VAL_UINT = ui; + } +} + +//! Return TRUE if the stored value is a 32-bits integer +/*! + This function is only available on 64-bits platforms and returns + TRUE if, and only if, the stored value is of type \b wxJSONTYPE_INT + and the numeric value fits in a 32-bits signed integer. + The function just calls IsLong() and returns the value returned by + that function. + The use of this function is deprecated: use \c IsLong() instead +*/ +bool +wxJSONValue::IsInt32() const +{ + bool r = IsLong(); + return r; +} + +//! Return TRUE if the stored value is a unsigned 32-bits integer +/*! + This function is only available on 64-bits platforms and returns + TRUE if, and only if, the stored value is of type \b wxJSONTYPE_UINT + and the numeric value fits in a 32-bits unsigned integer. + The function just calls IsULong() and returns the value returned by + that function. + The use of this function is deprecated: use \c IsULong() instead +*/ +bool +wxJSONValue::IsUInt32() const +{ + bool r = IsULong(); + return r; +} + + +//! Return TRUE if the stored value is integer. +/*! + This function returns TRUE if the stored value is of + type signed integer. + In other words, the function returns TRUE if the \c wxJSONRefData::m_type + data member is of type \c wxJSONTYPE_INT + The function is only available if 64-bits integer support is enabled. + + \sa \ref json_internals_integer +*/ +bool +wxJSONValue::IsInt64() const +{ + wxJSONRefData* data = GetRefData(); + wxJSON_ASSERT( data ); + bool r = false; + if ( data->m_type == wxJSONTYPE_INT ) { + r = true; + } + return r; +} + + +//! Return TRUE if the stored value is a unsigned integer +/*! + This function returns TRUE if the stored value is of + type unsigned integer. + In other words, the function returns TRUE if the \c wxJSONRefData::m_type + data member is of type \c wxJSONTYPE_UINT. + The function is only available if 64-bits integer support is enabled. + + \sa \ref json_internals_integer +*/ +bool +wxJSONValue::IsUInt64() const +{ + wxJSONRefData* data = GetRefData(); + wxJSON_ASSERT( data ); + bool r = false; + if ( data->m_type == wxJSONTYPE_UINT ) { + r = true; + } + return r; +} + +//! Returns the low-order 32 bits of the value as an integer +/*! + This function is only available on 64-bits platforms and returns + the low-order 32-bits of the integer stored in the JSON value. + Note that all integer types are stored as \b wx(U)Int64 data types by + the JSON value class and that the function does not check that the + numeric value fits in a 32-bit integer. + The function just calls AsLong() and casts the value in a wxInt32 data + type + + \sa \ref wxjson_tutorial_get +*/ +wxInt32 +wxJSONValue::AsInt32() const +{ + wxInt32 i; + i = (wxInt32) AsLong(); + return i; +} + +//! Returns the low-order 32 bits of the value as an unsigned integer +/*! + This function is only available on 64-bits platforms and returns + the low-order 32-bits of the integer stored in the JSON value. + Note that all integer types are stored as \b wx(U)Int64 data types by + the JSON value class and that the function does not check that the + numeric value fits in a 32-bit integer. + The function just calls AsULong() and casts the value in a wxUInt32 data + type + + \sa \ref wxjson_tutorial_get +*/ +wxUint32 +wxJSONValue::AsUInt32() const +{ + wxUint32 ui; + ui = (wxUint32) AsULong(); + return ui; +} + + +//! Return the numeric value as a 64-bit integer. +/*! + This function is only available on 64-bits platforms and returns + the numeric value as a 64-bit integer. + + Note that the function does not check that the type of the + value is actually an integer and it just returns the content + of the wxJSONValueHolder union. + However, in debug builds, the function ASSERTs that the + type of the stored value is wxJSONTYPE_INT. + + \sa \ref json_internals_integer + \sa \ref wxjson_tutorial_get +*/ +wxInt64 +wxJSONValue::AsInt64() const +{ + wxJSONRefData* data = GetRefData(); + wxJSON_ASSERT( data ); + wxInt64 i64 = data->m_value.m_valInt64; + + wxJSON_ASSERT( IsInt64()); // exapnds only in debug builds + return i64; +} + +//! Return the numeric value as a 64-bit unsigned integer. +/*! + This function is only available on 64-bits platforms and returns + the numeric value as a 64-bit unsigned integer. + + Note that the function does not check that the type of the + value is actually an integer and it just returns the content + of the wxJSONValueHolder union. + However, in debug builds, the function wxJSON_ASSERTs that the + type of the stored value is wxJSONTYPE_UINT. + + \sa \ref json_internals_integer + \sa \ref wxjson_tutorial_get +*/ +wxUint64 +wxJSONValue::AsUInt64() const +{ + wxJSONRefData* data = GetRefData(); + wxJSON_ASSERT( data ); + wxUint64 ui64 = data->m_value.m_valUInt64; + + wxJSON_ASSERT( IsUInt64()); // exapnds only in debug builds + return ui64; +} + +bool +wxJSONValue::AsInt32( wxInt32& i32 ) const +{ + bool r = IsInt32(); + if ( r ) { + i32 = AsInt32(); + } + return r; +} + +bool +wxJSONValue::AsUInt32( wxUint32& ui32 ) const +{ + bool r = IsUInt32(); + if ( r ) { + ui32 = AsUInt32(); + } + return r; +} + +bool +wxJSONValue::AsInt64( wxInt64& i64 ) const +{ + bool r = IsInt64(); + if ( r ) { + i64 = AsInt64(); + } + return r; +} + +bool +wxJSONValue::AsUInt64( wxUint64& ui64 ) const +{ + bool r = IsUInt64(); + if ( r ) { + ui64 = AsUInt64(); + } + return r; +} + +//! \overload Append( const wxJSONValue& ) +wxJSONValue& +wxJSONValue::Append( wxInt64 i ) +{ + wxJSONValue v( i ); + wxJSONValue& r = Append( v ); + return r; +} + +//! \overload Append( const wxJSONValue& ) +wxJSONValue& +wxJSONValue::Append( wxUint64 ui ) +{ + wxJSONValue v( ui ); + wxJSONValue& r = Append( v ); + return r; +} + + +//! \overload operator = (int) +wxJSONValue& +wxJSONValue::operator = ( wxInt64 i ) +{ + wxJSONRefData* data = SetType( wxJSONTYPE_INT ); + data->m_value.VAL_INT = i; + return *this; +} + +//! \overload operator = (int) +wxJSONValue& +wxJSONValue::operator = ( wxUint64 ui ) +{ + wxJSONRefData* data = SetType( wxJSONTYPE_UINT ); + data->m_value.VAL_UINT = ui; + return *this; +} + + +#endif // defined( wxJSON_64BIT_INT ) + + + + +/* +{ +} +*/ + diff --git a/ThirdParty/wxJSON/src/jsonwriter.cpp b/ThirdParty/wxJSON/src/jsonwriter.cpp new file mode 100644 index 0000000..37def98 --- /dev/null +++ b/ThirdParty/wxJSON/src/jsonwriter.cpp @@ -0,0 +1,1274 @@ +///////////////////////////////////////////////////////////////////////////// +// Name: jsonwriter.cpp +// Purpose: the wxJSONWriter class: a JSON text generator +// Author: Luciano Cattani +// Created: 2007/10/12 +// RCS-ID: $Id: jsonwriter.cpp,v 1.6 2008/03/03 19:05:47 luccat Exp $ +// Copyright: (c) 2007 Luciano Cattani +// Licence: wxWidgets licence +///////////////////////////////////////////////////////////////////////////// + +#ifdef __GNUG__ + #pragma implementation "jsonwriter.cpp" +#endif + + +#include + +#include +#include +#include +#include + +static const wxChar* writerTraceMask = _T("traceWriter"); + +/*! \class wxJSONWriter + \brief The JSON document writer + + This class is a JSON document writer and it is used to write a + wxJSONValue object to an output stream or to a string object. + The ctor accepts some parameters which can be used to + change the style of the output. + The default output is in human-readable format that uses a three-space + indentation for object / array sub-items and separates every + value with a linefeed character. + + + \par Examples + + Using the default writer constructor + + \code + // construct the JSON value object and add values to it + wxJSONValue root; + root["key1"] = "some value"; + ... + + // construct the string that will contain the JSON text + wxString str; + + // construct a JSON writer: use the default writer's settings + wxJSONWriter writer; + + // call the writer's Write() memberfunction + writer.Write( root, str ); + \endcode + + + To write a JSON value object using a four-spaces indentation and forcing all + comment strings to apear before the value they refer to, use the following code: + \code + wxJSONWriter writer( wxJSONWRITER_STYLED | // want a styled output + wxJSONWRITER_WRITE_COMMENTS | // want comments in the document + wxJSONWRITER_COMMENTS_BEFORE, // force comments before value + 0, // initial indentation + 4); // indentation step + writer.Write( value, document ); + \endcode + + The following code construct a JSON writer that produces the most compact + text output but it is hard to read by humans: + + \code + wxJSONWriter writer( wxJSONWRITER_NONE ); + writer.Write( value, document ); + \endcode + + + \par The two types of output objects + + You can write JSON text to two different kind of objects: + + \li a string object (\b wxString) + \li a stream object (\b wxOutputStream) + + When writing to a string object, the output is platform- and mode-dependent. + In ANSI builds, the JSON text output in the string object will + contain one-byte characters: the actual characters represented is + locale dependent. + In Unicode builds, the JSON text output in the string contains + wide characters which encoding format is platform dependent: UCS-2 in + Windows, UCS-4 in GNU/Linux. + Starting from wxWidgets version 2.9 the internal encoding for Unicode + builds in linux/unix systems is UTF-8. + + When writing to a stream object, the JSON text output is always + encoded in UTF-8 in both ANSI and Unicode builds. + In ANSI builds the user may want to suppress UTF-8 encoding so + that the JSON text can be stored in ANSI format. + Note that this is not valid JSON text unless all characters written + to the JSON text document are in the US-ASCII character ser (0x00..0x7F). + To know more read \ref wxjson_tutorial_unicode_ansi + + \par Efficiency + + In versions up to 1.0 the JSON writer wrote every character to the + output object (the string or the stream). + This is very inefficient becuase the writer converted each char to + UTF-8 when writing to streams but we have to note that only string values + have to be actually converted. + Special JSON characters, numbers and literals do not need the conversion + because they lay in the US-ASCII plane (0x00-0x7F) + and no conversion is needed as the UTF-8 encoding is the same as US-ASCII. + + For more info about the unicode topic see \ref wxjson_tutorial_unicode. + + \par The problem of writing doubles + + You can customize the ouput of doubles by specifing the format string + that has to be used by the JSON writer class. To know more about this issue + read \ref wxjson_tutorial_write_doubles +*/ + +//! Ctor. +/*! + Construct the JSON writer object with the specified parameters. + Note that if \c styled is FALSE the indentation is totally suppressed + and the values of the other two parameters are simply ignored. + + \param indent the initial indentation in number of spaces. Default is ZERO. + If you specify the wxJSONWRITER_TAB_INDENT flag for the \e style, + this value referes to the number of TABs in the initial indentation + + \param step the indentation increment for new objects/arrays in number of spaces + (default is 3). + This value is ignored if you specify the wxJSONWRITER_TAB_INDENT flag for + the \e style: the indentation increment is only one TAB character. + + \param style this is a combination of the following constants OR'ed togheter: + \li wxJSONWRITER_NONE: no indentation is performed and no LF character is + written between values. + This style produces strict JSON text but it is hard to read by humans + \li wxJSONWRITER_STYLED: output is human-readable: values are separated by + LF characters and sub-items are indented. + This style produces strict JSON text that is easy to read by humans. + \li wxJSONWRITER_WRITE_COMMENTS: this flag force the writer to write C/C++ + comment strings, if any. The comments will be written in their original position. + C/C++ comments may not be recognized by other JSON implementations because + they are not strict JSON text. + \li wxJSONWRITER_COMMENTS_BEFORE: this flag force the writer to write C/C++ comments + always before the value they refer to. + In order for this style to take effect, you also have to specify the + wxJSONWRITER_WRITE_COMMENTS flag. + \li wxJSONWRITER_COMMENTS_AFTER: this flag force the writer to write C/C++ comments + always after the value they refer to. + In order for this style to take effect, you also have to specify the + wxJSONWRITER_WRITE_COMMENTS flag. + \li wxJSONWRITER_SPLIT_STRINGS: this flag cause the writer to split strings + in more than one line if they are too long. + \li wxJSONWRITER_NO_LINEFEEDS: this flag cause the JSON writer to not add + newlines between values. It is ignored if wxJSONWRITER_STYLED is not set. + This style produces strict JSON text. + \li wxJSONWRITER_ESCAPE_SOLIDUS: the solidus character (/) should only be + escaped if the JSON text is meant for embedding in HTML. + Unlike in older 0.x versions, it is disabled by default and this flag cause + the solidus char to be escaped. + This style produces strict JSON text. + \li wxJSONWRITER_MULTILINE_STRING:this is a multiline-string mode where newlines + and tabs are not escaped. This is not strict JSON, but it helps immensely when + manually editing json files with multiline strings + \li wxJSONWRITER_RECOGNIZE_UNSIGNED: this flag cause the JSON writer to prepend + a plus sign (+) to unsigned integer values. This is used by the wxJSON reader to + force the integer to be stored in an \b unsigned \b int. Note that this + feature may be incompatible with other JSON implementations. + \li wxJSONWRITER_TAB_INDENT: this flag cause the indentation of sub-objects / arrays + to be done using a TAB character instead of SPACES. + In order for this style to take effect, you also have to specify the + wxJSONWRITER_STYLED flag. + This style produces strict JSON text. + \li wxJSONWRITER_NO_INDENTATION: this flag cause the JSON writer to not add + indentation. It is ignored if wxJSONWRITER_STYLED is not set. + This style produces strict JSON text. + \li wxJSONWRITER_NOUTF8_STREAM: suppress UTF-8 conversion when writing string + values to the stream thus producing ANSI text output; only meaningfull in + ANSI builds, this flag is simply ignored in Unicode builds. + \li wxJSONWRITER_MEMORYBUFF: + + + Note that for the style wxJSONWRITER_NONE the JSON text output is a bit + different from that of old 0.x versions although it is syntactically equal. + If you rely on the old JSON output formatting read the following page + \ref wxjson_tutorial_style_none. + To know more about the writer's styles see \ref wxjson_tutorial_style +*/ +wxJSONWriter::wxJSONWriter( int style, int indent, int step ) +{ + m_indent = indent; + m_step = step; + m_style = style; + m_noUtf8 = false; + if ( m_style == wxJSONWRITER_NONE ) { + m_indent = 0; + m_step = 0; + } + // set the default format string for doubles as + // 10 significant digits and suppress trailing ZEROes + SetDoubleFmtString( "%.10g") ; + +#if !defined( wxJSON_USE_UNICODE ) + // in ANSI builds we can suppress UTF-8 conversion for both the writer and the reader + if ( m_style == wxJSONWRITER_NOUTF8_STREAM ) { + m_noUtf8 = true; + } +#endif +} + +//! Dtor - does nothing +wxJSONWriter::~wxJSONWriter() +{ +} + +//! Write the JSONvalue object to a JSON text. +/*! + The two overloaded versions of this function let the user choose + the output object which can be: + + \li a string object (\b wxString) + \li a stream object ( \b wxOutputStream) + + The two types of output object are very different because the + text outputted is encoded in different formats depending on the + build mode. + When writing to a string object, the JSON text output is encoded + differently depending on the build mode and the platform. + Writing to a stream always produce UTF-8 encoded text. + To know more about this topic read \ref wxjson_tutorial_unicode. + + Also note that the Write() function does not return a status code. + If you are writing to a string, you do not have to warry about this + issue: no errors can occur when writing to strings. + On the other hand, wehn writing to a stream there could be errors + in the write operation. + If an error occurs, the \c Write(9 function immediatly returns + without trying further output operations. + You have to check the status of the stream by calling the stream's + memberfunctions. Example: + + \code + // construct the JSON value object and add values to it + wxJSONValue root; + root["key1"] = "some value"; + + // write to a stream + wxMemoryOutputStream mem; + wxJSONWriter writer; + writer.Write( root, mem ); + wxStreamError err = mem.GetLastError(); + if ( err != wxSTREAM_NO_ERROR ) { + MessageBox( _T("ERROR: cannot write the JSON text output")); + } +\endcode +*/ +void +wxJSONWriter::Write( const wxJSONValue& value, wxString& str ) +{ +#if !defined( wxJSON_USE_UNICODE ) + // in ANSI builds output to a string never use UTF-8 conversion + bool noUtf8_bak = m_noUtf8; // save the current setting + m_noUtf8 = true; +#endif + + wxMemoryOutputStream os; + Write( value, os ); + + // get the address of the buffer + wxFileOffset len = os.GetLength(); + wxStreamBuffer* osBuff = os.GetOutputStreamBuffer(); + void* buffStart = osBuff->GetBufferStart(); + + if ( m_noUtf8 ) { + str = wxString::From8BitData( (const char*) buffStart, len ); + } + else { + str = wxString::FromUTF8( (const char*) buffStart, len ); + } +#if !defined( wxJSON_USE_UNICODE ) + m_noUtf8 = noUtf8_bak; // restore the old setting +#endif +} + +//! \overload Write( const wxJSONValue&, wxString& ) +void +wxJSONWriter::Write( const wxJSONValue& value, wxOutputStream& os ) +{ + m_level = 0; + DoWrite( os, value, 0, false ); +} + +//! Set the format string for double values. +/*! + This function sets the format string used for printing double values. + Double values are outputted to JSON text using the \b snprintf function + with a default format string of: + \code + %.10g + \endcode + which prints doubles with a precision of 10 decimal digits and suppressing + trailing ZEROes. + + Note that the parameter is a pointer to \b char and not to \b wxChar. This + is because the JSON writer always procudes UTF-8 encoded text and decimal + digits in UTF-8 are made of only one UTF-8 code-unit (1 byte). +*/ +void +wxJSONWriter::SetDoubleFmtString( const char* fmt ) +{ + m_fmt = (char*) fmt; +} + + + +//! Perform the real write operation. +/*! + This is a recursive function that gets the type of the \c value object and + calls several protected functions depending on the type: + + \li \c WriteNullvalue for type NULL + \li \c WriteStringValue() for STRING and CSTRING types + \li \c WriteIntValue for INT types + \li \c WriteUIntValue for UINT types + \li \c WriteBoolValue for BOOL types + \li \c WriteDoubleValue for DOUBLE types + \li \c WriteMemoryBuff for MEMORYBUFF types + + If the value is an array or key/value map (types ARRAY and OBJECT), the function + iterates through all JSON value object in the array/map and calls itself for every + item in the container. +*/ +int +wxJSONWriter::DoWrite( wxOutputStream& os, const wxJSONValue& value, const wxString* key, bool comma ) +{ + // note that this function is recursive + + // some variables that cannot be allocated in the switch statement + const wxJSONInternalMap* map = 0; + int size; + m_colNo = 1; m_lineNo = 1; + // determine the comment position; it is one of: + // + // wxJSONVALUE_COMMENT_BEFORE + // wxJSONVALUE_COMMENT_AFTER + // wxJSONVALUE_COMMENT_INLINE + // + // or -1 if comments have not to be written + int commentPos = -1; + if ( value.GetCommentCount() > 0 && (m_style & wxJSONWRITER_WRITE_COMMENTS)) { + commentPos = value.GetCommentPos(); + if ( ( m_style & wxJSONWRITER_COMMENTS_BEFORE) != 0 ) { + commentPos = wxJSONVALUE_COMMENT_BEFORE; + } + else if ( (m_style & wxJSONWRITER_COMMENTS_AFTER) != 0 ) { + commentPos = wxJSONVALUE_COMMENT_AFTER; + } + } + + int lastChar = 0; // check if WriteComment() writes the last LF char + + // first write the comment if it is BEFORE + if ( commentPos == wxJSONVALUE_COMMENT_BEFORE ) { + lastChar = WriteComment( os, value, true ); + if ( lastChar < 0 ) { + return lastChar; + } + else if ( lastChar != '\n' ) { + WriteSeparator( os ); + } + } + + lastChar = WriteIndent( os ); + if ( lastChar < 0 ) { + return lastChar; + } + + // now write the key if it is not NULL + if ( key ) { + lastChar = WriteKey( os, *key ); + } + if ( lastChar < 0 ) { + return lastChar; + } + + // now write the value + wxJSONInternalMap::const_iterator it; // declare the map object + long int count = 0; + + wxJSONType t = value.GetType(); + switch ( t ) { + case wxJSONTYPE_INVALID : + WriteInvalid( os ); + wxFAIL_MSG( _T("wxJSONWriter::WriteEmpty() cannot be called (not a valid JSON text")); + break; + + case wxJSONTYPE_INT : + case wxJSONTYPE_SHORT : + case wxJSONTYPE_LONG : + case wxJSONTYPE_INT64 : + lastChar = WriteIntValue( os, value ); + break; + + case wxJSONTYPE_UINT : + case wxJSONTYPE_USHORT : + case wxJSONTYPE_ULONG : + case wxJSONTYPE_UINT64 : + lastChar = WriteUIntValue( os, value ); + break; + + case wxJSONTYPE_NULL : + lastChar = WriteNullValue( os ); + break; + case wxJSONTYPE_BOOL : + lastChar = WriteBoolValue( os, value ); + break; + + case wxJSONTYPE_DOUBLE : + lastChar = WriteDoubleValue( os, value ); + break; + + case wxJSONTYPE_STRING : + case wxJSONTYPE_CSTRING : + lastChar = WriteStringValue( os, value.AsString()); + break; + + case wxJSONTYPE_MEMORYBUFF : + lastChar = WriteMemoryBuff( os, value.AsMemoryBuff()); + break; + + case wxJSONTYPE_ARRAY : + ++m_level; + os.PutC( '[' ); + // the inline comment for objects and arrays are printed in the open char + if ( commentPos == wxJSONVALUE_COMMENT_INLINE ) { + commentPos = -1; // we have already written the comment + lastChar = WriteComment( os, value, false ); + if ( lastChar < 0 ) { + return lastChar; + } + if ( lastChar != '\n' ) { + lastChar = WriteSeparator( os ); + } + } + else { // comment is not to be printed inline, so write a LF + lastChar = WriteSeparator( os ); + if ( lastChar < 0 ) { + return lastChar; + } + } + + // now iterate through all sub-items and call DoWrite() recursively + size = value.Size(); + for ( int i = 0; i < size; i++ ) { + bool comma = false; + if ( i < size - 1 ) { + comma = true; + } + wxJSONValue v = value.ItemAt( i ); + lastChar = DoWrite( os, v, 0, comma ); + if ( lastChar < 0 ) { + return lastChar; + } + + } + --m_level; + lastChar = WriteIndent( os ); + if ( lastChar < 0 ) { + return lastChar; + } + os.PutC( ']' ); + break; + + case wxJSONTYPE_OBJECT : + ++m_level; + + os.PutC( '{' ); + // the inline comment for objects and arrays are printed in the open char + if ( commentPos == wxJSONVALUE_COMMENT_INLINE ) { + commentPos = -1; // we have already written the comment + lastChar = WriteComment( os, value, false ); + if ( lastChar < 0 ) { + return lastChar; + } + if ( lastChar != '\n' ) { + WriteSeparator( os ); + } + } + else { + lastChar = WriteSeparator( os ); + } + + map = value.AsMap(); + size = value.Size(); + count = 0; + for ( it = map->begin(); it != map->end(); ++it ) { + // get the key and the value + wxString key = it->first; + const wxJSONValue& v = it->second; + bool comma = false; + if ( count < size - 1 ) { + comma = true; + } + lastChar = DoWrite( os, v, &key, comma ); + if ( lastChar < 0 ) { + return lastChar; + } + count++; + } + --m_level; + lastChar = WriteIndent( os ); + if ( lastChar < 0 ) { + return lastChar; + } + os.PutC( '}' ); + break; + + default : + // a not yet defined wxJSONType: we FAIL + wxFAIL_MSG( _T("wxJSONWriter::DoWrite() undefined wxJSONType type")); + break; + } + + // writes the comma character before the inline comment + if ( comma ) { + os.PutC( ',' ); + } + + if ( commentPos == wxJSONVALUE_COMMENT_INLINE ) { + lastChar = WriteComment( os, value, false ); + if ( lastChar < 0 ) { + return lastChar; + } + } + else if ( commentPos == wxJSONVALUE_COMMENT_AFTER ) { + WriteSeparator( os ); + lastChar = WriteComment( os, value, true ); + if ( lastChar < 0 ) { + return lastChar; + } + } + if ( lastChar != '\n' ) { + lastChar = WriteSeparator( os ); + } + return lastChar; +} + + +//! Write the comment strings, if any. +int +wxJSONWriter::WriteComment( wxOutputStream& os, const wxJSONValue& value, bool indent ) +{ + // the function returns the last character written which should be + // a LF char or -1 in case of errors + // if nothing is written, returns ZERO + int lastChar = 0; + + // only write comments if the style include the WRITE_COMMENTS flag + if ( (m_style & wxJSONWRITER_WRITE_COMMENTS ) == 0 ) { + return lastChar; + } + + const wxArrayString cmt = value.GetCommentArray(); + int cmtSize = cmt.GetCount(); + for ( int i = 0; i < cmtSize; i++ ) { + if ( indent ) { + WriteIndent( os ); + } + else { + os.PutC( '\t' ); + } + WriteString( os, cmt[i]); + lastChar = cmt[i].Last(); + if ( lastChar != '\n' ) { + os.PutC( '\n' ); + lastChar = '\n'; + } + } + return lastChar; +} + +//! Writes the indentation to the JSON text. +/*! + The two functions write the indentation as \e spaces in the JSON output + text. When called with a int parameter, the function + writes the specified number of spaces. + If no parameter is given, the function computes the number of spaces + using the following formula: + If the wxJSONWRITER_TAB_INDENT flag is used in the writer's cnstructor, + the function calls WriteTabIndent(). + + The function also checks that wxJSONWRITER_STYLED is set and the + wxJSONWRITER_NO_INDENTATION is not set. +*/ +int +wxJSONWriter::WriteIndent( wxOutputStream& os ) +{ + int lastChar = WriteIndent( os, m_level ); + return lastChar; +} + +//! Write the specified number of indentation (spaces or tabs) +/*! + The function is called by WriteIndent() and other writer's functions. + It writes the indentation as specified in the \c num parameter which is + the actual \b level of annidation. + The function checks if wxJSONWRITER_STYLED is set: if not, no indentation + is performed. + Also, the function checks if wxJSONWRITER_TAB_INDENT is set: if it is, + indentation is done by writing \b num TAB characters otherwise, + it is performed by writing a number of spaces computed as: + \code + numSpaces = m_indent + ( m_step * num ) + \endcode + +*/ +int +wxJSONWriter::WriteIndent( wxOutputStream& os, int num ) +{ + int lastChar = 0; + if ( !(m_style & wxJSONWRITER_STYLED) || (m_style & wxJSONWRITER_NO_INDENTATION)) { + return lastChar; + } + + int numChars = m_indent + ( m_step * num ); + char c = ' '; + if ( m_style & wxJSONWRITER_TAB_INDENT ) { + c = '\t'; + numChars = num; + } + + for ( int i = 0; i < numChars; i++ ) { + os.PutC( c ); + if ( os.GetLastError() != wxSTREAM_NO_ERROR ) { + return -1; + } + + } + return c; +} + + +//! Write the provided string to the output object. +/*! + The function writes the string \c str to the output object that + was specified in the wxJSONWriter::Write() function. + The function may split strings in two or more lines if the + string contains LF characters if the \c m_style data member contains + the wxJSONWRITER_SPLIT_STRING flag. + + The function does not actually write the string: for every character + in the provided string the function calls WriteChar() which does + the actual character output. + + The function returns ZERO on success or -1 in case of errors. +*/ +int +wxJSONWriter::WriteStringValue( wxOutputStream& os, const wxString& str ) +{ + // JSON values of type STRING are written by converting the whole string + // to UTF-8 and then copying the UTF-8 buffer to the 'os' stream + // one byte at a time and processing them + os.PutC( '\"' ); // open quotes + + // the buffer that has to be written is either UTF-8 or ANSI c_str() depending + // on the 'm_noUtf8' flag + char* writeBuff = 0; + wxCharBuffer utf8CB = str.ToUTF8(); // the UTF-8 buffer +#if !defined( wxJSON_USE_UNICODE ) + wxCharBuffer ansiCB( str.c_str()); // the ANSI buffer + if ( m_noUtf8 ) { + writeBuff = ansiCB.data(); + } + else { + writeBuff = utf8CB.data(); + } +#else + writeBuff = utf8CB.data(); +#endif + + // NOTE: in ANSI builds UTF-8 conversion may fail (see samples/test5.cpp, + // test 7.3) although I do not know why + if ( writeBuff == 0 ) { + const char* err = ""; + os.Write( err, strlen( err )); + return 0; + } + size_t len = strlen( writeBuff ); + int lastChar = 0; + + // store the column at which the string starts + // splitting strings only happen if the string starts within + // column wxJSONWRITER_LAST_COL (default 50) + // see 'include/wx/json_defs.h' for the defines + int tempCol = m_colNo; + + // now write the UTF8 buffer processing the bytes + size_t i; + for ( i = 0; i < len; i++ ) { + bool shouldEscape = false; + unsigned char ch = *writeBuff; + ++writeBuff; // point to the next byte + + // the escaped character + char escCh = 0; + + // for every character we have to check if it is a character that + // needs to be escaped: note that characters that should be escaped + // may be not if some writer's flags are specified + switch ( ch ) { + case '\"' : // quotes + shouldEscape = true; + escCh = '\"'; + break; + case '\\' : // reverse solidus + shouldEscape = true; + escCh = '\\'; + break; + case '/' : // solidus + shouldEscape = true; + escCh = '/'; + break; + case '\b' : // backspace + shouldEscape = true; + escCh = 'b'; + break; + case '\f' : // formfeed + shouldEscape = true; + escCh = 'f'; + break; + case '\n' : // newline + shouldEscape = true; + escCh = 'n'; + break; + case '\r' : // carriage-return + shouldEscape = true; + escCh = 'r'; + break; + case '\t' : // horizontal tab + shouldEscape = true; + escCh = 't'; + break; + default : + shouldEscape = false; + break; + } // end switch + + + // if the character is a control character that is not identified by a + // lowercase letter, we should escape it + if ( !shouldEscape && ch < 32 ) { + char b[8]; + snprintf( b, 8, "\\u%04X", (int) ch ); + os.Write( b, 6 ); + if ( os.GetLastError() != wxSTREAM_NO_ERROR ) { + return -1; + } + } + + // the char is not a control character + else { + // some characters that should be escaped are not escaped + // if the writer was constructed with some flags + if ( shouldEscape && !( m_style & wxJSONWRITER_ESCAPE_SOLIDUS) ) { + if ( ch == '/' ) { + shouldEscape = false; + } + } + if ( shouldEscape && (m_style & wxJSONWRITER_MULTILINE_STRING)) { + if ( ch == '\n' || ch == '\t' ) { + shouldEscape = false; + } + } + + + // now write the character prepended by ESC if it should be escaped + if ( shouldEscape ) { + os.PutC( '\\' ); + os.PutC( escCh ); + if ( os.GetLastError() != wxSTREAM_NO_ERROR ) { + return -1; + } + } + else { + // a normal char or a UTF-8 units: write the character + os.PutC( ch ); + if ( os.GetLastError() != wxSTREAM_NO_ERROR ) { + return -1; + } + } + } + + // check if SPLIT_STRING flag is set and if the string has to + // be splitted + if ( (m_style & wxJSONWRITER_STYLED) && (m_style & wxJSONWRITER_SPLIT_STRING)) { + // split the string if the character written is LF + if ( ch == '\n' ) { + // close quotes and CR + os.Write( "\"\n", 2 ); + lastChar = WriteIndent( os, m_level + 2 ); // write indentation + os.PutC( '\"' ); // reopen quotes + if ( lastChar < 0 ) { + return lastChar; + } + } + // split the string only if there is at least wxJSONWRITER_MIN_LENGTH + // character to write and the character written is a punctuation or space + // BUG: the following does not work because the columns are not counted + else if ( (m_colNo >= wxJSONWRITER_SPLIT_COL) + && (tempCol <= wxJSONWRITER_LAST_COL )) { + if ( IsSpace( ch ) || IsPunctuation( ch )) { + if ( len - i > wxJSONWRITER_MIN_LENGTH ) { + // close quotes and CR + os.Write( "\"\n", 2 ); + lastChar = WriteIndent( os, m_level + 2 ); // write indentation + os.PutC( '\"' ); // reopen quotes + if ( lastChar < 0 ) { + return lastChar; + } + } + } + } + } + } // end for + os.PutC( '\"' ); // close quotes + return 0; +} + + + +//! Write a generic string +/*! + The function writes the wxString object \c str to the output object. + The string is written as is; you cannot use it to write JSON strings + to the output text. + The function converts the string \c str to UTF-8 and writes the buffer.. +*/ +int +wxJSONWriter::WriteString( wxOutputStream& os, const wxString& str ) +{ + wxLogTrace( writerTraceMask, _T("(%s) string to write=%s"), + __PRETTY_FUNCTION__, str.c_str() ); + int lastChar = 0; + char* writeBuff = 0; + + // the buffer that has to be written is either UTF-8 or ANSI c_str() depending + // on the 'm_noUtf8' flag + wxCharBuffer utf8CB = str.ToUTF8(); // the UTF-8 buffer +#if !defined( wxJSON_USE_UNICODE ) + wxCharBuffer ansiCB( str.c_str()); // the ANSI buffer + + if ( m_noUtf8 ) { + writeBuff = ansiCB.data(); + } + else { + writeBuff = utf8CB.data(); + } +#else + writeBuff = utf8CB.data(); +#endif + + // NOTE: in ANSI builds UTF-8 conversion may fail (see samples/test5.cpp, + // test 7.3) although I do not know why + if ( writeBuff == 0 ) { + const char* err = ""; + os.Write( err, strlen( err )); + return 0; + } + size_t len = strlen( writeBuff ); + + os.Write( writeBuff, len ); + if ( os.GetLastError() != wxSTREAM_NO_ERROR ) { + return -1; + } + + wxLogTrace( writerTraceMask, _T("(%s) result=%d"), + __PRETTY_FUNCTION__, lastChar ); + return lastChar; +} + +//! Write the NULL JSON value to the output stream. +/*! + The function writes the \b null literal string to the output stream. +*/ +int +wxJSONWriter::WriteNullValue( wxOutputStream& os ) +{ + os.Write( "null", 4 ); + if ( os.GetLastError() != wxSTREAM_NO_ERROR ) { + return -1; + } + return 0; +} + + +//! Writes a value of type INT. +/*! + This function is called for every value objects of INT type. + This function uses the \n snprintf function to get the US-ASCII + representation of the integer and simply copy it to the output stream. + Returns -1 on stream errors or ZERO if no errors. +*/ +int +wxJSONWriter::WriteIntValue( wxOutputStream& os, const wxJSONValue& value ) +{ + int r = 0; + char buffer[32]; // need to store 64-bits integers (max 20 digits) + size_t len; + + wxJSONRefData* data = value.GetRefData(); + wxASSERT( data ); + +#if defined( wxJSON_64BIT_INT ) + #if wxCHECK_VERSION(2, 9, 0 ) || !defined( wxJSON_USE_UNICODE ) + // this is fine for wxW 2.9 and for wxW 2.8 ANSI + snprintf( buffer, 32, "%" wxLongLongFmtSpec "d", + data->m_value.m_valInt64 ); + #else + // this is for wxW 2.8 Unicode: in order to use the cross-platform + // format specifier, we use the wxString's sprintf() function and then + // convert to UTF-8 before writing to the stream + wxString s; + s.Printf( _T("%") wxLongLongFmtSpec _T("d"), + data->m_value.m_valInt64 ); + wxCharBuffer cb = s.ToUTF8(); + const char* cbData = cb.data(); + len = strlen( cbData ); + wxASSERT( len < 32 ); + memcpy( buffer, cbData, len ); + buffer[len] = 0; + #endif +#else + snprintf( buffer, 32, "%ld", data->m_value.m_valLong ); +#endif + len = strlen( buffer ); + os.Write( buffer, len ); + if ( os.GetLastError() != wxSTREAM_NO_ERROR ) { + r = -1; + } + return r; +} + +//! Writes a value of type UNSIGNED INT. +/*! + This function is called for every value objects of UINT type. + This function uses the \n snprintf function to get the US-ASCII + representation of the integer and simply copy it to the output stream. + The function prepends a \b plus \b sign if the \c wxJSONWRITER_RECOGNIZE_UNSIGNED + flag is set in the \c m_flags data member. + Returns -1 on stream errors or ZERO if no errors. +*/ +int +wxJSONWriter::WriteUIntValue( wxOutputStream& os, const wxJSONValue& value ) +{ + int r = 0; size_t len; + + // prepend a plus sign if the style specifies that unsigned integers + // have to be recognized by the JSON reader + if ( m_style & wxJSONWRITER_RECOGNIZE_UNSIGNED ) { + os.PutC( '+' ); + } + + char buffer[32]; // need to store 64-bits integers (max 20 digits) + wxJSONRefData* data = value.GetRefData(); + wxASSERT( data ); + +#if defined( wxJSON_64BIT_INT ) + #if wxCHECK_VERSION(2, 9, 0 ) || !defined( wxJSON_USE_UNICODE ) + // this is fine for wxW 2.9 and for wxW 2.8 ANSI + snprintf( buffer, 32, "%" wxLongLongFmtSpec "u", + data->m_value.m_valUInt64 ); + #else + // this is for wxW 2.8 Unicode: in order to use the cross-platform + // format specifier, we use the wxString's sprintf() function and then + // convert to UTF-8 before writing to the stream + wxString s; + s.Printf( _T("%") wxLongLongFmtSpec _T("u"), + data->m_value.m_valInt64 ); + wxCharBuffer cb = s.ToUTF8(); + const char* cbData = cb.data(); + len = strlen( cbData ); + wxASSERT( len < 32 ); + memcpy( buffer, cbData, len ); + buffer[len] = 0; + #endif +#else + snprintf( buffer, 32, "%lu", data->m_value.m_valULong ); +#endif + len = strlen( buffer ); + os.Write( buffer, len ); + if ( os.GetLastError() != wxSTREAM_NO_ERROR ) { + r = -1; + } + return r; +} + +//! Writes a value of type DOUBLE. +/*! + This function is called for every value objects of DOUBLE type. + This function uses the \n snprintf function to get the US-ASCII + representation of the integer and simply copy it to the output stream. + Returns -1 on stream errors or ZERO if no errors. + + Note that writing a double to a decimal ASCII representation could + lay to unexpected results depending on the format string used in the + conversion. + See SetDoubleFmtString for details. +*/ +int +wxJSONWriter::WriteDoubleValue( wxOutputStream& os, const wxJSONValue& value ) +{ + int r = 0; + + char buffer[32]; + wxJSONRefData* data = value.GetRefData(); + wxASSERT( data ); + snprintf( buffer, 32, m_fmt, data->m_value.m_valDouble ); + size_t len = strlen( buffer ); + os.Write( buffer, len ); + if ( os.GetLastError() != wxSTREAM_NO_ERROR ) { + r = -1; + } + return r; +} + +//! Writes a value of type BOOL. +/*! + This function is called for every value objects of BOOL type. + This function prints the literals \b true or \b false depending on the + value in \c value. + Returns -1 on stream errors or ZERO if no errors. +*/ +int +wxJSONWriter::WriteBoolValue( wxOutputStream& os, const wxJSONValue& value ) +{ + int r = 0; + const char* f = "false"; const char* t = "true"; + wxJSONRefData* data = value.GetRefData(); + wxASSERT( data ); + + const char* c = f; // defaults to FALSE + + if ( data->m_value.m_valBool ) { + c = t; + } + + size_t len = strlen( c ); + os.Write( c, len ); + if ( os.GetLastError() != wxSTREAM_NO_ERROR ) { + r = -1; + } + return r; +} + + + + +//! Write the key of a key/value element to the output stream. +int +wxJSONWriter::WriteKey( wxOutputStream& os, const wxString& key ) +{ + wxLogTrace( writerTraceMask, _T("(%s) key write=%s"), + __PRETTY_FUNCTION__, key.c_str() ); + + int lastChar = WriteStringValue( os, key ); + os.Write( " : ", 3 ); + return lastChar; +} + +//! Write the invalid JSON value to the output stream. +/*! + An invalid wxJSONValue is a value that was not initialized and it is + an error. You should never write invalid values to JSON text because + the output is not valid JSON text. + Note that the NULL value is a legal JSON text and it is written: + \code + null + \endcode + + This function writes a non-JSON text to the output stream: + \code + + \endcode + In debug mode, the function always fails with an wxFAIL_MSG failure. +*/ +int +wxJSONWriter::WriteInvalid( wxOutputStream& os ) +{ + wxFAIL_MSG( _T("wxJSONWriter::WriteInvalid() cannot be called (not a valid JSON text")); + int lastChar = 0; + os.Write( "", 9 ); + return lastChar; +} + +//! Write a JSON value of type \e memory \e buffer +/*! + The type wxJSONTYPE_MEMORYBUFF is a \b wxJSON extension that is not correctly read by + other JSON implementations. + By default, the function writes such a type as an array of INTs as follows: + \code + [ 0,32,45,255,6,...] + \endcode + If the writer object was constructed using the \c wxJSONWRITER_MEMORYBUFF flag, then + the output is much more compact and recognized by the \b wxJSON reader as a memory buffer + type: + \code + '00203FFF06..' + \endcode + +*/ +int +wxJSONWriter::WriteMemoryBuff( wxOutputStream& os, const wxMemoryBuffer& buff ) +{ +#define MAX_BYTES_PER_ROW 20 + char str[16]; + + // if STYLED and SPLIT_STRING flags are set, the function writes 20 bytes on every row + // the following is the counter of bytes written. + // the string is splitted only for the special meory buffer type, not for array of INTs + int bytesWritten = 0; + bool splitString = false; + if ( (m_style & wxJSONWRITER_STYLED) && + (m_style & wxJSONWRITER_SPLIT_STRING)) { + splitString = true; + } + + size_t buffLen = buff.GetDataLen(); + unsigned char* ptr = (unsigned char*) buff.GetData(); + wxASSERT( ptr ); + char openChar = '\''; + char closeChar = '\''; + bool asArray = false; + + if ( (m_style & wxJSONWRITER_MEMORYBUFF ) == 0 ) { + // if the special flag is not specified, write as an array of INTs + openChar = '['; + closeChar = ']'; + asArray = true; + } + // write the open character + os.PutC( openChar ); + + for ( size_t i = 0; i < buffLen; i++ ) { + unsigned char c = *ptr; + ++ptr; + + if ( asArray ) { + snprintf( str, 14, "%d", c ); + size_t len = strlen( str ); + wxASSERT( len <= 3 ); + wxASSERT( len >= 1 ); + str[len] = ','; + // do not write the comma char for the last element + if ( i < buffLen - 1 ) { + ++len; + } + os.Write( str, len ); + if ( os.GetLastError() != wxSTREAM_NO_ERROR ) { + return -1; + } + } + else { + // now convert the byte in two hex digits + char c1 = c / 16; + char c2 = c % 16; + c1 += '0'; + c2 += '0'; + if ( c1 > '9' ) { + c1 += 7; + } + if ( c2 > '9' ) { + c2 += 7; + } + os.PutC( c1 ); + os.PutC( c2 ); + if ( os.GetLastError() != wxSTREAM_NO_ERROR ) { + return -1; + } + if ( splitString ) { + ++bytesWritten; + } + + if (( bytesWritten >= MAX_BYTES_PER_ROW ) && ((buffLen - i ) >= 5 )) { + // split the string if we wrote 20 bytes, but only is we have to + // write at least 5 bytes + os.Write( "\'\n", 2 ); + int lastChar = WriteIndent( os, m_level + 2 ); // write indentation + os.PutC( '\'' ); // reopen quotes + if ( lastChar < 0 ) { + return lastChar; + } + bytesWritten = 0; + } + } + } + + // write the close character + os.PutC( closeChar ); + return closeChar; +} + + +//! Writes the separator between values +/*! + The function is called when a value has been written to the JSON + text output and it writes the separator character: LF. + The LF char is actually written only if the wxJSONWRITER_STYLED flag + is specified and wxJSONWRITER_NO_LINEFEEDS is not set. + + Returns the last character written which is LF itself or -1 in case + of errors. Note that LF is returned even if the character is not + actually written. +*/ +int +wxJSONWriter::WriteSeparator( wxOutputStream& os ) +{ + int lastChar = '\n'; + if ( (m_style & wxJSONWRITER_STYLED) && !(m_style & wxJSONWRITER_NO_LINEFEEDS )) { + os.PutC( '\n' ); + } + return lastChar; +} + +//! Returns TRUE if the character is a space character. +bool +wxJSONWriter::IsSpace( wxChar ch ) +{ + bool r = false; + switch ( ch ) { + case ' ' : + case '\t' : + case '\r' : + case '\f' : + case '\n' : + r = true; + break; + default : + break; + } + return r; +} + +//! Returns TRUE if the character if a puctuation character +bool +wxJSONWriter::IsPunctuation( wxChar ch ) +{ + bool r = false; + switch ( ch ) { + case '.' : + case ',' : + case ';' : + case ':' : + case '!' : + case '?' : + r = true; + break; + default : + break; + } + return r; +} + + +/* +{ +} +*/ + + diff --git a/ThirdParty/wxTranslationHelper/CMakeLists.txt b/ThirdParty/wxTranslationHelper/CMakeLists.txt new file mode 100644 index 0000000..4a641b2 --- /dev/null +++ b/ThirdParty/wxTranslationHelper/CMakeLists.txt @@ -0,0 +1,22 @@ +# +# Stuff the average programmer needs to change +# +set(SRCS + wxTranslationHelper.cpp +) +set(HFILES + wxTranslationHelper.h +) +set(INCLUDE_DIRECTORIES ${BASE_INCLUDE_DIRECTORIES}) +set(LIBRARY_NAME wxTranslationHelper) +if(WIN32) + set(PREPROCESSOR_DEFINITIONS ${PREPROCESSOR_DEFINITIONS};${wxWidgets_DEFINITIONS};/D_LIB) +endif(WIN32) +set(SRCS ${SRCS} ${HFILES}) + +add_definitions(${PREPROCESSOR_DEFINITIONS}) +include_directories(${INCLUDE_DIRECTORIES}) + +add_library(${LIBRARY_NAME} STATIC ${SRCS}) + +target_link_libraries(${LIBRARY_NAME} ${wxWidgets_LIBRARIES}) diff --git a/ThirdParty/wxTranslationHelper/Win/wxTranslationHelper.vcxproj b/ThirdParty/wxTranslationHelper/Win/wxTranslationHelper.vcxproj new file mode 100644 index 0000000..404fc23 --- /dev/null +++ b/ThirdParty/wxTranslationHelper/Win/wxTranslationHelper.vcxproj @@ -0,0 +1,129 @@ + + + + + Debug + x64 + + + Release + x64 + + + + {F2E960FE-1817-3D7D-A503-16BD29FA5B15} + 10.0.16299.0 + Win32Proj + x64 + wxTranslationHelper + NoUpgrade + + + + StaticLibrary + Unicode + v141 + + + StaticLibrary + Unicode + v141 + + + + + + + + + + <_ProjectFileVersion>10.0.20506.1 + F:\IT-Dim\trunk\ThirdParty\wxTranslationHelper\Win\Debug\ + wxTranslationHelper.dir\Debug\ + wxTranslationHelper + .lib + F:\IT-Dim\trunk\ThirdParty\wxTranslationHelper\Win\Release\ + wxTranslationHelper.dir\Release\ + wxTranslationHelper + .lib + + + + F:\IT-Dim\wxWidgets\lib\vc_x64_dll\mswu;F:\IT-Dim\wxWidgets\include;F:\IT-Dim\trunk\ThirdParty\build\..\..\include;F:\IT-Dim\trunk\ThirdParty\build\..\..\ThirdParty\GLEW\include;%(AdditionalIncludeDirectories) + Debug/ + EnableFastChecks + CompileAsCpp + ProgramDatabase + 4996 + Sync + Disabled + true + Disabled + NotUsing + MultiThreadedDebugDLL + true + Level3 + WIN32;_WINDOWS;__WXDEBUG__=1;WXUSINGDLL;UNICODE;_UNICODE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;_LIB;CMAKE_INTDIR="Debug";%(PreprocessorDefinitions) + $(IntDir) + + + WIN32;_DEBUG;_WINDOWS;__WXDEBUG__=1;WXUSINGDLL;UNICODE;_UNICODE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;_LIB;CMAKE_INTDIR=\"Debug\";%(PreprocessorDefinitions) + F:\IT-Dim\wxWidgets\lib\vc_x64_dll\mswu;F:\IT-Dim\wxWidgets\include;F:\IT-Dim\trunk\ThirdParty\build\..\..\include;F:\IT-Dim\trunk\ThirdParty\build\..\..\ThirdParty\GLEW\include;%(AdditionalIncludeDirectories) + + + F:\IT-Dim\wxWidgets\lib\vc_x64_dll\mswu;F:\IT-Dim\wxWidgets\include;F:\IT-Dim\trunk\ThirdParty\build\..\..\include;F:\IT-Dim\trunk\ThirdParty\build\..\..\ThirdParty\GLEW\include;%(AdditionalIncludeDirectories) + $(ProjectDir)/$(IntDir) + %(Filename).h + %(Filename).tlb + %(Filename)_i.c + %(Filename)_p.c + + + %(AdditionalOptions) /machine:x64 + + + + + F:\IT-Dim\wxWidgets\lib\vc_x64_dll\mswu;F:\IT-Dim\wxWidgets\include;F:\IT-Dim\trunk\ThirdParty\build\..\..\include;F:\IT-Dim\trunk\ThirdParty\build\..\..\ThirdParty\GLEW\include;%(AdditionalIncludeDirectories) + Release/ + CompileAsCpp + 4996 + Sync + AnySuitable + true + MaxSpeed + NotUsing + MultiThreadedDLL + true + Level3 + WIN32;_WINDOWS;NDEBUG;WXUSINGDLL;UNICODE;_UNICODE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;__WXDEBUG__=1;_LIB;CMAKE_INTDIR="Release";%(PreprocessorDefinitions) + $(IntDir) + + + + + WIN32;_WINDOWS;NDEBUG;WXUSINGDLL;UNICODE;_UNICODE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;__WXDEBUG__=1;_LIB;CMAKE_INTDIR=\"Release\";%(PreprocessorDefinitions) + F:\IT-Dim\wxWidgets\lib\vc_x64_dll\mswu;F:\IT-Dim\wxWidgets\include;F:\IT-Dim\trunk\ThirdParty\build\..\..\include;F:\IT-Dim\trunk\ThirdParty\build\..\..\ThirdParty\GLEW\include;%(AdditionalIncludeDirectories) + + + F:\IT-Dim\wxWidgets\lib\vc_x64_dll\mswu;F:\IT-Dim\wxWidgets\include;F:\IT-Dim\trunk\ThirdParty\build\..\..\include;F:\IT-Dim\trunk\ThirdParty\build\..\..\ThirdParty\GLEW\include;%(AdditionalIncludeDirectories) + $(ProjectDir)/$(IntDir) + %(Filename).h + %(Filename).tlb + %(Filename)_i.c + %(Filename)_p.c + + + %(AdditionalOptions) /machine:x64 + + + + + + + + + + + + \ No newline at end of file diff --git a/ThirdParty/wxTranslationHelper/Win/wxTranslationHelper.vcxproj.filters b/ThirdParty/wxTranslationHelper/Win/wxTranslationHelper.vcxproj.filters new file mode 100644 index 0000000..20d762e --- /dev/null +++ b/ThirdParty/wxTranslationHelper/Win/wxTranslationHelper.vcxproj.filters @@ -0,0 +1,21 @@ + + + + + Source Files + + + + + Header Files + + + + + {51D244FA-C21C-3174-8217-FDB3769212C0} + + + {A07A887B-0B8A-3ED7-BD7F-43C26975F88D} + + + diff --git a/ThirdParty/wxTranslationHelper/wxTranslationHelper.cpp b/ThirdParty/wxTranslationHelper/wxTranslationHelper.cpp new file mode 100644 index 0000000..8ee0bdc --- /dev/null +++ b/ThirdParty/wxTranslationHelper/wxTranslationHelper.cpp @@ -0,0 +1,191 @@ +#include "stdwx.h" +#include "wxTranslationHelper.h" + +wxTranslationHelper::wxTranslationHelper(const wxString & search_path, + bool use_native_config) +: m_SearchPath(search_path), m_ConfigPath(wxEmptyString), +m_Locale(NULL), m_UseNativeConfig(use_native_config) +{ + if(search_path.IsEmpty()) + { + m_SearchPath = wxPathOnly(wxTheApp->argv[0]); + } +} + +wxTranslationHelper::~wxTranslationHelper() +{ + Save(); + if(m_Locale) + { + wxDELETE(m_Locale); + } +} + +wxLocale * wxTranslationHelper::GetLocale() +{ + return m_Locale; +} + +const wxString & wxTranslationHelper::GetSearchPath() +{ + return m_SearchPath; +} + +void wxTranslationHelper::SetSearchPath(wxString & value) +{ + m_SearchPath = value; + if(m_SearchPath.IsEmpty()) + { + m_SearchPath = wxPathOnly(wxTheApp->argv[0]); + } +} + +const wxString & wxTranslationHelper::GetConfigPath() +{ + return m_ConfigPath; +} + +void wxTranslationHelper::SetConfigPath(wxString & value) +{ + m_ConfigPath = value; +} + +bool wxTranslationHelper::Load() +{ + wxConfigBase * config; + if(m_UseNativeConfig) + { + config = new wxConfig(wxTheApp->GetAppName()); + } + else + { + config = new wxFileConfig(wxTheApp->GetAppName(), wxEmptyString, m_ConfigPath); + } + long language; + config->SetPath(wxT("wxTranslation")); + if(!config->Read(wxT("wxTranslationLanguage"), + &language, wxLANGUAGE_UNKNOWN)) + { + language = wxLANGUAGE_UNKNOWN; + } + delete config; + if(language == wxLANGUAGE_UNKNOWN) + { + return false; + } + wxArrayString names; + wxArrayLong identifiers; + GetInstalledLanguages(names, identifiers); + for(size_t i = 0; i < identifiers.Count(); i++) + { + if(identifiers[i] == language) + { + if(m_Locale) wxDELETE(m_Locale); + m_Locale = new wxLocale; + m_Locale->Init(identifiers[i]); + m_Locale->AddCatalogLookupPathPrefix(m_SearchPath); + m_Locale->AddCatalog(wxTheApp->GetAppName()); + return true; + } + } + return false; +} + +void wxTranslationHelper::Save(bool bReset) +{ + wxConfigBase * config; + if(m_UseNativeConfig) + { + config = new wxConfig(wxTheApp->GetAppName()); + } + else + { + config = new wxFileConfig(wxTheApp->GetAppName(), wxEmptyString, m_ConfigPath); + } + long language = wxLANGUAGE_UNKNOWN; + if(!bReset) + { + if(m_Locale) + { + language = m_Locale->GetLanguage(); + } + } + config->DeleteEntry(wxT("wxTranslation")); + config->SetPath(wxT("wxTranslation")); + config->Write(wxT("wxTranslationLanguage"), language); + config->Flush(); + delete config; +} + +void wxTranslationHelper::GetInstalledLanguages(wxArrayString & names, + wxArrayLong & identifiers) +{ + names.Clear(); + identifiers.Clear(); + wxString filename; + const wxLanguageInfo * langinfo; + wxString name = wxLocale::GetLanguageName(wxLANGUAGE_DEFAULT); + if(!name.IsEmpty()) + { + names.Add(_("Default")); + identifiers.Add(wxLANGUAGE_DEFAULT); + } + if(!wxDir::Exists(m_SearchPath)) + { + wxLogTrace(_("Directory %s DOES NOT EXIST !!!"), + m_SearchPath.GetData()); + return; + } + wxDir dir(m_SearchPath); + for(bool cont = dir.GetFirst(&filename, +#ifdef __WXMSW__ + wxT("*.*"), +#else + wxT("*"), +#endif + wxDIR_DEFAULT); + cont; cont = dir.GetNext(&filename)) + { + langinfo = wxLocale::FindLanguageInfo(filename); + if(langinfo != NULL) + { + wxLogTrace(_("SEARCHING FOR %s"), + wxString(dir.GetName()+wxFileName::GetPathSeparator()+ + filename+wxFileName::GetPathSeparator()+ + wxTheApp->GetAppName()+wxT(".mo")).GetData()); + if(wxFileExists(dir.GetName()+wxFileName::GetPathSeparator()+ + filename+wxFileName::GetPathSeparator()+ + wxTheApp->GetAppName()+wxT(".mo"))) + { + names.Add(langinfo->Description); + identifiers.Add(langinfo->Language); + } + } + } +} + +bool wxTranslationHelper::AskUserForLanguage(wxArrayString & names, + wxArrayLong & identifiers) +{ + wxCHECK_MSG(names.Count() == identifiers.Count(), false, + _("Array of language names and identifiers should have the same size.")); + long index = wxGetSingleChoiceIndex(_("Select the language"), + _("Language"), names); + if(index != -1) + { + if(m_Locale) + { + wxDELETE(m_Locale); + } + m_Locale = new wxLocale; + m_Locale->Init(identifiers[index]); + m_Locale->AddCatalogLookupPathPrefix(m_SearchPath); + wxLogTrace(_("wxTranslationHelper: Path Prefix = \"%s\""), + m_SearchPath.GetData()); + m_Locale->AddCatalog(wxTheApp->GetAppName()); + wxLogTrace(_("wxTranslationHelper: Catalog Name = \"%s\""), + wxTheApp->GetAppName().GetData()); + return true; + } + return false; +} diff --git a/ThirdParty/wxTranslationHelper/wxTranslationHelper.h b/ThirdParty/wxTranslationHelper/wxTranslationHelper.h new file mode 100644 index 0000000..c12b3e0 --- /dev/null +++ b/ThirdParty/wxTranslationHelper/wxTranslationHelper.h @@ -0,0 +1,26 @@ +#ifndef _WX_TRANSLATION_HELPER_H +#define _WX_TRANSLATION_HELPER_H + +class wxTranslationHelper +{ + wxString m_SearchPath; + wxString m_ConfigPath; + wxLocale * m_Locale; + bool m_UseNativeConfig; +public: + wxTranslationHelper(const wxString & search_path, bool use_native_config = true); + ~wxTranslationHelper(); + wxLocale * GetLocale(); + void GetInstalledLanguages(wxArrayString & names, wxArrayLong & identifiers); + bool AskUserForLanguage(wxArrayString & names, wxArrayLong & identifiers); + bool Load(); + void Save(bool bReset = false); + + const wxString & GetSearchPath(); + void SetSearchPath(wxString & value); + + const wxString & GetConfigPath(); + void SetConfigPath(wxString &); +}; + +#endif diff --git a/ThirdParty/wxXS/include/wx/wxxmlserializer/Defs.h b/ThirdParty/wxXS/include/wx/wxxmlserializer/Defs.h new file mode 100644 index 0000000..ba7e271 --- /dev/null +++ b/ThirdParty/wxXS/include/wx/wxxmlserializer/Defs.h @@ -0,0 +1,21 @@ +#ifndef _XSDEFS_H +#define _XSDEFS_H + +#ifdef USING_SOURCE_XS + #define WXDLLIMPEXP_XS + #define WXDLLIMPEXP_DATA_XS(type) type +#elif defined( LIB_USINGDLL ) + #define WXDLLIMPEXP_XS + #define WXDLLIMPEXP_DATA_XS(type) +#elif defined( WXMAKINGDLL_WXXS ) + #define WXDLLIMPEXP_XS WXEXPORT + #define WXDLLIMPEXP_DATA_XS(type) WXEXPORT type +#elif defined(WXUSINGDLL) + #define WXDLLIMPEXP_XS WXIMPORT + #define WXDLLIMPEXP_DATA_XS(type) WXIMPORT type +#else // not making nor using DLL + #define WXDLLIMPEXP_XS + #define WXDLLIMPEXP_DATA_XS(type) type +#endif + +#endif//_XSDEFS_H diff --git a/ThirdParty/wxXS/include/wx/wxxmlserializer/PropertyIO.h b/ThirdParty/wxXS/include/wx/wxxmlserializer/PropertyIO.h new file mode 100644 index 0000000..dc81e01 --- /dev/null +++ b/ThirdParty/wxXS/include/wx/wxxmlserializer/PropertyIO.h @@ -0,0 +1,313 @@ +/*************************************************************** + * Name: PropertyIO.cpp + * Purpose: Declares data types I/O and conversion functions + * Author: Michal Bližňák (michal.bliznak@tiscali.cz) + * Created: 2007-10-28 + * Copyright: Michal Bližňák + * License: wxWidgets license (www.wxwidgets.org) + * Notes: + **************************************************************/ + +#ifndef _XSPROPERTYIO_H +#define _XSPROPERTYIO_H + +#ifndef WX_PRECOMP + #include +#endif + +#include +#include +#include +#include + +#include + +class WXDLLIMPEXP_XS xsProperty; +class WXDLLIMPEXP_XS xsSerializable; + +WX_DECLARE_OBJARRAY_WITH_DECL(wxRealPoint, RealPointArray, class WXDLLIMPEXP_XS); +WX_DECLARE_LIST_WITH_DECL(wxRealPoint, RealPointList, class WXDLLIMPEXP_XS); + +WX_DEFINE_USER_EXPORTED_ARRAY_CHAR(char, CharArray, class WXDLLIMPEXP_XS); +WX_DEFINE_USER_EXPORTED_ARRAY_INT(int, IntArray, class WXDLLIMPEXP_XS); +WX_DEFINE_USER_EXPORTED_ARRAY_LONG(long, LongArray, class WXDLLIMPEXP_XS); +WX_DEFINE_USER_EXPORTED_ARRAY_DOUBLE(double, DoubleArray, class WXDLLIMPEXP_XS); + +WX_DECLARE_STRING_HASH_MAP_WITH_DECL(wxString, StringMap, class WXDLLIMPEXP_XS); + +/*! + * \brief Base class encapsulating a property I/O handler. The class is used by + * the xsSerializable class and is responsiblefor reading and writing of an XML node + * containing property information. Each supported property (data) type should have + * its own I/O handler class. Moreover, all derived classes must provide public functions + * 'static wxString classname::ToString(datatype value)' and 'static datatype classname:: + * FromString( const wxString& value )' responsible for conversion between datatype and + * and its string representation (these functions are used internally by class virtual functions. + */ +class WXDLLIMPEXP_XS xsPropertyIO : public wxObject +{ +public: + DECLARE_DYNAMIC_CLASS(xsProperty); + + /*! \brief Constructor. */ + xsPropertyIO(){;} + /*! \brief Destructor. */ + virtual ~xsPropertyIO(){;} + + /*! + * \brief Read content of the property XML node and store it to given property object. + * \param property Pointer to the target property object + * \param source Pointer to the source XML node + */ + virtual void Read(xsProperty *property, wxXmlNode *source){wxUnusedVar(property);wxUnusedVar(source);} + /*! + * \brief Write content of given property object to target XML node. + * \param property Pointer to the source property object + * \param target Pointer to the target XML node + */ + virtual void Write(xsProperty *property, wxXmlNode *target){wxUnusedVar(property);wxUnusedVar(target);} + /*! + * \brief Get textual representation of current property value. + * \param property Pointer to the source property object + * \return Textual representation of property's value + */ + virtual wxString GetValueStr(xsProperty *property){wxUnusedVar(property);return wxT("");} + /*! + * \brief Set value defined by its textual representation to given property. + * \param property Pointer to the target property object + * \param valstr Textual representation of given value + */ + virtual void SetValueStr(xsProperty *property, const wxString& valstr){wxUnusedVar(property); wxUnusedVar(valstr);} + + /*! + * \brief Create new XML node of given name and value and assign it to the given + * parent XML node. + * \param parent Pointer to parent XML node + * \param name Name of new XML node + * \param value Content of new XML node + * \param type Type of new XML (content) node + */ + static wxXmlNode* AddPropertyNode(wxXmlNode* parent, const wxString& name, const wxString& value, wxXmlNodeType type = wxXML_TEXT_NODE ); + +protected: + + /*! + * \brief Append info about the source property to given XML node. + * \param source Pointer to the source property + * \param target Pointer to modified XML node + */ + void AppendPropertyType(xsProperty *source, wxXmlNode *target); +}; + +/*! + * \brief Macro suitable for declaration of new property I/O handler + * \param datatype Property's data type + * \param name Handler class name + */ +#define XS_DECLARE_IO_HANDLER(datatype, name) \ +class name : public xsPropertyIO \ +{ \ +public: \ + DECLARE_DYNAMIC_CLASS(name); \ + name(){;} \ + virtual ~name(){;} \ +\ + virtual void Read(xsProperty *property, wxXmlNode *source); \ + virtual void Write(xsProperty *property, wxXmlNode *target); \ + virtual wxString GetValueStr(xsProperty *property); \ + virtual void SetValueStr(xsProperty *property, const wxString& valstr); \ + static wxString ToString(const datatype& value); \ + static datatype FromString(const wxString& value); \ +}; \ + + /*! + * \brief Macro suitable for declaration of exported new property I/O handler + * \param datatype Property's data type + * \param name Handler class name + * \param decoration Class decoration + */ +#define XS_DECLARE_EXPORTED_IO_HANDLER(datatype, name, decoration) \ +class decoration name : public xsPropertyIO \ +{ \ +public: \ + DECLARE_DYNAMIC_CLASS(name); \ + name(){;} \ + virtual ~name(){;} \ +\ + virtual void Read(xsProperty *property, wxXmlNode *source); \ + virtual void Write(xsProperty *property, wxXmlNode *target); \ + virtual wxString GetValueStr(xsProperty *property); \ + virtual void SetValueStr(xsProperty *property, const wxString& valstr); \ + static wxString ToString(const datatype& value); \ + static datatype FromString(const wxString& value); \ +}; \ + + /*! + * \brief Macro suitable for implementation of new property I/O handler + * \param datatype Property's data type + * \param name Handler class name + */ +#define XS_DEFINE_IO_HANDLER(datatype, name) \ +IMPLEMENT_DYNAMIC_CLASS(name, xsPropertyIO); \ +\ +void name::Read(xsProperty *property, wxXmlNode *source) \ +{ \ + datatype value = FromString(source->GetNodeContent()); \ + *((datatype*)property->m_pSourceVariable) = value; \ +} \ +\ +void name::Write(xsProperty *property, wxXmlNode *target) \ +{ \ + wxString val = ToString(*((datatype*)property->m_pSourceVariable)); \ +\ + if(val != property->m_sDefaultValueStr) \ + { \ + wxXmlNode *newNode = AddPropertyNode(target, wxT("property"), val); \ + AppendPropertyType(property, newNode); \ + } \ +} \ +\ +wxString name::GetValueStr(xsProperty *property) \ +{ \ + return ToString(*((datatype*)property->m_pSourceVariable)); \ +} \ +\ +void name::SetValueStr(xsProperty *property, const wxString& valstr) \ +{ \ + datatype value = FromString(valstr); \ + *((datatype*)property->m_pSourceVariable) = value; \ +} \ + +/*! + * \brief Property class encapsulating I/O functions used by 'wxString' properties. + */ +XS_DECLARE_EXPORTED_IO_HANDLER(wxString, xsStringPropIO, WXDLLIMPEXP_XS); + +/*! + * \brief Property class encapsulating I/O functions used by 'wxChar' properties. + */ +XS_DECLARE_EXPORTED_IO_HANDLER(wxChar, xsCharPropIO, WXDLLIMPEXP_XS); + +/*! + * \brief Property class encapsulating I/O functions used by 'long' properties. + */ +XS_DECLARE_EXPORTED_IO_HANDLER(long, xsLongPropIO, WXDLLIMPEXP_XS); + +/*! + * \brief Property class encapsulating I/O functions used by 'int' properties. + */ +XS_DECLARE_EXPORTED_IO_HANDLER(int, xsIntPropIO, WXDLLIMPEXP_XS); + +/*! + * \brief Property class encapsulating I/O functions used by 'bool' properties. + */ +XS_DECLARE_EXPORTED_IO_HANDLER(bool, xsBoolPropIO, WXDLLIMPEXP_XS); + +/*! + * \brief Property class encapsulating I/O functions used by 'double' properties. + */ +XS_DECLARE_EXPORTED_IO_HANDLER(double, xsDoublePropIO, WXDLLIMPEXP_XS); + +/*! + * \brief Property class encapsulating I/O functions used by 'float' properties. + */ +XS_DECLARE_EXPORTED_IO_HANDLER(float, xsFloatPropIO, WXDLLIMPEXP_XS); + +/*! + * \brief Property class encapsulating I/O functions used by 'wxPoint' properties. + */ +XS_DECLARE_EXPORTED_IO_HANDLER(wxPoint, xsPointPropIO, WXDLLIMPEXP_XS); + +/*! + * \brief Property class encapsulating I/O functions used by 'wxSize' properties. + */ +XS_DECLARE_EXPORTED_IO_HANDLER(wxSize, xsSizePropIO, WXDLLIMPEXP_XS); + +/*! + * \brief Property class encapsulating I/O functions used by 'wxRealPoint' properties. + */ +XS_DECLARE_EXPORTED_IO_HANDLER(wxRealPoint, xsRealPointPropIO, WXDLLIMPEXP_XS); + +/*! + * \brief Property class encapsulating I/O functions used by 'wxColour' properties. + */ +XS_DECLARE_EXPORTED_IO_HANDLER(wxColour, xsColourPropIO, WXDLLIMPEXP_XS); + +/*! + * \brief Property class encapsulating I/O functions used by 'wxPen' properties. + */ +XS_DECLARE_EXPORTED_IO_HANDLER(wxPen, xsPenPropIO, WXDLLIMPEXP_XS); + +/*! + * \brief Property class encapsulating I/O functions used by 'wxBrush' properties. + */ +XS_DECLARE_EXPORTED_IO_HANDLER(wxBrush, xsBrushPropIO, WXDLLIMPEXP_XS); + +/*! + * \brief Property class encapsulating I/O functions used by 'wxFont' properties. + */ +XS_DECLARE_EXPORTED_IO_HANDLER(wxFont, xsFontPropIO, WXDLLIMPEXP_XS); + +/*! + * \brief Property class encapsulating I/O functions used by 'wxArrayString' properties. + */ +XS_DECLARE_EXPORTED_IO_HANDLER(wxArrayString, xsArrayStringPropIO, WXDLLIMPEXP_XS); + +/*! + * \brief Property class encapsulating I/O functions used by 'CharArray' properties. + */ +XS_DECLARE_EXPORTED_IO_HANDLER(CharArray, xsArrayCharPropIO, WXDLLIMPEXP_XS); + +/*! + * \brief Property class encapsulating I/O functions used by 'IntArray' properties. + */ +XS_DECLARE_EXPORTED_IO_HANDLER(IntArray, xsArrayIntPropIO, WXDLLIMPEXP_XS); + +/*! + * \brief Property class encapsulating I/O functions used by 'LongArray' properties. + */ +XS_DECLARE_EXPORTED_IO_HANDLER(LongArray, xsArrayLongPropIO, WXDLLIMPEXP_XS); + +/*! + * \brief Property class encapsulating I/O functions used by 'DoubleArray' properties. + */ +XS_DECLARE_EXPORTED_IO_HANDLER(DoubleArray, xsArrayDoublePropIO, WXDLLIMPEXP_XS); + +/*! + * \brief Property class encapsulating I/O functions used by 'RealPointArray' (array of + * integer values) properties. + */ +XS_DECLARE_EXPORTED_IO_HANDLER(RealPointArray, xsArrayRealPointPropIO, WXDLLIMPEXP_XS); + +/*! + * \brief Property class encapsulating I/O functions used by 'ListRealPoint' (list of + * wxRealPoint objects) properties. + */ +XS_DECLARE_EXPORTED_IO_HANDLER(RealPointList, xsListRealPointPropIO, WXDLLIMPEXP_XS); + +/*! + * \brief Property class encapsulating I/O functions used by 'serializabledynamic' (xsSerializable + * dynamic class objects which are created during the deserialization process) properties. + */ +XS_DECLARE_EXPORTED_IO_HANDLER(xsSerializable, xsDynObjPropIO, WXDLLIMPEXP_XS); + +/*! + * \brief Property class encapsulating I/O functions used by 'serializabledynamicnocreate' (already + * existing xsSerializable dynamic class objects) properties. + */ +XS_DECLARE_EXPORTED_IO_HANDLER(xsSerializable, xsDynNCObjPropIO, WXDLLIMPEXP_XS); + +/*! + * \brief Property class encapsulating I/O functions used by 'serializablestatic' (static + * xsSerializable class objects) properties. + */ +XS_DECLARE_EXPORTED_IO_HANDLER(xsSerializable, xsStaticObjPropIO, WXDLLIMPEXP_XS); + +/*! + * \brief Property class encapsulating I/O functions used by 'mapstring' (string hash map) properties. + */ +XS_DECLARE_EXPORTED_IO_HANDLER(StringMap, xsMapStringPropIO, WXDLLIMPEXP_XS); + +WX_DECLARE_HASH_MAP( wxString, xsPropertyIO*, wxStringHash, wxStringEqual, PropertyIOMap ); + +#endif //_XSPROPERTYIO_H diff --git a/ThirdParty/wxXS/include/wx/wxxmlserializer/XmlSerializer.h b/ThirdParty/wxXS/include/wx/wxxmlserializer/XmlSerializer.h new file mode 100644 index 0000000..4c61c97 --- /dev/null +++ b/ThirdParty/wxXS/include/wx/wxxmlserializer/XmlSerializer.h @@ -0,0 +1,1063 @@ +/*************************************************************** + * Name: XmlSerializer.h + * Purpose: Defines XML serializer and related classes + * Author: Michal Bližňák (michal.bliznak@tiscali.cz) + * Created: 2007-08-28 + * Copyright: Michal Bližňák + * License: wxWidgets license (www.wxwidgets.org) + * Notes: + **************************************************************/ + +#ifndef _XSXMLSERIALIZE_H +#define _XSXMLSERIALIZE_H + +#ifndef WX_PRECOMP + #include +#endif + +#include + +#include +#include + +#define xsWITH_ROOT true +#define xsWITHOUT_ROOT false + +#define xsRECURSIVE true +#define xsNORECURSIVE false + +/*! \brief Macro creates new serialized STRING property */ +#define XS_SERIALIZE_STRING(x, name) XS_SERIALIZE_PROPERTY(x, wxT("string"), name); +/*! \brief Macro creates new serialized STRING property with defined default value */ +#define XS_SERIALIZE_STRING_EX(x, name, def) XS_SERIALIZE_PROPERTY_EX(x, wxT("string"), name, def); +/*! \brief Macro creates new serialized STRING property */ +#define XS_SERIALIZE_CHAR(x, name) XS_SERIALIZE_PROPERTY(x, wxT("char"), name); +/*! \brief Macro creates new serialized STRING property with defined default value */ +#define XS_SERIALIZE_CHAR_EX(x, name, def) XS_SERIALIZE_PROPERTY_EX(x, wxT("char"), name, def); +/*! \brief Macro creates new serialized LONG property */ +#define XS_SERIALIZE_LONG(x, name) XS_SERIALIZE_PROPERTY(x, wxT("long"), name); +/*! \brief Macro creates new serialized LONG property with defined default value */ +#define XS_SERIALIZE_LONG_EX(x, name, def) XS_SERIALIZE_PROPERTY_EX(x, wxT("long"), name, xsLongPropIO::ToString(def)); +/*! \brief Macro creates new serialized DOUBLE property */ +#define XS_SERIALIZE_DOUBLE(x, name) XS_SERIALIZE_PROPERTY(x, wxT("double"), name); +/*! \brief Macro creates new serialized DOUBLE property with defined default value */ +#define XS_SERIALIZE_DOUBLE_EX(x, name, def) XS_SERIALIZE_PROPERTY_EX(x, wxT("double"), name, xsDoublePropIO::ToString(def)); +/*! \brief Macro creates new serialized INT property */ +#define XS_SERIALIZE_INT(x, name) XS_SERIALIZE_PROPERTY(x, wxT("int"), name); +/*! \brief Macro creates new serialized INT property with defined default value */ +#define XS_SERIALIZE_INT_EX(x, name, def) XS_SERIALIZE_PROPERTY_EX(x, wxT("int"), name, xsIntPropIO::ToString(def)); +/*! \brief Macro creates new serialized FLOAT property */ +#define XS_SERIALIZE_FLOAT(x, name) XS_SERIALIZE_PROPERTY(x, wxT("float"), name); +/*! \brief Macro creates new serialized FLOAT property with defined default value */ +#define XS_SERIALIZE_FLOAT_EX(x, name, def) XS_SERIALIZE_PROPERTY_EX(x, wxT("float"), name, xsFloatPropIO::ToString(def)); + +/*! \brief Macro creates new serialized BOOL property */ +#define XS_SERIALIZE_BOOL(x, name) XS_SERIALIZE_PROPERTY(x, wxT("bool"), name); +/*! \brief Macro creates new serialized BOOL property with defined default value */ +#define XS_SERIALIZE_BOOL_EX(x, name, def) XS_SERIALIZE_PROPERTY_EX(x, wxT("bool"), name, xsBoolPropIO::ToString(def)); + +/*! \brief Macro creates new serialized wxPoint property */ +#define XS_SERIALIZE_POINT(x, name) XS_SERIALIZE_PROPERTY(x, wxT("point"), name); +/*! \brief Macro creates new serialized wxPoint property with defined default value */ +#define XS_SERIALIZE_POINT_EX(x, name, def) XS_SERIALIZE_PROPERTY_EX(x, wxT("point"), name, xsPointPropIO::ToString(def)); +/*! \brief Macro creates new serialized wxRealPoint property */ +#define XS_SERIALIZE_REALPOINT(x, name) XS_SERIALIZE_PROPERTY(x, wxT("realpoint"), name); +/*! \brief Macro creates new serialized wxRealPoint property with defined default value */ +#define XS_SERIALIZE_REALPOINT_EX(x, name, def) XS_SERIALIZE_PROPERTY_EX(x, wxT("realpoint"), name, xsRealPointPropIO::ToString(def)); +/*! \brief Macro creates new serialized wxSize property */ +#define XS_SERIALIZE_SIZE(x, name) XS_SERIALIZE_PROPERTY(x, wxT("size"), name); +/*! \brief Macro creates new serialized wxSize property with defined default value */ +#define XS_SERIALIZE_SIZE_EX(x, name, def) XS_SERIALIZE_PROPERTY_EX(x, wxT("size"), name, xsSizePropIO::ToString(def)); + +/*! \brief Macro creates new serialized wxColour property */ +#define XS_SERIALIZE_COLOUR(x, name) XS_SERIALIZE_PROPERTY(x, wxT("colour"), name); +/*! \brief Macro creates new serialized wxColour property with defined default value */ +#define XS_SERIALIZE_COLOUR_EX(x, name, def) XS_SERIALIZE_PROPERTY_EX(x, wxT("colour"), name, xsColourPropIO::ToString(def)); +/*! \brief Macro creates new serialized wxPen property */ +#define XS_SERIALIZE_PEN(x, name) XS_SERIALIZE_PROPERTY(x, wxT("pen"), name); +/*! \brief Macro creates new serialized wxPen property with defined default value */ +#define XS_SERIALIZE_PEN_EX(x, name, def) XS_SERIALIZE_PROPERTY_EX(x, wxT("pen"), name, xsPenPropIO::ToString(def)); +/*! \brief Macro creates new serialized wxBrush property */ +#define XS_SERIALIZE_BRUSH(x, name) XS_SERIALIZE_PROPERTY(x, wxT("brush"), name); +/*! \brief Macro creates new serialized wxBrush property with defined default value */ +#define XS_SERIALIZE_BRUSH_EX(x, name, def) XS_SERIALIZE_PROPERTY_EX(x, wxT("brush"), name, xsBrushPropIO::ToString(def)); +/*! \brief Macro creates new serialized wxFont property */ +#define XS_SERIALIZE_FONT(x, name) XS_SERIALIZE_PROPERTY(x, wxT("font"), name); +/*! \brief Macro creates new serialized wxFont property with defined default value */ +#define XS_SERIALIZE_FONT_EX(x, name, def) XS_SERIALIZE_PROPERTY_EX(x, wxT("font"), name, xsFontPropIO::ToString(def)); + +/*! \brief Macro creates new serialized property (type 'array of strings (wxArrayString)') */ +#define XS_SERIALIZE_ARRAYSTRING(x, name) XS_SERIALIZE_PROPERTY(x, wxT("arraystring"), name); +/*! \brief Macro creates new serialized property (type 'array of chars (CharArray)') */ +#define XS_SERIALIZE_ARRAYCHAR(x, name) XS_SERIALIZE_PROPERTY(x, wxT("arraychar"), name); +/*! \brief Macro creates new serialized property (type 'array of ints (IntArray)') */ +#define XS_SERIALIZE_ARRAYINT(x, name) XS_SERIALIZE_PROPERTY(x, wxT("arrayint"), name); +/*! \brief Macro creates new serialized property (type 'array of longs (LongArray)') */ +#define XS_SERIALIZE_ARRAYLONG(x, name) XS_SERIALIZE_PROPERTY(x, wxT("arraylong"), name); +/*! \brief Macro creates new serialized property (type 'array of doubles (DoubleArray)') */ +#define XS_SERIALIZE_ARRAYDOUBLE(x, name) XS_SERIALIZE_PROPERTY(x, wxT("arraydouble"), name); +/*! \brief Macro creates new serialized property (type 'array of wxRealPoint objects') */ +#define XS_SERIALIZE_ARRAYREALPOINT(x, name) XS_SERIALIZE_PROPERTY(x, wxT("arrayrealpoint"), name); +/*! \brief Macro creates new serialized property (type 'list of wxRealPoint objects') */ +#define XS_SERIALIZE_LISTREALPOINT(x, name) XS_SERIALIZE_PROPERTY(x, wxT("listrealpoint"), name); + +/*! \brief Macro creates new serialized property (type 'string hash map (StringMap)') */ +#define XS_SERIALIZE_MAPSTRING(x, name) XS_SERIALIZE_PROPERTY(x, wxT("mapstring"), name); + +/*! \brief Macro creates new serialized property encapsulating a dynamic serializable object */ +#define XS_SERIALIZE_DYNAMIC_OBJECT(x, name) XS_SERIALIZE_PROPERTY(x, wxT("serializabledynamic"), name); +/*! \brief Macro creates new serialized property encapsulating a dynamic serializable object */ +#define XS_SERIALIZE_DYNAMIC_OBJECT_NO_CREATE(x, name) XS_SERIALIZE_PROPERTY(x, wxT("serializabledynamicnocreate"), name); +/*! \brief Macro creates new serialized property encapsulating a static serializable object */ +#define XS_SERIALIZE_STATIC_OBJECT(x, name) XS_SERIALIZE_PROPERTY(x, wxT("serializablestatic"), name); + +/*! \brief Macro creates new serialized property of given type */ +#define XS_SERIALIZE_PROPERTY(x, type, name) AddProperty(new xsProperty(&x, type, name)); +/*! \brief Macro creates new serialized property of given type with defined dafult value */ +#define XS_SERIALIZE_PROPERTY_EX(x, type, name, def) AddProperty(new xsProperty(&x, type, name, def)); + +/*! \brief Macro creates new serialized property and automaticaly determines its type (if supported) */ +#define XS_SERIALIZE(x, name) AddProperty(new xsProperty(&x, name)); +/*! \brief Macro creates new serialized property with defined dafult value and automaticaly determines its type (if supported)*/ +#define XS_SERIALIZE_EX(x, name, def) AddProperty(new xsProperty(&x, name, def)); + +/*! \brief Macro registers new IO handler for specified data type (handler class must exist) */ +#define XS_REGISTER_IO_HANDLER(type, class) wxXmlSerializer::m_mapPropertyIOHandlers[type] = new class(); + + +/*! \brief Enable RTTI (the same as DECLARE_DYNAMIC_CLASS) and declare xsSerializable::Clone() function */ +#define XS_DECLARE_CLONABLE_CLASS(name) \ +public: \ + DECLARE_DYNAMIC_CLASS(name) \ + virtual wxObject* Clone(); \ + +/*! \brief Enable RTTI (the same as IMPLEMENT_DYNAMIC_CLASS) and implement xsSerializable::Clone() function */ +#define XS_IMPLEMENT_CLONABLE_CLASS(name, base) \ + IMPLEMENT_DYNAMIC_CLASS(name, base) \ + wxObject* name::Clone() \ + { \ + if( m_fClone ) return new name(*this); \ + else \ + return NULL; \ + } \ + + +class WXDLLIMPEXP_XS xsProperty; +class WXDLLIMPEXP_XS xsSerializable; +class WXDLLIMPEXP_XS wxXmlSerializer; + +WX_DECLARE_LIST_WITH_DECL(xsProperty, PropertyList, class WXDLLIMPEXP_XS); +WX_DECLARE_LIST_WITH_DECL(xsSerializable, SerializableList, class WXDLLIMPEXP_XS); + +WX_DECLARE_HASH_MAP( long, xsSerializable*, wxIntegerHash, wxIntegerEqual, IDMap ); + +/*! + * \brief Base class encapsulating object which can be serialized/deserialized to/from + * XML file (disk file or any stream). This class acts as a data container for properties + * (xsProperty class objects) encapsulating serialized class data members. + * + * Class data members which should be serialized must be marked by appropriate macro defined + * in wxXmlSerializer.h header file (it is recommended to mark desired data members in the class constructor). + * + * Instances of this class can be arranged into a list/d-ary tree hierarchy so it can behave like + * powerfull data container. All chained serializable class objects can be handled by class + * member functions or by member functions of wxXmlSerializer class object which should be + * used as their manager (recommended way). + * + * Another built-in (optional) functionality is class instaces' cloning. User can use + * XS_DECLARE_CLONABLE_CLASS and XS_IMPLEMENT_CLONABLE_CLASS macros instead of classic + * DECLARE_DYNAMIC_CLASS and IMPLEMENT_DYNAMIC_CLASS macros which lead to definition of + * xsSerializable::Clone() virtual function used for cloning of current class instance + * via its copy constructor (user must define it manually). Virtual xsSerializble::Clone() + * function is also used by the wxXmlSerializer::CopyItems() function (used by the + * wxXmlSerializer copy constructor). + */ +class WXDLLIMPEXP_XS xsSerializable : public wxObject +{ +public: + friend class wxXmlSerializer; + + XS_DECLARE_CLONABLE_CLASS(xsSerializable); + + enum SEARCHMODE + { + /*! \brief Depth-First-Search algorithm */ + searchDFS, + /*! \brief Breadth-First-Search algorithm */ + searchBFS + }; + + /*! \brief Constructor. */ + xsSerializable(); + /*! \brief Copy constructor. */ + xsSerializable(const xsSerializable& obj); + /*! \brief Destructor. */ + ~xsSerializable(); + + // public functions + + /*! + * \brief Get serializable parent object. + * \return Pointer to serializable parent object if exists, otherwise NULL + */ + inline xsSerializable* GetParent() { return m_pParentItem; } + /*! + * \brief Get parent data manager (instance of wxXmlSerializer). + * \return Pointer to parent data manager if set, otherwise NULL + */ + inline wxXmlSerializer* GetParentManager() { return m_pParentManager; } + /*! + * \brief Get first serializable child object. + * \return Pointer to child object if exists, otherwise NULL + */ + xsSerializable* GetFirstChild(); + /*! + * \brief Get first serializable child object of given type. + * \param type Child object type (can be NULL for any type) + * \return Pointer to child object if exists, otherwise NULL + */ + xsSerializable* GetFirstChild(wxClassInfo *type); + /*! + * \brief Get last serializable child object. + * \return Pointer to child object if exists, otherwise NULL + */ + xsSerializable* GetLastChild(); + /*! + * \brief Get last serializable child object of given type. + * \param type Child object type (can be NULL for any type) + * \return Pointer to child object if exists, otherwise NULL + */ + xsSerializable* GetLastChild(wxClassInfo *type); + /*! + * \brief Get next serializable sibbling object. + * \return Pointer to sibbling object if exists, otherwise NULL + */ + xsSerializable* GetSibbling(); + /*! + * \brief Get next serializable sibbling object of given type. + * \param type Child object type (can be NULL for any type) + * \return Pointer to sibbling object if exists, otherwise NULL + */ + xsSerializable* GetSibbling(wxClassInfo *type); + /*! + * \brief Get child item with given ID if exists. + * \param id ID of searched child item + * \param recursive If TRUE then the child shape will be searched recursivelly + * \return Pointer to first child with given ID if pressent, otherwise NULL + */ + xsSerializable* GetChild(long id, bool recursive = xsNORECURSIVE); + + /*! + * \brief Function finds out whether this serializable item has some children. + * \return TRUE if the parent shape has children, otherwise FALSE + */ + inline bool HasChildren() const { return !m_lstChildItems.IsEmpty(); } + /*! + * \brief Get list of children (serializable objects) of this object. + * \return Reference to a list with child serializable objects (can be empty) + */ + inline SerializableList& GetChildrenList() { return m_lstChildItems; } + /*! + * \brief Get children of given type. + * \param type Child object type (if NULL then all children are returned) + * \param list Reference to a list where all found child objects will be appended + */ + void GetChildren(wxClassInfo *type, SerializableList& list); + /*! + * \brief Get all children of given type recursively (i.e. children of children of .... ). + * \param type Get only children of given type (if NULL then all children are returned) + * \param list Reference to a list where all found child objects will be appended + * \param mode Search mode. User can choose Depth-First-Search or Breadth-First-Search algorithm (BFS is default) + * \sa SEARCHMODE + */ + void GetChildrenRecursively(wxClassInfo *type, SerializableList& list, SEARCHMODE mode = searchBFS); + /*! + * \brief Get pointer to list node containing first serializable child object. + */ + inline SerializableList::compatibility_iterator GetFirstChildNode() const { return m_lstChildItems.GetFirst(); } + /*! + * \brief Get pointer to list node containing last serializable child object. + */ + inline SerializableList::compatibility_iterator GetLastChildNode() const { return m_lstChildItems.GetLast(); } + + /*! + * \brief Set serializable parent object. + * \param parent Pointer to parent object + */ + inline void SetParent(xsSerializable* parent) { m_pParentItem = parent; } + /*! + * \brief Set parent data manager. + * \param parent Pointer to parent data manager + */ + inline void SetParentManager(wxXmlSerializer* parent) { m_pParentManager = parent; } + /*! + * \brief Add serializable child object to this object. + * \param child Pointer to added child object (must NOT be NULL) + * \return Pointer to to the added child object + */ + xsSerializable* AddChild(xsSerializable* child); + /*! + * \brief Insert serializable child object to this object at given position. + * \param pos Zero-based position + * \param child Pointer to added child object (must NOT be NULL) + * \return Pointer to to the added child object + */ + xsSerializable* InsertChild(size_t pos, xsSerializable* child); + /*! + * \brief Assign this object as a child to given parent object. + * \param parent Pointer to new parent object (must NOT be NULL) + */ + void Reparent(xsSerializable* parent); + + /*! + * \brief Set ID of this object. Can be used for further objects' handling by + * wxXmlSerializer class (default ID value is -1). This functions should NOT + * be used directly; it is called by wxXmlSerializer object in the case that this + * serializable object is attached to another one (or directly to root node of wxXmlSerializer) by + * wxXmlSerializer::AddItem() member function. + */ + void SetId(long id); + /*! + * \brief Get object ID. + * \return ID value or -1 if the ID hasn't been set yet + */ + inline long GetId() const { return m_nId; } + + /*! + * \brief Create new 'object' XML node and serialize all marked class data members (properties) into it. + * \param node Pointer to parent XML node + * \return Pointer to modified parent XML node + */ + wxXmlNode* SerializeObject(wxXmlNode* node); + /*! + * \brief Deserialize marked class data members (properties) from appropriate fields of given + * parent 'object' XML node. + * \param node Pointer to parent 'object' XML node + */ + void DeserializeObject(wxXmlNode* node); + + /*! + * \brief Add new property to the property list. + * \param property Pointer to added property object + * \sa xsProperty + */ + void AddProperty(xsProperty* property); + /** + * \brief Remove given property from the property list. + * \param property Pointer to existing property. + * \sa xsProperty, GetProperty() + */ + void RemoveProperty(xsProperty *property); + /*! + * \brief Get serialized property of given name. + * \return Pointer to the property object if exists, otherwise NULL + * \sa xsProperty + */ + xsProperty* GetProperty(const wxString& field); + /*! + * \brief Get reference to properties list. + * \sa xsProperty + */ + inline PropertyList& GetProperties() { return m_lstProperties; } + + /*! + * \brief Enable/disable serialization of given property. + * \param field Property name + * \param enab TRUE if the property should be serialized, otherwise FALSE + */ + void EnablePropertySerialization(const wxString& field, bool enab); + /*! + * \brief Returns information whether the given property is serialized or not. + * \param field Name of examined property + */ + bool IsPropertySerialized(const wxString& field); + /*! + * \brief Enable/disable object serialization. + * \param enab TRUE if the object should be serialized, otherwise FALSE + */ + inline void EnableSerialization(bool enab) { m_fSerialize = enab; } + /*! + * \brief Returns information whether the object can be serialized or not. + */ + inline bool IsSerialized() const { return m_fSerialize; } + /*! + * \brief Enable/disable object cloning. + * \param enab TRUE if the object can be cloned, otherwise FALSE + */ + inline void EnableCloning(bool enab) { m_fClone = enab; } + /*! + * \brief Returns information whether the object can be cloned or not. + */ + inline bool IsCloningEnabled() const { return m_fClone; } + + // overloaded operators + /*! + * \brief Add serializable child object to this object. + * \param child Pointer to added child object (should NOT be NULL) + * \return Pointer to added object + */ + xsSerializable* operator<<(xsSerializable *child); + +protected: + // protected data members + /*! \brief List of serialized properties */ + PropertyList m_lstProperties; + /*! \brief List of child objects */ + SerializableList m_lstChildItems; + + /*! \brief Pointer to parent serializable object */ + xsSerializable *m_pParentItem; + /*! \brief Pointer to parent data manager */ + wxXmlSerializer *m_pParentManager; + + /*! \brief Object serialization flag */ + bool m_fSerialize; + /*! \brief Object cloning flag */ + bool m_fClone; + + /** + * \brief Initialize new child object. + * \param child Pointer to new child object + */ + void InitChild(xsSerializable *child); + + // protected virtual functions + /*! + * \brief Serialize stored properties to the given XML node. The serialization + * routine is automatically called by the framework and cares about serialization + * of all defined properties. + * + * Note that default implementation automatically serializes all class data members + * marked by appropriate macros. If some non-standard class member should be serialized as well, + * the source code of derived function implementation can be as in following example. + * + * \param node Pointer to XML node where the property nodes will be appended to + * + * Example code: + * \code + * wxXmlNode* DerivedFrom_xsSerializable::Serialize(wxXmlNode* node) + * { + * if(node) + * { + * // call base class's serialization routine + * node = xsSeralizable::Serialize(node); + * + * // serialize custom property + * xsPropertyIO::AddPropertyNode(node, wxT("some_property_field_name"), wxT("string_repr_of_its_value")); + * } + * // return updated node + * return node; + * } + * \endcode + */ + virtual wxXmlNode* Serialize(wxXmlNode* node); + /*! + * \brief Deserialize object properties from the given XML node. The + * routine is automatically called by the framework and cares about deserialization + * of all defined properties. + * + * Note that default implementation automatically deserializes all class data members + * marked by appropriate macros. If some non-standard class member should be deserialized as well, + * the source code of derived function implementation can be as in following example. + * + * \param node Pointer to a source XML node containig the property nodes + * + * Example code: + * \code + * void DerivedFrom_xsSerializable::Deserialize(wxXmlNode* node) + * { + * // call base class's deserialization rountine (if necessary...) + * xsSerializable::Deserialize(node); + * + * // iterate through all custom property nodes + * wxXmlNode *propNode = node->GetChildren(); + * while(propNode) + * { + * if(propNode->GetName() == wxT("some_property_field_name")) + * { + * // read the node content and convert it to a proper data type + * } + * propNode = propNode->GetNext(); + * } + * } + * \endcode + */ + virtual void Deserialize(wxXmlNode* node); + +private: + /*! \brief Object ID */ + long m_nId; +}; + +/*! + * \brief Class encapsulates a serializable objects' manager which is responsible + * for handling stored serializable objects and their serialization/deserialization + * from/to XML files or streams. + * + * Stored objects can be arranged into a list or d-ary tree structure so this class + * can be used as a container for various application data. Also created XML files + * (root node) can be marked with given version and owner name, so it is possible to + * control a version of saved document. + * + * wxXmlSerializer class contains one instance of xsSerializable object created in the + * class constructor (can be set later via member functions as well). This serializable + * object called 'root object' holds all other inserted serializable objects (in case of + * tree structure it is a topmost tree node, in case of list structure all list items are + * its children). These child object can be handled via xsSerializable and wxXmlSerializer + * classes' member functions. + * + * Another built-in (optional) functionality is class instaces' cloning. User can use + * XS_DECLARE_CLONABLE_CLASS and XS_IMPLEMENT_CLONABLE_CLASS macros instead of classic + * DECLARE_DYNAMIC_CLASS and IMPLEMENT_DYNAMIC_CLASS macros which lead to definition of + * wxXmlSerializer::Clone() virtual function used for cloning of current class instance + * via its copy constructor (user must define it manually). + */ +class WXDLLIMPEXP_XS wxXmlSerializer : public wxObject +{ +public: + XS_DECLARE_CLONABLE_CLASS(wxXmlSerializer); + + /*! \brief Constructor. */ + wxXmlSerializer(); + /*! + * \brief User constructor. + * \param owner Owner name + * \param root Name of root node + * \param version File version + */ + wxXmlSerializer(const wxString& owner, const wxString& root, const wxString& version); + /*! \brief Copy constructor. */ + wxXmlSerializer(const wxXmlSerializer &obj); + /*! \brief Destructor. */ + virtual ~wxXmlSerializer(); + + // public member data accessors + /*! + * \brief Set owner name. + * \param name Owner name + */ + inline void SetSerializerOwner(const wxString& name) { m_sOwner = name; } + /*! + * \brief Set root name. + * \param name Root name + */ + inline void SetSerializerRootName(const wxString& name) { m_sRootName = name; } + /*! + * \brief Set file version. + * \param name File version + */ + inline void SetSerializerVersion(const wxString& name) { m_sVersion = name; } + /*! \brief Get owner name. */ + inline const wxString& GetSerializerOwner() const { return m_sOwner; } + /*! \brief Get name of root node. */ + inline const wxString& GetSerializerRootName() const { return m_sRootName; } + /*! \brief Get file version. */ + inline const wxString& GetSerializerVersion() const { return m_sVersion; } + /*! \brief Get the library version. */ + inline const wxString& GetLibraryVersion() const { return m_sLibraryVersion; } + + // public functions + /*! \brief Get pointer to root serializable object. */ + inline xsSerializable* GetRootItem() const { return m_pRoot; } + /*! + * \brief Get serializable object with given ID. + * \param id Object ID + * \return Pointer to serializable object if exists, otherwise NULL + */ + xsSerializable* GetItem(long id); + /*! + * \brief Get items of given class type. + * \param type Class type + * \param list List with matching serializable objects + * \param mode Search mode + * \sa xsSerializable::SEARCHMODE + */ + void GetItems(wxClassInfo* type, SerializableList& list, xsSerializable::SEARCHMODE mode = xsSerializable::searchBFS); + /*! + * \brief Check whether given object is included in the serializer. + * \param object Pointer to checked object + * \return True if the object is included in the serializer, otherwise False + */ + bool Contains(xsSerializable *object) const; + /*! + * \brief Check whether any object of given type is included in the serializer. + * \param type Pointer to class info + * \return True if at least one object of given type is included in the serializer, otherwise False + */ + bool Contains(wxClassInfo *type); + + /*! + * \brief Set root item. + * \param root Pointer to root item + */ + void SetRootItem(xsSerializable* root, bool bDeleteCurrentRoot = true); + + /*! + * \brief Replace current stored data with a content stored in given source manager. + * + * For proper functionality all stored data items derived from the xsSerializable class + * MUST implement virtual function xsSerializable::Clone() as well as the copy + * constructor. For more details see the xsSerializable::Clone() function documentation. + * \param src Reference to the source data manager + */ + void CopyItems(const wxXmlSerializer& src); + /*! + * \brief Add serializable object to the serializer. + * \param parentId ID of parent serializable object + * \param item Added serializable object + * \return Pointer to added item + */ + xsSerializable* AddItem(long parentId, xsSerializable* item); + /*! + * \brief Add serializable object to the serializer. + * \param parent Pointer to parent serializable object (if NULL then the object + * is added directly to the root item) + * \param item Added serializable object + * \return Pointer to added item + */ + xsSerializable* AddItem(xsSerializable* parent, xsSerializable* item); + /*! + * \brief Remove serializable object from the serializer (object will be destroyed). + * \param id Object ID + */ + void RemoveItem(long id); + /*! + * \brief Remove serializable object from the serializer (object will be destroyed). + * \param item Pointer to removed object + */ + void RemoveItem(xsSerializable* item); + /*! \brief Remove and destroy all stored serializable objects*/ + void RemoveAll(); + /*! + * \brief Enable/disable object cloning. + * \param enab TRUE if the object can be cloned, otherwise FALSE + */ + inline void EnableCloning(bool enab) { m_fClone = enab; } + /*! + * \brief Returns information whether the object can be cloned or not. + */ + inline bool IsCloned() const { return m_fClone; } + + /*! + * \brief Serialize stored objects to given file. + * \param file Full path to output file + * \param withroot If TRUE then the root item's properties are serialized as well + * \return TRUE on success, otherwise FALSE + */ + virtual bool SerializeToXml(const wxString& file, bool withroot = false); + /*! + * \brief Serialize stored objects to given stream. + * \param outstream Output stream + * \param withroot If TRUE then the root item's properties are serialized as well + * \return TRUE on success, otherwise FALSE + */ + virtual bool SerializeToXml(wxOutputStream& outstream, bool withroot = false); + /*! + * \brief Deserialize objects from given file. + * \param file Full path to input file + * \return TRUE on success, otherwise FALSE + */ + virtual bool DeserializeFromXml(const wxString& file); + /*! + * \brief Deserialize objects from given stream. + * \param instream Input stream + * \return TRUE on success, otherwise FALSE + */ + virtual bool DeserializeFromXml(wxInputStream& instream); + + /*! + * \brief Serialize child objects of given parent object (parent object can be optionaly + * serialized as well) to given XML node. The function can be overriden if necessary. + * \param parent Pointer to parent serializable object + * \param node Pointer to output XML node + * \param withparent TRUE if the parent object should be serialized as well + */ + virtual void SerializeObjects(xsSerializable* parent, wxXmlNode* node, bool withparent); + /*! + * \brief Deserialize child objects of given parent object from given XML node. + * The function can be overriden if necessary. + * \param parent Pointer to parent serializable object + * \param node Pointer to input XML node + */ + virtual void DeserializeObjects(xsSerializable* parent, wxXmlNode* node); + + /*! + * \brief Get the lowest free object ID + */ + long GetNewId(); + /*! + * \brief Find out whether given object ID is already used. + * \param id Object ID + * \return TRUE if the object ID is used, otherwise FALSE + */ + bool IsIdUsed(long id); + /*! + * \brief Get number of occurences of given ID. + * \param id Object ID + * \return Number of ID's occurences + */ + int GetIDCount(long id); + /*! + * \brief Get map of used IDs. + * \return Reference to map where all used IDs are stored + */ + IDMap& GetUsedIDs() { return m_mapUsedIDs; } + + /*! \brief Initialize all standard property IO handlers */ + void InitializeAllIOHandlers(); + /*! \brief Clear all initialized property IO handlers */ + void ClearIOHandlers(); + /*! + * \brief Get property I/O handler for given datatype. + * \param datatype String ID of data type + * \return Pointer to I/O handler suitable for given data type if exists, otherwise NULL + */ + inline static xsPropertyIO* GetPropertyIOHandler(const wxString& datatype) { return m_mapPropertyIOHandlers[datatype]; } + + /*! \brief Map of property IO handlers */ + static PropertyIOMap m_mapPropertyIOHandlers; + + // overloaded operators + /*! + * \brief Add serializable object to the serializer's root. + * \param obj Pointer to serializable object + */ + void operator<< (xsSerializable *obj) { if( obj ) this->AddItem( (xsSerializable*)NULL, obj); } + +protected: + // protected data members + /*! \brief Owner name */ + wxString m_sOwner; + /*! \brief Root node name */ + wxString m_sRootName; + /*! \brief File version */ + wxString m_sVersion; + + /*! \brief Pointer to root object */ + xsSerializable* m_pRoot; + + /*! \brief Object cloning flag */ + bool m_fClone; + + /*! \brief Map storing information which ID is already used */ + IDMap m_mapUsedIDs; + +private: + // private data members + //int m_nCounter; + static int m_nRefCounter; + static wxString m_sLibraryVersion; + + // private functions + /*! \brief Auxiliary function */ + xsSerializable* _GetItem(long id, xsSerializable* parent); + /*! \brief Auxiliary function */ + bool _Contains(xsSerializable *object, xsSerializable* parent) const; +}; + +/*! + * \brief Class encapsulates a property stored in a list included inside a parent serializable + * object (class xsSerializable) which is serialized/deserialized to/from XML file. The + * property object type is defined by a string name and is processed by parent xsSerializable class object. + * + * Allowed property data types (keywords) are: 'long', 'double', 'bool', 'string', 'point', 'size', + * 'realpoint', 'colour', 'brush', 'pen', 'font', 'arraystring', 'arrayrealpoint', 'listrealpoint', + * 'serializabledynamic' and 'serializablestatic'. Only properties of these data types are recognized + * and processed by parent serializable object. + */ +class WXDLLIMPEXP_XS xsProperty : public wxObject +{ +public: + DECLARE_DYNAMIC_CLASS(xsProperty); + + /*! \brief Default constructor */ + xsProperty() + { + m_pSourceVariable = NULL; + m_sDataType = wxT("Undefined"); + m_sFieldName = wxT("Undefined"); + m_sDefaultValueStr = wxT(""); + m_fSerialize = false; + } + + /*! + * \brief Constructor + * \param src Pointer to serialized object + * \param type String value describing data type of serialized object + * \param field Property name used in XML files and for property handling + * \param def String representation of default poperty value + */ + xsProperty(void* src, const wxString& type, const wxString& field, const wxString& def = wxT("")) + { + m_pSourceVariable = src; + m_sDataType = type; + m_sFieldName = field; + m_sDefaultValueStr = def; + m_fSerialize = true; + } + + /*! \brief Constructor for BOOL property. */ + xsProperty(bool* src, const wxString& field) : m_pSourceVariable((void*)src), m_sFieldName(field), m_sDataType(wxT("bool")), m_sDefaultValueStr(wxT("")), m_fSerialize(true) {;} + /*! \brief Constructor for BOOL property with defined default value. */ + xsProperty(bool* src, const wxString& field, bool def) : m_pSourceVariable((void*)src), m_sFieldName(field), m_sDataType(wxT("bool")), m_sDefaultValueStr(xsBoolPropIO::ToString(def)), m_fSerialize(true) {;} + + /*! \brief Constructor for LONG property. */ + xsProperty(long* src, const wxString& field) : m_pSourceVariable((void*)src), m_sFieldName(field), m_sDataType(wxT("long")), m_sDefaultValueStr(wxT("")), m_fSerialize(true) {;} + /*! \brief Constructor for LONG property with defined default value. */ + xsProperty(long* src, const wxString& field, long def) : m_pSourceVariable((void*)src), m_sFieldName(field), m_sDataType(wxT("long")), m_sDefaultValueStr(xsLongPropIO::ToString(def)), m_fSerialize(true) {;} + + /*! \brief Constructor for INT property. */ + xsProperty(int* src, const wxString& field) : m_pSourceVariable((void*)src), m_sFieldName(field), m_sDataType(wxT("int")), m_sDefaultValueStr(wxT("")), m_fSerialize(true) {;} + /*! \brief Constructor for INT property with defined default value. */ + xsProperty(int* src, const wxString& field, int def) : m_pSourceVariable((void*)src), m_sFieldName(field), m_sDataType(wxT("int")), m_sDefaultValueStr(xsIntPropIO::ToString(def)), m_fSerialize(true) {;} + + /*! \brief Constructor for DOUBLE property. */ + xsProperty(double* src, const wxString& field) : m_pSourceVariable((void*)src), m_sFieldName(field), m_sDataType(wxT("double")), m_sDefaultValueStr(wxT("")), m_fSerialize(true) {;} + /*! \brief Constructor for DOUBLE property with defined default value. */ + xsProperty(double* src, const wxString& field, double def) : m_pSourceVariable((void*)src), m_sFieldName(field), m_sDataType(wxT("double")), m_sDefaultValueStr(xsDoublePropIO::ToString(def)), m_fSerialize(true) {;} + + /*! \brief Constructor for FLOAT property. */ + xsProperty(float* src, const wxString& field) : m_pSourceVariable((void*)src), m_sFieldName(field), m_sDataType(wxT("float")), m_sDefaultValueStr(wxT("")), m_fSerialize(true) {;} + /*! \brief Constructor for FLOAT property with defined default value. */ + xsProperty(float* src, const wxString& field, float def) : m_pSourceVariable((void*)src), m_sFieldName(field), m_sDataType(wxT("float")), m_sDefaultValueStr(xsFloatPropIO::ToString(def)), m_fSerialize(true) {;} + + /*! \brief Constructor for wxString property. */ + xsProperty(wxString* src, const wxString& field) : m_pSourceVariable((void*)src), m_sFieldName(field), m_sDataType(wxT("string")), m_sDefaultValueStr(wxT("")), m_fSerialize(true) {;} + /*! \brief Constructor for wxString property with defined default value. */ + xsProperty(wxString* src, const wxString& field, const wxString& def) : m_pSourceVariable((void*)src), m_sFieldName(field), m_sDataType(wxT("string")), m_sDefaultValueStr(def), m_fSerialize(true) {;} + + /*! \brief Constructor for wxChar property. */ + xsProperty(wxChar* src, const wxString& field) : m_pSourceVariable((void*)src), m_sFieldName(field), m_sDataType(wxT("char")), m_sDefaultValueStr(wxT("")), m_fSerialize(true) {;} + /*! \brief Constructor for wxChar property with defined default value. */ + xsProperty(wxChar* src, const wxString& field, wxChar def) : m_pSourceVariable((void*)src), m_sFieldName(field), m_sDataType(wxT("char")), m_sDefaultValueStr(xsCharPropIO::ToString(def)), m_fSerialize(true) {;} + + /*! \brief Constructor for wxPoint property. */ + xsProperty(wxPoint* src, const wxString& field) : m_pSourceVariable((void*)src), m_sFieldName(field), m_sDataType(wxT("point")), m_sDefaultValueStr(wxT("")), m_fSerialize(true) {;} + /*! \brief Constructor for wxPoint property with defined default value. */ + xsProperty(wxPoint* src, const wxString& field, const wxPoint& def) : m_pSourceVariable((void*)src), m_sFieldName(field), m_sDataType(wxT("point")), m_sDefaultValueStr(xsPointPropIO::ToString(def)), m_fSerialize(true) {;} + + /*! \brief Constructor for wxRealPoint property. */ + xsProperty(wxRealPoint* src, const wxString& field) : m_pSourceVariable((void*)src), m_sFieldName(field), m_sDataType(wxT("realpoint")), m_sDefaultValueStr(wxT("")), m_fSerialize(true) {;} + /*! \brief Constructor for wxRealPoint property with defined default value. */ + xsProperty(wxRealPoint* src, const wxString& field, const wxRealPoint& def) : m_pSourceVariable((void*)src), m_sFieldName(field), m_sDataType(wxT("realpoint")), m_sDefaultValueStr(xsRealPointPropIO::ToString(def)), m_fSerialize(true) {;} + + /*! \brief Constructor for wxSize property. */ + xsProperty(wxSize* src, const wxString& field) : m_pSourceVariable((void*)src), m_sFieldName(field), m_sDataType(wxT("size")), m_sDefaultValueStr(wxT("")), m_fSerialize(true) {;} + /*! \brief Constructor for wxSize property with defined default value. */ + xsProperty(wxSize* src, const wxString& field, const wxSize& def) : m_pSourceVariable((void*)src), m_sFieldName(field), m_sDataType(wxT("size")), m_sDefaultValueStr(xsSizePropIO::ToString(def)), m_fSerialize(true) {;} + + /*! \brief Constructor for wxBrush property. */ + xsProperty(wxBrush* src, const wxString& field) : m_pSourceVariable((void*)src), m_sFieldName(field), m_sDataType(wxT("brush")), m_sDefaultValueStr(wxT("")), m_fSerialize(true) {;} + /*! \brief Constructor for wxBrush property with defined default value. */ + xsProperty(wxBrush* src, const wxString& field, const wxBrush& def) : m_pSourceVariable((void*)src), m_sFieldName(field), m_sDataType(wxT("brush")), m_sDefaultValueStr(xsBrushPropIO::ToString(def)), m_fSerialize(true) {;} + + /*! \brief Constructor for wxPen property. */ + xsProperty(wxPen* src, const wxString& field) : m_pSourceVariable((void*)src), m_sFieldName(field), m_sDataType(wxT("pen")), m_sDefaultValueStr(wxT("")), m_fSerialize(true) {;} + /*! \brief Constructor for wxPen property with defined default value. */ + xsProperty(wxPen* src, const wxString& field, const wxPen& def) : m_pSourceVariable((void*)src), m_sFieldName(field), m_sDataType(wxT("pen")), m_sDefaultValueStr(xsPenPropIO::ToString(def)), m_fSerialize(true) {;} + + /*! \brief Constructor for wxFont property. */ + xsProperty(wxFont* src, const wxString& field) : m_pSourceVariable((void*)src), m_sFieldName(field), m_sDataType(wxT("font")), m_sDefaultValueStr(wxT("")), m_fSerialize(true) {;} + /*! \brief Constructor for wxFont property with defined default value. */ + xsProperty(wxFont* src, const wxString& field, const wxFont& def) : m_pSourceVariable((void*)src), m_sFieldName(field), m_sDataType(wxT("font")), m_sDefaultValueStr(xsFontPropIO::ToString(def)), m_fSerialize(true) {;} + + /*! \brief Constructor for wxColour property. */ + xsProperty(wxColour* src, const wxString& field) : m_pSourceVariable((void*)src), m_sFieldName(field), m_sDataType(wxT("colour")), m_sDefaultValueStr(wxT("")), m_fSerialize(true) {;} + /*! \brief Constructor for wxColour property with defined default value. */ + xsProperty(wxColour* src, const wxString& field, const wxColour& def) : m_pSourceVariable((void*)src), m_sFieldName(field), m_sDataType(wxT("colour")), m_sDefaultValueStr(xsColourPropIO::ToString(def)), m_fSerialize(true) {;} + + /*! \brief Constructor for wxArrayString property. */ + xsProperty(wxArrayString* src, const wxString& field) : m_pSourceVariable((void*)src), m_sFieldName(field), m_sDataType(wxT("arraystring")), m_sDefaultValueStr(wxT("")), m_fSerialize(true) {;} + + /*! \brief Constructor for CharArray property. */ + xsProperty(CharArray* src, const wxString& field) : m_pSourceVariable((void*)src), m_sFieldName(field), m_sDataType(wxT("arraychar")), m_sDefaultValueStr(wxT("")), m_fSerialize(true) {;} + + /*! \brief Constructor for IntArray property. */ + xsProperty(IntArray* src, const wxString& field) : m_pSourceVariable((void*)src), m_sFieldName(field), m_sDataType(wxT("arrayint")), m_sDefaultValueStr(wxT("")), m_fSerialize(true) {;} + + /*! \brief Constructor for LongArray property. */ + xsProperty(LongArray* src, const wxString& field) : m_pSourceVariable((void*)src), m_sFieldName(field), m_sDataType(wxT("arraylong")), m_sDefaultValueStr(wxT("")), m_fSerialize(true) {;} + + /*! \brief Constructor for DoubleArray property. */ + xsProperty(DoubleArray* src, const wxString& field) : m_pSourceVariable((void*)src), m_sFieldName(field), m_sDataType(wxT("arraydoubles")), m_sDefaultValueStr(wxT("")), m_fSerialize(true) {;} + + /*! \brief Constructor for RealPointArray property. */ + xsProperty(RealPointArray* src, const wxString& field) : m_pSourceVariable((void*)src), m_sFieldName(field), m_sDataType(wxT("arrayrealpoint")), m_sDefaultValueStr(wxT("")), m_fSerialize(true) {;} + + /*! \brief Constructor for RealPointList property. */ + xsProperty(RealPointList* src, const wxString& field) : m_pSourceVariable((void*)src), m_sFieldName(field), m_sDataType(wxT("listrealpoint")), m_sDefaultValueStr(wxT("")), m_fSerialize(true) {;} + + /*! \brief Constructor for StringMap property. */ + xsProperty(StringMap* src, const wxString& field) : m_pSourceVariable((void*)src), m_sFieldName(field), m_sDataType(wxT("mapstring")), m_sDefaultValueStr(wxT("")), m_fSerialize(true) {;} + + /*! \brief Constructor for static serializable property. */ + xsProperty(xsSerializable* src, const wxString& field) : m_pSourceVariable((void*)src), m_sFieldName(field), m_sDataType(wxT("serializablestatic")), m_sDefaultValueStr(wxT("")), m_fSerialize(true) {;} + + /*! \brief Constructor for dynamic serializable property. */ + xsProperty(xsSerializable** src, const wxString& field) : m_pSourceVariable((void**)src), m_sFieldName(field), m_sDataType(wxT("serializabledynamic")), m_sDefaultValueStr(wxT("")), m_fSerialize(true) {;} + + /*! \brief Copy constructor. */ + xsProperty(const xsProperty& obj) : wxObject( obj ), m_pSourceVariable(obj.m_pSourceVariable), m_sFieldName(obj.m_sFieldName), m_sDataType(obj.m_sDataType), m_sDefaultValueStr(obj.m_sDefaultValueStr), m_fSerialize(obj.m_fSerialize) {;} + + ~xsProperty(){;} + + // public functions + /** + * \brief Get textual representation of the property's value. + * \return Textual representation of current value + */ + wxString ToString() + { + xsPropertyIO *pIO = wxXmlSerializer::m_mapPropertyIOHandlers[m_sDataType]; + if(pIO) return pIO->GetValueStr(this); + else + return wxEmptyString; + } + + /** + * \brief Set value defined by its textual representation. + * \param val Textual representation of given value + */ + void FromString(const wxString& val) + { + xsPropertyIO *pIO = wxXmlSerializer::m_mapPropertyIOHandlers[m_sDataType]; + if(pIO) return pIO->SetValueStr(this, val); + } + + /** + * \brief Get reference to managed data member as BOOL. + * \return Reference to managed data member + */ + inline bool& AsBool() { wxASSERT(m_sDataType == wxT("bool")); return *(bool*)m_pSourceVariable; } + /** + * \brief Get reference to managed data member as INT. + * \return Reference to managed data member + */ + inline int& AsInt() { wxASSERT(m_sDataType == wxT("int")); return *(int*)m_pSourceVariable; } + /** + * \brief Get reference to managed data member as LONG. + * \return Reference to managed data member + */ + inline long& AsLong() { wxASSERT(m_sDataType == wxT("long")); return *(long*)m_pSourceVariable; } + /** + * \brief Get reference to managed data member as FLOAT. + * \return Reference to managed data member + */ + inline float& AsFloat() { wxASSERT(m_sDataType == wxT("float")); return *(float*)m_pSourceVariable; } + /** + * \brief Get reference to managed data member as DOUBLE. + * \return Reference to managed data member + */ + inline double& AsDouble() { wxASSERT(m_sDataType == wxT("double")); return *(double*)m_pSourceVariable; } + /** + * \brief Get reference to managed data member as wxChar. + * \return Reference to managed data member + */ + inline wxChar& AsChar() { wxASSERT(m_sDataType == wxT("char")); return *(wxChar*)m_pSourceVariable; } + /** + * \brief Get reference to managed data member as wxString. + * \return Reference to managed data member + */ + inline wxString& AsString() { wxASSERT(m_sDataType == wxT("string")); return *(wxString*)m_pSourceVariable; } + /** + * \brief Get reference to managed data member as wxSize. + * \return Reference to managed data member + */ + inline wxSize& AsSize() { wxASSERT(m_sDataType == wxT("size")); return *(wxSize*)m_pSourceVariable; } + /** + * \brief Get reference to managed data member as wxPoint. + * \return Reference to managed data member + */ + inline wxPoint& AsPoint() { wxASSERT(m_sDataType == wxT("point")); return *(wxPoint*)m_pSourceVariable; } + /** + * \brief Get reference to managed data member as wxRealPoint. + * \return Reference to managed data member + */ + inline wxRealPoint& AsRealPoint() { wxASSERT(m_sDataType == wxT("realpoint")); return *(wxRealPoint*)m_pSourceVariable; } + /** + * \brief Get reference to managed data member as wxBrush. + * \return Reference to managed data member + */ + inline wxBrush& AsBrush() { wxASSERT(m_sDataType == wxT("brush")); return *(wxBrush*)m_pSourceVariable; } + /** + * \brief Get reference to managed data member as wxPen. + * \return Reference to managed data member + */ + inline wxPen& AsPen() { wxASSERT(m_sDataType == wxT("pen")); return *(wxPen*)m_pSourceVariable; } + /** + * \brief Get reference to managed data member as wxFont. + * \return Reference to managed data member + */ + inline wxFont& AsFont() { wxASSERT(m_sDataType == wxT("font")); return *(wxFont*)m_pSourceVariable; } + /** + * \brief Get reference to managed data member as wxColour. + * \return Reference to managed data member + */ + inline wxColour& AsColour() { wxASSERT(m_sDataType == wxT("colour")); return *(wxColour*)m_pSourceVariable; } + + /** + * \brief Get reference to managed data member as wxArrayString. + * \return Reference to managed data member + */ + inline wxArrayString& AsStringArray() { wxASSERT(m_sDataType == wxT("arraystring")); return *(wxArrayString*)m_pSourceVariable; } + /** + * \brief Get reference to managed data member as CharArray. + * \return Reference to managed data member + */ + inline CharArray& AsCharArray() { wxASSERT(m_sDataType == wxT("arraychar")); return *(CharArray*)m_pSourceVariable; } + /** + * \brief Get reference to managed data member as IntArray. + * \return Reference to managed data member + */ + inline IntArray& AsIntArray() { wxASSERT(m_sDataType == wxT("arrayint")); return *(IntArray*)m_pSourceVariable; } + /** + * \brief Get reference to managed data member as LongArray. + * \return Reference to managed data member + */ + inline LongArray& AsLongArray() { wxASSERT(m_sDataType == wxT("arraylong")); return *(LongArray*)m_pSourceVariable; } + /** + * \brief Get reference to managed data member as DoubleArray. + * \return Reference to managed data member + */ + inline DoubleArray& AsDoubleArray() { wxASSERT(m_sDataType == wxT("arraydouble")); return *(DoubleArray*)m_pSourceVariable; } + /** + * \brief Get reference to managed data member as RealPointArray. + * \return Reference to managed data member + */ + inline RealPointArray& AsRealPointArray() { wxASSERT(m_sDataType == wxT("arrayrealpoint")); return *(RealPointArray*)m_pSourceVariable; } + /** + * \brief Get reference to managed data member as RealPointList. + * \return Reference to managed data member + */ + inline RealPointList& AsRealPointList() { wxASSERT(m_sDataType == wxT("listrealpoint")); return *(RealPointList*)m_pSourceVariable; } + /** + * \brief Get reference to managed data member as StringMap. + * \return Reference to managed data member + */ + inline StringMap& AsStringMap() { wxASSERT(m_sDataType == wxT("mapstring")); return *(StringMap*)m_pSourceVariable; } + + /** + * \brief Get reference to managed data member as serializable static object. + * \return Reference to managed data member + */ + inline xsSerializable& AsSerializableStatic() { wxASSERT(m_sDataType == wxT("serializablestatic")); return *(xsSerializable*)m_pSourceVariable; } + /** + * \brief Get reference to managed data member as serializable dynamic object. + * \return Reference to managed data member + */ + inline xsSerializable& AsSerializableDynamic() { wxASSERT(m_sDataType == wxT("serializabledynamic")); return **(xsSerializable**)m_pSourceVariable; } + + // public data members + /*! \brief General (void) pointer to serialized object encapsulated by the property */ + void* m_pSourceVariable; + /*! \brief Field (property) name */ + wxString m_sFieldName; + /*! \brief Data type */ + wxString m_sDataType; + /*! \brief String representation of property's default value */ + wxString m_sDefaultValueStr; + /*! \brief Flag used for enabling/disabling of property serialization */ + bool m_fSerialize; +}; + +#endif //_XSXMLSERIALIZE_H diff --git a/ThirdParty/wxXS/src/PropertyIO.cpp b/ThirdParty/wxXS/src/PropertyIO.cpp new file mode 100644 index 0000000..96c8c85 --- /dev/null +++ b/ThirdParty/wxXS/src/PropertyIO.cpp @@ -0,0 +1,1209 @@ +/*************************************************************** + * Name: PropertyIO.cpp + * Purpose: Implements data types I/O and conversion functions + * Author: Michal Bližňák (michal.bliznak@tiscali.cz) + * Created: 2007-10-28 + * Copyright: Michal Bliňák + * License: wxWidgets license (www.wxwidgets.org) + * Notes: + **************************************************************/ + +#include "wx_pch.h" + +#ifdef _DEBUG_MSVC +#define new DEBUG_NEW +#endif + +#include "wx/wxxmlserializer/PropertyIO.h" +#include "wx/wxxmlserializer/XmlSerializer.h" + +#include +#include +#include + +using namespace std; + +WX_DEFINE_EXPORTED_OBJARRAY(RealPointArray); +WX_DEFINE_EXPORTED_LIST(RealPointList); + +///////////////////////////////////////////////////////////////////////////////////// +// xsPropertyIO class /////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////// + +IMPLEMENT_DYNAMIC_CLASS(xsPropertyIO, wxObject); + +wxXmlNode* xsPropertyIO::AddPropertyNode(wxXmlNode* parent, const wxString& name, const wxString& value, wxXmlNodeType type) +{ + if(parent) + { + wxXmlNode* child = new wxXmlNode(wxXML_ELEMENT_NODE, name); + child->AddChild(new wxXmlNode(type, wxT(""), value)); + parent->AddChild(child); + return child; + } + return NULL; +} + +void xsPropertyIO::AppendPropertyType(xsProperty *source, wxXmlNode *target) +{ + target->AddAttribute(wxT("name"), source->m_sFieldName); + target->AddAttribute(wxT("type"), source->m_sDataType); +} + +///////////////////////////////////////////////////////////////////////////////////// +// xsStringPropIO class ///////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////// + +XS_DEFINE_IO_HANDLER(wxString, xsStringPropIO); + +wxString xsStringPropIO::ToString(const wxString& value) +{ + return value; +} + +wxString xsStringPropIO::FromString(const wxString& value) +{ + return value; +} + +///////////////////////////////////////////////////////////////////////////////////// +// xsCharPropIO class /////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////// + +XS_DEFINE_IO_HANDLER(wxChar, xsCharPropIO); + +wxString xsCharPropIO::ToString(const wxChar& value) +{ + return wxString::Format(wxT("%c"), value); +} + +wxChar xsCharPropIO::FromString(const wxString& value) +{ + return value.GetChar(0); +} + + +///////////////////////////////////////////////////////////////////////////////////// +// xsLongPropIO class /////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////// + +XS_DEFINE_IO_HANDLER(long, xsLongPropIO); + +wxString xsLongPropIO::ToString(const long& value) +{ + return wxString::Format(wxT("%ld"), value); +} + +long xsLongPropIO::FromString(const wxString& value) +{ + long num = 0; + if(!value.IsEmpty()) + { + value.ToLong(&num); + } + return num; +} + +///////////////////////////////////////////////////////////////////////////////////// +// xsIntPropIO class //////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////// + +XS_DEFINE_IO_HANDLER(int, xsIntPropIO); + +wxString xsIntPropIO::ToString(const int& value) +{ + return wxString::Format(wxT("%d"), value); +} + +int xsIntPropIO::FromString(const wxString& value) +{ + long num = 0; + if(!value.IsEmpty()) + { + value.ToLong(&num); + } + return (int)num; +} + +///////////////////////////////////////////////////////////////////////////////////// +// xsBoolPropIO class /////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////// + +XS_DEFINE_IO_HANDLER(bool, xsBoolPropIO); + +wxString xsBoolPropIO::ToString(const bool& value) +{ + return wxString::Format(wxT("%d"), value); +} + +bool xsBoolPropIO::FromString(const wxString& value) +{ + long num = 0; + if(!value.IsEmpty()) + { + value.ToLong(&num); + } + return (num == 1); +} + +///////////////////////////////////////////////////////////////////////////////////// +// xsDoublePropIO class ///////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////// + +XS_DEFINE_IO_HANDLER(double, xsDoublePropIO); + +wxString xsDoublePropIO::ToString(const double& value) +{ + wxString sVal; + + if( wxIsNaN(value) ) + { + sVal = wxT("NAN"); + } + else if( wxFinite(value) == 0 ) + { + sVal = wxT("INF"); + } + else + { + // use '.' decimal point character + sVal= wxString::Format(wxT("%lf"), value); + sVal.Replace(wxLocale::GetInfo(wxLOCALE_DECIMAL_POINT, wxLOCALE_CAT_NUMBER), wxT(".")); + } + + return sVal; +} + +double xsDoublePropIO::FromString(const wxString& value) +{ + double num = 0; + + if(!value.IsEmpty()) + { + if( value == wxT("NAN") ) + { + num = numeric_limits::quiet_NaN(); + } + else if( value == wxT("INF") ) + { + num = numeric_limits::infinity(); + } + else + { + // decimal point character used in wxXS is strictly '.'... + value.ToCDouble(&num); + } + } + + return num; +} + +///////////////////////////////////////////////////////////////////////////////////// +// xsFloatPropIO class ////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////// + +XS_DEFINE_IO_HANDLER(float, xsFloatPropIO); + +wxString xsFloatPropIO::ToString(const float& value) +{ + wxString sVal; + if( wxIsNaN(value) ) + { + sVal = wxT("NAN"); + } + else if( wxFinite(value) == 0 ) + { + sVal = wxT("INF"); + } + else + { + sVal = wxString::Format(wxT("%f"), value); + sVal.Replace(wxLocale::GetInfo(wxLOCALE_DECIMAL_POINT, wxLOCALE_CAT_NUMBER), wxT(".")); + } + + return sVal; +} + +float xsFloatPropIO::FromString(const wxString& value) +{ + double num = 0; + + if(!value.IsEmpty()) + { + if( value == wxT("NAN") ) + { + num = numeric_limits::quiet_NaN(); + } + else if( value == wxT("INF") ) + { + num = numeric_limits::infinity(); + } + else + { + // decimal point character used in wxXS is strictly '.'... + value.ToCDouble(&num); + } + } + + return (float)num; +} + +///////////////////////////////////////////////////////////////////////////////////// +// xsPointPropIO class ////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////// + +XS_DEFINE_IO_HANDLER(wxPoint, xsPointPropIO); + +wxString xsPointPropIO::ToString(const wxPoint& value) +{ + return wxString::Format(wxT("%d,%d"), value.x, value.y); +} + +wxPoint xsPointPropIO::FromString(const wxString& value) +{ + wxPoint pt; + + //long x, y; + + if(!value.IsEmpty()) + { + wxSscanf( value, wxT("%d,%d"), &pt.x, &pt.y ); + +// wxStringTokenizer tokens(value, wxT(","), wxTOKEN_STRTOK); +// +// tokens.GetNextToken().ToLong(&x); +// tokens.GetNextToken().ToLong(&y); +// pt.x = x; +// pt.y = y; + } + + return pt; +} + +///////////////////////////////////////////////////////////////////////////////////// +// xsSizePropIO class /////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////// + +XS_DEFINE_IO_HANDLER(wxSize, xsSizePropIO); + +wxString xsSizePropIO::ToString(const wxSize& value) +{ + return wxString::Format(wxT("%d,%d"), value.x, value.y); +} + +wxSize xsSizePropIO::FromString(const wxString& value) +{ + wxPoint pt = xsPointPropIO::FromString(value); + return wxSize(pt.x, pt.y); +} + +///////////////////////////////////////////////////////////////////////////////////// +// xsRealPointPropIO class ////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////// + +XS_DEFINE_IO_HANDLER(wxRealPoint, xsRealPointPropIO); + +wxString xsRealPointPropIO::ToString(const wxRealPoint& value) +{ + return wxString::Format(wxT("%s,%s"), xsDoublePropIO::ToString(value.x).c_str(), xsDoublePropIO::ToString(value.y).c_str()); +} + +wxRealPoint xsRealPointPropIO::FromString(const wxString& value) +{ + wxRealPoint pt; + + if(!value.IsEmpty()) + { + wxStringTokenizer tokens(value, wxT(","), wxTOKEN_STRTOK); + + pt.x = xsDoublePropIO::FromString(tokens.GetNextToken()); + pt.y = xsDoublePropIO::FromString(tokens.GetNextToken()); + } + + return pt; +} + +///////////////////////////////////////////////////////////////////////////////////// +// xsColourPropIO class ///////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////// + +XS_DEFINE_IO_HANDLER(wxColour, xsColourPropIO); + +wxString xsColourPropIO::ToString(const wxColour& value) +{ + return wxString::Format(wxT("%d,%d,%d,%d"), value.Red(), value.Green(), value.Blue(), value.Alpha()); +} + +wxColour xsColourPropIO::FromString(const wxString& value) +{ + int nRed = 0; + int nGreen = 0; + int nBlue = 0; + int nAlpha = 0; + + if(!value.IsEmpty()) + { + if( wxSscanf( value, wxT("%d,%d,%d,%d"), &nRed, &nGreen, &nBlue, &nAlpha ) == 3 ) nAlpha = 255; + } + + return wxColour(nRed, nGreen, nBlue, nAlpha); +} + +///////////////////////////////////////////////////////////////////////////////////// +// xsPenPropIO class //////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////// + +XS_DEFINE_IO_HANDLER(wxPen, xsPenPropIO); + +wxString xsPenPropIO::ToString(const wxPen& value) +{ + return wxString::Format(wxT("%s %d %d"), xsColourPropIO::ToString(value.GetColour()).c_str(), value.GetWidth(), value.GetStyle()); +} + +wxPen xsPenPropIO::FromString(const wxString& value) +{ + wxPen pen; + + wxStringTokenizer tokens(value, wxT(" "), wxTOKEN_STRTOK); + pen.SetColour(xsColourPropIO::FromString(tokens.GetNextToken())); + pen.SetWidth(xsLongPropIO::FromString(tokens.GetNextToken())); + pen.SetStyle(xsLongPropIO::FromString(tokens.GetNextToken())); + + return pen; +} + +///////////////////////////////////////////////////////////////////////////////////// +// xsBrushPropIO class ////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////// + +XS_DEFINE_IO_HANDLER(wxBrush, xsBrushPropIO); + +wxString xsBrushPropIO::ToString(const wxBrush& value) +{ + return wxString::Format(wxT("%s %d"), xsColourPropIO::ToString(value.GetColour()).c_str(), value.GetStyle()); +} + +wxBrush xsBrushPropIO::FromString(const wxString& value) +{ + wxBrush brush; + + wxStringTokenizer tokens(value, wxT(" "), wxTOKEN_STRTOK); + brush.SetColour(xsColourPropIO::FromString(tokens.GetNextToken())); + brush.SetStyle(xsLongPropIO::FromString(tokens.GetNextToken())); + + return brush; +} + +///////////////////////////////////////////////////////////////////////////////////// +// xsFontPropIO class /////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////// + +XS_DEFINE_IO_HANDLER(wxFont, xsFontPropIO); + +wxString xsFontPropIO::ToString(const wxFont& value) +{ + return value.GetNativeFontInfoUserDesc(); +} + +wxFont xsFontPropIO::FromString(const wxString& value) +{ + wxFont font; + + if( !font.SetNativeFontInfoUserDesc(value) ) + { + return *wxSWISS_FONT; + } + else + return font; +} + +///////////////////////////////////////////////////////////////////////////////////// +// xsArrayStringPropIO class //////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////// + +IMPLEMENT_DYNAMIC_CLASS(xsArrayStringPropIO, xsPropertyIO); + +void xsArrayStringPropIO::Read(xsProperty *property, wxXmlNode *source) +{ + ((wxArrayString*)property->m_pSourceVariable)->Clear(); + + wxXmlNode *listNode = source->GetChildren(); + while(listNode) + { + if(listNode->GetName() == wxT("item")) + { + ((wxArrayString*)property->m_pSourceVariable)->Add(listNode->GetNodeContent()); + } + + listNode = listNode->GetNext(); + } +} + +void xsArrayStringPropIO::Write(xsProperty *property, wxXmlNode *target) +{ + wxArrayString& array = *((wxArrayString*)property->m_pSourceVariable); + + size_t cnt = array.GetCount(); + if(cnt > 0) + { + wxXmlNode *newNode = new wxXmlNode(wxXML_ELEMENT_NODE, wxT("property")); + for(size_t i = 0; i < cnt; i++) + { + AddPropertyNode(newNode, wxT("item"), array[i]); + } + + target->AddChild(newNode); + AppendPropertyType(property, newNode); + } +} + +wxString xsArrayStringPropIO::GetValueStr(xsProperty *property) +{ + return ToString(*((wxArrayString*)property->m_pSourceVariable)); +} + +void xsArrayStringPropIO::SetValueStr(xsProperty *property, const wxString& valstr) +{ + *((wxArrayString*)property->m_pSourceVariable) = FromString(valstr); +} + +wxString xsArrayStringPropIO::ToString(const wxArrayString& value) +{ + wxString out; + + for( size_t i = 0; i < value.GetCount(); i++) + { + out << value[i]; + if( i < value.GetCount()-1 ) out << wxT("|"); + } + + return out; +} + +wxArrayString xsArrayStringPropIO::FromString(const wxString& value) +{ + wxArrayString arrData; + + wxStringTokenizer tokens( value, wxT("|") ); + while( tokens.HasMoreTokens() ) + { + arrData.Add( tokens.GetNextToken() ); + } + + return arrData; +} + +///////////////////////////////////////////////////////////////////////////////////// +// xsArrayIntPropIO class //////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////// + +IMPLEMENT_DYNAMIC_CLASS(xsArrayIntPropIO, xsPropertyIO); + +void xsArrayIntPropIO::Read(xsProperty *property, wxXmlNode *source) +{ + ((IntArray*)property->m_pSourceVariable)->Clear(); + + wxXmlNode *listNode = source->GetChildren(); + while(listNode) + { + if(listNode->GetName() == wxT("item")) + { + ((IntArray*)property->m_pSourceVariable)->Add(xsIntPropIO::FromString(listNode->GetNodeContent())); + } + + listNode = listNode->GetNext(); + } +} + +void xsArrayIntPropIO::Write(xsProperty *property, wxXmlNode *target) +{ + IntArray& array = *((IntArray*)property->m_pSourceVariable); + + size_t cnt = array.GetCount(); + if(cnt > 0) + { + wxXmlNode *newNode = new wxXmlNode(wxXML_ELEMENT_NODE, wxT("property")); + for(size_t i = 0; i < cnt; i++) + { + AddPropertyNode(newNode, wxT("item"), xsIntPropIO::ToString(array[i])); + } + + target->AddChild(newNode); + AppendPropertyType(property, newNode); + } +} + +wxString xsArrayIntPropIO::GetValueStr(xsProperty *property) +{ + return ToString(*((IntArray*)property->m_pSourceVariable)); +} + +void xsArrayIntPropIO::SetValueStr(xsProperty *property, const wxString& valstr) +{ + *((IntArray*)property->m_pSourceVariable) = FromString(valstr); +} + +wxString xsArrayIntPropIO::ToString(const IntArray& value) +{ + wxString out; + + for( size_t i = 0; i < value.GetCount(); i++) + { + out << xsIntPropIO::ToString(value[i]); + if( i < value.GetCount()-1 ) out << wxT("|"); + } + + return out; +} + +IntArray xsArrayIntPropIO::FromString(const wxString& value) +{ + IntArray arrData; + + wxStringTokenizer tokens( value, wxT("|") ); + while( tokens.HasMoreTokens() ) + { + arrData.Add( xsIntPropIO::FromString( tokens.GetNextToken() ) ); + } + + return arrData; +} + +///////////////////////////////////////////////////////////////////////////////////// +// xsArrayLongPropIO class //////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////// + +IMPLEMENT_DYNAMIC_CLASS(xsArrayLongPropIO, xsPropertyIO); + +void xsArrayLongPropIO::Read(xsProperty *property, wxXmlNode *source) +{ + ((LongArray*)property->m_pSourceVariable)->Clear(); + + wxXmlNode *listNode = source->GetChildren(); + while(listNode) + { + if(listNode->GetName() == wxT("item")) + { + ((LongArray*)property->m_pSourceVariable)->Add(xsLongPropIO::FromString(listNode->GetNodeContent())); + } + + listNode = listNode->GetNext(); + } +} + +void xsArrayLongPropIO::Write(xsProperty *property, wxXmlNode *target) +{ + LongArray& array = *((LongArray*)property->m_pSourceVariable); + + size_t cnt = array.GetCount(); + if(cnt > 0) + { + wxXmlNode *newNode = new wxXmlNode(wxXML_ELEMENT_NODE, wxT("property")); + for(size_t i = 0; i < cnt; i++) + { + AddPropertyNode(newNode, wxT("item"), xsLongPropIO::ToString(array[i])); + } + + target->AddChild(newNode); + AppendPropertyType(property, newNode); + } +} + +wxString xsArrayLongPropIO::GetValueStr(xsProperty *property) +{ + return ToString(*((LongArray*)property->m_pSourceVariable)); +} + +void xsArrayLongPropIO::SetValueStr(xsProperty *property, const wxString& valstr) +{ + *((LongArray*)property->m_pSourceVariable) = FromString(valstr); +} + +wxString xsArrayLongPropIO::ToString(const LongArray& value) +{ + wxString out; + + for( size_t i = 0; i < value.GetCount(); i++) + { + out << xsLongPropIO::ToString(value[i]); + if( i < value.GetCount()-1 ) out << wxT("|"); + } + + return out; +} + +LongArray xsArrayLongPropIO::FromString(const wxString& value) +{ + LongArray arrData; + + wxStringTokenizer tokens( value, wxT("|") ); + while( tokens.HasMoreTokens() ) + { + arrData.Add( xsLongPropIO::FromString( tokens.GetNextToken() ) ); + } + + return arrData; +} + +///////////////////////////////////////////////////////////////////////////////////// +// xsArrayDoublePropIO class //////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////// + +IMPLEMENT_DYNAMIC_CLASS(xsArrayDoublePropIO, xsPropertyIO); + +void xsArrayDoublePropIO::Read(xsProperty *property, wxXmlNode *source) +{ + ((DoubleArray*)property->m_pSourceVariable)->Clear(); + + wxXmlNode *listNode = source->GetChildren(); + while(listNode) + { + if(listNode->GetName() == wxT("item")) + { + ((DoubleArray*)property->m_pSourceVariable)->Add(xsDoublePropIO::FromString(listNode->GetNodeContent())); + } + + listNode = listNode->GetNext(); + } +} + +void xsArrayDoublePropIO::Write(xsProperty *property, wxXmlNode *target) +{ + DoubleArray& array = *((DoubleArray*)property->m_pSourceVariable); + + size_t cnt = array.GetCount(); + if(cnt > 0) + { + wxXmlNode *newNode = new wxXmlNode(wxXML_ELEMENT_NODE, wxT("property")); + for(size_t i = 0; i < cnt; i++) + { + AddPropertyNode(newNode, wxT("item"), xsDoublePropIO::ToString(array[i])); + } + + target->AddChild(newNode); + AppendPropertyType(property, newNode); + } +} + +wxString xsArrayDoublePropIO::GetValueStr(xsProperty *property) +{ + return ToString(*((DoubleArray*)property->m_pSourceVariable)); +} + +void xsArrayDoublePropIO::SetValueStr(xsProperty *property, const wxString& valstr) +{ + *((DoubleArray*)property->m_pSourceVariable) = FromString(valstr); +} + +wxString xsArrayDoublePropIO::ToString(const DoubleArray& value) +{ + wxString out; + + for( size_t i = 0; i < value.GetCount(); i++) + { + out << xsDoublePropIO::ToString(value[i]); + if( i < value.GetCount()-1 ) out << wxT("|"); + } + + return out; +} + +DoubleArray xsArrayDoublePropIO::FromString(const wxString& value) +{ + DoubleArray arrData; + + wxStringTokenizer tokens( value, wxT("|") ); + while( tokens.HasMoreTokens() ) + { + arrData.Add( xsDoublePropIO::FromString( tokens.GetNextToken() ) ); + } + + return arrData; +} + +///////////////////////////////////////////////////////////////////////////////////// +// xsArrayCharPropIO class //////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////// + +IMPLEMENT_DYNAMIC_CLASS(xsArrayCharPropIO, xsPropertyIO); + +void xsArrayCharPropIO::Read(xsProperty *property, wxXmlNode *source) +{ + ((CharArray*)property->m_pSourceVariable)->Clear(); + + wxXmlNode *listNode = source->GetChildren(); + while(listNode) + { + if(listNode->GetName() == wxT("item")) + { + ((CharArray*)property->m_pSourceVariable)->Add(xsCharPropIO::FromString(listNode->GetNodeContent())); + } + + listNode = listNode->GetNext(); + } +} + +void xsArrayCharPropIO::Write(xsProperty *property, wxXmlNode *target) +{ + CharArray& array = *((CharArray*)property->m_pSourceVariable); + + size_t cnt = array.GetCount(); + if(cnt > 0) + { + wxXmlNode *newNode = new wxXmlNode(wxXML_ELEMENT_NODE, wxT("property")); + for(size_t i = 0; i < cnt; i++) + { + AddPropertyNode(newNode, wxT("item"), xsCharPropIO::ToString(array[i])); + } + + target->AddChild(newNode); + AppendPropertyType(property, newNode); + } +} + +wxString xsArrayCharPropIO::GetValueStr(xsProperty *property) +{ + return ToString(*((CharArray*)property->m_pSourceVariable)); +} + +void xsArrayCharPropIO::SetValueStr(xsProperty *property, const wxString& valstr) +{ + *((CharArray*)property->m_pSourceVariable) = FromString(valstr); +} + +wxString xsArrayCharPropIO::ToString(const CharArray& value) +{ + wxString out; + + for( size_t i = 0; i < value.GetCount(); i++) + { + out << xsCharPropIO::ToString(value[i]); + if( i < value.GetCount()-1 ) out << wxT("|"); + } + + return out; +} + +CharArray xsArrayCharPropIO::FromString(const wxString& value) +{ + CharArray arrData; + + wxStringTokenizer tokens( value, wxT("|") ); + while( tokens.HasMoreTokens() ) + { + arrData.Add( xsCharPropIO::FromString( tokens.GetNextToken() ) ); + } + + return arrData; +} + + + +///////////////////////////////////////////////////////////////////////////////////// +// xsArrayRealPointPropIO class ///////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////// + +IMPLEMENT_DYNAMIC_CLASS(xsArrayRealPointPropIO, xsPropertyIO); + +void xsArrayRealPointPropIO::Read(xsProperty *property, wxXmlNode *source) +{ + ((RealPointArray*)property->m_pSourceVariable)->Clear(); + + wxXmlNode *listNode = source->GetChildren(); + while(listNode) + { + if(listNode->GetName() == wxT("item")) + { + ((RealPointArray*)property->m_pSourceVariable)->Add(xsRealPointPropIO::FromString(listNode->GetNodeContent())); + } + + listNode = listNode->GetNext(); + } +} + +void xsArrayRealPointPropIO::Write(xsProperty *property, wxXmlNode *target) +{ + RealPointArray& array = *((RealPointArray*)property->m_pSourceVariable); + + size_t cnt = array.GetCount(); + if(cnt > 0) + { + wxXmlNode *newNode = new wxXmlNode(wxXML_ELEMENT_NODE, wxT("property")); + for(size_t i = 0; i < cnt; i++) + { + AddPropertyNode(newNode, wxT("item"), xsRealPointPropIO::ToString(array[i])); + } + + target->AddChild(newNode); + AppendPropertyType(property, newNode); + } +} + +wxString xsArrayRealPointPropIO::GetValueStr(xsProperty *property) +{ + return ToString(*((RealPointArray*)property->m_pSourceVariable)); +} + +void xsArrayRealPointPropIO::SetValueStr(xsProperty *property, const wxString& valstr) +{ + *((RealPointArray*)property->m_pSourceVariable) = FromString(valstr); +} + +wxString xsArrayRealPointPropIO::ToString(const RealPointArray& value) +{ + wxString out; + + for( size_t i = 0; i < value.GetCount(); i++) + { + out << xsRealPointPropIO::ToString(value[i]); + if( i < value.GetCount()-1 ) out << wxT("|"); + } + + return out; +} + +RealPointArray xsArrayRealPointPropIO::FromString(const wxString& value) +{ + RealPointArray arrData; + + wxStringTokenizer tokens( value, wxT("|") ); + while( tokens.HasMoreTokens() ) + { + arrData.Add( xsRealPointPropIO::FromString( tokens.GetNextToken() ) ); + } + + return arrData; +} + +///////////////////////////////////////////////////////////////////////////////////// +// xsListRealPointPropIO class ////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////// + +IMPLEMENT_DYNAMIC_CLASS(xsListRealPointPropIO, xsPropertyIO); + +void xsListRealPointPropIO::Read(xsProperty *property, wxXmlNode *source) +{ + RealPointList *list = (RealPointList*)property->m_pSourceVariable; + + // clear previous list content + bool fDelState = list->GetDeleteContents(); + + list->DeleteContents(true); + list->Clear(); + list->DeleteContents(fDelState); + + wxXmlNode *listNode = source->GetChildren(); + while(listNode) + { + if(listNode->GetName() == wxT("item")) + { + list->Append(new wxRealPoint(xsRealPointPropIO::FromString(listNode->GetNodeContent()))); + } + + listNode = listNode->GetNext(); + } +} + +void xsListRealPointPropIO::Write(xsProperty *property, wxXmlNode *target) +{ + RealPointList *list = (RealPointList*)property->m_pSourceVariable; + + if( !list->IsEmpty() ) + { + wxXmlNode *newNode = new wxXmlNode(wxXML_ELEMENT_NODE, wxT("property")); + RealPointList::compatibility_iterator listNode = list->GetFirst(); + while(listNode) + { + AddPropertyNode(newNode, wxT("item"), xsRealPointPropIO::ToString(*(wxRealPoint*)listNode->GetData())); + listNode = listNode->GetNext(); + } + + target->AddChild(newNode); + AppendPropertyType(property, newNode); + } +} + +wxString xsListRealPointPropIO::GetValueStr(xsProperty *property) +{ + return ToString(*((RealPointList*)property->m_pSourceVariable)); +} + +void xsListRealPointPropIO::SetValueStr(xsProperty *property, const wxString& valstr) +{ + *((RealPointList*)property->m_pSourceVariable) = FromString(valstr); +} + +wxString xsListRealPointPropIO::ToString(const RealPointList& value) +{ + wxString out; + + RealPointList::compatibility_iterator node = value.GetFirst(); + while( node ) + { + out << xsRealPointPropIO::ToString(*(wxRealPoint*)node->GetData()); + if( node != value.GetLast() ) out << wxT("|"); + + node = node->GetNext(); + } + + return out; +} + +RealPointList xsListRealPointPropIO::FromString(const wxString& value) +{ + RealPointList lstData; + + wxStringTokenizer tokens( value, wxT("|") ); + while( tokens.HasMoreTokens() ) + { + lstData.Append( new wxRealPoint(xsRealPointPropIO::FromString( tokens.GetNextToken() )) ); + } + + return lstData; +} + +///////////////////////////////////////////////////////////////////////////////////// +// xsDynObjPropIO class ///////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////// + +IMPLEMENT_DYNAMIC_CLASS(xsDynObjPropIO, xsPropertyIO); + +void xsDynObjPropIO::Read(xsProperty *property, wxXmlNode *source) +{ + wxXmlNode *objectNode = source->GetChildren(); + + if( objectNode && (objectNode->GetName() == wxT("object")) ) + { + *(xsSerializable**)(property->m_pSourceVariable) = (xsSerializable*)wxCreateDynamicObject(objectNode->GetAttribute(wxT("type"), wxT(""))); + + xsSerializable* object = *(xsSerializable**)(property->m_pSourceVariable); + if(object) + { + object->DeserializeObject(objectNode); + } + } +} + +void xsDynObjPropIO::Write(xsProperty *property, wxXmlNode *target) +{ + xsSerializable* object = *(xsSerializable**)(property->m_pSourceVariable); + + if( object && object->IsKindOf(CLASSINFO(xsSerializable))) + { + wxXmlNode *newNode = new wxXmlNode(wxXML_ELEMENT_NODE, wxT("property")); + newNode->AddChild(object->SerializeObject(NULL)); + + target->AddChild(newNode); + AppendPropertyType(property, newNode); + } +} + +wxString xsDynObjPropIO::GetValueStr(xsProperty *property) +{ + return ToString(**(xsSerializable**)(property->m_pSourceVariable)); +} + +void xsDynObjPropIO::SetValueStr(xsProperty *property, const wxString& valstr) +{ + **((xsSerializable**)property->m_pSourceVariable) = FromString(valstr); +} + +wxString xsDynObjPropIO::ToString(const xsSerializable& value) +{ + return wxString::Format(wxT("Dynamic object at address 0x%x"), &value); +} + +xsSerializable xsDynObjPropIO::FromString(const wxString& value) +{ + wxUnusedVar( value ); + + return xsSerializable(); +} + +///////////////////////////////////////////////////////////////////////////////////// +// xsDynNCObjPropIO class /////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////// + +IMPLEMENT_DYNAMIC_CLASS(xsDynNCObjPropIO, xsPropertyIO); + +void xsDynNCObjPropIO::Read(xsProperty *property, wxXmlNode *source) +{ + wxXmlNode *objectNode = source->GetChildren(); + + if( objectNode && (objectNode->GetName() == wxT("object")) ) + { + xsSerializable* object = *(xsSerializable**)(property->m_pSourceVariable); + if(object) + { + object->DeserializeObject(objectNode); + } + } +} + +void xsDynNCObjPropIO::Write(xsProperty *property, wxXmlNode *target) +{ + xsSerializable* object = *(xsSerializable**)(property->m_pSourceVariable); + + if( object && object->IsKindOf(CLASSINFO(xsSerializable))) + { + wxXmlNode *newNode = new wxXmlNode(wxXML_ELEMENT_NODE, wxT("property")); + newNode->AddChild(object->SerializeObject(NULL)); + + target->AddChild(newNode); + AppendPropertyType(property, newNode); + } +} + +wxString xsDynNCObjPropIO::GetValueStr(xsProperty *property) +{ + return ToString(**(xsSerializable**)(property->m_pSourceVariable)); +} + +void xsDynNCObjPropIO::SetValueStr(xsProperty *property, const wxString& valstr) +{ + **((xsSerializable**)property->m_pSourceVariable) = FromString(valstr); +} + +wxString xsDynNCObjPropIO::ToString(const xsSerializable& value) +{ + return wxString::Format(wxT("Dynamic object at address 0x%x"), &value); +} + +xsSerializable xsDynNCObjPropIO::FromString(const wxString& value) +{ + wxUnusedVar( value ); + + return xsSerializable(); +} + +///////////////////////////////////////////////////////////////////////////////////// +// xsStaticObjPropIO class ////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////// + +IMPLEMENT_DYNAMIC_CLASS(xsStaticObjPropIO, xsPropertyIO); + +void xsStaticObjPropIO::Read(xsProperty *property, wxXmlNode *source) +{ + wxXmlNode *objectNode = source->GetChildren(); + + if( objectNode && (objectNode->GetName() == wxT("object")) ) + { + (*((xsSerializable*)property->m_pSourceVariable)).DeserializeObject(objectNode); + } +} + +void xsStaticObjPropIO::Write(xsProperty *property, wxXmlNode *target) +{ + wxXmlNode *newNode = new wxXmlNode(wxXML_ELEMENT_NODE, wxT("property")); + newNode->AddChild((*((xsSerializable*)property->m_pSourceVariable)).SerializeObject(NULL)); + + target->AddChild(newNode); + AppendPropertyType(property, newNode); +} + +wxString xsStaticObjPropIO::GetValueStr(xsProperty *property) +{ + return ToString(*(xsSerializable*)property->m_pSourceVariable); +} + +void xsStaticObjPropIO::SetValueStr(xsProperty *property, const wxString& valstr) +{ + *((xsSerializable*)property->m_pSourceVariable) = FromString(valstr); +} + +wxString xsStaticObjPropIO::ToString(const xsSerializable& value) +{ + return wxString::Format(wxT("Static object at address 0x%x"), &value); +} + +xsSerializable xsStaticObjPropIO::FromString(const wxString& value) +{ + wxUnusedVar( value ); + + return xsSerializable(); +} + +///////////////////////////////////////////////////////////////////////////////////// +// xsMapStringPropIO class //////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////// + +IMPLEMENT_DYNAMIC_CLASS(xsMapStringPropIO, xsPropertyIO); + +void xsMapStringPropIO::Read(xsProperty *property, wxXmlNode *source) +{ + ((StringMap*)property->m_pSourceVariable)->clear(); + + wxXmlNode *listNode = source->GetChildren(); + while(listNode) + { + if(listNode->GetName() == wxT("item")) + { + (*(StringMap*)property->m_pSourceVariable)[listNode->GetAttribute( wxT("key"), wxT("undef_key") )] = listNode->GetNodeContent(); + } + + listNode = listNode->GetNext(); + } +} + +void xsMapStringPropIO::Write(xsProperty *property, wxXmlNode *target) +{ + StringMap& map = *((StringMap*)property->m_pSourceVariable); + + if( !map.empty() ) + { + wxXmlNode *pXmlNode, *newNode = new wxXmlNode(wxXML_ELEMENT_NODE, wxT("property")); + StringMap::iterator it; + + for( it = map.begin(); it != map.end(); ++it ) + { + wxString key = it->first, value = it->second; + pXmlNode = AddPropertyNode(newNode, wxT("item"), it->second); + pXmlNode->AddAttribute(wxT("key"), it->first); + } + + target->AddChild(newNode); + AppendPropertyType(property, newNode); + } +} + +wxString xsMapStringPropIO::GetValueStr(xsProperty *property) +{ + return ToString(*((StringMap*)property->m_pSourceVariable)); +} + +void xsMapStringPropIO::SetValueStr(xsProperty *property, const wxString& valstr) +{ + *((StringMap*)property->m_pSourceVariable) = FromString(valstr); +} + +wxString xsMapStringPropIO::ToString(const StringMap& value) +{ + wxString out; + + StringMap::const_iterator it; + + for( it = value.begin(); it != value.end(); ++it ) + { + if( it != value.begin() ) out << wxT("|"); + out << it->first << wxT("->") << it->second; + } + + return out; +} + +StringMap xsMapStringPropIO::FromString(const wxString& value) +{ + StringMap mapData; + + wxString token; + wxStringTokenizer tokens( value, wxT("|") ); + while( tokens.HasMoreTokens() ) + { + token = tokens.GetNextToken(); + token.Replace(wxT("->"), wxT("|")); + mapData[token.BeforeFirst(wxT('|'))] = token.AfterFirst(wxT('|')); + } + + return mapData; +} + diff --git a/ThirdParty/wxXS/src/XmlSerializer.cpp b/ThirdParty/wxXS/src/XmlSerializer.cpp new file mode 100644 index 0000000..33264e9 --- /dev/null +++ b/ThirdParty/wxXS/src/XmlSerializer.cpp @@ -0,0 +1,974 @@ +/*************************************************************** + * Name: XmlSerializer.cpp + * Purpose: Implements XML serializer and related classes + * Author: Michal Bližňák (michal.bliznak@tiscali.cz) + * Created: 2007-08-28 + * Copyright: Michal Bližňák + * License: wxWidgets license (www.wxwidgets.org) + * Notes: + **************************************************************/ + +#include "wx_pch.h" + +#ifdef _DEBUG_MSVC +#define new DEBUG_NEW +#endif + +#include "wx/wxxmlserializer/XmlSerializer.h" + +#include +#include +#include + +WX_DEFINE_EXPORTED_LIST(PropertyList); +WX_DEFINE_EXPORTED_LIST(SerializableList); + +// static members +PropertyIOMap wxXmlSerializer::m_mapPropertyIOHandlers; +int wxXmlSerializer::m_nRefCounter = 0; +wxString wxXmlSerializer::m_sLibraryVersion = wxT("1.3.1 beta"); + +///////////////////////////////////////////////////////////////////////////////////// +// xsProperty class ///////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////// + +IMPLEMENT_DYNAMIC_CLASS(xsProperty, wxObject); + +///////////////////////////////////////////////////////////////////////////////////// +// xsSerializable class ///////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////// + +XS_IMPLEMENT_CLONABLE_CLASS(xsSerializable, wxObject); + +// constructor and destructor /////////////////////////////////////////////////////// + +xsSerializable::xsSerializable() +{ + m_pParentManager = NULL; + m_pParentItem = NULL; + m_fSerialize = true; + m_fClone = true; + m_nId = -1; + + XS_SERIALIZE(m_nId, wxT("id")); +} + +xsSerializable::xsSerializable(const xsSerializable& obj) +: wxObject(obj) +{ + m_pParentManager = NULL; + m_pParentItem = NULL; + m_fSerialize = obj.m_fSerialize; + m_fClone = obj.m_fClone; + m_nId = obj.m_nId; + + XS_SERIALIZE(m_nId, wxT("id")); + + // copy serialized children as well + SerializableList::compatibility_iterator node = obj.GetFirstChildNode(); + while( node ) + { + if( node->GetData()->IsSerialized() ) AddChild( (xsSerializable*)node->GetData()->Clone() ); + node = node->GetNext(); + } +} + +xsSerializable::~xsSerializable() +{ + if( m_pParentManager ) + { + m_pParentManager->GetUsedIDs().erase( m_nId ); + } + + m_lstProperties.DeleteContents(true); + m_lstProperties.Clear(); + + m_lstChildItems.DeleteContents(true); + m_lstChildItems.Clear(); +} + +// public functions ///////////////////////////////////////////////////////////////// + +void xsSerializable::SetId(long id) +{ + m_nId = id; + + if( m_pParentManager ) m_pParentManager->GetUsedIDs()[id] = this; +} + +xsSerializable* xsSerializable::AddChild(xsSerializable* child) +{ + wxASSERT(child); + + if( child ) + { + InitChild( child ); + + m_lstChildItems.Append(child); + } + + return child; +} + +xsSerializable* xsSerializable::InsertChild(size_t pos, xsSerializable* child) +{ + wxASSERT(child); + + if( child ) + { + InitChild( child ); + + m_lstChildItems.Insert(pos, child); + } + + return child; +} + +void xsSerializable::Reparent(xsSerializable* parent) +{ + if(m_pParentItem) + { + m_pParentItem->m_lstChildItems.DeleteObject(this); + } + + if(parent) + { + parent->AddChild(this); + } + else + m_pParentItem = NULL; +} + +xsSerializable* xsSerializable::GetFirstChild() +{ + SerializableList::compatibility_iterator node = m_lstChildItems.GetFirst(); + if( node )return node->GetData(); + else + return NULL; +} + +xsSerializable* xsSerializable::GetFirstChild(wxClassInfo *type) +{ + SerializableList::compatibility_iterator node = m_lstChildItems.GetFirst(); + while( node ) + { + if( node->GetData()->IsKindOf( type ) ) return node->GetData(); + node = node->GetNext(); + } + return NULL; +} + +xsSerializable* xsSerializable::GetLastChild() +{ + SerializableList::compatibility_iterator node = m_lstChildItems.GetLast(); + if( node )return node->GetData(); + else + return NULL; +} + +xsSerializable* xsSerializable::GetLastChild(wxClassInfo *type) +{ + SerializableList::compatibility_iterator node = m_lstChildItems.GetLast(); + while( node ) + { + if( node->GetData()->IsKindOf( type ) ) return node->GetData(); + node = node->GetPrevious(); + } + return NULL; +} + +xsSerializable* xsSerializable::GetSibbling() +{ + wxASSERT( m_pParentItem ); + + if( m_pParentItem ) + { + SerializableList::compatibility_iterator node = m_pParentItem->GetChildrenList().Find( this ); + if( node ) + { + if( node->GetNext() ) return node->GetNext()->GetData(); + } + } + + return NULL; +} + +xsSerializable* xsSerializable::GetSibbling(wxClassInfo *type) +{ + wxASSERT( m_pParentItem ); + + if( m_pParentItem ) + { + SerializableList::compatibility_iterator node = m_pParentItem->GetChildrenList().Find( this ); + while( node ) + { + node = node->GetNext(); + + if( node && (node->GetData()->IsKindOf( type ) ) ) return node->GetData(); + } + } + + return NULL; +} + + +xsSerializable* xsSerializable::GetChild(long id, bool recursive) +{ + SerializableList lstChildren; + SerializableList::compatibility_iterator node; + + if( recursive ) + { + GetChildrenRecursively( CLASSINFO(xsSerializable), lstChildren ); + node = lstChildren.GetFirst(); + } + else + node = m_lstChildItems.GetFirst(); + + while(node) + { + if( node->GetData()->GetId() == id) return node->GetData(); + node = node->GetNext(); + } + + return NULL; +} + +void xsSerializable::GetChildren(wxClassInfo *type, SerializableList& list) +{ + xsSerializable *pChild; + + SerializableList::compatibility_iterator node = m_lstChildItems.GetFirst(); + while(node) + { + pChild = node->GetData(); + + if( !type || pChild->IsKindOf(type) ) list.Append(pChild); + + node = node->GetNext(); + } +} + +void xsSerializable::GetChildrenRecursively(wxClassInfo *type, SerializableList& list, SEARCHMODE mode) +{ + xsSerializable *pChild; + + SerializableList::compatibility_iterator node = m_lstChildItems.GetFirst(); + while(node) + { + pChild = node->GetData(); + if( !type || pChild->IsKindOf(type) ) list.Append(pChild); + if( mode == searchDFS ) pChild->GetChildrenRecursively(type, list); + + node = node->GetNext(); + } + + if( mode == searchBFS ) + { + node = m_lstChildItems.GetFirst(); + while(node) + { + node->GetData()->GetChildrenRecursively(type, list); + node = node->GetNext(); + } + } +} + +void xsSerializable::AddProperty(xsProperty* property) +{ + if(property) + { + if(!GetProperty(property->m_sFieldName)) + { + m_lstProperties.Append(property); + } + } +} + +void xsSerializable::RemoveProperty(xsProperty* property) +{ + if( property ) + { + m_lstProperties.DeleteObject( property ); + delete property; + } +} + +xsProperty* xsSerializable::GetProperty(const wxString& field) +{ + PropertyList::compatibility_iterator node = m_lstProperties.GetFirst(); + while(node) + { + if(node->GetData()->m_sFieldName == field)return node->GetData(); + node = node->GetNext(); + } + return NULL; +} + +void xsSerializable::EnablePropertySerialization(const wxString& field, bool enab) +{ + xsProperty* property = GetProperty(field); + if(property) + { + property->m_fSerialize = enab; + } +} + +bool xsSerializable::IsPropertySerialized(const wxString& field) +{ + xsProperty* property = GetProperty(field); + if(property) + { + return property->m_fSerialize; + } + return false; +} + +wxXmlNode* xsSerializable::SerializeObject(wxXmlNode* node) +{ + if(!node || (node->GetName() != wxT("object"))) + { + node = new wxXmlNode(wxXML_ELEMENT_NODE, wxT("object")); + node->AddAttribute(new wxXmlAttribute(wxT("type"), this->GetClassInfo()->GetClassName())); + } + + if(node) return this->Serialize(node); + else + return NULL; +} + +void xsSerializable::DeserializeObject(wxXmlNode* node) +{ + if(node && (node->GetName()==wxT("object"))) + { + this->Deserialize(node); + } +} + +// overloaded operators ///////////////////////////////////////////////////////////// + +xsSerializable* xsSerializable::operator<<( xsSerializable *child ) +{ + if( child && (child != this ) ) + { + return this->AddChild( child ); + } + else + return this; +} + +// protected functions ////////////////////////////////////////////////////////////// + +wxXmlNode* xsSerializable::Serialize(wxXmlNode* node) +{ + xsProperty* property; + xsPropertyIO* ioHandler; + + PropertyList::compatibility_iterator propNode = m_lstProperties.GetFirst(); + while(propNode) + { + property = propNode->GetData(); + + if(property->m_fSerialize) + { + ioHandler = wxXmlSerializer::m_mapPropertyIOHandlers[property->m_sDataType]; + if(ioHandler) + { + ioHandler->Write(property, node); + } + } + + propNode = propNode->GetNext(); + } + + return node; +} + +void xsSerializable::Deserialize(wxXmlNode* node) +{ + wxASSERT(node); + if(!node)return; + + xsProperty* property; + xsPropertyIO* ioHandler; + wxString propName; + + wxXmlNode *xmlNode = node->GetChildren(); + while(xmlNode) + { + if(xmlNode->GetName() == wxT("property")) + { + xmlNode->GetAttribute(wxT("name"), &propName); + property = GetProperty(propName); + + if(property) + { + ioHandler = wxXmlSerializer::m_mapPropertyIOHandlers[property->m_sDataType]; + if(ioHandler) + { + ioHandler->Read(property, xmlNode); + } + } + } + + xmlNode = xmlNode->GetNext(); + } +} + +void xsSerializable::InitChild(xsSerializable* child) +{ + if( child ) + { + child->m_pParentItem = this; + + if( m_pParentManager ) + { + if( child->m_pParentManager != m_pParentManager ) + { + child->m_pParentManager = m_pParentManager; + + // assign unique ids to the child object + if( child->GetId() == -1 ) child->SetId(m_pParentManager->GetNewId()); + else + m_pParentManager->GetUsedIDs()[child->GetId()] = child; + + // if the child has another children, set their parent manager and ID as well + xsSerializable *pItem; + SerializableList lstChildren; + child->GetChildrenRecursively( NULL, lstChildren ); + + SerializableList::compatibility_iterator node = lstChildren.GetFirst(); + while( node ) + { + pItem = node->GetData(); + + pItem->SetParentManager( m_pParentManager ); + + if( pItem->GetId() == -1 ) pItem->SetId(m_pParentManager->GetNewId()); + else + m_pParentManager->GetUsedIDs()[pItem->GetId()] = pItem; + + node = node->GetNext(); + } + } + } + } +} + +///////////////////////////////////////////////////////////////////////////////////// +// wxXmlSerializer class //////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////// + +XS_IMPLEMENT_CLONABLE_CLASS(wxXmlSerializer, wxObject); + +// constructor and destructor /////////////////////////////////////////////////////// + +wxXmlSerializer::wxXmlSerializer() +{ + m_sOwner = wxT(""); + m_sRootName = wxT("root"); + m_sVersion = wxT(""); + m_fClone = true; + + m_pRoot = NULL; + SetRootItem(new xsSerializable()); + + if(m_nRefCounter == 0) + { + InitializeAllIOHandlers(); + } + m_nRefCounter++; +} + +wxXmlSerializer::wxXmlSerializer(const wxXmlSerializer &obj) +: wxObject(obj) +{ + m_sOwner = obj.m_sOwner; + m_sRootName = obj.m_sRootName; + m_sVersion = obj.m_sVersion; + m_fClone = obj.m_fClone; + + m_pRoot = NULL; + + SetRootItem((xsSerializable*)obj.m_pRoot->Clone()); + + m_nRefCounter++; +} + +wxXmlSerializer::wxXmlSerializer(const wxString& owner, const wxString& root, const wxString& version) +{ + m_sOwner = owner; + m_sRootName = root; + m_sVersion = version; + m_fClone = true; + + m_pRoot = NULL; + SetRootItem(new xsSerializable()); + + if(m_nRefCounter == 0) + { + InitializeAllIOHandlers(); + } + m_nRefCounter++; +} + +wxXmlSerializer::~wxXmlSerializer() +{ + if( m_pRoot ) delete m_pRoot; + + m_nRefCounter--; + if(m_nRefCounter == 0) + { + ClearIOHandlers(); + } +} + +// public functions ////////////////////////////////////////////////////////////////// + +void wxXmlSerializer::InitializeAllIOHandlers() +{ + ClearIOHandlers(); + + XS_REGISTER_IO_HANDLER(wxT("string"), xsStringPropIO); + XS_REGISTER_IO_HANDLER(wxT("char"), xsCharPropIO); + XS_REGISTER_IO_HANDLER(wxT("int"), xsIntPropIO); + XS_REGISTER_IO_HANDLER(wxT("long"), xsLongPropIO); + XS_REGISTER_IO_HANDLER(wxT("float"), xsFloatPropIO); + XS_REGISTER_IO_HANDLER(wxT("double"), xsDoublePropIO); + XS_REGISTER_IO_HANDLER(wxT("bool"), xsBoolPropIO); + XS_REGISTER_IO_HANDLER(wxT("point"), xsPointPropIO); + XS_REGISTER_IO_HANDLER(wxT("size"), xsSizePropIO); + XS_REGISTER_IO_HANDLER(wxT("realpoint"), xsRealPointPropIO); + XS_REGISTER_IO_HANDLER(wxT("colour"), xsColourPropIO); + XS_REGISTER_IO_HANDLER(wxT("brush"), xsBrushPropIO); + XS_REGISTER_IO_HANDLER(wxT("pen"), xsPenPropIO); + XS_REGISTER_IO_HANDLER(wxT("font"), xsFontPropIO); + XS_REGISTER_IO_HANDLER(wxT("arraystring"), xsArrayStringPropIO); + XS_REGISTER_IO_HANDLER(wxT("arraychar"), xsArrayCharPropIO); + XS_REGISTER_IO_HANDLER(wxT("arrayint"), xsArrayIntPropIO); + XS_REGISTER_IO_HANDLER(wxT("arraylong"), xsArrayLongPropIO); + XS_REGISTER_IO_HANDLER(wxT("arraydouble"), xsArrayDoublePropIO); + XS_REGISTER_IO_HANDLER(wxT("arrayrealpoint"), xsArrayRealPointPropIO); + XS_REGISTER_IO_HANDLER(wxT("mapstring"), xsMapStringPropIO); + XS_REGISTER_IO_HANDLER(wxT("listrealpoint"), xsListRealPointPropIO); + XS_REGISTER_IO_HANDLER(wxT("serializablestatic"), xsStaticObjPropIO); + XS_REGISTER_IO_HANDLER(wxT("serializabledynamic"), xsDynObjPropIO); + XS_REGISTER_IO_HANDLER(wxT("serializabledynamicnocreate"), xsDynNCObjPropIO); +} + +void wxXmlSerializer::ClearIOHandlers() +{ + PropertyIOMap::iterator it = m_mapPropertyIOHandlers.begin(); + while(it != m_mapPropertyIOHandlers.end()) + { + if(it->second)delete it->second; + it++; + } + m_mapPropertyIOHandlers.clear(); +} + +xsSerializable* wxXmlSerializer::GetItem(long id) +{ + if( m_pRoot ) + { + IDMap::iterator it = m_mapUsedIDs.find( id ); + if( it != m_mapUsedIDs.end() ) return it->second; + } + + return NULL; +} + +bool wxXmlSerializer::Contains(xsSerializable *object) const +{ + if( m_pRoot ) + { + return _Contains(object, m_pRoot); + } + + return false; +} + +bool wxXmlSerializer::Contains(wxClassInfo *type) +{ + SerializableList lstItems; + + GetItems( type, lstItems ); + + return !lstItems.IsEmpty(); +} + +void wxXmlSerializer::GetItems(wxClassInfo* type, SerializableList& list, xsSerializable::SEARCHMODE mode) +{ + if( m_pRoot ) + { + m_pRoot->GetChildrenRecursively(type, list, mode); + } +} + +void wxXmlSerializer::CopyItems(const wxXmlSerializer& src) +{ + // clear current content + m_pRoot->GetChildrenList().DeleteContents( true ); + m_pRoot->GetChildrenList().Clear(); + m_pRoot->GetChildrenList().DeleteContents( false ); + + m_mapUsedIDs.clear(); + + SerializableList::compatibility_iterator node = src.GetRootItem()->GetFirstChildNode(); + while( node ) + { + AddItem( m_pRoot, (xsSerializable*)node->GetData()->Clone() ); + node = node->GetNext(); + } +} + +xsSerializable* wxXmlSerializer::AddItem(long parentId, xsSerializable* item) +{ + return AddItem(GetItem(parentId), item); +} + +xsSerializable* wxXmlSerializer::AddItem(xsSerializable* parent, xsSerializable* item) +{ + wxASSERT(m_pRoot); + wxASSERT(item); + + if( item ) + { + if( parent )parent->AddChild(item); + else + m_pRoot->AddChild(item); + } + + return item; +} + +void wxXmlSerializer::RemoveItem(long id) +{ + RemoveItem(GetItem(id)); +} + +void wxXmlSerializer::RemoveItem(xsSerializable* item) +{ + wxASSERT(item); + + if(item) + { + if( item->GetParent() ) + { + item->GetParent()->GetChildrenList().DeleteObject(item); + } + delete item; + } +} + +void wxXmlSerializer::RemoveAll() +{ + SetRootItem(new xsSerializable()); +} + +void wxXmlSerializer::SetRootItem(xsSerializable* root, bool bDeleteCurrentRoot /*= true*/) +{ + //wxASSERT(root); + //wxASSERT(root->IsKindOf(CLASSINFO(xsSerializable))); + + if (m_pRoot && bDeleteCurrentRoot) + delete m_pRoot; + + if(root && root->IsKindOf(CLASSINFO(xsSerializable))) + { + m_pRoot = root; + } + else + m_pRoot = new xsSerializable(); + + // update pointers to parent manager + m_mapUsedIDs.clear(); + + m_pRoot->m_pParentManager = this; + m_mapUsedIDs[m_pRoot->GetId()] = m_pRoot; + + xsSerializable *pItem; + SerializableList lstItems; + GetItems(NULL, lstItems); + + SerializableList::compatibility_iterator node = lstItems.GetFirst(); + while( node ) + { + pItem = node->GetData(); + + pItem->m_pParentManager = this; + m_mapUsedIDs[pItem->GetId()] = pItem; + + node = node->GetNext(); + } +} + +bool wxXmlSerializer::SerializeToXml(const wxString& file, bool withroot) +{ + wxFileOutputStream outstream(file); + + if(outstream.IsOk()) + { + return this->SerializeToXml(outstream, withroot); + } + else + wxMessageBox(wxT("Unable to initialize output file stream."), m_sOwner, wxOK|wxICON_ERROR); + + return false; +} + +bool wxXmlSerializer::SerializeToXml(wxOutputStream& outstream, bool withroot) +{ + // create root node + wxXmlNode *root = new wxXmlNode(wxXML_ELEMENT_NODE, m_sRootName); + + if(root) + { + // add version + root->AddAttribute(wxT("owner"), m_sOwner); + root->AddAttribute(wxT("version"), m_sVersion); + + // serialize root item properties + if(withroot) + { + wxXmlNode *root_props = new wxXmlNode(wxXML_ELEMENT_NODE, m_sRootName + wxT("_properties")); + root_props->AddChild(m_pRoot->SerializeObject(NULL)); + root->AddChild(root_props); + } + + // serialize shapes recursively + this->SerializeObjects(m_pRoot, root, false); + + // create XML document + try + { + wxXmlDocument xmlDoc; + xmlDoc.SetRoot(root); + + xmlDoc.Save(outstream, 2); + } + catch (...) + { + wxMessageBox(wxT("Unable to save XML document."), m_sOwner, wxOK|wxICON_ERROR); + return false; + } + } + + return true; +} + +bool wxXmlSerializer::DeserializeFromXml(const wxString& file) +{ + wxFileInputStream instream(file); + + if(instream.IsOk()) + { + return this->DeserializeFromXml(instream); + } + else + wxMessageBox(wxT("Unable to initialize input stream."), m_sOwner, wxOK|wxICON_ERROR); + + return false; +} + +bool wxXmlSerializer::DeserializeFromXml(wxInputStream& instream) +{ + // load an XML file + try + { + wxXmlDocument xmlDoc; + xmlDoc.Load(instream); + + wxXmlNode* root = xmlDoc.GetRoot(); + if(root && (root->GetName() == m_sRootName)) + { + // read project node's properties here... + wxString version, owner; + root->GetAttribute(wxT("owner"), &owner); + root->GetAttribute(wxT("version"), &version); + + if( (owner == m_sOwner) && (version == m_sVersion) ) + { + // read shape objects from XML recursively + this->DeserializeObjects(NULL, root); + return true; + } + else + wxMessageBox(wxT("No matching file owner or version."), m_sOwner, wxOK|wxICON_WARNING); + } + else + wxMessageBox(wxT("Unknown file format."), m_sOwner, wxOK|wxICON_WARNING); + } + catch (...) + { + wxMessageBox(wxT("Unable to load XML file."), m_sOwner, wxOK|wxICON_ERROR); + } + + return false; +} + +void wxXmlSerializer::SerializeObjects(xsSerializable* parent, wxXmlNode* node, bool withparent) +{ + wxASSERT(parent); + if(!parent)return; + + wxXmlNode* projectNode = NULL; + xsSerializable* pChild; + + // serialize parent shape + if(withparent) + { + if(parent->IsSerialized()) + { + projectNode = parent->SerializeObject(NULL); + if(projectNode) + { + SerializeObjects(parent, projectNode, false); + node->AddChild(projectNode); + } + } + } + else + { + // serialize parent's children + SerializableList::compatibility_iterator snode = parent->GetChildrenList().GetFirst(); + while(snode) + { + pChild = snode->GetData(); + + if(pChild->IsSerialized()) + { + projectNode = pChild->SerializeObject(NULL); + if(projectNode) + { + SerializeObjects(pChild, projectNode, false); + node->AddChild(projectNode); + } + } + + snode = snode->GetNext(); + } + } +} + +void wxXmlSerializer::DeserializeObjects(xsSerializable* parent, wxXmlNode* node) +{ + wxASSERT(m_pRoot); + if(!m_pRoot)return; + + xsSerializable *pItem; + + wxXmlNode* projectNode = node->GetChildren(); + while(projectNode) + { + if(projectNode->GetName() == wxT("object")) + { + pItem = (xsSerializable*)wxCreateDynamicObject(projectNode->GetAttribute(wxT("type"), wxT(""))); + if(pItem) + { + if(parent) + { + parent->AddChild(pItem); + } + else + m_pRoot->AddChild(pItem); + + pItem->DeserializeObject(projectNode); + // id could change so we must update the IDs map + m_mapUsedIDs[pItem->GetId()] = pItem; + + // deserialize child objects + DeserializeObjects(pItem, projectNode); + } + } + else if(projectNode->GetName() == m_sRootName + wxT("_properties")) + { + m_pRoot->DeserializeObject(projectNode->GetChildren()); + } + + projectNode = projectNode->GetNext(); + } +} + +bool wxXmlSerializer::IsIdUsed(long id) +{ + //return (GetIDCount(id) > 0); + return (m_mapUsedIDs.find( id ) != m_mapUsedIDs.end()); +} + +int wxXmlSerializer::GetIDCount(long id) +{ + int nCount = 0; + + SerializableList items; + GetItems(CLASSINFO(xsSerializable), items); + + SerializableList::compatibility_iterator node = items.GetFirst(); + while(node) + { + if( node->GetData()->GetId() == id ) nCount++; + node = node->GetNext(); + } + + if( m_pRoot->GetId() == id ) nCount++; + + return nCount; +} + +long wxXmlSerializer::GetNewId() +{ +/* long nId = 1; + + for ( IDMap::iterator it = m_mapUsedIDs.begin(); it != m_mapUsedIDs.end(); it++, nId++ ) + { + if (it->first != nId) break; + } + + return nId;*/ + + long nId = 1; + + while( m_mapUsedIDs.find( nId ) != m_mapUsedIDs.end() ) nId++; + + return nId; +} + +// private virtual functions /////////////////////////////////////////////////////// + +xsSerializable* wxXmlSerializer::_GetItem(long id, xsSerializable* parent) +{ + wxASSERT(parent); + + if( !parent )return NULL; + + if( parent->GetId() == id )return parent; + + xsSerializable *pItem = NULL; + SerializableList::compatibility_iterator node = parent->GetChildrenList().GetFirst(); + while(node) + { + pItem = _GetItem(id, node->GetData()); + if( pItem )break; + node = node->GetNext(); + } + return pItem; +} + +bool wxXmlSerializer::_Contains(xsSerializable* object, xsSerializable* parent) const +{ + wxASSERT(parent); + + if( !parent )return false; + + if( parent == object )return true; + + bool fFound = false; + SerializableList::compatibility_iterator node = parent->GetChildrenList().GetFirst(); + while(node) + { + fFound = _Contains(object, node->GetData()); + if( fFound )break; + node = node->GetNext(); + } + return fFound; +} diff --git a/ThirdParty/wxXS/src/wx_pch.cpp b/ThirdParty/wxXS/src/wx_pch.cpp new file mode 100644 index 0000000..ed9c389 --- /dev/null +++ b/ThirdParty/wxXS/src/wx_pch.cpp @@ -0,0 +1 @@ +#include "wx_pch.h" diff --git a/ThirdParty/wxXS/src/wx_pch.h b/ThirdParty/wxXS/src/wx_pch.h new file mode 100644 index 0000000..d2fb6a9 --- /dev/null +++ b/ThirdParty/wxXS/src/wx_pch.h @@ -0,0 +1,43 @@ +/*************************************************************** + * Name: wx_pch.h + * Purpose: Header to create Pre-Compiled Header (PCH) + * Author: Michal Bližňák () + * Created: 2007-03-04 + * Copyright: Michal Bližňák () + * License: + **************************************************************/ + +#ifndef WX_PCH_H_INCLUDED +#define WX_PCH_H_INCLUDED + +#ifdef _DISWARNINGS_MSVC +//#pragma warning( disable : 4100 ) +#pragma warning( disable : 4251 ) +#pragma warning( disable : 4275 ) +#endif + +// basic wxWidgets headers +#include + +#ifdef _DEBUG_MSVC +#ifdef _DEBUG +#include +#define DEBUG_NEW new(_NORMAL_BLOCK ,__FILE__, __LINE__) +#else +#define DEBUG_NEW new +#endif +#endif + +#ifdef __BORLANDC__ + #pragma hdrstop +#endif + +#ifndef WX_PRECOMP + #include +#endif + +#ifdef WX_PRECOMP + // put here all your rarely-changing header files +#endif // WX_PRECOMP + +#endif // WX_PCH_H_INCLUDED diff --git a/Utils/BlobToJSONFormatter.cpp b/Utils/BlobToJSONFormatter.cpp new file mode 100644 index 0000000..6cd394c --- /dev/null +++ b/Utils/BlobToJSONFormatter.cpp @@ -0,0 +1,46 @@ +#include "stdwx.h" +#include +#include +#include "JSONObjectFactory.h" +#include "BlobToJSONFormatter.h" + +wxString BlobToJSONFormatter::Format(iFloorBlobVector & blobs) +{ + wxString result; + + wxJSONValue root = JSONObjectFactory::Create(JSON_ACTION_BLOBS); + if (blobs.size() > 0) + { + // This should resize the array of items inside root["b"] + auto & blobRoot = root["b"]; + auto & tmp = blobRoot[blobs.size() - 1]; + + #pragma omp parallel for shared(root) + for (auto i = 0; i < blobs.size(); i++) + { + auto & item = blobRoot[i]; + item["c"]["x"] = static_cast(blobs[i].centroid.x); + item["c"]["y"] = static_cast(blobs[i].centroid.y); + item["l"]["x"] = static_cast(blobs[i].lastCentroid.x); + item["l"]["y"] = static_cast(blobs[i].lastCentroid.y); + auto pointsCount = blobs[i].pts.size(); + // This should resize the array of point coords + auto & pointRoot = item["p"]; + auto & tmp1 = pointRoot[pointsCount - 1]; + for (auto j = 0; j < pointsCount; ++j) + { + pointRoot[j]["x"] = static_cast(blobs[i].pts[j].x); + pointRoot[j]["y"] = static_cast(blobs[i].pts[j].y); + } + } + } + else + { + wxJSONValue null; + root["b"] = null; + } + wxJSONWriter writer(wxJSONWRITER_NONE); + writer.Write(root, result); + result.Replace(" ", ""); + return result; +} \ No newline at end of file diff --git a/Utils/BlobToJSONFormatter.h b/Utils/BlobToJSONFormatter.h new file mode 100644 index 0000000..a1bec70 --- /dev/null +++ b/Utils/BlobToJSONFormatter.h @@ -0,0 +1,11 @@ +#pragma once + +#include + +#define JSON_ACTION_BLOBS wxT("blobs") + +class BlobToJSONFormatter +{ +public: + static wxString Format(iFloorBlobVector & blobs); +}; \ No newline at end of file diff --git a/Utils/CMakeLists.txt b/Utils/CMakeLists.txt new file mode 100644 index 0000000..3a509c7 --- /dev/null +++ b/Utils/CMakeLists.txt @@ -0,0 +1,46 @@ +# +# Stuff the average programmer needs to change +# +set(SRCS + wxFPValidator.cpp + MACAddressUtility.cpp + Guid.cpp + BlobToJSONFormatter.cpp + JSONObjectFactory.cpp +) +set(HFILES + wxFPValidator.h + wxMACAddressUtility.h + MACAddressUtility.h + wxTemplateClientData.h + Guid.h + BlobToJSONFormatter.h + JSONObjectFactory.h +) + + +set(INCLUDE_DIRECTORIES ${BASE_INCLUDE_DIRECTORIES} + ${THIRD_PARTY_DIR}/wxJSON/include + ${THIRD_PARTY_DIR}/MotionPrimitives) +set(LIBRARY_NAME Utils) +if(WIN32) + set(PREPROCESSOR_DEFINITIONS ${PREPROCESSOR_DEFINITIONS};/D_LIB) +endif(WIN32) +set(SRCS ${SRCS} ${HFILES} ${PROJECT_ROOT_DIR}/include/stdwx.cpp ${PROJECT_ROOT_DIR}/include/stdwx.h) + +# +# Non Builds programmer should not need to change anything beyond this +# + +add_definitions(${PREPROCESSOR_DEFINITIONS}) +include_directories(${INCLUDE_DIRECTORIES}) + +add_library(${LIBRARY_NAME} STATIC ${SRCS}) +# Precompiled header stuff must be after the target is added +#set_precompiled_header(${LIBRARY_NAME} ${PROJECT_ROOT_DIR}/include/stdwx.h ${PROJECT_ROOT_DIR}/include/stdwx.cpp) + + +target_precompile_headers(${LIBRARY_NAME} + PRIVATE + "${PROJECT_ROOT_DIR}/include/stdwx.h" +) diff --git a/Utils/DiskInfo.cpp b/Utils/DiskInfo.cpp new file mode 100644 index 0000000..d1a82cd --- /dev/null +++ b/Utils/DiskInfo.cpp @@ -0,0 +1,932 @@ +#include "stdwx.h" +//for native code only uncomment the next line and use UnmanagedCode.h and UnmanagedCode.cpp +//as source files in your project, for managed C++ leave it as is +//#define NATIVE_CODE +#include "DiskInfo.h" +#ifndef NATIVE_CODE +using namespace System::Runtime::InteropServices; +#endif + +#undef UNICODE +#undef _UNICODE + +char DiskInfo::HardDriveSerialNumber [1024]; + +DiskInfo* DiskInfo::m_instance = NULL; + +BOOL DiskInfo::AddIfNew(USHORT *pIdSector) +{ + BOOL bAdd = TRUE; + for(size_t i = 0; i< m_list.size();i++) + { + if(memcmp(pIdSector, m_list[i], 256 * sizeof(WORD)) == 0) + { + bAdd = false; + break; + } + } + if(bAdd) + { + auto diskdata = new WORD[256]; + ::memcpy(diskdata,pIdSector,256*sizeof(WORD)); + m_list.push_back(diskdata); + } + return bAdd; +} + +int DiskInfo::ReadPhysicalDriveInNTUsingSmart (void) +{ + int done = FALSE; + int drive = 0; + + for (drive = 0; drive < MAX_IDE_DRIVES; drive++) + { + HANDLE hPhysicalDriveIOCTL = 0; + + // Try to get a handle to PhysicalDrive IOCTL, report failure + // and exit if can't. + + TCHAR driveName [256]; + sprintf(driveName, "\\\\.\\PhysicalDrive%d", drive); + + // Windows NT, Windows 2000, Windows Server 2003, Vista + hPhysicalDriveIOCTL = CreateFile (driveName, + GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, + nullptr, OPEN_EXISTING, 0, nullptr); + + if (hPhysicalDriveIOCTL == INVALID_HANDLE_VALUE) + { + + } + else + { + GETVERSIONINPARAMS GetVersionParams; + DWORD cbBytesReturned = 0; + + // Get the version, etc of PhysicalDrive IOCTL + memset ((void*) & GetVersionParams, 0, sizeof(GetVersionParams)); + + if ( DeviceIoControl (hPhysicalDriveIOCTL, SMART_GET_VERSION, + nullptr, + 0, + &GetVersionParams, sizeof (GETVERSIONINPARAMS), + &cbBytesReturned, nullptr) ) + { + // Allocate the command buffer + ULONG CommandSize = sizeof(SENDCMDINPARAMS) + IDENTIFY_BUFFER_SIZE; + PSENDCMDINPARAMS Command = (PSENDCMDINPARAMS) malloc (CommandSize); + // Retrieve the IDENTIFY data + // Prepare the command +#define ID_CMD 0xEC // Returns ID sector for ATA + Command -> irDriveRegs.bCommandReg = ID_CMD; + DWORD BytesReturned = 0; + if ( DeviceIoControl (hPhysicalDriveIOCTL, + SMART_RCV_DRIVE_DATA, Command, sizeof(SENDCMDINPARAMS), + Command, CommandSize, + &BytesReturned, NULL) ) + + { + // Print the IDENTIFY data + //DWORD diskdata [256]; + USHORT *pIdSector = (USHORT *) + (PIDENTIFY_DATA) ((PSENDCMDOUTPARAMS) Command) -> bBuffer; + + + AddIfNew(pIdSector); + + done = TRUE; + } + // Done + CloseHandle (hPhysicalDriveIOCTL); + free (Command); + } + } + } + + return done; +} + +int DiskInfo::ReadPhysicalDriveInNTWithAdminRights (void) +{ + int done = FALSE; + int drive = 0; + BYTE IdOutCmd [sizeof (SENDCMDOUTPARAMS) + IDENTIFY_BUFFER_SIZE - 1]; + for (drive = 0; drive < MAX_IDE_DRIVES; drive++) + { + HANDLE hPhysicalDriveIOCTL = 0; + + // Try to get a handle to PhysicalDrive IOCTL, report failure + // and exit if can't. + TCHAR driveName [256]; + + swprintf (driveName, L"\\\\.\\PhysicalDrive%d", drive); + + // Windows NT, Windows 2000, must have admin rights + hPhysicalDriveIOCTL = CreateFile (driveName, + GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, + OPEN_EXISTING, 0, nullptr); + + if (hPhysicalDriveIOCTL != INVALID_HANDLE_VALUE) + { + GETVERSIONOUTPARAMS VersionParams; + DWORD cbBytesReturned = 0; + + // Get the version, etc of PhysicalDrive IOCTL + memset ((void*) &VersionParams, 0, sizeof(VersionParams)); + + if ( DeviceIoControl (hPhysicalDriveIOCTL, DFP_GET_VERSION, + nullptr, + 0, + &VersionParams, + sizeof(VersionParams), + &cbBytesReturned, nullptr) ) + { + + + // If there is a IDE device at number "i" issue commands + // to the device + if (VersionParams.bIDEDeviceMap > 0) + { + BYTE bIDCmd = 0; // IDE or ATAPI IDENTIFY cmd + SENDCMDINPARAMS scip; + + // Now, get the ID sector for all IDE devices in the system. + // If the device is ATAPI use the IDE_ATAPI_IDENTIFY command, + // otherwise use the IDE_ATA_IDENTIFY command + bIDCmd = (VersionParams.bIDEDeviceMap >> drive & 0x10) ? \ + IDE_ATAPI_IDENTIFY : IDE_ATA_IDENTIFY; + + memset (&scip, 0, sizeof(scip)); + memset (IdOutCmd, 0, sizeof(IdOutCmd)); + + if ( DoIDENTIFY (hPhysicalDriveIOCTL, + &scip, + (PSENDCMDOUTPARAMS)&IdOutCmd, + (BYTE) bIDCmd, + (BYTE) drive, + &cbBytesReturned)) + { + + + USHORT *pIdSector = (USHORT *) ((PSENDCMDOUTPARAMS)IdOutCmd)->bBuffer; + AddIfNew(pIdSector); + done = TRUE; + } + } + } + + CloseHandle (hPhysicalDriveIOCTL); + } + } + + return done; +} + + +// Required to ensure correct PhysicalDrive IOCTL structure setup +#pragma pack(4) + + +// +// IOCTL_STORAGE_QUERY_PROPERTY +// +// Input Buffer: +// a STORAGE_PROPERTY_QUERY structure which describes what type of query +// is being done, what property is being queried for, and any additional +// parameters which a particular property query requires. +// +// Output Buffer: +// Contains a buffer to place the results of the query into. Since all +// property descriptors can be cast into a STORAGE_DESCRIPTOR_HEADER, +// the IOCTL can be called once with a small buffer then again using +// a buffer as large as the header reports is necessary. +// + + +// +// Types of queries +// + +//typedef enum _STORAGE_QUERY_TYPE { +// PropertyStandardQuery = 0, // Retrieves the descriptor +// PropertyExistsQuery, // Used to test whether the descriptor is supported +// PropertyMaskQuery, // Used to retrieve a mask of writeable fields in the descriptor +// PropertyQueryMaxDefined // use to validate the value +//} STORAGE_QUERY_TYPE, *PSTORAGE_QUERY_TYPE; + +// +// define some initial property id's +// + +//typedef enum _STORAGE_PROPERTY_ID { +// StorageDeviceProperty = 0, +// StorageAdapterProperty +//} STORAGE_PROPERTY_ID, *PSTORAGE_PROPERTY_ID; + +// +// Query structure - additional parameters for specific queries can follow +// the header +// + +//typedef struct _STORAGE_PROPERTY_QUERY { +// +// // +// // ID of the property being retrieved +// // +// +// STORAGE_PROPERTY_ID PropertyId; +// +// // +// // Flags indicating the type of query being performed +// // +// +// STORAGE_QUERY_TYPE QueryType; +// +// // +// // Space for additional parameters if necessary +// // +// +// UCHAR AdditionalParameters[1]; +// +//} STORAGE_PROPERTY_QUERY, *PSTORAGE_PROPERTY_QUERY; + + +#define IOCTL_STORAGE_QUERY_PROPERTY CTL_CODE(IOCTL_STORAGE_BASE, 0x0500, METHOD_BUFFERED, FILE_ANY_ACCESS) + + +// +// Device property descriptor - this is really just a rehash of the inquiry +// data retrieved from a scsi device +// +// This may only be retrieved from a target device. Sending this to the bus +// will result in an error +// + +//typedef struct _STORAGE_DEVICE_DESCRIPTOR { +// +// // +// // Sizeof(STORAGE_DEVICE_DESCRIPTOR) +// // +// +// ULONG Version; +// +// // +// // Total size of the descriptor, including the space for additional +// // data and id strings +// // +// +// ULONG Size; +// +// // +// // The SCSI-2 device type +// // +// +// UCHAR DeviceType; +// +// // +// // The SCSI-2 device type modifier (if any) - this may be zero +// // +// +// UCHAR DeviceTypeModifier; +// +// // +// // Flag indicating whether the device's media (if any) is removable. This +// // field should be ignored for media-less devices +// // +// +// BOOLEAN RemovableMedia; +// +// // +// // Flag indicating whether the device can support mulitple outstanding +// // commands. The actual synchronization in this case is the responsibility +// // of the port driver. +// // +// +// BOOLEAN CommandQueueing; +// +// // +// // Byte offset to the zero-terminated ascii string containing the device's +// // vendor id string. For devices with no such ID this will be zero +// // +// +// ULONG VendorIdOffset; +// +// // +// // Byte offset to the zero-terminated ascii string containing the device's +// // product id string. For devices with no such ID this will be zero +// // +// +// ULONG ProductIdOffset; +// +// // +// // Byte offset to the zero-terminated ascii string containing the device's +// // product revision string. For devices with no such string this will be +// // zero +// // +// +// ULONG ProductRevisionOffset; +// +// // +// // Byte offset to the zero-terminated ascii string containing the device's +// // serial number. For devices with no serial number this will be zero +// // +// +// ULONG SerialNumberOffset; +// +// // +// // Contains the bus type (as defined above) of the device. It should be +// // used to interpret the raw device properties at the end of this structure +// // (if any) +// // +// +// STORAGE_BUS_TYPE BusType; +// +// // +// // The number of bytes of bus-specific data which have been appended to +// // this descriptor +// // +// +// ULONG RawPropertiesLength; +// +// // +// // Place holder for the first byte of the bus specific property data +// // +// +// UCHAR RawDeviceProperties[1]; +// +//} STORAGE_DEVICE_DESCRIPTOR, *PSTORAGE_DEVICE_DESCRIPTOR; + + + // function to decode the serial numbers of IDE hard drives + // using the IOCTL_STORAGE_QUERY_PROPERTY command +char * DiskInfo::flipAndCodeBytes (char * str) +{ + static char flipped [1000]; + int num = strlen (str); + strcpy (flipped, ""); + for (int i = 0; i < num; i += 4) + { + for (int j = 1; j >= 0; j--) + { + int sum = 0; + for (int k = 0; k < 2; k++) + { + sum *= 16; + switch (str [i + j * 2 + k]) + { + case '0': sum += 0; break; + case '1': sum += 1; break; + case '2': sum += 2; break; + case '3': sum += 3; break; + case '4': sum += 4; break; + case '5': sum += 5; break; + case '6': sum += 6; break; + case '7': sum += 7; break; + case '8': sum += 8; break; + case '9': sum += 9; break; + case 'a': sum += 10; break; + case 'b': sum += 11; break; + case 'c': sum += 12; break; + case 'd': sum += 13; break; + case 'e': sum += 14; break; + case 'f': sum += 15; break; + } + } + if (sum > 0) + { + char sub [2]; + sub [0] = (char) sum; + sub [1] = 0; + strcat (flipped, sub); + } + } + } + + return flipped; +} + + +typedef struct _MEDIA_SERAL_NUMBER_DATA { + ULONG SerialNumberLength; + ULONG Result; + ULONG Reserved[2]; + UCHAR SerialNumberData[1]; +} MEDIA_SERIAL_NUMBER_DATA, *PMEDIA_SERIAL_NUMBER_DATA; + + +int DiskInfo::ReadPhysicalDriveInNTWithZeroRights (void) +{ + int done = FALSE; + int drive = 0; + + for (drive = 0; drive < MAX_IDE_DRIVES; drive++) + { + HANDLE hPhysicalDriveIOCTL = 0; + + // Try to get a handle to PhysicalDrive IOCTL, report failure + // and exit if can't. + TCHAR driveName [256]; + + swprintf (driveName, L"\\\\.\\PhysicalDrive%d", drive); + + // Windows NT, Windows 2000, Windows XP - admin rights not required + hPhysicalDriveIOCTL = CreateFile (driveName, 0, + FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, + OPEN_EXISTING, 0, NULL); + + + if (hPhysicalDriveIOCTL != INVALID_HANDLE_VALUE) + { + STORAGE_PROPERTY_QUERY query; + DWORD cbBytesReturned = 0; + char buffer [10000]; + + memset ((void *) & query, 0, sizeof (query)); + query.PropertyId = StorageDeviceProperty; + query.QueryType = PropertyStandardQuery; + + memset (buffer, 0, sizeof (buffer)); + + if ( DeviceIoControl (hPhysicalDriveIOCTL, IOCTL_STORAGE_QUERY_PROPERTY, + & query, + sizeof (query), + & buffer, + sizeof (buffer), + & cbBytesReturned, NULL) ) + { + STORAGE_DEVICE_DESCRIPTOR * descrip = (STORAGE_DEVICE_DESCRIPTOR *) & buffer; + char serialNumber [1000]; + + strcpy (serialNumber, + flipAndCodeBytes ( & buffer [descrip -> SerialNumberOffset])); + } + else + { + DWORD err = GetLastError (); + + } + + memset (buffer, 0, sizeof (buffer)); + + if ( DeviceIoControl (hPhysicalDriveIOCTL, IOCTL_STORAGE_GET_MEDIA_SERIAL_NUMBER, + NULL, + 0, + & buffer, + sizeof (buffer), + & cbBytesReturned, NULL) ) + { + MEDIA_SERIAL_NUMBER_DATA * mediaSerialNumber = + (MEDIA_SERIAL_NUMBER_DATA *) & buffer; + char serialNumber [1000]; + + strcpy (serialNumber, (char *) mediaSerialNumber -> SerialNumberData); + + } + else + { + DWORD err = GetLastError (); + } + + CloseHandle (hPhysicalDriveIOCTL); + } + } + + return done; +} + + + // DoIDENTIFY + // FUNCTION: Send an IDENTIFY command to the drive + // bDriveNum = 0-3 + // bIDCmd = IDE_ATA_IDENTIFY or IDE_ATAPI_IDENTIFY +BOOL DiskInfo::DoIDENTIFY (HANDLE hPhysicalDriveIOCTL, PSENDCMDINPARAMS pSCIP, + PSENDCMDOUTPARAMS pSCOP, BYTE bIDCmd, BYTE bDriveNum, + PDWORD lpcbBytesReturned) +{ + // Set up data structures for IDENTIFY command. + pSCIP -> cBufferSize = IDENTIFY_BUFFER_SIZE; + pSCIP -> irDriveRegs.bFeaturesReg = 0; + pSCIP -> irDriveRegs.bSectorCountReg = 1; + pSCIP -> irDriveRegs.bSectorNumberReg = 1; + pSCIP -> irDriveRegs.bCylLowReg = 0; + pSCIP -> irDriveRegs.bCylHighReg = 0; + + // Compute the drive number. + pSCIP -> irDriveRegs.bDriveHeadReg = 0xA0 | ((bDriveNum & 1) << 4); + + // The command can either be IDE identify or ATAPI identify. + pSCIP -> irDriveRegs.bCommandReg = bIDCmd; + pSCIP -> bDriveNumber = bDriveNum; + pSCIP -> cBufferSize = IDENTIFY_BUFFER_SIZE; + + return ( DeviceIoControl (hPhysicalDriveIOCTL, DFP_RECEIVE_DRIVE_DATA, + (LPVOID) pSCIP, + sizeof(SENDCMDINPARAMS) - 1, + (LPVOID) pSCOP, + sizeof(SENDCMDOUTPARAMS) + IDENTIFY_BUFFER_SIZE - 1, + lpcbBytesReturned, NULL) ); +} + + +// --------------------------------------------------- + + // (* Output Bbuffer for the VxD (rt_IdeDinfo record) *) +typedef struct _rt_IdeDInfo_ +{ + BYTE IDEExists[4]; + BYTE DiskExists[8]; + WORD DisksRawInfo[8*256]; +} rt_IdeDInfo, *pt_IdeDInfo; + + + // (* IdeDinfo "data fields" *) +typedef struct _rt_DiskInfo_ +{ + BOOL DiskExists; + BOOL ATAdevice; + BOOL RemovableDevice; + WORD TotLogCyl; + WORD TotLogHeads; + WORD TotLogSPT; + char SerialNumber[20]; + char FirmwareRevision[8]; + char ModelNumber[40]; + WORD CurLogCyl; + WORD CurLogHeads; + WORD CurLogSPT; +} rt_DiskInfo; + + +#define m_cVxDFunctionIdesDInfo 1 + + +// --------------------------------------------------- + + +int DiskInfo::ReadDrivePortsInWin9X (void) +{ + int done = FALSE; + + HANDLE VxDHandle = 0; + pt_IdeDInfo pOutBufVxD = 0; + DWORD lpBytesReturned = 0; + + // set the thread priority high so that we get exclusive access to the disk + BOOL status = + // SetThreadPriority (GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL); + SetPriorityClass (GetCurrentProcess (), REALTIME_PRIORITY_CLASS); + // SetPriorityClass (GetCurrentProcess (), HIGH_PRIORITY_CLASS); + + // 1. Make an output buffer for the VxD + rt_IdeDInfo info; + pOutBufVxD = &info; + + // ***************** + // KLUDGE WARNING!!! + // HAVE to zero out the buffer space for the IDE information! + // If this is NOT done then garbage could be in the memory + // locations indicating if a disk exists or not. + ZeroMemory (&info, sizeof(info)); + + // 1. Try to load the VxD + // must use the short file name path to open a VXD file + //char StartupDirectory [2048]; + //char shortFileNamePath [2048]; + //char *p = NULL; + //char vxd [2048]; + // get the directory that the exe was started from + //GetModuleFileName (hInst, (LPSTR) StartupDirectory, sizeof (StartupDirectory)); + // cut the exe name from string + //p = &(StartupDirectory [strlen (StartupDirectory) - 1]); + //while (p >= StartupDirectory && *p && '\\' != *p) p--; + //*p = '\0'; + //GetShortPathName (StartupDirectory, shortFileNamePath, 2048); + //sprintf (vxd, "\\\\.\\%s\\IDE21201.VXD", shortFileNamePath); + //VxDHandle = CreateFile (vxd, 0, 0, 0, + // 0, FILE_FLAG_DELETE_ON_CLOSE, 0); + VxDHandle = CreateFile (L"\\\\.\\IDE21201.VXD", 0, 0, 0, + 0, FILE_FLAG_DELETE_ON_CLOSE, 0); + + if (VxDHandle != INVALID_HANDLE_VALUE) + { + // 2. Run VxD function + DeviceIoControl (VxDHandle, m_cVxDFunctionIdesDInfo, + 0, 0, pOutBufVxD, sizeof(pt_IdeDInfo), &lpBytesReturned, 0); + + // 3. Unload VxD + CloseHandle (VxDHandle); + } + else + //::MessageBox (NULL, L"ERROR: Could not open IDE21201.VXD file", + // TITLE, MB_ICONSTOP); + return FALSE; + + + // 4. Translate and store data + unsigned long int i = 0; + for (i=0; i<8; i++) + { + if((pOutBufVxD->DiskExists[i]) && (pOutBufVxD->IDEExists[i/2])) + { + + WORD* diskdata = new WORD[256]; + for (int j = 0; j < 256; j++) + diskdata [j] = pOutBufVxD -> DisksRawInfo [i * 256 + j]; + + // process the information for this buffer + BOOL bAdd = TRUE; + for(UINT j =0; j< m_list.size();j++) + { + if(memcmp(diskdata,m_list[j],256 * sizeof(WORD)) == 0) + { + bAdd = false; + break; + } + } + if(bAdd) + m_list.push_back(diskdata); + else + wxDELETEA(diskdata); + done = TRUE; + } + } + + // reset the thread priority back to normal + SetPriorityClass (GetCurrentProcess (), NORMAL_PRIORITY_CLASS); + + return done; +} + + +#define SENDIDLENGTH sizeof (SENDCMDOUTPARAMS) + IDENTIFY_BUFFER_SIZE + + +int DiskInfo::ReadIdeDriveAsScsiDriveInNT (void) +{ + int done = FALSE; + int controller = 0; + + for (controller = 0; controller < 16; controller++) + { + HANDLE hScsiDriveIOCTL = 0; + TCHAR driveName [256]; + + // Try to get a handle to PhysicalDrive IOCTL, report failure + // and exit if can't. + swprintf (driveName, L"\\\\.\\Scsi%d:", controller); + + // Windows NT, Windows 2000, any rights should do + hScsiDriveIOCTL = CreateFile (driveName, + GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, + OPEN_EXISTING, 0, NULL); + + + if (hScsiDriveIOCTL != INVALID_HANDLE_VALUE) + { + int drive = 0; + + for (drive = 0; drive < 2; drive++) + { + char buffer [sizeof (SRB_IO_CONTROL) + SENDIDLENGTH]; + SRB_IO_CONTROL *p = (SRB_IO_CONTROL *) buffer; + SENDCMDINPARAMS *pin = + (SENDCMDINPARAMS *) (buffer + sizeof (SRB_IO_CONTROL)); + DWORD dummy; + + memset (buffer, 0, sizeof (buffer)); + p -> HeaderLength = sizeof (SRB_IO_CONTROL); + p -> Timeout = 10000; + p -> Length = SENDIDLENGTH; + p -> ControlCode = IOCTL_SCSI_MINIPORT_IDENTIFY; + strncpy ((char *) p -> Signature, "SCSIDISK", 8); + + pin -> irDriveRegs.bCommandReg = IDE_ATA_IDENTIFY; + pin -> bDriveNumber = drive; + + if (DeviceIoControl (hScsiDriveIOCTL, IOCTL_SCSI_MINIPORT, + buffer, + sizeof (SRB_IO_CONTROL) + + sizeof (SENDCMDINPARAMS) - 1, + buffer, + sizeof (SRB_IO_CONTROL) + SENDIDLENGTH, + &dummy, NULL)) + { + SENDCMDOUTPARAMS *pOut = + (SENDCMDOUTPARAMS *) (buffer + sizeof (SRB_IO_CONTROL)); + IDSECTOR *pId = (IDSECTOR *) (pOut -> bBuffer); + if (pId -> sModelNumber [0]) + { + + USHORT *pIdSector = (USHORT *) pId; + AddIfNew(pIdSector); + //BOOL bAdd = TRUE; + // for(UINT i =0; i< m_list.size();i++) + // { + // if(memcmp(pIdSector,m_list[i],256 * sizeof(WORD)) == 0) + // { + // bAdd = false; + // break; + // } + // } + // if(bAdd) + // { + // WORD* diskdata = new WORD[256]; + // ::memcpy(diskdata,pIdSector,256*sizeof(WORD)); + // m_list.push_back(diskdata); + // } + + done = TRUE; + } + } + } + CloseHandle (hScsiDriveIOCTL); + } + } + + return done; +} + +#ifndef NATIVE_CODE +String^ DiskInfo::GetModelNumber(UINT drive) +{ + String^ ms = GetString (m_list[drive], 27, 46); + return ms->Trim(); +} +String^ DiskInfo::GetSerialNumber(UINT drive) +{ + String^ ms = GetString (m_list[drive], 10, 19); + return ms->Trim(); +} + +String^ DiskInfo::GetRevisionNumber(UINT drive) +{ + String^ ms = GetString (m_list[drive], 23, 26); + return ms->Trim(); +} + +String^ DiskInfo::GetDriveType (UINT drive) +{ + if (m_list[drive][0] & 0x0080) + return "Removable"; + else if (m_list[drive][0] & 0x0040) + return "Fixed"; + else return "Unknown"; +} + +String^ DiskInfo::GetString (WORD* pdiskdata, int firstIndex, int lastIndex) +{ + int index = 0; + int position = 0; + //if(lastIndex < firstIndex) + // return (String^)NULL; + // each integer has two characters stored in it backwards + char* string = new char[2*(lastIndex- firstIndex +1)]; + for (index = firstIndex; index <= lastIndex; index++) + { + // get high byte for 1st character + string [position] = (char) (pdiskdata [index] / 256); + position++; + + // get low byte for 2nd character + string [position] = (char) (pdiskdata [index] % 256); + position++; + } + + String^ ms = Marshal::PtrToStringAnsi((IntPtr)string,2*(lastIndex- firstIndex +1)); + delete string; + return ms; +} +#else + +char* DiskInfo::ModelNumber (UINT drive) +{ + return ConvertToString(m_list[drive], 27, 46); +} + +char* DiskInfo::SerialNumber (UINT drive) +{ + return ConvertToString(m_list[drive], 10, 19); +} + + + +char* DiskInfo::RevisionNumber (UINT drive) +{ + return ConvertToString(m_list[drive], 23, 26); +} + +char* DiskInfo::DriveType (UINT drive) +{ + if (m_list[drive][0] & 0x0080) + return "Removable"; + else if (m_list[drive][0] & 0x0040) + return "Fixed"; + else return "Unknown"; +} + + + +char * DiskInfo::ConvertToString (WORD diskdata [256], int firstIndex, int lastIndex) +{ + static char string [1024]; + int index = 0; + int position = 0; + + // each integer has two characters stored in it backwards + for (index = firstIndex; index <= lastIndex; index++) + { + // get high byte for 1st character + string [position] = (char) (diskdata [index] / 256); + position++; + + // get low byte for 2nd character + string [position] = (char) (diskdata [index] % 256); + position++; + } + + // end the string + string [position] = '\0'; + + // cut off the trailing blanks + for (index = position - 1; index > 0 && ' ' == string [index]; index--) + string [index] = '\0'; + + return string; +} +#endif + +unsigned __int64 DiskInfo::DriveSize (UINT drive) +{ + unsigned __int64 bytes = 0,sectors =0; + if (m_list[drive] [83] & 0x400) + sectors = m_list[drive][103] * 65536I64 * 65536I64 * 65536I64 + + m_list[drive][102] * 65536I64 * 65536I64 + + m_list[drive][101] * 65536I64 + + m_list[drive][100]; + else + sectors = m_list[drive][61] * 65536 + m_list[drive][60]; + // there are 512 bytes in a sector + bytes = sectors * 512; + return bytes; +} + +DiskInfo::DiskInfo(void) +{ + //m_DriveCount = 0; +} + +DiskInfo::~DiskInfo(void) +{ + for(UINT i = 0; i< m_list.size(); i++) + delete m_list[i]; + m_list.clear(); +} + + +long DiskInfo::LoadDiskInfo () + { + int done = FALSE; + __int64 id = 0; + OSVERSIONINFO version; + memset (&version, 0, sizeof (version)); + version.dwOSVersionInfoSize = sizeof (OSVERSIONINFO); + GetVersionEx (&version); + for(UINT i = 0; i< m_list.size(); i++) + delete m_list[i]; + m_list.clear(); + if (version.dwPlatformId == VER_PLATFORM_WIN32_NT) + { + // this works under WinNT4 or Win2K if you have admin rights + done = ReadPhysicalDriveInNTWithAdminRights (); + + // this should work in WinNT or Win2K if previous did not work + // this is kind of a backdoor via the SCSI mini port driver into + // the IDE drives + done = ReadIdeDriveAsScsiDriveInNT (); + + //this works under WinNT4 or Win2K or WinXP if you have any rights + //done = ReadPhysicalDriveInNTWithZeroRights (); + done = ReadPhysicalDriveInNTUsingSmart(); + + } + else + { + // this works under Win9X and calls a VXD + int attempt = 0; + + // try this up to 10 times to get a hard drive serial number + for (attempt = 0; + attempt < 10 && !done ; + attempt++) + done = ReadDrivePortsInWin9X (); + } + return (long) m_list.size(); + } + +UINT DiskInfo::BufferSize (UINT drive) +{ + return m_list[drive][21] * 512; +} \ No newline at end of file diff --git a/Utils/DiskInfo.h b/Utils/DiskInfo.h new file mode 100644 index 0000000..2b33346 --- /dev/null +++ b/Utils/DiskInfo.h @@ -0,0 +1,280 @@ +#pragma once + + +#include +#include +#include +#include +#include +#include + +#define NATIVE_CODE + +#pragma warning(disable:4996) + // Required to ensure correct PhysicalDrive IOCTL structure setup +#pragma pack(1) + +#define IDENTIFY_BUFFER_SIZE 512 + + + // IOCTL commands +#define DFP_GET_VERSION 0x00074080 +#define DFP_SEND_DRIVE_COMMAND 0x0007c084 +#define DFP_RECEIVE_DRIVE_DATA 0x0007c088 + +#define FILE_DEVICE_SCSI 0x0000001b +#define IOCTL_SCSI_MINIPORT_IDENTIFY ((FILE_DEVICE_SCSI << 16) + 0x0501) +#define IOCTL_SCSI_MINIPORT 0x0004D008 // see NTDDSCSI.H for definition + + +#define SMART_GET_VERSION CTL_CODE(IOCTL_DISK_BASE, 0x0020, METHOD_BUFFERED, FILE_READ_ACCESS) +#define SMART_SEND_DRIVE_COMMAND CTL_CODE(IOCTL_DISK_BASE, 0x0021, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) +#define SMART_RCV_DRIVE_DATA CTL_CODE(IOCTL_DISK_BASE, 0x0022, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) + + // GETVERSIONOUTPARAMS contains the data returned from the + // Get Driver Version function. +typedef struct _GETVERSIONOUTPARAMS +{ + BYTE bVersion; // Binary driver version. + BYTE bRevision; // Binary driver revision. + BYTE bReserved; // Not used. + BYTE bIDEDeviceMap; // Bit map of IDE devices. + DWORD fCapabilities; // Bit mask of driver capabilities. + DWORD dwReserved[4]; // For future use. +} GETVERSIONOUTPARAMS, *PGETVERSIONOUTPARAMS, *LPGETVERSIONOUTPARAMS; + + + // Bits returned in the fCapabilities member of GETVERSIONOUTPARAMS +#define CAP_IDE_ID_FUNCTION 1 // ATA ID command supported +#define CAP_IDE_ATAPI_ID 2 // ATAPI ID command supported +#define CAP_IDE_EXECUTE_SMART_FUNCTION 4 // SMART commannds supported + +//typedef struct _GETVERSIONINPARAMS { +// UCHAR bVersion; // Binary driver version. +// UCHAR bRevision; // Binary driver revision. +// UCHAR bReserved; // Not used. +// UCHAR bIDEDeviceMap; // Bit map of IDE devices. +// ULONG fCapabilities; // Bit mask of driver capabilities. +// ULONG dwReserved[4]; // For future use. +//} GETVERSIONINPARAMS, *PGETVERSIONINPARAMS, *LPGETVERSIONINPARAMS; +// +// // IDE registers +//typedef struct _IDEREGS +//{ +// BYTE bFeaturesReg; // Used for specifying SMART "commands". +// BYTE bSectorCountReg; // IDE sector count register +// BYTE bSectorNumberReg; // IDE sector number register +// BYTE bCylLowReg; // IDE low order cylinder value +// BYTE bCylHighReg; // IDE high order cylinder value +// BYTE bDriveHeadReg; // IDE drive/head register +// BYTE bCommandReg; // Actual IDE command. +// BYTE bReserved; // reserved for future use. Must be zero. +//} IDEREGS, *PIDEREGS, *LPIDEREGS; +// +// +// // SENDCMDINPARAMS contains the input parameters for the +// // Send Command to Drive function. +//typedef struct _SENDCMDINPARAMS +//{ +// DWORD cBufferSize; // Buffer size in bytes +// IDEREGS irDriveRegs; // Structure with drive register values. +// BYTE bDriveNumber; // Physical drive number to send +// // command to (0,1,2,3). +// BYTE bReserved[3]; // Reserved for future expansion. +// DWORD dwReserved[4]; // For future use. +// BYTE bBuffer[1]; // Input buffer. +//} SENDCMDINPARAMS, *PSENDCMDINPARAMS, *LPSENDCMDINPARAMS; + + + // Valid values for the bCommandReg member of IDEREGS. +#define IDE_ATAPI_IDENTIFY 0xA1 // Returns ID sector for ATAPI. +#define IDE_ATA_IDENTIFY 0xEC // Returns ID sector for ATA. + + + // Status returned from driver +//typedef struct _DRIVERSTATUS +//{ +// BYTE bDriverError; // Error code from driver, or 0 if no error. +// BYTE bIDEStatus; // Contents of IDE Error register. +// // Only valid when bDriverError is SMART_IDE_ERROR. +// BYTE bReserved[2]; // Reserved for future expansion. +// DWORD dwReserved[2]; // Reserved for future expansion. +//} DRIVERSTATUS, *PDRIVERSTATUS, *LPDRIVERSTATUS; +// +// +// // Structure returned by PhysicalDrive IOCTL for several commands +//typedef struct _SENDCMDOUTPARAMS +//{ +// DWORD cBufferSize; // Size of bBuffer in bytes +// DRIVERSTATUS DriverStatus; // Driver status structure. +// BYTE bBuffer[1]; // Buffer of arbitrary length in which to store the data read from the // drive. +//} SENDCMDOUTPARAMS, *PSENDCMDOUTPARAMS, *LPSENDCMDOUTPARAMS; +// +// +// // The following struct defines the interesting part of the IDENTIFY +// // buffer: +typedef struct _IDSECTOR +{ + USHORT wGenConfig; + USHORT wNumCyls; + USHORT wReserved; + USHORT wNumHeads; + USHORT wBytesPerTrack; + USHORT wBytesPerSector; + USHORT wSectorsPerTrack; + USHORT wVendorUnique[3]; + CHAR sSerialNumber[20]; + USHORT wBufferType; + USHORT wBufferSize; + USHORT wECCSize; + CHAR sFirmwareRev[8]; + CHAR sModelNumber[40]; + USHORT wMoreVendorUnique; + USHORT wDoubleWordIO; + USHORT wCapabilities; + USHORT wReserved1; + USHORT wPIOTiming; + USHORT wDMATiming; + USHORT wBS; + USHORT wNumCurrentCyls; + USHORT wNumCurrentHeads; + USHORT wNumCurrentSectorsPerTrack; + ULONG ulCurrentSectorCapacity; + USHORT wMultSectorStuff; + ULONG ulTotalAddressableSectors; + USHORT wSingleWordDMA; + USHORT wMultiWordDMA; + BYTE bReserved[128]; +} IDSECTOR, *PIDSECTOR; + +// +// IDENTIFY data (from ATAPI driver source) +// + +#pragma pack(1) + +typedef struct _IDENTIFY_DATA { + USHORT GeneralConfiguration; // 00 00 + USHORT NumberOfCylinders; // 02 1 + USHORT Reserved1; // 04 2 + USHORT NumberOfHeads; // 06 3 + USHORT UnformattedBytesPerTrack; // 08 4 + USHORT UnformattedBytesPerSector; // 0A 5 + USHORT SectorsPerTrack; // 0C 6 + USHORT VendorUnique1[3]; // 0E 7-9 + USHORT SerialNumber[10]; // 14 10-19 + USHORT BufferType; // 28 20 + USHORT BufferSectorSize; // 2A 21 + USHORT NumberOfEccBytes; // 2C 22 + USHORT FirmwareRevision[4]; // 2E 23-26 + USHORT ModelNumber[20]; // 36 27-46 + UCHAR MaximumBlockTransfer; // 5E 47 + UCHAR VendorUnique2; // 5F + USHORT DoubleWordIo; // 60 48 + USHORT Capabilities; // 62 49 + USHORT Reserved2; // 64 50 + UCHAR VendorUnique3; // 66 51 + UCHAR PioCycleTimingMode; // 67 + UCHAR VendorUnique4; // 68 52 + UCHAR DmaCycleTimingMode; // 69 + USHORT TranslationFieldsValid:1; // 6A 53 + USHORT Reserved3:15; + USHORT NumberOfCurrentCylinders; // 6C 54 + USHORT NumberOfCurrentHeads; // 6E 55 + USHORT CurrentSectorsPerTrack; // 70 56 + ULONG CurrentSectorCapacity; // 72 57-58 + USHORT CurrentMultiSectorSetting; // 59 + ULONG UserAddressableSectors; // 60-61 + USHORT SingleWordDMASupport : 8; // 62 + USHORT SingleWordDMAActive : 8; + USHORT MultiWordDMASupport : 8; // 63 + USHORT MultiWordDMAActive : 8; + USHORT AdvancedPIOModes : 8; // 64 + USHORT Reserved4 : 8; + USHORT MinimumMWXferCycleTime; // 65 + USHORT RecommendedMWXferCycleTime; // 66 + USHORT MinimumPIOCycleTime; // 67 + USHORT MinimumPIOCycleTimeIORDY; // 68 + USHORT Reserved5[2]; // 69-70 + USHORT ReleaseTimeOverlapped; // 71 + USHORT ReleaseTimeServiceCommand; // 72 + USHORT MajorRevision; // 73 + USHORT MinorRevision; // 74 + USHORT Reserved6[50]; // 75-126 + USHORT SpecialFunctionsEnabled; // 127 + USHORT Reserved7[128]; // 128-255 +} IDENTIFY_DATA, *PIDENTIFY_DATA; + +#pragma pack() + +typedef struct _SRB_IO_CONTROL +{ + ULONG HeaderLength; + UCHAR Signature[8]; + ULONG Timeout; + ULONG ControlCode; + ULONG ReturnCode; + ULONG Length; +} SRB_IO_CONTROL, *PSRB_IO_CONTROL; +#define SMART_RCV_DRIVE_DATA CTL_CODE(IOCTL_DISK_BASE, 0x0022, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) +#ifndef NATIVE_CODE +using namespace System; +#endif +#include +using namespace std ; +typedef vector LISTDATA; +#define MAX_IDE_DRIVES 16 +class DiskInfo +{ + static DiskInfo* m_instance; + static char HardDriveSerialNumber [1024]; + LISTDATA m_list; +public: + static DiskInfo& GetDiskInfo() + { + if(m_instance == NULL) + m_instance = new DiskInfo(); + return *m_instance; + }; + + static void Destroy() + { + if(m_instance != NULL) + delete m_instance; + m_instance = NULL; + }; + ~DiskInfo(void); + int ReadIdeDriveAsScsiDriveInNT (void); + long LoadDiskInfo (); + int ReadPhysicalDriveInNTWithAdminRights (void); + int ReadPhysicalDriveInNTWithZeroRights (void); + int ReadDrivePortsInWin9X (void); + int ReadPhysicalDriveInNTUsingSmart (void); + + unsigned __int64 DriveSize (UINT drive); + UINT GetCount (){return m_list.size();}; + UINT BufferSize (UINT drive); +#ifndef NATIVE_CODE + String^ GetModelNumber (UINT drive); + String^ GetSerialNumber(UINT drive); + String^ GetRevisionNumber (UINT drive); + String^ GetDriveType (UINT drive); +#else + char* ModelNumber (UINT drive); + char* SerialNumber (UINT drive); + char* RevisionNumber (UINT drive); + char* DriveType (UINT drive); +#endif +private: + DiskInfo(void); + static char * flipAndCodeBytes (char * str); + static char * ConvertToString (WORD diskdata [256], int firstIndex, int lastIndex); + + static BOOL DoIDENTIFY (HANDLE hPhysicalDriveIOCTL, PSENDCMDINPARAMS pSCIP, + PSENDCMDOUTPARAMS pSCOP, BYTE bIDCmd, BYTE bDriveNum, + PDWORD lpcbBytesReturned); + BOOL AddIfNew(USHORT *pIdSector); +#ifndef NATIVE_CODE + String^ GetString (WORD* pdiskdata, int firstIndex, int lastIndex); +#endif +}; diff --git a/Utils/Guid.cpp b/Utils/Guid.cpp new file mode 100644 index 0000000..1900a5a --- /dev/null +++ b/Utils/Guid.cpp @@ -0,0 +1,23 @@ +#include "stdwx.h" +#include "Guid.h" +#include +#if defined(__WXMSW__) +#include +#endif + +wxString GenerateGuid() +{ +#if defined(__WXMSW__) + Uuid uid; + uid.Create(); + return wxString::Format(wxT("{%s}"), (const wxChar*)uid).Upper(); +#else + srand(time(NULL)); + return wxString::Format(wxT("{%x%x-%x-%x-%x-%x%x%x}"), + rand(), rand(), // Generates a 64-bit Hex number + rand(), // Generates a 32-bit Hex number + ((rand() & 0x0fff) | 0x4000), // Generates a 32-bit Hex number of the form 4xxx (4 indicates the UUID version) + rand() % 0x3fff + 0x8000, // Generates a 32-bit Hex number in the range [0x8000, 0xbfff] + rand(), rand(), rand()); // Generates a 96-bit Hex number +#endif +} diff --git a/Utils/Guid.h b/Utils/Guid.h new file mode 100644 index 0000000..4527a54 --- /dev/null +++ b/Utils/Guid.h @@ -0,0 +1,6 @@ +#ifndef _GUID_H +#define _GUID_H + +wxString GenerateGuid(); + +#endif // _GUID_H \ No newline at end of file diff --git a/Utils/JSONObjectFactory.cpp b/Utils/JSONObjectFactory.cpp new file mode 100644 index 0000000..e53308a --- /dev/null +++ b/Utils/JSONObjectFactory.cpp @@ -0,0 +1,10 @@ +#include "stdwx.h" +#include +#include "JSONObjectFactory.h" + +wxJSONValue JSONObjectFactory::Create(const wxString & action) +{ + wxJSONValue result; + result[JSON_KEY_ACTION] = action; + return result; +} \ No newline at end of file diff --git a/Utils/JSONObjectFactory.h b/Utils/JSONObjectFactory.h new file mode 100644 index 0000000..eaf9892 --- /dev/null +++ b/Utils/JSONObjectFactory.h @@ -0,0 +1,12 @@ +#pragma once + +class wxJSONValue; + +#define JSON_KEY_ACTION wxT("action") +#define JSON_KEY_PROCESSED wxT("processed") + +class JSONObjectFactory +{ +public: + static wxJSONValue Create(const wxString & action); +}; \ No newline at end of file diff --git a/Utils/MACAddressUtility.cpp b/Utils/MACAddressUtility.cpp new file mode 100644 index 0000000..2e63728 --- /dev/null +++ b/Utils/MACAddressUtility.cpp @@ -0,0 +1,214 @@ +#include "stdwx.h" +#include "MACAddressUtility.h" +#include + +#include + +#if defined(WIN32) || defined(UNDER_CE) +# include +# if defined(UNDER_CE) +# include +# endif +#elif defined(__APPLE__) +# include +# include +# include +# include +# include +#elif defined(LINUX) || defined(linux) +# include +# include +# include +# include +# include +#endif + +long MACAddressUtility::GetMACAddress(unsigned char * result) +{ + // Fill result with zeroes + memset(result, 0, 6); + // Call appropriate function for each platform +#if defined(WIN32) || defined(UNDER_CE) + return GetMACAddressMSW(result); +#elif defined(__APPLE__) + return GetMACAddressMAC(result); +#elif defined(LINUX) || defined(linux) + return GetMACAddressLinux(result); +#endif + // If platform is not supported then return error code + return -1; +} + +#if defined(WIN32) || defined(UNDER_CE) + +inline long MACAddressUtility::GetMACAddressMSW(unsigned char * result) +{ + +#if defined(UNDER_CE) + IP_ADAPTER_INFO AdapterInfo[16]; // Allocate information + DWORD dwBufLen = sizeof(AdapterInfo); // Save memory size of buffer + if(GetAdaptersInfo(AdapterInfo, &dwBufLen) == ERROR_SUCCESS) + { + memcpy(result, AdapterInfo->Address, 6); + } + else return -1; +#else + //UUID uuid; + //if(UuidCreateSequential(&uuid) == RPC_S_UUID_NO_ADDRESS) return -1; + //memcpy(result, (char*)(uuid.Data4+2), 6); +#endif + + for (int i = 0; i < 6; ++i) { + result[i] = 1; + } + return 0; + + return 0; +} + +#elif defined(__APPLE__) + +static kern_return_t FindEthernetInterfaces(io_iterator_t *matchingServices) +{ + kern_return_t kernResult; + CFMutableDictionaryRef matchingDict; + CFMutableDictionaryRef propertyMatchDict; + + matchingDict = IOServiceMatching(kIOEthernetInterfaceClass); + + if (NULL != matchingDict) + { + propertyMatchDict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + + if (NULL != propertyMatchDict) + { + CFDictionarySetValue(propertyMatchDict, CFSTR(kIOPrimaryInterface), kCFBooleanTrue); + CFDictionarySetValue(matchingDict, CFSTR(kIOPropertyMatchKey), propertyMatchDict); + CFRelease(propertyMatchDict); + } + } + kernResult = IOServiceGetMatchingServices(kIOMasterPortDefault, matchingDict, matchingServices); + return kernResult; +} + +static kern_return_t GetMACAddress(io_iterator_t intfIterator, UInt8 *MACAddress, UInt8 bufferSize) +{ + io_object_t intfService; + io_object_t controllerService; + kern_return_t kernResult = KERN_FAILURE; + + if (bufferSize < kIOEthernetAddressSize) { + return kernResult; + } + + bzero(MACAddress, bufferSize); + + while (intfService = IOIteratorNext(intfIterator)) + { + CFTypeRef MACAddressAsCFData; + + // IONetworkControllers can't be found directly by the IOServiceGetMatchingServices call, + // since they are hardware nubs and do not participate in driver matching. In other words, + // registerService() is never called on them. So we've found the IONetworkInterface and will + // get its parent controller by asking for it specifically. + + // IORegistryEntryGetParentEntry retains the returned object, so release it when we're done with it. + kernResult = IORegistryEntryGetParentEntry(intfService, + kIOServicePlane, + &controllerService); + + if (KERN_SUCCESS != kernResult) { + printf("IORegistryEntryGetParentEntry returned 0x%08x\n", kernResult); + } + else { + // Retrieve the MAC address property from the I/O Registry in the form of a CFData + MACAddressAsCFData = IORegistryEntryCreateCFProperty(controllerService, + CFSTR(kIOMACAddress), + kCFAllocatorDefault, + 0); + if (MACAddressAsCFData) { + CFShow(MACAddressAsCFData); // for display purposes only; output goes to stderr + + // Get the raw bytes of the MAC address from the CFData + CFDataGetBytes((CFDataRef)MACAddressAsCFData, CFRangeMake(0, kIOEthernetAddressSize), MACAddress); + CFRelease(MACAddressAsCFData); + } + + // Done with the parent Ethernet controller object so we release it. + (void) IOObjectRelease(controllerService); + } + + // Done with the Ethernet interface object so we release it. + (void) IOObjectRelease(intfService); + } + + return kernResult; +} + +long MACAddressUtility::GetMACAddressMAC(unsigned char * result) +{ + io_iterator_t intfIterator; + kern_return_t kernResult = KERN_FAILURE; + do + { + kernResult = ::FindEthernetInterfaces(&intfIterator); + if (KERN_SUCCESS != kernResult) break; + kernResult = ::GetMACAddress(intfIterator, (UInt8*)result, 6); + } + while(false); + (void) IOObjectRelease(intfIterator); +} + +#elif defined(LINUX) || defined(linux) + +long MACAddressUtility::GetMACAddressLinux(unsigned char * result) +{ + struct ifreq ifr; + struct ifreq *IFR; + struct ifconf ifc; + char buf[1024]; + int s, i; + int ok = 0; + + s = socket(AF_INET, SOCK_DGRAM, 0); + if (s == -1) + { + return -1; + } + + ifc.ifc_len = sizeof(buf); + ifc.ifc_buf = buf; + ioctl(s, SIOCGIFCONF, &ifc); + + IFR = ifc.ifc_req; + for (i = ifc.ifc_len / sizeof(struct ifreq); --i >= 0; IFR++) + { + strcpy(ifr.ifr_name, IFR->ifr_name); + if (ioctl(s, SIOCGIFFLAGS, &ifr) == 0) + { + if (! (ifr.ifr_flags & IFF_LOOPBACK)) + { + if (ioctl(s, SIOCGIFHWADDR, &ifr) == 0) + { + ok = 1; + break; + } + } + } + } + + shutdown(s, SHUT_RDWR); + if (ok) + { + bcopy( ifr.ifr_hwaddr.sa_data, result, 6); + } + else + { + return -1; + } + return 0; +} + +#endif diff --git a/Utils/MACAddressUtility.h b/Utils/MACAddressUtility.h new file mode 100644 index 0000000..2340c7d --- /dev/null +++ b/Utils/MACAddressUtility.h @@ -0,0 +1,18 @@ +#ifndef _MACADDRESS_UTILITY_H +#define _MACADDRESS_UTILITY_H + +class MACAddressUtility +{ +public: + static long GetMACAddress(unsigned char * result); +private: +#if defined(WIN32) || defined(UNDER_CE) + static long GetMACAddressMSW(unsigned char * result); +#elif defined(__APPLE__) + static long GetMACAddressMAC(unsigned char * result); +#elif defined(LINUX) || defined(linux) + static long GetMACAddressLinux(unsigned char * result); +#endif +}; + +#endif diff --git a/Utils/Win/Utils.vcxproj b/Utils/Win/Utils.vcxproj new file mode 100644 index 0000000..92f6b46 --- /dev/null +++ b/Utils/Win/Utils.vcxproj @@ -0,0 +1,235 @@ + + + + x64 + + + + Debug + x64 + + + Release + x64 + + + + {14D82890-BB7F-3748-8713-43304E91CFA1} + Win32Proj + 10.0.26100.0 + x64 + Utils + NoUpgrade + + + + StaticLibrary + Unicode + v143 + + + StaticLibrary + Unicode + v143 + + + + + + + + + + <_ProjectFileVersion>10.0.20506.1 + C:\Users\Admin\IFloor\projects\wxVideoProcessing\bin\lib\Debug\ + Utils.dir\Debug\ + Utils + .lib + C:\Users\Admin\IFloor\projects\wxVideoProcessing\bin\lib\Release\ + Utils.dir\Release\ + Utils + .lib + + + + C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\mswu;C:\Users\Admin\IFloor\libs\wxWidgets\include;C:\Users\Admin\IFloor\libs\opencv\build\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\Utils;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\MotionDetectorCore;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxJSON\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxxmlserializer\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxxmlserializer;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\MotionPrimitives;%(AdditionalIncludeDirectories) + $(IntDir) + EnableFastChecks + + + ProgramDatabase + 4996 + Sync + + + Disabled + stdcpp17 + + true + Disabled + Use + C:/Users/Admin/IFloor/projects/wxVideoProcessing/Utils/Win/CMakeFiles/Utils.dir/Debug/cmake_pch.hxx + C:/Users/Admin/IFloor/projects/wxVideoProcessing/Utils/Win/Utils.dir/Debug//cmake_pch.pch + + MultiThreadedDebugDLL + true + + + false + %(PreprocessorDefinitions);WIN32;_WINDOWS;__WXDEBUG__=1;WIN32_LEAN_AND_MEAN;WXUSINGDLL;UNICODE;_UNICODE;_CRT_SECURE_NO_DEPRECATE;_LIB;CMAKE_INTDIR="Debug" + $(IntDir) + false + + + %(PreprocessorDefinitions);WIN32;_DEBUG;_WINDOWS;__WXDEBUG__=1;WIN32_LEAN_AND_MEAN;WXUSINGDLL;UNICODE;_UNICODE;_CRT_SECURE_NO_DEPRECATE;_LIB;CMAKE_INTDIR=\"Debug\" + C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\mswu;C:\Users\Admin\IFloor\libs\wxWidgets\include;C:\Users\Admin\IFloor\libs\opencv\build\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\Utils;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\MotionDetectorCore;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxJSON\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxxmlserializer\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxxmlserializer;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\MotionPrimitives;%(AdditionalIncludeDirectories) + + + C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\mswu;C:\Users\Admin\IFloor\libs\wxWidgets\include;C:\Users\Admin\IFloor\libs\opencv\build\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\Utils;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\MotionDetectorCore;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxJSON\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxxmlserializer\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxxmlserializer;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\MotionPrimitives;%(AdditionalIncludeDirectories) + $(ProjectDir)/$(IntDir) + %(Filename).h + %(Filename).tlb + %(Filename)_i.c + %(Filename)_p.c + + + %(AdditionalOptions) /machine:x64 + + + + + C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\mswu;C:\Users\Admin\IFloor\libs\wxWidgets\include;C:\Users\Admin\IFloor\libs\opencv\build\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\Utils;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\MotionDetectorCore;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxJSON\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxxmlserializer\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxxmlserializer;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\MotionPrimitives;%(AdditionalIncludeDirectories) + $(IntDir) + Default + + + 4996 + Sync + + + AnySuitable + stdcpp17 + + true + MaxSpeed + Use + C:/Users/Admin/IFloor/projects/wxVideoProcessing/Utils/Win/CMakeFiles/Utils.dir/Release/cmake_pch.hxx + C:/Users/Admin/IFloor/projects/wxVideoProcessing/Utils/Win/Utils.dir/Release//cmake_pch.pch + + MultiThreadedDLL + true + + + false + %(PreprocessorDefinitions);WIN32;_WINDOWS;NDEBUG;WIN32_LEAN_AND_MEAN;WXUSINGDLL;UNICODE;_UNICODE;_CRT_SECURE_NO_DEPRECATE;_LIB;CMAKE_INTDIR="Release" + $(IntDir) + + + false + + + %(PreprocessorDefinitions);WIN32;_WINDOWS;NDEBUG;WIN32_LEAN_AND_MEAN;WXUSINGDLL;UNICODE;_UNICODE;_CRT_SECURE_NO_DEPRECATE;_LIB;CMAKE_INTDIR=\"Release\" + C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\mswu;C:\Users\Admin\IFloor\libs\wxWidgets\include;C:\Users\Admin\IFloor\libs\opencv\build\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\Utils;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\MotionDetectorCore;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxJSON\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxxmlserializer\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxxmlserializer;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\MotionPrimitives;%(AdditionalIncludeDirectories) + + + C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\mswu;C:\Users\Admin\IFloor\libs\wxWidgets\include;C:\Users\Admin\IFloor\libs\opencv\build\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\Utils;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\MotionDetectorCore;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxJSON\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxxmlserializer\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxxmlserializer;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\MotionPrimitives;%(AdditionalIncludeDirectories) + $(ProjectDir)/$(IntDir) + %(Filename).h + %(Filename).tlb + %(Filename)_i.c + %(Filename)_p.c + + + %(AdditionalOptions) /machine:x64 + + + + + Always + Building Custom Rule C:/Users/Admin/IFloor/projects/wxVideoProcessing/Utils/CMakeLists.txt + setlocal +"C:\Program Files\CMake\bin\cmake.exe" -SC:/Users/Admin/IFloor/projects/wxVideoProcessing/build -BC:/Users/Admin/IFloor/projects/wxVideoProcessing/build/Win --check-stamp-file C:/Users/Admin/IFloor/projects/wxVideoProcessing/Utils/Win/CMakeFiles/generate.stamp +if %errorlevel% neq 0 goto :cmEnd +:cmEnd +endlocal & call :cmErrorLevel %errorlevel% & goto :cmDone +:cmErrorLevel +exit /b %1 +:cmDone +if %errorlevel% neq 0 goto :VCEnd + %(AdditionalInputs) + C:\Users\Admin\IFloor\projects\wxVideoProcessing\Utils\Win\CMakeFiles\generate.stamp + false + Building Custom Rule C:/Users/Admin/IFloor/projects/wxVideoProcessing/Utils/CMakeLists.txt + setlocal +"C:\Program Files\CMake\bin\cmake.exe" -SC:/Users/Admin/IFloor/projects/wxVideoProcessing/build -BC:/Users/Admin/IFloor/projects/wxVideoProcessing/build/Win --check-stamp-file C:/Users/Admin/IFloor/projects/wxVideoProcessing/Utils/Win/CMakeFiles/generate.stamp +if %errorlevel% neq 0 goto :cmEnd +:cmEnd +endlocal & call :cmErrorLevel %errorlevel% & goto :cmDone +:cmErrorLevel +exit /b %1 +:cmDone +if %errorlevel% neq 0 goto :VCEnd + %(AdditionalInputs) + C:\Users\Admin\IFloor\projects\wxVideoProcessing\Utils\Win\CMakeFiles\generate.stamp + false + + + + + C:/Users/Admin/IFloor/projects/wxVideoProcessing/Utils/Win/CMakeFiles/Utils.dir/Debug/cmake_pch.hxx;%(ForcedIncludeFiles) + Create + C:/Users/Admin/IFloor/projects/wxVideoProcessing/Utils/Win/CMakeFiles/Utils.dir/Debug/cmake_pch.hxx + C:/Users/Admin/IFloor/projects/wxVideoProcessing/Utils/Win/Utils.dir/Debug//cmake_pch.pch + C:/Users/Admin/IFloor/projects/wxVideoProcessing/Utils/Win/CMakeFiles/Utils.dir/Release/cmake_pch.hxx;%(ForcedIncludeFiles) + Create + C:/Users/Admin/IFloor/projects/wxVideoProcessing/Utils/Win/CMakeFiles/Utils.dir/Release/cmake_pch.hxx + C:/Users/Admin/IFloor/projects/wxVideoProcessing/Utils/Win/Utils.dir/Release//cmake_pch.pch + + + C:/Users/Admin/IFloor/projects/wxVideoProcessing/Utils/Win/CMakeFiles/Utils.dir/Debug/cmake_pch.hxx;%(ForcedIncludeFiles) + C:/Users/Admin/IFloor/projects/wxVideoProcessing/Utils/Win/CMakeFiles/Utils.dir/Release/cmake_pch.hxx;%(ForcedIncludeFiles) + + + C:/Users/Admin/IFloor/projects/wxVideoProcessing/Utils/Win/CMakeFiles/Utils.dir/Debug/cmake_pch.hxx;%(ForcedIncludeFiles) + C:/Users/Admin/IFloor/projects/wxVideoProcessing/Utils/Win/CMakeFiles/Utils.dir/Release/cmake_pch.hxx;%(ForcedIncludeFiles) + + + C:/Users/Admin/IFloor/projects/wxVideoProcessing/Utils/Win/CMakeFiles/Utils.dir/Debug/cmake_pch.hxx;%(ForcedIncludeFiles) + C:/Users/Admin/IFloor/projects/wxVideoProcessing/Utils/Win/CMakeFiles/Utils.dir/Release/cmake_pch.hxx;%(ForcedIncludeFiles) + + + C:/Users/Admin/IFloor/projects/wxVideoProcessing/Utils/Win/CMakeFiles/Utils.dir/Debug/cmake_pch.hxx;%(ForcedIncludeFiles) + C:/Users/Admin/IFloor/projects/wxVideoProcessing/Utils/Win/CMakeFiles/Utils.dir/Release/cmake_pch.hxx;%(ForcedIncludeFiles) + + + C:/Users/Admin/IFloor/projects/wxVideoProcessing/Utils/Win/CMakeFiles/Utils.dir/Debug/cmake_pch.hxx;%(ForcedIncludeFiles) + C:/Users/Admin/IFloor/projects/wxVideoProcessing/Utils/Win/CMakeFiles/Utils.dir/Release/cmake_pch.hxx;%(ForcedIncludeFiles) + + + + + + + + + + C:/Users/Admin/IFloor/projects/wxVideoProcessing/Utils/Win/CMakeFiles/Utils.dir/Debug/cmake_pch.hxx;%(ForcedIncludeFiles) + C:/Users/Admin/IFloor/projects/wxVideoProcessing/Utils/Win/CMakeFiles/Utils.dir/Release/cmake_pch.hxx;%(ForcedIncludeFiles) + + + + + + + + + {6F4BDD20-5B99-3029-BDF0-7D719B283AF5} + ZERO_CHECK + false + Never + + + + + + \ No newline at end of file diff --git a/Utils/Win/Utils.vcxproj.filters b/Utils/Win/Utils.vcxproj.filters new file mode 100644 index 0000000..e7e1063 --- /dev/null +++ b/Utils/Win/Utils.vcxproj.filters @@ -0,0 +1,72 @@ + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Precompile Header File + + + Precompile Header File + + + + + + + + {4951AEE1-75F2-3C38-B593-C6BC8C809FB0} + + + {112D4227-BB51-3360-BB5B-5EA5DBA5A65F} + + + {05450AB2-CB7B-34EA-83DF-1EE86424EDF0} + + + diff --git a/Utils/wxFPValidator.cpp b/Utils/wxFPValidator.cpp new file mode 100644 index 0000000..6465cc6 --- /dev/null +++ b/Utils/wxFPValidator.cpp @@ -0,0 +1,265 @@ +#include "stdwx.h" +#ifdef linux +#include +#include +#endif +#include "wxFPValidator.h" +#include +#include +#include + +wxFPValidator::wxFPValidator(double * valPtr) +{ + Initialize(); + m_pDouble = valPtr; +} + +wxFPValidator::wxFPValidator(int * valPtr) +{ + Initialize(); + m_pInt = valPtr; +} + +wxFPValidator::wxFPValidator(long * valPtr) +{ + Initialize(); + m_pLong = valPtr; +} + +wxFPValidator::wxFPValidator(wxString * valPtr) +{ + Initialize(); + m_pString = valPtr; +} + +wxFPValidator::wxFPValidator(const wxFPValidator & validator) +: m_pDouble(validator.m_pDouble), m_pInt(validator.m_pInt), +m_pLong(validator.m_pLong), m_pString(validator.m_pString) +{ +} + +wxFPValidator::~wxFPValidator() +{ +} + +void wxFPValidator::Initialize() +{ + m_pDouble = nullptr; + m_pInt = nullptr; + m_pLong = nullptr; + m_pString = nullptr; +} + +wxValidator* wxFPValidator::Clone() const +{ + return new wxFPValidator(*this); +} + +bool wxFPValidator::TransferFromWindow() +{ + if(!m_validatorWindow) + { + return false; + } +#if wxUSE_TEXTCTRL + if (m_validatorWindow->IsKindOf(CLASSINFO(wxTextCtrl)) ) + { + wxTextCtrl * pControl = (wxTextCtrl*) m_validatorWindow; + if(m_pDouble) + { + return pControl->GetValue().ToDouble(m_pDouble); + } + else if(m_pInt) + { + double tmp; + bool res = pControl->GetValue().ToDouble(&tmp); + if(tmp >= INT_MIN && tmp <= INT_MAX && (fmod(tmp,1) == 0)) + { + *m_pInt = (int)tmp; + } else res = false; + return res; + } + else if(m_pLong) + { + double tmp; + bool res = pControl->GetValue().ToDouble(&tmp); + if(tmp >= LONG_MIN && tmp <= LONG_MAX && (fmod(tmp,1) == 0)) + { + *m_pLong = (long)tmp; + } else res = false; + return res; + } + else if(m_pString) + { + wxString tmp = pControl->GetValue(); + if(!tmp.IsEmpty()) + { + *m_pString = tmp; + return true; + } + return false; + } + } + else +#endif + +#if wxUSE_SPINCTRL + if (m_validatorWindow->IsKindOf(CLASSINFO(wxSpinCtrl)) ) + { + wxSpinCtrl * pControl = (wxSpinCtrl*) m_validatorWindow; + + if(m_pLong) + { + int tmp = pControl->GetValue(); + *m_pLong = (long)tmp; + return true; + } + } + else +#endif + +#if wxUSE_FILEPICKERCTRL + if (m_validatorWindow->IsKindOf(CLASSINFO(wxFilePickerCtrl))) + { + wxFilePickerCtrl * pControl = (wxFilePickerCtrl*) m_validatorWindow; + if (m_pString) + { + *m_pString = pControl->GetPath(); + return true; + } + else + return false; + } + else +#endif + return false; + return false; +} + +bool wxFPValidator::TransferToWindow() +{ + if(!m_validatorWindow) + { + return false; + } +#if wxUSE_SPINCTRL + if (m_validatorWindow->IsKindOf(CLASSINFO(wxSpinCtrl)) ) + { + wxSpinCtrl * pControl = (wxSpinCtrl *) m_validatorWindow; + if(m_pLong) + { + pControl->SetValue((int)*m_pLong); + return true; + } + } + else +#endif +#if wxUSE_TEXTCTRL + if (m_validatorWindow->IsKindOf(CLASSINFO(wxTextCtrl)) ) + { + wxTextCtrl * pControl = (wxTextCtrl*) m_validatorWindow; + if(m_pDouble) + { + pControl->SetValue(wxString::Format(wxT("%1.6f"), *m_pDouble)); + return true; + } + else if(m_pInt) + { + pControl->SetValue(wxString::Format(wxT("%i"), *m_pInt)); + return true; + } + else if(m_pLong) + { + pControl->SetValue(wxString::Format(wxT("%l"), *m_pLong)); + return true; + } + else if(m_pString) + { + pControl->SetValue(*m_pString); + return true; + } + } + else +#endif +#if wxUSE_FILEPICKERCTRL + if (m_validatorWindow->IsKindOf(CLASSINFO(wxFilePickerCtrl))) + { + wxFilePickerCtrl * pControl = (wxFilePickerCtrl*) m_validatorWindow; + if (m_pString) + { + pControl->SetPath(*m_pString); + return true; + } + else + return false; + } + else +#endif + return false; + return false; +} + +bool wxFPValidator::Validate(wxWindow * parent) +{ + if(!m_validatorWindow) + { + return false; + } +#if wxUSE_TEXTCTRL + if (m_validatorWindow->IsKindOf(CLASSINFO(wxTextCtrl)) ) + { + wxTextCtrl * pControl = (wxTextCtrl*) m_validatorWindow; + if(m_pDouble != NULL) + { + return pControl->GetValue().ToDouble(m_pDouble); + } + else if(m_pInt != NULL) + { + double tmp; + bool res = pControl->GetValue().ToDouble(&tmp); + if(tmp >= INT_MIN && tmp <= INT_MAX && (fmod(tmp,1) == 0)) + { + *m_pInt = (int)tmp; + } else res = false; + return res; + } + else if(m_pLong != NULL) + { + double tmp; + bool res = pControl->GetValue().ToDouble(&tmp); + if(tmp >= LONG_MIN && tmp <= LONG_MAX && (fmod(tmp,1) == 0)) + { + *m_pLong = (long)tmp; + } else res = false; + return res; + } + else if(m_pString != NULL) + { + return !pControl->GetValue().IsEmpty(); + } + } + else +#endif +#if wxUSE_TEXTCTRL + if (m_validatorWindow->IsKindOf(CLASSINFO(wxSpinCtrl)) ) + { + wxSpinCtrl * pControl = (wxSpinCtrl*) m_validatorWindow; + if(m_pLong != NULL) + { + int tmp = pControl->GetValue(); + *m_pLong = (long)tmp; + return true; + } + } + else +#endif +#if wxUSE_FILEPICKERCTRL + if (m_validatorWindow->IsKindOf(CLASSINFO(wxFilePickerCtrl))) + { + return true; + } + else +#endif + return false; + return false; +} diff --git a/Utils/wxFPValidator.h b/Utils/wxFPValidator.h new file mode 100644 index 0000000..41f31ac --- /dev/null +++ b/Utils/wxFPValidator.h @@ -0,0 +1,25 @@ +#ifndef _WX_FP_VALIDATOR_H +#define _WX_FP_VALIDATOR_H + +class wxFPValidator : public wxValidator +{ +protected: + double * m_pDouble; + int * m_pInt; + long * m_pLong; + wxString * m_pString; + void Initialize(); +public: + wxFPValidator(double * valPtr); + wxFPValidator(int * valPtr); + wxFPValidator(long * valPtr); + wxFPValidator(wxString * valPtr); + wxFPValidator(const wxFPValidator & validator); + ~wxFPValidator(); + virtual wxValidator* Clone() const; + virtual bool TransferFromWindow(); + virtual bool TransferToWindow(); + virtual bool Validate(wxWindow * parent); +}; + +#endif diff --git a/Utils/wxMACAddressUtility.h b/Utils/wxMACAddressUtility.h new file mode 100644 index 0000000..2035dbe --- /dev/null +++ b/Utils/wxMACAddressUtility.h @@ -0,0 +1,28 @@ +#ifndef _WX_MACADDRESS_UTILITY_H +#define _WX_MACADDRESS_UTILITY_H + +#include "MACAddressUtility.h" +#include + +class wxMACAddressUtility +{ +public: + static wxString GetMACAddress() + { + unsigned char result[6]; + if(MACAddressUtility::GetMACAddress(result) == 0) + { + return wxString::Format(wxT("%02X:%02X:%02X:%02X:%02X:%02X"), + (unsigned int)result[0], (unsigned int)result[1], (unsigned int)result[2], + (unsigned int)result[3], (unsigned int)result[4], (unsigned int)result[5]); + } + return wxEmptyString; + } + + static bool GetMACAddress(unsigned char * data) + { + return (MACAddressUtility::GetMACAddress(data) == 0); + } +}; + +#endif diff --git a/Utils/wxTemplateClientData.h b/Utils/wxTemplateClientData.h new file mode 100644 index 0000000..7bbb60a --- /dev/null +++ b/Utils/wxTemplateClientData.h @@ -0,0 +1,40 @@ +#ifndef _WXTEMPLATECLIENTDATA_H +#define _WXTEMPLATECLIENTDATA_H + +template +class wxTemplateClientData : public wxClientData +{ +public: + wxTemplateClientData(T * item, bool takeOwnership = true) + : m_Item(item) + , m_TakeOwnership(takeOwnership) + {} + ~wxTemplateClientData() + { + if (m_TakeOwnership) + wxDELETE(m_Item); + } + T * GetItem() + { + return m_Item; + } + void SetItem(T * item, bool takeOwnership = true) + { + if (m_TakeOwnership) + wxDELETE(m_Item); + m_Item = item; + m_TakeOwnership = takeOwnership; + } + T * DetachItem() + { + T * res = m_Item; + m_Item = NULL; + return res; + } + +private: + T * m_Item; + bool m_TakeOwnership; +}; + +#endif // _WXTEMPLATECLIENTDATA_H \ No newline at end of file diff --git a/VideoSourceDirectShow/CMakeLists.txt b/VideoSourceDirectShow/CMakeLists.txt new file mode 100644 index 0000000..ff02a32 --- /dev/null +++ b/VideoSourceDirectShow/CMakeLists.txt @@ -0,0 +1,117 @@ +set(SRCS + VideoSourceDirectShowPlugin.cpp + VideoSourceDirectShowApp.cpp + VideoSourceDirectShowExports.cpp + VideoGrabberDirectShow.cpp + VideoSourceDirectShow.def +) +set(HFILES + VideoSourceDirectShowPlugin.h + VideoGrabberDirectShow.h + VideoSourceDirectShowApp.h +) +set(DSNATIVE_SRCS + DSNative/Crossbar.cpp + DSNative/Device.cpp + DSNative/DeviceManager.cpp + DSNative/Format.cpp + DSNative/FrameGrabber.cpp + DSNative/DirectShow/Sources/DSCategoryHelper.cpp + DSNative/DirectShow/Sources/DSFileWriter.cpp + DSNative/DirectShow/Sources/DSFilter.cpp + DSNative/DirectShow/Sources/DSFilterGraphBase.cpp + DSNative/DirectShow/Sources/DSObjectBase.cpp + DSNative/DirectShow/Sources/DSSampleGrabber.cpp +) +set(DSNATIVE_HFILES + DSNative/Crossbar.h + DSNative/Device.h + DSNative/DeviceManager.h + DSNative/Format.h + DSNative/FrameGrabber.h + DSNative/DirectShow/Include/DSCategoryHelper.h + DSNative/DirectShow/Include/DSCommon.h + DSNative/DirectShow/Include/DSFilter.h + DSNative/DirectShow/Include/DSFilterGraphBase.h + DSNative/DirectShow/Include/DSFiltersDefinition.h + DSNative/DirectShow/Include/DSObjectBase.h +) + +source_group("DSNative\\DirectShow\\Sources" REGULAR_EXPRESSION DSNative/DirectShow/Sources/.*) +source_group("DSNative\\DirectShow\\Include" REGULAR_EXPRESSION DSNative/DirectShow/Include/.*) +source_group("DSNative\\Sources" REGULAR_EXPRESSION DSNative/.*\\.cpp) +source_group("DSNative\\Include" REGULAR_EXPRESSION DSNative/.*\\.h) + +set(LOCAL_INCLUDE_DIR "${PROJECT_ROOT_DIR}/include") + +set(INCLUDE_DIRECTORIES + ${LOCAL_INCLUDE_DIR} + DSNative + ${CMAKE_CURRENT_SOURCE_DIR}/../VideoSourcePluginBase + ${CMAKE_CURRENT_SOURCE_DIR}/../CommonPluginBase + ${CMAKE_CURRENT_SOURCE_DIR}/../ThirdParty/wxXS/include + ${CMAKE_CURRENT_SOURCE_DIR}/../ThirdParty/strmbas + ${OpenCV_INCLUDE_DIRS} +) + +if(WIN32) + set(INCLUDE_DIRECTORIES ${INCLUDE_DIRECTORIES} ${QEDIT_PATH}) + set(PREPROCESSOR_DEFINITIONS ${PREPROCESSOR_DEFINITIONS};/D_USRDLL;/DEXPORTS_API=) +endif() + +set(ALL_PROJECT_FILES ${SRCS} ${HFILES} ${DSNATIVE_SRCS} ${DSNATIVE_HFILES} "${LOCAL_INCLUDE_DIR}/stdwx.h" "${LOCAL_INCLUDE_DIR}/stdwx.cpp") + +add_definitions(${PREPROCESSOR_DEFINITIONS}) +include_directories(${INCLUDE_DIRECTORIES}) + +set(LIBRARY_NAME VideoSourceDirectShow) + +add_library(${LIBRARY_NAME} SHARED ${ALL_PROJECT_FILES}) + +target_link_libraries(${LIBRARY_NAME} + ${wxWidgets_LIBRARIES} + VideoSourcePluginBase + CommonPluginBase + MotionDetectorPluginBase + Utils + strmbas + strmiids + quartz + ole32 + oleaut32 + winmm +) + +add_dependencies(${LIBRARY_NAME} + VideoSourcePluginBase + CommonPluginBase + MotionDetectorPluginBase +) + + +if(WIN32) + set_target_properties(${LIBRARY_NAME} PROPERTIES + LINK_FLAGS "/NODEFAULTLIB:atlthunk.lib" + ) +endif() + +target_precompile_headers(${LIBRARY_NAME} + PRIVATE + "${LOCAL_INCLUDE_DIR}/stdwx.h" +) + +# Post Build Events +if(WIN32) + add_custom_command(TARGET ${LIBRARY_NAME} POST_BUILD + COMMAND ${CMAKE_COMMAND} ARGS -E copy_if_different "\"$(TargetPath)\"" "\"$(TargetDir)../$(TargetFileName)\"" + ) +endif() + +set(PLUGIN_TARGET_DIR "${OUTPUT_BIN_DIR}/plugins/video_source") + +add_custom_command(TARGET ${LIBRARY_NAME} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E make_directory "${PLUGIN_TARGET_DIR}" + COMMAND ${CMAKE_COMMAND} -E copy_if_different + "$" + "${PLUGIN_TARGET_DIR}/$" +) \ No newline at end of file diff --git a/VideoSourceDirectShow/DSNative/Crossbar.cpp b/VideoSourceDirectShow/DSNative/Crossbar.cpp new file mode 100644 index 0000000..902b79d --- /dev/null +++ b/VideoSourceDirectShow/DSNative/Crossbar.cpp @@ -0,0 +1,235 @@ +#include "stdwx.h" +#include "Crossbar.h" +#include "DirectShow\Include\DSCommon.h" + +CCrossBar::CCrossBar(IAMCrossbar * pCrossbar) + //: CFactoryObject() + : m_pCrossbar(pCrossbar) +{ + if (m_pCrossbar) + { + m_pCrossbar->AddRef(); + } +} + +CCrossBar::~CCrossBar() +{ + SAFERELEASE(m_pCrossbar); +} + +long CCrossBar::get_Count(BOOL bVideo) +{ + long lCount = 0; + if (m_pCrossbar) + { + long lInputs = 0; + long lOutputs = 0; + m_pCrossbar->get_PinCounts(&lOutputs,&lInputs); + if (lInputs > 0) + { + for (long i = 0; i < lInputs; i++) + { + if (IsVideo(i,FALSE)) + { + lCount++; + } + } + } + + } + return lCount; +} + +long CCrossBar::get_Active(BOOL bVideo) +{ + long lInput = -1; + if (m_pCrossbar) + { + long lInputs = 0; + long lOutputs = 0; + m_pCrossbar->get_PinCounts(&lOutputs,&lInputs); + long lPin = 0; + while (lPin < lOutputs) + { + if (IsVideo(lPin,TRUE)) + { + if (bVideo) break; + } + else + { + if (!bVideo) break; + } + lPin++; + } + m_pCrossbar->get_IsRoutedTo(lPin,&lInput); + long lCount = get_Count(bVideo); + if (lInput >= lCount) + { + lInput -= get_Count(!bVideo); + } + } + return lInput; +} + +LPWSTR CCrossBar::get_PinName(long lIndex,BOOL bVideo) +{ + if (m_pCrossbar) + { + long lRelatedIndex; + long lPhysicalType; + if (IsVideo(lIndex,FALSE)) + { + if (!bVideo) lIndex += get_Count(bVideo); + } + else + { + if (bVideo) lIndex += get_Count(!bVideo); + } + + if (S_OK == m_pCrossbar->get_CrossbarPinInfo(TRUE,lIndex,&lRelatedIndex,&lPhysicalType)) + { + switch (lPhysicalType) + { + case PhysConn_Video_Tuner: + return L"Video Tuner"; + case PhysConn_Video_Composite: + return L"Video Composite"; + case PhysConn_Video_SVideo: + return L"Video S-Video"; + case PhysConn_Video_RGB: + return L"Video RGB"; + case PhysConn_Video_YRYBY: + return L"Video YRYBY"; + case PhysConn_Video_SerialDigital: + return L"Video SerialDigital"; + case PhysConn_Video_ParallelDigital: + return L"Video ParallelDigital"; + case PhysConn_Video_SCSI: + return L"Video SCSI"; + case PhysConn_Video_AUX: + return L"Video AUX"; + case PhysConn_Video_1394: + return L"Video 1394"; + case PhysConn_Video_USB: + return L"Video USB"; + case PhysConn_Video_VideoDecoder: + return L"Video VideoDecoder"; + case PhysConn_Video_VideoEncoder: + return L"Video VideoEncoder"; + case PhysConn_Video_SCART: + return L"Video SCART"; + case PhysConn_Audio_Tuner: + return L"Audio Tuner"; + case PhysConn_Audio_Line: + return L"Audio Line"; + case PhysConn_Audio_Mic: + return L"Audio Mic"; + case PhysConn_Audio_AESDigital: + return L"Audio AESDigital"; + case PhysConn_Audio_SPDIFDigital: + return L"Audio SPDIFDigital"; + case PhysConn_Audio_SCSI: + return L"Audio SCSI"; + case PhysConn_Audio_AUX: + return L"Audio AUX"; + case PhysConn_Audio_1394: + return L"Audio 1394"; + case PhysConn_Audio_USB: + return L"Audio USB"; + case PhysConn_Audio_AudioDecoder: + return L"Audio AudioDecoder"; + } + } + } + return NULL; +} + +HRESULT CCrossBar::Route(long lIndex,BOOL bVideo) +{ + if (!m_pCrossbar) return E_FAIL; + if (IsVideo(lIndex,FALSE)) + { + if (!bVideo) lIndex += get_Count(bVideo); + } + else + { + if (bVideo) lIndex += get_Count(!bVideo); + } + long lInputs = 0; + long lOutputs = 0; + m_pCrossbar->get_PinCounts(&lOutputs,&lInputs); + long lPin = 0; + while (lPin < lOutputs) + { + if (IsVideo(lPin,TRUE)) + { + if (bVideo) break; + } + else + { + if (!bVideo) break; + } + lPin++; + } + return m_pCrossbar->Route(lPin,lIndex); +} + +BOOL CCrossBar::IsVideo(long lPin,BOOL bOutput) +{ + if (m_pCrossbar) + { + long lRelatedIndex; + long lPhysicalType; + if (S_OK == m_pCrossbar->get_CrossbarPinInfo(!bOutput,lPin,&lRelatedIndex,&lPhysicalType)) + { + return (lPhysicalType < PhysConn_Audio_Tuner); + } + } + return FALSE; +} + +long CCrossBar::get_InputByType(PhysicalConnectorType _type) +{ + if (m_pCrossbar) + { + long lInputs = 0; + long lOutputs = 0; + m_pCrossbar->get_PinCounts(&lOutputs,&lInputs); + if (lInputs > 0) + { + for (long i = 0; i < lInputs; i++) + { + long lRelatedIndex; + long lPhysicalType; + if (S_OK == m_pCrossbar->get_CrossbarPinInfo(TRUE,i,&lRelatedIndex,&lPhysicalType)) + { + if (lPhysicalType == _type) return i; + } + } + } + } + return -1; +} + +long CCrossBar::get_OutputByType(PhysicalConnectorType _type) +{ + if (m_pCrossbar) + { + long lInputs = 0; + long lOutputs = 0; + m_pCrossbar->get_PinCounts(&lOutputs,&lInputs); + if (lOutputs > 0) + { + for (long i = 0; i < lOutputs; i++) + { + long lRelatedIndex; + long lPhysicalType; + if (S_OK == m_pCrossbar->get_CrossbarPinInfo(FALSE,i,&lRelatedIndex,&lPhysicalType)) + { + if (lPhysicalType == _type) return i; + } + } + } + } + return -1; +} \ No newline at end of file diff --git a/VideoSourceDirectShow/DSNative/Crossbar.h b/VideoSourceDirectShow/DSNative/Crossbar.h new file mode 100644 index 0000000..9d41d0e --- /dev/null +++ b/VideoSourceDirectShow/DSNative/Crossbar.h @@ -0,0 +1,28 @@ +#ifndef __Crossbar_H__ +#define __Crossbar_H__ +///////////////////////////////////////////////////// +//#include "ClassFactory.h" +#include "stdwx.h" +#include +///////////////////////////////////////////////////// +class EXPORTS_API CCrossBar //: public CFactoryObject +{ + friend class CFrameGrabber; +private: + IAMCrossbar * m_pCrossbar; +private: + BOOL IsVideo(long lPin,BOOL bOutput); +public: + long get_Count(BOOL bVideo = TRUE); + long get_Active(BOOL bVideo = TRUE); + long get_InputByType(PhysicalConnectorType _type); + long get_OutputByType(PhysicalConnectorType _type); + LPWSTR get_PinName(long lIndex,BOOL bVideo = TRUE); + HRESULT Route(long lIndex,BOOL bVideo = TRUE); +private: + CCrossBar(IAMCrossbar * pCrossbar); +public: + virtual ~CCrossBar(); +}; +///////////////////////////////////////////////////// +#endif // __Crossbar_H__ \ No newline at end of file diff --git a/VideoSourceDirectShow/DSNative/Device.cpp b/VideoSourceDirectShow/DSNative/Device.cpp new file mode 100644 index 0000000..cf668cc --- /dev/null +++ b/VideoSourceDirectShow/DSNative/Device.cpp @@ -0,0 +1,124 @@ + +#include "stdwx.h" +#include "Device.h" +#include "FrameGrabber.h" +#include "Format.h" + +CDevice::CDevice(LPOLESTR szMoniker) + //: CFactoryObject() + : m_lpwstrMoniker(NULL) +{ + m_pFilter = new DSFilterBase(szMoniker); + IBaseFilter * pFilter = NULL; + BOOL bFailed = TRUE; + if (m_pFilter->GetIBaseFilter(&pFilter)) + { + bFailed = (pFilter == NULL); + } + if (bFailed) + { + SAFEDELETE(m_pFilter); + } + else + { + set_Moniker(szMoniker); + } +} + +CDevice::CDevice(REFCLSID iid) + //: CFactoryObject() + : m_lpwstrMoniker(NULL) +{ + m_pFilter = new DSFilterBase(iid); + IBaseFilter * pFilter = NULL; + BOOL bFailed = TRUE; + if (m_pFilter->GetIBaseFilter(&pFilter)) + { + bFailed = (pFilter == NULL); + } + if (bFailed) + { + SAFEDELETE(m_pFilter); + } +} + +CDevice::CDevice(DSFilterBase * pFilter) + //: CFactoryObject() + : m_pFilter(pFilter) + , m_lpwstrMoniker(NULL) +{ + IBaseFilter * pBaseFilter = NULL; + BOOL bFailed = TRUE; + if (m_pFilter->GetIBaseFilter(&pBaseFilter)) + { + bFailed = (pBaseFilter == NULL); + } + if (bFailed) + { + SAFEDELETE(m_pFilter); + } +} + +CDevice::~CDevice() +{ + SAFEDELETEARRAY(m_lpwstrMoniker); + SAFEDELETE(m_pFilter); +} + +HRESULT CDevice::ShowProperties(HWND hwndParent) +{ + if (m_pFilter) + { + return m_pFilter->ShowProperties(hwndParent); + } + return E_FAIL; +} + +BOOL CDevice::HaveProperties() +{ + if (m_pFilter) + { + return m_pFilter->HaveProperties()? TRUE : FALSE; + } + return FALSE; +} +LPWSTR CDevice::get_Moniker() +{ + return m_lpwstrMoniker; +} + +void CDevice::set_Moniker(LPCWSTR lpwstrMoniker) +{ + SAFEDELETEARRAY(m_lpwstrMoniker); + if (lpwstrMoniker) + { + int cch = (int)(wcslen(lpwstrMoniker) + 1); + m_lpwstrMoniker = new WCHAR[cch]; + ZeroMemory(m_lpwstrMoniker,cch * sizeof(WCHAR)); + wcscpy_s(m_lpwstrMoniker, cch, lpwstrMoniker); + } +} +CFrameGrabber * CDevice::CreateFrameGrabber() +{ + return new CFrameGrabber(this); +} + +CVideoFormats * CDevice::get_VideoFormats() +{ + CVideoFormats * pFormats = NULL; + if (m_pFilter) + { + IAMStreamConfig * pStreamConfig = NULL; + IPin * pPin = m_pFilter->GetPinByCategory(PIN_CATEGORY_CAPTURE); + if (pPin) + { + if (SUCCEEDED(pPin->QueryInterface(IID_IAMStreamConfig,(void**)&pStreamConfig))) + { + pFormats = new CVideoFormats(pStreamConfig); + pStreamConfig->Release(); + } + pPin->Release(); + } + } + return pFormats; +} \ No newline at end of file diff --git a/VideoSourceDirectShow/DSNative/Device.h b/VideoSourceDirectShow/DSNative/Device.h new file mode 100644 index 0000000..774a8c6 --- /dev/null +++ b/VideoSourceDirectShow/DSNative/Device.h @@ -0,0 +1,36 @@ +#ifndef __Device_H__ +#define __Device_H__ +///////////////////////////////////////////////////// +//#include "ClassFactory.h" +#include "stdwx.h" +#include "DirectShow/Include/DSCategoryHelper.h" +#include "DirectShow/Include/DSFilter.h" +///////////////////////////////////////////////////// +class CDeviceManager; +class CFrameGrabber; +class CVideoFormats; +///////////////////////////////////////////////////// +class EXPORTS_API CDevice //: public CFactoryObject +{ + friend class CDeviceManager; + friend class CFrameGrabber; +private: + LPWSTR m_lpwstrMoniker; + DSFilterBase * m_pFilter; +private: + CDevice(DSFilterBase * pFilter); +public: + LPWSTR get_Moniker(); + void set_Moniker(LPCWSTR lpwstrMoniker); + CFrameGrabber * CreateFrameGrabber(); + CVideoFormats * get_VideoFormats(); +public: + HRESULT ShowProperties(HWND hwndParent = NULL); + BOOL HaveProperties(); +public: + CDevice(LPOLESTR szMoniker); + CDevice(REFCLSID iid); + virtual ~CDevice(); +}; +///////////////////////////////////////////////////// +#endif //__Device_H__ \ No newline at end of file diff --git a/VideoSourceDirectShow/DSNative/DeviceManager.cpp b/VideoSourceDirectShow/DSNative/DeviceManager.cpp new file mode 100644 index 0000000..c2cedc0 --- /dev/null +++ b/VideoSourceDirectShow/DSNative/DeviceManager.cpp @@ -0,0 +1,132 @@ +#include "stdwx.h" +#include "DeviceManager.h" +///////////////////////////////////////////////////// +CDeviceManager::CDeviceManager(REFCLSID _category) +//: CFactoryObject() +: m_pCategory(NULL) +, m_clsidCategory(_category) +{ + m_pCategory = new DSCategory(m_clsidCategory); +} + +CDeviceManager::~CDeviceManager() +{ + SAFEDELETE(m_pCategory); +} + +///////////////////////////////////////////////////// +long CDeviceManager::get_Count() +{ + if (m_pCategory) + { + return m_pCategory->get_Count(); + } + return 0; +} + +///////////////////////////////////////////////////// +CDevice * CDeviceManager::get_DeviceByMoniker(LPCWSTR lpwstrMonikerName) +{ + CDevice * pDevice = new CDevice((LPOLESTR)lpwstrMonikerName); + if (pDevice->m_pFilter) + { + return pDevice; + } + else + { + delete pDevice; + } + return NULL; +} + +///////////////////////////////////////////////////// +LPWSTR CDeviceManager::get_DeviceName(const long lIndex) +{ + if (m_pCategory) + { + long lNumber = lIndex; + return m_pCategory->GetName(lNumber); + } + return NULL; +} + +LPWSTR CDeviceManager::get_DeviceMonikerString(const long lIndex) +{ + if (m_pCategory) + { + long lNumber = lIndex; + return m_pCategory->GetDeviceMonikerString(lNumber); + } + return NULL; +} +///////////////////////////////////////////////////// +CDevice * CDeviceManager::get_Device(const long lIndex) +{ + if (m_pCategory) + { + DSFilterBase * pFilter = m_pCategory->GetDevice(lIndex); + if (pFilter) + { + CDevice * pDevice = new CDevice(pFilter); + if (pDevice) + { + LPWSTR lpwstrMoniker = get_DeviceMonikerString(lIndex); + if (lpwstrMoniker) + { + pDevice->set_Moniker(lpwstrMoniker); + SAFEDELETEARRAY(lpwstrMoniker); + } + else + { + delete pDevice; + pDevice = NULL; + } + } + return pDevice; + } + } + return NULL; +} + +CDevice * CDeviceManager::get_Device(LPCWSTR lpwstrName) +{ + if (m_pCategory) + { + long lCount = m_pCategory->get_Count(); + while (lCount > 0) + { + lCount--; + LPWSTR lpwstrDevice = m_pCategory->GetName(lCount); + bool found = wcscmp(lpwstrDevice, lpwstrName) == 0; + SAFEDELETEARRAY(lpwstrDevice); + if (found) + return get_Device(lCount); + } + } + return NULL; +} + +long CDeviceManager::get_DeviceIndex(LPCWSTR lpwstrMonikerName) +{ + if (m_pCategory) + { + long lCount = m_pCategory->get_Count(); + while (lCount > 0) + { + lCount--; + LPWSTR lpwstrDevice = get_DeviceMonikerString(lCount); + bool found = wcscmp(lpwstrDevice, lpwstrMonikerName) == 0; + SAFEDELETEARRAY(lpwstrDevice); + if (found) + return lCount; + } + } + return -1; +} + +void CDeviceManager::RefreshDevices() +{ + SAFEDELETE(m_pCategory); + m_pCategory = new DSCategory(m_clsidCategory); +} +///////////////////////////////////////////////////// \ No newline at end of file diff --git a/VideoSourceDirectShow/DSNative/DeviceManager.h b/VideoSourceDirectShow/DSNative/DeviceManager.h new file mode 100644 index 0000000..edcaeef --- /dev/null +++ b/VideoSourceDirectShow/DSNative/DeviceManager.h @@ -0,0 +1,29 @@ +#ifndef __DeviceManager_H__ +#define __DeviceManager_H__ +///////////////////////////////////////////////////// +//#include "ClassFactory.h" +#include "Device.h" +#include "DirectShow/Include/DSCategoryHelper.h" +///////////////////////////////////////////////////// +class EXPORTS_API CDeviceManager//: public CFactoryObject +{ +private: + DSCategory * m_pCategory; + REFCLSID m_clsidCategory; +public: + static CDevice * get_DeviceByMoniker(LPCWSTR lpwstrMonikerName); +public: + long get_Count(); + CDevice * get_Device(LPCWSTR lpwstrName); + CDevice * get_Device(const long lIndex); + LPWSTR get_DeviceName(const long lIndex); + LPWSTR get_DeviceMonikerString(const long lIndex); + long get_DeviceIndex(LPCWSTR lpwstrMonikerName); + + void RefreshDevices(); +public: + CDeviceManager(REFCLSID _category); + virtual ~CDeviceManager(); +}; +///////////////////////////////////////////////////// +#endif // __DeviceManager_H__ \ No newline at end of file diff --git a/VideoSourceDirectShow/DSNative/DirectShow/Include/DSCategoryHelper.h b/VideoSourceDirectShow/DSNative/DirectShow/Include/DSCategoryHelper.h new file mode 100644 index 0000000..e3913dc --- /dev/null +++ b/VideoSourceDirectShow/DSNative/DirectShow/Include/DSCategoryHelper.h @@ -0,0 +1,64 @@ +#ifndef __DS_CATEGORY_HELPER_H__ +#define __DS_CATEGORY_HELPER_H__ + +#include "DSFilter.h" + +// Base category helper class +class DSCategory : public DSObjectBase +{ +protected: + HRESULT GetFiltersCount(long & lCount); + HRESULT GetFilterName(long lNumber, LPWSTR * plpwstrDevName); + HRESULT GetDeviceFilter(long lNumber, IBaseFilter ** ppOutFilter); + HRESULT GetFilterMonikerString(long lNumber, LPWSTR * plpwstrDevMoniker); +protected: + ICreateDevEnum * m_pDevEnum; + IEnumMoniker * m_pEnumMoniker; + REFCLSID m_clsidCategory; +public: + virtual void Release(); + virtual HRESULT QueryInterface(const IID &riid,void ** ppvObject); +public: + long get_Count(); + LPWSTR GetName(long lNumber); + LPWSTR GetDeviceMonikerString(long lNumber); +public: + virtual DSFilterBase * GetDevice(long lNumber); +public: + DSCategory(REFCLSID clsidCategory); + virtual ~DSCategory(); +}; + +// Base video capture category helper class +class DSVideoCaptureCategory: public DSCategory +{ +public: + DSVideoCaptureCategory():DSCategory(CLSID_VideoInputDeviceCategory) {} + virtual ~DSVideoCaptureCategory() {} +}; + +// Base audio capture category helper class +class DSAudioCaptureCategory: public DSCategory +{ +public: + DSAudioCaptureCategory():DSCategory(CLSID_AudioInputDeviceCategory) {} + virtual ~DSAudioCaptureCategory() {} +}; + +// Base video compressor category helper class +class DSVideoCompressorCategory: public DSCategory +{ +public: + DSVideoCompressorCategory():DSCategory(CLSID_VideoCompressorCategory) {} + virtual ~DSVideoCompressorCategory() {} +}; + +// Base audio compressor category helper class +class DSAudioCompressorCategory: public DSCategory +{ +public: + DSAudioCompressorCategory():DSCategory(CLSID_AudioCompressorCategory) {} + virtual ~DSAudioCompressorCategory() {} +}; + +#endif diff --git a/VideoSourceDirectShow/DSNative/DirectShow/Include/DSCommon.h b/VideoSourceDirectShow/DSNative/DirectShow/Include/DSCommon.h new file mode 100644 index 0000000..3de16eb --- /dev/null +++ b/VideoSourceDirectShow/DSNative/DirectShow/Include/DSCommon.h @@ -0,0 +1,96 @@ +////////////////////////////////////////////////////////// +// Helper macro definitions +// Created by Sonic +// for any additional features contact sonic@YoooMedia.com +///////////////////////////////////////////////////////// + +#ifndef __ATLBASE_H__ +#include +#endif + +#ifndef __YoooCommonDefs_H__ +#define __YoooCommonDefs_H__ + +// Safe releasing COM object +#define SAFERELEASE(comobj) \ +if (comobj) \ +{ \ + comobj->Release(); \ + comobj = NULL; \ +} \ + +// Deleting object +#define SAFEDELETE(obj) \ +if (obj) \ +{ \ + delete obj; \ + obj = NULL; \ +} + +// Free memory +#define SAFECOTASKFREE(obj) \ +if (obj) \ +{ \ + CoTaskMemFree((LPVOID)obj); \ + obj = NULL; \ +} + +// delete an array +#define SAFEDELETEARRAY(obj) \ +if (obj) \ +{ \ + delete[] obj; \ + obj = NULL; \ +} + +// Safe Check HRESULT value +#define SAFECHECK(hr) \ +ASSERT( S_OK == hr ); \ +if (FAILED(hr)) \ +{ \ + return hr; \ +} + +// Safe Check HRESULT value and close interfaces +#define SAFECHECKCLOSE(hr) \ +ASSERT( S_OK == hr ); \ +if (FAILED(hr)) \ +{ \ + CloseInterfaces(); \ + return hr; \ +} + +#define CHECKRET(object,ret) \ +if (!object) return ret + +#define CHECKRETBOOL(object) \ +CHECKRET(object,false) + +#define CHECKRETHR(object) \ +CHECKRET(object,E_POINTER) + +#define THROWHR(hr) \ +if (hr != S_OK) throw hr; + +#define SAFERELEASEDC(hdc,hwnd) \ +if (hdc) \ +{ \ + ReleaseDC(hwnd,hdc); \ + hdc = NULL; \ +} + +#define DELETEGDIOBJECT(obj) \ +if (obj) \ +{ \ + DeleteObject(obj); \ + obj = NULL; \ +} + +#define DELETEGDIDC(hdc) \ +if (hdc) \ +{ \ + DeleteDC(hdc); \ + hdc = NULL; \ +} + +#endif // __YoooCommonDefs_H__ \ No newline at end of file diff --git a/VideoSourceDirectShow/DSNative/DirectShow/Include/DSFilter.h b/VideoSourceDirectShow/DSNative/DirectShow/Include/DSFilter.h new file mode 100644 index 0000000..f99f60b --- /dev/null +++ b/VideoSourceDirectShow/DSNative/DirectShow/Include/DSFilter.h @@ -0,0 +1,52 @@ + +#ifndef __DS_FILTER_HELPER_H__ +#define __DS_FILTER_HELPER_H__ + +#include "DSObjectBase.h" + +class DSFilterBase : public virtual DSObjectBase +{ +protected: + IBaseFilter * m_pFilter; +protected: + HRESULT GetFilterByMoniker(LPOLESTR szMoniker); +public: + virtual void Release(); + virtual HRESULT QueryInterface(const IID &riid,void ** ppvObject); +public: + bool GetIBaseFilter(IBaseFilter ** pIBaseFilter); + HRESULT AddToFilterGraph(IGraphBuilder * pGraphBuilder); + HRESULT RemoveFromFilterGraph(IGraphBuilder * pGraphBuilder); +public: + HRESULT ShowProperties(HWND hwndParent = NULL); + bool HaveProperties(); + IPin * GetPinByDirection(PIN_DIRECTION direction,long lPinNumber); + IPin * GetPinByCategory(REFGUID guidPinCategory); + + HRESULT GetOutputPinCapabilitiesCount(long lPinNumber, int *piCount); + HRESULT GetOutputPinCapabilitiesCount(IPin * pPin, int *piCount); + HRESULT GetOutputPinCapabilitiesCount(REFGUID guidPinCategory, int *piCount); + + HRESULT GetOutputPinCapabilities(REFGUID guidPinCategory, int iCapNumber, AM_MEDIA_TYPE ** ppmt); + HRESULT GetOutputPinCapabilities(long lPinNumber, int iCapNumber, AM_MEDIA_TYPE ** ppmt); + HRESULT GetOutputPinCapabilities(IPin * pPin, int iCapNumber, AM_MEDIA_TYPE ** ppmt); + + HRESULT GetOutputPinFormat(long lPinNumber, AM_MEDIA_TYPE ** ppmt); + HRESULT GetOutputPinFormat(IPin * pPin, AM_MEDIA_TYPE ** ppmt); + HRESULT GetOutputPinFormat(REFGUID guidPinCategory, AM_MEDIA_TYPE ** ppmt); + + HRESULT SetOutputPinFormat(long lPinNumber, AM_MEDIA_TYPE * pmt); + HRESULT SetOutputPinFormat(IPin * pPin, AM_MEDIA_TYPE * pmt); + HRESULT SetOutputPinFormat(REFGUID guidPinCategory, AM_MEDIA_TYPE * pmt); + +protected: + DSFilterBase(); +public: + DSFilterBase(LPOLESTR szMoniker); + DSFilterBase(REFCLSID iid); + virtual ~DSFilterBase(); +public: + DSFilterBase(IBaseFilter * pFilter, bool bReleaseOnDestroy = false); +}; + +#endif diff --git a/VideoSourceDirectShow/DSNative/DirectShow/Include/DSFilterGraphBase.h b/VideoSourceDirectShow/DSNative/DirectShow/Include/DSFilterGraphBase.h new file mode 100644 index 0000000..f26d3bf --- /dev/null +++ b/VideoSourceDirectShow/DSNative/DirectShow/Include/DSFilterGraphBase.h @@ -0,0 +1,100 @@ + +#ifndef __DS_FILTERGRAPH_BASE_H__ +#define __DS_FILTERGRAPH_BASE_H__ + +#include "DSFilter.h" + +#define WM_GRAPH_NOTIFY WM_USER + 0x100 +#define WM_SAMPLE_CALLBACK WM_USER + 0x101 +#define WM_VIDEO_SIZE WM_USER + 0x102 +//#define USE_DS_OBJECT + +#ifdef _DEBUG + //#define ROT_DEBUG +#else + #ifdef ROT_DEBUG + #undef ROT_DEBUG + #endif +#endif + +#ifdef ROT_DEBUG + +class DSYoooROTFilter : public DSFilterBase +{ +public: + DSYoooROTFilter():DSFilterBase(L"@device:sw:{1C41D721-C2B6-46B2-A3E7-2845ED43A8F5}\\{CECB095D-B900-496F-9FDF-9C6EF331468F}") + { + } +}; + + +#endif + +class DSFilterGraphBase +#ifdef USE_DS_OBJECT + : public DSObjectBase +#endif +{ +private: +#ifdef ROT_DEBUG + DSYoooROTFilter * m_pDSYoooROTFilter; +#endif +protected: + IGraphBuilder * m_pGraphBuilder; // graph for rendering + IMediaControl * m_pMediaControl; // main media + IMediaEventEx * m_pMediaEventEx; // to check the stop + IVideoWindow * m_pVideoWindow; + IBasicAudio * m_pBasicAudio; + IBasicVideo * m_pBasicVideo; + IMediaSeeking * m_pMediaSeeking; +protected: + HWND m_hWnd; + HWND m_hWndNotify; + bool m_bShouldPreview; + bool m_bMute; + int m_iVolume; +protected: + virtual HRESULT InitInterfaces(); + virtual HRESULT CloseInterfaces(); + virtual void SettingUpVideoWindow(); + virtual HRESULT PreparePlayback(); +public: + virtual HRESULT ProcessGraphMessage(); + virtual HRESULT ProcessStepMessage(); +protected: + HRESULT SetVolume(int nVolume); + IBaseFilter * GetVideoRenderer(); + IBaseFilter * GetAudioRenderer(); +public: + int get_Volume(); + void set_Volume(int nVolume); + bool get_Mute(); + void set_Mute(bool bMute); + bool IsAudioSupported(); + bool IsRunning(); + bool IsPaused(); + HWND get_PreviewWindow(); + void set_PreviewWindow(HWND hWnd); + HWND get_NotifyWindow(); + void set_NotifyWindow(HWND hWnd); + bool get_ShouldPreview(); + void set_ShouldPreview(bool bShouldPreview); + RECT get_VideoSize(); + RECT get_SourceVideoSize(); + void set_VideoSize(RECT _rect); + REFERENCE_TIME get_Position(); + void set_Position(REFERENCE_TIME _time); + REFERENCE_TIME get_Duration(); +public: + STDMETHOD(Start)(); + STDMETHOD(Stop)(); + STDMETHOD(Pause)(); + + void ResizeVideoWindow(); + void ResizeVideoWindow(RECT rect); +public: + DSFilterGraphBase(); + virtual ~DSFilterGraphBase(); +}; + +#endif \ No newline at end of file diff --git a/VideoSourceDirectShow/DSNative/DirectShow/Include/DSFiltersDefinition.h b/VideoSourceDirectShow/DSNative/DirectShow/Include/DSFiltersDefinition.h new file mode 100644 index 0000000..8600316 --- /dev/null +++ b/VideoSourceDirectShow/DSNative/DirectShow/Include/DSFiltersDefinition.h @@ -0,0 +1,243 @@ + +#ifndef __DS_FILTERS_DEFINITION_H__ +#define __DS_FILTERS_DEFINITION_H__ + +#include "DSFilter.h" + +#pragma include_alias( "dxtrans.h", "qedit.h" ) +#define __IDxtCompositor_INTERFACE_DEFINED__ +#define __IDxtAlphaSetter_INTERFACE_DEFINED__ +#define __IDxtJpeg_INTERFACE_DEFINED__ +#define __IDxtKey_INTERFACE_DEFINED__ +#include + +#define SAMPLE_GETTED WM_USER + 500 + +class DecompressorType +{ +public: + typedef enum { + None = 0, + AVI = 1, + MJPEG = 2 + } DECOMPTYPE, * LPDECOMPTYPE; +private: + DecompressorType() {} + ~DecompressorType() {} +}; + +// Inf Tee Filter +class DSInfTeeFilter : public DSFilterBase +{ +public: + DSInfTeeFilter():DSFilterBase(CLSID_InfTee) + { + } +}; + +class DSSmartTeeFilter : public DSFilterBase +{ +public: + DSSmartTeeFilter():DSFilterBase(CLSID_SmartTee) + { + } +}; + +class DSFileWritterBase: public DSFilterBase +{ +public: + HRESULT SetOutputFileName(LPWSTR lpwstrFileName); +public: + DSFileWritterBase(LPOLESTR szMoniker); + DSFileWritterBase(REFCLSID iid); +}; + +// File Writer Filter +class DSFileWriter: public DSFileWritterBase +{ +public: + DSFileWriter():DSFileWritterBase(CLSID_FileWriter) + { + } +}; + +// DSound Renderer +class DSDSoundRendererFilter : public DSFilterBase +{ +public: + DSDSoundRendererFilter():DSFilterBase(CLSID_DSoundRender) + { + } +}; + +// Sample Grabber +class CSampleCallBack +{ +protected: + GUID m_FormatType; + + LPBYTE m_lpFormat; + +public: + double m_dTimeStamp; + virtual void OnFormat(GUID guidFormatType,BYTE *pbFormat,long lFormatLength) + { + SAFECOTASKFREE(m_lpFormat); + m_FormatType = guidFormatType; + m_lpFormat = (LPBYTE)CoTaskMemAlloc(lFormatLength); + CopyMemory(m_lpFormat,pbFormat,lFormatLength); + } + virtual void OnSample(BYTE *pBuffer,long lBufferLen)PURE; +public: + CSampleCallBack() + :m_lpFormat(NULL) + ,m_FormatType(GUID_NULL) + { + + } + virtual ~CSampleCallBack() + { + SAFECOTASKFREE(m_lpFormat); + } + virtual HRESULT GetSampleTime(double& time)PURE; +}; + +class DSSampleGrabberFilter : public DSFilterBase +{ +private: + class SampleGrabberCB:public ISampleGrabberCB + { + public: + HWND m_hwnd; + UINT m_Message; + private: + ISampleGrabber * m_pISampleGrabber; + HANDLE m_hMutex; + BOOL m_bGrabbing; + BYTE * m_pbtBuffer; + long m_lBufferSize; + DWORD m_dwWidth; + DWORD m_dwHeight; + CSampleCallBack * m_pCallBack; + BOOL m_bFormatSetted; + public: + STDMETHODIMP_(ULONG) AddRef() { return 2; } + STDMETHODIMP_(ULONG) Release() { return 1; } + STDMETHODIMP QueryInterface(REFIID riid, void ** ppv) + { + if( riid == IID_ISampleGrabberCB || riid == IID_IUnknown ) + { + *ppv = (void *) static_cast ( this ); + return NOERROR; + } + return E_NOINTERFACE; + } + public: + virtual HRESULT STDMETHODCALLTYPE BufferCB(double SampleTime,BYTE *pBuffer,long BufferLen); + virtual HRESULT STDMETHODCALLTYPE SampleCB(double SampleTime,IMediaSample *pSample); + public: + HRESULT SetCallback(CSampleCallBack * pCallBack); + BOOL Grab(BYTE ** ppbtBuffer,long * plBufferSize); + public: + SampleGrabberCB(ISampleGrabber * pISampleGrabber) + : m_pISampleGrabber(pISampleGrabber) + , m_hMutex(INVALID_HANDLE_VALUE) + , m_bGrabbing(FALSE) + , m_pbtBuffer(NULL) + , m_lBufferSize(0) + , m_dwWidth(0) + , m_dwHeight(0) + , m_pCallBack(NULL) + , m_bFormatSetted(FALSE) + { + m_hMutex = CreateMutex(NULL,FALSE,NULL); + } + ~SampleGrabberCB() + { + m_pCallBack = NULL; + SAFECOTASKFREE(m_pbtBuffer); + m_lBufferSize = 0; + CloseHandle(m_hMutex); + m_hMutex = INVALID_HANDLE_VALUE; + } + }; +protected: + SampleGrabberCB * m_pCB; + ISampleGrabber * m_pISampleGrabber; +public: + BOOL Grab(BYTE ** ppbtBuffer,long * plBufferSize); + HRESULT SetMediaType(AM_MEDIA_TYPE mt); + HRESULT GetMediaType(AM_MEDIA_TYPE * pmt); + HRESULT SetCallback(CSampleCallBack * pCallBack); + HRESULT SetCallback(ISampleGrabberCB *pCallback,long WhichMethodToCallback = 0); + HRESULT SetOneShot(BOOL bOneShot = FALSE); + void SetCallbackWnd(HWND wnd, UINT message); + bool IsFrameReady(); +public: + DSSampleGrabberFilter(); + virtual ~DSSampleGrabberFilter(); +}; + +// WM ASF Writer +class DSWMAsfWritter : public DSFileWritterBase +{ +public: + DSWMAsfWritter(): DSFileWritterBase(CLSID_WMAsfWriter) + { + } +}; + +// Default Video Renderer +class DSVideoRenderer: public DSFilterBase +{ +public: + DSVideoRenderer():DSFilterBase(CLSID_VideoRenderer) + { + } +}; + +// VMR Renderer +class DSVMRRenderer: public DSFilterBase +{ +public: + DSVMRRenderer():DSFilterBase(CLSID_VideoMixingRenderer9) + { + } +}; + +// Avi Muxer +class DSAviMuxFilter: public DSFilterBase +{ +public: + DSAviMuxFilter():DSFilterBase(CLSID_AviDest) + { + } +}; + +// Avi Decompressor +class DSAviDecompressorFilter: public DSFilterBase +{ +public: + DSAviDecompressorFilter():DSFilterBase(CLSID_AVIDec) + { + } +}; + +// MJPEG Decompressor Filter +class DSMJPEGDecompressorFilter: public DSFilterBase +{ +public: + DSMJPEGDecompressorFilter():DSFilterBase(CLSID_MjpegDec) + { + } +}; + +class DSNullRenderer: public DSFilterBase +{ +public: + DSNullRenderer():DSFilterBase(CLSID_NullRenderer) + { + } +}; + +#endif \ No newline at end of file diff --git a/VideoSourceDirectShow/DSNative/DirectShow/Include/DSObjectBase.h b/VideoSourceDirectShow/DSNative/DirectShow/Include/DSObjectBase.h new file mode 100644 index 0000000..3eae5ce --- /dev/null +++ b/VideoSourceDirectShow/DSNative/DirectShow/Include/DSObjectBase.h @@ -0,0 +1,25 @@ + +#ifndef __DS_OBJECT_BASE_H__ +#define __DS_OBJECT_BASE_H__ + +#include +#include "DSCommon.h" +#include + +class DSObjectBase +{ +protected: + bool m_bReleaseOnDestroy; +public: + bool get_ReleaseOnDestroy(); + void set_ReleaseOnDestroy(bool bReleaseOnDestroy); +public: + virtual void Release(); + virtual HRESULT QueryInterface(const IID &riid,void ** ppvObject); +protected: + DSObjectBase(); +public: + virtual ~DSObjectBase(); +}; + +#endif \ No newline at end of file diff --git a/VideoSourceDirectShow/DSNative/DirectShow/Sources/DSCategoryHelper.cpp b/VideoSourceDirectShow/DSNative/DirectShow/Sources/DSCategoryHelper.cpp new file mode 100644 index 0000000..b21912f --- /dev/null +++ b/VideoSourceDirectShow/DSNative/DirectShow/Sources/DSCategoryHelper.cpp @@ -0,0 +1,242 @@ + +#include "../Include/DSCategoryHelper.h" + +DSCategory::DSCategory(REFCLSID clsidCategory) + : DSObjectBase() + , m_clsidCategory(clsidCategory) + , m_pDevEnum(NULL) + , m_pEnumMoniker(NULL) +{ + HRESULT hr = CoCreateInstance( + CLSID_SystemDeviceEnum, + NULL, + CLSCTX_INPROC_SERVER, + IID_ICreateDevEnum, + (void**) &m_pDevEnum); + + ASSERT(hr == S_OK && m_pDevEnum != NULL); + + hr = m_pDevEnum->CreateClassEnumerator( + m_clsidCategory, + &m_pEnumMoniker, + 0); + + //ASSERT(hr == S_OK && m_pEnumMoniker != NULL); +} + +DSCategory::~DSCategory() +{ +} + +void DSCategory::Release() +{ + SAFERELEASE(m_pDevEnum); + SAFERELEASE(m_pEnumMoniker); +} + +HRESULT DSCategory::QueryInterface(const IID &riid,void ** ppvObject) +{ + HRESULT hr = E_NOINTERFACE; + if (m_pEnumMoniker) + { + hr = m_pEnumMoniker->QueryInterface(riid,ppvObject); + } + if (hr == S_OK) return hr; + if (m_pDevEnum) return m_pDevEnum->QueryInterface(riid,ppvObject); + return E_NOINTERFACE; +} + +HRESULT DSCategory::GetFiltersCount(long & lCount) +{ + lCount = 0; + if (!m_pEnumMoniker) return NOERROR; + m_pEnumMoniker->Reset(); + while(1) + { + ULONG cFetched = 0; + CComPtr< IMoniker > pMoniker; + + HRESULT hr = m_pEnumMoniker->Next(1, &pMoniker, &cFetched); + if(!pMoniker || hr != S_OK) break; + + CComPtr< IPropertyBag > pBag; + hr = pMoniker->BindToStorage(0, 0, IID_IPropertyBag, (void**) &pBag); + if(!FAILED(hr)) + { + lCount++; + pBag.Release(); + } + pMoniker.Release(); + } + + return S_OK; +} + +HRESULT DSCategory::GetFilterName(long lNumber, LPWSTR * plpwstrDevName) +{ + * plpwstrDevName = NULL; + HRESULT hr = S_OK; + if (!m_pEnumMoniker) return E_FAIL; + m_pEnumMoniker->Reset(); + while(1) + { + ULONG cFetched = 0; + CComPtr< IMoniker > pMoniker; + + hr = m_pEnumMoniker->Next(1, &pMoniker, &cFetched); + if(!pMoniker || hr != S_OK) break; + if (lNumber > 0) + { + lNumber--; + pMoniker.Release(); + continue; + } + + CComPtr< IPropertyBag > pBag; + hr = pMoniker->BindToStorage(0, 0, IID_IPropertyBag, (void**) &pBag); + if(!FAILED(hr)) + { + VARIANT var; + VariantInit(&var); + //var.vt = VT_BSTR; + //hr = pBag->Read(L"FilterData",&var, NULL); + //if (S_OK == hr) VariantClear(&var); + //hr = pBag->Read(L"CLSID",&var, NULL); + //if (S_OK == hr) VariantClear(&var); + hr = pBag->Read(L"FriendlyName",&var, NULL); + if(hr == NOERROR) + { + *plpwstrDevName = new WCHAR[MAX_PATH]; + wcscpy_s(*plpwstrDevName, MAX_PATH, var.bstrVal); + //SysFreeString(var.bstrVal); + VariantClear(&var); + } + pBag.Release(); + } + pMoniker.Release(); + break; + } + if (!(*plpwstrDevName)) hr = E_FAIL; + return hr; +} + +HRESULT DSCategory::GetDeviceFilter(long lNumber, IBaseFilter ** ppOutFilter) +{ + HRESULT hr = S_OK; + (*ppOutFilter) = NULL; + if (!m_pEnumMoniker) return E_FAIL; + m_pEnumMoniker->Reset(); + while(1) + { + ULONG cFetched = 0; + CComPtr< IMoniker > pMoniker; + + hr = m_pEnumMoniker->Next(1, &pMoniker, &cFetched); + if(!pMoniker || hr != S_OK) break; + if (lNumber > 0) + { + lNumber--; + pMoniker.Release(); + continue; + } + CComPtr< IPropertyBag > pBag; + hr = pMoniker->BindToStorage(0, 0, IID_IPropertyBag, (void**) &pBag); + if(SUCCEEDED(hr)) + { + VARIANT var; + VariantInit(&var); + hr = pBag->Read(L"FriendlyName",&var, NULL); + if(FAILED(hr)) + { + hr = pBag->Read(L"Description",&var, NULL); + } + VariantClear(&var); + pBag.Release(); + if (SUCCEEDED(hr)) + { + hr = BindMoniker(pMoniker,0,IID_IBaseFilter, (void**)ppOutFilter); + //hr = pMoniker->BindToObject(0, 0, IID_IBaseFilter, (void**)ppOutFilter); + } + } + pMoniker.Release(); + break; + } + if (hr == S_OK && (*ppOutFilter) == NULL) hr = E_FAIL; + return hr; +} + +HRESULT DSCategory::GetFilterMonikerString(long lNumber, LPWSTR * plpwstrDevMoniker) +{ + * plpwstrDevMoniker = NULL; + HRESULT hr = S_OK; + if (!m_pEnumMoniker) return E_FAIL; + m_pEnumMoniker->Reset(); + while(1) + { + ULONG cFetched = 0; + CComPtr< IMoniker > pMoniker; + + hr = m_pEnumMoniker->Next(1, &pMoniker, &cFetched); + if(!pMoniker || hr != S_OK) break; + if (lNumber > 0) + { + lNumber--; + pMoniker.Release(); + continue; + } + /* + CLSID _clsid; + hr = pMoniker->GetClassID(&_clsid); + _clsid = GUID_NULL; + + */ + CComPtr pBindCtx = NULL; + hr = CreateBindCtx(NULL,&pBindCtx); + + LPOLESTR lpolestrTemp = NULL; + hr = pMoniker->GetDisplayName(pBindCtx,NULL,&lpolestrTemp); + + *plpwstrDevMoniker = new WCHAR[MAX_PATH]; + wcscpy_s(*plpwstrDevMoniker, MAX_PATH, lpolestrTemp); + //*plpwstrDevMoniker + + pBindCtx.Release(); + pMoniker.Release(); + break; + } + if (!(*plpwstrDevMoniker)) hr = E_FAIL; + return hr; +} +long DSCategory::get_Count() +{ + long lResult = -1; + if (GetFiltersCount(lResult) == S_OK) return lResult; + return -1; +} + +LPWSTR DSCategory::GetName(long lNumber) +{ + LPWSTR lpwstrName = NULL; + if (GetFilterName(lNumber,(WCHAR**)&lpwstrName) == S_OK) return lpwstrName; + return NULL; +} + +DSFilterBase * DSCategory::GetDevice(long lNumber) +{ + IBaseFilter * pFilter = NULL; + if (SUCCEEDED(GetDeviceFilter(lNumber, &pFilter))) + { + return new DSFilterBase(pFilter); + } + return NULL; +} + +LPWSTR DSCategory::GetDeviceMonikerString(long lNumber) +{ + LPWSTR lpwstrName = NULL; + if (GetFilterMonikerString(lNumber,(WCHAR**)&lpwstrName) == S_OK) + { + return lpwstrName; + } + return NULL; +} diff --git a/VideoSourceDirectShow/DSNative/DirectShow/Sources/DSFileWriter.cpp b/VideoSourceDirectShow/DSNative/DirectShow/Sources/DSFileWriter.cpp new file mode 100644 index 0000000..9a9e29c --- /dev/null +++ b/VideoSourceDirectShow/DSNative/DirectShow/Sources/DSFileWriter.cpp @@ -0,0 +1,26 @@ + +#include "../Include/DSFiltersDefinition.h" + +DSFileWritterBase::DSFileWritterBase(LPOLESTR szMoniker) + : DSFilterBase(szMoniker) +{ + m_bReleaseOnDestroy = true; +} + +DSFileWritterBase::DSFileWritterBase(REFCLSID iid) + : DSFilterBase(iid) +{ + m_bReleaseOnDestroy = true; +} + +HRESULT DSFileWritterBase::SetOutputFileName(LPWSTR lpwstrFileName) +{ + if (!m_pFilter) return E_POINTER; + CComPtr pSinkFilter; + HRESULT hr = m_pFilter->QueryInterface(IID_IFileSinkFilter,(void**)&pSinkFilter); + SAFECHECK(hr); + DeleteFileW(lpwstrFileName); + hr = pSinkFilter->SetFileName(lpwstrFileName, NULL); + SAFECHECK(hr); + return S_OK; +} \ No newline at end of file diff --git a/VideoSourceDirectShow/DSNative/DirectShow/Sources/DSFilter.cpp b/VideoSourceDirectShow/DSNative/DirectShow/Sources/DSFilter.cpp new file mode 100644 index 0000000..3e3570a --- /dev/null +++ b/VideoSourceDirectShow/DSNative/DirectShow/Sources/DSFilter.cpp @@ -0,0 +1,392 @@ + +#include "../Include/DSFilter.h" + +//#define USE_NON_CRITICAL_ASSERTS + +DSFilterBase::DSFilterBase() + : DSObjectBase() + , m_pFilter(NULL) +{ +} + +DSFilterBase::DSFilterBase(LPOLESTR szMoniker) + : DSObjectBase() + , m_pFilter(NULL) +{ + HRESULT hr = GetFilterByMoniker(szMoniker); +#ifdef USE_NON_CRITICAL_ASSERTS + ASSERT(hr == S_OK); +#endif +} + +DSFilterBase::DSFilterBase(REFCLSID iid) + : DSObjectBase() + , m_pFilter(NULL) +{ + HRESULT hr = CoCreateInstance(iid, NULL, CLSCTX_INPROC, IID_IBaseFilter, (void **)&m_pFilter); +#ifdef USE_NON_CRITICAL_ASSERTS + ASSERT(hr == S_OK); +#endif +} + +DSFilterBase::DSFilterBase(IBaseFilter * pFilter, bool bReleaseOnDestroy) + : DSObjectBase() + , m_pFilter(pFilter) +{ + m_bReleaseOnDestroy = bReleaseOnDestroy; +#ifdef USE_NON_CRITICAL_ASSERTS + ASSERT(m_pFilter != NULL); +#endif +} + +DSFilterBase::~DSFilterBase() +{ + if (m_bReleaseOnDestroy) Release(); +} + +HRESULT DSFilterBase::QueryInterface(const IID &riid,void ** ppvObject) +{ + return m_pFilter->QueryInterface(riid,ppvObject); +} + +void DSFilterBase::Release() +{ + SAFERELEASE(m_pFilter); +} + +bool DSFilterBase::HaveProperties() +{ + CComPtr< ISpecifyPropertyPages > pPropertyPages; + HRESULT hr = S_OK; + hr = m_pFilter->QueryInterface(IID_ISpecifyPropertyPages,(void **)&pPropertyPages); + return SUCCEEDED(hr); +} + +HRESULT DSFilterBase::ShowProperties(HWND hwndParent) +{ + CComPtr< ISpecifyPropertyPages > pPropertyPages; + HRESULT hr = S_OK; + hr = m_pFilter->QueryInterface(IID_ISpecifyPropertyPages,(void **)&pPropertyPages); + if (FAILED(hr)) return hr; + CAUUID caGUID; + hr = pPropertyPages->GetPages(&caGUID); + if (FAILED(hr)) return hr; + + IUnknown * pFilterUnk; + hr = m_pFilter->QueryInterface(IID_IUnknown, (void **)&pFilterUnk); + if (FAILED(hr)) return hr; + + FILTER_INFO fiInfo; + hr = m_pFilter->QueryFilterInfo(&fiInfo); + if (FAILED(hr)) return hr; + + hr = OleCreatePropertyFrame(hwndParent,0,0,fiInfo.achName,1, &pFilterUnk, caGUID.cElems,caGUID.pElems,NULL, 0, NULL); + SAFERELEASE(pFilterUnk); + CoTaskMemFree(caGUID.pElems); + SAFERELEASE(fiInfo.pGraph); + return hr; +} +bool DSFilterBase::GetIBaseFilter(IBaseFilter ** pIBaseFilter) +{ + *pIBaseFilter = m_pFilter; + return true; +} + +HRESULT DSFilterBase::AddToFilterGraph(IGraphBuilder * pGraphBuilder) +{ + ASSERT(pGraphBuilder != NULL); + return pGraphBuilder->AddFilter(m_pFilter,NULL); +} + +HRESULT DSFilterBase::RemoveFromFilterGraph(IGraphBuilder * pGraphBuilder) +{ + ASSERT(pGraphBuilder != NULL); + return pGraphBuilder->RemoveFilter(m_pFilter); +} + +IPin * DSFilterBase::GetPinByDirection(PIN_DIRECTION direction,long lPinNumber) +{ + IPin * pPin = NULL; + HRESULT hr = S_OK; + + CComPtr pEnumPins; + + hr = m_pFilter->EnumPins(&pEnumPins); +#ifdef USE_NON_CRITICAL_ASSERTS + ASSERT( S_OK == hr ); +#endif + if(!FAILED(hr)) + { + while (pEnumPins->Next(1, &pPin, NULL) == S_OK) + { + PIN_DIRECTION pinDirection; + hr = pPin->QueryDirection(&pinDirection); + ASSERT( S_OK == hr ); + if(pinDirection != direction) + { + pPin->Release(); + continue; + } + if(lPinNumber > 0) + { + lPinNumber--; + pPin->Release(); + continue; + } + break; + } + pEnumPins.Release(); + } + return pPin; +} + + +IPin * DSFilterBase::GetPinByCategory(REFGUID guidPinCategory) +{ + IPin * pPin = NULL; + HRESULT hr = S_OK; + + CComPtr pEnumPins; + + hr = m_pFilter->EnumPins(&pEnumPins); +#ifdef USE_NON_CRITICAL_ASSERTS + ASSERT( S_OK == hr ); +#endif + if(!FAILED(hr)) + { + while (pEnumPins->Next(1, &pPin, NULL) == S_OK) + { + CComPtr pPropertySet; + hr = pPin->QueryInterface(IID_IKsPropertySet,(void**)&pPropertySet); + if (hr == S_OK) + { + GUID guidOutput; + DWORD dwReturned; + hr = pPropertySet->Get(AMPROPSETID_Pin,AMPROPERTY_PIN_CATEGORY,NULL,NULL,(PVOID)&guidOutput,sizeof(GUID),&dwReturned); + if (hr == S_OK && guidOutput == guidPinCategory) + { + pPropertySet.Release(); + break; + } + pPropertySet.Release(); + } + pPin->Release(); + pPin = NULL; + } + pEnumPins.Release(); + } + return pPin; +} + +HRESULT DSFilterBase::GetFilterByMoniker(LPOLESTR szMoniker) +{ + HRESULT hr = S_OK; + + m_pFilter = NULL; + + CComPtr pBindCtx; + hr = CreateBindCtx(0, &pBindCtx); + ASSERT( S_OK == hr ); + + ULONG chEaten = 0; + CComPtr pIMoniker; + hr = MkParseDisplayName(pBindCtx, szMoniker, &chEaten, &pIMoniker); + pBindCtx.Release(); + if (SUCCEEDED(hr)) + { + // Get the display name, or bind to a DirectShow filter. + hr = pIMoniker->BindToObject(0, 0, IID_IBaseFilter, (void**)&m_pFilter); +#ifdef USE_NON_CRITICAL_ASSERTS + ASSERT( S_OK == hr ); +#endif + if (FAILED(hr)) m_pFilter = NULL; + pIMoniker.Release(); + } + if(m_pFilter == NULL && SUCCEEDED(hr)) hr = -1; + return hr; +} + + +HRESULT DSFilterBase::GetOutputPinCapabilitiesCount(IPin * pPin, int *piCount) +{ + *piCount = -1; + HRESULT hr; + /* + CComPtr pEnum; + hr = pPin->EnumMediaTypes(&pEnum); + if (hr == S_OK && pEnum != NULL) + { + pEnum->Reset(); + AM_MEDIA_TYPE * pmt = NULL; + int iCount = 0; + while (hr == S_OK) + { + hr = pEnum->Next(1,&pmt, NULL); + if (hr == S_OK && pmt != NULL) + { + iCount++; + DeleteMediaType(pmt); + } + } + hr = S_OK; + if (iCount != 0) *piCount = iCount; + pEnum.Release(); + } + */ + + CComPtr pStreamConfig; + hr = pPin->QueryInterface(IID_IAMStreamConfig,(void**)&pStreamConfig); + if (hr == S_OK && pStreamConfig != NULL) + { + int iSize; + hr = pStreamConfig->GetNumberOfCapabilities(piCount,&iSize); + pStreamConfig.Release(); + } + + return hr; +} + +HRESULT DSFilterBase::GetOutputPinCapabilitiesCount(long lPinNumber, int *piCount) +{ + *piCount = -1; + IPin * pPin = GetPinByDirection(PINDIR_OUTPUT, lPinNumber); + if (pPin == NULL) return E_FAIL; + HRESULT hr = GetOutputPinCapabilitiesCount(pPin,piCount); + pPin->Release(); + return hr; +} + +HRESULT DSFilterBase::GetOutputPinCapabilitiesCount(REFGUID guidPinCategory, int *piCount) +{ + *piCount = -1; + IPin * pPin = GetPinByCategory(guidPinCategory); + if (pPin == NULL) return E_FAIL; + HRESULT hr = GetOutputPinCapabilitiesCount(pPin,piCount); + pPin->Release(); + return hr; +} + +HRESULT DSFilterBase::GetOutputPinCapabilities(long lPinNumber, int iCapNumber, AM_MEDIA_TYPE ** ppmt) +{ + IPin * pPin = GetPinByDirection(PINDIR_OUTPUT, lPinNumber); + if (pPin == NULL) return E_FAIL; + HRESULT hr = GetOutputPinCapabilities(pPin,iCapNumber,ppmt); + pPin->Release(); + return hr; +} + + +HRESULT DSFilterBase::GetOutputPinCapabilities(IPin * pPin, int iCapNumber, AM_MEDIA_TYPE ** ppmt) +{ + *ppmt = NULL; + HRESULT hr; +/* + CComPtr pEnum; + hr = pPin->EnumMediaTypes(&pEnum); + if (hr == S_OK && pEnum != NULL) + { + pEnum->Reset(); + AM_MEDIA_TYPE * pmt = NULL; + while (hr == S_OK) + { + hr = pEnum->Next(1,&pmt, NULL); + if (iCapNumber > 0) + { + iCapNumber--; + DeleteMediaType(pmt); + } + else + { + *ppmt = pmt; + } + } + if (*ppmt == NULL) hr = E_FAIL; else hr = S_OK; + pEnum.Release(); + } +*/ + CComPtr pStreamConfig; + hr = pPin->QueryInterface(IID_IAMStreamConfig,(void**)&pStreamConfig); + if (hr == S_OK && pStreamConfig != NULL) + { + int iCount, iSize; + hr = pStreamConfig->GetNumberOfCapabilities(&iCount,&iSize); + if (iCapNumber >= 0 && iCount > iCapNumber) + { + LPBYTE pbtSCC = (LPBYTE)CoTaskMemAlloc(iSize); + hr = pStreamConfig->GetStreamCaps(iCapNumber,ppmt,pbtSCC); + CoTaskMemFree(pbtSCC); + } + pStreamConfig.Release(); + } + return hr; +} + +HRESULT DSFilterBase::GetOutputPinCapabilities(REFGUID guidPinCategory, int iCapNumber, AM_MEDIA_TYPE ** ppmt) +{ + IPin * pPin = GetPinByCategory(guidPinCategory); + if (pPin == NULL) return E_FAIL; + HRESULT hr = GetOutputPinCapabilities(pPin,iCapNumber,ppmt); + pPin->Release(); + return hr; +} + +HRESULT DSFilterBase::GetOutputPinFormat(IPin * pPin, AM_MEDIA_TYPE ** ppmt) +{ + *ppmt = NULL; + CComPtr pStreamConfig; + HRESULT hr = pPin->QueryInterface(IID_IAMStreamConfig,(void**)&pStreamConfig); + if (hr == S_OK && pStreamConfig != NULL) + { + hr = pStreamConfig->GetFormat(ppmt); + pStreamConfig.Release(); + } + return hr; +} + +HRESULT DSFilterBase::GetOutputPinFormat(long lPinNumber, AM_MEDIA_TYPE ** ppmt) +{ + IPin * pPin = GetPinByDirection(PINDIR_OUTPUT, lPinNumber); + if (pPin == NULL) return E_FAIL; + HRESULT hr = GetOutputPinFormat(pPin,ppmt); + pPin->Release(); + return hr; +} + +HRESULT DSFilterBase::GetOutputPinFormat(REFGUID guidPinCategory, AM_MEDIA_TYPE ** ppmt) +{ + IPin * pPin = GetPinByCategory(guidPinCategory); + if (pPin == NULL) return E_FAIL; + HRESULT hr = GetOutputPinFormat(pPin,ppmt); + pPin->Release(); + return hr; +} + +HRESULT DSFilterBase::SetOutputPinFormat(long lPinNumber, AM_MEDIA_TYPE * pmt) +{ + IPin * pPin = GetPinByDirection(PINDIR_OUTPUT, lPinNumber); + if (pPin == NULL) return E_FAIL; + HRESULT hr = SetOutputPinFormat(pPin,pmt); + pPin->Release(); + return hr; +} + +HRESULT DSFilterBase::SetOutputPinFormat(IPin * pPin, AM_MEDIA_TYPE * pmt) +{ + CComPtr pStreamConfig; + HRESULT hr = pPin->QueryInterface(IID_IAMStreamConfig,(void**)&pStreamConfig); + if (hr == S_OK && pStreamConfig != NULL) + { + hr = pStreamConfig->SetFormat(pmt); + pStreamConfig.Release(); + } + return hr; +} + +HRESULT DSFilterBase::SetOutputPinFormat(REFGUID guidPinCategory, AM_MEDIA_TYPE * pmt) +{ + IPin * pPin = GetPinByCategory(guidPinCategory); + if (pPin == NULL) return E_FAIL; + HRESULT hr = SetOutputPinFormat(pPin,pmt); + pPin->Release(); + return hr; +} diff --git a/VideoSourceDirectShow/DSNative/DirectShow/Sources/DSFilterGraphBase.cpp b/VideoSourceDirectShow/DSNative/DirectShow/Sources/DSFilterGraphBase.cpp new file mode 100644 index 0000000..6c95fd7 --- /dev/null +++ b/VideoSourceDirectShow/DSNative/DirectShow/Sources/DSFilterGraphBase.cpp @@ -0,0 +1,485 @@ + +#include "../Include/DSFilterGraphBase.h" + +DSFilterGraphBase::DSFilterGraphBase() + : m_pGraphBuilder(NULL) +#ifdef USE_DS_OBJECT + , DSObjectBase() +#endif + , m_pMediaControl(NULL) + , m_pVideoWindow(NULL) + , m_pMediaEventEx(NULL) + , m_pMediaSeeking(NULL) + , m_hWnd(NULL) + , m_hWndNotify(NULL) + , m_bShouldPreview(true) + , m_pBasicAudio(NULL) + , m_pBasicVideo(NULL) + , m_bMute(false) + , m_iVolume(0) +#ifdef ROT_DEBUG + , m_pDSYoooROTFilter(NULL) +#endif +{ +} + +DSFilterGraphBase::~DSFilterGraphBase() +{ + Stop(); + CloseInterfaces(); +} + +STDMETHODIMP DSFilterGraphBase::Start() +{ + HRESULT hr = S_OK; + if (!m_pMediaControl) + { + hr = InitInterfaces(); + CHECKRET(hr == S_OK,hr); + CHECKRET(m_pMediaControl,E_FAIL); + } + SetVolume(m_iVolume); + hr = S_FALSE; + while (hr == S_FALSE) + { + hr = m_pMediaControl->Run(); + if (hr == S_FALSE) Sleep(50); + } + return hr; +} + +STDMETHODIMP DSFilterGraphBase::Stop() +{ + CHECKRET(m_pMediaControl,E_FAIL); + m_pMediaControl->Stop(); + return CloseInterfaces(); +} + +STDMETHODIMP DSFilterGraphBase::Pause() +{ + if (!m_pMediaControl) + { + HRESULT hr = Start(); + hr = InitInterfaces(); + CHECKRET(hr == S_OK,hr); + } + return m_pMediaControl->Pause(); +} + +HRESULT DSFilterGraphBase::InitInterfaces() +{ + try + { + CloseInterfaces(); + HRESULT hr = CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC, + IID_IGraphBuilder, (void **)&m_pGraphBuilder); + SAFECHECK(hr); + +#ifdef ROT_DEBUG + m_pDSYoooROTFilter = new DSYoooROTFilter(); + hr = m_pDSYoooROTFilter->AddToFilterGraph(m_pGraphBuilder); + SAFECHECK(hr); +#endif + return S_OK; + } + catch (...) + { + return E_FAIL; + } +} + +HRESULT DSFilterGraphBase::CloseInterfaces() +{ +#ifdef ROT_DEBUG + if (m_pGraphBuilder && m_pDSYoooROTFilter) m_pDSYoooROTFilter->RemoveFromFilterGraph(m_pGraphBuilder); + SAFEDELETE(m_pDSYoooROTFilter); +#endif + if (m_pMediaEventEx) + { + m_pMediaEventEx->SetNotifyWindow(NULL,NULL,NULL); + SAFERELEASE(m_pMediaEventEx); + } + if (m_pVideoWindow) + { + m_pVideoWindow->put_Visible(FALSE); + m_pVideoWindow->put_Owner(NULL); + SAFERELEASE(m_pVideoWindow); + } + SAFERELEASE(m_pMediaSeeking); + SAFERELEASE(m_pBasicVideo); + SAFERELEASE(m_pBasicAudio); + SAFERELEASE(m_pMediaControl); + SAFERELEASE(m_pGraphBuilder); + return S_OK; +} + +HWND DSFilterGraphBase::get_NotifyWindow() +{ + return m_hWndNotify; +} + +void DSFilterGraphBase::set_NotifyWindow(HWND hWnd) +{ + m_hWndNotify = hWnd; +} + +HWND DSFilterGraphBase::get_PreviewWindow() +{ + return m_hWnd; +} + +void DSFilterGraphBase::set_PreviewWindow(HWND hWnd) +{ + m_hWnd = hWnd; +} + +bool DSFilterGraphBase::get_ShouldPreview() +{ + return m_bShouldPreview; +} + +void DSFilterGraphBase::set_ShouldPreview(bool bShouldPreview) +{ + m_bShouldPreview = bShouldPreview; + SettingUpVideoWindow(); +} + +HRESULT DSFilterGraphBase::PreparePlayback() +{ + HRESULT hr = m_pGraphBuilder->QueryInterface(IID_IMediaControl,(void**)&m_pMediaControl); + SAFECHECKCLOSE(hr); + hr = m_pGraphBuilder->QueryInterface(IID_IBasicVideo,(void**)&m_pBasicVideo); + hr = m_pGraphBuilder->QueryInterface(IID_IMediaSeeking,(void**)&m_pMediaSeeking); + hr = m_pGraphBuilder->QueryInterface(IID_IVideoWindow,(void**)&m_pVideoWindow); + if (hr != E_NOINTERFACE && m_pBasicVideo) SettingUpVideoWindow(); + if (m_hWndNotify) + { + hr = m_pGraphBuilder->QueryInterface(IID_IMediaEventEx,(void**)&m_pMediaEventEx); + hr = m_pMediaEventEx->SetNotifyWindow((OAHWND)m_hWndNotify,WM_GRAPH_NOTIFY,(LONG_PTR)this); + //::SetWindowLong(m_hWnd, GWL_STYLE, ::GetWindowLong(m_hWnd, GWL_STYLE) &~SS_GRAYRECT); + } + return hr; +} + +HRESULT DSFilterGraphBase::ProcessStepMessage() +{ + HRESULT bFinished = S_OK; + if (m_pMediaEventEx) + { + long lEventCode; + LONG_PTR lParam1,lParam2; + HRESULT hr = m_pMediaEventEx->GetEvent(&lEventCode,&lParam1,&lParam2,INFINITE); + if (SUCCEEDED(hr)) + { + bFinished = lEventCode == EC_STEP_COMPLETE ? S_FALSE : NOERROR; + hr = m_pMediaEventEx->FreeEventParams(lEventCode,lParam1,lParam2); + ASSERT(hr == S_OK); + } + } + return bFinished; +} + +HRESULT DSFilterGraphBase::ProcessGraphMessage() +{ + HRESULT bFinished = S_OK; + if (m_pMediaEventEx) + { + long lEventCode; + LONG_PTR lParam1,lParam2; + //HRESULT hr = m_pMediaEventEx->GetEvent(&lEventCode,&lParam1,&lParam2,INFINITE); + HRESULT hr = m_pMediaEventEx->GetEvent(&lEventCode,&lParam1,&lParam2,50); + if (SUCCEEDED(hr)) + { + bFinished = lEventCode == EC_COMPLETE ? S_FALSE : NOERROR; + hr = m_pMediaEventEx->FreeEventParams(lEventCode,lParam1,lParam2); + ASSERT(hr == S_OK); + } + } + return bFinished; +} + +bool DSFilterGraphBase::IsAudioSupported() +{ + if (!m_pBasicAudio) + { + if (m_pGraphBuilder) + { + if (SUCCEEDED(m_pGraphBuilder->QueryInterface(IID_IBasicAudio,(void**)&m_pBasicAudio))) + { + return true; + } + } + return false; + } + return true; +} + +void DSFilterGraphBase::SettingUpVideoWindow() +{ + if (m_pVideoWindow != NULL) + { + if (m_hWnd) + { + m_pVideoWindow->put_Owner((OAHWND)m_hWnd); + m_pVideoWindow->put_MessageDrain((OAHWND)m_hWnd); + m_pVideoWindow->put_WindowStyle(WS_CHILD | WS_CLIPSIBLINGS); + ResizeVideoWindow(); + } + if (!m_bShouldPreview) + { + m_pVideoWindow->put_AutoShow(OAFALSE); + m_pVideoWindow->put_Visible(OAFALSE); + } + else + { + m_pVideoWindow->put_AutoShow(OATRUE); + m_pVideoWindow->put_Visible(OATRUE); + } + } +} +void DSFilterGraphBase::ResizeVideoWindow() +{ + if (m_hWnd) + { + RECT rect; + GetClientRect(m_hWnd, &rect); + ResizeVideoWindow(rect); + } +} + +void DSFilterGraphBase::ResizeVideoWindow(RECT rect) +{ + if (m_pVideoWindow != NULL) m_pVideoWindow->SetWindowPosition(rect.left, rect.top, rect.right, rect.bottom); +} + +int DSFilterGraphBase::get_Volume() +{ + return m_iVolume; +} +void DSFilterGraphBase::set_Volume(int nVolume) +{ + m_iVolume = nVolume; + if (!m_bMute) + { + SetVolume(m_iVolume); + } +} + +bool DSFilterGraphBase::get_Mute() +{ + return m_bMute; +} + +void DSFilterGraphBase::set_Mute(bool bMute) +{ + m_bMute = bMute; + if (!m_bMute) + { + SetVolume(m_iVolume); + } + else + { + SetVolume(-10000); + } +} + + +HRESULT DSFilterGraphBase::SetVolume(int nVolume) +{ + if (!m_pBasicAudio) + { + if (m_pGraphBuilder) + { + HRESULT hr = m_pGraphBuilder->QueryInterface(IID_IBasicAudio,(void**)&m_pBasicAudio); + if (FAILED(hr)) return hr; + } + return E_NOINTERFACE; + } + return m_pBasicAudio->put_Volume(nVolume); +} + +void DSFilterGraphBase::set_VideoSize(RECT _rect) +{ + if (m_pBasicVideo != NULL) + { + HRESULT hr; + LONG lLeft,lTop,lWidth,lHeight; + lLeft = _rect.left; + lTop = _rect.top; + lHeight = _rect.bottom - lTop; + lWidth = _rect.right - lLeft; + hr = m_pBasicVideo->SetDestinationPosition(lLeft,lTop,lWidth,lHeight); + ASSERT(hr == S_OK); + } +} + +RECT DSFilterGraphBase::get_VideoSize() +{ + RECT _rect; + _rect.left = 0; + _rect.top = 0; + _rect.bottom = 0; + _rect.right = 0; + if (m_pBasicVideo != NULL) + { + HRESULT hr; + LONG lLeft,lTop,lWidth,lHeight; + hr = m_pBasicVideo->GetDestinationPosition(&lLeft,&lTop,&lWidth,&lHeight); + ASSERT(hr == S_OK); + _rect.left = lLeft; + _rect.top = lTop; + _rect.bottom = lTop + lHeight; + _rect.right = lLeft + lWidth; + } + return _rect; +} + +RECT DSFilterGraphBase::get_SourceVideoSize() +{ + RECT _rect; + _rect.left = 0; + _rect.top = 0; + _rect.bottom = 0; + _rect.right = 0; + if (m_pBasicVideo != NULL) + { + HRESULT hr; + LONG lLeft,lTop,lWidth,lHeight; + hr = m_pBasicVideo->GetSourcePosition(&lLeft,&lTop,&lWidth,&lHeight); + ASSERT(hr == S_OK); + _rect.left = lLeft; + _rect.top = lTop; + _rect.bottom = lTop + lHeight; + _rect.right = lLeft + lWidth; + } + return _rect; +} + +bool DSFilterGraphBase::IsRunning() +{ + if (m_pMediaControl) + { + OAFilterState _state; + HRESULT hr = m_pMediaControl->GetState(50,&_state); + ASSERT(hr == S_OK); + if (SUCCEEDED(hr)) + { + return (_state != State_Stopped); + } + } + return false; +} + +bool DSFilterGraphBase::IsPaused() +{ + if (m_pMediaControl) + { + OAFilterState _state; + HRESULT hr = m_pMediaControl->GetState(50,&_state); + ASSERT(hr == S_OK); + if (SUCCEEDED(hr)) + { + return (_state == State_Paused); + } + } + return false; +} +REFERENCE_TIME DSFilterGraphBase::get_Position() +{ + if (m_pMediaSeeking) + { + REFERENCE_TIME _time; + HRESULT hr = m_pMediaSeeking->GetCurrentPosition(&_time); + ASSERT(hr == S_OK); + if (SUCCEEDED(hr)) return _time; + } + return -1; +} +void DSFilterGraphBase::set_Position(REFERENCE_TIME _time) +{ + if (m_pMediaSeeking) + { + REFERENCE_TIME _stop = 0; + HRESULT hr = m_pMediaSeeking->SetPositions(&_time,AM_SEEKING_AbsolutePositioning,&_stop,AM_SEEKING_NoPositioning); + //ASSERT(hr == S_OK); + } +} + +REFERENCE_TIME DSFilterGraphBase::get_Duration() +{ + if (m_pMediaSeeking) + { + REFERENCE_TIME _time; + HRESULT hr = m_pMediaSeeking->GetDuration(&_time); + ASSERT(hr == S_OK); + if (SUCCEEDED(hr)) return _time; + } + return -1; +} + +IBaseFilter * DSFilterGraphBase::GetVideoRenderer() +{ + IBaseFilter * _filter = NULL; + if (m_pGraphBuilder) + { + HRESULT hr; + IEnumFilters * pEnumFilters; + hr = m_pGraphBuilder->EnumFilters(&pEnumFilters); + ASSERT(hr == S_OK); + if (SUCCEEDED(hr)) + { + IBaseFilter * pFilter; + ULONG ulFetched; + hr = pEnumFilters->Next(1,&pFilter,&ulFetched); + while (hr != S_FALSE) + { + IVideoWindow * pVideoWindow; + hr = pFilter->QueryInterface(IID_IVideoWindow,(void**)&pVideoWindow); + if (SUCCEEDED(hr)) + { + SAFERELEASE(pVideoWindow); + _filter = pFilter; + break; + } + SAFERELEASE(pFilter); + hr = pEnumFilters->Next(1,&pFilter,&ulFetched); + } + SAFERELEASE(pEnumFilters); + } + } + return _filter; +} + +IBaseFilter * DSFilterGraphBase::GetAudioRenderer() +{ + IBaseFilter * _filter = NULL; + if (m_pGraphBuilder) + { + HRESULT hr; + IEnumFilters * pEnumFilters; + hr = m_pGraphBuilder->EnumFilters(&pEnumFilters); + ASSERT(hr == S_OK); + if (SUCCEEDED(hr)) + { + IBaseFilter * pFilter; + ULONG ulFetched; + hr = pEnumFilters->Next(1,&pFilter,&ulFetched); + while (hr != S_FALSE) + { + IBasicAudio * pBasicAudio; + hr = pFilter->QueryInterface(IID_IBasicAudio,(void**)&pBasicAudio); + if (SUCCEEDED(hr)) + { + SAFERELEASE(pBasicAudio); + _filter = pFilter; + //_filter = new DSFilterBase(pFilter,true); + break; + } + SAFERELEASE(pFilter); + hr = pEnumFilters->Next(1,&pFilter,&ulFetched); + } + SAFERELEASE(pEnumFilters); + } + } + return _filter; +} \ No newline at end of file diff --git a/VideoSourceDirectShow/DSNative/DirectShow/Sources/DSObjectBase.cpp b/VideoSourceDirectShow/DSNative/DirectShow/Sources/DSObjectBase.cpp new file mode 100644 index 0000000..9fc0b70 --- /dev/null +++ b/VideoSourceDirectShow/DSNative/DirectShow/Sources/DSObjectBase.cpp @@ -0,0 +1,23 @@ + +#include "../Include/DSObjectBase.h" +#include + +DSObjectBase::DSObjectBase() + : m_bReleaseOnDestroy(true) +{ + CoInitialize(NULL); +} + +DSObjectBase::~DSObjectBase() +{ + if (m_bReleaseOnDestroy) Release(); +} + +HRESULT DSObjectBase::QueryInterface(const IID &riid,void ** ppvObject) +{ + return E_NOINTERFACE; +} + +void DSObjectBase::Release() +{ +} diff --git a/VideoSourceDirectShow/DSNative/DirectShow/Sources/DSSampleGrabber.cpp b/VideoSourceDirectShow/DSNative/DirectShow/Sources/DSSampleGrabber.cpp new file mode 100644 index 0000000..ad57acc --- /dev/null +++ b/VideoSourceDirectShow/DSNative/DirectShow/Sources/DSSampleGrabber.cpp @@ -0,0 +1,200 @@ + +#include "../Include/DSFiltersDefinition.h" +#include + +DSSampleGrabberFilter::DSSampleGrabberFilter() + : DSFilterBase() + , m_pISampleGrabber(NULL) + , m_pCB(NULL) +{ + m_bReleaseOnDestroy = true; + HRESULT hr = CoCreateInstance(CLSID_SampleGrabber, NULL, CLSCTX_INPROC, IID_IBaseFilter, (void **)&m_pFilter); + THROWHR(hr); + hr = m_pFilter->QueryInterface(IID_ISampleGrabber,(void**)&m_pISampleGrabber); + THROWHR(hr); + m_pCB = new SampleGrabberCB(m_pISampleGrabber); + hr = m_pISampleGrabber->SetCallback(m_pCB,1); + THROWHR(hr); +} + +DSSampleGrabberFilter::~DSSampleGrabberFilter() +{ + if (m_pISampleGrabber) + { + m_pISampleGrabber->SetCallback(NULL,0); + SAFERELEASE(m_pISampleGrabber); + } + SAFEDELETE(m_pCB); +} + +HRESULT DSSampleGrabberFilter::SetMediaType(AM_MEDIA_TYPE mt) +{ + return m_pISampleGrabber->SetMediaType(&mt); +} + +HRESULT DSSampleGrabberFilter::SetCallback(ISampleGrabberCB *pCallback,long WhichMethodToCallback) +{ + SAFEDELETE(m_pCB); + return m_pISampleGrabber->SetCallback(pCallback,WhichMethodToCallback); +} + +void DSSampleGrabberFilter::SetCallbackWnd( HWND wnd, UINT message ) +{ + m_pCB->m_hwnd = wnd; + m_pCB->m_Message = message; +} + +HRESULT DSSampleGrabberFilter::SetCallback(CSampleCallBack * pCallBack) +{ + if (m_pCB) + { + return m_pCB->SetCallback(pCallBack); + } + return E_FAIL; +} +HRESULT DSSampleGrabberFilter::GetMediaType(AM_MEDIA_TYPE * pmt) +{ + CheckPointer(pmt,E_POINTER); + if (m_pISampleGrabber) + { + return m_pISampleGrabber->GetConnectedMediaType(pmt); + } + return E_FAIL; +} + +HRESULT DSSampleGrabberFilter::SetOneShot( BOOL bOneShot /*= FALSE*/ ) +{ + m_pISampleGrabber->SetOneShot(bOneShot); + return S_FALSE; +} + +BOOL DSSampleGrabberFilter::Grab(BYTE ** ppbtBuffer,long * plBufferSize) +{ + if (m_pCB) + { + return m_pCB->Grab(ppbtBuffer,plBufferSize); + } + return FALSE; +} + +HRESULT DSSampleGrabberFilter::SampleGrabberCB::SetCallback(CSampleCallBack * pCallBack) +{ + if (m_pCallBack != pCallBack) + { + if (WaitForSingleObject(m_hMutex,INFINITE) == WAIT_OBJECT_0) + { + m_bFormatSetted = FALSE; + m_pCallBack = pCallBack; + ReleaseMutex(m_hMutex); + return NOERROR; + } + } + return E_FAIL; +} + +BOOL DSSampleGrabberFilter::SampleGrabberCB::Grab(BYTE ** ppbtBuffer,long * plBufferSize) +{ + if (WaitForSingleObject(m_hMutex,INFINITE) == WAIT_OBJECT_0) + { + m_bGrabbing = TRUE; + ReleaseMutex(m_hMutex); + } + while(m_bGrabbing) + { + Sleep(50); + } + + if (*ppbtBuffer == NULL) + { + *ppbtBuffer = static_cast(CoTaskMemAlloc(m_lBufferSize)); + } + + LPBYTE lpbtTemp = *ppbtBuffer; + DWORD _pitch = m_dwWidth * 3; + + auto segmentCount = max(1, omp_get_num_procs() / 4); + auto segmentLength = m_dwHeight / segmentCount; + auto i = 0; + + // Rotate and reset BGR to RGB + #pragma omp parallel for private(i) shared(lpbtTemp) + for (i = 0; i < segmentCount; ++i) + { + DWORD start = segmentLength * i; + DWORD end = start + segmentLength; + for (DWORD y = start; y < end; ++y) + { + for (DWORD x = 0; x < _pitch; x += 3) + { + lpbtTemp[y * _pitch + x] = m_pbtBuffer[(m_dwHeight - (y + 1)) * _pitch + x + 2]; + lpbtTemp[y * _pitch + x + 1] = m_pbtBuffer[(m_dwHeight - (y + 1)) * _pitch + x + 1]; + lpbtTemp[y * _pitch + x + 2] = m_pbtBuffer[(m_dwHeight - (y + 1)) * _pitch + x]; + } + } + } + // Rotate + /* + for (DWORD y = 0; y < m_dwHeight; y++) + { + for (DWORD x = 0;x < _pitch; x++) + { + lpbtTemp[y * _pitch + x] = m_pbtBuffer[(m_dwHeight - (y + 1)) * _pitch + x]; + } + } + */ + + *plBufferSize = m_lBufferSize; + return TRUE; +} + +HRESULT STDMETHODCALLTYPE DSSampleGrabberFilter::SampleGrabberCB::BufferCB(double SampleTime,BYTE *pBuffer,long BufferLen) +{ + if (WaitForSingleObject(m_hMutex,INFINITE) == WAIT_OBJECT_0) + { + if (!m_bFormatSetted) + { + if (m_pISampleGrabber) + { + AM_MEDIA_TYPE mt; + if (SUCCEEDED(m_pISampleGrabber->GetConnectedMediaType(&mt))) + { + VIDEOINFOHEADER *vih = (VIDEOINFOHEADER*) mt.pbFormat; + m_dwWidth = vih->bmiHeader.biWidth; + m_dwHeight = vih->bmiHeader.biHeight; + if (m_pCallBack) + { + m_pCallBack->OnFormat(mt.formattype,mt.pbFormat,mt.cbFormat); + } + m_bFormatSetted = TRUE; + FreeMediaType(mt); + } + } + } + if (m_pCallBack) + { + m_pCallBack->OnSample(pBuffer,BufferLen); + m_pCallBack->m_dTimeStamp = SampleTime; + } + if (m_bGrabbing) + { + if (m_lBufferSize != BufferLen) + { + SAFECOTASKFREE(m_pbtBuffer); + } + if (!m_pbtBuffer) + { + m_pbtBuffer = (LPBYTE)CoTaskMemAlloc(BufferLen); + } + m_lBufferSize = BufferLen; + CopyMemory(m_pbtBuffer,pBuffer,m_lBufferSize); + m_bGrabbing = FALSE; + } + ReleaseMutex(m_hMutex); + } + return S_OK; +} + +HRESULT STDMETHODCALLTYPE DSSampleGrabberFilter::SampleGrabberCB::SampleCB(double SampleTime,IMediaSample *pSample) +{ + return S_OK; +} \ No newline at end of file diff --git a/VideoSourceDirectShow/DSNative/Format.cpp b/VideoSourceDirectShow/DSNative/Format.cpp new file mode 100644 index 0000000..129c53e --- /dev/null +++ b/VideoSourceDirectShow/DSNative/Format.cpp @@ -0,0 +1,248 @@ +#include "stdwx.h" +#include "Format.h" +#include +#include "DirectShow\Include\DSCommon.h" + +CVideoFormats::CVideoFormats(IAMStreamConfig * pConfig) + //: CFactoryObject() + : m_pConfig(pConfig) +{ + if (m_pConfig) + { + m_pConfig->AddRef(); + } +} + +CVideoFormats::~CVideoFormats() +{ + SAFERELEASE(m_pConfig); +} + +SIZE CVideoFormats::get_Resolution() +{ + SIZE _size; + _size.cx = 0; + _size.cy = 0; + if (m_pConfig) + { + AM_MEDIA_TYPE *pmt = NULL; + if (SUCCEEDED(m_pConfig->GetFormat(&pmt))) + { + if (pmt->majortype == MEDIATYPE_Video) + { + if (pmt->formattype == FORMAT_VideoInfo) + { + VIDEOINFO * pvi = (VIDEOINFO *)pmt->pbFormat; + _size.cx = pvi->bmiHeader.biWidth; + _size.cy = pvi->bmiHeader.biHeight; + } + else + if (pmt->formattype == FORMAT_VideoInfo2) + { + VIDEOINFOHEADER2 * pvi = (VIDEOINFOHEADER2 *)pmt->pbFormat; + _size.cx = pvi->bmiHeader.biWidth; + _size.cy = pvi->bmiHeader.biHeight; + } + } + DeleteMediaType(pmt); + } + } + return _size; +} + +int CVideoFormats::get_Count() +{ + SIZE _aPrefferedSizes[] = + { + { 160,120 }, + { 176,144 }, + { 240,176 }, + { 240,180 }, + { 320,240 }, + { 352,288 }, + { 640,240 }, + { 640,288 }, + { 640,480 }, + { 704,576 }, + { 720,240 }, + { 720,288 }, + { 720,480 }, + { 720,576 }, + { 800,480 }, + { 800,600 }, + { 1024,768 }, + { 1280,1024 }, + { 1600,1200 } + }; + + int iSizesCount = sizeof(_aPrefferedSizes)/sizeof(_aPrefferedSizes[0]); + int iCurrentIndex = 0; + HRESULT hr = NOERROR; + bool bResult = false; + int nIndex = -1; + if (m_pConfig) + { + int iCount, iSize; + hr = m_pConfig->GetNumberOfCapabilities(&iCount,&iSize); + if (hr == S_OK) + { + LPBYTE pbtSCC = (LPBYTE)CoTaskMemAlloc(iSize); + for (int i = 0; i < iCount; i++) + { + AM_MEDIA_TYPE * pmt = NULL; + hr = m_pConfig->GetStreamCaps(i,&pmt,pbtSCC); + if (hr == S_OK) + { + VIDEO_STREAM_CONFIG_CAPS * pCaps = (VIDEO_STREAM_CONFIG_CAPS *)pbtSCC; + if (pmt->formattype == FORMAT_VideoInfo) + { + if (iCurrentIndex == nIndex) + { + /* + VIDEOINFOHEADER * pFormat = (VIDEOINFOHEADER*)pmt->pbFormat; + _size.cx = pFormat->bmiHeader.biWidth; + _size.cy = pFormat->bmiHeader.biHeight; + bResult = true; + */ + } + else + { + iCurrentIndex++; + if ( pCaps->MinOutputSize.cx != pCaps->MaxOutputSize.cx + && pCaps->MinOutputSize.cy != pCaps->MaxOutputSize.cy) + { + // check list of pre-defined formats + for (int j = 0; j < iSizesCount; j++) + { + // check if supported + if ( + pCaps->MinOutputSize.cx <= _aPrefferedSizes[j].cx + && pCaps->MaxOutputSize.cx >= _aPrefferedSizes[j].cx + && pCaps->MinOutputSize.cy <= _aPrefferedSizes[j].cy + && pCaps->MaxOutputSize.cy >= _aPrefferedSizes[j].cy + ) + { + if (iCurrentIndex == nIndex) + { + /* + _size.cx = _aPrefferedSizes[j].cx; + _size.cx = _aPrefferedSizes[j].cy; + bResult = true; + */ + } + else iCurrentIndex++; + } + if (bResult) break; + } + } + } + } + DeleteMediaType(pmt); + } + if (bResult) break; + } + CoTaskMemFree(pbtSCC); + } + } + return iCurrentIndex; +} + +SIZE CVideoFormats::get_Resolution(int nIndex) +{ + SIZE _aPrefferedSizes[] = + { + { 160,120 }, + { 176,144 }, + { 240,176 }, + { 240,180 }, + { 320,240 }, + { 352,288 }, + { 640,240 }, + { 640,288 }, + { 640,480 }, + { 704,576 }, + { 720,240 }, + { 720,288 }, + { 720,480 }, + { 720,576 }, + { 800,480 }, + { 800,600 }, + { 1024,768 }, + { 1280,1024 }, + { 1600,1200 } + }; + + int iSizesCount = sizeof(_aPrefferedSizes)/sizeof(_aPrefferedSizes[0]); + int iCurrentIndex = 0; + HRESULT hr = NOERROR; + bool bResult = false; + SIZE _size; + _size.cx = 0; + _size.cy = 0; + if (m_pConfig) + { + int iCount, iSize; + hr = m_pConfig->GetNumberOfCapabilities(&iCount,&iSize); + if (hr == S_OK) + { + LPBYTE pbtSCC = (LPBYTE)CoTaskMemAlloc(iSize); + for (int i = 0; i < iCount; i++) + { + AM_MEDIA_TYPE * pmt = NULL; + hr = m_pConfig->GetStreamCaps(i,&pmt,pbtSCC); + if (hr == S_OK) + { + VIDEO_STREAM_CONFIG_CAPS * pCaps = (VIDEO_STREAM_CONFIG_CAPS *)pbtSCC; + if (pmt->formattype == FORMAT_VideoInfo) + { + if (iCurrentIndex == nIndex) + { + VIDEOINFOHEADER * pFormat = (VIDEOINFOHEADER*)pmt->pbFormat; + _size.cx = pFormat->bmiHeader.biWidth; + _size.cy = pFormat->bmiHeader.biHeight; + bResult = true; + } + else + { + iCurrentIndex++; + if ( pCaps->MinOutputSize.cx != pCaps->MaxOutputSize.cx + && pCaps->MinOutputSize.cy != pCaps->MaxOutputSize.cy) + { + // check list of pre-defined formats + for (int j = 0; j < iSizesCount; j++) + { + // check if supported + if ( + pCaps->MinOutputSize.cx <= _aPrefferedSizes[j].cx + && pCaps->MaxOutputSize.cx >= _aPrefferedSizes[j].cx + && pCaps->MinOutputSize.cy <= _aPrefferedSizes[j].cy + && pCaps->MaxOutputSize.cy >= _aPrefferedSizes[j].cy + ) + { + if (iCurrentIndex == nIndex) + { + _size.cx = _aPrefferedSizes[j].cx; + _size.cy = _aPrefferedSizes[j].cy; + bResult = true; + } + else iCurrentIndex++; + } + if (bResult) break; + } + } + } + } + DeleteMediaType(pmt); + } + if (bResult) break; + } + CoTaskMemFree(pbtSCC); + } + } + return _size; +} + +HRESULT CVideoFormats::set_Resolution(SIZE _size) +{ + return NOERROR; +} \ No newline at end of file diff --git a/VideoSourceDirectShow/DSNative/Format.h b/VideoSourceDirectShow/DSNative/Format.h new file mode 100644 index 0000000..70614b3 --- /dev/null +++ b/VideoSourceDirectShow/DSNative/Format.h @@ -0,0 +1,24 @@ +#ifndef __Formats_H__ +#define __Formats_H__ +///////////////////////////////////////////////////// +//#include "ClassFactory.h" +#include "stdwx.h" +#include +///////////////////////////////////////////////////// +class EXPORTS_API CVideoFormats //: public CFactoryObject +{ + friend class CDevice; +private: + IAMStreamConfig * m_pConfig; +public: + SIZE get_Resolution(); + int get_Count(); + SIZE get_Resolution(int nIndex); + HRESULT set_Resolution(SIZE _size); +private: + CVideoFormats(IAMStreamConfig * pConfig); +public: + virtual ~CVideoFormats(); +}; +///////////////////////////////////////////////////// +#endif // __Formats_H__ \ No newline at end of file diff --git a/VideoSourceDirectShow/DSNative/FrameGrabber.cpp b/VideoSourceDirectShow/DSNative/FrameGrabber.cpp new file mode 100644 index 0000000..a9100bd --- /dev/null +++ b/VideoSourceDirectShow/DSNative/FrameGrabber.cpp @@ -0,0 +1,251 @@ +#include "stdwx.h" +#include "FrameGrabber.h" +#include "Crossbar.h" +#include + +CFrameGrabber::CFrameGrabber(CDevice * pVideoDevice) + //: CFactoryObject() + : m_pVideoDevice(pVideoDevice) + , m_pCaptureGraph(NULL) + , m_pVideoPicker(NULL) + , m_pNullRenderer(NULL) + , m_pDecompressor(NULL) +{ + +} + +CFrameGrabber::~CFrameGrabber() +{ + CloseInterfaces(); +} + +CDevice * CFrameGrabber::get_VideoDevice() +{ + return m_pVideoDevice; +} + +HRESULT CFrameGrabber::InitInterfaces() +{ + HRESULT hr = __super::InitInterfaces(); + SAFECHECKCLOSE(hr); + + hr = CoCreateInstance(CLSID_CaptureGraphBuilder2, NULL, CLSCTX_INPROC, + IID_ICaptureGraphBuilder2, (void **)&m_pCaptureGraph); + SAFECHECKCLOSE(hr); + + hr = m_pCaptureGraph->SetFiltergraph(m_pGraphBuilder); + SAFECHECKCLOSE(hr); + + if (m_pVideoDevice && m_pVideoDevice->m_pFilter) + { + hr = m_pVideoDevice->m_pFilter->AddToFilterGraph(m_pGraphBuilder); + SAFECHECKCLOSE(hr); + + DecompressorType::DECOMPTYPE _decompressorType = ConfigureVideoSource(); + + IBaseFilter * pDecompressor = NULL; + if (_decompressorType == DecompressorType::AVI) + { + m_pDecompressor = new DSAviDecompressorFilter(); + m_pDecompressor->GetIBaseFilter(&pDecompressor); + } else + if (_decompressorType == DecompressorType::MJPEG) + { + m_pDecompressor = new DSMJPEGDecompressorFilter(); + m_pDecompressor->GetIBaseFilter(&pDecompressor); + } + + m_pVideoPicker = new DSSampleGrabberFilter(); + AM_MEDIA_TYPE mt; + ZeroMemory(&mt,sizeof(mt)); + mt.majortype = MEDIATYPE_Video; + mt.subtype = MEDIASUBTYPE_RGB24; + m_pVideoPicker->SetMediaType(mt); + m_pVideoPicker->AddToFilterGraph(m_pGraphBuilder); + IBaseFilter * pPicker; + m_pVideoPicker->GetIBaseFilter(&pPicker); + + if (pDecompressor) + { + m_pDecompressor->AddToFilterGraph(m_pGraphBuilder); + + IBaseFilter * pSource; + m_pVideoDevice->m_pFilter->GetIBaseFilter(&pSource); + hr = m_pCaptureGraph->RenderStream(&PIN_CATEGORY_CAPTURE, &MEDIATYPE_Video,pSource,pDecompressor,pPicker); + //hr = m_pCaptureGraph->RenderStream(&PIN_CATEGORY_PREVIEW, &MEDIATYPE_Video,pSource,pDecompressor,pPicker); + if (FAILED(hr)) + { + CloseInterfaces(); + return hr; + } + hr = m_pCaptureGraph->RenderStream(NULL , NULL ,pPicker,NULL,NULL); + if (FAILED(hr)) + { + CloseInterfaces(); + return hr; + } + } + else + { + IBaseFilter * pSource; + m_pVideoDevice->m_pFilter->GetIBaseFilter(&pSource); + hr = m_pCaptureGraph->RenderStream(&PIN_CATEGORY_CAPTURE, &MEDIATYPE_Video,pSource,pPicker,NULL); + + //m_pNullRenderer = new DSNullRenderer(); + /* + m_pNullRenderer->AddToFilterGraph(m_pGraphBuilder); + + IBaseFilter * pNullRenderer; + m_pNullRenderer->GetIBaseFilter(&pNullRenderer); + hr = m_pCaptureGraph->RenderStream(&PIN_CATEGORY_CAPTURE, &MEDIATYPE_Video,pSource,pPicker,pNullRenderer); + if (FAILED(hr)) + { + CloseInterfaces(); + return hr; + } + */ + } + } + return PreparePlayback(); +} + +HRESULT CFrameGrabber::CloseInterfaces() +{ + if (m_pVideoDevice && m_pVideoDevice->m_pFilter && m_pGraphBuilder) + { + m_pVideoDevice->m_pFilter->RemoveFromFilterGraph(m_pGraphBuilder); + } + SAFEDELETE(m_pNullRenderer); + SAFERELEASE(m_pCaptureGraph); + SAFEDELETE(m_pDecompressor); + SAFEDELETE(m_pVideoPicker); + return __super::CloseInterfaces(); +} + +DecompressorType::DECOMPTYPE CFrameGrabber::ConfigureVideoSource() +{ + DecompressorType::DECOMPTYPE _decompressorType = DecompressorType::None; + if (m_pVideoDevice && m_pVideoDevice->m_pFilter) + { + IPin * pPin = NULL; + pPin = m_pVideoDevice->m_pFilter->GetPinByCategory(PIN_CATEGORY_CAPTURE); + if (!pPin) pPin = m_pVideoDevice->m_pFilter->GetPinByDirection(PINDIR_OUTPUT,0); + if (pPin) + { + AM_MEDIA_TYPE * pmt; + HRESULT hr = m_pVideoDevice->m_pFilter->GetOutputPinFormat(pPin,&pmt); + if (hr == S_OK) + { + if ( pmt->formattype == FORMAT_VideoInfo) + { + GUID _fmtSubType = pmt->subtype; + hr = E_FAIL; + if (hr != S_OK) + { + if (_fmtSubType != MEDIASUBTYPE_YUY2) pmt->subtype = MEDIASUBTYPE_YUY2; + hr = m_pVideoDevice->m_pFilter->SetOutputPinFormat(pPin,pmt); + _decompressorType = DecompressorType::AVI; + } + if (hr != S_OK) + { + if (_fmtSubType != MEDIASUBTYPE_RGB32) pmt->subtype = MEDIASUBTYPE_RGB32; + hr = m_pVideoDevice->m_pFilter->SetOutputPinFormat(pPin,pmt); + _decompressorType = DecompressorType::None; + } + if (hr != S_OK) + { + if (_fmtSubType != MEDIASUBTYPE_RGB24) pmt->subtype = MEDIASUBTYPE_RGB24; + hr = m_pVideoDevice->m_pFilter->SetOutputPinFormat(pPin,pmt); + _decompressorType = DecompressorType::None; + } + if (hr != S_OK) + { + if (_fmtSubType != MEDIASUBTYPE_MJPG) pmt->subtype = MEDIASUBTYPE_MJPG; + hr = m_pVideoDevice->m_pFilter->SetOutputPinFormat(pPin,pmt); + _decompressorType = DecompressorType::MJPEG; + } + if (hr != S_OK) + { + _decompressorType = DecompressorType::None; + } + } + DeleteMediaType(pmt); + } + pPin->Release(); + } + } + return _decompressorType; +} +void CFrameGrabber::set_PreviewWindow(HWND hWnd) +{ + __super::set_PreviewWindow(hWnd); +} + +void CFrameGrabber::set_ShouldPreview(bool bShouldPreview) +{ + __super::set_ShouldPreview(bShouldPreview); +} + +void CFrameGrabber::ResizeVideoWindow() +{ + __super::ResizeVideoWindow(); +} + +void CFrameGrabber::ResizeVideoWindow(RECT rect) +{ + __super::ResizeVideoWindow(rect); +} + +CCrossBar * CFrameGrabber::get_Crossbar() +{ + if (m_pCaptureGraph && m_pVideoPicker) + { + IBaseFilter * pPicker; + m_pVideoPicker->GetIBaseFilter(&pPicker); + IAMCrossbar *pICrossBar = NULL; + if (SUCCEEDED(m_pCaptureGraph->FindInterface(&LOOK_UPSTREAM_ONLY,NULL,pPicker,IID_IAMCrossbar,(void**)&pICrossBar))) + { + CCrossBar * pCrossbar = new CCrossBar(pICrossBar); + pICrossBar->Release(); + return pCrossbar; + } + } + return NULL; +} + +HRESULT CFrameGrabber::get_Format(BITMAPINFOHEADER ** pbmi) +{ + CheckPointer(pbmi,E_POINTER); + *pbmi = NULL; + if (m_pVideoPicker) + { + AM_MEDIA_TYPE mt; + HRESULT hr = m_pVideoPicker->GetMediaType(&mt); + if (FAILED(hr)) return hr; + if (mt.formattype == FORMAT_VideoInfo) + { + VIDEOINFO * pvi = (VIDEOINFO *)mt.pbFormat; + *pbmi = (BITMAPINFOHEADER *)CoTaskMemAlloc(sizeof(pvi->bmiHeader)); + CopyMemory(*pbmi,&pvi->bmiHeader,sizeof(pvi->bmiHeader)); + } + if (mt.formattype == FORMAT_VideoInfo2) + { + VIDEOINFOHEADER2 * pvi = (VIDEOINFOHEADER2 *)mt.pbFormat; + *pbmi = (BITMAPINFOHEADER *)CoTaskMemAlloc(sizeof(pvi->bmiHeader)); + CopyMemory(*pbmi,&pvi->bmiHeader,sizeof(pvi->bmiHeader)); + } + FreeMediaType(mt); + if (*pbmi == NULL) return E_UNEXPECTED; + return NOERROR; + } + return E_FAIL; +} + +BOOL CFrameGrabber::GrabFrame(BYTE ** ppbtBuffer,long * plBufferSize) +{ + if (m_pVideoPicker) + { + return m_pVideoPicker->Grab(ppbtBuffer,plBufferSize); + } + return FALSE; +} \ No newline at end of file diff --git a/VideoSourceDirectShow/DSNative/FrameGrabber.h b/VideoSourceDirectShow/DSNative/FrameGrabber.h new file mode 100644 index 0000000..6605a3a --- /dev/null +++ b/VideoSourceDirectShow/DSNative/FrameGrabber.h @@ -0,0 +1,43 @@ +#ifndef __FrameGrabber_H__ +#define __FrameGrabber_H__ + +///////////////////////////////////////////////////// +#include "Device.h" +#include "DirectShow/Include/DSFilterGraphBase.h" +#include "DirectShow/Include/DSFiltersDefinition.h" +///////////////////////////////////////////////////// +#pragma warning( disable : 4275 ) +///////////////////////////////////////////////////// +class CCrossBar; +///////////////////////////////////////////////////// +class EXPORTS_API CFrameGrabber//: public CFactoryObject + : public DSFilterGraphBase +{ +protected: + ICaptureGraphBuilder2 * m_pCaptureGraph; + DSSampleGrabberFilter * m_pVideoPicker; + DSFilterBase * m_pNullRenderer; + DSFilterBase * m_pDecompressor; + CDevice * m_pVideoDevice; +public: + CDevice * get_VideoDevice(); + CCrossBar * get_Crossbar(); +protected: + DecompressorType::DECOMPTYPE ConfigureVideoSource(); +protected: + virtual HRESULT InitInterfaces(); + virtual HRESULT CloseInterfaces(); +public: + void set_PreviewWindow(HWND hWnd); + void set_ShouldPreview(bool bShouldPreview); + void ResizeVideoWindow(); + void ResizeVideoWindow(RECT rect); +public: + BOOL GrabFrame(BYTE ** ppbtBuffer,long * plBufferSize); + HRESULT get_Format(BITMAPINFOHEADER ** pbmi); +public: + CFrameGrabber(CDevice * pVideoDevice); + virtual ~CFrameGrabber(); +}; +///////////////////////////////////////////////////// +#endif // __FrameGrabber_H__ \ No newline at end of file diff --git a/VideoSourceDirectShow/VideoGrabberDirectShow.cpp b/VideoSourceDirectShow/VideoGrabberDirectShow.cpp new file mode 100644 index 0000000..d948362 --- /dev/null +++ b/VideoSourceDirectShow/VideoGrabberDirectShow.cpp @@ -0,0 +1,253 @@ +#include "stdwx.h" +#include "VideoGrabberDirectShow.h" +#include "Crossbar.h" + +VideoGrabberDirectShow::VideoGrabberDirectShow() +: m_DeviceIndex(0) +, m_DeviceManager(CLSID_VideoInputDeviceCategory) +, m_pDevice(nullptr) +, m_pGrabber(nullptr) +, m_pbmi(nullptr) +, m_pBuffer(nullptr) +{ +} + +VideoGrabberDirectShow::VideoGrabberDirectShow(VideoSourcePluginBase * owner, size_t index) +: VideoGrabberBase(owner) +, m_DeviceIndex((int)index) +, m_DeviceManager(CLSID_VideoInputDeviceCategory) +, m_pDevice(nullptr) +, m_pGrabber(nullptr) +, m_pbmi(nullptr) +, m_pBuffer(nullptr) +{ + m_ImageSize = wxDefaultSize; + m_pDevice = m_DeviceManager.get_Device(m_DeviceIndex); + if (m_pDevice) + { + m_DeviceID = m_pDevice->get_Moniker(); + ReadName(); + } + StartGrabber(); +} + +VideoGrabberDirectShow::VideoGrabberDirectShow(VideoSourcePluginBase * owner, const wxString & id) +: VideoGrabberBase(owner) +, m_DeviceManager(CLSID_VideoInputDeviceCategory) +, m_pDevice(nullptr) +, m_pGrabber(nullptr) +, m_DeviceID(id) +, m_pbmi(nullptr) +, m_pBuffer(nullptr) +{ + m_ImageSize = wxDefaultSize; + m_pDevice = m_DeviceManager.get_DeviceByMoniker(m_DeviceID.wc_str()); + m_DeviceIndex = m_DeviceManager.get_DeviceIndex(m_DeviceID.wc_str()); + if (m_pDevice) + ReadName(); + StartGrabber(); +} +VideoGrabberDirectShow::~VideoGrabberDirectShow() +{ + CAutoLock lock(&m_csLock); + StopGrabber(); + wxDELETE(m_pDevice); +} + +wxString VideoGrabberDirectShow::GetName() const +{ + return m_DeviceName; +} + +wxString VideoGrabberDirectShow::GetID() const +{ + return m_DeviceID; +} + +size_t VideoGrabberDirectShow::GetFrameDataLength() +{ + return m_ImageSize.GetWidth() * m_ImageSize.GetHeight() * 3; +} + +wxSize VideoGrabberDirectShow::GetFrameSize() +{ + return m_ImageSize; +} + +size_t VideoGrabberDirectShow::get_ResolutionsCount() +{ + size_t _result = 0; + if (m_pDevice) + { + CVideoFormats * pFormats = m_pDevice->get_VideoFormats(); + if (pFormats) + { + _result = pFormats->get_Count(); + delete pFormats; + pFormats = nullptr; + } + } + return _result; +} + +wxSize VideoGrabberDirectShow::get_ResolutionByIndex(size_t nIndex) +{ + wxSize _result = m_ImageSize; + if (m_pDevice) + { + CVideoFormats * pFormats = m_pDevice->get_VideoFormats(); + if (pFormats) + { + SIZE _size = pFormats->get_Resolution((int)nIndex); + _result.x = _size.cx; + _result.y = _size.cy; + delete pFormats; + pFormats = nullptr; + } + } + return _result; +} + +bool VideoGrabberDirectShow::set_Resolution(wxSize _size) +{ + bool bResult = false; + if (m_pDevice) + { + CAutoLock lock(&m_csLock); + StopGrabber(); + CVideoFormats * pFormats = m_pDevice->get_VideoFormats(); + if (pFormats) + { + SIZE _SizeToSet; + _SizeToSet.cx = _size.x; + _SizeToSet.cy = _size.y; + bResult = SUCCEEDED(pFormats->set_Resolution(_SizeToSet)); + delete pFormats; + pFormats = nullptr; + } + StartGrabber(); + } + return bResult; +} + +bool VideoGrabberDirectShow::set_Resolution(size_t nIndex) +{ + return set_Resolution(get_ResolutionByIndex(nIndex)); +} + +unsigned char * VideoGrabberDirectShow::GetBuffer() +{ + return m_pBuffer; +} + +unsigned char * VideoGrabberDirectShow::GrabFrame(bool bUseInternalBuffer) +{ + unsigned char * pBuffer = nullptr; + long lLength = 0; + if (bUseInternalBuffer) + { + lLength = GetFrameDataLength(); + pBuffer = m_pBuffer; + } + if (GrabFrame(&pBuffer,&lLength)) + { + return pBuffer; + } + else + { + if (!bUseInternalBuffer) + { + if (pBuffer) CoTaskMemFree(pBuffer); + pBuffer = nullptr; + } + } + return nullptr; +} + +int VideoGrabberDirectShow::GrabFrame(unsigned char ** data, long * length) +{ + CAutoLock lock(&m_csLock); + if (m_pGrabber) + { + try + { + return m_pGrabber->GrabFrame(data,length); + } + catch (...) + { + DbgOutString(_T("Error while perform grab into passed buffer")); + } + } + return 0; +} + +bool VideoGrabberDirectShow::IsOK() +{ + return m_pGrabber != nullptr; +} + +bool VideoGrabberDirectShow::StartGrabber() +{ + if (m_pDevice) + { + CAutoLock lock(&m_csLock); + if(m_pGrabber) StopGrabber(); + m_pGrabber = m_pDevice->CreateFrameGrabber(); + if (m_pGrabber) + { + m_pGrabber->set_ShouldPreview(false); + if (SUCCEEDED(m_pGrabber->Start())) + { + if (S_OK == m_pGrabber->get_Format(&m_pbmi)) + { + m_ImageSize.x = m_pbmi->biWidth; + m_ImageSize.y = m_pbmi->biHeight; + } + m_pBuffer = (LPBYTE)CoTaskMemAlloc(GetFrameDataLength()); + /* + CCrossBar * pCrossBar = m_pGrabber->get_Crossbar(); + if (pCrossBar) + { + long lIndex = pCrossBar->get_InputByType(PhysConn_Video_Composite); + //long lIndex = pCrossBar->get_InputByType(PhysConn_Video_Tuner); + if (lIndex != -1) + { + pCrossBar->Route(lIndex); + } + delete pCrossBar; + } + */ + return true; + } + else + { + StopGrabber(); + } + } + } + return false; +} +bool VideoGrabberDirectShow::StopGrabber() +{ + CAutoLock lock(&m_csLock); + wxDELETE(m_pGrabber); + + if (m_pbmi != nullptr) + { + CoTaskMemFree(m_pbmi); + m_pbmi = nullptr; + } + if (m_pBuffer) + { + CoTaskMemFree(m_pBuffer); + m_pBuffer = nullptr; + } + return true; +} + +void VideoGrabberDirectShow::ReadName() +{ + LPWSTR lpwstrName = m_DeviceManager.get_DeviceName(m_DeviceIndex); + m_DeviceName = lpwstrName; + wxDELETEA(lpwstrName); +} \ No newline at end of file diff --git a/VideoSourceDirectShow/VideoGrabberDirectShow.h b/VideoSourceDirectShow/VideoGrabberDirectShow.h new file mode 100644 index 0000000..ce78a83 --- /dev/null +++ b/VideoSourceDirectShow/VideoGrabberDirectShow.h @@ -0,0 +1,49 @@ +#ifndef _VIDEOGRABBERDIRECTSHOW_H +#define _VIDEOGRABBERDIRECTSHOW_H + +#include +#include "FrameGrabber.h" +#include "DeviceManager.h" +#include "Format.h" + +class VideoGrabberDirectShow : public VideoGrabberBase +{ +private: + CDeviceManager m_DeviceManager; + CDevice * m_pDevice; + CFrameGrabber * m_pGrabber; + int m_DeviceIndex; + wxString m_DeviceID; + wxString m_DeviceName; + wxSize m_ImageSize; + CCritSec m_csLock; + BITMAPINFOHEADER * m_pbmi; + LPBYTE m_pBuffer; +private: + bool StopGrabber(); + bool StartGrabber(); + void ReadName(); +public: + // TODO: make it compatible with your plugin + virtual size_t get_ResolutionsCount(); + virtual wxSize get_ResolutionByIndex(size_t nIndex); + virtual bool set_Resolution(wxSize _size); + virtual bool set_Resolution(size_t nIndex); +public: + virtual wxString GetName() const; + virtual wxString GetID() const; + virtual wxSize GetFrameSize(); + virtual size_t GetFrameDataLength(); + virtual unsigned char * GetBuffer(); + virtual unsigned char * GrabFrame(bool bUseInternalBuffer = true); + virtual int GrabFrame(unsigned char ** data, long * length); + virtual bool IsOK(); + +public: + VideoGrabberDirectShow(); + VideoGrabberDirectShow(VideoSourcePluginBase * owner, size_t index); + VideoGrabberDirectShow(VideoSourcePluginBase * owner, const wxString & id); + ~VideoGrabberDirectShow(); +}; + +#endif // _VIDEOGRABBERDIRECTSHOW_H \ No newline at end of file diff --git a/SampleGuiPlugin2/SampleGuiPlugin2.def b/VideoSourceDirectShow/VideoSourceDirectShow.def similarity index 65% rename from SampleGuiPlugin2/SampleGuiPlugin2.def rename to VideoSourceDirectShow/VideoSourceDirectShow.def index 806b31b..3cc3882 100644 --- a/SampleGuiPlugin2/SampleGuiPlugin2.def +++ b/VideoSourceDirectShow/VideoSourceDirectShow.def @@ -1,5 +1,5 @@ -LIBRARY "SampleGuiPlugin2" - -EXPORTS - CreatePlugin=CreatePlugin +LIBRARY "VideoSourceDirectShow" + +EXPORTS + CreatePlugin=CreatePlugin DeletePlugin=DeletePlugin \ No newline at end of file diff --git a/VideoSourceDirectShow/VideoSourceDirectShowApp.cpp b/VideoSourceDirectShow/VideoSourceDirectShowApp.cpp new file mode 100644 index 0000000..456741a --- /dev/null +++ b/VideoSourceDirectShow/VideoSourceDirectShowApp.cpp @@ -0,0 +1,9 @@ +#include "stdwx.h" +#include "VideoSourceDirectShowApp.h" + +IMPLEMENT_APP_NO_MAIN(VideoSourceDirectShowApp); + +bool VideoSourceDirectShowApp::OnInit() +{ + return true; +} diff --git a/VideoSourceDirectShow/VideoSourceDirectShowApp.h b/VideoSourceDirectShow/VideoSourceDirectShowApp.h new file mode 100644 index 0000000..73fdfa0 --- /dev/null +++ b/VideoSourceDirectShow/VideoSourceDirectShowApp.h @@ -0,0 +1,10 @@ +#ifndef _VIDEOSOURCEDIRECTSHOWAPP_H +#define _VIDEOSOURCEDIRECTSHOWAPP_H + +class VideoSourceDirectShowApp : public wxApp +{ +public: + virtual bool OnInit(); +}; + +#endif // _VIDEOSOURCEDIRECTSHOWAPP_H diff --git a/VideoSourceDirectShow/VideoSourceDirectShowExports.cpp b/VideoSourceDirectShow/VideoSourceDirectShowExports.cpp new file mode 100644 index 0000000..8a521c6 --- /dev/null +++ b/VideoSourceDirectShow/VideoSourceDirectShowExports.cpp @@ -0,0 +1,13 @@ +#include "stdwx.h" +#include +#include "VideoSourceDirectShowPlugin.h" + +WXEXPORT VideoSourcePluginBase * CreatePlugin() +{ + return new VideoSourceDirectShowPlugin; +} + +WXEXPORT void DeletePlugin(VideoSourcePluginBase * plugin) +{ + wxDELETE(plugin); +} diff --git a/VideoSourceDirectShow/VideoSourceDirectShowPlugin.cpp b/VideoSourceDirectShow/VideoSourceDirectShowPlugin.cpp new file mode 100644 index 0000000..7822525 --- /dev/null +++ b/VideoSourceDirectShow/VideoSourceDirectShowPlugin.cpp @@ -0,0 +1,90 @@ +#include "stdwx.h" +#include "DeviceManager.h" +#include "VideoSourceDirectShowPlugin.h" +#include "VideoGrabberDirectShow.h" + +IMPLEMENT_DYNAMIC_CLASS(VideoSourceDirectShowPlugin, VideoSourcePluginBase) + +VideoSourceDirectShowPlugin::VideoSourceDirectShowPlugin() +: m_ID(wxT("{DEADA8F3-62BB-45d5-9F04-45AC1FF3A996}")) +, m_pDeviceManager(NULL) +{ + CoInitializeEx(NULL,COINIT_MULTITHREADED); + m_pDeviceManager = new CDeviceManager(CLSID_VideoInputDeviceCategory); +} + +VideoSourceDirectShowPlugin::~VideoSourceDirectShowPlugin() +{ + wxDELETE(m_pDeviceManager); + CoUninitialize(); +} + +wxString VideoSourceDirectShowPlugin::GetID() const +{ + return m_ID; +} + +wxString VideoSourceDirectShowPlugin::GetName() const +{ + return _("DirectShow Video Source"); +} + +size_t VideoSourceDirectShowPlugin::GetDeviceCount() const +{ + return m_pDeviceManager->get_Count(); +} + +wxString VideoSourceDirectShowPlugin::GetName(size_t index) const +{ + LPWSTR lpwstrName = m_pDeviceManager->get_DeviceName(index); + wxString name(lpwstrName); + wxDELETEA(lpwstrName); + return name; +} + +wxString VideoSourceDirectShowPlugin::GetName(const wxString & id) const +{ + long lCount = m_pDeviceManager->get_Count(); + while (lCount > 0) + { + lCount--; + wxString _string = GetID(lCount); + if (_string == id) return GetName(lCount); + } + return wxEmptyString; +} + +wxString VideoSourceDirectShowPlugin::GetID(size_t index) const +{ + LPWSTR lpwstrName = m_pDeviceManager->get_DeviceMonikerString(index); + wxString name(lpwstrName); + wxDELETEA(lpwstrName); + return name; +} + +VideoGrabberBase * VideoSourceDirectShowPlugin::CreateGrabber(size_t index) +{ + VideoGrabberDirectShow * grabber = new VideoGrabberDirectShow(this, index); + if (!grabber->IsOK()) + wxDELETE(grabber); + return grabber; +} + +VideoGrabberBase * VideoSourceDirectShowPlugin::CreateGrabber(const wxString & id) +{ + VideoGrabberDirectShow * grabber = new VideoGrabberDirectShow(this, id); + if (!grabber->IsOK()) + wxDELETE(grabber); + return grabber; +} + +void VideoSourceDirectShowPlugin::DeleteGrabber(VideoGrabberBase * grabber) +{ + wxLogDebug(wxT("VideoSourceDirectShowPlugin::DeleteGrabber")); + wxDELETE(grabber); +} + +void VideoSourceDirectShowPlugin::RefreshDevices() +{ + m_pDeviceManager->RefreshDevices(); +} \ No newline at end of file diff --git a/VideoSourceDirectShow/VideoSourceDirectShowPlugin.h b/VideoSourceDirectShow/VideoSourceDirectShowPlugin.h new file mode 100644 index 0000000..9131b6c --- /dev/null +++ b/VideoSourceDirectShow/VideoSourceDirectShowPlugin.h @@ -0,0 +1,31 @@ +#ifndef _VIDEOSOURCEDIRECTSHOWPLUGIN_H +#define _VIDEOSOURCEDIRECTSHOWPLUGIN_H + +#include + +class CDeviceManager; + +/// Base class for all iFloor effect plugins +class VideoSourceDirectShowPlugin : public VideoSourcePluginBase +{ + DECLARE_DYNAMIC_CLASS(VideoSourceDirectShowPlugin) +public: + VideoSourceDirectShowPlugin(); + ~VideoSourceDirectShowPlugin(); + virtual wxString GetID() const; + virtual wxString GetName() const; + + virtual size_t GetDeviceCount() const; + virtual wxString GetName(size_t index) const; + virtual wxString GetName(const wxString & id) const; + virtual wxString GetID(size_t index) const; + virtual VideoGrabberBase * CreateGrabber(size_t index); + virtual VideoGrabberBase * CreateGrabber(const wxString & id); + virtual void DeleteGrabber(VideoGrabberBase * grabber); + virtual void RefreshDevices(); +private: + wxString m_ID; + CDeviceManager * m_pDeviceManager; +}; + +#endif // _VIDEOSOURCEDIRECTSHOWPLUGIN_H \ No newline at end of file diff --git a/VideoSourceDirectShow/Win/Qedit.h b/VideoSourceDirectShow/Win/Qedit.h new file mode 100644 index 0000000..31aab12 --- /dev/null +++ b/VideoSourceDirectShow/Win/Qedit.h @@ -0,0 +1,142 @@ +#pragma once + +#ifndef __ISampleGrabberCB_INTERFACE_DEFINED__ +#define __ISampleGrabberCB_INTERFACE_DEFINED__ + +#include + +/* interface ISampleGrabberCB */ +/* [unique][helpstring][local][uuid][object] */ + +EXTERN_C const IID IID_ISampleGrabberCB; + +MIDL_INTERFACE("0579154A-2B53-4994-B0D0-E773148EFF85") +ISampleGrabberCB : public IUnknown +{ +public: + virtual HRESULT STDMETHODCALLTYPE SampleCB( + double SampleTime, + IMediaSample * pSample) = 0; + + virtual HRESULT STDMETHODCALLTYPE BufferCB( + double SampleTime, + BYTE* pBuffer, + long BufferLen) = 0; +}; + +#endif /* __ISampleGrabberCB_INTERFACE_DEFINED__ */ + +#ifndef __ISampleGrabber_INTERFACE_DEFINED__ +#define __ISampleGrabber_INTERFACE_DEFINED__ + +/* interface ISampleGrabber */ +/* [unique][helpstring][local][uuid][object] */ + +EXTERN_C const IID IID_ISampleGrabber; + +MIDL_INTERFACE("6B652FFF-11FE-4fce-92AD-0266B5D7C78F") +ISampleGrabber : public IUnknown +{ +public: + virtual HRESULT STDMETHODCALLTYPE SetOneShot( + BOOL OneShot) = 0; + + virtual HRESULT STDMETHODCALLTYPE SetMediaType( + const AM_MEDIA_TYPE* pType) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetConnectedMediaType( + AM_MEDIA_TYPE* pType) = 0; + + virtual HRESULT STDMETHODCALLTYPE SetBufferSamples( + BOOL BufferThem) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetCurrentBuffer( + /* [out][in] */ long* pBufferSize, + /* [out] */ long* pBuffer) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetCurrentSample( + /* [retval][out] */ IMediaSample** ppSample) = 0; + + virtual HRESULT STDMETHODCALLTYPE SetCallback( + ISampleGrabberCB* pCallback, + long WhichMethodToCallback) = 0; +}; + +DEFINE_GUID(CLSID_SampleGrabber, 0xc1f400a0, 0x3f08, 0x11d3, 0x9f, 0x0b, 0x00, 0x60, 0x08, 0x03, 0x9e, 0x37); + +#endif /* __ISampleGrabber_INTERFACE_DEFINED__ */ + +#ifndef __IMediaDet_INTERFACE_DEFINED__ +#define __IMediaDet_INTERFACE_DEFINED__ + +/* interface IMediaDet */ +/* [unique][helpstring][uuid][object] */ + +EXTERN_C const IID IID_IMediaDet; + +MIDL_INTERFACE("65BD0710-24D2-4ff7-9324-ED2E5D3ABAFA") +IMediaDet : public IUnknown +{ +public: + virtual /* [helpstring][id][propget] */ HRESULT STDMETHODCALLTYPE get_Filter( + /* [retval][out] */ __RPC__deref_out_opt IUnknown * *pVal) = 0; + + virtual /* [helpstring][id][propput] */ HRESULT STDMETHODCALLTYPE put_Filter( + /* [in] */ __RPC__in_opt IUnknown* newVal) = 0; + + virtual /* [helpstring][id][propget] */ HRESULT STDMETHODCALLTYPE get_OutputStreams( + /* [retval][out] */ __RPC__out long* pVal) = 0; + + virtual /* [helpstring][id][propget] */ HRESULT STDMETHODCALLTYPE get_CurrentStream( + /* [retval][out] */ __RPC__out long* pVal) = 0; + + virtual /* [helpstring][id][propput] */ HRESULT STDMETHODCALLTYPE put_CurrentStream( + /* [in] */ long newVal) = 0; + + virtual /* [helpstring][id][propget] */ HRESULT STDMETHODCALLTYPE get_StreamType( + /* [retval][out] */ __RPC__out GUID* pVal) = 0; + + virtual /* [helpstring][id][propget] */ HRESULT STDMETHODCALLTYPE get_StreamTypeB( + /* [retval][out] */ __RPC__deref_out_opt BSTR* pVal) = 0; + + virtual /* [helpstring][id][propget] */ HRESULT STDMETHODCALLTYPE get_StreamLength( + /* [retval][out] */ __RPC__out double* pVal) = 0; + + virtual /* [helpstring][id][propget] */ HRESULT STDMETHODCALLTYPE get_Filename( + /* [retval][out] */ __RPC__deref_out_opt BSTR* pVal) = 0; + + virtual /* [helpstring][id][propput] */ HRESULT STDMETHODCALLTYPE put_Filename( + /* [in] */ __RPC__in BSTR newVal) = 0; + + virtual /* [helpstring][id] */ HRESULT STDMETHODCALLTYPE GetBitmapBits( + double StreamTime, + __RPC__in long* pBufferSize, + __RPC__in char* pBuffer, + long Width, + long Height) = 0; + + virtual /* [helpstring][id] */ HRESULT STDMETHODCALLTYPE WriteBitmapBits( + double StreamTime, + long Width, + long Height, + __RPC__in BSTR Filename) = 0; + + virtual /* [helpstring][id][propget] */ HRESULT STDMETHODCALLTYPE get_StreamMediaType( + /* [retval][out] */ __RPC__out AM_MEDIA_TYPE* pVal) = 0; + + virtual /* [helpstring][id] */ HRESULT STDMETHODCALLTYPE GetSampleGrabber( + /* [out] */ __RPC__deref_out_opt ISampleGrabber** ppVal) = 0; + + virtual /* [helpstring][id][propget] */ HRESULT STDMETHODCALLTYPE get_FrameRate( + /* [retval][out] */ __RPC__out double* pVal) = 0; + + virtual /* [helpstring][id] */ HRESULT STDMETHODCALLTYPE EnterBitmapGrabMode( + double SeekTime) = 0; +}; + +#endif /* __IMediaDet_INTERFACE_DEFINED__ */ + +EXTERN_C const CLSID CLSID_MediaDet; + +class DECLSPEC_UUID("65BD0711-24D2-4ff7-9324-ED2E5D3ABAFA") + MediaDet; diff --git a/VideoSourceDirectShow/Win/VideoSourceDirectShow.dll b/VideoSourceDirectShow/Win/VideoSourceDirectShow.dll new file mode 100644 index 0000000000000000000000000000000000000000..7319ae50c9913c2dd7451005e4f9741c41b970d6 GIT binary patch literal 56320 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 TXDzrESzBH!uXUwxV?O>3&h@ZC literal 0 HcmV?d00001 diff --git a/VideoSourceDirectShow/Win/VideoSourceDirectShow.vcxproj b/VideoSourceDirectShow/Win/VideoSourceDirectShow.vcxproj new file mode 100644 index 0000000..26ee474 --- /dev/null +++ b/VideoSourceDirectShow/Win/VideoSourceDirectShow.vcxproj @@ -0,0 +1,392 @@ + + + + x64 + + + + Debug + x64 + + + Release + x64 + + + + {CED1E685-B9B8-3A52-B44F-57BB9BCEC78D} + Win32Proj + 10.0.26100.0 + x64 + VideoSourceDirectShow + NoUpgrade + + + + DynamicLibrary + Unicode + v143 + + + DynamicLibrary + Unicode + v143 + + + + + + + + + + <_ProjectFileVersion>10.0.20506.1 + C:\Users\Admin\IFloor\projects\wxVideoProcessing\bin\ + VideoSourceDirectShow.dir\Debug\ + VideoSourceDirectShow + .dll + true + true + C:\Users\Admin\IFloor\projects\wxVideoProcessing\bin\ + VideoSourceDirectShow.dir\Release\ + VideoSourceDirectShow + .dll + false + true + + + + + C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\mswu;C:\Users\Admin\IFloor\libs\wxWidgets\include;C:\Users\Admin\IFloor\libs\opencv\build\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\Utils;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\MotionDetectorCore;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxJSON\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxxmlserializer\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxxmlserializer;C:\Users\Admin\IFloor\projects\wxVideoProcessing\VideoSourceDirectShow\DSNative;C:\Users\Admin\IFloor\projects\wxVideoProcessing\VideoSourceDirectShow\..\VideoSourcePluginBase;C:\Users\Admin\IFloor\projects\wxVideoProcessing\VideoSourceDirectShow\..\CommonPluginBase;C:\Users\Admin\IFloor\projects\wxVideoProcessing\VideoSourceDirectShow\..\ThirdParty\wxXS\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\VideoSourceDirectShow\..\ThirdParty\strmbas;C:\Users\Admin\IFloor\projects\wxVideoProcessing\ThirdParty\wxXS\build\..\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\ThirdParty\wxJSON\build\..\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\ThirdParty\strmbas;%(AdditionalIncludeDirectories) + $(IntDir) + EnableFastChecks + + + ProgramDatabase + 4996 + Sync + + + Disabled + stdcpp17 + + true + Disabled + Use + C:/Users/Admin/IFloor/projects/wxVideoProcessing/VideoSourceDirectShow/Win/CMakeFiles/VideoSourceDirectShow.dir/Debug/cmake_pch.hxx + C:/Users/Admin/IFloor/projects/wxVideoProcessing/VideoSourceDirectShow/Win/VideoSourceDirectShow.dir/Debug//cmake_pch.pch + + MultiThreadedDebugDLL + true + + + false + %(PreprocessorDefinitions);WIN32;_WINDOWS;__WXDEBUG__=1;WIN32_LEAN_AND_MEAN;WXUSINGDLL;UNICODE;_UNICODE;_CRT_SECURE_NO_DEPRECATE;_USRDLL;EXPORTS_API=;CMAKE_INTDIR="Debug";VideoSourceDirectShow_EXPORTS + $(IntDir) + false + + + %(PreprocessorDefinitions);WIN32;_DEBUG;_WINDOWS;__WXDEBUG__=1;WIN32_LEAN_AND_MEAN;WXUSINGDLL;UNICODE;_UNICODE;_CRT_SECURE_NO_DEPRECATE;_USRDLL;EXPORTS_API=;CMAKE_INTDIR=\"Debug\";VideoSourceDirectShow_EXPORTS + C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\mswu;C:\Users\Admin\IFloor\libs\wxWidgets\include;C:\Users\Admin\IFloor\libs\opencv\build\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\Utils;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\MotionDetectorCore;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxJSON\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxxmlserializer\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxxmlserializer;C:\Users\Admin\IFloor\projects\wxVideoProcessing\VideoSourceDirectShow\DSNative;C:\Users\Admin\IFloor\projects\wxVideoProcessing\VideoSourceDirectShow\..\VideoSourcePluginBase;C:\Users\Admin\IFloor\projects\wxVideoProcessing\VideoSourceDirectShow\..\CommonPluginBase;C:\Users\Admin\IFloor\projects\wxVideoProcessing\VideoSourceDirectShow\..\ThirdParty\wxXS\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\VideoSourceDirectShow\..\ThirdParty\strmbas;C:\Users\Admin\IFloor\projects\wxVideoProcessing\ThirdParty\wxXS\build\..\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\ThirdParty\wxJSON\build\..\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\ThirdParty\strmbas;%(AdditionalIncludeDirectories) + + + C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\mswu;C:\Users\Admin\IFloor\libs\wxWidgets\include;C:\Users\Admin\IFloor\libs\opencv\build\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\Utils;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\MotionDetectorCore;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxJSON\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxxmlserializer\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxxmlserializer;C:\Users\Admin\IFloor\projects\wxVideoProcessing\VideoSourceDirectShow\DSNative;C:\Users\Admin\IFloor\projects\wxVideoProcessing\VideoSourceDirectShow\..\VideoSourcePluginBase;C:\Users\Admin\IFloor\projects\wxVideoProcessing\VideoSourceDirectShow\..\CommonPluginBase;C:\Users\Admin\IFloor\projects\wxVideoProcessing\VideoSourceDirectShow\..\ThirdParty\wxXS\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\VideoSourceDirectShow\..\ThirdParty\strmbas;C:\Users\Admin\IFloor\projects\wxVideoProcessing\ThirdParty\wxXS\build\..\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\ThirdParty\wxJSON\build\..\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\ThirdParty\strmbas;%(AdditionalIncludeDirectories) + $(ProjectDir)/$(IntDir) + %(Filename).h + %(Filename).tlb + %(Filename)_i.c + %(Filename)_p.c + + + + setlocal +"C:\Program Files\CMake\bin\cmake.exe" -E copy_if_different "$(TargetPath)" "$(TargetDir)../$(TargetFileName)" +if %errorlevel% neq 0 goto :cmEnd +:cmEnd +endlocal & call :cmErrorLevel %errorlevel% & goto :cmDone +:cmErrorLevel +exit /b %1 +:cmDone +if %errorlevel% neq 0 goto :VCEnd +setlocal +"C:\Program Files\CMake\bin\cmake.exe" -E make_directory C:/Users/Admin/IFloor/projects/wxVideoProcessing/build/../bin/plugins/video_source +if %errorlevel% neq 0 goto :cmEnd +"C:\Program Files\CMake\bin\cmake.exe" -E copy_if_different C:/Users/Admin/IFloor/projects/wxVideoProcessing/bin/VideoSourceDirectShow.dll C:/Users/Admin/IFloor/projects/wxVideoProcessing/build/../bin/plugins/video_source/VideoSourceDirectShow.dll +if %errorlevel% neq 0 goto :cmEnd +:cmEnd +endlocal & call :cmErrorLevel %errorlevel% & goto :cmDone +:cmErrorLevel +exit /b %1 +:cmDone +if %errorlevel% neq 0 goto :VCEnd + + + C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxbase33ud.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33ud_core.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33ud_adv.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33ud_aui.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxbase33ud_net.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33ud_gl.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxbase33ud_xml.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33ud_propgrid.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33ud_html.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxpngd.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxtiffd.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxjpegd.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxzlibd.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxregexud.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxexpatd.lib;opengl32.lib;glu32.lib;winmm.lib;comctl32.lib;uuid.lib;oleacc.lib;uxtheme.lib;rpcrt4.lib;shlwapi.lib;version.lib;wsock32.lib;C:\Users\Admin\IFloor\projects\wxVideoProcessing\bin\lib\Debug\VideoSourcePluginBase.lib;C:\Users\Admin\IFloor\projects\wxVideoProcessing\bin\lib\Debug\MotionDetectorPluginBase.lib;C:\Users\Admin\IFloor\projects\wxVideoProcessing\bin\lib\Debug\Utils.lib;C:\Users\Admin\IFloor\projects\wxVideoProcessing\bin\lib\Debug\strmbas.lib;strmiids.lib;quartz.lib;ole32.lib;oleaut32.lib;winmm.lib;C:\Users\Admin\IFloor\projects\wxVideoProcessing\bin\lib\Debug\CommonPluginBase.lib;C:\Users\Admin\IFloor\projects\wxVideoProcessing\bin\lib\Debug\wxXS.lib;C:\Users\Admin\IFloor\projects\wxVideoProcessing\bin\lib\Debug\wxJSON.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxbase33ud.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33ud_core.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33ud_adv.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33ud_aui.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxbase33ud_net.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33ud_gl.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxbase33ud_xml.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33ud_propgrid.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33ud_html.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxpngd.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxtiffd.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxjpegd.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxzlibd.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxregexud.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxexpatd.lib;opengl32.lib;glu32.lib;winmm.lib;comctl32.lib;uuid.lib;oleacc.lib;uxtheme.lib;rpcrt4.lib;shlwapi.lib;version.lib;wsock32.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;comdlg32.lib;advapi32.lib + C:/Users/Admin/IFloor/projects/wxVideoProcessing/build/Win/lib;C:/Users/Admin/IFloor/projects/wxVideoProcessing/build/Win/lib/$(Configuration);%(AdditionalLibraryDirectories) + %(AdditionalOptions) /machine:x64 + + true + atlthunk.lib;%(IgnoreSpecificDefaultLibraries) + + C:/Users/Admin/IFloor/projects/wxVideoProcessing/bin/lib/Debug/VideoSourceDirectShow.lib + + C:/Users/Admin/IFloor/projects/wxVideoProcessing/VideoSourceDirectShow/VideoSourceDirectShow.def + C:/Users/Admin/IFloor/projects/wxVideoProcessing/bin/VideoSourceDirectShow.pdb + + Console + + + false + + + + + C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\mswu;C:\Users\Admin\IFloor\libs\wxWidgets\include;C:\Users\Admin\IFloor\libs\opencv\build\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\Utils;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\MotionDetectorCore;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxJSON\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxxmlserializer\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxxmlserializer;C:\Users\Admin\IFloor\projects\wxVideoProcessing\VideoSourceDirectShow\DSNative;C:\Users\Admin\IFloor\projects\wxVideoProcessing\VideoSourceDirectShow\..\VideoSourcePluginBase;C:\Users\Admin\IFloor\projects\wxVideoProcessing\VideoSourceDirectShow\..\CommonPluginBase;C:\Users\Admin\IFloor\projects\wxVideoProcessing\VideoSourceDirectShow\..\ThirdParty\wxXS\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\VideoSourceDirectShow\..\ThirdParty\strmbas;C:\Users\Admin\IFloor\projects\wxVideoProcessing\ThirdParty\wxXS\build\..\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\ThirdParty\wxJSON\build\..\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\ThirdParty\strmbas;%(AdditionalIncludeDirectories) + $(IntDir) + Default + + + 4996 + Sync + + + AnySuitable + stdcpp17 + + true + MaxSpeed + Use + C:/Users/Admin/IFloor/projects/wxVideoProcessing/VideoSourceDirectShow/Win/CMakeFiles/VideoSourceDirectShow.dir/Release/cmake_pch.hxx + C:/Users/Admin/IFloor/projects/wxVideoProcessing/VideoSourceDirectShow/Win/VideoSourceDirectShow.dir/Release//cmake_pch.pch + + MultiThreadedDLL + true + + + false + %(PreprocessorDefinitions);WIN32;_WINDOWS;NDEBUG;WIN32_LEAN_AND_MEAN;WXUSINGDLL;UNICODE;_UNICODE;_CRT_SECURE_NO_DEPRECATE;_USRDLL;EXPORTS_API=;CMAKE_INTDIR="Release";VideoSourceDirectShow_EXPORTS + $(IntDir) + + + false + + + %(PreprocessorDefinitions);WIN32;_WINDOWS;NDEBUG;WIN32_LEAN_AND_MEAN;WXUSINGDLL;UNICODE;_UNICODE;_CRT_SECURE_NO_DEPRECATE;_USRDLL;EXPORTS_API=;CMAKE_INTDIR=\"Release\";VideoSourceDirectShow_EXPORTS + C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\mswu;C:\Users\Admin\IFloor\libs\wxWidgets\include;C:\Users\Admin\IFloor\libs\opencv\build\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\Utils;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\MotionDetectorCore;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxJSON\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxxmlserializer\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxxmlserializer;C:\Users\Admin\IFloor\projects\wxVideoProcessing\VideoSourceDirectShow\DSNative;C:\Users\Admin\IFloor\projects\wxVideoProcessing\VideoSourceDirectShow\..\VideoSourcePluginBase;C:\Users\Admin\IFloor\projects\wxVideoProcessing\VideoSourceDirectShow\..\CommonPluginBase;C:\Users\Admin\IFloor\projects\wxVideoProcessing\VideoSourceDirectShow\..\ThirdParty\wxXS\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\VideoSourceDirectShow\..\ThirdParty\strmbas;C:\Users\Admin\IFloor\projects\wxVideoProcessing\ThirdParty\wxXS\build\..\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\ThirdParty\wxJSON\build\..\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\ThirdParty\strmbas;%(AdditionalIncludeDirectories) + + + C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\mswu;C:\Users\Admin\IFloor\libs\wxWidgets\include;C:\Users\Admin\IFloor\libs\opencv\build\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\Utils;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\MotionDetectorCore;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxJSON\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxxmlserializer\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxxmlserializer;C:\Users\Admin\IFloor\projects\wxVideoProcessing\VideoSourceDirectShow\DSNative;C:\Users\Admin\IFloor\projects\wxVideoProcessing\VideoSourceDirectShow\..\VideoSourcePluginBase;C:\Users\Admin\IFloor\projects\wxVideoProcessing\VideoSourceDirectShow\..\CommonPluginBase;C:\Users\Admin\IFloor\projects\wxVideoProcessing\VideoSourceDirectShow\..\ThirdParty\wxXS\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\VideoSourceDirectShow\..\ThirdParty\strmbas;C:\Users\Admin\IFloor\projects\wxVideoProcessing\ThirdParty\wxXS\build\..\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\ThirdParty\wxJSON\build\..\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\ThirdParty\strmbas;%(AdditionalIncludeDirectories) + $(ProjectDir)/$(IntDir) + %(Filename).h + %(Filename).tlb + %(Filename)_i.c + %(Filename)_p.c + + + + setlocal +"C:\Program Files\CMake\bin\cmake.exe" -E copy_if_different "$(TargetPath)" "$(TargetDir)../$(TargetFileName)" +if %errorlevel% neq 0 goto :cmEnd +:cmEnd +endlocal & call :cmErrorLevel %errorlevel% & goto :cmDone +:cmErrorLevel +exit /b %1 +:cmDone +if %errorlevel% neq 0 goto :VCEnd +setlocal +"C:\Program Files\CMake\bin\cmake.exe" -E make_directory C:/Users/Admin/IFloor/projects/wxVideoProcessing/build/../bin/plugins/video_source +if %errorlevel% neq 0 goto :cmEnd +"C:\Program Files\CMake\bin\cmake.exe" -E copy_if_different C:/Users/Admin/IFloor/projects/wxVideoProcessing/bin/VideoSourceDirectShow.dll C:/Users/Admin/IFloor/projects/wxVideoProcessing/build/../bin/plugins/video_source/VideoSourceDirectShow.dll +if %errorlevel% neq 0 goto :cmEnd +:cmEnd +endlocal & call :cmErrorLevel %errorlevel% & goto :cmDone +:cmErrorLevel +exit /b %1 +:cmDone +if %errorlevel% neq 0 goto :VCEnd + + + C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxbase33u.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33u_core.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33u_adv.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33u_aui.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxbase33u_net.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33u_gl.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxbase33u_xml.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33u_propgrid.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33u_html.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxpng.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxtiff.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxjpeg.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxzlib.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxregexu.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxexpat.lib;opengl32.lib;glu32.lib;winmm.lib;comctl32.lib;uuid.lib;oleacc.lib;uxtheme.lib;rpcrt4.lib;shlwapi.lib;version.lib;wsock32.lib;C:\Users\Admin\IFloor\projects\wxVideoProcessing\bin\lib\Release\VideoSourcePluginBase.lib;C:\Users\Admin\IFloor\projects\wxVideoProcessing\bin\lib\Release\MotionDetectorPluginBase.lib;C:\Users\Admin\IFloor\projects\wxVideoProcessing\bin\lib\Release\Utils.lib;C:\Users\Admin\IFloor\projects\wxVideoProcessing\bin\lib\Release\strmbas.lib;strmiids.lib;quartz.lib;ole32.lib;oleaut32.lib;winmm.lib;C:\Users\Admin\IFloor\projects\wxVideoProcessing\bin\lib\Release\CommonPluginBase.lib;C:\Users\Admin\IFloor\projects\wxVideoProcessing\bin\lib\Release\wxXS.lib;C:\Users\Admin\IFloor\projects\wxVideoProcessing\bin\lib\Release\wxJSON.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxbase33u.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33u_core.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33u_adv.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33u_aui.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxbase33u_net.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33u_gl.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxbase33u_xml.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33u_propgrid.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33u_html.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxpng.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxtiff.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxjpeg.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxzlib.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxregexu.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxexpat.lib;opengl32.lib;glu32.lib;winmm.lib;comctl32.lib;uuid.lib;oleacc.lib;uxtheme.lib;rpcrt4.lib;shlwapi.lib;version.lib;wsock32.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;comdlg32.lib;advapi32.lib + C:/Users/Admin/IFloor/projects/wxVideoProcessing/build/Win/lib;C:/Users/Admin/IFloor/projects/wxVideoProcessing/build/Win/lib/$(Configuration);%(AdditionalLibraryDirectories) + %(AdditionalOptions) /machine:x64 + + false + atlthunk.lib;%(IgnoreSpecificDefaultLibraries) + + C:/Users/Admin/IFloor/projects/wxVideoProcessing/bin/lib/Release/VideoSourceDirectShow.lib + + C:/Users/Admin/IFloor/projects/wxVideoProcessing/VideoSourceDirectShow/VideoSourceDirectShow.def + C:/Users/Admin/IFloor/projects/wxVideoProcessing/bin/VideoSourceDirectShow.pdb + + Console + + + false + + + + + Always + Building Custom Rule C:/Users/Admin/IFloor/projects/wxVideoProcessing/VideoSourceDirectShow/CMakeLists.txt + setlocal +"C:\Program Files\CMake\bin\cmake.exe" -SC:/Users/Admin/IFloor/projects/wxVideoProcessing/build -BC:/Users/Admin/IFloor/projects/wxVideoProcessing/build/Win --check-stamp-file C:/Users/Admin/IFloor/projects/wxVideoProcessing/VideoSourceDirectShow/Win/CMakeFiles/generate.stamp +if %errorlevel% neq 0 goto :cmEnd +:cmEnd +endlocal & call :cmErrorLevel %errorlevel% & goto :cmDone +:cmErrorLevel +exit /b %1 +:cmDone +if %errorlevel% neq 0 goto :VCEnd + %(AdditionalInputs) + C:\Users\Admin\IFloor\projects\wxVideoProcessing\VideoSourceDirectShow\Win\CMakeFiles\generate.stamp + false + Building Custom Rule C:/Users/Admin/IFloor/projects/wxVideoProcessing/VideoSourceDirectShow/CMakeLists.txt + setlocal +"C:\Program Files\CMake\bin\cmake.exe" -SC:/Users/Admin/IFloor/projects/wxVideoProcessing/build -BC:/Users/Admin/IFloor/projects/wxVideoProcessing/build/Win --check-stamp-file C:/Users/Admin/IFloor/projects/wxVideoProcessing/VideoSourceDirectShow/Win/CMakeFiles/generate.stamp +if %errorlevel% neq 0 goto :cmEnd +:cmEnd +endlocal & call :cmErrorLevel %errorlevel% & goto :cmDone +:cmErrorLevel +exit /b %1 +:cmDone +if %errorlevel% neq 0 goto :VCEnd + %(AdditionalInputs) + C:\Users\Admin\IFloor\projects\wxVideoProcessing\VideoSourceDirectShow\Win\CMakeFiles\generate.stamp + false + + + + + C:/Users/Admin/IFloor/projects/wxVideoProcessing/VideoSourceDirectShow/Win/CMakeFiles/VideoSourceDirectShow.dir/Debug/cmake_pch.hxx;%(ForcedIncludeFiles) + Create + C:/Users/Admin/IFloor/projects/wxVideoProcessing/VideoSourceDirectShow/Win/CMakeFiles/VideoSourceDirectShow.dir/Debug/cmake_pch.hxx + C:/Users/Admin/IFloor/projects/wxVideoProcessing/VideoSourceDirectShow/Win/VideoSourceDirectShow.dir/Debug//cmake_pch.pch + C:/Users/Admin/IFloor/projects/wxVideoProcessing/VideoSourceDirectShow/Win/CMakeFiles/VideoSourceDirectShow.dir/Release/cmake_pch.hxx;%(ForcedIncludeFiles) + Create + C:/Users/Admin/IFloor/projects/wxVideoProcessing/VideoSourceDirectShow/Win/CMakeFiles/VideoSourceDirectShow.dir/Release/cmake_pch.hxx + C:/Users/Admin/IFloor/projects/wxVideoProcessing/VideoSourceDirectShow/Win/VideoSourceDirectShow.dir/Release//cmake_pch.pch + + + C:/Users/Admin/IFloor/projects/wxVideoProcessing/VideoSourceDirectShow/Win/CMakeFiles/VideoSourceDirectShow.dir/Debug/cmake_pch.hxx;%(ForcedIncludeFiles) + C:/Users/Admin/IFloor/projects/wxVideoProcessing/VideoSourceDirectShow/Win/CMakeFiles/VideoSourceDirectShow.dir/Release/cmake_pch.hxx;%(ForcedIncludeFiles) + + + C:/Users/Admin/IFloor/projects/wxVideoProcessing/VideoSourceDirectShow/Win/CMakeFiles/VideoSourceDirectShow.dir/Debug/cmake_pch.hxx;%(ForcedIncludeFiles) + C:/Users/Admin/IFloor/projects/wxVideoProcessing/VideoSourceDirectShow/Win/CMakeFiles/VideoSourceDirectShow.dir/Release/cmake_pch.hxx;%(ForcedIncludeFiles) + + + C:/Users/Admin/IFloor/projects/wxVideoProcessing/VideoSourceDirectShow/Win/CMakeFiles/VideoSourceDirectShow.dir/Debug/cmake_pch.hxx;%(ForcedIncludeFiles) + C:/Users/Admin/IFloor/projects/wxVideoProcessing/VideoSourceDirectShow/Win/CMakeFiles/VideoSourceDirectShow.dir/Release/cmake_pch.hxx;%(ForcedIncludeFiles) + + + C:/Users/Admin/IFloor/projects/wxVideoProcessing/VideoSourceDirectShow/Win/CMakeFiles/VideoSourceDirectShow.dir/Debug/cmake_pch.hxx;%(ForcedIncludeFiles) + C:/Users/Admin/IFloor/projects/wxVideoProcessing/VideoSourceDirectShow/Win/CMakeFiles/VideoSourceDirectShow.dir/Release/cmake_pch.hxx;%(ForcedIncludeFiles) + + + + + + + C:/Users/Admin/IFloor/projects/wxVideoProcessing/VideoSourceDirectShow/Win/CMakeFiles/VideoSourceDirectShow.dir/Debug/cmake_pch.hxx;%(ForcedIncludeFiles) + C:/Users/Admin/IFloor/projects/wxVideoProcessing/VideoSourceDirectShow/Win/CMakeFiles/VideoSourceDirectShow.dir/Release/cmake_pch.hxx;%(ForcedIncludeFiles) + + + C:/Users/Admin/IFloor/projects/wxVideoProcessing/VideoSourceDirectShow/Win/CMakeFiles/VideoSourceDirectShow.dir/Debug/cmake_pch.hxx;%(ForcedIncludeFiles) + C:/Users/Admin/IFloor/projects/wxVideoProcessing/VideoSourceDirectShow/Win/CMakeFiles/VideoSourceDirectShow.dir/Release/cmake_pch.hxx;%(ForcedIncludeFiles) + + + C:/Users/Admin/IFloor/projects/wxVideoProcessing/VideoSourceDirectShow/Win/CMakeFiles/VideoSourceDirectShow.dir/Debug/cmake_pch.hxx;%(ForcedIncludeFiles) + C:/Users/Admin/IFloor/projects/wxVideoProcessing/VideoSourceDirectShow/Win/CMakeFiles/VideoSourceDirectShow.dir/Release/cmake_pch.hxx;%(ForcedIncludeFiles) + + + C:/Users/Admin/IFloor/projects/wxVideoProcessing/VideoSourceDirectShow/Win/CMakeFiles/VideoSourceDirectShow.dir/Debug/cmake_pch.hxx;%(ForcedIncludeFiles) + C:/Users/Admin/IFloor/projects/wxVideoProcessing/VideoSourceDirectShow/Win/CMakeFiles/VideoSourceDirectShow.dir/Release/cmake_pch.hxx;%(ForcedIncludeFiles) + + + C:/Users/Admin/IFloor/projects/wxVideoProcessing/VideoSourceDirectShow/Win/CMakeFiles/VideoSourceDirectShow.dir/Debug/cmake_pch.hxx;%(ForcedIncludeFiles) + C:/Users/Admin/IFloor/projects/wxVideoProcessing/VideoSourceDirectShow/Win/CMakeFiles/VideoSourceDirectShow.dir/Release/cmake_pch.hxx;%(ForcedIncludeFiles) + + + C:/Users/Admin/IFloor/projects/wxVideoProcessing/VideoSourceDirectShow/Win/CMakeFiles/VideoSourceDirectShow.dir/Debug/cmake_pch.hxx;%(ForcedIncludeFiles) + C:/Users/Admin/IFloor/projects/wxVideoProcessing/VideoSourceDirectShow/Win/CMakeFiles/VideoSourceDirectShow.dir/Release/cmake_pch.hxx;%(ForcedIncludeFiles) + + + C:/Users/Admin/IFloor/projects/wxVideoProcessing/VideoSourceDirectShow/Win/CMakeFiles/VideoSourceDirectShow.dir/Debug/cmake_pch.hxx;%(ForcedIncludeFiles) + C:/Users/Admin/IFloor/projects/wxVideoProcessing/VideoSourceDirectShow/Win/CMakeFiles/VideoSourceDirectShow.dir/Release/cmake_pch.hxx;%(ForcedIncludeFiles) + + + C:/Users/Admin/IFloor/projects/wxVideoProcessing/VideoSourceDirectShow/Win/CMakeFiles/VideoSourceDirectShow.dir/Debug/cmake_pch.hxx;%(ForcedIncludeFiles) + C:/Users/Admin/IFloor/projects/wxVideoProcessing/VideoSourceDirectShow/Win/CMakeFiles/VideoSourceDirectShow.dir/Release/cmake_pch.hxx;%(ForcedIncludeFiles) + + + C:/Users/Admin/IFloor/projects/wxVideoProcessing/VideoSourceDirectShow/Win/CMakeFiles/VideoSourceDirectShow.dir/Debug/cmake_pch.hxx;%(ForcedIncludeFiles) + C:/Users/Admin/IFloor/projects/wxVideoProcessing/VideoSourceDirectShow/Win/CMakeFiles/VideoSourceDirectShow.dir/Release/cmake_pch.hxx;%(ForcedIncludeFiles) + + + C:/Users/Admin/IFloor/projects/wxVideoProcessing/VideoSourceDirectShow/Win/CMakeFiles/VideoSourceDirectShow.dir/Debug/cmake_pch.hxx;%(ForcedIncludeFiles) + C:/Users/Admin/IFloor/projects/wxVideoProcessing/VideoSourceDirectShow/Win/CMakeFiles/VideoSourceDirectShow.dir/Release/cmake_pch.hxx;%(ForcedIncludeFiles) + + + C:/Users/Admin/IFloor/projects/wxVideoProcessing/VideoSourceDirectShow/Win/CMakeFiles/VideoSourceDirectShow.dir/Debug/cmake_pch.hxx;%(ForcedIncludeFiles) + C:/Users/Admin/IFloor/projects/wxVideoProcessing/VideoSourceDirectShow/Win/CMakeFiles/VideoSourceDirectShow.dir/Release/cmake_pch.hxx;%(ForcedIncludeFiles) + + + + + + + + + + + + + + + C:/Users/Admin/IFloor/projects/wxVideoProcessing/VideoSourceDirectShow/Win/CMakeFiles/VideoSourceDirectShow.dir/Debug/cmake_pch.hxx;%(ForcedIncludeFiles) + C:/Users/Admin/IFloor/projects/wxVideoProcessing/VideoSourceDirectShow/Win/CMakeFiles/VideoSourceDirectShow.dir/Release/cmake_pch.hxx;%(ForcedIncludeFiles) + + + + + + + + {6F4BDD20-5B99-3029-BDF0-7D719B283AF5} + ZERO_CHECK + false + Never + + + {751A15C0-76C2-3506-AB9D-0CD3545003FA} + CommonPluginBase + + + {1F4B7EAD-4694-3453-AD34-1757E387661E} + MotionDetectorPluginBase + + + {14D82890-BB7F-3748-8713-43304E91CFA1} + Utils + + + {08E74159-4CA2-36BC-B137-45C65F9868A7} + VideoSourcePluginBase + + + {23CF91DE-A1B5-3EE0-827D-ACAFD80C7CB2} + strmbas + + + {7887B55C-77B9-3262-AFAD-ABA738C61029} + wxJSON + + + {78CFCEBE-5466-3128-9717-547416DBFFE5} + wxXS + + + + + + \ No newline at end of file diff --git a/VideoSourceDirectShow/Win/VideoSourceDirectShow.vcxproj.filters b/VideoSourceDirectShow/Win/VideoSourceDirectShow.vcxproj.filters new file mode 100644 index 0000000..ca72303 --- /dev/null +++ b/VideoSourceDirectShow/Win/VideoSourceDirectShow.vcxproj.filters @@ -0,0 +1,146 @@ + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + DSNative\Sources + + + DSNative\Sources + + + DSNative\Sources + + + DSNative\Sources + + + DSNative\Sources + + + DSNative\DirectShow\Sources + + + DSNative\DirectShow\Sources + + + DSNative\DirectShow\Sources + + + DSNative\DirectShow\Sources + + + DSNative\DirectShow\Sources + + + DSNative\DirectShow\Sources + + + Source Files + + + + + Header Files + + + Header Files + + + Header Files + + + DSNative\Include + + + DSNative\Include + + + DSNative\Include + + + DSNative\Include + + + DSNative\Include + + + DSNative\DirectShow\Include + + + DSNative\DirectShow\Include + + + DSNative\DirectShow\Include + + + DSNative\DirectShow\Include + + + DSNative\DirectShow\Include + + + DSNative\DirectShow\Include + + + Header Files + + + Precompile Header File + + + Precompile Header File + + + + + + + + Source Files + + + + + {92D75CDB-6B0D-36E2-B6CD-6A45DA6910EF} + + + {5230ED5F-4004-3097-AD60-9838A9A11E79} + + + {E59ADCBE-1FB0-3336-8CF7-0AFA452EC6A3} + + + {A30B23EA-255B-3698-99B5-7D45717AF558} + + + {4B99EE2E-88CC-366B-B27A-5CE169B8688C} + + + {A8A68941-16AF-3A8A-9CFE-F73D7F1D5ACB} + + + {4951AEE1-75F2-3C38-B593-C6BC8C809FB0} + + + {112D4227-BB51-3360-BB5B-5EA5DBA5A65F} + + + {05450AB2-CB7B-34EA-83DF-1EE86424EDF0} + + + diff --git a/VideoSourcePluginBase/CMakeLists.txt b/VideoSourcePluginBase/CMakeLists.txt new file mode 100644 index 0000000..a688b42 --- /dev/null +++ b/VideoSourcePluginBase/CMakeLists.txt @@ -0,0 +1,48 @@ +# +# Stuff the average programmer needs to change +# +set(SRCS + VideoSourcePluginBase.cpp + VideoGrabberBase.cpp + VideoSourceConfigWindowBase.cpp +) +set(HFILES + VideoSourcePluginBase.h + VideoGrabberBase.h + VideoSourceConfigWindowBase.h + VideoSourcePlugin.h +) + +set(INCLUDE_DIRECTORIES ${BASE_INCLUDE_DIRECTORIES} + ${THIRD_PARTY_DIR}/wxXS/include + ${PROJECT_ROOT_DIR}/CommonPluginBase +) + +set(LIBRARY_NAME VideoSourcePluginBase) + +if(WIN32) + set(PREPROCESSOR_DEFINITIONS ${PREPROCESSOR_DEFINITIONS};/D_USRDLL;/DIFLOOR_EXPORTS) +endif(WIN32) + +set(SRCS ${SRCS} ${HFILES} ${PROJECT_ROOT_DIR}/include/stdwx.h ${PROJECT_ROOT_DIR}/include/stdwx.cpp) + +add_definitions(${PREPROCESSOR_DEFINITIONS}) +include_directories(${INCLUDE_DIRECTORIES}) + +add_library(${LIBRARY_NAME} SHARED ${SRCS}) + +target_link_libraries(${LIBRARY_NAME} + ${wxWidgets_LIBRARIES} + CommonPluginBase + Utils + wxXS +) + +add_dependencies(${LIBRARY_NAME} + CommonPluginBase +) + +target_precompile_headers(${LIBRARY_NAME} + PRIVATE + "${PROJECT_ROOT_DIR}/include/stdwx.h" +) \ No newline at end of file diff --git a/VideoSourcePluginBase/VideoGrabberBase.cpp b/VideoSourcePluginBase/VideoGrabberBase.cpp new file mode 100644 index 0000000..77fa772 --- /dev/null +++ b/VideoSourcePluginBase/VideoGrabberBase.cpp @@ -0,0 +1,27 @@ +#include "stdwx.h" +#include "VideoGrabberBase.h" +#include "VideoSourcePluginBase.h" +#include + +IMPLEMENT_ABSTRACT_CLASS(VideoGrabberBase, wxObject) + +WX_DEFINE_USER_EXPORTED_LIST(VideoGrabberBaseList); + +VideoGrabberBase::VideoGrabberBase() +: m_VideoSource(NULL) +{ +} + +VideoGrabberBase::VideoGrabberBase(VideoSourcePluginBase * owner) +: m_VideoSource(owner) +{ +} + +VideoGrabberBase::~VideoGrabberBase() +{ +} + +VideoSourcePluginBase * VideoGrabberBase::GetVideoSource() +{ + return m_VideoSource; +} diff --git a/VideoSourcePluginBase/VideoGrabberBase.h b/VideoSourcePluginBase/VideoGrabberBase.h new file mode 100644 index 0000000..63f1456 --- /dev/null +++ b/VideoSourcePluginBase/VideoGrabberBase.h @@ -0,0 +1,31 @@ +#ifndef _VIDEOGRABBERBASE_H +#define _VIDEOGRABBERBASE_H + +#include "VideoSourcePlugin.h" +#include + +class VideoSourcePluginBase; + +class IFLOOR_API VideoGrabberBase : public SerializableBase +{ + DECLARE_ABSTRACT_CLASS(VideoGrabberBase) +public: + VideoGrabberBase(); + VideoGrabberBase(VideoSourcePluginBase * owner); + virtual ~VideoGrabberBase(); + virtual wxSize GetFrameSize() = 0; + virtual size_t GetFrameDataLength() = 0; + virtual unsigned char * GetBuffer() = 0; + virtual unsigned char * GrabFrame(bool bUseInternalBuffer = true) = 0; + virtual int GrabFrame(unsigned char ** data, long * length) = 0; + virtual bool IsOK() = 0; + VideoSourcePluginBase * GetVideoSource(); + virtual CommonConfigWindowBase * CreateSettingsEditor(wxWindow * parent = NULL){return NULL;}; + +protected: + VideoSourcePluginBase * m_VideoSource; +}; + +WX_DECLARE_USER_EXPORTED_LIST(VideoGrabberBase, VideoGrabberBaseList, IFLOOR_API); //-V521 + +#endif // _VIDEOGRABBERBASE_H \ No newline at end of file diff --git a/VideoSourcePluginBase/VideoSourceConfigWindowBase.cpp b/VideoSourcePluginBase/VideoSourceConfigWindowBase.cpp new file mode 100644 index 0000000..e35d22f --- /dev/null +++ b/VideoSourcePluginBase/VideoSourceConfigWindowBase.cpp @@ -0,0 +1,33 @@ +#include "stdwx.h" +#include "VideoSourceConfigWindowBase.h" + + +VideoSourceConfigWindowBase::VideoSourceConfigWindowBase() +: m_Plugin(NULL) +{ +} + +VideoSourceConfigWindowBase::VideoSourceConfigWindowBase(VideoSourcePluginBase * plugin, wxWindow * parent) +{ + Create(plugin, parent); +} + +bool VideoSourceConfigWindowBase::Create(VideoSourcePluginBase * plugin, wxWindow * parent) +{ + m_Plugin = plugin; + return wxPanel::Create(parent); +} + +VideoSourceConfigWindowBase::~VideoSourceConfigWindowBase(void) +{ +} + +bool VideoSourceConfigWindowBase::ReadConfig() +{ + return false; +} + +bool VideoSourceConfigWindowBase::SaveConfig() +{ + return false; +} diff --git a/VideoSourcePluginBase/VideoSourceConfigWindowBase.h b/VideoSourcePluginBase/VideoSourceConfigWindowBase.h new file mode 100644 index 0000000..f7f945c --- /dev/null +++ b/VideoSourcePluginBase/VideoSourceConfigWindowBase.h @@ -0,0 +1,28 @@ +#ifndef _VIDEOSOURCECONFIGWINDOWBASE_H +#define _VIDEOSOURCECONFIGWINDOWBASE_H + +#include "VideoSourcePlugin.h" +#include + +class VideoSourcePluginBase; + +class IFLOOR_API VideoSourceConfigWindowBase : public CommonConfigWindowBase +{ +public: + VideoSourceConfigWindowBase(); + VideoSourceConfigWindowBase(VideoSourcePluginBase * plugin, wxWindow * parent); + + bool Create(VideoSourcePluginBase * plugin, wxWindow * parent); + virtual ~VideoSourceConfigWindowBase(void); + + /// Reads config from the video source plugin + virtual bool ReadConfig(); + + /// Saves config to the video source plugin + virtual bool SaveConfig(); + +protected: + VideoSourcePluginBase * m_Plugin; +}; + +#endif // _VIDEOSOURCECONFIGWINDOWBASE_H \ No newline at end of file diff --git a/VideoSourcePluginBase/VideoSourcePlugin.h b/VideoSourcePluginBase/VideoSourcePlugin.h new file mode 100644 index 0000000..1ab6fcb --- /dev/null +++ b/VideoSourcePluginBase/VideoSourcePlugin.h @@ -0,0 +1,14 @@ +#ifndef _VIDEOSOURCEPLUGIN_H +#define _VIDEOSOURCEPLUGIN_H + +#if defined(__WXMSW__) +#ifdef IFLOOR_EXPORTS +#define IFLOOR_API __declspec(dllexport) +#else +#define IFLOOR_API __declspec(dllimport) +#endif +#else +#define IFLOOR_API +#endif + +#endif // _VIDEOSOURCEPLUGIN_H diff --git a/VideoSourcePluginBase/VideoSourcePluginBase.cpp b/VideoSourcePluginBase/VideoSourcePluginBase.cpp new file mode 100644 index 0000000..0e7fc78 --- /dev/null +++ b/VideoSourcePluginBase/VideoSourcePluginBase.cpp @@ -0,0 +1,19 @@ +#include "stdwx.h" +#include "VideoSourcePluginBase.h" +#include "VideoGrabberBase.h" + +IMPLEMENT_ABSTRACT_CLASS(VideoSourcePluginBase, SerializableBase) + +VideoSourcePluginBase::~VideoSourcePluginBase() +{ +} + +void VideoSourcePluginBase::DeleteGrabber(VideoGrabberBase * grabber) +{ + wxDELETE(grabber); +} + +VideoSourceConfigWindowBase* VideoSourcePluginBase::CreateSettingsEditor(wxWindow* parent) +{ + return new VideoSourceConfigWindowBase(this, parent); +}; \ No newline at end of file diff --git a/VideoSourcePluginBase/VideoSourcePluginBase.h b/VideoSourcePluginBase/VideoSourcePluginBase.h new file mode 100644 index 0000000..d6c7fba --- /dev/null +++ b/VideoSourcePluginBase/VideoSourcePluginBase.h @@ -0,0 +1,40 @@ +#ifndef _VIDEOSOURCEPLUGINBASE_H +#define _VIDEOSOURCEPLUGINBASE_H + +#include +#include "VideoSourcePlugin.h" +#include "VideoSourceConfigWindowBase.h" +#if defined(__LINUX__) +#include +#endif + +class VideoGrabberBase; + +/// Base class for all iFloor effect plugins +class IFLOOR_API VideoSourcePluginBase : public SerializableBase//wxObject +{ + DECLARE_ABSTRACT_CLASS(VideoSourcePluginBase) +public: + virtual ~VideoSourcePluginBase(); + /// Returns GUID (unique identifier) of plugin + /// \return string which contains unique identifier of plugin + virtual wxString GetID() const = 0; + /// Returns name of plugin + /// \return string which contains human-readable name of plugin + virtual wxString GetName() const = 0; + + virtual VideoSourceConfigWindowBase* CreateSettingsEditor(wxWindow* parent); + virtual size_t GetDeviceCount() const = 0; + virtual wxString GetName(size_t index) const = 0; + virtual wxString GetName(const wxString & id) const = 0; + virtual wxString GetID(size_t index) const = 0; + virtual VideoGrabberBase * CreateGrabber(size_t index) = 0; + virtual VideoGrabberBase * CreateGrabber(const wxString & id) = 0; + virtual void DeleteGrabber(VideoGrabberBase * grabber); + virtual void RefreshDevices() {}; +}; + +typedef VideoSourcePluginBase * (*CreateVideoSourcePlugin_function)(); +typedef void (*DeleteVideoSourcePlugin_function)(VideoSourcePluginBase * plugin); + +#endif // _VIDEOSOURCEPLUGINBASE_H \ No newline at end of file diff --git a/VideoSourcePluginBase/Win/VideoSourcePluginBase.vcxproj b/VideoSourcePluginBase/Win/VideoSourcePluginBase.vcxproj new file mode 100644 index 0000000..90b1278 --- /dev/null +++ b/VideoSourcePluginBase/Win/VideoSourcePluginBase.vcxproj @@ -0,0 +1,273 @@ + + + + x64 + + + + Debug + x64 + + + Release + x64 + + + + {08E74159-4CA2-36BC-B137-45C65F9868A7} + Win32Proj + 10.0.26100.0 + x64 + VideoSourcePluginBase + NoUpgrade + + + + DynamicLibrary + Unicode + v143 + + + DynamicLibrary + Unicode + v143 + + + + + + + + + + <_ProjectFileVersion>10.0.20506.1 + C:\Users\Admin\IFloor\projects\wxVideoProcessing\bin\ + VideoSourcePluginBase.dir\Debug\ + VideoSourcePluginBase + .dll + true + true + C:\Users\Admin\IFloor\projects\wxVideoProcessing\bin\ + VideoSourcePluginBase.dir\Release\ + VideoSourcePluginBase + .dll + false + true + + + + + C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\mswu;C:\Users\Admin\IFloor\libs\wxWidgets\include;C:\Users\Admin\IFloor\libs\opencv\build\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\Utils;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\MotionDetectorCore;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxJSON\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxxmlserializer\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxxmlserializer;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxXS\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\CommonPluginBase;C:\Users\Admin\IFloor\projects\wxVideoProcessing\ThirdParty\wxXS\build\..\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\ThirdParty\wxJSON\build\..\include;%(AdditionalIncludeDirectories) + $(IntDir) + EnableFastChecks + + + ProgramDatabase + 4996 + Sync + + + Disabled + stdcpp17 + + true + Disabled + Use + C:/Users/Admin/IFloor/projects/wxVideoProcessing/VideoSourcePluginBase/Win/CMakeFiles/VideoSourcePluginBase.dir/Debug/cmake_pch.hxx + C:/Users/Admin/IFloor/projects/wxVideoProcessing/VideoSourcePluginBase/Win/VideoSourcePluginBase.dir/Debug//cmake_pch.pch + + MultiThreadedDebugDLL + true + + + false + %(PreprocessorDefinitions);WIN32;_WINDOWS;__WXDEBUG__=1;WIN32_LEAN_AND_MEAN;WXUSINGDLL;UNICODE;_UNICODE;_CRT_SECURE_NO_DEPRECATE;_USRDLL;IFLOOR_EXPORTS;CMAKE_INTDIR="Debug";VideoSourcePluginBase_EXPORTS + $(IntDir) + false + + + %(PreprocessorDefinitions);WIN32;_DEBUG;_WINDOWS;__WXDEBUG__=1;WIN32_LEAN_AND_MEAN;WXUSINGDLL;UNICODE;_UNICODE;_CRT_SECURE_NO_DEPRECATE;_USRDLL;IFLOOR_EXPORTS;CMAKE_INTDIR=\"Debug\";VideoSourcePluginBase_EXPORTS + C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\mswu;C:\Users\Admin\IFloor\libs\wxWidgets\include;C:\Users\Admin\IFloor\libs\opencv\build\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\Utils;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\MotionDetectorCore;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxJSON\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxxmlserializer\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxxmlserializer;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxXS\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\CommonPluginBase;C:\Users\Admin\IFloor\projects\wxVideoProcessing\ThirdParty\wxXS\build\..\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\ThirdParty\wxJSON\build\..\include;%(AdditionalIncludeDirectories) + + + C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\mswu;C:\Users\Admin\IFloor\libs\wxWidgets\include;C:\Users\Admin\IFloor\libs\opencv\build\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\Utils;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\MotionDetectorCore;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxJSON\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxxmlserializer\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxxmlserializer;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxXS\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\CommonPluginBase;C:\Users\Admin\IFloor\projects\wxVideoProcessing\ThirdParty\wxXS\build\..\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\ThirdParty\wxJSON\build\..\include;%(AdditionalIncludeDirectories) + $(ProjectDir)/$(IntDir) + %(Filename).h + %(Filename).tlb + %(Filename)_i.c + %(Filename)_p.c + + + C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxbase33ud.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33ud_core.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33ud_adv.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33ud_aui.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxbase33ud_net.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33ud_gl.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxbase33ud_xml.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33ud_propgrid.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33ud_html.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxpngd.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxtiffd.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxjpegd.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxzlibd.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxregexud.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxexpatd.lib;opengl32.lib;glu32.lib;winmm.lib;comctl32.lib;uuid.lib;oleacc.lib;uxtheme.lib;rpcrt4.lib;shlwapi.lib;version.lib;wsock32.lib;C:\Users\Admin\IFloor\projects\wxVideoProcessing\bin\lib\Debug\CommonPluginBase.lib;C:\Users\Admin\IFloor\projects\wxVideoProcessing\bin\lib\Debug\Utils.lib;C:\Users\Admin\IFloor\projects\wxVideoProcessing\bin\lib\Debug\wxXS.lib;C:\Users\Admin\IFloor\projects\wxVideoProcessing\bin\lib\Debug\wxJSON.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxbase33ud.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33ud_core.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33ud_adv.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33ud_aui.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxbase33ud_net.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33ud_gl.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxbase33ud_xml.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33ud_propgrid.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33ud_html.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxpngd.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxtiffd.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxjpegd.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxzlibd.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxregexud.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxexpatd.lib;opengl32.lib;glu32.lib;winmm.lib;comctl32.lib;uuid.lib;oleacc.lib;uxtheme.lib;rpcrt4.lib;shlwapi.lib;version.lib;wsock32.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;comdlg32.lib;advapi32.lib + C:/Users/Admin/IFloor/projects/wxVideoProcessing/build/Win/lib;C:/Users/Admin/IFloor/projects/wxVideoProcessing/build/Win/lib/$(Configuration);%(AdditionalLibraryDirectories) + %(AdditionalOptions) /machine:x64 + + true + %(IgnoreSpecificDefaultLibraries) + + C:/Users/Admin/IFloor/projects/wxVideoProcessing/bin/lib/Debug/VideoSourcePluginBase.lib + + C:/Users/Admin/IFloor/projects/wxVideoProcessing/bin/VideoSourcePluginBase.pdb + + Console + + + false + + + + + C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\mswu;C:\Users\Admin\IFloor\libs\wxWidgets\include;C:\Users\Admin\IFloor\libs\opencv\build\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\Utils;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\MotionDetectorCore;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxJSON\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxxmlserializer\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxxmlserializer;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxXS\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\CommonPluginBase;C:\Users\Admin\IFloor\projects\wxVideoProcessing\ThirdParty\wxXS\build\..\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\ThirdParty\wxJSON\build\..\include;%(AdditionalIncludeDirectories) + $(IntDir) + Default + + + 4996 + Sync + + + AnySuitable + stdcpp17 + + true + MaxSpeed + Use + C:/Users/Admin/IFloor/projects/wxVideoProcessing/VideoSourcePluginBase/Win/CMakeFiles/VideoSourcePluginBase.dir/Release/cmake_pch.hxx + C:/Users/Admin/IFloor/projects/wxVideoProcessing/VideoSourcePluginBase/Win/VideoSourcePluginBase.dir/Release//cmake_pch.pch + + MultiThreadedDLL + true + + + false + %(PreprocessorDefinitions);WIN32;_WINDOWS;NDEBUG;WIN32_LEAN_AND_MEAN;WXUSINGDLL;UNICODE;_UNICODE;_CRT_SECURE_NO_DEPRECATE;_USRDLL;IFLOOR_EXPORTS;CMAKE_INTDIR="Release";VideoSourcePluginBase_EXPORTS + $(IntDir) + + + false + + + %(PreprocessorDefinitions);WIN32;_WINDOWS;NDEBUG;WIN32_LEAN_AND_MEAN;WXUSINGDLL;UNICODE;_UNICODE;_CRT_SECURE_NO_DEPRECATE;_USRDLL;IFLOOR_EXPORTS;CMAKE_INTDIR=\"Release\";VideoSourcePluginBase_EXPORTS + C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\mswu;C:\Users\Admin\IFloor\libs\wxWidgets\include;C:\Users\Admin\IFloor\libs\opencv\build\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\Utils;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\MotionDetectorCore;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxJSON\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxxmlserializer\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxxmlserializer;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxXS\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\CommonPluginBase;C:\Users\Admin\IFloor\projects\wxVideoProcessing\ThirdParty\wxXS\build\..\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\ThirdParty\wxJSON\build\..\include;%(AdditionalIncludeDirectories) + + + C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\mswu;C:\Users\Admin\IFloor\libs\wxWidgets\include;C:\Users\Admin\IFloor\libs\opencv\build\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\Utils;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\MotionDetectorCore;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxJSON\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxxmlserializer\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxxmlserializer;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxXS\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\CommonPluginBase;C:\Users\Admin\IFloor\projects\wxVideoProcessing\ThirdParty\wxXS\build\..\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\ThirdParty\wxJSON\build\..\include;%(AdditionalIncludeDirectories) + $(ProjectDir)/$(IntDir) + %(Filename).h + %(Filename).tlb + %(Filename)_i.c + %(Filename)_p.c + + + C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxbase33u.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33u_core.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33u_adv.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33u_aui.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxbase33u_net.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33u_gl.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxbase33u_xml.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33u_propgrid.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33u_html.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxpng.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxtiff.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxjpeg.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxzlib.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxregexu.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxexpat.lib;opengl32.lib;glu32.lib;winmm.lib;comctl32.lib;uuid.lib;oleacc.lib;uxtheme.lib;rpcrt4.lib;shlwapi.lib;version.lib;wsock32.lib;C:\Users\Admin\IFloor\projects\wxVideoProcessing\bin\lib\Release\CommonPluginBase.lib;C:\Users\Admin\IFloor\projects\wxVideoProcessing\bin\lib\Release\Utils.lib;C:\Users\Admin\IFloor\projects\wxVideoProcessing\bin\lib\Release\wxXS.lib;C:\Users\Admin\IFloor\projects\wxVideoProcessing\bin\lib\Release\wxJSON.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxbase33u.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33u_core.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33u_adv.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33u_aui.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxbase33u_net.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33u_gl.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxbase33u_xml.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33u_propgrid.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33u_html.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxpng.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxtiff.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxjpeg.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxzlib.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxregexu.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxexpat.lib;opengl32.lib;glu32.lib;winmm.lib;comctl32.lib;uuid.lib;oleacc.lib;uxtheme.lib;rpcrt4.lib;shlwapi.lib;version.lib;wsock32.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;comdlg32.lib;advapi32.lib + C:/Users/Admin/IFloor/projects/wxVideoProcessing/build/Win/lib;C:/Users/Admin/IFloor/projects/wxVideoProcessing/build/Win/lib/$(Configuration);%(AdditionalLibraryDirectories) + %(AdditionalOptions) /machine:x64 + + false + %(IgnoreSpecificDefaultLibraries) + + C:/Users/Admin/IFloor/projects/wxVideoProcessing/bin/lib/Release/VideoSourcePluginBase.lib + + C:/Users/Admin/IFloor/projects/wxVideoProcessing/bin/VideoSourcePluginBase.pdb + + Console + + + false + + + + + Always + Building Custom Rule C:/Users/Admin/IFloor/projects/wxVideoProcessing/VideoSourcePluginBase/CMakeLists.txt + setlocal +"C:\Program Files\CMake\bin\cmake.exe" -SC:/Users/Admin/IFloor/projects/wxVideoProcessing/build -BC:/Users/Admin/IFloor/projects/wxVideoProcessing/build/Win --check-stamp-file C:/Users/Admin/IFloor/projects/wxVideoProcessing/VideoSourcePluginBase/Win/CMakeFiles/generate.stamp +if %errorlevel% neq 0 goto :cmEnd +:cmEnd +endlocal & call :cmErrorLevel %errorlevel% & goto :cmDone +:cmErrorLevel +exit /b %1 +:cmDone +if %errorlevel% neq 0 goto :VCEnd + %(AdditionalInputs) + C:\Users\Admin\IFloor\projects\wxVideoProcessing\VideoSourcePluginBase\Win\CMakeFiles\generate.stamp + false + Building Custom Rule C:/Users/Admin/IFloor/projects/wxVideoProcessing/VideoSourcePluginBase/CMakeLists.txt + setlocal +"C:\Program Files\CMake\bin\cmake.exe" -SC:/Users/Admin/IFloor/projects/wxVideoProcessing/build -BC:/Users/Admin/IFloor/projects/wxVideoProcessing/build/Win --check-stamp-file C:/Users/Admin/IFloor/projects/wxVideoProcessing/VideoSourcePluginBase/Win/CMakeFiles/generate.stamp +if %errorlevel% neq 0 goto :cmEnd +:cmEnd +endlocal & call :cmErrorLevel %errorlevel% & goto :cmDone +:cmErrorLevel +exit /b %1 +:cmDone +if %errorlevel% neq 0 goto :VCEnd + %(AdditionalInputs) + C:\Users\Admin\IFloor\projects\wxVideoProcessing\VideoSourcePluginBase\Win\CMakeFiles\generate.stamp + false + + + + + C:/Users/Admin/IFloor/projects/wxVideoProcessing/VideoSourcePluginBase/Win/CMakeFiles/VideoSourcePluginBase.dir/Debug/cmake_pch.hxx;%(ForcedIncludeFiles) + Create + C:/Users/Admin/IFloor/projects/wxVideoProcessing/VideoSourcePluginBase/Win/CMakeFiles/VideoSourcePluginBase.dir/Debug/cmake_pch.hxx + C:/Users/Admin/IFloor/projects/wxVideoProcessing/VideoSourcePluginBase/Win/VideoSourcePluginBase.dir/Debug//cmake_pch.pch + C:/Users/Admin/IFloor/projects/wxVideoProcessing/VideoSourcePluginBase/Win/CMakeFiles/VideoSourcePluginBase.dir/Release/cmake_pch.hxx;%(ForcedIncludeFiles) + Create + C:/Users/Admin/IFloor/projects/wxVideoProcessing/VideoSourcePluginBase/Win/CMakeFiles/VideoSourcePluginBase.dir/Release/cmake_pch.hxx + C:/Users/Admin/IFloor/projects/wxVideoProcessing/VideoSourcePluginBase/Win/VideoSourcePluginBase.dir/Release//cmake_pch.pch + + + C:/Users/Admin/IFloor/projects/wxVideoProcessing/VideoSourcePluginBase/Win/CMakeFiles/VideoSourcePluginBase.dir/Debug/cmake_pch.hxx;%(ForcedIncludeFiles) + C:/Users/Admin/IFloor/projects/wxVideoProcessing/VideoSourcePluginBase/Win/CMakeFiles/VideoSourcePluginBase.dir/Release/cmake_pch.hxx;%(ForcedIncludeFiles) + + + C:/Users/Admin/IFloor/projects/wxVideoProcessing/VideoSourcePluginBase/Win/CMakeFiles/VideoSourcePluginBase.dir/Debug/cmake_pch.hxx;%(ForcedIncludeFiles) + C:/Users/Admin/IFloor/projects/wxVideoProcessing/VideoSourcePluginBase/Win/CMakeFiles/VideoSourcePluginBase.dir/Release/cmake_pch.hxx;%(ForcedIncludeFiles) + + + C:/Users/Admin/IFloor/projects/wxVideoProcessing/VideoSourcePluginBase/Win/CMakeFiles/VideoSourcePluginBase.dir/Debug/cmake_pch.hxx;%(ForcedIncludeFiles) + C:/Users/Admin/IFloor/projects/wxVideoProcessing/VideoSourcePluginBase/Win/CMakeFiles/VideoSourcePluginBase.dir/Release/cmake_pch.hxx;%(ForcedIncludeFiles) + + + + + + + + C:/Users/Admin/IFloor/projects/wxVideoProcessing/VideoSourcePluginBase/Win/CMakeFiles/VideoSourcePluginBase.dir/Debug/cmake_pch.hxx;%(ForcedIncludeFiles) + C:/Users/Admin/IFloor/projects/wxVideoProcessing/VideoSourcePluginBase/Win/CMakeFiles/VideoSourcePluginBase.dir/Release/cmake_pch.hxx;%(ForcedIncludeFiles) + + + + + + + + {6F4BDD20-5B99-3029-BDF0-7D719B283AF5} + ZERO_CHECK + false + Never + + + {751A15C0-76C2-3506-AB9D-0CD3545003FA} + CommonPluginBase + + + {14D82890-BB7F-3748-8713-43304E91CFA1} + Utils + + + {7887B55C-77B9-3262-AFAD-ABA738C61029} + wxJSON + + + {78CFCEBE-5466-3128-9717-547416DBFFE5} + wxXS + + + + + + \ No newline at end of file diff --git a/VideoSourcePluginBase/Win/VideoSourcePluginBase.vcxproj.filters b/VideoSourcePluginBase/Win/VideoSourcePluginBase.vcxproj.filters new file mode 100644 index 0000000..89d5bd5 --- /dev/null +++ b/VideoSourcePluginBase/Win/VideoSourcePluginBase.vcxproj.filters @@ -0,0 +1,57 @@ + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Precompile Header File + + + Precompile Header File + + + + + + + + {4951AEE1-75F2-3C38-B593-C6BC8C809FB0} + + + {112D4227-BB51-3360-BB5B-5EA5DBA5A65F} + + + {05450AB2-CB7B-34EA-83DF-1EE86424EDF0} + + + diff --git a/bin/CommonPluginBase.dll b/bin/CommonPluginBase.dll new file mode 100644 index 0000000000000000000000000000000000000000..ea755ec31e30fbdda2f8429fe4a175397d93c833 GIT binary patch literal 778240 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 literal 0 HcmV?d00001 diff --git a/bin/MotionDetectorPluginBase.dll b/bin/MotionDetectorPluginBase.dll new file mode 100644 index 0000000000000000000000000000000000000000..7d7a1f117920399bfc3d0c8cc6d93c24ae718328 GIT binary patch literal 145408 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 literal 0 HcmV?d00001 diff --git a/bin/VideoSourceDirectShow.dll b/bin/VideoSourceDirectShow.dll new file mode 100644 index 0000000000000000000000000000000000000000..e4561e9dc72fe3fd28b47d95f7426d979508e3db GIT binary patch literal 392192 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

=9$20jb775*;Z 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 literal 0 HcmV?d00001 diff --git a/bin/plugins/motion_detector/MotionDetectorCore.dll b/bin/plugins/motion_detector/MotionDetectorCore.dll new file mode 100644 index 0000000000000000000000000000000000000000..d92f22b1623223b6e85ab045c6a76bf20b9c3aa0 GIT binary patch literal 976384 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

nPE(7(m^HlF_Ic@{Z|av^%D z=zT}d?Q0rgJ0B;9_X67B@cYp)~SVjgXO8=R%sZ1kXK>xi_`az&yOxVat!pNVT699{zLTRdf57* z17PIA6M5g~CV7u+`Ul@H^#mxxh)_KqM~3&`SoTwc>j8+61&Fi8m;{%_@2^Gh)&=l7 zHwfNgMeqg#cugAwZ}%d2UBP&)yMIII{VJ3t%D<5SzwT=AO5-Q|{wQ4jYx#bCK4;=- ze@f<4{Hm$J|B^exS~Z{=Q|eUwupq)AnVuI`)+6J^8);stFAXQ3^y3nX6vk&Z6+HXi zDyiM__d)*%-?Ngw+WZiF8pZmn=!sR(?;?EVpep>cY(0aqNp~Hag8XbD`B{K#E=PVO zJ|*-Q!|Rk-+XUdHj|uPAC9{juA^!GJ8Yj8!f6DV<;*K+(7q=wF(0*~U$+Z9x^)aUI z+c$g)ncE^p-zHBG?|*r;Lr&zv7wxQ%&Bp!PGP!Cweq%dR6PDM+X1@i3(~OBSHeACu zicsC%cmcyUC}>rdzWpC6pl>%zM(OK>&3|LiC-R+-ACK(=uf!3pvfpcBxZ7=ct@;Q88-*1%^?1;12+q6fQHbA+{8kzPgNDU>@s z6oNna8?>L1HdcTBdIqE6Q0(=qU>Ak4WFM6GcQ}0JlIS%MM@DM|r&mAX>(ni{RnT0Z zLH3-U=N`C2u7U6oc82^jaRycy?3Va?9+&*n*Fy}SY4^-h@>7E}!ylT#mxf9TvN!?= zB-ck846TV!8?>5T+N#610*u(u7t6KkNS5@~QEPp1yKVS*UoIm9I%~>|#Xu&HeIE4U zUbsQ%w2LdiSXOsGn9D<^zRo>xO>z0aaAyEIdfmYag87bs@!XUFl|BYocrk2sF9(M^ zJ{b%mn-IE`ZjbSiz9jW)93haDfc>EM%mEjn!+yp)zk@NKzlKs-pp}L8h^Nhx;2*k4 zoO&3v1qzvQd#k^h>XRI zo!W?!*zo3fVgGe^W17ybDf!$!G9Te*aIuIYh7EOhFT4(oy1NOkAp7f*&0Y(r4h@px z8y3EK|6RzN4S6f{k1;}#-_Q6gPf{SO`1|AAuZylfs(gQZG%EKBl<4=z+XA$9#`CK6 zt2n<`FSc=fTh*X z$B!HyYd+;Jth@KifFW)Y*23ZMAAW3!W^Yhj&ibFfJ}dSxe!}Cye3nKIOeiM={?XdB z5{+I=>)^PaZN#)Ybb(E~mJ_GN%HT_+d7ra0`D~Vn%glxBHfGzI8QSIyOkOXVxM%4v z=)AQI>FFzQj3u^;|FFsuR(TlGVO40Sc}|v9`OyH=tB9*kPfw#4&XC^D%M)oam>zjU zb+P64yBUIVUR-J1XNG^aB$f5m|@odOiMUc_a_W4I*PSar<`=V*_O|G}WUZ zeDfePae|zg$7v_jmTh%^7~zlKVARGsz&Xw#MtTs2wH$Qagr<7hf^Ym|Dc^~hN}@j) zq4GPLXb02)9XUzH14cwi4!{or2+nWaf=Wu}hkKPw2l7ZJpGxMpc^(R6HXvs-(Z4JL zS5Ai&lF6UgBblsiWD?Y9;;+{$nbJYNc8LqflpGL3-BB|6#y^tDcak|M0CAJBJUy8U z0W~CZvYqoMN^*`$ao&qVqhy}&B_(r&P(UW1O6G6*K?2CkA_wQGZjQi}Bk+Y}@(1%s zCTkm+1T~tdF*2oteC-{73dock5Yilw$v6IyOum!M7X%QwL`lw+ zDb9y-Xb7v7u%HcTKH)e02@XE|rYw@7lMtCPLVK=rQoi<;&ziZzL>C&h^Dcn7!K8t# z0YR_dogi79qeiZx#Vj=Z)<unu7^Tv(A; z^!5VMZq{P(R_!^5Ox7XN+J-QXZ5MY4dgb7b7+iTG%`=Pv{^nR?9U{5?ZkC*x5dimz z!vN`Aiay-j5lkp(&P&aT<`@c@yZGt=r;PO|Pc)$nut#|!-C}8ZBCVohPo&$dTse`F zlz8`)J@*YiOGcS2#SJX}(pPK}mEv$^Krrq!-ntr#qz5gn3?iPi7C@^LukPe)q6~_zta@4eF^r=qw6-1)Qz5OFv3M8X znsij;ATb~Z*&#Vd$V5Ou+;pCCP*TTz*LWtq--l?Fa8OV^m}PnsyXY*;3VkFl$IyGB zS*g4pyM{Y%JzS6d>Z{^^PGCJ2_VMQWCSttbeEna~fx`6{CeX-w@{HSQt6}}*KC2~E zPUuIzcq_=reC=P@a0M-<)O162|nqKc8hQ1e({cht zb$KGa!qS6$%Bc8n1N+^YHkowj7`;PYjr-(SAM0ao*`F>;1Mky|}N! zMvK@}B$IAehRu=00VpQFZjptQUTukhz1~KKV(=|94Hfm4C}hZVD*HsugplUOd^)E0 zMWtD+H8R0{QE40;z=0WT!|1`CEa-?0f0dv;Vi2R6%T4bhc@N~&0JH-ik<%^CR4zPbhZNM%n!1{8=#Kl=jT!WTXD zwO2=H^z_qjCIa|QMV1y2uB1B(%_v;G){{JuUJ~h9xn@CsFzcP_!?5`kT#*d#QrIIz zjIDF4!RrC{txt=gP`{dTcOTk7?#NHi?ZWfzxNnr+mfVYWA4rW{En=&i*B%H4fu1~g zxB^z4e-v<5av$=Zn>4X;-xav%vS&|LY+}V-kO$5XfW{Q~MuJ#6^eIaa3icM(p$p6v zvsoSh2D`iuD|uq~_$4J=!KH>1E4Z610MKth<%!kn{(61s)865FFDPJ|PbBggB(!R1TJhA&cO*BBVh$vIv&$9yu*q zhw%VkN8$mb*ezq7OrG1WdHqE|_a(^!Y~|6QFs9!t94|GDx91@I@@yTHrWJ^>c1egCJjTYhnEJP)FB0VDM#rpJUU!;`S zY4TmAFy2G>y0#&Euf!{RWUJs>kdI%8YeDYE>}S`4#8?PD zSqzT@dO}?Y^~8G*WzIikE8jfY=6(qeK@GLUeCrlmD$KScs!eW&u7hUaq-NX?&7Lr& zYEs@$BO56Z)FKufmxsuSM!Ca**9~5DiCJOa2;~_L6%0R@CUyB&N3rEKdEdT(4Ywft z>+Y7AXqoE>Z9W1T^$>A7v|DZfS_6*&09pxPf7jsw z$b@bHAfxQLC*85+y+)ENb61Bxxwz#J+ z@~2BK}-f8KGnP;O9LA;a?~1aF-xKaQn_B zmVn&kcE>9TW8v^ShR%@<9@g19tTTnLysKqIukGLbJeZIR5%r)Y#kB%x>McbXdzlJ` zHSLari$o%l%*(2v?8DhQSAP4tV1Jp1uDAq zwCR{>QS_^fNjHl(~ZyX$ZOg+D+?nv3}1=fO9hZ*t&G4*c)uK)K)7 z_@{UV3=F_nU8Y8asZ#4$xITpKC^_&W_Pw-|wEM2iBi2cB>`o$VjLy$?sJ7W|I8V7o z%#lS{mdq~*7>La^SL=54Y!i(7O-wY=um(XAut8JxEJJUaYG5k;ARHF?X@e&BE7>{` ze;aFx@J9C4BU4y4%ZAwu;}Ae#&Y7Hj`NkQ(4^Q_r+V`>&+S<&W$GHL)UDjddU+0FI zn9Y)Esv|(USTijXKdgUd;iREEVBQH3IG4kyg=H-sqq@sEXMvtpX@j6Fp7L>+A@NB8 zjaUWxAwc-sQ~T*BFoW$T@nmf)j)xr`rJ`;4T}-w~)B}9L{A4gVBUy99GR9C;TSr10 zfHB$^?ntZ|UKP|qn{MLZxpU@)0e3hfu^vdnIXfoy?9cS9HilU{e%Nnb)iw*+)Yx^Y zH5UH+_Cll0wK^~e!bE!?x*+I%De}3xmGa2QFyD1x3^`^=qobbvw!0?N0~sSs<~5jC zm!tk0bY3mgPcV$3&OX_c9avdIKD6i$2f;)c>>MS7M)Y z>$7BbtGhiID=7w=-s6A!{ta=t7lfqVbkCUE@Mf3;{C{@1WxCa~|~( zcF{&z^s(7J#pq6Mg1@>DPEcj&NwdGK`z=;_CVPgbkkXP_Jt*@-O3A+FTt%yeXn`g{ ztI2&x(Mne{3+43#zkEjM8FySM66_~SAqTU#z`Jq@U4G3(Iy0po2DF-51`H@ezsyts z&VUK|?cM^FA3kDO4i%^Uv>-zTA44}LKsd*7v|q;)t2o>~f>m5}cEy}KH$1n^)+`xAWTnM95Lrs9#Sl{Wu|NZB)Ch6N&O%L* z7}p0VW%2n+DLy-o51+ftwcAAa01V@gYEdcGIsEbyQs4@8cADv|DWrNep(Lc;^Ihih zG>Nw%4eQyq)ji5!FrWhztfQ!cB`6}O0tJWT#e#`wYY9Io{N?aBA~2YTH)N*ObnL)B zTih8`AUY6KX-7d$X~9vZSxAORH=7ch=x5!>=&Fow(M>+ITcV4fFndK(QzSiQ6*za#!LnY*b9gnHc*Rc<~2>aRDnxe+f2~wDu&_U z!)0b~<}NwI1~-!#?PKp*DhB>B4F7wMVu(1YUEG&+yV03K)lT+?e`vV#Gx}>dZ+4Mx zWGBN(lyy+D)Jocz;EYI`yZA&}JcCGj1@rUac5Oe?z^NW2yDR>i{bMIaGD8&L=wdCj z@_a2FL@E6|t%Y3-sm&nfZLr=^)Y;CwSd~_A5Kb=6uc5cu?}|F|!rgi{#vs(V6heBv z!zV;*EY_c~^EI|c4HBvZ%p@zI7$MGJo3-v^HhKnheCa|uA4j+k*Xt?kfW0^vZNXFk z&f6dzce)Lt9}IQ*2S{;VucdbbG)F*0GJVYN$G8aE&C9WbF+U1zv8^q(&2NNsG#*%P zzoUA&|Grf}nyp@6zQ@D(Rl0A0OF?K1mZJe=7zvSjiCmZShcR+}7!dbfs3-;dV-#Dl z4=ABLx$Ugv{Z^8}0&f=Vt+}VSOSX?w)3b=l1mxF7bR_=5MXPBiwWSwu=`E`Zs~ZpQ zM4Bm3*F;^6`GkifThDH2OQu&m?z)n zwiNApG9&GNV`|+^1SRL&@C_-^lH(}CmSF$jX54)3_7m5ITac}rknJ19-$kD_z7pY1cd;cQY!o zpOC+6aXY$uLOh#I&EJT9lToXdE-__XxF z`OY`!`BdD*fd6u3KJyHw^60E&@Kqx=8T@MVW6{(I)IlXaIyG;}C-n9wNx{M7)#852 z4QSy@8Tk=@rQ9tR$V(A=vx2Zib;{M9Wo}gYD0Zi^@}$&|4`#f_j_ll!f>A#hWaG;d1REq0M^D;nJxf8Tx1$Ruahh75x;B{2hEmZe3|56>a!>n+u`tZe0#>z~h6gk6{M~ICz>?@G19k z@u67Hnsa&T4^{f?OwU4Dg!+@kk$kSyG7G!q0q`8lOyMb%$bfC+YgB(E)_^#axR{`< z#@&HAvlrbbD|#eF?Qzn*o-9FY9>kEB8rLX`tg>LD?I|(Y6UXkcxc-yyZO6hesGqHT zO0CsBB({wjH1AB>50T~l-NVezSNVPHMdHp4aIBN=X}B`I8gah!<8V4o;;La{@f)%C z;uX*;<~nO)9fy;XX$v7iF|*J{F`1bL*MF)SJYn9GwLZF}z$I02EGbH% z{zdM$Nu7*7;EQEvZ_bXh!)Fs@)_sDDC};utWbEj_Eo=vk<3dDw{Uv=zv}p;n1)_n0 zE;4Z8k2x#4S-1FDZI}hh=`m3*(0~wsu^+%f7{#?$U_UZyG5&w{z64ILqFR5l3`~GH z!xD(B4Gh>2mWF^dvNaIkf&rR+*c$;E_5qSGvPj|#7?=zWzNlTSAcYI;K$3~7ilkY# zCG=-I7@Y9bsVicO(J(tmfCtWl{N`i`l-*Uj0(J(C6lHHu1k1cx`ADk*pcJP|IkV{y z99({ZoIlC9zq|g1oArP{eBNQ#1>1r`IKHH#Z>;+Oc*7}GJ^(&=GGBy{tzKAr?yf&J z0(`F+@XuS|8;8Jug0>w)eO>$q0vy7Bl6NNW57|wnypoi;TZB-8Cp`$^H2HP&k0zBJHq5h zo4CEuM$aOe;e3~!v7EVbKbg^eaPl)GbHJ-gE>RpFq?-Plog!g$geW34xX+TTOk`M# zCB$qRNfgXK89LjAgFf?Dl|l$5MUM7NePOf>3nH^vZeUhkVl-|y$k#r3f-Z6V1z_I% zmT3Yz2u}OAGsfa5_@){#ziTvw<=wZ~sZ0GZb2CG{g>-SdgnB7mcLf+3KDO!L>Ktj& zQmiN3%t*{ZrF<7SLHBMR+)K|6VGO8Al4?XDxUdv18L=dc5n`mx(=KKMesGreVoaAJ z5QKnD7H@dYVmEM88UgA!x@R_+3w<3_r)Rct2oYrjEe!<#jQ}OJ-()tPPmzYa`NM&ho{$Ur&A(w0SD9dk zzG<9dulr(XmQVVA)GR;BfpkyUv)7yD$BwIJ|51U1s?CX3`w3blug65~tEg_Dd4|@q zqk=(9sDD|>lpCQ|a{j3_KlCizXO1W-Dow~QJ(*_i&B?jo3|r z4lfeNG;{Br$Zn9yL4}Q2!Nzq*OhH_C3B$x%Xqtde%8EJcffyZ^WrWlp7j&@%QN{cT z%LqGQ%eaQEp!`CG8qSuqCC&ncc{vAPKjcFNRb1N^oyfEKRJKJr3HwRDRe@?T7Q_k> zd5A)?yEz=1ooDre*|Noox(s?`2KP8&H%6HegImEg^d3Yds?FfuPKgzOj!qR! z!_u1qPY=g+?5(+sNy23D4fAilev>(3i28C~X$q&9J#)|4I& zskKp|X90CIIJH-;;TSlMwdhz>VB_Nr2O%R*nN>Jq!5O2&y&Tj6gQM!FsrnfbsKs2N z0eVZ-{yl$1KU2Y_s?J?2LGvVsJJIEEJCfEBe0%6it*#<46<&Zt7emT#eUPnd?A;#Uq%7=Y~^GJ;)XjWflSdkt+GHZX@EGe%_? zI^h)+9$b5VB=VC{aD!wdn7g$p56Y~hJQ~oKois~eMvGPC^K4}uSYYMT1_-6{`IhjS zS+#2V;l~rI0%SZ&d!Z7zUJnlI)Y zC7^iWd7J;_>_@9t9LKLJW+=awQ@Du{^G`*7mB zwE6Lrs=XG+7DwZ)%2?mNKOhF9H#p%J`+33Y9)AYUC$-@ctc*Qls){e+pjX zFQvhgQ;CxdWrux9uS&x&U+yd4m&HJ5)aPP~Z_7oIeG|S0q(!_i7b)wD*o-KBc1Y;R?gh5bb2uDB z8^R|k#Q%mh#%5?ep(rKcp*V4Ae?n0TClsa2OnS_YhBL_jzxpI8Q2wfD#%#f)B)FZS zBXi&$L(U%w_5dQmpzca0=|qa@<6}1@O+rWPCf>@BZYs_O$@v!xzh&11_$LH<6Cb6T zT?6>?`3B$5vyu*cu}BOI8N%GRC*WFZ$yCg{k|FCet7tYMtxZG^%3ex2b9*IWKJH7@ z!uLw~W0)e{P1St*rx&4NqijdCM?Tf_vTNeG4>IlaCE{Ffl#0LC1*pjfy;#3uVrnzCRFkLV53Q zLkeYtuodC0!94^vG{Xsq>hESa@Lds;%NSsPt9ZXEpGo-MDO(Nu$(D?{pX((K`Pa7@ z2K@N^prQ%#L*keiIDoyNn2|MZ_$lji5=QCZ=Ji+8gswjWw zONIk5-fnEFd%JOE7)Vm6n#5JMw|7CTEK(7Bd$2GTy)0vU_$T$mgdlnI8tn>HXEc+Z zRysw@lI`K9u-qz=!xpe8KR~b{b zrAV8zuoxk@N-VyS*nzO?M*e=$CE>C+rcdSZ8ZWXi9UPwdPuy%T0ua8>5<_ zSEH1MLL28AaFM((O=(yE;Di}_+U^+w2_+y83vq!*N9v<05)y$we^f$59C>c{lhG^v zojO5En#Z&V_W6B-C)q{9SnL{(D!fX7ADHu^@N?$W1o#Kj2qaHIA#hxk!6#S2#&_Wn zDR*5sKR98_J&=s(9TIG1Q324G8Mjho;@X1{_m5NbU@u8Nr+xA{4PlYwGvd$0`7}0| zisRAkk=r-S;PP|-Q3&zHUUt`;uSFBVQ|l36cTYHMe7#G~Pu;yrB6xRH=&2F88u80S z@C%agyK^Ov-z~dU!S8xh7%_ejV4oizBT+%AKM|LoX#Hm-(5qTs^tFlRB4gI$bRTa~ zCQq8#k^J4-5RORxZXSw3_gwqNaJN11(d@USI~cEi;6uojPvRiU246L8uCdXSOYLAk za^uCn7D#;h7)B8|SWhw>WLJ%DaRdBpT=og+OOEfSWbM2l1$a)Ux*2 zXI7wHabpky(pKv><25#qpW{X>Lb^89Wvyr(#gQYVce zQz8gYPIBtECM5;P$P}o(53s}H&Xho@eTD>);1(nTOzt=9i#H+RsWaJH^;cV<=|Gyt zS9X!7ld;v)JV|lxc%Us|qFzQkEaOMXd1RdkcBw-cXm}72CVmc~mOXfQ)7PCxwzrU6 z=40_^B3ta(L!LnM?)0x2Cs7Cc!BVoem#lpiv09H+Wh((#ftX|84!=lZJJI2ps2V?{ zzyaYCb205-7lZ?$=`uc#Y-6<%edvc>K-cX{%e?VPo=0|^gc3p>s|Z|(233VHL!U=> zCuWMFM&Y2pYcFGz1eiX69leqj9dBNi7(jZThx=4Vc^+Xe@0-tW%y*PU(!x2k z&5ZL)tw5uV3CEB3K&WM;XAVp?eOrxIeA(#Ri^98+>)UkLstWoxUS_>o^ldkoH7`KR z_WZxmqRO5Aypd0hzWq&%{D|~zRdEc|JdI4>&I)@UQr}uJlO^ceReO5r>sy&W zkCEYJy9UnNSqf&xyu2ahydyc4U8b)ciwo9YX@b%6{t8VKh=xDTQ;{{m>EoRXGjJWM zyDG9hT_#aBXrjJnTtx=~zO$QS;cKFVPw7ZjMBHk+6mA~{T$GstywCJW4d>Nxmc7)z zM-Gn`{frUoDkt;UcRbpnZ(ReSCD!-YZa?@Q2Qh`&wd~Jdh`x`FQ8T|lO9Su79;t9V z$6zfWcNAQKX@Rv;;`UWNk1@_~g+Cz*e@qDW&N#S31x19VQoiFk-1q zTM3rY_B!uU&olk{Caf)U@jP~`ZmT&nynhq!YsBChhu`DjLo*!`Alh8GWsy z(6)P`@3cWF(R(S#N&3@_xlD*Yl>Xcf8r7~p|1C5x*Pm;{`_=X5OmRzwvU&$2ZKF!_ zOIT*ToXjMQwR-=(6&TmcK=u>XAMyXVm1M>BfQ-zO5VCgj0q1!XF==Qr=ykmVCxm_% zp26W{1zhEL1@BBmTb=F5k-&LA#vhVQ19T~i&)R3cx+}k4B~d*8jBF=~jY!MvgFo+h zlA@pDWYlWb_T!V$05$jO=m^g{ehhL=vupPp=)uBS^DWw#juF4oS9v`DprZ$(>3Ck% zBD`MpikEUc@2{UYo(HME%XB zGucqg3WuVC{Y(;xet$Gfk4Pngpxyk;X>ZvK2&@-<9zHUNiTMW-{|(ku)`6FBI$hK!l*H?T0cP(<>INLAwn5=7_))x@+< zeC9^2mc?g_+VGk38H@xt660lnPloserUgrjo<6`4phf;dh7?z_{o04aU`XQl7fy)u zr{iD8-jX?tqqAADQhqe9Y4KP_;|Ca;A}1Ota9H{U7W8=*f7T+Uew$r&0twWz=FEy9 zOAldSgHoOR>I7RbIi(mti8g2Sah;PQ_;W4=^vLIYEB6{lwv{10EP3jW;??+WL-8l=D0O5v4ODEsT>~w5Q@@Nh>ldR+);+* z%^l2^)!=q`s>kU)e0-l@C4%obxJ(Y;d-$IUrw@EoqoNn zT~`nyWYaWQFHu6zO+-e{=YLc(b2XgLKLHa|PsxZtfqss-ThHfTNlc?VLw$)2@SoWP z0sfyDQ((>EKj(b@5$ofM*XEpOAv_7-u7WSBy zgvg^zzv1N5iWca3U`K*R5_xlkm^SNK!_SmAMCJ6f6kexA-Msj`Rj2yo{4$evfKdR9 zoE4=FF_GXy6%x(x&r^$d9~Jh7LpKGdsNxZ?q>c5pS(10#)6;Plio9JUE65GD@NljH zf?6kqfnVrV31EI3L(`n%RRbAzqZ>;vMHsK&d1xaxIrGA34Sc6>9h@TZ$qoB%oMa)%NXznSqnIwXS2tiAC=b4tKcXXhe{* zN7w^>GRR8AdPF*<6+j7%h;!!IF*GMnVgLz37(prCKOhM^`pnf_g_D8=Z++%c+}L#i zH>Ue|CS!INv(k~g6+(JRgs=Qf$(levnU*ojk79>D2el<){)(F{V)(oBl(AzjH7ali zi;S`Y8x}E1<#0CLs|W~tf|F60$)Qvuk2{aUpFdxu0~Z;v<|{y{CUBuVEUWRkhu@V9 z-#>qGxn-D!wUh)7G3nB$WC+rkC8wcn}%c z`3P75D9h+8Jw=7OUgjMu_|AVBw!}A`nx9lTh)|uo-Tdh1v$S zcy3ULD=ssxCGd~56a&|9zCJ|DBqZ6}L1@!c#5P~STmbo$<#v1qxv3=u6p`-XUw&%% zwJIa6Ve;!w;yzG9hRd%X4$-m_zs^FNO8h#6&ptVR&05^XS2bk+#KF)bzi$jng?s=n zaJKx~v=_{_ePHT!kq3%~U0tl=6fNU&Rfy9>_pECndW>6AG0;a`M(oP@gUtXU&!zb* z0k3X){XmgNcmarPzUIxwOOD48vAhLvehtzoJv3Ia+lt0(5_K3~kYhs_dk z@*nH8)9o!V$6*<$oF4qw2dLYvuQ09j0ZIeYg#_a5l!%xO&`6d=0PAF{vEw$DoMfp* z@$2J44Ovdt-I8knKVq7dOp3~5X)9^aZwIvQXf_apLOJ4iz&x#T0n|~(A|E_@DIvO;x$$GyJLR|744Vcrb76{z{NuZ}%k> zWBdX)w$RwwAVNUR5syoRu%oxZm!w{*u~YOxo}wPal6rV~Jtao$y`;Mf3_7zxH}|Nl z4!yGri`!EY8Hou{7!GN#;YGo0FbGqOCRwwKfD`-xa}+-v!m8*mu<=U3YxGUw>M#N> zu%Z_B`#os5?RO0#8JK~wR;oZeW7H8 z()aQ6?+KX#lKGcGW~L_oeF==VB)v$$EhYaJAhj3c-zEyRgnz5VZzu3?@n^xm23fWI zSor7Hn{!s{8sb`)=Lw2w+^gHw9$pKX#h4h7Y`OLr_xX}-ppN#iP=3)^-q+s73FP@J zV?f&>%_#C%)&Gygi(*p~RWL38G=68q3b4Z|Pi1!DnOh7U5qQ%k6aI0%5BBLC`9 zycvF<+%<(6u$j-3B^nnY3n(XnIZWTZ3#VcWOWN=!f0%Go?^H7^SECIw+{&b6_y1Myzb zfsl+jW)$nxWd{xxZ%o&PASx~AO!cM*OB;&9&;Rb)Z^5S+omd-vdX0xJVt~!cRvcT3 zKjfHONFR866h>UN+0P2Mx>|S=cHi3A!8an=3GPQb+0vmfZEW$^#UnkESeaBbR&!{e zMy@E%u=vz1)7B#>z~dg0<;3?TRvt_FwqjTwI2tsuNVYjbwGsU`hN%=IizMN-nN|Nv z79z*sF>3c1o&T1z?_-Pei$d7;_^1l6hc&h;Zfc3-n8Q*#^;ZufMq8ShDR8KJ1W8g( z#2R(lro%!-2syoZYBxtI4m$+77kHchVKN7`SQx02fFugx2==Hn11mKXnl(?u0*k1J zMDi5HLD_h!h+DpAEf}S5>%ceBTO2nC5Xq?n72;-HNm`59YKxq|+-CNTAYr8!P&|rT z@@Y}BCsDWuTlcEfa1tp&Yudkf4#N`b`J`9D%s)jcaccte9mAhkYM)a7qiTMV_IfT9 zs5X0DXY(R)dwn==QhP-pA#_f^E7%Jy_h-58fJTD z$h`O$XYp`~1>2&RQW!S7v?7BJ2dZ5n+_6}Lm)Q(h+pRYqvFQhUysrv67L5uf-xo}} zFTkgR5$+3mnw7B__L*DgMwf#R#}2H}tA3MVK8O6-61xWTXU~U@8);cT%gEs{rR!&~irAbJ$&Ys|8GXT2>NsUx=-)dUvo$D$Yv!+>dsNA9bi)6o1TeATLbBmxY%cD0g2ajSm#skA$f=bBOEtBzF z$+#glo=fD;5aYT0MY2K0^ZZzh=WO`c!yZp8UuP-YM0z(>h=?oMH^@5;vY)t8MFA7N zV{0INk-t!q4-kJkAXw3$^={#!JQ!IpTP)S9RmDl0Nlw!Q z_l?|1Mdx&Qpj>uVk6*-xCE2>1&m{&fweTbN%n+9_S^amSYuxbya>s-UBT@9>`ZpE|IA$|Be)`hsX zm?z@ze*{Pb69=$^=2@I`O4cMl1Th@QT@Q_y zd`HVb*C}AhUPCm;-a|A1hzW_&IBFFX0lBKqCB!4?k}u#B2eU%U8b-E*Bt-QG^& zbelPlR6;#zL?zbQR@%PZ81l^hg_$~}v zkDYLzcpWzXJUYs=9_U503QlXyv+L4V9I<@e{sKl3%GW9I^Dsyyz!MD6?OdYEeoHr< zl*re`zp>z>^7V{H-k+R&g%+gEJTDoFTIA|Y$^2j&a}9Ep6-jb(wFk6J((G(1DK;?9`F_Af!7Xfa0G{-{5)eUpGNXZ)6AtAs+CK41`b zy*<7N3{*HjggfIH`LE$UZ&cJv99OX#t_F6XTqaq(Q25 zlVE}75r0aPAgPPw>jdqQY{ps70+oT6Xe+U0u&wSQS^bOFpgfa+(R-^?;s>LGYNJ}@@slx$F#?7C2>{V7?_GdhHb|O?KA6!DIg3MSrhAx zOzo~8_xE}LS1+O)hQ!N$Dts{a&Fte}WKCl(>{@&lHVgkGa31`S`>iRFn6}b5WMka%ZUMXzj zRITvr}!)-snz^c^uE)+GADO75gVy@+%~kQUWRDh z`v>Ct@oj5ZI{v;Yb90|rkt;?q{ywHp-lDz7lM?=}ClPFvzuW4akiQ%19rCdvf2YCD zEGz#003FuI-@>mV{QV7O3>IcK5VaiNo0baFBzGKtS0;b|2&?kR^SAOxE+C$5rb(2h z&%7bi0!%%WGcC$F6GJp#f;{UvAMhoad{~hcF7cSV~VIfqH##{h1)^qu+3 z2z}oGu_N?74eN1n`tJ36(02?I7V^^6$3fqU{x{|K5D2JsH$F#cESUL}5O@WxoQ&R* zoRp8NkR;z?XhH9Cht0qNdC^g(_$*h)NYQ~A|^AtGsn!t)T$DQo7C_N zA0XOPQ50hVpWDncc46_1WCh;`)VFpuuHb3To2hAQ+8%Bc9`%!WS#we^2@9gJa5}x! zP`?*zKp5)xUXO#Y9dI2#3i&Gw)gdA{-ATMp9}6%v24ULFo~kQi!Gu`W7P>dgr_*Tx z9i_TFDjrU%SoVjuP^~E9S7HTcjJFjQGs{x^A6u9zF1j~>kBJWvHGmuc76ked zG6YLapm^3I!~Sz-(XTCoM4kfklorJ~iV6%wiU)2{o__Sw=sMCNKrlGezL8RY^xv;s zc3uw&FcE%dA1|=J9s5jkDSfg0oNAC+RTuJ7W`n0KwL#tsn$weeg`jW$IGK6VE)AMj?Wd6~RBaciF*j1J%tP z8lH$0=gk$+I(7%rYZ5DEPKT;8-Cp<*g&_X)c4E$ZFpto8jb0Qv$ zHu@zRsb3&oPK(+3XJ|(62?Wtx*F1t>ANTDW_^0-Y%IEZ$AUI(6&W`%gU4G(rSEetrGhe^b`P-KV}TxQpCm zNL7Qru8ZuS)7N9hBso&?)@V51XE_LBr>)bBK$@^LwG1JCGcckq^&zDc(a~u z6)&ocuN!7Y$M=4Ge2d-qs-c>vxPb;KL{7s;H32_((~Rd!&gUtREUlISNc^O5XyT2S zc`3(ec3^six$PLKr7GC!l~-!C4Q^M^sf zro{EGf4}j>;wg$^)=a&LlW#u@8MB$VzjSar61D{;`(GW4+pJQo29b{hOEXbKu$0FS zG-0kTx#>Q<17Fg<(yPi*ag&WBy^6BDXl_hdLZRuwm4{lHxG3tJ!9FjBz*$E1^nk+88@ zd-Pq7>Yg3=3SZZTMhWf`T;I%K0^?Ts`zXTsfz(-Q1}~SSoc(vgSgrNm(1q6wtH@ zG+Z=Tgpf7wKoNS1f3k(sVb{bR%bGR_7@BdN%G`v(Z9|jSG3*znqw>K?mhy?MjtxYp zDR?+XQK^$cvdv4NPoC8a9JLIyl_1mR5>5f~K;5GZY0$=p8MC*HOP{91s{5x|^RCqa z0)04_TV4@jAs~7LQ81^$UwXT-seYHo#@4;vxU%3}fGZ}@a`OcKeZ!PfpA?LNitDXNKH;E*K zrDQNKYYWMeVbM;)*A5k2Lp~6Krd~qEtbbc|yp!QkhV}s`qmYB921KL zTJP!_fq?`%f9uxoj5&y5ldw9XTbU?rhZX`4FGDCf(Xz(FdLi+cdZxl+;Lnui=Ege? zZpJ5EsCa=j6s%lBm{=kY6K(9)WKS8fE_h5HOX`E>QTt&Ffwd-yVp2wLjA|$#gb$S_ zb6rSK5g#GC*Rn2gvZ-VM0nOVROL-Q|P!x}jFAi^q5y2}Pua}I=#5?f#32t6B{)QQ) z$I4AoR1lJ&GQJzZr>+j7a{FU{P)qxy@s;Y&{g?%-V`9ftDc(tuiy1SPGp(qTFjc=y zBrJw*IQHUMfYQVo2%Hdg9VApPY566l!d8&QQ*M|b8gjSI$-w7(kPq-)?mqo!s)>Y zUyjtOEyYz^|K^+5sR&jZ(h%?%sGbJW_}tz7mQK9Ct}1gi@Fm|Pj?s&7&X7sX63tAr zUJ~zjXz(>G_@Hl@1&@ypT=Mqv;`vtc!HzmnHomC@5JQW&mv4j`u+)Z$c>;GBnnCKgj9lhAQA%p5OH+@@sXSJZD zS`(`UTzH(N)pD%XQc|sf)mm8X2(6Y6y7WUDuVIOTPo5-q;QzL9#lCsm%LC4 z*hQ$B1o4;eL@RzAOOs{uLAbP^x@&FV~h_~*@;pqSua z#R`S~R_RroKRIF~sHcg#Y}XM2r_E{(b$Kk~$(tLo3xiq>_N{N8y~-BAkBph)ysu$m zRwF$HsjRsO-U!Ce+)q6{D-acIXYmW&wkU|Um|fJ;73Ls(2DWKN9eCEy+IiCrF$5fr zy<>R!w#fY9Nf+@-F8?nN7`O>CdDA8{l~V0{S4OCIC=_;hcqGsZy+oa)d<32SEmw4_ zU(RG5Q>)I)PHv*|f^M2fKc9a|h?t`FHFU_Dc}f7Z&6sVGnU^tVK>H#0XT{^06F|7i z?UL|~w{Nboy?#$pBrapFojY`|@$qJ;87Z?994)_10)x((J-QT3HNq$ISAZJWZG9sx z&)EP|0wIqZ9!ul+>LBm4V(49~7cU)p(2m&n_4=To&UF%<$X(VC{reHglSlHYd9@%J z?Jq75SJc=(E)NenM%FMsITg=&Q=VlhbGtJV)r28|zBf2_*U>ow&kZBJRKB0L3@M5+GbWhN)xHd!KT&>UtkvsiZ>XDww=zg(=KC?hy06sAtvtX| zCHrLN@2DE2d3gqWs@Ku5pmdqr{qBNDSD1TzJi4k(%~d<_@S!gxH50%9|3h(??G0eu zXSVw~rMuif`u6!sy9}6YzLKsb;1>jS2)a(%+nDEJ&%D!_VgND4@fzy}2RGw0C4J_q z(0Nap*R0RPME)+_@E=d*#9OoX1XTmLAs0IkvQkpNr=}yU*Mp&%s*W#zag92rJy_x@n*1pdpdsXQa(5x&R*V zHY$s5Qjo1b%-#ry0M%{xB>XRn~FcMN-p#<5O*Jw24)1tn(LM6a!)^+o)%b<^Zb z_Dq07e6EL!nWP0_m!<8ysPF`Y)<;V;8JC@s`ct|6+SZRWd=OcH3f)HAQ=Vsv+{F6` zu`8b+e<)#L4fTiA?<*MSL7xiduNU)5c)pEU3CKj08{;bIF)AsO5>m=~+zXe0**DNC zR9EBZc&&LtB-b}MVeU06)G6Umx&)(DFzzQUY)+c>`CyB5Ml|g7A z2}!W@dWF`@(Q6e34K4z&WDMto4tvV{&L${m3=gC+JO=31K*dlZJie+QzrVSns7a&J z`dRCLkTmySlF+oY{zJ18Duw8T^izDc6aS(HdZT{1zSc>0sB-HFzo4O!Ia>-&8FPVX zu}s^NcI&7-ZTs;mJn;wiR&aINM_!Nx*kGa=fCARkb-l!zLv$VIPqcse_71wU{z#>< z-5|kPl@9#)08YYH*hi$?cnJ%)IxV)F)^Mjz<~x}bY!$&kP(vEYnmSaoP7#hfx`-jg z6q}g9)X}7XL8kjSec%!9%KinUTd)?|F}z?I6pH%aHJsAf!I>t>=|LdYG_=B2-k@+%%M9oA{l%RmY$&E|1_OKObCnajW@ zzCVI5{mV+ zZr1!-rbAJbVT-Eja~Pj`{4eHQ8O0wTxg}=bAtoXI_k6_`p)(%Yfv;L*B>|WrviJk* zU6R0*Y=Jzpx|uguLt^bHhoy9J{z+ef*-C?$0~Ns(Q3%X(=Xv$-=bfC{1$WqK<-jx! zAAo#V_z;YV9bfJ85pBORW~vdi=S;Yvh^EJ6O%78iU5-g*{0F#PQlV9-SC!k}yp-+l zzi{dLKUe17Y6FPkkGeyyw^e(t`jIz_$$IF4h+B*KqeudUe22bid@XT}o9o4pVgaN6 z+$YA^X>pIqkLRA^*gj~Qz5)ZQQGi!$ z;1%r$*A=kKePouv@;pkf@ck5+Yi^a_zG3>fhw;T5cDg(+Y@cI?dP^k^4+Qgxyv-Sz zV^}#+&e*V0epo5n1*hze%1%)zuCe)7@=?COZ86=*re>ksNV}*!p3UGYFLU^uVFS*- zV#0D}fF8WKJy@AfB1jxR(3WyzMOEb$JczF)!O*vyO|i`ut*BCLdmoF6Z5KBAL2R#k zeT);^<~dcwb}dv8u|Xh6Be<0SN#Am#4G9-_fA_8rQf6UP+i^QlY8;uperMKcMa`sqU=P}$X~|k zulPPH4r_?-x#+7L-GA5CzMa2gR26nW;!`f=XqMpRW(qqD%B=bur*2;v?mC@@=eMjMB+ndtX%U%&Ht;a@>ZDcgEYoAAgm<-1($)IvuBv44Gz`wosdIy5a8(9lMWyf%LkeA3O zIMO81uWY?4kyVb*k4NQw`56|UOjPJ2gz)v%o?J2A4hKAzsLsjnEvJX_+x$ROe)qLp z9`my2^6btkB6blfMCJDz5#SHs9|gXj1%Bg89{8kcz&8-!g1s87P*#tLbaYItqy241 zJB1zH*HL8@ccDU$Duoy<@iO4;>TO6!D^E_E7g@~PVFcRen z{!yh3-5$Qm$64nj%g1To3*}?%eNh%3YbpEkBG1CsuT`P!v8WK0kNqRSf3z?PykLP} z5&~bb8t~Bq+!8Bd-^7-!8|i50SVza%j@Ah~`oXL!qqqtcqVj>Safw0wnB^myUxAQ# ze`Is4c&f~}{qM|J>ISa_(Gd|!*e_Aha{6P)Pdb@h>EUQ;^P()9Xvzl1t0LcIjpx0Z zl4$kD^QRZWc%Cenj=*>V;{<%gv_YJ0SWLX~WK1xKDCgqd3s->4oAv>qBo7(aV%aY# z-f?>6erxEDropCuF-bHmWv_AnILhDWuo9^J1zRz|th~G~Hc>^e(xl)9gWt;VUzaJ3 zHm8!T5KLtI3DTR;x@^ay(FhS*5Kz1>g* zIYv8c^CLOAYG+CC9)r9`;f0j>Y*lz=@;7VoJTYCQp>GL#xwV#q!~sq-ZB3^4gKqtd3;@b8;=Q=Zow@d*oYSeNYjM4n%H3)fA&wH?DQ}*W z%UKW1L$hHf*vrO;Buji5AZM;Qi>Ly%UGJhE>xzp{#B3wh2#;&g=(7H1E$#Yu~OVAcbq_Aq56S#xW<8@G2EB9B*vC@f(T zY@$Q~9f{LHynQM%-ySJK-%(<7D%YP>p?-XS+_R8%xJo5Z!?Ak*4XDsC77C;|udYsP zSz7`s0`oaxHv}OCaiqR2jX1{n4f&#NwQHJC3!=LxC@MRWQs}DA9wM7kg%c(Q z<~=$@$k(OL!8@Irn+?ptu+VNVp2FIXGE}^`7DJ~ZT;#jAZOV_a{_y;CK$3Prq~%u=t6ra^r3ww zTvI#-h@&RI7eccuzbNy2Ae@h>1bj{bWzY6^PO~(=R%efi+E{*3Ii=-ZX$*c(?Vi)z ziks9Efec7e2|CeYc1Q4sDO|s)0L>HN2PkHAnu(eG0(b^x6zlNABUbDcQR4$DS&MlJ z{*0_yOgq!wFtSA2%RYz(Z)Qz%n_Jn~n-7ePP&>cx6iXJaMETA}M>^vJ3;Sm$d@zUy zIov3&2tM%wC&dh+887ju50D)Y@lsdsxIys$R{*-OUvmhQtjE`(bLr}Q$U6P3vlZ*u zJ~zfCY75{cc5=HKISBJ@j8pa) za<{f~YiZM3{k^|)Q2-sa5Iw9EimVWz<;1y6gbntA%i9S4+S&Ki%(s}idX+Yp;R;M+ zy@AR6bCw(^q%BUf7*l+Y^Gy!p&4Ez@Is7Dzj5*cHF^6I+H`h5*#-vp-ENY}#DIB?! z?`zRoMuwrau7zV{mF83qUTV%iH$M2O?9uvpTH_bTiS%a$MZGs0(*r>eyg6SBY-P?s z0VH7U;^cu-b^OxRL3jjqXsbPc}KO3V%#mk$irF3u>p7CJ7c{P-a(HTN3t4C9M3SJWtE= zi}FnZG8>0)gNRCSBogV6JChy?g?<#!3L023X`MJFpM+ zR3zMAg!^W7F35nhGg&z2bYUX%4z2DoZ;qDBWg1gfqNkNuDoqCXM3hDZ4M_spQYHz` z5rLXxzY2GhgevqAH6<{BN|;{G%I3E`Up>oy&l)6nfy1<^h_axW>@!z! zngxe?gdXvrnKX>*ZOAVTqURC9JqK9`Hz@@91;qJMc0mParPnlZNDa2f450%SD5DZt z3rfd*2>`npS^pVXH?W?e_4Do$@6yZVGW;lSX}tGON^M7&9w~kPeA)9beVZTY2P-6U^xHUMTm({!KA8C#5kNG7^iTh)&gOREs za8?oo)s z$2YeJKYyZ!h~~``Kn4Py;Z~NxX`^{KfBiKlDXsUwhLbz6L0eP+s|PRP8svTrKX*TB z$^F}lNbUnbSu{KX4a4n7Q&IBdzNxsLY)kr1a6Ee3bpdUTQXp?`K7xYUF~YUm1pkQ+ zKMiJzm}3z}wz6yZ`WIPFb#93MLgAJGBdTbtwAFx#H_d1BATVmkJc1hXi!v=DTeNA5 zHkvyzldtV>TZkXmHCN+$2G$!u`v{8$+#6YxQv$;txWVW1NFU4`)+{LMlN9x|HkMyh z&Y&EkK38+VbL74`QgO*jqq!1b5K;DE{)>?kAmkLH{GuEP#5sZ%A+t<$ULaa%TtL~J zOF_oW!kcCq0x1@HSvW}x#NHes1x`76y=pOg>J?Ijxaz@NK$d+nb3uaq2uCr@nH=Va zq&nW!n2iE<>m3b2|H=sw4S59?$7#qCYmmm#AaNxG8}@F$fEu#piJ~D{Gz>LlQQ#O z6eKR*L4^aE0T?xsgqSS^#faOJ zVxwoK+D2*VX0FXyb#X)8XTEHoWaNnzXMz`rg+n?IgkX6Ae*w0azm7X=DE2vREWfB6 zpd4Z!N-po4y%d+cG@2`!5<+AT<|4`_Ahak%`9--P5a$S5gv>J0c{7U6@kgUE)POfl zL8f9=kb|_)%O`tkVKxif>x|uIw$!T@vk|VS7H#O{lTnLs0vYC2nLc5XoZ3BwyT1KS zh*u@el04~^&UbY~)Fo=A^=~@L&L*h}bc@c1)xOFL+ua8i1>IAz(QH;gz|c8aK7q%j z$>W()!z&j?nWr%I;>+)d~LIMI9%)>QHqy-@kHnw`e(zUS1TNo;nHianwH_vYc)DR z!=+a{yildr4y4z)%r-1@dd2<2XAfkbrBWY}N0C^SXSa)huiU4=;YdI`A4N(Sf6bGS zN?Lu0=r-n!eNcd|$$?ub3LNX`K`klPX)!AwBqP`6J!}pcY`+_p22dF|Z1P3kQ(<*N z3rlC2(r41lzqtDye6>G(wvs+`I9h?n(HGM9Tkv%yBxx}xgW$@5yY~SD{z=<6q74dF z?31?-8EPy}l*=_>q!_B0Bs+Ec!Kch-Bq7L+;xFss^fT6z2^hk`Nc+Wi31k!~pZ zDsb!SlbjxKC(S#poW@g)9k&TKlDwgr=@IJ7%uXP`7U`ySkp#sw83lmln-pr$U=BsH zhkGuvddt3Hmp1>{Q)n!mV*xsd9?pr>hW)TcP8-RP?RlL5Z%Y$txZ7x+P>W00bC@+K zuA{dFa1@ah=xTsy`3knT3ab(yNAv@qV$9if5VFu~1e}Pw$B>ASvF5=ElMkY=)=1v52W0MR^k6%x#2xYeQ=}Gn0B2s`j~ZPRM-2SI>l+jK7*Buz_(O_q z1cm4+;{6O3zPh`mPo=qu1Wi?&_#vOH&+w0hl-M?qR!65q{oWCetzy;nx&_xxAVY zy!#fPx=-~|IcLb*yy@b03H4Iyal7HSSg%}Kv=r;@(5eZSr+Rqd?Gxq*FuKX!N0@Oo-FUQ?Mu0kwLOV&mIT^nDi|SLGQ>IJYTNxh8t$Nx$1O;JYjQOY{ zciY`vnC0P%L`d9(D^G9WEZEyaXM|okp%TcyIt7Ljfw%R$$1gk!#^X;Lj^>u>suP5V z55ma4tO$I!%LFW;SoVX-a*nv5lE#wP1>@fi8aM63O-%2_^&knsQv*TAqEW$$eSGrN z+l?(xH>Mx86)cq7$p|!Eo)vnFpDkgA+Gc)C!Y##^Gx6M0g73FS}Uqft6NT>Qj=X_!e$z zvT^`hIkXf%2zFV3;c;Tw$kAf(H9$qW8oCp*V6MhYy^JTwI6X!C@@7X&G^8g+K%Z}O z7IEd@S03EKCN0uP@jZM+bbr51>`l0FzJGaO(t%aB-EV*j98|8RSw9iqQL@-4R?1J@ zKiSod0h34uYWMmnP`hh5vvE4~hKy=?wwT>iaZ)ggYrM#$oU;~LlL{6t8WZe*x@Thw z*0MFuuyQqG>_t~DBKj=nVU@Zf>?R*z{|!-uc=J!=y}c+jWui0QV#@^wc8tWBb{LkIcg~z3w?DLmlg-yF%6#lp+NO7aAVjKlRhS z(HJPSyJluAJOPYCV|T{ZT>ElMHM}htcQX*W5B{_4`)KUk1tJx6>(0lcrVuJ?d_}^Y zKGUlq(@ip%FmFq4m>X!BFj5;6>F-mE97=e%4{A7eA*(DS;mN>(;7 zQ+IUxQ?NVH0Reh4uWAEmKg$D6vRB02gdVw-M5yxM}nNu1JRzPpVGA~QDc?E>|B2XQ$9U@CY`b~J94 z{&P!eZ=ta^{`og5(|auAWd#4Bo=Yev6FA;4+zz7AQSSQ3=c5#@T84r=YMAxkMc6(Z z_}3jdMEy$fNWo59O;7+oZPDF_%Fm=y@XQD|spBrZWc$&2hyScRz?@GDE3I&_m+!{ivd6Kqr!ewiDNQVLq$NwZpefq0kBcjK5b!|| z&@?k@6|=3=eB`cJYQ9#2%6Znza!=X@T67oyDXHVj#oc`k86?`sr%M4Mth1iBVx)=+ zO{G9Qshp=#+#c5DJ38ig{6{Ag`Q(s}@K&X85(u@a-{NuhMsTn@j2k9n$1Vwz+~ zcx&%e3h%c=)=bZ7uUC>(on;DE*h=6pnK24i+ zR1KUfh-Gv{RT-~yvSONF)mXKHeSX+}-idzeivtq}Q>UT~TE*ax>-X~1* ztmYFYEe|0HilYIbjpjUV6p)7~q8c(Fuwpg^LO88_C|TcN#*UPhfORG4s46-#HCr-M;o8DF%Ax99KdUg(Ir*zxSh8g zcoYc!Vn|!qcT2SIcJs1$-?ne=4iS3>1h~ysq~BTZ5UgUog{Y#)j$qrw!OrAhgM%%d zFa&#te(J~ZDU=El6~FEEF>fcV(yv@cQ_X!lGSw_;a0Xp6fq~$1!-^P)A+`(|2geRy z6YLlM2sv|d#>+~)6_Alx>ZCq)X#^>f^76I#;tfIfR3^Ol0Tw+rtv$FEZ2eCd^mL3g z37=$N8I)lpK84%>r=Jmjl7UWWk9m^ISPfu)Wa^J4?Z3JM#=ve0>;{s-eAokUxDiLl zAWvikJIMddVvrA6n^(=cCcclJB4snoA_&jM;)C$`&#cgKaz*X4Hle|l!4U{B835I4 zI9vGC!0X&OjlAFFKo!@vMaMG&mm@@QiVwCyeyal2;xZU5MeUs&dP8j@-k3gri5P;y zA_GFw!QQ=RGkZ5pQsNP9(@v9}&S`F-k3J%rf(+DYy}O0xjUdE2nPtsBpSR4ePHI8h z>yJRri4AlpM)IA0U;QOa&D+gzIdd&SI||kit8N~?3_9Zm{Rk|mg2|YzLzo#8D9j{b z1;!p;`2HP9k`usiDbY=z`3zuGKug<;3`96>r2w8nsk=4&`qBAY*qJq>W!0AnJsvEO ztcWT^arr@euf@9|q$jcCgo)R$L3QunF1FsbE#h9)*`MJ_kJZ_SHi6ErLbg=%h~oW= z(gA5CDyT^!>hyfY0n|nWPpA=8In0?_G5p}z~y)aY-n;%MfQ+GM$sU`(5|zL=3hPHg~=Wk)r4~8NxpvK{)IAQ?JHONNk zCXPL8&TF=?l7iA&+Ml`pQ0>F>1*B`J!9w^~Ks#=jl>%NK9IB{)#gO%*^G9WRb#jdi zgC8cngiM0B74odvcf-nqEvI*R``uDi+LzL+$y@f$?YjuBNKmcpJ|c zT_CHp!bA4Oq*28Ny^{Bg`vD)XC%3aQT3}Lk;h+98?v%z)p{8xjD{0J&*`1ok3wUk<2d_H?;;5#J$myvmQ z5X37N2+kt_{?*h*R-V&Myo?HTG z8x}1-y zZAf`JQwJ$c>BXNA02T>(2k;1G`s!heKJzbRI(r5MWi2O4pU|0d2L+z-i%wOq)Ptm`A4?-A>KhV8sL?EL1< ztBi0SD(DEmTHd)oVw0VJm7Vuu$2rMQ^7fq;9r<~-^B;Bjk#AP5^9^n1RTp|H$ltQQ z9gjy;-ZoR)CQNBD>#zv+`2AqN{--4Z33BJDQG(EGgS>tFGEb1THmkyoRZyYF3N-qF zRkmkW&miT$7wKrfSVwea(9z~$M?Z)C?pQKv`WPH(@6X1P;KOn!RuBZCue76V@` zp)^ba=MKX*^At8c?G8-Ld+&{+WJN5Be}2uLFwbw5J&)5rMKWj2hhoM1%xw%LqtFuK zPuAa3QcHd2q8jUePvP%ht-c6AU`8>YiP&L-RjeHnGz0TIip-~=*iXcBrg2rqA03s` z6Xowe=tr>Jw0bbo$JW$geiMfViU*VJ@@6PONNq_{76B*4d5{LP4I@E3&+|DdHJHzG zeAv#HKlfzbL)8XT&wEhq{5ce`?!7|Qexo%i$$Qj`fIa?oFUbdNQboW5ROrD9UyH2p zWzCh$w+pT$j-*6lD|`h$EI3bX#vY-=pgzWA1Pij(hP0LQo}EvjLp(ro1>&2J8u%TR z!Vc7R7+vjaOfy7kURRdj1HD)cO9pn#v+ ztFzGIRvrJXBKSXbLKOeUEdKL5JpS8O!+*17i$7@JgczuN=yCZA2MM8q z=$TE|v^Fvw3K}^W&x*I088njUqxz1ND*gRx#hUs%Zncs1mq+ZCg-pN|(6!5g$?Fhh z?4oS7+5@SQqMhY z0SysdK}uaWh;`K91ggBEDfpcW%5aN4cPq!?Mv_YP{inMw9jg)7O86DKKy*jI&yMwLhS%BrP$cig`t#lHz$yS5q1w>jm?BWI885 zE&B-gvKGv2YKF*tL98!YrB&`F;{p1z)$X5xEZ8}$ASnj;J*%!n`GAOlhpcm9qh+x8 zC4`nC4mpRBBx>d3m#Cooassn9ylDDHL(5l4spP8!`<1e)eU`50p=bkUjFrLTAAr1? zX#MahDHC%xCm}3)e+ORq0*TiZOTaUcQSNSEpl1?;zBOFOdx)jeqU}1*L zCGtVJ(c}e{bRQWX$)BOSi^@WNw2p%3qIwC=br+#F=0=kmnbNy$C`ic3OlOfzQMX&nFi>!Ga zxW?lg*JVQc43h{!-E>JNu+|*BCy|_*l-=H5A zdN{niyuV|Ek-+SsNLE}cbvQ?VYsCLIiZk)Q{ip=|Z@^8(|FhNbZxZ}Lry z@X*0j6mh+-5&!WK{NFh;0smKUQ}I81?JD%oqe24xAH>Suu<`fyqKOPm1((L=Cw?gi z%T1W|`HF>o<&ZuhRyUX%Oq{XeMDp;V4=^%j#s+cSiR*WUNFYVn3)?8x(ytfqwXW4x zEc6T!?RxzK7!0U;t%f`>mm}aNj@8icM2L@1jHWp8 zLXMPKp~-fuR#{ge%lyMb>0_bW7L0yq|GP?m-y2m^f4i@qYimbj?hM zIgj4EBldC<2q{h9ldTH6dh;nsMkLsKu-vV)dpVPJC-s=%U|5-ggy|mZ9hSFL6#8V% zSh|d$P(W!}kCcUEI%yFJK~uVD+c%ucEZS{bh7$6lL!uH=Tux^5OQv}VS#k9$5;7VU zL_%_*R4^a3WgU^C*1#N-!C;wWxmIRs@~4l=3BdxKif;qbE{U7n#WP0hh3r%NNTld$)OwW=dF(K z-VuBk9vH>R4QW(B!Pp*q6o3 zCDb6W3t30HI=F(;yZ7g)4WEIe@OIvF78zt13F@NgRJ+x)SD|*em9z2#x~jU|4VOt+ zv^4rsX*`_i?RX;CtNqWKgX(o`M7W&@!_w&)N(J+)_uh{qOlz@{!?yaI?}Z5$$@tmh zxqxE3CAoWV+1qE`lw*nR%q>bnyhIhp5z!fdJTmPPcSvc;c; ztSO(}@#XsHO~^Tj({9BRGOLI0t+h5$)jeV##nEC>F{$~wl03;_l-OjLxhufWFlvds-bfMEJB4~q;JY@=cBSyQ0aCqLN z8T`;PGKT`$mNO5MZFnqqAea-a!4ScetvTXrwun&PfSvy2Je|wa_KgbVc4I{-zla5Q z70UkcRfO_kR1l#As%1i1AQmF6z&*AO2MxccxgF?_;_fDjB2kr%i2?7*u0~$TCOTeV zS7V!N#H8bMvUQU_Cl=QTsqvCcc?1N22`Vjd{b=`0*m5jLHHj{SHpFo>e}YefQCL76 zNV_{b%4xr|%F4;l!+!Z)McqylXd=EUiAon_xL@SZWMv5~BCc``O_Xhcz!-aII_*K0 zz~Ls!5X${3OR^Y<@(Ws)Mkp^TDI1o%^*)|2(dPmWV-gIg!&&O^OrinR7j$4Q&L>%1 z@2)n9CSWcd#c;n3`xV#o5G-8(F4z$W<9doGls(Rno_Vta9uvOB%DVRw6%0Wlh1JW6 z0}{k}_aE>Edje=1sl$K#2*Q=}8?wq`=2-Q?`)JXvHgI>$+dTtUQeJdG2*to$p@z1& z7k0mAU>a_PgksJRFqi`|>%5~7rL>CJ+0N#)fKaz9CVicH0JP58Ac%JKw~;H{@wucS#`9fzrZJWCjN9(xsG z;DJ1ZZ52p)ptyqzeWon}uVO1{AV_A+xr6V+sdpVanIk)YxE9&z2>VmS8#09y$dgtf zsFLcZ@S*`Bi+h|!>=g+q2JvZ$5jwTW68lEe!GmM#OpXDG?9QY4E?b+ac~bcQ6_*(W ze7pih;M~;)?*K2YBN$9h>uG)YkPmh_uUE>%lINqUJg?MVK5Zb=RiBy42^Nz6@Y^)= z@XgJhup&BXa@n&RON!>Qzp++DydA+x$NM7*9s0~;x}=pd1Mimu-@G>9=%f_>f;Wg( zpSfQ1Q7Ln20{q|nwI<}F@KfYh#w=#GCKC1d{`0gw1yTu|W92&QyBLj@BNUVzKS2R3o7)mA9Uf<3NV*QEA$iXg87mVG+Lq0oC?E4 zd~>$SKAP*2NJZ`i=2r{?O5rZkGY;eVS|M`QoGPgoK|Ny*g}2cYCAkwV`7LYj*SC(6 z$uh#Kr26h#55CoANWoO@t#tnj@bxYK3z}TsGLBy^=ni9DZJ9SaQPC`?TFiThvV@nl z5-e%a&ySB7p5{x>nFjTnECuZ;Qnn*hl4^Ni0~t)57a zR&Fe3`jMNI#djzZHGVm$PB~wl5XR{rm51|@11N77ub-JJ@&F-%)^R#$K`xAqDy|_W z3ov95ixR6 z{>pg_^ph{+qzmfjr|rX5E#?^ACM7`fW`8Y$i~dN4K2Dn$c9>FaQ60>fXE4c9z=mFm zOR~2SDg7tr;BqNd{&d&>e&=yX9;@tc&fV1uf7U$q7oZ4HOmo3MT{jJhd3q+}Xzn-< z7svTel~_3o#kL~uem+4#>FYme9#c+=O7yUG8Sx`_M=jz=)bBj!x0${a&ike4}?GL(<%O$Jx7!zqS zQK88%%Be)?_HLiq&q7xizhLA#A3o6>OWMhgn{nt9+k8p;a#c={-;7`S=$>%s+K>ha zTwqp%ErU!%7cc_IN1_eS9af>Es1SXMucP)w&wK5v-)jRew9E=b5){_o=~^3tfx93O~jL)SSM4yi-BPq(=&QKOS>&HDCdGm@=X@Pu(KsY3zGumCJ z1}FUUecney0wFMxC4j(PNmuWqJVrBioEp#5&34F_t6T7ew1K~)a~iLyUAZN9_X=t> zN?X1z@{5|IU|2S^_U+`{5~;zxD3Ed(ya8PFo96~ZO~nM7hT^93&|D7CwljigiU6O% ziGE0vIi$45$74HNzhY+CFm|u=J$`i~lb-_sH zmq+E563ZlW>no&oWNz~FOHM3${k7|QRn5ws&gMLSb$e?i3Ann2E+BdVtc-15##NE5Oll%fkhHIF3$m3C1V;do>d9Ke&R3}A|b{j zWQ-ta_^_3(R}4X<3xwiqOO&MAt@kN{Qo0usE9eUR^@w0m60Knma4iJ8QPU6iOeI~x zl}rLx1R)+|ZvOI`z{VGorP>^+ZhpK_`_tRR4!P2u47N*aKj-_rYB+yvJ=FZs zM%9?~yTtk9oj=*zOE-VyIJUgm@Fg8V9uvrQNBapRAJp3kzR&E8;maU#ZxM|I;)qQn z_pIXfnK$pH&Vur;En}$0!=7$D>=C*+Df1-a0BldX8w;fXLdv#aCU#8(G@4|?CTVkR zNt3ME$Tw*`jOV+~JWcEddew4kN%@45a>11$ALRk4^2eV)+dclWqdZYEm|&%KpZU{0 zn1V#gJWYNnX(kGuC*4hQiz7Ycj~r=aVpWOs^Xp6jT)*QQetvD2J8%{8Yi@unNZ>Y| zf!l%d2pbLf>nGNufs@o}?EHhy5K=+sx7RS~!5rtKZ85{L3RV#QAA8>dXXkXaKZy(z z#2jh})jBk7rzi(0qx5q^`Z*!h)1rpDjJkAO$}|!km)494otPY*Y7qC-HAM;9NJ1Gz za**m!m-tXoEjyi*RM3dl{GVs7z4yD{bD2vL?Z5qHe&jv-w)R?Uuf6tKYwvfz!be`B}HaBnPb8|}p>?F$@`s9jgUi$+l12(TLIKj9wK`}*c z6LVorEM^+?Fl|1?COj1o84R_ohjhw9ktfo!&HzbyBAvG7@^%Y1w|}cG6)`W+25cTG{i* z$GphlW`vNT7Y{Do93*h5i^5Ks9U|BW;1=D7Wwo;_sSyI8^NO&%V6OFMCjx@)IV zX*&a*va&GAtWh`SZ`FbD1Q z1{`pS2~%f#z_F&PNC7vPPeyPWBRC>&V09OoDy#X+taw3r(O7`yG!znOZJy)TWEO~F zAmqy^==eFHdDQkiX2M);;Wih*osPng$I=f6(w^Wu#`WV)T{%9uuDaQk<1`Z{C0+UZ z=mY32><#>t3f~%qYdXVK*?x@^p zQ@hGdsE6vqAiIi)LXs7r4QDfCD-XhUJhgYkyxVeC@0UYs1XT;f~umzi9uCCve+ zB6~KZ8tUcOiIRNZa#(fl&0;13umL!JyFDo(YK`fb&jP$~sCUs6 z8G1V%erS$K!fbUsqm{z?E}hZAL%%Bv`eoYu^4Xx@iU42st1B=19i{_>5Uc*s9JArm z@qSAWK^36}^fGO}r2WbOAnwX40m0;}5KPZh2xbi-m^o$_S^RaZNwAt_g=0UGP=k({ zHV^z>2(}$3StaP0q97fQLVz6$|D54sZN1M?ay9M56D2d9!}SQ1j4b2Qmo@#QcovIqWHBNET&7u{ z%QOq92~%eNy=o2B3Z5`X_=I4jY+7Mf02dzW(cHZG=pI?w3E*)3Y0rvoE^!U0Kfz3Q z9zEg;C2#gX6V(_a?7>TPR?)Lja0-*x7S;(0_EaP+f=|)yDEgHBe|kK6z>eX%it9M= zF>s*0#Oo2&qS5F`!qcu>BO8)T{aaPI|IZ?4QtHkvUu34>nN;i4^l zaDxSCfevZ-<5!x$kc4At_y3WJ7eZ)c9`_ol15G3`V{xoAZDej+KW{~rAL!EO+j;yA zoo39rSVNU4hiomo&t^L8y$ZGF?)Q9{1lZLM@`Evj{ERtgKs!D@;19R!FUPL2#|pUM zo%%eNYcNaLjV$|S2a!z@S>#h37R%PH5Wu&TbT0>6`ttGc;^;{+-%8>$s~DfUf#I|1 zn&Tt)5rE7D4EI9rmHeJY16k2z&cH60XQCWo5#C{Q2V4OsQ!165TS$qFiew@mM?S)P ztwd_&#h2zYzr|LlQo&B}K|%fG62uiC>_KEC*#9Ttnb_eG@W7uEHw4=eeiqO_TzV_5 z3$)iBsxP|2rgcb*ul35Za+c>in$OyrJDDxgKt5l{jxU%GmC$pj?L@q`dWA12`}5T} zN95Xe6SsPE4qEaTd}Exij!r=n>UcOB42U%pk3q2%t-YMW`IBi~)UpB5@=0(oTAsI6Baj&g*3*Bszs0LP0`IQB;#1ni;l45;K{|MQ>8f zv4v@}g49Px+hxR;NV$x7lWBj1Bd#$8d#GNHmjOX1v?A_R=CYSK{;)ZT7XPTL>nTFg z2UHN+SoUs>wV^YUvoHR=CTC#eJlCCHtweAHiXMh(7hwErWvP@@TK^r?CQ9m;pj?nG zx1L~;D2@H~-VNe6asL8ZG zqZlQbY&H{Y5(>)(a<+b1b`Jc)guMh&Ei&WcmV#}9x$tn$=@h4SbSZDC@Co>J=IqBl z?Cse^Cy7x7G=^@9)TfZf)`|&7vM0A!PfLx)fbZM<#zOK_Jm?mVuuq z1J|R!Bn(`eDy~R>$;o@1b(DLf%obKFfFADdTq{uskPnTa$F55rDhdR-ANaGejbRlX z;lDX(_=WO6*fb&mICY7CaesqNE8OP`_wV zY4~-7pIS@s4;?j9dkJV4^q7sYg6fcD63O&1Ntd627KWJiG}sy;o9@~{c@qgca9!_I zZ#ry<%NSuh4yqWm< zNYZ;emu10gVY&)4T=<9Yw4|wNd}3sjXe;4F1sDfoB24%EO|zFQWxuw1wm;>198^iC zFG=aF`>Lo;(Hz^EF8Vck_&jAkhlSi$FSYp?;NgDy{*yu_PF{gUvV`)3B=V!m{FCav zv~2tK+0qLNF>WxY2^aZmifApteY6=y+;d|`jn*lTubv_ev3IQK3`Utff31!G()KIq^jbS2HJ^g?(sc#Kf&EUCgz~B zo$b|cXGP=9W$kQkhGC{dmYBTtK~2_j*8;gRguJ2=rS820S`KGzz~b7TJxkO}x_6>w z%FPPAkixoMobc zK63$19=HL+_bd)5@IkE#I)`Uf02vcbrbyuO%8fKqEy2R1Ibg1bh%|f?(PWJ*Y!VsA z54wzmC%NhuBR8!^%%CjNnD`VWtiC}{y4aLS`_LH_-bb{7$}J;ae~BIfd-LYE)KKN> z7p;%zi5vzW{hMKpPm*7yA(UvJR!z^D6{6XSOhw0(&D45 z&#{8)@pJZs9^Zp;t$~PV349zUcu2G=Gi8`w;MxXRIxf?}RD}yaK@Kzx>4U7$W5g`^ z7k`g5h!45Or9;Z_c1o7uF8!J&^}khE{y|fs}#V|64*HVZs*O z@A=COqow<@;o;J4@AsUHOuKY{@OVE<_i|L|joC zb-!eP?}v+*3H@sxrf&|Vw)Gr5F-?=^WVbrfA`}n4*8K8r*20AEm)H{HQ>6dSyRW?d z>zhAND6p4zd{{2}Y3t*CxLoC_!}~gq*^SLmbM-8x87pM?zSx;i&t%1#oX2!WpDOox zyUM-4)eQxX_!#4F{L6IMFW@zS^$;(6CLuTCgRfP>)C8%Tv#=!(WH*-v~(d=F*lEY%)GluiM{j@cg)N{X2rzQV9e+E5*n5UyqmIL9@34zh?l<8 z!e6d;J%%+NP6Ot4H*ubU(%zc$9>h5ov|3$+#{_4|L8o0P>DibOLH^<&>v!>q z(&>_Rcf}|@y*29ts>em%KH0qOCc*3@i|rl6R;hdWPNi-ZL4i6xmAZ#UD|L7a=Y)j; z#Qae79H)-FBXw9ag*pQ1t-0hkN}Yhtjo5PoPo3ld3i+$l@fZI{9esb$GcM}jc>zf_|tp!JmM5Flmg5jY}mK!loK2?OFpy_|- zJq1(E)^A;wq@zUCI`?DLLW5t04VaJy{DB4pjs{Hq@GzVbVM~wV zJqsZS$4`~g>e<7ps%K9XH1>Y@N1rdOdr?8%cEz}-W9eb?tw~L)+>=XYGYP`#KwCxP zJH88LBATrEPW29=l@N%UEQeDIML>8;*)X(Kd^=c-j^RsKc-uS_@^lxE)V))k_*BjI z>+y*fLOIS55|J;Ah*UqV)1H8p)ZfL7Mw#?;1T0!P^J-(ZpJB}Q>(~)z%oa0AgcvjF za|jz?CQX#K`F@RuI=Kx=)S}%rIqx-TG0@Xy1^b(tvC(?##z`$}{{Qj?nvD~(iC)%vE zX?Y@D!ASvKS<5HXM<>eh{x4b&;Qe3hwE;bnQ}X>^0LEUBY-BKPwG2-8{MCPb))j_*R5Phpu-+ckN~Fc zD`xf@GHGbf{?K&S?gvdLcEFxof2iM<s%+x(^yY%Du)G5!_2kNs2FKw`y7{I**} zg{I8)cZ9*$=>0mWA>^Z$dmsC&sNAWxU=Y9E1E4Xc_Lc6$ya@OkkZ4zbEyZsizqedx zXZPD#p*_p6lBF0Q&pYVH?x?HW0uK4BKn$m6SkF*=bFr-y`hh%mh{;kgSbg{R(nTfu z+qkd(g7M`X;%sw9vYma2&}}Qg#>Yo;l|8GfN~r{U--(z!FZ~?yf|*y~zy29aUQ_?o z??3+j7gi?WSv1sRQfcfgwNMOd+F>GV3tMPvw9se+*$~E3W5G#L$nV^U4INa9`bRfY z8iUrhe)ItYYHKwk^Jbq5otwe;Azo(*lrw9z&FKO@6+gliFfM}#R-Dg@^MZF+gO^X`oA%k*wKFW{d#-0gPE%xUKVB(7s4=RON*FKK_36 zE&F2hAdeX{5mq;-7?Az?pJvYm^A9o%Duey8tKkxgc#3}I6e2%TX5U*weHH0fsZpq3 z|9p!KuLyaxKb8h)?TlM&iFpz5i)5P;WqnMFva7|SMR9r*-`@jd>CU$5w=+ARa+OcM z->-f-_gYmX1^Q)2o(aPQSd;je*Dq&c!mG-_eHY&g^$X{#M!ovtU9moycbbL5p}i9- zva~+wmba%WuX)Lvs$KSdswI5-P{!gGr(-0Pp@p+B0cox_vnssQdTEO5jPW$N8^cgW z(sTj>E4N4``$W3kro;ZG+1$LjL1Pgxl+1>|6p@29j25b|R%EJo=vq;IOy!^68;ZAF z;Dw_T(S8DrZZ>-8410)N?*?R>U7+1MS5zOVEyaZ!e(A3hwvBH1#`b`ILkJaCmPPN=_P#nh>qsw&D9ci za4;&wxIv02nRiG6;S*6~?ng>rdhH@87-(Ncb^5AEI(1oIKVEc*(~pP3uEYHLuhfry zsG5>Ras8uOhmlL(AM`v{x>EY|ZA`jIKlP(eFOwOH>d=poK0OzpBM_lJ-9t7Hw0=1w zqa-Do8K+0}=_X}6dmJ$stmSq|e^ zaz?#$g(~6#{rm?G%i7YvJ)<{2RDs_p=o5#7W86QShh%>~$|6y+~V!JFbY#777qmJ^TdE&P>8*q6O!LAsgy zM;iU_0zT$hMhbYsm=#G4b2&Bw<*jBLdzQNAvaaJphxkIdu@$_EJ%L7JWczg<0t+~9 zheP&+J~|rPq>wNf?=J&Bn$+2}Jdw^I&9NsatfG7n%o2iGb)9GGNZU-FXjW^}@gY5}6i)|#(TxRy z+(zb(yTKw3$YR_43j6-D!Qge|KCwYY@^A7Ze$ z38)_nKY*#ER>{U5QquWR#0?u#v(R7&sUauLMrUzGYcArYdiEEO&2VWdZ!cYNyt9|y#To$>zs_EYtwjHwiBKMVy#Mg0{90OtNjiXd|6?=P zJl@}GVJx?+l{}$M1{Uw%9j*J1_aBV#7gP{e(^B#NPZJ22W$fK==6)mIzrOrN!z9>2 zjYhW6BbiBa>f?;}2li%B{39z%&{%sum?mVVgBHcUmxso7WUot&qWH&Uq6PsawAbH* z_Ja!W@#P5kMdGCOVFC6%PvZ0_zU^f@+p6ErynTHT_NYQdXVI|nRned4PQ_rskS?&V z8?D*BCh-r%Get5gw|UY}ohF?+89ew$u@T!QJ54wacb-jhBeuH28pt-s-%hqKAjk?O z6)U+>kE30^HT5^!-6zd&)O?eOSI^t*56%dnENJ0IIkv}^UB?qKiqVwTQBqg?U(m80 zt8ir``~@wwT*O>*VGBiJdR3IOpk*i4*E2jLJIG?JjwB6dc=*&aJXd3#;>^NqnsFoW z2Y7NuXCZ&N9x|0gF+8C}AiXvJccq@;5zx62XTClZ#uhejY-i{+i+Xy7hrjs8QvN1N zGG$?Y=~F4kc_KjdxcC!F#K)ab+VCdSQ0l&Xu~H{m0MzlR)IB#zse|5l(76$p{n1k=Il#Xj^^`jP;vcEwZ&EjsPf({%N8Lq$8d2v?C=nlb zLTM+Cg;ICa6s0ajP@s;_iis1j`?jr62cXF4t$FR|0nq#~bRH*=;xIl3=@AsWDP;uJ zTQkzqC>`WR-2IxTQE~vN0riwd{^B2LmQ~)hRHh>Zkg! zPE}k9qOs9{?6{IF_SY}I6kM>oLU1cUCC`)o9JyB(sf0Zi`3k^Av)4{LZg{{2&29f|Nt z^U7IdGMJlY6PV)dP->J+rp@iSFv5!{Ga#^X#=@5;()1fVMn1Huxp~vY?I5bSl&Qs% zBICf$aafHi6dTY-_ts2=z5yg1AEm9Mh3Xf$FmuN|x6^T55E|~?N?q}zr0Y^OGhVyU zcB+|&^e~|Db0J6b(H}K9569(2416|cCbMs~630$5(YlhqcDORPdh&&=k*hiB`#K}0 z1CIF(Ed^fl4E8aDV%NojM=iVREqmk%A{%U4yJ1y|wdUr{izhn=oG^BADd}n&6m93y z4`XO*`@Go_HAMufC%45axFM#mM;trt5HoG^kUe)r^;Ef-+T=0eILb{HO?e{SToP@~ z&71#|;5|s^DrNRn>q$8*LJ9yaB>;$HIUN|8%PtWy0S_&HtZt3~G7uhkgRFCu+aVz9 zf(1F`WZsNGn`kmzC;ateP4qa{kAX+#?e8k7?J>-{g?)nAEcW^#rh%8*AY_nd;qNXD z3&U%)B#tqQ#XOe5APFN1?277GR<|;y69N?S89x(+JOjy?LJs}0_ z(oy+?F0W)~hz!^hgHe#o}Hm1=`Zhob=H;cPR#b;s zlW)@{x81goCnid#P0JJM=`n~p;US*9f*{)|GEy^|TtH6J?99Pp%t~72>|F{;v$qZl zt9S0z*sBEtJu(pM`711us>l9UoLm}O_l3! zk&b*IzrH0oJ#%!k;DO-$0U~D1sbo89q2;7KSF`bHI?9b^&V|Y}=l-&SJ;AA2YF3$N zX9$sUiou;!cfhFx2)sf%uUUwVKo09nfHL(KewEc5tYhJ8mW4l2;bSHfLjW^;di=RW zF*l)IoM*dWB{K|%OIbD&Wzmt;ntOwDSv*B&_?j zjsyRF94K`^-gZ}E+CwX*=s46s&A_N&s;$KSZn!>Ckrqz)!uQV#%R~Q#miLMuyS^9x zJMNo!PYr**TF4z+uGd+Ukv$$+$hI@5bK&PZ8pL+C*n*%t3+AK?X)7fyp5AO<_eIEp ziyj)xB8d@7-=obtFn+iO^?6^!Kd{nT1e4}KS$5^Vi2Wv4R+y(4D{C9Xl|%7(A1DpZ zZ+(*C6iUW;ey=D+8%wxyerp(fjN+G4qcDE?SyXoN6`eQeQWMF?FC*X!5FB;&OUPp> zo`-pHdKBM7%64`{znum4gbcqQ-=v>Ghfrcl%^l+9>t~5egLxUd=@dO_-k{M=uoxjv zzS}J#si_20b?k&?;e`UbV){A`m4Cl^D_TitofaO1cjyh3MP0f|urO&p35!d~(Ie>m zU6+Y|r`_+tNLDy1(eFZik>Q)M#qSdz{S<>Tq&-*p)!*vxq+A7J-zOf8O#43ZFLM37 zPdoz^gkgN27}xi7lj@AA5j(T3-&brS@vssgYU9@m|FlN6=w%6(!8e z6{0?#-ARLzVeao=VuM)<#YLjN5U#kF^%)GBq~Ia8?<%O2G6snmV%XG$I7+JET?AWB?2P93=w#3 zPC(~I{N_ndo#X(2x&cZZfANph@i!U1MAFmj(@}S4Yk-S<|Bi3o5+4`WK9pmj)J>kC z)U6~aP{*eRd~ao_Gv{K&!(#+8t{o?kf>{vPCaB(;Qayqfqbh+{6OC)1vgUoZCYUhy?C5l@V&brBh4eN(b%@Vo_; z6$=4dd|H<^@w4cN|K8>GO{3%>oW`d_YH+OqAVndszgDu1tzqM_dGr3Eu8zcirMZv) zE@u-&atz}z;1|>8_8&ySL1K9)AW-;LTKMv$JcJn8{_tTm$wmAZG4@WX0>ppSC{fH1 zGNG!m@!z&s{Flgev`|d}mrk(D#eZ3_cPll9#($Ys4JPqlO%klee_t1Je1Ft*4E~*l zr(+P%;1mtxzl4&h^n;oIabf)T&Y$Q6nGVd%Z_WpOCF8%AT~!1}&BQ0zm9S~;Muh<} zG|5H$_YfOCV}aSlrKGEAP`8~+uNU!AqB4FJ*%CDi;=kI#EKVbIJCYq>h?mQ- zc!lxbZ|W!~Et>K~`&yfhqpi7!|B8hVr9pW&`;sS)!x_J%oe9u=;+V2HuCzEZZAQg# zY{B4~Q5MHKi<3NoV>*VTPQ7V17he!?I0}rbe-z@#i39+d7yum`cnS8b*55E8Aj*(! znE^N-M*M#L4Lf3To@aMxs-5x}e}D zw@iO2-|*giNvsgv3hizho_0lKQ^Hk5MKa9q&?t z(_@zbuDJpAiGO_ze_ehB#RlSVC#_(RooGJzXKYm^DZzzZVOtw@_?f7pcTgdBTao0H^{#F-C z@nRPLoWRrFN3NT<}T zo(9uAzEW$~OEg8ynNS5RepBW5 z6@M1_%yB`7+avMkC2U;Dc9dq(p1GQ5rzn>iP2Cx4HqDVfw1N%SYL>l0;85<>WdK?9 z%1m2Zfph&L@Gj=&YJMWXZT$I7I*=&Kqs*~v%v={7W((UJD{!e^f~Q&XUG}!_zJi$3 zxGu4d1OFlp#N%I3LqR-m%xRd>$kKQoLljhGDRbBNO2_l0MqxbfFjR&Dq@oJmA9~}| z5JX~L1pNIAqWWtoo_BWH&ZhL+nbps^%Do@#H-6J|JT;&M;KKXL4#Dcr?6nfVnR+zeG@91K)m}^Nae)DavNjWUiy){+m0%4F>0X_^oezV*6)%zFF zxQ_zgE418`9ISAw6Tey16yQ=aelvmCYJ}s4W@jCi{Fep zI{+%@5;>5Oyizbr#&1%*)T?9nfT2wiL*x$a-Ss1ux`Kp zeGomM_{}Ld+v!S`>Ek!A?hhkhw5XqWi=r$)N56>vZ8dek^5*8KT9@!IZ!VQAs`8yr zpsr|(6s?cn=&`QKZLahBrryRRn)tNwnx4w7gU`~K3R6TwS;4UY~nU&Yeacq51PIQz}K{nRU}vN*|;;#~qqdk_eb ztwq>wgz-xNqhT-0P!gk9%%=q!0+3p)G_u^se3HOA5BIvpXD^JEeFsa z4S5E4J`B1EVm`NV_IQ4I0eE(jkNLE-My}=_?DKI_wF8cMoU5_E{zkSkk7cu1w#ag9 zB+w*ed4kTQO%q?|DowMwdGp6H*4q{sbuJ~nT?;MUxpc>X0M|ZmlBj7zD(-bg>A(us zZHO45@7h@3E+Uj-F44DA!L*sp)mNp0)&!+OTicU^ATt4$nxCgtFzPI5d7`UytSg-= z2*F5h-rOtO-VVk{HEOK15@i6$lmH+e^mL$OPCrQ`D>^2~>Si|}19`K@1iz$v>k2jq z!GxU8n>Y3i=NvCfF|Q(vC}d+O1R;!BM4<=s#!Ix$V8JiX!iP=?3&Rl15*E*3F#`bX zf?AglIRWMT|f7=lslwBY55^mLn+C(<))x{J?fpV+!joknZL8>*-LoK3`c0ZQj*&)ezq^Em&=A<1&Rj@99^LfTJUT&E zI(7azeBXnFVE8ykqv3P=*;u(gp7_SH^HmBOO+BzCx8{%w7z`p(D!wuCY@u!-@r_#r zxQ%aYfIR@hC;3hc`;Tu_9M0a>#W(&%?(Dj5{>wPfSA1f(9ooF2t@@3NgOh1bw`XFfjh z(l7gpPo!x19E5&K#3wE|OtaTYd}7&8{Zg958d)knk^Ht0Wy21+5!>AZ6&}VXy2W73 zo9aEeBtezKs;JN#?C?XByXJHu$@@&^X|_fg%pq9wqE>m7K{D*QkIXkoYpHV|8}XD2 zSIFL)?o&d?lb1$R0Eh#rD{S$(k5vt^xWSFLdCz(=kp`41KvA!gzxYR({LRvCNf(~` zm;&UI{Sn33HZp351 z@YG2T@L!#{!R%uLTuR0bj^bD-byKrS-AYRxpBgtfe>$miaf3}`1QwtB;5`*3;|6uW zh9e>!!IE7>9zuebG z9hMvM%$;^RN&LXgrR#TR{D9hVZ)nY!%kMzRv&jFy#Ls83)4?9`Mx6f-u>Jb~u1*>M zAC3{lC+51)c;loP$y|6DoWis{nKp~4u+-D5LcUzZBCYWqa>Nj6T+?DWSL0F|rYG`|w-efsch=QYERWv(zLkHB6h2WEE3tEj8L2 zFaHWPdXU5{-Bk%vkJS3B=qm1ZDw@-5Zr*$-+t!>L5Gd0imc1Rr*tIyzu|QD%NUUDO zYVXT=7X=0PFLGE~< zC@>*!1VK_7|EYBC8_sSwn^B^`U5r9Cd1ZznwM+HI-?RD(+G6mV)0 znq#nchBA_)JDU7Oh09vKH9Hds?H}0r+q>T&ZwvX2hGJHt6LC6VUe4hv{_nc)Xc!i} z$bYAcN`0O(U;kR@d`JGf)F|}d-y1J06cuxDzTg)c5{}*%^1v_Wo`m)H*OLFfPub4C z&~InnfB!Z%+DfcJ;eGNQ`%}jkoi8}ky7g=3zpI#4?sS2lIxP?>EG12NU|{rpYIFQS z!af&19x?fTEJEtM&tUx)u7~_npKbAeigF|0*=ekpcqG=XU2j4Y$SCCU;G^gpp$`f( z*F1>m3^dM26g%><@zv7ank&Dddz+1La=v=C_07iWweQV6!!y9OnLTrGo5`n!Ovj=8 z$9)qm0|45aIMSQ8oiM7&y*=^nw>fiM_h`~WNSGm)le?|+$(|C!tC(PBH0OX2MQVM4i2)%9;wZI02VRRIEvf6V21rV}8N&UXp2JDL;6cFwZVeXK zBHU_GU|s|700L`}RKu}e=HC1n+l|zji7*<*2RCoB4rYSHUvNMd$!I2o>u>yg?l)&( z9zdUG%+Z(x1GUfn{Ai)%UAMWdN5CuT73BipxHL=P3k@I4eN z0kjZ?IqfT?47VpC5mBO+%{qJcUu)ro^5gBdThH}Mae($)&9~OvetY^bg|QrB!RaJV zOr?R@Z*6GZzx{R{>@9%qMi-^*x6=uP`W5!ucieBRli!lxXqXfg71?hqVg3Bu?6)gm z-9+|VUQpxG@Dtc6nD5s3itIP3QE0z?36-H{3hcLi06GE@-Y@?=99>sGT2==vw1NNc zzj5(9D38*eo!oC{-hO*&TEF(&lY4<*#rE4b_tEUNV!xf%?57UZTao>C;}~Jz`{})C0vUx|-r_B)0oZRZ9uV1Y7yfiL?6-CRC}+Qo z{$?5b?RlCBgR$TK__*Z=MR^lhDoWXJYGYLX<1Ekb3Rz3l+Hm&Um$wP+wy+ZuKJ|fZ$?_ev*i;Ioe`w{P*KUNtWqm)?oiu<05+G{k;RDje|Mpd-2c7Of($`) z)0mlw0&}91z=%jGWOK6@?Pa%y^IIo3{c@<@2QENCDPa(nRR0h}KwsL_MRL}{^UwP2n&KxBbK=hppj{fXNzkIYizB4e(F5I{c+D84u-`{fMVMWeZTZ`A>T{i0v` zEM>pkF`9FDo&7RkrQb)z_s+d68;L{#{Vm(R$I(`gRy9yY(u% z&*TnBVgw>w-`-cZNY=x(w7%_IwzDbycIMZ&y|EGKCQZOk8=sKb*t44oal!o+S*jx} zduy`3`L+7>F9R4jm%`e$_zzZ7_rWZSf8d|r;97Agcixku(-tw>&)<}2^U7_$@AbRr zE9kp)pq!7~BGm8GrH!rO_4^*KPW9v#QKzngQ{tj>q1w7d!tf3|HQ}3eANG!!Qz;D+ zTy9h5%^FqNPQS_%DqGZ>L(F!IEmJ`zZ$982jRH6h(HO{LoP>A4g0_|GRbP0JbgzXS z>1F`#jOCn%oN35GICVN6{F4uAGa=u{<_`d8D|{cDsp{O{P%G9WfK&l9xZ$jj>3}VZ zr1`}j6cQLAlIb`k%MWs?v4a*&nuic2WD70`Kxq~p{44p5Z=!9#@%_W2Pu0rnWu~)t z=`c5T$PmJsV|(Yb`o4;jkVVO^zE7X{CIr3lcQC0i1qZYq zuJfk@Wm)29Z2f^~RNj7?RXB)WA_&$_nV$GRl$jR%%Ct2nDkkpgl5NeyhMf1AISe9Y zPVEvokXjw%(jCL9An|N|rhamFD90~HJu;t0EsZQ(-hw^k5$zB2dvcXeOQ%9 z+@>N+Z15ZuyL@uD`ofd3|6Jm;yW=vM<8dCJcytUKg3ky)LYWj?_!;32TxCVU8^@)f zZE#=YEy1)PtB8N6p87HIZ-TIg?3m0YJ>cI;yx@gN9PoU$zPeE<`(igzLVt;>2NW+@iL#@j? zWv*48*XZpN&bA_d|6Yn7)x`|f&yD!g+nD6hevOT%VtKk@1jW@>^jG0(JO7o5i}s-l zulp&{S0)HL`i_Kd{JdJLM23Nbiod7^Lg3(gW1K5r7&u_;xnSdG#-35&V0`Op1P;Cq z0ObM)9}*Ksw^v%wRo61~Q4}~RwkI!r%W{+=Yu~=en_V`BttPYO$(=X?fr%LtT5oT5 z^W;oNG{Y@Q!_^RdnT!B~22n-m!FCfEoIu zam4rSzASpqF;x{)-h6*U%lPQ)dxYo4xr!>~Xbma4Nn_j}F6+8EGS>6oe{!#`uB#m)%GE7gAaj8sE z0)QDM0EpR{L4kQ_7Xg5Sgi;Mtp1Ey6M!-s)iw+LJS{)IIqvPD3XcHX~T^4Xxos8w+ zyKB{vrjx+y80K4sorH+ z`-H81UGzm{;bW)|llCP66t*T5ht&lu##A3Qtld|&Km@|lO<^Q1Jphi7x*c7_1RC`q z#I2xFyOH!OXL)~P)$7o!(4ZM}WoqF5Mx;;4&Kz|zR^Z@%9PxoT+EQL~lkKI^jFc6t z|M$m6%Z3FYmpGxv#ft*grNECB;P!;xLhJ{#ERQl5;q3qYv92WNSosrr>-?+Oa=I?j z4+rA*v4$XByz?icxFli9=>5&2Ice`d^!d{EKH~>aw^d^2WZIVEN4Gf)Z$wBnR1<783bcXcjoQ;C(rKFzK0Go zAAX86y68MsC)CxN+xIkVU=Ln%l(z?)kb@N^g9rb(M#$>k{aL#VGX)cWtIGAAi7z?c zCVgm#HSslD#{d`eF5bhMz?$DE1D`jY@#=Y-eRi1bJ%iIy>KPD3qe?SD(hv^eQ*W~UNKXNIs+MyKR zGue?CBeux8xe+TS`nP=~2bwyI5`ABbzxc=c{7pSx#V0Hbed<{B*8FLQ02lwh7V&W> z%`PUk>Lktkq*9k8C{V|zQg_&&lscRP*l%)oIl#Xjohfzv z#Xr{PZ&EjsPf({%N8S1{>Zmt(PcNx+_w?RDY?Zo~w^r&>1O@8&)c3Wn=ekJa6dAoW z$L$^xIEe4hP5*+@C>`WR?0TjjLCFCX4d_y7;mk<_5<~gaxb?N)izlEN)xBt4tH?BPDaIZfZ zCM7N1d|H3{DgcX|lg+T*;%j(+`Ycx`@~1-(6;Su!GT+CE+KP3F+qFV!oQ{3 zRe!n*vlx(cOqCzrpJt}>r`a&{riF9XjFLU&waDb-CtwTP`AE%vb(>A z*}*sKHs7V_{+d^H7EK3cW;m^j=>8h!QISwK%*GT9(A7kU7-xAR-E7m^lZp`{5R#iW z+k?;I(h7{`RW>EPy#oCl-Cq;7&zlC+EO-x4D!4!m+ie05Sqr>P(VO>=X;u{Sc+R`5f9rbIyI{j&*`4%wteu zrpl5!GlrSUykJJp(rhl6#U{2CE@q*DE$C-K#+pJ>yqv|4vzXqPd##RL@wGbfSwSs< zko8i#6Gs7sT1jpBCE*D6=?H1KO9@V=(yG6w6uVh^Rt=`TD2J;hALdb}=$U z#)ODE77i;Uf?bgaLgtVW1c{K^PyXE!A*pZ5SrWPmt!Xn{bTQeGkv6jCId-9QcprKs^2g zRTw;%S|D@Q?a=3DIcTOgM<`AvVEx?KkrAH0DIFIC53d99}8Yix-Yu&VrUa za(r@O%d<$bBUfQz3-RISsd{U6{SBIIje;hN#Px@6c6fdxJ_I@HSuUk*z@ zzQ?q^y72_Av9GY3OeH%GV_yV<_bx|jNI^j7M*RIaA5xGUENS$AG^D^^{3Gi8%>^&b zC)`ebD(by8X3GE?=-SH`*24RYXwu9@rL2qjZoP@udbo zf|3J3Gf_`zJ*&_E`i zO6CLWDVcd>^w#`n+W@Y_zT#wdp&iL&Z99|%)mw9Ojgl!HkHlHY+{}{MPG}&LPbKrOLzT=5#oCrkg6ge#aC0S7I>?Rq?lGQB$pN4_sHbG|7yk&Bze#2b zpCD78j?8*MjmS*co2Q7<+a!}%^wxYxoIQ$ALtTW$^DnfYvGIsxptpc7Vtz7 zm$pAZXT?HtJ{|`F1&Wo6_V@ZXD>)b=K5f`!!=+(t;3J{}GNp!($Gu@(9r+%Z7SfF& zC+Y|cE-%rhQf1m)y;1bBC^}5n0Rk(hEe7&LI%Csee>edlxp^}SAsz9pl4fL$T_Ut5 z$CaH)s`X*vAhyEx-tog;92v$g{V|oZ@Tvzk$K6mln##0%ZDiE)EZ1^!32wk`wC993 zp;xh@`pC(nSg*P^&k{Dd?qsn-xKXL>4pc&$lsu{CG1_(1&qol|;UyupkZOuS+IsH(#=t;@P8P&t}i@$&;RxR4bQxLnOPwwFe; zF+(FLn1hCqGw7@WR=3U|E693}r&&@#7NegMrwK$j0I`b=aNo~IB*=QV*MVE?jk>D1 zH@2|7v4Zbyg7jAYG{L(2ewB_DXkFxc;_vCHO_o zhcyN+USd~@Ija(uSqsxuPyt{S3K!UC+2g21_nEXQv`e{DO$b=1rNb*kTelS<8R}zv z1e9)R?mWCwKy^BYBDLanYNHkwKyRgpTF(dt-P% z6tb5c(2IW;XUtrHa%xr)R>I8F|6J9JRU?f%Z_>S!b}NLdw#lLVTK7~jRLnD2crErz zuVbj)u>e?zC>{@J)?#GZ{2UG1nyY%NCns1P^kzt}|Di0+Si&P(x1QJpl6w6MLPAdjX@26W1ZF=3Ld4U1cA=glOX0_a@YYTJr5pj|D~K}SrRKO@Wm zLoK~IUX~s^DjOn}DyYx2`L(S-QtH>o*oZ#F;&o#n=I-QJ2MvV{((J@FV8R>{>!Go* zLB=)^>X|Ux$9iZgY>*8bFk$`%zkoC=4b6Cs4V1rAebm@d4fu`C6a2vN-UR}Joa&1% z!M}wh)rTZ8VLlC#R2mzkEE2Cqrh`#u+PsZ;2c(g8tu%%L)+8Y8L}J1`g!&7uLi=JN zHqExWj0h8^GpK%SVfB`{dz{K8eDfvwvSDvvP(M zB1Xd}0L+MM~m61SLz04h%aSR!fMrYcXog#m!(zM+b^ zv6bbC%FCGsC)(L7(lhwXVdr0@SYL28tK_Jno|k7&E7=H@E3ky|(m?_t(M+@POd$cQ z9`emNo^S#Om}$Nbtt}2+6H~`a3Q<%#HT>)O1i>>UYmfbjEHCZQ{999b;@~^0Rk!iw z+zdc;A;muh(S=_Yg!}{uXB4~y?Zf8GuWriI!;d+FZ#75K?WBH*e9R3Aqj8(*+HgHVtgp)|KR76F>Q^JFJ_|rRVbn z<~{Kb$7Cgn_@`sSlpPcK>Bzy59>8EVU&ug}>b4hH(Uk&)j733P;hH(3@XC7DhPm)Y zTfGI@s+F?RKpB7>*l!pM7Ec)yTgmIC9$xbh2nYZpZ6*USsuM;DRofsA!dCP=l zSo~C-R$|&ibU!!eXdpvZRW#$>HiLvrwX2pp(m?nZ9ZjCj3BTLG&PGVQ&V% zy)1k{N`RJ>LKf;J9Sj-L=IJ4kO$5~|qZu`6C}_g8xz08jDcDOm2E9eV4+CDLRr$PD(ezf(ZTVF2dn>y}MkOkw(iV zdAi|yiMq%s?@QQA&*#3ndlw;e2~dl`&p!YV)MKVONwIPL+A1U7S=Fltx3*H4_M`}%RtY@v z^zVOFb}+{r4&H$0%wI2#_gNch_6i0=i7p4qe4h1}KK~EZ78z!p@s=I7W}+^j#=K{< zlD^j+-qw+97gRx)lvo~aN|lW+Sl!HDiN<~;>%w|3&}2eAW}0hQkNnhrEvWq=G4}A{ zc)`c!f10e*P_bY=0j!Dkqkf0P9cY7U|3qUiIYMWx#JARJ4@DDC%@Rd-PBw}&<4+ixMZ zK-u<1ro5%1&TJx3uwQ}20?X$3QEAh$jDV447drYj;O(P$klxBB+U7Tukl4Zz8(iAzHguL)2qH)Ze(IhQxWq)P&+_v9 zI7Ex%gK+>8t$hCpewJhoRKCMeFX|kmt!a4~c}^3_26Na)UgiHCO)0Hy^0Z5`M;1X9O^9*8j5wC=*)n$(nI*lBeCorq+ym)z=u% zgf8wcNkD1yhzy@hBW&NotL)_9mtNEY5i631)@4g5MY74d6MhAuqU9o9h!FD$*Q!g> zdhpBa$NrI-XC`LNuWcdyZu237l_=~+f>He0c(zX-b)QZ0iR2tSB(lRN#xKd!K)*tQ zF10qDW-Dpv#H?B&bRyo2TIO-^Qry7tqsAI)F0NT)2?;}Q>)gq(1aj&*_Vv^WDLPAy zW;)hRu*_Eb5kT1*-5Fppo$$Y_dMgSwNw2dB(N&p9PgLOD1lJpWm~HyNE0SVIo2ZOh zHg;IQY#2_xna+QZ0QzN1&|hrc21E9-{v6w0Dt_pVj%ipNs3^C6nNM$gh@e4RqGSgc zM*|Z*p(E4>IB!LnmARbBbPUhzRdv>3nOx zT$4>(5N-~_U=dIQCav_|#$E6DUjR$p`{O9b^Hg{Xg5VvaH;9ZrIJ zLx?)&E4BP9+_~};rok&Yxsf+tW01`iAUZA*nrV9&g4u0SVIf5q1ouVX(iG1aK!j4j z+M@lL7iBycqkj~yp_^AF(6l!1pDZDQgUTu)e+?>s2~gqX4`vu82mfGPIi2`DrDVk< zoSaeiFTI{b+bY;gkkwQhz@HM0>c&YSK;~FSY$g%1yg8AY5xFRWFw2=!lIA191}G zoWz}8A$H)dmDRV_7w(^)5wDe`=Tl<71Y<3jZxG~0^!yDgrqJF8J*$R*p3C7yuO>aA zgP*3f)SH7aO;xIq%$j*(#HyGAS!P}Lg4PfR6EY@a1^F{a(T?xK_#4dpvhUW`n!Uvg z6vel(xT4s5gdCY07b_N7@(89Hwof52Z*q;r6cZB#ML29iq?^p2pF@Gvglu7kU}wzw zZ)^)+#pZrC$d$2>y45b%c_%6HOcX22j*M#OZ@^k%M;R}4hjEbp25W0=&#?IVnj)cT z^Rjr=-E%qF%zErqR20fG=8&aC0!E5;Q`$;~0ldyE3IDth{*By$iQsP_M6Mb9Z9@3_ z$KmfMM(Ucu@06v?^5=EC=z}5T&%506SwsHlQfcA0#o-?;DtpcFUo2y2;eRF$|0!!( zuF3c|hwvY@mWAcd6j8COi9co|4Um(`eo%YtEwMNZ-uS+LxRjqm03eeI@s|a8L%^|XOA1%PBWOWmAP!%^mM>O z7&6d|bPlyw8tc-*MPk#6r86Wf&f4;mqHFf8kUrx=!X<{))s1?Ea!=l}QtjG7-w zq-t#&HNl9*^&`0I{hL~v7;Oda4dRIfM@!n4rAcgqU#GHWUyKpLD+q+(AG{^jWIbsg z8~!vZ=*BjR`GxV1wYl^ ziv+#@bP4-oi|18dL`btT4gdPvK>Cmc>lU0WGCz*BkxABIjI)WNl@)iZ52GZYAwHe} z!=XV>f(opBI7LW_v^Sb#D3M5t4nba(`Cp4>n*BDIvtQB*BJQYlsCCZG{r_jZ{Mzw| z7V2C`Xq)NY5@a0FDm(y(x2xn}0*Ofrqh;_aB~BmEWVE*weU?bjJ#Hid2@|&>cJ<~| zQUBe-R&(Q`RlPkaE%+u2V(5}Flw4G>lOk(w!>-1l1gmJiv@T=RNS%q%_~o4mjiXU7 zRdA30qsWHExfBKmfRAV#b+RH3S0#^(N&K$DAj`>W-!y?b= zd@-c$5ugBv^@J}lpX;9>hB#Vw*Q-+Bu2LX;cM1EIEg7?>CZ~S(GTTEYN!5A=|`dT zpEaY{KlCRA#P3BPBD?K2nK5U=NbKyn5nL&?FI`d?kXgW=Z}FG{v?K$@TVQSlt3xN} zCgwVy<`y~F=a+R6QHUuptb<{hMitbRV$B6%)D|eO=dXHaDo?c)>!qm_snMJ_JKLB* zgYWbjQI}(>Q=FQ%CS9X>9s;5!-N+CD^rn_heb2Vchl&)$q(4ClHDqb4UwS;_tvOL@ z57@U2md_oD1}ko&PMj`HSE_M*;$NYLj$^=0f?&5(qF%(pXj4^YR zzut!5s$We}4*qR61mxjg8Du2bVLLL4Iy?cHeX6E%{cS+m65=d9?>`=8NPmAuMp1v4 zQ5E#1h)VV+TLZYmk`k^;C1t)Q!8Omhm--{bid7l-7^u9#OS2LDDl8I#iv4=NAH)X$ z|3LxSt!viX80rB2E0`O7GRDKV@!-?~2+$)RSb#&zY)WB` zAB6Gp@BMe-Z!lgq?aSQ(!aB29bd&f{(ELNf@h(Iwj*pB(otZA}AyAZoytO)|Gn)sDMViG=YEF zTB^{gF^6L(sFQ0jQ%nDs&|T~M==izBBn}+Lv_6xwI~)f|0Kxff)-TIY3~5=}ll>hG ziNpJtL%J|nG#X&b6dW-5^b)P5zL9hJILU#SG)2!iGBRTPU)K{ZbO|_nwQ7!WyzwIc z&ZEJ*@~B~!J*Y@GWTeh2G3ImDz(E$62&3a~a}%h%;wH`<)WjXi0-Yg+=f(F`17*Qv zvCoE#2LKvstf_{Jb=8WUrR9SF#sdhWy2bH zxB>sl=oeXkJ^%jG>X#pHD4Aw4-_027Nnm(6NUHR$BXdgRSf(V(#NsR~ino%^bXXt*W(ly*38^yh`RaTC=$$V=X>TU-q2Zg$2n;}g8reUR@!SUfG_?bG!piNWiIKS zuuL5g)u1uA4$QPn$8xK&0J^TcGAV!`;NL|2FQQfKz-P@D=@=s!;{OQ*3kHmTA^gw9 z;lE;aaR~ojjCFbVftD$?Uhn=42fNPv9?F0~l7&;b@Ae5*GW73;jk3SF!Gq8#_sNh~ z#;ov4HGmJ%SO<^8F&O@h#rz!^XJT5HlB)Pp0-eEw5>W72a}yU3`L)ujdh9K6slJ8Q zHRQKWmk&nL#RvsP)(EZT^1-=@`VHo-e6O{QhV~5Y!O~z$vgU90EJoI^;k&(vMdK-1 zOSoS*AZyu+iU0ARrvLx{$G3$2z7UIrcl;;+h6#0Xiqu2+zJ1B{yWDx%GWHI9stn{7 zX0ChbfCkSj>_c&4G@EI&RY0M??ZP(S`D~e-eJ>f3Io52xqPG_>-T;taNS@~V!K$li zN>R*7m$izHQ}ty99`Jq10fd3I*)FuJ9OQ_#RvUkEe3F=!B#?2$fEXd^k<;4_3Dmyd(I9>pMI zq@$BeeFRUXh;g>@m*2f6smxo zeGpvO{&-o6v^^~v8IFMhD;f{b#;FD+TRR@hgv>m7eR4YzU29TUOh6|0xF)*kfVf;4 z-=lcE`-6WZmIVucLnq{Y!!I#jwi95P5yeZ3?f_YDfVaMRAo`@r{DsR5h?Uh08U|4# zw8GM-XX^Uynbh?q&rSDRTA*}pAE0y)o{gsiC+fPk`uZ8)$_iDi_ArF?57faBTGez)=&)>IyJ-`tvuWxQ05QtLv6|QgJnKeLUME+JQ z{^MVj;#3dbU|cTIOE4&Hw%14_r%E3a+{fCiR1fOvh;cg!ueHO~q0GitrKD0M*ekT#YtRj}20%Br=i$s$fyl z)Vm=s&!RYd@W;=W3m?^)jj2*0b3HiLR_sS8b%pvSP<7R_hqsHBnIa`pj=&RtX(f?dNXfe3DxquPLaq7by-}q;Rsu&s{29oP zEIoWjZQ|!G-W9@kw`nUQqi0x>=01^$J_OTtv%2t)xr;=7eDH6O*|i$*>nbMh5Mz%d z^Z-hXLmSB0_1A;v4@RHsO&$?O(p^ph3+rcX^se9fWgJ>!-<08~PpVuhJ05Ux>zpCe zgf)4jOgZ%p^X37e3U$(9odlH+1V3-CaT2RdsowZ%T5ZL|J=j&PRWr5bAsYhDC5^3N^@Zi1HOg)tC%@JerGjmwK2#JHb5GPYlGP*GGv(C zowT_*e9X_8VD>gP8)|m6X=D?G$w&KH4GBoFVy*d=I#}GwIO;JFUZ__X#Z zPj8?2*V9&49ZN*hHw5^v)QYXS1mZ`>F#>jdU<|WNeK&-sxC<$*u1P>y727~;*1Fk;EJhC7Yo#o9}3o?-hrMKIHnW8_-^Z)ug71$v8&jBODyuGK%$lh~lXYaYRv-cQsO%Mb+ zei%Qx&FAe%utaLJ1V6fsq2ljXMnazZ}6OZPd{()OhAQBBAsO)^%6+hE0d6uNp(1Sq9>EY z#NY-mh+f^j7SbK37X*4?*%fASbS5h4drkrcITMu}h!hpTr>mF=JgptL;R)NNtqW#O zDVZ$PM@ywa-gI1ph9xGxuWyI$Y0dylA&3M%n^7@|!!_&4Xt)}ehr@L#NW*Y#i3)bO z7R~BsxE@7?Xt=<@aa9=(7d!fK&{6N(VMjmHj_!QIcXWJz9UU2TlupPbSfi15^E)12 zIp~TwUkmx!I-{7M4f89#_>YW{IYY@@__!zYyu140=b5Nbj-Pu59o_X-$j_f^N5@1R zeWJgPHVirfKRMAUiT0+2?3zt0uTsWB_6qiI%dYf1j)--=*0jP2AVLl1ao9SXE~4@q z%|+BaQe|_oCCBnzqm^nYzB3oA{K2v)a=&PR#%_st4tODs1~hEP@V%;jIH;RX_db2uWG%Jt07xcbz4{EOsdS`%R>LMobD;VEdEWn`fX=pq+i@+#T3?vl7G;c02K)xf+zcY|9*u zQipLW;nmM?@?x@;#+b;qFaH{ByM>6f&YuC8t0g90zZCxP`Z~pFl@51o)1uxW@}Bsl zIg08WgPpdo*!F7`4if(Mv|v9*_$NcPtTuca(KVdq&csfed#z2K%1A+a`=>3|Dh+u`G8Tx8_#GL zd80@UrjO;SDX|9ePjD7c_Z#7>aGBhzYSKO|M! zx*Lh2j)+fBqi>3eAQpL(J{{#L&ek@}n&wVf=`6ba^`ZL^S@SvWaFt^v}YUc1X|;j;TL zOUM0bnLd;n$lWkGpo_%D-2BtY!n_LmrZ4JBlC!c_r!3P<=oL3ro^c|VGD`URlx3#C z#YFFM`w*NGb}TnzEr4MbdY@9&vuQyNXRhl__a#xCRiO{sO23&jFqW3kUl@D4vb9|0 zw_?+zw9XtT2Af7rN^bdN^!x%K0ONEb;+@T)JSTcyWf!{O7oZty?^s_g)yXbt{aV@9 zh4C2pgQeaYnTM~~z*rtXYE*B{>sR6f;J~Jd;Ax(s3z@da(CG)#g<$Nw$vi}WgRRsxqQ6 z{2YWzVI8PC(OF`1IF&Le1-7T{2xCHMk)8u0%oDYg=yABYEQ^mSPgl4$R9I{PpkuyCGOFp2E*7h7E%81wdT&z&H|A2702D;1{^S z7}PJ#ZSJ;y1$3F_sTcy}MBja7rr42jRnu%@NNm>R6ck2>OXMmz9 zheLufcnS&0xjS@)~1)iG=5uFbMc(5@rhd&{ldB;^PM* zxPLId0}a4hG-vqeD@@{7Dr5H#UOgeY0qlhr!?@k6(tBUkPXKi{ROlpc zveKK|KaiWG9}GG=J=W1P+R@jdj-G+?ZtLqN>BmL8x2;{%D$YMJovZe)IWjD7s#R}= z3wO!-9k2yi|J6(Jj1N@F`nO-K_3!GhJ_eAp1fc&)l%jbfVkyge^lo`Akq&V5`oX^i6*@Bzl58L(8S@^d;-E>0>yH#?6lQkbd}WHA*EYw_u1Q* zcE^mFK?cNRvR+RBWCUm0$AVfWgRKTOHdoLPL3snUM%wHt8*U6}?VX?W4b~z=^L<;Z zQEP2#Yc;Z#TrUgo;5CfQ)PzrxJ1PFIn;v4PC# zS=g(#bK*;v_9Ig}p+YD7vsa6pC8H{rRmv)1H@SKctE3Ewpa7kOs>ttJt1wGCJ4xpm z?aGZNfnzPWW;R8RebFszI%GRROgsr2Rw-9LM7G_QNf9E&+$@H3j^_3HQe3^~`}wer zIwSsytz>V%T7!M2BEelt8KvdXe)J+0zwbfcXoQrW0Qw=NmbE+EwG$#_-+Ao3kh2h% z7div+^qVWyxPX|<{#_^`V3w>&y;7MXpJi zAqd*LJ94za-fd<*cX6enjvJm6zU>bt+d+0)XO@_1tRxuc6Lg$U$OyDg$-au?z>^RK z!j|nbx1T-qN;oh-670C2-4t^p*V5<}gNf_2Bu61-PT`>l(7>}cq4A04)_$7f?r|!_ z(%V-Me5#Q%#J$OzW1O93mRb#hrI7A9*i&o1_ycx`C{4dg*u%G|ZaWn%11x~{2e_rpO z7bvXw0EG3?e^E@%($6BUK83^M#xGZS@6#Jp6qPPge>y#vpTafYo&?^?Vg^GDC$bTd z;9!}vG&|KZJo;P;I)t|_W;6e3E9WIy{Bo6_c&23kzI^}E_}TK-WeM(_NVM=g%-vxB zJSkf$l{enIK}BZw`Mg$$-(aS$VM%YzZr94#O_n4A2N&N?!KljQ+_k%t<=R5}15@Rf z`90mM|Hs~yz(-MJ{Rw0s0x?k;jjqRtLBk;qhZ?z@U|<_ebmR=m5rjY@A;e??5mAF7 zN^HlC%DQ+i>#^&qpR(#I#AAc-K}1(0x{AkYbal5euHvx>c+B^Iud2IelAi7vkG;ML zKVEfJy?XWP)vH&ps;g^2XBWR8ks;#k6h|?ILd4e~6vgOC8(J-jX_Sr8&LPk+t8uX= z3K4HZD2maOHsDfG3@cME#v#yL;JyGXQLS`Q4wF&W%HOcYunl`=G(V7Bcc6cVG; zZ8YozlaSe}s~C~&)Ga}_+Lf&CpkK<$Oc%)%=Oc>g+BhiqWEHobs_bg7cz*112x`?w zVuEp}|IW}#ax)awv*kMQ8sfILUxP>VbRFl&5OFb0$Pf(rAo*Ru5xmOlt!V6#dbA=V z9|$c>#QgmmwA@m zpxX}m|?cK;luH|^xb1Lx;#bMl4n~5&Y z|FX~fl9bfL?2!xpPZV6yaiGPw!%F;uc9Se`laA=muEv7<8|OFqR=gbU6nb4W4y;8? z0Djq9Iohs^rJdp=wT?|8+QeLk_)W5CLfA97j^xH^kSleRb}*f5KpO?UO~Mo!w=4mb z9azc@d;ZQ_3J@MeRo2;;+V#R}BkAx<@8+v5Dx`BQ_iWgcg7HTZV@gshv}#NmY+AWi zVX44z8n1641N@hmRt(+70N zOg#%yC7v4B5I&Q>S1==f=h2chaiN!I(k)y>w-thCISD{wnJh8tB#5>hs9{eN_D%9O z^}#cXeXaOmOYlOtVb4rt>n%}2o5W0M^tTj>jv)H9agD$dN}|=0Gio4$SX~& z)PSEPe3-2*tIS$XgUXRhb?MdI>oh)Yy%pnQIc?Cc+Da#uxdyE=No$mWMzFj3?y^W_g^Dm0DOYBR{C!;^vqSoH}aS8V^) zYA4h5Ef!25xfRn@Q^)U>5btrUq}8w~B3>Nt6E8rAQ#&4_T^N;?Gew=+p<86O|2&>) zj8JQQn{J704Guzb$Gdm_)|fhJD2lI4#nC<*Jlus3UMPL!)Jykb~GC7EmUnR<}&89f%&N^Ug6JW6N_^w&3;)EhlneP zn-dPjs156x+ca4wAj@!wGDRF;D?;dFZrJnG^=uCHlH2qsF%kCk6g)9zJ+d|(gdPif zGKs!iT8BMXQpVAo@i#o085dJVZ_ao%D#K4P)L*I{S1zp}OT;wX#oQigc_|Y1RIi{y za-5SD(WYOA!rND8(;*-F>&QgUPz1U!9+deqM7)$qQl*p0Ux!+7%Ba?i`V`92QAN8- zwP$Is0wUc7f{MC7Lj5sGwoxA;llXu<^wt3z8CotJN2xwkI_z0W9vDro!o2Gf!!B%C zP$~_7y{fQhB#_xuGBTP(vVo;!Ux})hbI&YH;ib*_*&RWa4q+!tTC&-ZL>`KJ1?Y^8Q3rVDXu`Q7Z37Y=l3@Y7a z1JKNe(}jy;vlKNb?`fu=-N46N(KhSH#<$oisiT>d-P$ZX8E&oIrBL0^{!%)E_^t(} zz7j~lhZ`OCoC9Z|iFQBMQ<`e-=jUkCS3sWd6LJ()Pzrloz($KJ#ZG;tq%n{j3XtGE z5q)p#=QqO8zsp^My)C%RWayv3@)Cx=4s}_sRU^73=3}S!vnRZfm`8&Y?Qc{gW;No4 zW;OD&lu&+lwp=%Q2`!)Z9cV$A$AxxyMM=JG;$XzS@ zN8HFB_B@1IAV_L=wD!dmTih9@{)(d!=yKHH(!p8&45R z*+Lm;FNInK?Vr(3oTAEi8zZz3Sp)Q$esPDsIfS={^g0nMFp@a>s7Om?x3~UD-hXgq ztiGRV%Y7rgjw9xgymc!2?jRGQm)yFHMGt7**f}x!fH-X`@<2F9M+T3ZF}ZYrO_V=#Ts-de&4_!~Ri`c58+4c-Uq1$ez-I>oI`V)7JK?tt89u zJYeGPd4XtgJ|jTFF4i8o5X^92AzG^4Eu*jx+<#Dc`Gb0!_jMyYSaZt26BLM6zj*S~ z6QbC@mB>g`h<>HE6JwXe%7>NE%bA3htG!DysE_w^)h?~I)=%0mN2Q8(&Lw)IbY}T{ zR)CZY%+ff17afL9p>qoqSGu>-uLjmTzIn5jXXk#59&cP_k@izV)S_DaOzk_E(MWL6QYd|>`k2qx?Ayfa2W(y;+~ zUHdmm*?xeCg}w4oJCseSs9*c5d?kx9LKFYTbBOPPY_IC#pQZ6RG8mz+jQbtqc-2C$ zENO@?B+=3swRCWe@n_(#Oc4KeIBv1&cafJEX_1#WgG<~Zdq zRnjB#Ny5~)?fx|M0MtW7G9x2dW~AQ2$&b*BuJVpNFV5ZS(|Rq9+2m;ZD)ARykQn~^ zvtr`MCr^@KnxzE0l$XC(1)GhTk{q6514X5Vp7GB#dWuw*GAd6um{0o&KZ)0O)X_0VmC=6~3XF(vocI%2^i`VH#)=-ZFnH=zUcZ+SrRggWq6ji$gkNqo zvW@caD(OuOy15h_(2*bHwo4s5P2bvQzGDG>M&b5SOb9i@BRKAkamx&I>VXJYMX z1-sGX*AnNV)1kU%oEhA|r;=2rtmW zS?Il$azZ{9_E-=ldVbtO2IA;vBF<|B^-%SoK|3PrIG>D(Z5@&Q5wkwj!&P?A;}A{Z z`nvautn1^&PIX0DNiW#KEj!&{uZm#Zt~{4U3e*^*cwh5++KwkNX=l!eypM;!T=Xer z{(X#5--2;YT3^2#4{h29)6Kb2OY6YhtwiD^kQSARD}7AjSFvq~TRxGpqUY%Lh{~GJ ziB;K=j+I4^Z*(O#jIL`#;N)~{*SaXJ9?;%=lX{%OBjd8%I`l$^76Cp8fDxUpm zof{L;e;di)R2H}Nlm(}f8{0P}y2OuD6PlzKEPcCI@WDgr%CMUBJ^Gs|bxwG8Zm6Bk?gKMguzBd94Wp zMiksm^QViRb0dDS8P@`d^hC8MQj^59@xAFIEh#Ro%-8jM)5!6HX3?I{X+^|et9B;* z^iG-*p^ujP0~YoarmD;LQMdofoXQ&Twx0%2*0X+ zYp1s};ro$=fFc_+hH7{1DI z7sEFgzQypb4A(K7%rKwfX$*519>wsBxx~ZU4BHq!!|+jt3mN(t4r4fi;b{zW8IELF z&eX>8v!CHOhWQN7VOYdC7cwm16oFsL&*L~f$Z*?I;%Xzq`xxHF@H&Qd467I}U^tzj z!f+(R!3>XOn8~mk!!Ig`@?RP5Vz`6hlMEkZxSrt+3|BK;#;}y(`3wsfj%Da$*pFdf zhCLYW!y+u)|3ijvF?@kx3&V#Q-p%kPhHDtEU|7y@4#NP$aSTT=Jdt4*!(I%(hF2Hv z|1raMhC3Nf(2N>SLu!-T741)|8GAv>^iQ#C5Ll}A)9?CG4;ogg=E{)+E44-HCG{X%H?_{`^ z;Z+Q48D7M27Q=HG`WX&mn9cBThG`5Blu=zDF?^e08^dQ9KFaXd4A(KdhT%$v6%30R zPG*?T@HB=w43A>ihvBzaG==-`VfY@yR~T+%xRK#~3~yt29m6_?RSXv}oX${TIFjLD zhQ~6@WY~@27g*Ln{~7LLxP#%73?F2;p5YA)S2JA3u$1BX3=0^JW$0trk6~YiJs9p= zNOgV4@GXWfFl=G?FvGhU-o$VX!xaq68O~uCU^tGUu#-dhIc+x4@5QhW!-E-SGCY)F zUxtS>%wl*H!=o7<%h1cPAH!^hCo;@oIFR9BhC>+o7!G4Ng5hZla~Y0gIGW*DhJJ?Q z80Isaz))d0iD3c5a~K8~PG&fj;dF*Y49{mci{TuG#SAZGxPak8hNTQIVpz_wf?*}Y zDuzLZ%NW)&T)}W9!^;`gF}#xDYKB)aT*L4hhSxH@j$sqS8yK!-coV~Q3~yoh3d2_! z?qc``!?zf|&9I%}dko)a_*aG+!w(sL#PDN=dl-Jk@C$}}8SZ0vfZ^8+zh&r}L;BN= zVJgEO4AU6)V%Ue_!3;AQ9?GyU!^0V7F+7Ul(F_mf(e35uehjl2p2#qV;XsCi84h9S zV>pcA2!^LI%w;%|;b?|q8TuKHW0=oy0z-x2B!&eH&tVu~IGN#8hSM1qF+88)EQWI! z7Bjq%;R1#W8J04L`$M8yqs~KL!a1Fz27+%Zp zI)+UQZ(z8V;Y|$JF}#K0Z4B>VxSrvi4DV+6YlinSypQ1n3?F2;f#JgpA7%I$!;K7^ z89vGIX@)HfpJBL_;Wmb?40kYmp5Y4&+ZgU-_zJ^U8SY~E2E(@)zRj?m;d>0G5 zXNZF#Ec-HYc4g$e!~()|E44?_&Zp%#$}2YO!07trq7J1Y=1f<{kdzf?)bRaUywayc ze`>~%ywnx_YLLSF8Uhy#56Meg(Yq$0+r^{%%ltj@?^!=>hII7Yo|=tUKzQGSyUIUANpXAswMI0qP&9a1mZLM5ZZF^`eP9hzvZ8rlLb^ zl;@b4mdX=f{Ww?=!4g-GZH0&=r`mtXJ4zO+8%v%T2Sr>fC`)&a&v=SzY^gmR%B`f&gZ8_Z_1AQBe$Fc+H{N25&Pu_$ni+9*LBICw6%0EKRZ(Akv1r<^1J1X8EwI5|1!l9!|!Av59nVI_aZ)e8p zSEE1K3B*@0>E~Z$G#w`;T9~ch52f;v^RliIru^{fUHwNn)b&{MQ87A{RKFPyZQ9zA zddDG^-s8*T&wj#EFo)#zF{UjujXef5LprkfE~s{}61sRW-;yZ4wiL+77>$h45gEh- z<|Dxa9@?}o@(gzp+9h-F(5AhC9BN(85B>3+E~I~A*!5FbpO%8&7lRrD;?PrLsRDCp z_NVptuU-6a`RNQkKWTv((XKcNbwJRgW*TFZhN=SaQ^Zdyq z7bJ@BJmoihgb_icj#@RF$Ei3%xBC)0_>Ee{*owhd5n&YRd}UnqzaWFQ6=S;)5a+?! zb=7$)?dV7Nr4Ugc;<1#ZN8c5R1o~&jz|5pDI&daZq-?2v=zptbZzACl529gG}Y9IO#f-NsJIfwgF=!*Fjbgp*MXkw4A zF81mz^b)hRvQ4$rrdru1FIAAM{jwhwdQL>1$rZKWZYIk63k$?{;Zx zF*Ay*k&3_1aw1}V!ek=nLz+21A0lf1@xEV@=R;Ww0wmqY?`J0J^FQaX1_ZR9Kw5OL z5aKyrTR2WcXf$e%>Bc>P>C7LGT#WYGet$73pDLmAtG_(nsC$I=Ax)`H-(P%vy1}4V zn}kS{=*4mK(2FmOMP_{hXObiI8SVyzMqfAG0{4W@X9sF<))zs?#grb@L% zx0NuE#H~-8Nn@Yb^;tS&ARaBB0O1x(5s)0@6wDJc#dr!vV?|!%dGp7PL(4+)ym>!4 zBw0yWa{eyo%@p# zcrA}s>pp+6_8N4G+|fWh7s3{{D%IZY#Pe zF427tQHA^8cnTdQXc$WHXZ(CULy`V-e*Oi+n;G89@OFm3WcVwFcQL$&;cpoJmf`&j z|A*m241dS)5r)5K_&CE&44+{56vIC-+{|zb!~bQto#C?#pJVt(hA%RFiQ&r(|HSY$ zhOaYxli{BkzQgcchJRuB0mHvB+|96q;olj4!thgupELXi!!H@`XZRJvZy1Idruay< z-5I(W_GEYv!xIKjZhwX+F&w~f5W|xhzLrBcL-~0)!&4a^!BC_>$2decLW{xP}M24Z17^)a1^=erTiT* z&0>P4_5!;J^;?xKPUF9GKDW2iB|5y0#|@W$stX^LwnXeC49EOW#wjO8O>AFCVAKXQ z1S=7qZ~^d*J=(AeZsz>zCGEj(9YeI=j2AOoj+{+U0DI3Q5$y(<1+n0^3q+cRx1e69 z7RKs7k6{}=IuCi$V(?i=$GkC|7UCA4K=?^4j?H^cN zV)^no$e~D->PE>IOwrtqF3zNuw0beN<2Zw-y7?sIU&PdG;o=nLq-YP*Sm8xU(7`}hO z2fi4Jlw93|k~>m6p5!rM(JgxO9YdDS-Q&6nMwZn33%(YvmUY#n4r}SCByKS-iS5+v zjK^JL3eR`Bf|P|_4WzQz`ed8Umf~#GrM41RE((`V*y6;~F_K%bQ_?dj6?o{8z9BSX8)!%XzhzN7?8fl_(M^BydrwWWi)+EIEv zN;pE>dv0@gLj@^X5q3VBw1Yw4?1dw>bZicYcyG{rZH2wXvrd`b!S-sDDt_Sq>^{WY+9Kp&~&)sWII1`TcZSTdBsp2J#p z-wb?`3nlL~ldnW_$8w$p^*1zZ#drkplTT~-2eL9{rXCuc9Kv3Djd;o zxZwlJM}|4qbjHB2al@CkAFr@L35#sQ=D`pnLG@?%hal?ou}g*kgNbI9qONnJm6=*2 z^-Fu|Xx7zk9Y>IB=L5xV9erSZLg|g=km1!a>;diX>olN2CBQ||bfS?^5~H7?cg7-@j?ioF$1!5d z(NFp4TfVp|eG&2U@+do_5<#D6J$xu82gtFP?h19J(Gv|BYACqwWSn3t(hAgPm4;`7 z1C@rI!TyCP?Nu5_XK#3(Z$j#^>x(PW#bYy>qO& zB0|=G{wdQbmI6+)3)xVQJcx+2DHH#QJTSqiS3$Hy7Z>)c{?) zilqsvQ`9Yj{f0M#OE8)EQ^VWQg*E@kI1bL`s!>F)<|nHz?aL2`uDUdf9}Ze|>CyB+ z?@3_^^40B%`m#2cb`sUI`m9=+#hgXw=5g)?so@uuhBs^OhQM;>UXZB7sM6Sf=KLDv znPPZi?+T5>8-W(Ssg18iMDNL6tRn59Lx^=UQoPRM(F$SIw=aY+(n)*k+fy()vO9W< zNH>bW!e!&f$}Lxw+fNt2k;7wvt~96HUgI9Qk!_67*HP&cC-!vJWGb6CXDU2gMNj zl1ZK}a!UDrAXMWwD&#i#GamDI&(2^kCWvJh5GbWZgJX}x#n>TUN9FZoX-p@%xopg99ShTD^OQ##zdpF`85SHtgGyE=~4t`<%*+{m7A_R#hYx8Av(Y<^5; zkr6#=;T7I)_=r7+x9mO~b!qQmW?q9H0}sci)Q@RefW;v#AZxV+*BTmL;6`}NF@M{8E`l;*%cI)tIzlF&Zo@qC{S`K;s zK9JF_>kqW{5x5N5eFSR25{q6+ACH&PBnUTVhbHnQjwerIcuU7(tuGb7VWc(C2MkRbS%_e52z{nas-kA9gG!(?kQVj~`T#D!>i%MWPx?Ix_eThI zF+1(;*vUOsp7fP+>&L7@O|C1bSu0g{4OQRT=w5;l6Fh&&qfJ8lsbA{Hd~+d$qjyOS zWZ9(kg$u4_U?Hb1K__f?zXlZg6QbKe-7$P8Mf}()PK_BBG}LRF&l`3D@d!-pem@zM;b%fDZ!o;bgP-b8-*AOFgoPQjRgjE5^}MY zckkYe0-aFQufyR;lsmej+>D(aiBgnOGfEH}!F0s(E}$&~N>suKkc~=s1azB-ON>bh zy4RfEiIbElPT(ka%D)&Chz*{eXvt606Iu2jLyvU*>S~Xa`Z4E>$11I+C+jhJ!tSw1 zpfSMK-OB@rm=EZ42OD8p|GtO&x@;AqOMSeIj=1FSP54G2krTrW=~^Db&P}Q5q1_BM zZX&KCh+RiN*b$o;+czv=BI?rp=PVlDXfB$a%G*L9lNEud%TLD_^M^J*74{{v!cO_K zp|CJUDbGhC>BMw?Xu(bj?$clmc_F+h59;v80bNTm{RqA~)%SN4Lw&ftk*H-LMzdLj zaCLmFGSi#d(_Gr&P1EUVSF=5CA$oDy}ac2vP9}@orC4fuiM{~<#U&F zqE9B`3eBwLT|UrMp%76r?8HJ;ROd(PQz-rA*;w>pH5@~&SihqtT`@pt{?8S?SSECW z*Fm@u8%sGGjf#Wbk%Ar$V3CeI@h#Kq8B^n;n%DXJy9iAbcyZKEJ3MH?k)r-lqJF8d zvoU}>X{>GiG;-JmM9AxmDEe=LqB$a#d7?exwA4w?ucDuCF=GVq1+= zLLJn_Y#1aAe9?R7;`Mt}e_`_Y^!%Rs?lt&V-Mb^>_4`woXDIVGqaC})La1u$<}Hyt zZKVCe2o_QPS17MErr$!rcK0t3(4WLXexu00Qsgg<&Zqi3s1LW^9yz+FD^KlHm)F?0 zPTjnPuM~@@Ni@-DpZI+_xSo^FBsNJd0qjGOA)#%{M<>G}688lY8*SB#!P35Mqv+?oIqjKVkf@&eQSxS>gMqphPjG@W@DrXZTjKd;z?{dN$)u zx+2-1*XYV?EK1G8f#tl$K%b4N5FOs@KvPovK+V<@QYhinpO#eA@I~Xw;%B?71%3Rh zJ}s*#nb>=?p4d$!Rs>7RdvC_NycgWIqYWLTUweZ?iKI2Dohawk1WR=yl5$B!u&fK^ z`FVx;24#L9XKFW5_QJvK!;VV4O-!W^+;MgB>IQ21cK63;K!NJ`u>+5CYb>?@o-@N4 zV_JHoxPk%nv5Xh@=;jLg&3Gln>BeQ5AL!BD74(|%Vv1*)@k+#NGB74)^mNhB%(yi? z=rZpnJ0TN7Oe4`Db*7?jP1kpke#V&W^MP1*dOSLxlRsL#-Fq3$H&u1rD-07(q;`t}}5{k|R<4P%hB-TmO{ zJze-UPnYU`08jO;X^rl?@IVr+Z})<)OSZeOK>`^t)xCysyp8TF@BqH9rJ%n38}PLX zk5md(c5fR)pk}P7v412gxJ!bJ`EL|ARH<8?-0RO{-G#Yzc1x-ylHgbLHYIVe?a{=;t|EEelOL} zdo(_U*-_B?G*=C=Tuvpxa!(W~#xuRgq(wJ=W@%~~3}OA&;vaaY(wNfzUp=ttjF|N> zdt}v*@tg{?5{%)iL@k5p{oi)?z$sCaaD)UYe};lz)^@vGn~s3;hCT15vf}l|(Ros5 zwe^n!Hqk)RhS1B3&}u$*f%k30TQ;>}diwl6C3G#Pg*|iJbRYC;@tdsB!~hOw(4o#E zR*taeaLPxzLc0ozD>#l`5axn5rdT@FPotZjYUwG*s0$IVJR1a>UvVz-SXy zw-3zIR~1<>MpTI7eDoAQXiWA2X?0WyGrbN^pWlaHMWQ5+%jL)8YCHmwnb)Z`As+Tz zib^m{YG@mS504<8uKt8 z_ZE_U1&gdEd}=_w@ME09jOg%AkGLOK{@6Xl?h^tkMZ>9A;X~oPQFs2)wQ{jX+&klO z^b_@mnC5!{m6E(*em$NYnTP)7Xor3VkJx&N7U^3OtH94CQRa6On&TzS@nZ7>ubx+- zLIt+U8+9b4f|K;4G+xu=&!LQ*h@&9+oPZNGS?1{vhyY0S8%ZG!UQRDtkJayW=GT8Y z^-rd(L7q67B2G1QS#)=jab%R*4|jC&0lf3|WS~(|-uUzQ?S_cKPVJWm1lXqg<)){X zo46{M&b8Ix>^VeGUz?R$vrwehdcE!&kHq^y%2-wFJ)UAm>#=?mNKc%N@yt+rcHm%2d|JB_Ivteh=k+|Z1cCH&g?+h#H zQeV?BVY_yJfad~rQXev!b$C`9Z{uIeomjs+TM3;HJGVs%i627L9wK|DP5^FtIS7$1 zRD$R(dal=ij$gZG5_Ci}7n%T$Zm#T%=j=$dOi4Z0l9-DA$oUy3ZC=neJN4) z6J{HeBkv*Pp2(PNk==#-JPZ9sV_(~jZL|W3t~XCn|EL{{c|uV|gA^6N<4#<_CCGDO z=2*3rWSBQ+?wr{+9U9fUiAU-Wq%I$%%zp-AI^@s$!{gPpt6Br;u0r+N&C1l?uDtpK z;b2a_`b?g>b2t44aeE8s03k526Hfg6g6Rp{@3LOenx zFj6>K`z@t}+<%G2yOC3Y)X@`7fACaNJG205r>HMzS5ZCa58{UU<99f(QV_Z=oA{lm zUVvBV)j1Q@zc^ZN=lZoEoj)ipP_cMeNiUs0q!;&IoMy;-dMJwW0R;X0L0-mVM~_DHza9bARiB%v zzDjy`dbb1NiS-|MUvr#N|G(Xp&=ok%@^MPdPI`3@LQEA^(Ak3k*}tQdyBZ(Oz87RR?f!;5nEeOn%+BQ_$Ix1>dN>BKETM$(qi>kOQIZN_ zV9OHQQ9=nl%9*IJrxH3fdzc&d(UWn;$3o%NEy3eqAx;{CpC?jZQ2X8s6-75J<*bZ` zc~Cbx@mJWex8@+F0XFd*q@d8jcpvu_ag)(7np6@D6oV-k{+v{fpZyi{K|dMghhgC~ zyjazCxnV#W!oj?J^d2mgoA{w&=OZ&jG)NHSYKjv>dH4}ab$i%z^0Vx{Wvu=q6lwRf zq=-O-bz7ZXhiQN!pa7?mSm*`oC1e>bj$QkEied$lUv(E3Br25{LD6No0mFAnQQn5K zlIQeYFfR^g7~ z4TUOtnZ`9K4UOy9;a1QWtB69M=a5Fvo&6uR4kP~v=bMJ_%-cvNdDEK1miWV-vHN78|wF_MJoeTNd%*U9cKr&CkYwV8O&2ctVO!stFYD9Y&i4@Y-I z8Ql?-f_>#^ykj|4H@cgsqtF?-=*+y0y!G&!)`u#Ku)L@5r*V8+wgM#d6dB;}Wp$w- zGsj2n-@>G(kv+wsu3&Z+*q z{aYN84ST|Ssjqn1@iiX9o`rzT2c^2eN^96tj94dkxwH8-{bjkB?Ta$l#puburGXc8 zq4YQM@mA{}?5L2R)9Bs|`4OXZ)Cr<;xqdI-Vs!|6cah_(L*U$D5+&|n`e{K3tAXoluSBS;GH z|J>J;L?ADm%wl;R&k}O4prFy+F^**P5}=ULb9fRm`jdRpn@uwMBhsLZIKa_21<}Va zp=f7dh&Xj~%yzT>(fI}0uw2=&K4!!GvSGxdhI1_3Fj9n#)L{{dpims|Gbm_u9}4vC z?tOWB!#=|kHH>#MmSu|bJ~ea#LqRGhAH1v=WCj-ib1O*?Zb74aC(1>NUzhBxB?XvlLt3i4b2iE3(#(wMrVV+TZ7eKJJ2_sbtd zc`T%?kJnl?!-4#DobIF-5K0M*%fh?)|{w!dG2k&ryVibU#VT-+Aw+h_ZO^ zrPDanHW--47;oykAB1b(zdbcUZh z!%wVtyR5X+w$_yX~VGF=I=X?}MP?-2X&gUcvI zz1FEb)$Z+*D1Q=&bcCuqp;f-t2PUD{$9I1~r|uN*TQ?57^PQ7###>P34EF`c^>p#3 z`eZ!N?subm4W2s2YwzOCHFbbjiNHs+CPUdcTqnT!%uu5{lPK^rlgXoKq6XQpN4nzW zK!tD~2H%*Cx%zmxI5mAp9@uER?-O)8Z}*~-Xd@lbXtLE9phQS?ENta)ZdZ+rRjN>5h?sOi$N7z&K6n6aTx1=Gb zYBzZ#KD5+p?|smISp@x4%9;MYN-O$@emDB7J}M&DO?7{EG}J-t1o@FtH|AHEAY2&@ zUQ7zx+-ng@(XOM}g;qQ1^bkrvHV4LeLT^qCF4q@0YqNbky|!xok%iXJ)^nz2Lrm9l zwst2fL0%r#CK!j_yp67dFsp~Le&ZVM$2bprYW_|yb!OE3#K7XY|8*U!0a%okK5GE& z5V(-8Jt;D4%EO-1WbwH=1A;XiD^-Dn+9&84k({Lo&IU%;SwdtnNsw@8Z8GL#UYn5g zaDpH*=Y)uy2vz@49$7!gHHLq@GCPZR8t_}G4`eryby)Qg?FpfCJwK71+fa<2V~O2) zB1;VN)UJW#+z&dqKQbC_9CP|s;584>3b;s+{pAtkdBQ2N@ zrFuR^dA>{|@v$Wdd49)LZ$s5#&pnujxS+a=m$5(RIO_lCLpYpoW;E1d2LqNdKP97K z2_DHI+WQP5#fZH0VZOSf{y?{kD<>ls-oXp@g?VQj9ViT?2C$WY#L&2KUh^ynd13w; zQ}YACo(sn}X6C6csn2}bR=+PRof|BM<5^Jfld>WR>4C=w=}1C-{awBc`x zb@jr=qj;-Fdk93aLA4Z1^XhCY;n829BsPS!bs}>zGAB?bYC&c}cCmK3$l>K_v4Sl` zs;CPmSRa#GvpQ>Ma*>R+ThuKb-Guq#7GcCJ*v*EGnml*T553vKS0zk?P{pR95-QK$ zP5XvyPWIvw!tnUUp#|!P3-cR?4H!nWFCivyo|@s z>pSSv@fnZj^_bZGr9Ac9JoWAIus{7Uf77l>pE@;vNA{>aRlU@22kjdb+~KXt3~$LB zwX5n~fQ;2|z(|C*kh}e@Ev-{!8pUB(2M3T4}1Fl zo{S*cPo>nt7bmKF3mR)4S5z#7aWP^EKGF(?t9ADjM`Fjuv)lu)Qih|b}#(HsQp#AMZHjPLlu4U2sN(eR^B*O$IqpP?gW$SuCV8X z4>0e9QvZx7%L85ELXyH$QO0U=M~D{p2E;)Vka~!sj>`gHbD2pzY+=zrPf-!Jhf4-s zwsh3ksyXVkxle#Ym-^hOmzSlhn@5e_R@EcCdDM=o&(&#LLV5VyYFjEAy9hOG?+-JM zGjplgh3fwbDNtvla~nwoY@dgi(QIJuf0*W~LVTYG zdDqo(=?_HOYoZO6Vb3cf;YE>96!vUELY}&fjf1!|Q0~9Zx!^~9-Ss0{^6y1D+`j|L z5bpmCWE<}P3B&U^i41dM&MurUxmoqG5cmcyghG#My%J2k+=?udfQT~+SUl{(0;dX|b7~aqD zc7{J=xQbyl!xDxw7*1sP1jCcxBg)}-2_DJMy%~PPDW5QWo#ELG$1psF;qeSdMWmm} z&v^{bVmO}RPZ>^RDCiXO^SKPqV>pH3G=?)6&IEjZLyECfWQA~wufcv|Bu?je=kpEU zOUsgllM+ZuASr>Q1dQ1dOw0;3nU<75 zQUXZ{BqflPKvDup2_z+ult5AfNeLt+kd#1D0{?Xq5Z6r51vt~h59O&JHrI+T7<061 zp1_x?xLjf`zT)QlaiZ|>%^R+vc;bKYHJmMuvTAcFD26??PN;r-U+#!wkQ-o+8Y%?2 zF^}1D7b+~qeKcF`aYDKHwlM7Z%n6Ntfe!coI-#}l2ZE_iXd0tk_Irc3e~s!{^n0@x ztfBLBw2L+w`Vose+wudyXZa5|ewX_3fv*?ecRp$iz3$aY5ZFtgE4D-8vhT)&%ZwYEs9vtR}1~x>CN%WlgIy!cgM{S z@Rj<_mD&@R;Hm^MRU2Jk%})T6EZr`kt+6O#l%)sj@;>b-b8tE0)3~|emiXIG_yeCd z@U2(~6)M^7Xw}2Eq@>|Vxj7CjNR|ip5Zqv6=Qpt}19C2S&{E<= zUPs97kJ_;jVgG(-hi7U39x!yyTIjHUciRz#{VT?eAX@p84p>cO*>>LLfVGBbZFs~1 zYdv9I!H19qC^d)|B8sE9);GB z?P)i=-)yQSeqpY5)_Ow=Ok_M+2`1ScY8fxFlEVisE$m?H8;e8g_)#YhWgc?zi^Hp%7ier|rGoR=}Z93t_c6V8O|| z#i_bIMCi@i9I6YD1b^>u0>UAv}T?nH}jrPPX?u-HTnbHZZ(eWk6dO{0fu{Oi5;3Q_3Q_ANF9AL)f8pZ{GX zU^%YfFFdRoeH+E9HGYwHWg_^(8lG!a#FBpKUa~*O#gET>0iRrB(F9BSXzw#t$Y{AW zevx)VBKV@cbFGS4(iiPLC4PKvZ@4k;_QD^3`Sy7IJF%a9R|5FD|7%giqP@*N+$_re z_=MTP;gB8jbaZK^Gb+iH62g zv$_0_;y)JtH3{I;rgQGChCVpb4~hNS%_}U5Sn)&jcbUB(Fa5o16QEz4IQ>QD29C*+ zCI4buXuG-Z78m{))6a6i$9{J9FDx31VwU{iW!D!sC4!%;ZA}QjNc+F+M59HI*WWkIL{?XSaf}g8>9>ca+w>MY2@#@6ryAt9* zSDT+8KZC6ngE8fkqgDPae*RUA_tEPT!4GJC62K4PDAE_E7w)J(^uGPegzO>q@26ZH zzkf&j{U;=d|M^w%(r<3Zw9f0h56wnf>6sd8A_w3i(=Jwk4=GD4-|SV~5n= zLlJo}=GE&A=54CroyybVs)80{vI;E5$e*o_s|p`A_+`^DTX3VdB2}#Ss1;wqk7(lc zUV2=$_^Fm9an+J$*77B4p-tql=Qklcaq+4$qsZRH6(%-2#wd>(0{_y8MF&v(!k)XX zHDnOUpfx`Ae**ZjGyN7tEa*44pgVay-4esdMlfhqeh)Tpx%~NNc?j>k8a8Ky8c zmj1r3=X6^=5G3{Yb#F|I54nzZoBziaIm#?tX&QSAj`abRVvbp97XEOJ(Z?3$^eWga z92Zv=UCqL*xT;{Y@Z@!Na@Sj7X%_Z2#?ufs3)ADO#nLP^+17$#0Abze zgjGsde>Ro(RA*5giC^aE1b@lG{vAgr%!wFKO^64rR98ruo$Atze<2&>V=iZEx% z#{?x{sUI|-z1(1!h)9yw_?XY`N&sKZXO$L3tm%WFvn-Iom^D85IV=Hu$xr+0u1&U} zZ=T;CX}(dhz=MJFX>VT|kAeI29NUQwMu}#)9_EIDWpyF?&_$~>S3cHQS%mewiyZiB zBCNHx&Mk)Zy@d6x`69}?#uma-oM`PLtcRUwk#nMbZR@LouQZwv7CO=L64nMMTDgRE zlu0XcIQ+|>Ed@^F_pmFwDzn{q`OB*K@#T2gU{S=1AKKr0BZ2)5;N!gfGJ~HOH2Dnp z<7eBB9NLGMN54byU>1|RG2a$bx_Q{M8D+b7{UnbeMiM9tum0|d* zZ7?w~b^ERWRem}a^qcUQqZKZpUqBDJ-~ZKD`pK3UwlYUM9=LX=U);Ak5K;M)GHs9p z-@MHj_FO{j#{9yrxg7&sQA7N8;q`hu&eR5a$!NS!rHJ`H^)TC7^rF%Mr=qkjSMc4^ z<+j|_VZjUTa!5}`*>Wd8I~1bHv_;jqk-j={LreG;UR+PIXQ)yzM9Y5dzANp-)htnq z2=$7%P)iAQsx2ynK_?)?o;xqI6`ZWxM-;o+Qf%HQG=P^MudT9clG@+{uCaqd5n8I@ z_X+#jb3=0tI1dmv!MLiBuhXdtdY!(3`{u!wc5T+Vlif;1sarKJT5VetXk?1Qo@^(J zkprq>Pk&o#&26H&>ru1TV7s(Y8|qN6AyJBuL}6u~xzw($V3-c!U|+)=cmMLqG0A|H z&2P>^M=A8Z>x#>b!4*qX7IUvze&ly5kE;Fn&bro6r^vc3>J#{C-1w${e#>P>`>g2F zX_v8;_9$Wl;#$*tu_P4QNQPvg6IL5xwOwJ<7}@YRpDn6yUdN*w{dwO?gR})6>>V#7 z2F{NIi@YY{cE&hcUqp}uG8s2*pgl6lyQc$+I12M_o?Wf_M%yl8@G;v3mfGL~V}^@B z5pogQ*;MVZbusl?G^!;KS1odmpjv2-AQvX=xhAe!_E9Y#+P>M)+ajkEs)eQ#n!_+p z*wz9I?$@R`xOpL>s10=(GodWPy42S71y`&OAK2?74m%L7KR97E5!OLYHS$ul+N2fd zfcg(ZvmV!^&sYwnY4?t`8#u5460=mhJTA;to2h`d_k3HELE1IMo*s?;ofp|rQ5!a* zpX=Lc5FJ*34@GDYt*8258e`im+Gu&u0VPyPBW#TmRy$!`=zs-N>zZZP7V?<1KVRU0 zlBYc?(M#ml}Asx69G_hgI+TBG)S4UNEg-?y>Ve8^oz86W5~sl zA7L-*N-e6e#D_gn5~uG?guYf~!3-p8$&cW_WsyY@OMLLZCUN@3anl#){r+Ut#g_aC z{CncXS3_%P7JZ{8-u8;~?BnC59}4Xwr=XkZEt@KB&3`WOv(q#N_V{8CL}_fGY7}+6 z7mqGbz@vi4H1X(#H?@u)L#eGu&E(VuPU+r)G3Q0bW)9V#3%our6~O8i5J9y~+fF^a zo$Ej~L_CV3B;Ng5MV$sxeRdS6pgKNRQ8Tqe=IsxIOpc7B5=9$Y3v!%a2qx2p?;QTD zw*Im(Z2}8%y$l4}5q|uFul3ztQw-nv$x7aa{7CHFuEqwxcqI8LLQ**tG&a!k#S{y- z4}zsk?VgKSLPUlBqFO94Mw8dksnPeZ6pf~0+S@>&M*F{OqpjLU{?5z3SPG`{Irpzpz#tVrA^01=j5?)&6F>08zRU3;*$i@N>1R6U3j95Ppt!QbPE- z+LwvUAMhWyCxEZ=?y=TvZatYIwU%d*c8a-}Fp*>Qm!yAwBJ^K0S9o#IZ^CfY4lIpR zKQSK={*JiuF}^M|{X}PeuuK}35Pp%?Eg}4Xw$pSr9r+1CAFj7*Y^?s0>x21ccNM6W zK8W>!S8KvF(Ahv(<72KNVPr$cjFW(~vLqz|{Eg z&GW}cnMT%G-?)j*#7frcbHC{C5m`I^^s|&tg$vy2Tm_>kaZhjt#_iO5mqlJTKqC$s!7YT zw-QgZXkK$Awu-R^!uv1N!dc(}C7_+G*vbGXc$w8gEi5o+{20W5*-n`K;=yEj9CVVg zqi02v_R;@miYA2lh{Q)0wKc)>}}u+=Uj%;Legh{K2$@@G;*#a$dalr90N46Y5M`f_P&CB;%|IMd+*ujO|S`4dyKv(W_vM*=z3VTcGMy$a7j$ z1)bA^eI9OG1>4RGO&`{RG+exOG)NYj+fLS4t%P-rxn8!$;$iv4Ttl?qTT{Oq0ZZn1 z{!7GvqWN!z(S?zmSkl+$KZ_!k_?XwK&a#%21-^NnV~4p2w!lN5;~ocdoLQp;v~x|R zj%=Crvjq{8pOH@dc(u0+jdog)f)4WMCQsT90$??f4&LBIYY&<8audtCNw%pLOg7UI zGqx|2%uNCdJRWl&n@*)AifBoz6IKhIx&F1oJ63To?H&gwGvuNVdd_#E<)a$Qjx**a zYwiO?>vE?WO9@MJP!4g~WyHRPb&czY)@>#&+ur-(GcB1Sd-BMX`0WGP>-q%nr9BB) z6tUo^nYxbk^S&9jgH3*~IB;wnSh^QD;}~0wFuqsxVT^=5cT6^fV!^0lV3*QB{rc$m zNVgL5i?+!6jG9Si;%;-XWWj_Q^P}f?O^?H(oL+^Os>qA03OQ*jC?O~9%j2rTM^(IS zyYSIlASl_#)kv8eNBtEcYkWDY0?2%G}-~Hg*biTC|hj_ ziBAWXI@U;L?kUqvvFK?`H+=K#FPyMiiB^jJn8SL7u->#)7}U6ru=+dI$d}yy%!yVG znanbKELDEG{?R!Oy%^AnCfWB7c6YwERU=G{UTxJ#gUSzYrT_m>w9wb)Cg_f^|K}Mp zw!nk=;y?5d&x)-j1#$uCyR>&RN3K830gKIePmgUsN;7_Mo&&8qGM866VY%po_qLf1 zw6X~6J|`?6VfD2iV4;vgSRdPVIOJ7KSh-F$))Lm8PPCc`Yo!y`y@a*RX{FynSUx9O zy9le7Jr+)6ggs4)p<4fc4D=7&QVTn7+WThng{cLeZtq79vmF42o#pYl=n&iSX&|$` z9~mdIu=lGb80rw4TTCTv@88Lcj}Nx@58EQ^#+dE>)wa%zZYkx}ClU63>v%in^(xrj zXUA1VS9?D%t}58xZ#LgHTZmF`g{8g!D&MXl?5YiPa>&<^L%zXht5))t(T>Ql?Z=SV zg5NmGfz~>*_dU!Pa2BnGU1hhu-RT7ayX}+ghmLgHv+c2TC;B&MImk4i9d@RD3*kgR zZmT!kGow#2sQkco=0BC3g(5W5UIhEYw@fg^Wr3&L$D>Y;0}FPEUNdMv?PI6Ux*cFs zbF^*k`XKFUYG&ryX_HGb|#7umWK^BrjA(4xQ6q!rmwKhU#vJF)*h ze00~&L!H+6(2wp3;7k2z8`ZT63;L#h4D{OyOzOvdC)#0|`tg>n%V4D+$C(UU@X7jd zj{RZ)w-`{*-{#D8w=Mh6JTJ7v{>=qVC>2BPgbV#=&3@AKpB*@lCC7*R z8}&p{h=qS;{P?E-^2QjW5-Yk=pU%&Y154k}ySjTE{tJxLT-&Oxl<&@5W3*T?OMAdC zneMA4ig-DCzY`YEv}E;D`mE=KHph_6sHXzOA{RL;ujrZM}c~mSWdGa9Jg? zc;7bUbX%Dl*rjBsuZ@#5ufONoBI{0QCK-znwu?9YO;y+1L@z$gj(NQbFIDl9`SQS` zL-Z=TzHd7?t}1*~MMYdyNIy6FecQdK#?uf#)shxhEtc=w#@W`w#{J^Kwwfk4YQOY4 zV6lrZC;Y8`8q7lEFr>T#6dvS2>t6D03!NN^7Q#Bo32PT&U2m&x;A;&Xd_`A=R_-)u=d;bECy~XHtNHkmBa0H1-%&1v?2B~#a6+!_LY&XJ>)op$`5Q3 z|6h@_(2HhrArv}qJJSA@2X337G2eW&(GynGNH3rsWKy)}zdR+LwydFAX2exXrnnWJYWV~v$b~TMx!$%G40W&8V!IRoCH7Viwp|F2$ssV)e z(Q&&DF(hdgv8JD+y`E`-jAF6ypFT1X{9NsTX;(~UV$m1;zijSPn?Gt@%=m=PcXNAjWX8lEz*k2@n+!CLoEE91o8Ko zL&uT6D(-Wbe!N8&Sn(s*@h8TQ-$a(-RI4FiMW4=v7Ywvu##%nj>-G015uI#BSLHPT z?yIq1g<@MK>9jmSS96Qv}ZvU&6!9Yih!W zb>7)qqPl`3MtP;N5O>?q{V%QBNuPd;TU#tkMB*#J$A6dVY8;=fj?W&0gIOW=nk z#Ta#U^uz-~7hSAlNvjk8SdUbnWXEe){IeqQ4c4?7*pZIWWJdBqUS@ll{}I%7wQz*e zn4g`~Sdi^42&GrL!gS3~u`bDen1e|yJ}W^=(HcxBYRd3uSLLobY>7W(?PhgzY!S$k z$sj3#qy&-@NJ=0nfusbI5=crQDS@N}k`hQtASr>Q1d2cie_70YmGfuE%)g)W$HdHkj`NR+ncuo?`OvKTiUIg|0d} zExAXA8)WEPB+~U3ZCD%~&n%CQS9XfGb&C6zMB_Jgitp(Z4^%|sZ;P5Q+!XS zcwl)n{)SHR%-ZPu%1-gNPI2EQ(fCa=?pq@RaiE^YH9V- zg+cH1vg(?7<=!d5ngwM`XAScW8#e2#rAwC7RFnlTnKk{aSyPuTEeHM-JPsW(WW|6SxY!ByrecLiM~ zt}0gm|4UrUkW%7W?wWzH!nMG)6wfZ#IZM40m&{wl&0a9yRHQcXOt~iR1&Nc#64l-XB@4?cN)~v_D!dg- zD^6ZsJAPSE7Cd=DNmbc0Dp_?`^klHsc| zY?I**89pt;jWT>#h7ZW_*D_o$!&_vyR)*KfaE%O$C4X1S_(~ZrlVPO{FOuN`8P1Vm zkqjrxut0_rWayXSNEwch;Sd?-$grObkCx%#GR%}=FBzuF@Y@%JJom})GZ}s)!@tU~ zU50PS@D&-pAj4J}J|n{?W%!s3H^}fl8Qv|!J7l;{hBwIYS{Ys?!#WwRkYP}U6*4T9 z;e|3hNA|}o8J{l0fDF%<`IBUPs*LB$_*fa{%5azr2g~q88G2=SlnndI@L(CH$*`LY zzy70;_g)$9k>Q6jd|!rd%W#(rcgpa28E%tdiwv7(_^1pYl;OQHyiS$k7cOI@I4v6CBs)`*e1grGTbV|r)9WNh7Zf|0U7>UhU;Z`iwxJw z@H!c;k>QmxTq(n4GOU#0MKatf$K?VUpCiK}8BUgAfea_e&@aQ0G8`epAu`O7VLurj zEyKfQm?^_vGE9}>x6euYAj8jO_>m0%D#Laez9GX`WcY#%TV?o+44;(YV=~+z!~0}- zw+!!);W`=KAj4~Ac$Ez6WVk|xK^a!auvCT@%5atpr^_%P!$~rXoLiI6|2zrQUL^dO z!%q`_#lAyD_`pdr%o1V05hCo&+4{wz{Bb!VzxiYlmgS1@t7Ap@>FFZuT;5+U%D*^N z0ofEpv~%X2MpRbqu$=33~w#1*+(6zz`AFW~iK3Gns5yd{<8WeYEH z$Cdd{U|Dc}X<5ahSqn;n^UBI+EuU9~rBC(TvWoJ~ z`Xn$$j-0$SI4@YXv|6C9Tu30_F26O0Z`_%bTTDV@Hl8%H!tE zUyN{ptGa6b+zYFQ)IuTYF<6erLEfRHnY1h)vb<~oR!S$H$hkng#6-L#3bC}LY*A^@ z_0Qff`HtiDN0j%J%2F&pv9%LZ=DJQ(_#?>w-%S6`x}?0UVnMtT`8Sb=u;)wW1&3IS zzruMz7 zI!ev}n)iQnesg@a!*WNA7&dI^Q195W-kh9Z)x5G`^~jO=C6&Qa$u{<(YDxx>XOJ`0 zJ9@Nt_yFM`3>-m;Lr-N-p`^TI33(AiBet8qEB`;D{hLr$U0QNU>>0<@6?*zN8Sg)$ zJf;Los!HgMMP+H(e8)NAzh54BCsSEff_>Qqli`uTVXi2dR~1t(|6k>?w2JLSOu6Wz z=)Ye+fq4~W6=jZskOKKX_TC3Dj-$LAUY`{qWLwC_ab(w+4RM@vuoYQxjG{O~{u?Ka zjVj5B2~MQbY2~~4ba&jHY{{fS1TZFq)HNxu;iJ5U(lmt-Og^A)3#pp&f(aq0ls9!# z3L=HnPzd_c6bz;5_j`7qxtm?@>Q;9;=`7DkJI~DCJo7yB%-@}7{#-Qj@L52nS|0kK zuIbIX_BNJxf&FI*+uNN+Doh$9ezzbH2g?r_RAypedebM&3<{v z-)H$HGW+FW&6nhtgw+5}wLC<>uAq{Dl7NzcQvzMLCc^urmn-3>Ly*S9)_NrACtpt5 zD`T2ofA}jquH7I|2(HINaU^}>6OL>I{b8T@U7KQ9H`mo48nj{&>#xRr;>)zjE$C~8 zzw~$Q45jxQxg|YljbD2^@+ZwIkhv1^xcuqzx&hQd%h(R=bIGub6o zr|_-{Pi>!-zT@oI*iS38YVn+7`di^9nT-472SLl)P~9M-b9yab;r1Bo9V=NcS;_0T zM2x%R`{Rj&aordUV}1|H8U7&>xAc|YTxIDcop_Oc>-PlfJ)OVzglJ7{Dm0|Wjf2pB z#rI_>>$1j64~Fj%l$7aPFDn>=A_~eZJ(Ec2u|#~IcwJ)sP<)_2OUdqt55!`6G7*hq zHck&?k~opEykHRv0ejG>U=3iOxt8#dkxH1_FMAZGufXp+&-r~pf;4u}E71IIbF*{N zBLb10A&6ORp=mhI(~9tTL0l(M=0VCCam)HQ(g!766?*K;jiI%Rx`YU;ZRHE zFtrN;-hY`@jxW31f^WUvf`XnC29p+jeT@|^!Fqqc^siz4{x^7?fRp|?u2)XF!r!cI z(spXiS_pdRW~?YCcM@*NY$3N=58%1ZcOw1ugzF#N)&p!tPK34n+CHomPiOjjK!e5@x4s`Ubn6>4(pcQLeoYQ2Dc{i0eg;71-arLQQz@x#on7bt)IILpUp zfhm*mRSCa8{Ak58>@Oek_gQ|4%zk;O^#ZxpQMr@^lmyCI z0xLVNZ<6^F`um3W_|t#Oxa1c4^H!huUh5Z5`o#C5KjIUg^y8AcTF;&)ZwW;TwMO@KJlIOsjw0KqdxHw z)MiBaI^h$)bo!J7=Ke_=bCSpURZkD}q{FFbGNU68NS}3H-&W>gCN!$O=FEbEl}wS zCyc(nXgG>vi_+R=EHW`-`qu2v`j!OVV55$;l%)*yr-kVAl)i@dujV#%s{NgG3ZU&g zhYMFBX>SWA^l|u=ihVltNB-ZTnzeOd44s&L}vf| zsya`=p^s4K3E0jzQtJck<-=Vb`874x7wGJ7Pt^K=Jm-a3=PmdwFtfkBsPzH%_C>7^ zu*)Mie)UsjW`BK=$FKP%GW+E*v+D!;q`Nbyhi}ER1CM^9IDJq9<7bTiWV?|bh(Y6& zN<=l;4>DRC6gb_0M>+f{xM{!X1MF{wyBp6i`{67rGnctJ!Tj|kQn+2f=%tN%^b^UF z^n=My6erK&0`Z9lWt0Sz1e643vjhg;ZtWi)Zm{&&>tAOzoeBS4dmu~%u=lx`b%;zipU_SkzI5W6`&J5&wM=-mp4f*D)WX?&I~LFUmhtWsUmFq`xd-dpNWE=gM%s(~Rmz%1<6jA7XD` zl|IBS52X*W%R~Mi%iqgf1pSM-el))%O4i?&{5%?2r;VHWhHmcPr-foM9K;sSBnDFE zj=fMai3=XZD@}`UInZIeJ-t0K5I0Y&>>t{h=r>Pn!@fHl&qmVdNepV|0-ClM?)hh| z{b;A*r~P&n>>tN|yb<`%{iF5!Dfnss+FAAw!mq=BhW&NyKh6GD{ML@+9-3A*0uy~58P&c{gah{LH3WapV3POP@GINK27vWmJk)Ol7N!Hte1e- zdDHq2ircT<;1l1A{t2J>&J03#Y?_lk@mF@NYiN)nSr0eGp^p`rexLZB>Gz2*v2HrE z>Gz4hvZLvGIgV`le^_Mted2qj-zUDzJGryz_lb}6H_35i)Bk*t>Gz56nSP)6GViwZ z`^4|s8I?|Vx6XE_@v#1{MW){;zVm!}8o$-yIpGtZ(kz>7{`|aJe)5`v`5)POV^64&+ zKgefYqYwEcU$Dz3!hRv2ZuXa}e1;3;uj{rDpRjJFjOrSFlMVk{aqY`dpZHGtJD9$9 zwmAB=KJmNS`l2c9neW0f6WTgY?FY3dM;M>vLH=Q%@gZnYMtq+h^-0fpUj@mp4o~Y( zij!ZPPkbkRk{8h*@rjS1HY56nQJ?tv?n60_*n@c5erm}h0ypJnEBoOrsS)|vT_C-D z={o}MDSZw9Zl!Mz+>}0@{cx7li1f7RUef4g5S zPQFQ>_+7iVTm8%KTh0E($q&hs@_U@)+W|Mp_bmH^>>u+<55cF^i1C^Bi^AmT?Efi$ zD9zepYdbykya+h%Z z^*|;Xi)MzL6fHmBvlYc=_z*5SO6$?Mbr+W&N*Q`zD$%d^M*I43lw%y~SSa2?(YRr$ z-fR&%;^8=OPE!{_kV(=Ojl)ybDqg7l0n@WTU<*zSOK8dS&I}tw zMo#I-SS?|B?>G7rDQ9sn zzx6-UzTcqa`g+P^VaiY5x%D*pSM#g;Q}!RUUH7S#$$A>!>$Q8M{b_yUjXKTAktwh| z`l8nx%7iQ`IlGkfh>E(8u4f>6{`qmf^2?N8uwk&VY5n@f#&zrTmKHr2#8@SoNjEod zHIkXgMjRd3(%gI?6dN#B>#OkdI(_3t{o2*~T7BK>)mN{lm+P)G4;vIaFxLgeVS^x0 zVE&hIJ@N&)Kcj^5Q2QGk>nqj%2DQH-6pb}EwOF^^uB>_VO z39cqJLdp46`j6&j=s1&`Lt&?`lS)pR)OV%-n7;dvl>S5MKPErlpMU5;&B;a3p742B zekN}(3VGz$caY9Tn5ph>^k!Xqn}{5+!0|*0`yZwMu+MMjzR&zrp{Xt}xm`VXl&Q%)rTC4n-KKr(IJFY%|>T6&AC>n5l7Snx(Ge4TE=OM@2tlSLN%BKz|t zwfy?8Xw~mC3{0IZR9GjzD`KLH+o<{z>TsZq1%CG+9pWl~9D6_vkQSP^)5|}5eKF_<W_x?%{Q4|?N%d{it9aCoV3rJX0YCB8d}-#e!Vx8+OPM-5txUr@xWppt-+fReyuB~X{K^glmcZ|Q5^d$|SAe&dp1 zI-~7*hS&Vc@~>ui1;aaDZ-xK&CJQ?67f&s*-n;RYSoCgc%En$lmUy%E`>+LTldaD1 zYJbf1?2p-|y&b(9%Itl9SV0jLWO8TyWBI+MWdPW@Jn zd5uf({a32-f!Z(QzF($P^0tk~L_ICP@B94vOTjzA+5f#Ff4_Onn&u=@Ci3vTe?#fh za(PFt0_&r1c`JRIq0S3Sg}XAb`XSxD|0%ciEJ~lI^l3>WwK04$ROBuA1B^il^F*dl{2;)LUTq5Lp%3PVWem~|NB)^Q{=R^&v?owDyU5dZEDkl(cVl%U$x5o6#<8G0bTX01S>G1<%{pXr3<6gT0Q1x&MD#Jk%32dbNYt``>}x z|55zD1^XaV2th_1i+H;h0S284pqo1;b%dceIxq>R7v}~X)U55T2cvO12OysAGg8}A ziT>N8@!kaP9FvWEd!4jq)6#nn4&D~+H4+d}*hr_N@qK%DCg{rNtwzS2w%(FR8GE-! zW7zZ1Y4j&!I82~EoICB&)c=%9zS;eOG)GBiggVc%r*yifFBa`d?@c6)c=*8Ho`Gnr zcW-ps?hjG=aHS8IZ)KDFi~N(=vwyzjf;^v5tqdKV(RF9{OL9F4- zSZCJ|fjxLgXS4N)rLPIXU5BTY{cx7li2XZl(?qYNiBXX&2`CAawFFKMTIakSJl z>L0B0+??mU?HIM*Z)W&CKL71mhNBFhVz`mRZD9CSe*XoI|1pM7@cUB?A7}U|!v@B` zn&C=@D;U->T*Pob!%Bu4!{2<{;{RoaFETvK@Og&cWB4q?QHD=3e1hRAhL1CRl;Oh+ zPcZx_!-p6?$nXfmZszYk_TR(s0K+81yBYQ}yo=!;hV2Y*Ww@PTE5l}n>lxNF3^IHJ z!{rQ@Fsx-*&F~S+ic48O$hPcr;8 z!%s2%IK$%%-@|whu>UZ_VTOkoW*Ej9Mi}-m+{O8^m;JXfY-4x_|9%VmJK4XL{Vfce z7&bCo!*CVDdzs&5>|e~VhG7-M0K?yY%aZpm8ICjjDZ?Kz{651o48OzhX@*~8IKuF+ z7=D4_V+_R)*UdwlZvHxSnA> z!yv;qFkH@X3By{3)eI{bzVc0O9~i#G@MjEPU^vF`Ifmb5_zc6-44-89Wrkm3c#`3# z8Gef4#~B`Hc#Po#3=cCLW_XBUhGCrHAlK6#_U~nQ8^bn+w=gX0&VCj%clsUHIHvne z*1q2Fea9+)SH$k7G`7ydPy)uH%@8m_IR&d6512#xX}YT)B^9B0N6v8ppJ7c@pE8 zCiXWnypPkliv9Pn{{X|q{Jw@^6~h3-B!~MYkEdSZ_jmLAUWRuu+{17d4_Pkgq#eE2 z8gHFkYmK+sIlsznymc${b)k>9wsShlW!|Wj`SCk%yfxMHM$Yk8vGYbj&bLyHx8A_oq_?+`t&++^3GJJ-i-+8Rl z9R5j$vt=IZ%OX7t#XQzY_KR`Vr`a#YRi9#iK7E`TS6SuUuRhK>uKGC3xzKs6a zRfOq@aa9ld_X<83wlSQALzD|TN#}-my%XPSnc{J>b9@!!_vd(=)5rd9hIcURWVnmr z4u+c4T#yDj`JK1^HFEM}RqUS!z{1m%huZHCz^xV#QSDp3p+3y3U zE4~l>uGdrF2gQ9K1UR1x{yyM*6W<5N*#7`Sul0I|`FF8iZx9P{{*>SUlKsDB_#=M*eTHWkeuv@H48O*3gkhdxhx&PjC2*F671Z_kJC*K>{Y;)q@ir)_4u};s|H0SRfTG)(5#IOUmyCg&D zw208_-hdM7H*EI>2tLYh{5b9UjgsFVAY@Ij{N$Y*ulxDrpZ(waN}rP2oA>?-U;3Ng zP$ndo{UXZ0FM1Sz?mTvNKKwwh!IzLU`j?-&Ki=b}bgLxK{`ODlPwex1N`K-<9y2wc zQ-0$qbzZ)`yv*!<@#U|tR{9isc~SZlyF7B=XMWn)**`v!zt8eZWcJI$nlH&OiPDT; ztyq<Kbky;QLg2|OLsWc?N9loKlgp^B?D153C@0b$lvGrB{KWvF|+%13al@_ z^@VDGo?N>NUfJ<+G9Lh*fI!i%JZ7mbHgMnA^)ljK&@ocR8@Js;1( zppHF*8Jz~%4&w-y*1>wBKN-qIdtzeGA))l3`3bJpp)8I;Zi%>V^d~bzEC(shzEmQf z@e%N5?3|2Yr(^>AC2!XHvBz;Ac0snmXJBt7?V$|AMLR4hL5<{;a%w(gW(W8qvRw7RaA7e?<8ly{x$W8S{yc@<~%n zStI1#R#zPTZlCzh@xB;OjrzpTGR_WBKgN9GcWq0B(nfCVAjUUsON&guPyDW%;~<~u z3iV+WgEVB*?-SoM{XX%#wpn8Xkq^v|Au1@w|AU#XsL3R~6#VuWeTftX3TMCo)!*S| zmb{L$Kgs?<_II;?5BvA9zlr^A?1%TFGM8{_kCrI?ZsT`qUmKPeSH4?);-me)o{}iq z{|MY>`_A~Hy*cSKK8mXtBfb|$eA4rppGm&1IQb3x#P|CC?&|DMcBS#v6WS*a*R?7% zfF$3l6&8P|;ifi4W51}M<38yV>U0{>pX$tSv9z7?+kptH@HD)>xcqDNiSO0FpY)0E zOuy*gMttJ;AOb2NQ9QC+#9N05>)_d{Tk<#yH~CxHe~SGZ*?)xnP3+&p{`Kq+vcG}- z@G3Kb1Sl>{6Z^92$KU&!@!j%e&_PEU_Jum(%uCVj3vAH*i7(@lTeJtGKJmTix2`O%J#6!dpS(F^^h=2l}Yp}@l`^0yS$AmnbP^7O33Dn^k zK9Bdb)ORHAv4TOcNX=uhy%J%zvNw zU2o~RThJy*^{*Dsh);aNkxjPz`^5L6-}=TP)9(|1Wykdm#J~6+7Uf=~=?EN@AEO+f zkk4uMi|^$TpY%k$(*?!%+gO48K|bbuc|Q4!ud(t=$mcBkg?z@?KV9keOGn5@TU%Ip zgM1o^RzCUE*4yP%#eN|ljs1T4p0030K6-)t34OlcHwXvSC;ghj5as~>GX@xADe_{0}- z5Kz?5QJ?r;^johjGW|aByY4Vj3Bf!d)fK8ABR=sZRMVMFzfb&p<@-2dC7#;WTk?p& zP5ia8AI_2*QNFtiq~|w%4cFPzr?bDLi9_TfeXRx36a8Dh^=h>ntn}>wP0GJ2_6OOo zu^-;DGTHvWK>qy7r{PU@`2^W7Hx^k7 zK7w#i`5W_zFYp8u@*DSwPvb}VyPfD$```WM;>u6bC%&`%i1It;6JMlTz-;<`;(NCL ztwpBaC%$L;ed3FB3z$v6PkgbyO{~Wg^|yOdk?GHapTLQ2w247~+npxsROV%q!Ty7E zGLgn2{A3~;$6`TiM}i2$>-iB$+Q{yR(6M(R9ql#jK~0o_=*{t7V^H6yXCl$`+LknS zR0LOx*d{y|Ds?@&CHN`;2%Be^)p6^mm3f!LwpxyvAj zgZ<(qIuDCD$T^S$hYCdmKb{sF8J7Wp}S(pDsYi+{mEDen_PsXJaaCLZ5E+u z+(_AFsbe*!o<11L4xYrm2#!BJ(31|QqRFfT#QqO+VP}4_yg#MaC~kjC4u5j{TXJ}U zZ{G8xTkB8(_C@1zj)?R}Lg|R!GZ5Zy;8cXfKwJo5Af8Ck?xNnV{!sFoG=9w_5_&8V z-)F_b`I$D1{gc|uBjfo=qa5CE+K9u6@XWT$vFYc?`>cEFKFIgYz3v+K^HXo&b5))9 z)XB0XcQTRe^0PMxSC&Qh#dBMtu#?EFXg!7=(sP@;(4r;K5*f%NF5eD?Oc7(~sdJy}M~+M5-P>jwunQES!Q+)m-7S^Bts`HgyT?K)}- z<==Wjy@-lhOki6-Y{Gu0EX`bdYHvwJP!8Co&cB?V^Dl44jxoAlFNNJ>@}4ouhnwRE zu;kv#5z$USYcoKll-^#My=Bkr2TRl+4SJmixf>e-(t9`c_DAD;Z{8kDBz)YbRl@VA ztn)g(PU4&W=kLw<`HZD1f7$avt(suj<#%4s1$iE%@ALT7`G<1poAA2>pGfiWfxSHg z(O7Tx)5Cf>iTmjW;xr&JdRMFS4@vUnSKlwl^G-|H|ElvK?dRF4^C11mL!AfNYha=z z*4(VlgS3|ybsnT$9y5C$qC7j<^`VPnZ3YK;miG8V1G7=4=Z=}`0iBwB-^TALmNvEL$=C-huN&8pA z@}cw{_VVE_kNh)FR+BsX%Zt)?*vrez>N{wDUpD2}|ID!dqgmJ9CJCtgtcGv!ngP!cEu35>G-`G}eA-wUii)_mzeCK|&%cJ+~DcAsSUKyx#$ z6WbT>fKmqSR4}fu(qXOD*R5Vn%9;jy<5dd!z0m`^iJ_3*GA;B{*az7QiZZ>K)(5tk zd$rQI1I)k;V07ab?cQ=~FL5pv_C_T!BN*jPd&rNH>(AtG_yY64J`Yqq`R{BI^owV&+=@ZXi&r)Xl zm(~6$$M+4cc}*E4o^{i#mACz$Xr1XimL$Pk8Hsj==jp(9*U56f@7J+t~k`)E-4lmwInlmv1TSlO|z!8`z&U#$nE{T{8y ziaX!1%_qLseV8LY@pCD$T;hDkQJ?tv&WAsOXoGm#K4i&5oPXQOekc8@_KWjUy9=b} zyx)M*Qin%>sJQ%X@QLrFPv@5s{S!X%5!7Zx{+`T(KlpxYf7933Tl%>7yw-wezhUiX zdY+-z{-&=ivBLk(1`DeFO)29V4}D2F=quW@dV)S}$VE@ENgIF`A)$339Iedn)FLKjrd$uJj%D?{lT^@Nr(GulfGoP$neWH319!-u6Y0Xyo(@YX1}+tv%`U?Sc)1 zjZN#PXG2(HsNZq%<`Z7q1X zdOf{dcb&QNfsV_iRS`A*H`b#Ph88i-qFGMZ6v~H(vLeBa-WS3C5=- z-_IoTUw+?9^7ldh94LYO3z@(4t=~}lRpipA_N(wX2b)m&{6M9@@i^(tDmP_P-%B{3 zsP?Pa=M&xa6<$5V>@Oeg^2k4vcCpkSrN6P)pPAL)l;8MrX7x4dzMr)^wb2*QdWvMk zNJYb;Seu_V_9EzihOlL{*RMoozdUAEe`EieQa&XCB>^P?O*_r{k7wRw=_yV(O-}!D z1Mi1<%}NW;ssHeM-ks8aOwWBk9XK^BgncarcDXq99~~IH7%8h(DE&v-=|3*=`TiN# zw-hqpU&8r6rTl@+klL{+ZQ>lwW_W z_OICcpWOGEpDHxf^{4#Ghx~n(Um~+#9!mcqb*IXyB%mZv1`8j6-q{;;k?tlWzznYQeUp_Gtp@mS+9`^MPtp)?)swqGgVffIs40t+8<({ z-<{e0Axb}Tq3wrI`Vo8mQTh?PJml}Q{JqXa(BGKnY2=qgY5L!c(Vy%{45Y$*{z=1J zP3s1!hF=zUzE^93_*N>Ftt6l%aIs0CT1s%;1D1Z~`Zbn*rm=Q%`%{j;!3rO^%7T;A z&$PA*R+K)b^!gZ*bq8*A?}xsHcBpLDZaQy|N+2;QMefVJ!U)^U|2W2^x+-RjB`93t=19kPsR*=^G*CB+`P9Z z6yCotl^BTk*592Jr()iU%TeQc?k9ndQE+&_-Wy8o*Lz}#p0s$sCDd=ELb@@SOr!@= zhMtM`8+3V$SNvXjrfhsl-=xkX4{X9zl zqx3&Y|C3kW;ng#gNq;cy`mmXre=6a6saS&cwTpo=L(nU;{k%%wWS<|NS$$Lal@GQ5 z#$G;VSiht-YkRfbm}8)mG}3Ty!c0OxW)b3W-;8;S7-k?6_-!xGH&B>_xso)F`HKVa z^JN+;L6}0+az3RtqX7Ku|Abd5SVeii)#A-X;x#k zwne)QVRnMEPQb007ioiQ2f~UtR%^PpQM(bg6?TKR9-NaYu>4!!FPWjFk?soDgSGGh zoX(H<`s?i{8gID(EG@Br#wHFf7CzwzlUaaf8u%T zn`W%N7b6XMbfIDVV4(LhFv6k!t`F`IMjF)PwV3UHgWT9Fw~~O8fRcccz_dwVWyiIR zGBLXHY!06B->DuZI9Zd$Sw1oT2(Qoh+%ju@#rzv4w?5;Ims{a)Ugf?%V_&oNJ|F)D zey^5re$^UJT)gwkx$(A_3|32-I{U|$@_5@zA~}IfwLED3(%W*;7dOA7?ia+oR&%qt z255Iu3+XL4>W%?$f$fpf|115!(*L8Kl6R`LCo-W--u>R{d`$cNjyfOHE)Vzpj$S?S zRQJCH_6H>#pSsH<|4g7Yh?xEIQTlj${ZaaOyF8RW-Y$srIRH*%WQu|eto3eV9x&et(nzF7C0ZO^kYgtru1WIsFZ#z9;=Te zlcLR@ygZbC%szkXt`AcBv9h^8u2kb!+x(cQPX+eRC7eH2`Z#-iRr)x)Jd{4pE{_@3 z$IaOO6|pl>?T;<>{$jO1)_Xs;D1~Z&tlAIlx*uAtS5WH}QsJ<bRzpAm&eJcD;ho zbBjF;Gk2bV+8-;v5fxMtP!doQC?^TjK4`6H_(afJckn*G*VXTOhMH?Eywi;5T+i@+ zULTP!&1yZv^sHx)&kNsSo_-a<=~yA0Op!kC>~PAn&E^?fDeYFAWpThf)56NhU04?p z1&-nPCHKAqXJ62XUKyOY)yZdY;ko^XY0mgnhnLt%peMjYWIXdyCeu_l7bdt5wa}1?rQ0ulES5 z`(g*8vEIGWcsQo+i&guD)q03x*N3R}5RU%Q_5NJ7Us&9ytJWvj%gfBJPdIPBxa{;T zYQ2KJyv*==g|aIjx$iSSZS2LhUOm4=X1_dURv(x9vQsW40VM$?0dEP=eXew#D6W(? z4Y^OTp5}E6Eq%*}UTZ1T3U6T0JMpF=Bi zt8u`HA$L0Z6NyYD8W;DeTK=5k#7l1?GFI*HRQeN_`OgyWN63xOz4pk6vPp3Ek4KgM zMCnhaMt?;0C8y5`M%U?A>sz5y>cq8D8@oD_EzQm0NGKjRV(HcTMqLVOo`98n+qye} zA%X-~(>c>6*FUNIR@Hs0>b_NV-)hQheT%z3DgR6swT2hN_(ADU?DOGDe{y>$l{7r- zlhU8q%Zt*V*yWM?KJ(MYUR>X2`6V*@<)QQ^*2qHHN&-p(W4!S? z3;NZMJhsFNe~#~0bLvM1S1SETZ}b3eZ_=jz{-#Ns$JvS9By_KP8j$V>?{(-$&i~!I zTe~xoGSZPmtd|RJ)}K?En7?@GO_aVw=}VNpB=3B63H3)xU!wFSdCOk``(Kp4ggU+g z^*tr5@9KU!$NWsjQ2G+3FY%n;Rr->N=f9P{#NM7LeaXb-Md?fIL~f5}s;PKuJJJpv)vtb=18E z`IEn2^6yY8ll5Nt+Yi3~%xv5?-l#Wnn^m&*3iQ-@W@(3}wL}~h8Y3fTkl?0RolmwNA5h#iNbwg~U#$6TkNsew zSae^!12c<=KN#1cq>Jlo^>wRPo4P!rnlq)KUow4ee<)KQJ^%duouN!~b1CNUJ>;88 z48(h}!!JZy(1x5W$yJm*^Q2Gd3zWV<=?m8C)Fxjz_uDFcfn$Ec^}Y|UvB^~TzXi6> zB^;kAeZj=@ze-*#$Ym$9!ME_CfaY%jmqL)uLAqG{Ob!#w4Pd|OF<<8B>^RY$x2|)A6omH z{$rJ;=eg&a$?1>&ZLt;pE{Fc;GCm(}!_^b(kJNoR({o>r+~1zx%W7IDdejhhKke7H z0;T{hjc~iy+j*aI}ldHv0Br$joOW{t*{%k z_28ULf$gL9{gS~&mFcc2a2yEt+*lgUL8sD!-kzGgo?MWqvmo zG+$i8??bh|!rp(%^JDo}oRrD@SP9F=%&xBxdVR6yV&>XQG18Dn7aGP726`_8BOL1Q z`rr;>q(MDii`o7=win1vzj7-HC`X%5vMP;)-T+#Y;x-&208rS@%gV} zU#(ad@o#UMcwK}@Qi0!#CGXF@G=H;yJd+#mddWcTr)_C&#y;BQW~|Xr`)U2`|1KN- zbb;-S#lOdXS$Ti1sX5~NF~xpXc^_?2^z-Q3sZGu4o1+c-M(OzC?19Ci<+b4S@uT$L zB=Mr;?NRutN9n)q^+)Ny z?eb9iZ@WBZSpTi`JGwa(j~LN?kxZfE!4i(olzzuvUuIUnQ-=FDre(dIyq~8NYgK3s zOB#^YLi9TJ(9%ffR{T!;dg5BUxw=B^!_B?ljQ6zvXP>$1f>yoI4j#Yzf6nK@TB~sW zd+TP6&Pd8gM-s7K98b1&C?4vMhRw}Dx%V;SfDzNz(dJwj=le#y7rH$wgtCe3{=z>0bYozE8AbO5bPiFK1TYC*~-#`wz-azoYbj_VS|i ze|CA~zR&!$v1KyeDdGH>{C$>RB2z7oxN)${i1%t4qd$3TD4p42#51WxG<#*u8H@?$ z<2iDbwSN6L+;w<{*$-z)O@L!R!SoJB<8dRUY4M&!igw;cae4{`>5Zmo`!e=bTgLo- z8xjU{AdxY|E?xOO{WdtqN>4IqrEdrOd)Z&d{ypqJ2b?NAZR{uckbejJkHAgxX=VQ& z_BXMAJ^U4T8rTn~GBYRv$~!W{DnBeiC|gNDN#KH!z{-y6ZCiS&e?ga}wO(WOXOm3` z;gV_diNCUAUE{R~-vuqtAP#@0{;n3!X~q})jQETXs6`nuUKsUBuj|%CSe#8ugjeFB z{&~2eIQbp*iQlz*moaFLT!^}+HQ_P)q;#nR z{I0hoZBbDAb$ZiSWcq#Lqx`NT0f_S3waYjdF+%3Jk=UT|9OcJRj!%^DBka%0%O^dG zb2`mjCU&Ag{=DkPiEE3?pOZfEyE^-A1tf^{)Zx+B7e~LrCqC*270Ij-`F+ACeio4( zqJEt8iBIh-t#A|NQ&*F(*u{;y! zV_E%k_nV4Lzfb(Go8!2?IMWsCvp&>!fWDdkKJi7m1g@K9MQND z;|Pxf(s(7=WXU7K{s{ZG!cR22*$-z~8PUEE7sy{%yOADAL z!muJU+xl#rI?7!-+H2^kP<&su2ePR2hmzN%jf|d2B=lG!F0SX$l8Iy&UEC2|t>bpD z7}!k2b<9m?hD_=*i>7-kW_;5)XOqsk8p3T~26ima9bOsiIvCP!H_z+rHBWuiv@0lX1x z)5%z9NKB_|;aC)lLoMU?bAf<<#FlR|-4u^clh|UDq>l~B z1W^F0;6KXoRk8m8_MgFT#P569{|NgB*&l&l#}i?Hw*O&2eo|%v2~_fLCzQNQNn{%y zmJ8JvDpL(xi>oiKKJn3Zn>8f+J>eLy9sw^^ct$wBJ#bU~IK_VPy?)XsJ)lmj5#JM| z1@b4p_q%qJHW}Ytj`1h82X%PHwpsbL18$Pn8TJR+Kgxc1%gPA(j2FnC*L>^v_TtK~ zcGJZ86Rd+hICy)scb}0-@5N{_HqdMAJvewxI+HpON`+GU=x}h!^d(YQg{h~LMmX9R zHBx$S!br!j%IJ6-j_5!bFo&CTw77((9UR=0+P904Y4DjFZU%utVQDSl!NKij>>Zg@ zG`_Dxkl5InU2$lxD`X5gc8qU6WTpQI-yb;2_YXSvUF{gXWEfqTwwdAgzG3;FWjM<4 zDTW(4+y;hU<@f)!*4m%-%|#Y`g5RHF_&CEy88$Hf)eKiMT*0u8;Ub3f8CEjX82;wl z7QZhue39WZrDjxv0T;S&r`F?^iiqYNKrc!J?a89v1DL54>db~AtXvHu>1 z2N)(9-p#O=;av>(Fl=XdE5q#!TNySpT+gtcVUXb)7%peHgkdeiYK9dIUwOuo=Q)Ni zG5i_B7Z{E)e2(FF89u}CG{YwuewpEy7@lPKX@;L-_;H5E86IQ!0K>x!hZ!DXm|+-a z7-87Ma2MytUiRO{u#Mpz{QE8J?_~d0_O~!>V%W%V4Z~Fob%x6rE@oK6u!>=T;cvfX z$@`ZK#~J>V;g1-8pWzvX-(mPP!>=(MVfa@JzrgS@hL14(B*Tv}{4m3#4DV<79)|Za z9Aubc7-QJSu$$o>3_BU_Vz`6hW`-LXZeZBJ@M?xD8LnVh$8ZtD`3x%=Y7Bq#G`9;3 zUu1Ze;qwf?$M9K(qYR&7_*I5aFg(TZafXjFe3;=0h97145W@!<9$|PN!+RJWV3=fh zH^Z-Tz3gTGT@3dyY-e~Y!|e=P88$Oq&#<0hkl`B`E@!xeVJ*XIh7}B7`6jmy3}0gS zGlnlP9Ao$#!|yVDhT&<3Pcr;6!!I#B$?($*KgICl439HB#_$1#hZzntJj5`=FwQW- zu!rGZhPN?nV|WWgd+(}z(ty z+iUSXH2!-Nk6&#_l844}rfK(w@BAJS^ASm(_=22(g8pzG{G*(&H(YOx7X^OZA6a~N z@_pChy%X-~u)+;8JjrmBVO6I+{T&&)&p4Tg#xcRGhZCvPKr%PWgn3SUoni7-Ph&tH zLkM#W9u5tpG4jTQY&xwc`fwd`Cgfnmir3nD?Gmj9b!#Yw$>vlv)Pwt&2hy0q?J@Ax zU|=qq=bl3{BOJdKZ^+zQu6Kba#3SN2va2yLpBU=ZtzYetH0-w0-+G$`kKb;=5r*0x z%iqLMs)<2)IA(5v)l8VM(9nld;T%|tLA&abkf zFnY5wh?pjZ79H%qqWsy`(PpwkGyTa_B59;zLwY)N05T7SQB2MCQ+Hw>Gita z83AMXA4m^Cjzb86sX%A|DL$HUCyphoULza}r4S({0CVw1Hc&VbKR~)vL~Z^iA&FvV zM-J(|(Y`(-McN2KDHloog6fG=?*8j0L}iuaP_`^nv9$_xgz^f7ngn7i)eywZeR_su zLHS9w#7s&kwniUH4CpvYXaHvk^@q~?5z5fhiGftulsWlKl4hdG7fO=sQQ$L%ZccZn z_al0I%Id~oh;Ais*7XI|uMS5-sEGQFdN7KQ-G;&K+gIPPpn6LLx)GAO87~y|;6?B? z1i{8)I^}K%f(xUTk$lLR26U1t( z@f6lp>oY9jtm4TQfsKx%`cFVs+a2E@PaKTv?E}cSena09G0@&^OQjMiTm0?jeQO!$ zfk-Q)=Zih&sX8H=B_h{KJe?TC$Tni~LL#K9G&wo1ocE;?LNH=ZRXmdng(kK-+x9mA%-(DGd~?eBpir)hBMC^bWAlMf0BJ z0=l*O#yvXxqWv^Nml@)UP4y5-!} z-B+gZm&9|Z7ZyGI$00Z6Iv&Vn@*PQQVu3l&HO5dd(1Y?tHy?&jA9^*qM{qAnatCyT zTeZ!&4{kj)hV^jYia#>9p{%!~UhG1Ny%{05ahRD+E=k4aKQXBT|d z&5t`lIc&BcX}s$L|9c@VO348Dib19sxTy6=;1`l?JyNt6?@2p70M04=7N@`*{x*|B zFT#g`wH2}D=1wVSE~hL+XI_fvyxzBM-Ar`u z#w@ow!}ESG5kKeA$@8L?`DIxsKF&G9Ipe$pFKVMX8JtG= z9q{{=tB5ZG`jj_hWcq_r+}5u-o639 zLvEtsCMTlnX7(Vw;89Lb6KG9jh(?KFVOZR=4nAZ`3&V>QVMq*?$dbOc(TO>8gBVV zblpr4;RTO!dd_)-7jY1c+GH+HFWyAlgtv#%51gQcC*pHL>fe%5xD#;umI2cu8IqCH zMfBZ_%;ypCsl9!kHtG>p#wR}O5LTESa8HI$qUFXX(RVX4pQnKP2*M3|gq87$&k-ry zINX!rlW4i|N%Y-}%%`?zf!3Bn*~OF35T6k#+;O-k!za;lHYf=JN<} zcVsZe!jsPspWRZpqi|0~oYy@*8L3y1V>C#7(82k?D@XB^M*gJ^>XiyODQ(WQ69R0>n-wCw<&uVRK|K6S{_%MhRR9nk6$rd0Vb2tLK$Bbm?B zE<6aniBC$~m=tEJ%E$@uZLq!gyu@=9?EGYX!@5cf$be({?Og)YJ>o#QS%2_A`W>UT~`Ve;jRz@02ifzr4rZBa4Vt(@7m}kW^ks%x!6Sqm>j=(+HF%i)qnl!c|Bex^OhnpeV zVtgR@J>?OW@a6Qif>zyopfj8xyuhJxR0F~avj=X{w;Yk)iQi;MG~C8*MAyxXAUqk$ zcaeVTe@=OXr?6xwU87Q%G09E8i+IT`;%$Z7D_&Vn`dtgO*7ssui)SK3I3%Y%Qn+Eb zNlquFcW!YI4L3OvT{m+Q;mJ7VH0BXr#6dislfqOTw#P5xrSP(xyyBJRRNJ*ciy*!x zN1TGT45^F{OW{t!J(+nIqUHAeK=j>=%x4pDYu^X`k4IP;pZE+StT21vo(!Kv%Z*Q> z?`C8^Bfvd{a7R4C%J@WMR0?+vZm&6f8INeVl{=#AW@P>z0q&k7n0vyL&yXCGQn(Xv zPezVJ%T10%-_6K;o&m1*e$4-Sgq87$&pLz^W(VAp;ge{&@k#XEjLfIjjro6s8}tY( z;}f4FQn+!rC&MSva^sWeyBV3!2H>{ck8gcE`3&(Hk-{B^dop|yEjK=izMGNx>;~>R zggfmKR>mhjYd?^SvlZ^i@JY1X_$2ynM&|Pmhjt3H^EvkC6W z@JY1X_$2ynM&`3Fg!zBai%9g{aOfTJc~S~DHc38-mK&c$-_6K;?f`Dr}hWAI2+)e44*{HjZdQQW@J7e0q#-I z>z3%d;m|wc^ARcBnMv|VwA}b4`ff(%^9*pCj^g_VPd-C@wn^cJ;hu~DBP5LghWj;>;_YCMgBGGq2F<%j%<5D>N zLzCl^Xu0u8^xcfi=NaG*gC5StatoS$M>wQIIxdAf19vgHDWdJBuOoikjLdg!81?@l z)PFqr4B@p&;kqR^;Zj_}$lsL|znhWyXaepC!r|-&x1hFngm*>?cUE#!K#EHknGcHJ z&B%Oo19!(CVf=$9pCP<%DO^%=6E4LijLZkcuZ)s_EP?iptsQ^cSiS1eHK{F6JbC+1 zlE2vTXH>r2&l1ET2z1dw8m9-|hC`ndxDY6e4b?cAU~f+}zISJW_GoW4G6<7MZNY&c z^~v5IlicsZriaWG7Erd5fG-KGY_y-Jqx?!9a^G3%^5CnGsz6EtN&-bm;L029?V9o{ zdC2X2smnuAg;yaf33y0gPURe}GKqBr^X6#tWLP@~`=T^$X-z<@X{=7}YpK-M{VBfD zyEnkLG^cZ36VP7MJ};S#L&UW-pe;>Znti{pGN4ta0%jZ;Ufa|_}zkCyU^XKDRhZTXzV+NF@u-9gL& zA&O(58+5y=|H7+??zY;hhZV@j|KKtL@I+7D*`W74OIkw9;m(}u)(8h>9fr;2iONZP0Oba-5il@E3s9L9egWqP zG?bF=ISdz4{(h3)Xd0E%=O|t+_^$vzp~!27512X85;$s;10^6wgX@7PcNFJ+R-9%y zQ4TMit6kc&Fj?De(c1oqN$V{DyYU2SP98LAZ4P|GOq47~GoHl(ZSk?%q*-^4q3(#X z;G`$sv)n{lvrzEkNc*!EKR*EYvB^*C1Zfv>IMTH2&&@Pl0k9emzYEUd23s^{FG%}oZnsSMurX;X�!|?){KE}eeEEu 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 literal 0 HcmV?d00001 diff --git a/bin/plugins/video_source/VideoSourceDirectShow.dll b/bin/plugins/video_source/VideoSourceDirectShow.dll new file mode 100644 index 0000000000000000000000000000000000000000..e4561e9dc72fe3fd28b47d95f7426d979508e3db GIT binary patch literal 392192 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 bfis}K?*&8>zM& literal 0 HcmV?d00001 diff --git a/bin/wxModularHost.exe b/bin/wxModularHost.exe new file mode 100644 index 0000000000000000000000000000000000000000..5b4e905b7efb8a2dbfef90da2446266b31ddf52e GIT binary patch literal 342016 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 yH|ayqF�Zf7p(CFas?~7a{ZSkI_@IqL<`A?+DSAO<4kE36v#JmcVaO0{;(gmXo3Y literal 0 HcmV?d00001 diff --git a/bin/wxNonGuiPluginBase.dll b/bin/wxNonGuiPluginBase.dll new file mode 100644 index 0000000000000000000000000000000000000000..8fd96cdd6e1225890a6941202150a0f47eb3c15f GIT binary patch literal 66560 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 K)t`Tn1pYs?X`hGy literal 0 HcmV?d00001 diff --git a/build/CMakeLists.txt b/build/CMakeLists.txt deleted file mode 100644 index 8eb0f4f..0000000 --- a/build/CMakeLists.txt +++ /dev/null @@ -1,123 +0,0 @@ -cmake_minimum_required(VERSION 2.6.0) - -include(PCHSupport.cmake) - -# We will generate both Debug and Release project files at the same time -# for Windows and OS X -if(WIN32 OR APPLE) - set(CMAKE_CONFIGURATION_TYPES "Debug;Release" CACHE STRING "" FORCE) - set(LIB_SUFFIX "") -endif(WIN32 OR APPLE) - -# For Linux we will need to execute CMake twice in order to generate -# Debug and Release versions of Makefiles -if(UNIX AND NOT APPLE) - set(LINUX ON) - set(LIB_SUFFIX /${CMAKE_BUILD_TYPE}) -endif(UNIX AND NOT APPLE) - -set(PROJECT_NAME wxModularHost) -project(${PROJECT_NAME}) - -# If there are any additional CMake modules (e.g. module which searches -# for OpenCV or for DirectShow libs), then CMake should start searching -# for them in current folder -set(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}) - -if(APPLE) - set(OS_BASE_NAME Mac) - set(CMAKE_OSX_SYSROOT "macosx10.8") -endif(APPLE) -if(LINUX) - set(OS_BASE_NAME Linux) -endif(LINUX) -if(WIN32) - set(OS_BASE_NAME Win) -endif(WIN32) - -# Here we specify the list of wxWidgets libs which we will use in our project -set(wxWidgets_USE_LIBS base core adv aui net gl xml propgrid html) - -# Here we specify that we need DLL version of wxWidgets libs and dynamic CRT -# This is a MUST for applications with plugins. Both app and DLL plugin MUST -# use the same instance of wxWidgets and the same event loop. -set(BUILD_SHARED_LIBS 1) - -# Find wxWidgets library on current PC -# You should have %WXWIN% environment variable which should point to the -# directory where wxWidgets source code is placed. -# wxWidgets libs MUST be compiled for both Debug and Release versions -find_package(wxWidgets REQUIRED) - - -# For some reason CMake generates wrong list of definitions. -# Each item should start with /D but it does not. -# We need to fix that manually -set(wxWidgets_DEFINITIONS_TEMP) -foreach(DEFINITION ${wxWidgets_DEFINITIONS}) - - if(NOT ${DEFINITION} MATCHES "/D.*") - set(DEFINITION "/D${DEFINITION}") - endif() - set(wxWidgets_DEFINITIONS_TEMP ${wxWidgets_DEFINITIONS_TEMP} - ${DEFINITION}) -endforeach(${DEFINITION}) -set(wxWidgets_DEFINITIONS ${wxWidgets_DEFINITIONS_TEMP}) - -# Here we add some definitions which prevent Visual Studio from -# generating tons of warnings about unsecure function calls. -# See http://msdn.microsoft.com/en-us/library/ttcz0bys.aspx -if(WIN32) - set(wxWidgets_DEFINITIONS ${wxWidgets_DEFINITIONS}; - /D_CRT_SECURE_NO_DEPRECATE; - /D_CRT_NONSTDC_NO_DEPRECATE; - /D_UNICODE) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP /wd4996") -endif(WIN32) - -# Since we are going to use wxWidgets in all subrojects, -# it's OK to create the variable which will contain -# common preprocessor definitions. This variable will be -# used in subprojects. -set(PREPROCESSOR_DEFINITIONS ${PREPROCESSOR_DEFINITIONS}; - ${wxWidgets_DEFINITIONS}) - -# Variable which points to root folder of our source code -set(PROJECT_ROOT_DIR ${PROJECT_SOURCE_DIR}/..) - -# If any ThirdParty libraries are going to be -# used in our project then it would be better to put -# them into separate subfolder. We will create -# the variable which points to this subfolder. -set(THIRD_PARTY_DIR ${PROJECT_ROOT_DIR}/ThirdParty) - -set(BASE_INCLUDE_DIRECTORIES ${PROJECT_ROOT_DIR}/include) - -# Add wxWidgets include paths to the list of -# include directories for all projects. -include_directories(${wxWidgets_INCLUDE_DIRS}) - -if(WIN32 OR LINUX) - set(CMAKE_CXX_FLAGS_DEBUG - "${CMAKE_CXX_FLAGS_DEBUG} - /D__WXDEBUG__=1" ) -endif(WIN32 OR LINUX) - -# Now we can include all our subprojects. -# CMake will generate project files for them -add_subdirectory (../wxModularHost - ../../wxModularHost/${OS_BASE_NAME}${LIB_SUFFIX}) -add_subdirectory (../wxModularCore - ../../wxModularCore/${OS_BASE_NAME}${LIB_SUFFIX}) - -add_subdirectory (../wxNonGuiPluginBase - ../../wxNonGuiPluginBase/${OS_BASE_NAME}${LIB_SUFFIX}) -add_subdirectory (../SampleNonGuiPlugin - ../../SampleNonGuiPlugin/${OS_BASE_NAME}${LIB_SUFFIX}) - -add_subdirectory (../wxGuiPluginBase - ../../wxGuiPluginBase/${OS_BASE_NAME}${LIB_SUFFIX}) -add_subdirectory (../SampleGuiPlugin1 - ../../SampleGuiPlugin1/${OS_BASE_NAME}${LIB_SUFFIX}) -add_subdirectory (../SampleGuiPlugin2 - ../../SampleGuiPlugin2/${OS_BASE_NAME}${LIB_SUFFIX}) diff --git a/build/FindPackageHandleStandardArgs.cmake b/build/FindPackageHandleStandardArgs.cmake deleted file mode 100644 index c6db433..0000000 --- a/build/FindPackageHandleStandardArgs.cmake +++ /dev/null @@ -1,614 +0,0 @@ -# Distributed under the OSI-approved BSD 3-Clause License. See accompanying -# file Copyright.txt or https://cmake.org/licensing for details. - -#[=======================================================================[.rst: -FindPackageHandleStandardArgs ------------------------------ - -This module provides functions intended to be used in :ref:`Find Modules` -implementing :command:`find_package()` calls. - -.. command:: find_package_handle_standard_args - - This command handles the ``REQUIRED``, ``QUIET`` and version-related - arguments of :command:`find_package`. It also sets the - ``_FOUND`` variable. The package is considered found if all - variables listed contain valid results, e.g. valid filepaths. - - There are two signatures: - - .. code-block:: cmake - - find_package_handle_standard_args( - (DEFAULT_MSG|) - ... - ) - - find_package_handle_standard_args( - [FOUND_VAR ] - [REQUIRED_VARS ...] - [VERSION_VAR ] - [HANDLE_VERSION_RANGE] - [HANDLE_COMPONENTS] - [CONFIG_MODE] - [NAME_MISMATCHED] - [REASON_FAILURE_MESSAGE ] - [FAIL_MESSAGE ] - ) - - The ``_FOUND`` variable will be set to ``TRUE`` if all - the variables ``...`` are valid and any optional - constraints are satisfied, and ``FALSE`` otherwise. A success or - failure message may be displayed based on the results and on - whether the ``REQUIRED`` and/or ``QUIET`` option was given to - the :command:`find_package` call. - - The options are: - - ``(DEFAULT_MSG|)`` - In the simple signature this specifies the failure message. - Use ``DEFAULT_MSG`` to ask for a default message to be computed - (recommended). Not valid in the full signature. - - ``FOUND_VAR `` - .. deprecated:: 3.3 - - Specifies either ``_FOUND`` or - ``_FOUND`` as the result variable. This exists only - for compatibility with older versions of CMake and is now ignored. - Result variables of both names are always set for compatibility. - - ``REQUIRED_VARS ...`` - Specify the variables which are required for this package. - These may be named in the generated failure message asking the - user to set the missing variable values. Therefore these should - typically be cache entries such as ``FOO_LIBRARY`` and not output - variables like ``FOO_LIBRARIES``. - - .. versionchanged:: 3.18 - If ``HANDLE_COMPONENTS`` is specified, this option can be omitted. - - ``VERSION_VAR `` - Specify the name of a variable that holds the version of the package - that has been found. This version will be checked against the - (potentially) specified required version given to the - :command:`find_package` call, including its ``EXACT`` option. - The default messages include information about the required - version and the version which has been actually found, both - if the version is ok or not. - - ``HANDLE_VERSION_RANGE`` - .. versionadded:: 3.19 - - Enable handling of a version range, if one is specified. Without this - option, a developer warning will be displayed if a version range is - specified. - - ``HANDLE_COMPONENTS`` - Enable handling of package components. In this case, the command - will report which components have been found and which are missing, - and the ``_FOUND`` variable will be set to ``FALSE`` - if any of the required components (i.e. not the ones listed after - the ``OPTIONAL_COMPONENTS`` option of :command:`find_package`) are - missing. - - ``CONFIG_MODE`` - Specify that the calling find module is a wrapper around a - call to ``find_package( NO_MODULE)``. This implies - a ``VERSION_VAR`` value of ``_VERSION``. The command - will automatically check whether the package configuration file - was found. - - ``REASON_FAILURE_MESSAGE `` - .. versionadded:: 3.16 - - Specify a custom message of the reason for the failure which will be - appended to the default generated message. - - ``FAIL_MESSAGE `` - Specify a custom failure message instead of using the default - generated message. Not recommended. - - ``NAME_MISMATCHED`` - .. versionadded:: 3.17 - - Indicate that the ```` does not match - ``${CMAKE_FIND_PACKAGE_NAME}``. This is usually a mistake and raises a - warning, but it may be intentional for usage of the command for components - of a larger package. - -Example for the simple signature: - -.. code-block:: cmake - - find_package_handle_standard_args(LibXml2 DEFAULT_MSG - LIBXML2_LIBRARY LIBXML2_INCLUDE_DIR) - -The ``LibXml2`` package is considered to be found if both -``LIBXML2_LIBRARY`` and ``LIBXML2_INCLUDE_DIR`` are valid. -Then also ``LibXml2_FOUND`` is set to ``TRUE``. If it is not found -and ``REQUIRED`` was used, it fails with a -:command:`message(FATAL_ERROR)`, independent whether ``QUIET`` was -used or not. If it is found, success will be reported, including -the content of the first ````. On repeated CMake runs, -the same message will not be printed again. - -.. note:: - - If ```` does not match ``CMAKE_FIND_PACKAGE_NAME`` for the - calling module, a warning that there is a mismatch is given. The - ``FPHSA_NAME_MISMATCHED`` variable may be set to bypass the warning if using - the old signature and the ``NAME_MISMATCHED`` argument using the new - signature. To avoid forcing the caller to require newer versions of CMake for - usage, the variable's value will be used if defined when the - ``NAME_MISMATCHED`` argument is not passed for the new signature (but using - both is an error).. - -Example for the full signature: - -.. code-block:: cmake - - find_package_handle_standard_args(LibArchive - REQUIRED_VARS LibArchive_LIBRARY LibArchive_INCLUDE_DIR - VERSION_VAR LibArchive_VERSION) - -In this case, the ``LibArchive`` package is considered to be found if -both ``LibArchive_LIBRARY`` and ``LibArchive_INCLUDE_DIR`` are valid. -Also the version of ``LibArchive`` will be checked by using the version -contained in ``LibArchive_VERSION``. Since no ``FAIL_MESSAGE`` is given, -the default messages will be printed. - -Another example for the full signature: - -.. code-block:: cmake - - find_package(Automoc4 QUIET NO_MODULE HINTS /opt/automoc4) - find_package_handle_standard_args(Automoc4 CONFIG_MODE) - -In this case, a ``FindAutmoc4.cmake`` module wraps a call to -``find_package(Automoc4 NO_MODULE)`` and adds an additional search -directory for ``automoc4``. Then the call to -``find_package_handle_standard_args`` produces a proper success/failure -message. - -.. command:: find_package_check_version - - .. versionadded:: 3.19 - - Helper function which can be used to check if a ```` is valid - against version-related arguments of :command:`find_package`. - - .. code-block:: cmake - - find_package_check_version( - [HANDLE_VERSION_RANGE] - [RESULT_MESSAGE_VARIABLE ] - ) - - The ```` will hold a boolean value giving the result of the check. - - The options are: - - ``HANDLE_VERSION_RANGE`` - Enable handling of a version range, if one is specified. Without this - option, a developer warning will be displayed if a version range is - specified. - - ``RESULT_MESSAGE_VARIABLE `` - Specify a variable to get back a message describing the result of the check. - -Example for the usage: - -.. code-block:: cmake - - find_package_check_version(1.2.3 result HANDLE_VERSION_RANGE - RESULT_MESSAGE_VARIABLE reason) - if (result) - message (STATUS "${reason}") - else() - message (FATAL_ERROR "${reason}") - endif() -#]=======================================================================] - -include(${CMAKE_CURRENT_LIST_DIR}/FindPackageMessage.cmake) - - -cmake_policy(PUSH) -# numbers and boolean constants -cmake_policy (SET CMP0012 NEW) -# IN_LIST operator -cmake_policy (SET CMP0057 NEW) - - -# internal helper macro -macro(_FPHSA_FAILURE_MESSAGE _msg) - set (__msg "${_msg}") - if (FPHSA_REASON_FAILURE_MESSAGE) - string(APPEND __msg "\n Reason given by package: ${FPHSA_REASON_FAILURE_MESSAGE}\n") - elseif(NOT DEFINED PROJECT_NAME) - string(APPEND __msg "\n" - "Hint: The project() command has not yet been called. It sets up system-specific search paths.") - endif() - if (${_NAME}_FIND_REQUIRED) - message(FATAL_ERROR "${__msg}") - else () - if (NOT ${_NAME}_FIND_QUIETLY) - message(STATUS "${__msg}") - endif () - endif () -endmacro() - - -# internal helper macro to generate the failure message when used in CONFIG_MODE: -macro(_FPHSA_HANDLE_FAILURE_CONFIG_MODE) - # _CONFIG is set, but FOUND is false, this means that some other of the REQUIRED_VARS was not found: - if(${_NAME}_CONFIG) - _FPHSA_FAILURE_MESSAGE("${FPHSA_FAIL_MESSAGE}: missing:${MISSING_VARS} (found ${${_NAME}_CONFIG} ${VERSION_MSG})") - else() - # If _CONSIDERED_CONFIGS is set, the config-file has been found, but no suitable version. - # List them all in the error message: - if(${_NAME}_CONSIDERED_CONFIGS) - set(configsText "") - list(LENGTH ${_NAME}_CONSIDERED_CONFIGS configsCount) - math(EXPR configsCount "${configsCount} - 1") - foreach(currentConfigIndex RANGE ${configsCount}) - list(GET ${_NAME}_CONSIDERED_CONFIGS ${currentConfigIndex} filename) - list(GET ${_NAME}_CONSIDERED_VERSIONS ${currentConfigIndex} version) - string(APPEND configsText "\n ${filename} (version ${version})") - endforeach() - if (${_NAME}_NOT_FOUND_MESSAGE) - if (FPHSA_REASON_FAILURE_MESSAGE) - string(PREPEND FPHSA_REASON_FAILURE_MESSAGE "${${_NAME}_NOT_FOUND_MESSAGE}\n ") - else() - set(FPHSA_REASON_FAILURE_MESSAGE "${${_NAME}_NOT_FOUND_MESSAGE}") - endif() - else() - string(APPEND configsText "\n") - endif() - _FPHSA_FAILURE_MESSAGE("${FPHSA_FAIL_MESSAGE} ${VERSION_MSG}, checked the following files:${configsText}") - - else() - # Simple case: No Config-file was found at all: - _FPHSA_FAILURE_MESSAGE("${FPHSA_FAIL_MESSAGE}: found neither ${_NAME}Config.cmake nor ${_NAME_LOWER}-config.cmake ${VERSION_MSG}") - endif() - endif() -endmacro() - - -function(FIND_PACKAGE_CHECK_VERSION version result) - cmake_parse_arguments (PARSE_ARGV 2 FPCV "HANDLE_VERSION_RANGE;NO_AUTHOR_WARNING_VERSION_RANGE" "RESULT_MESSAGE_VARIABLE" "") - - if (FPCV_UNPARSED_ARGUMENTS) - message (FATAL_ERROR "find_package_check_version(): ${FPCV_UNPARSED_ARGUMENTS}: unexpected arguments") - endif() - if ("RESULT_MESSAGE_VARIABLE" IN_LIST FPCV_KEYWORDS_MISSING_VALUES) - message (FATAL_ERROR "find_package_check_version(): RESULT_MESSAGE_VARIABLE expects an argument") - endif() - - set (${result} FALSE PARENT_SCOPE) - if (FPCV_RESULT_MESSAGE_VARIABLE) - unset (${FPCV_RESULT_MESSAGE_VARIABLE} PARENT_SCOPE) - endif() - - if (_CMAKE_FPHSA_PACKAGE_NAME) - set (package "${_CMAKE_FPHSA_PACKAGE_NAME}") - elseif (CMAKE_FIND_PACKAGE_NAME) - set (package "${CMAKE_FIND_PACKAGE_NAME}") - else() - message (FATAL_ERROR "find_package_check_version(): Cannot be used outside a 'Find Module'") - endif() - - if (NOT FPCV_NO_AUTHOR_WARNING_VERSION_RANGE - AND ${package}_FIND_VERSION_RANGE AND NOT FPCV_HANDLE_VERSION_RANGE) - message(AUTHOR_WARNING - "`find_package()` specify a version range but the option " - "HANDLE_VERSION_RANGE` is not passed to `find_package_check_version()`. " - "Only the lower endpoint of the range will be used.") - endif() - - - set (version_ok FALSE) - unset (version_msg) - - if (FPCV_HANDLE_VERSION_RANGE AND ${package}_FIND_VERSION_RANGE) - if ((${package}_FIND_VERSION_RANGE_MIN STREQUAL "INCLUDE" - AND version VERSION_GREATER_EQUAL ${package}_FIND_VERSION_MIN) - AND ((${package}_FIND_VERSION_RANGE_MAX STREQUAL "INCLUDE" - AND version VERSION_LESS_EQUAL ${package}_FIND_VERSION_MAX) - OR (${package}_FIND_VERSION_RANGE_MAX STREQUAL "EXCLUDE" - AND version VERSION_LESS ${package}_FIND_VERSION_MAX))) - set (version_ok TRUE) - set(version_msg "(found suitable version \"${version}\", required range is \"${${package}_FIND_VERSION_RANGE}\")") - else() - set(version_msg "Found unsuitable version \"${version}\", required range is \"${${package}_FIND_VERSION_RANGE}\"") - endif() - elseif (DEFINED ${package}_FIND_VERSION) - if(${package}_FIND_VERSION_EXACT) # exact version required - # count the dots in the version string - string(REGEX REPLACE "[^.]" "" version_dots "${version}") - # add one dot because there is one dot more than there are components - string(LENGTH "${version_dots}." version_dots) - if (version_dots GREATER ${package}_FIND_VERSION_COUNT) - # Because of the C++ implementation of find_package() ${package}_FIND_VERSION_COUNT - # is at most 4 here. Therefore a simple lookup table is used. - if (${package}_FIND_VERSION_COUNT EQUAL 1) - set(version_regex "[^.]*") - elseif (${package}_FIND_VERSION_COUNT EQUAL 2) - set(version_regex "[^.]*\\.[^.]*") - elseif (${package}_FIND_VERSION_COUNT EQUAL 3) - set(version_regex "[^.]*\\.[^.]*\\.[^.]*") - else() - set(version_regex "[^.]*\\.[^.]*\\.[^.]*\\.[^.]*") - endif() - string(REGEX REPLACE "^(${version_regex})\\..*" "\\1" version_head "${version}") - if (NOT ${package}_FIND_VERSION VERSION_EQUAL version_head) - set(version_msg "Found unsuitable version \"${version}\", but required is exact version \"${${package}_FIND_VERSION}\"") - else () - set(version_ok TRUE) - set(version_msg "(found suitable exact version \"${version}\")") - endif () - else () - if (NOT ${package}_FIND_VERSION VERSION_EQUAL version) - set(version_msg "Found unsuitable version \"${version}\", but required is exact version \"${${package}_FIND_VERSION}\"") - else () - set(version_ok TRUE) - set(version_msg "(found suitable exact version \"${version}\")") - endif () - endif () - else() # minimum version - if (${package}_FIND_VERSION VERSION_GREATER version) - set(version_msg "Found unsuitable version \"${version}\", but required is at least \"${${package}_FIND_VERSION}\"") - else() - set(version_ok TRUE) - set(version_msg "(found suitable version \"${version}\", minimum required is \"${${package}_FIND_VERSION}\")") - endif() - endif() - else () - set(version_ok TRUE) - set(version_msg "(found version \"${version}\")") - endif() - - set (${result} ${version_ok} PARENT_SCOPE) - if (FPCV_RESULT_MESSAGE_VARIABLE) - set (${FPCV_RESULT_MESSAGE_VARIABLE} "${version_msg}" PARENT_SCOPE) - endif() -endfunction() - - -function(FIND_PACKAGE_HANDLE_STANDARD_ARGS _NAME _FIRST_ARG) - - # Set up the arguments for `cmake_parse_arguments`. - set(options CONFIG_MODE HANDLE_COMPONENTS NAME_MISMATCHED HANDLE_VERSION_RANGE) - set(oneValueArgs FAIL_MESSAGE REASON_FAILURE_MESSAGE VERSION_VAR FOUND_VAR) - set(multiValueArgs REQUIRED_VARS) - - # Check whether we are in 'simple' or 'extended' mode: - set(_KEYWORDS_FOR_EXTENDED_MODE ${options} ${oneValueArgs} ${multiValueArgs} ) - list(FIND _KEYWORDS_FOR_EXTENDED_MODE "${_FIRST_ARG}" INDEX) - - unset(FPHSA_NAME_MISMATCHED_override) - if (DEFINED FPHSA_NAME_MISMATCHED) - # If the variable NAME_MISMATCHED variable is set, error if it is passed as - # an argument. The former is for old signatures, the latter is for new - # signatures. - list(FIND ARGN "NAME_MISMATCHED" name_mismatched_idx) - if (NOT name_mismatched_idx EQUAL "-1") - message(FATAL_ERROR - "The `NAME_MISMATCHED` argument may only be specified by the argument or " - "the variable, not both.") - endif () - - # But use the variable if it is not an argument to avoid forcing minimum - # CMake version bumps for calling modules. - set(FPHSA_NAME_MISMATCHED_override "${FPHSA_NAME_MISMATCHED}") - endif () - - if(${INDEX} EQUAL -1) - set(FPHSA_FAIL_MESSAGE ${_FIRST_ARG}) - set(FPHSA_REQUIRED_VARS ${ARGN}) - set(FPHSA_VERSION_VAR) - else() - cmake_parse_arguments(FPHSA "${options}" "${oneValueArgs}" "${multiValueArgs}" ${_FIRST_ARG} ${ARGN}) - - if(FPHSA_UNPARSED_ARGUMENTS) - message(FATAL_ERROR "Unknown keywords given to FIND_PACKAGE_HANDLE_STANDARD_ARGS(): \"${FPHSA_UNPARSED_ARGUMENTS}\"") - endif() - - if(NOT FPHSA_FAIL_MESSAGE) - set(FPHSA_FAIL_MESSAGE "DEFAULT_MSG") - endif() - - # In config-mode, we rely on the variable _CONFIG, which is set by find_package() - # when it successfully found the config-file, including version checking: - if(FPHSA_CONFIG_MODE) - list(INSERT FPHSA_REQUIRED_VARS 0 ${_NAME}_CONFIG) - list(REMOVE_DUPLICATES FPHSA_REQUIRED_VARS) - set(FPHSA_VERSION_VAR ${_NAME}_VERSION) - endif() - - if(NOT FPHSA_REQUIRED_VARS AND NOT FPHSA_HANDLE_COMPONENTS) - message(FATAL_ERROR "No REQUIRED_VARS specified for FIND_PACKAGE_HANDLE_STANDARD_ARGS()") - endif() - endif() - - if (DEFINED FPHSA_NAME_MISMATCHED_override) - set(FPHSA_NAME_MISMATCHED "${FPHSA_NAME_MISMATCHED_override}") - endif () - - if (DEFINED CMAKE_FIND_PACKAGE_NAME - AND NOT FPHSA_NAME_MISMATCHED - AND NOT _NAME STREQUAL CMAKE_FIND_PACKAGE_NAME) - message(AUTHOR_WARNING - "The package name passed to `find_package_handle_standard_args` " - "(${_NAME}) does not match the name of the calling package " - "(${CMAKE_FIND_PACKAGE_NAME}). This can lead to problems in calling " - "code that expects `find_package` result variables (e.g., `_FOUND`) " - "to follow a certain pattern.") - endif () - - if (${_NAME}_FIND_VERSION_RANGE AND NOT FPHSA_HANDLE_VERSION_RANGE) - message(AUTHOR_WARNING - "`find_package()` specify a version range but the module ${_NAME} does " - "not support this capability. Only the lower endpoint of the range " - "will be used.") - endif() - - # to propagate package name to FIND_PACKAGE_CHECK_VERSION - set(_CMAKE_FPHSA_PACKAGE_NAME "${_NAME}") - - # now that we collected all arguments, process them - - if("x${FPHSA_FAIL_MESSAGE}" STREQUAL "xDEFAULT_MSG") - set(FPHSA_FAIL_MESSAGE "Could NOT find ${_NAME}") - endif() - - if (FPHSA_REQUIRED_VARS) - list(GET FPHSA_REQUIRED_VARS 0 _FIRST_REQUIRED_VAR) - endif() - - string(TOUPPER ${_NAME} _NAME_UPPER) - string(TOLOWER ${_NAME} _NAME_LOWER) - - if(FPHSA_FOUND_VAR) - set(_FOUND_VAR_UPPER ${_NAME_UPPER}_FOUND) - set(_FOUND_VAR_MIXED ${_NAME}_FOUND) - if(FPHSA_FOUND_VAR STREQUAL _FOUND_VAR_MIXED OR FPHSA_FOUND_VAR STREQUAL _FOUND_VAR_UPPER) - set(_FOUND_VAR ${FPHSA_FOUND_VAR}) - else() - message(FATAL_ERROR "The argument for FOUND_VAR is \"${FPHSA_FOUND_VAR}\", but only \"${_FOUND_VAR_MIXED}\" and \"${_FOUND_VAR_UPPER}\" are valid names.") - endif() - else() - set(_FOUND_VAR ${_NAME_UPPER}_FOUND) - endif() - - # collect all variables which were not found, so they can be printed, so the - # user knows better what went wrong (#6375) - set(MISSING_VARS "") - set(DETAILS "") - # check if all passed variables are valid - set(FPHSA_FOUND_${_NAME} TRUE) - foreach(_CURRENT_VAR ${FPHSA_REQUIRED_VARS}) - if(NOT ${_CURRENT_VAR}) - set(FPHSA_FOUND_${_NAME} FALSE) - string(APPEND MISSING_VARS " ${_CURRENT_VAR}") - else() - string(APPEND DETAILS "[${${_CURRENT_VAR}}]") - endif() - endforeach() - if(FPHSA_FOUND_${_NAME}) - set(${_NAME}_FOUND TRUE) - set(${_NAME_UPPER}_FOUND TRUE) - else() - set(${_NAME}_FOUND FALSE) - set(${_NAME_UPPER}_FOUND FALSE) - endif() - - # component handling - unset(FOUND_COMPONENTS_MSG) - unset(MISSING_COMPONENTS_MSG) - - if(FPHSA_HANDLE_COMPONENTS) - foreach(comp ${${_NAME}_FIND_COMPONENTS}) - if(${_NAME}_${comp}_FOUND) - - if(NOT DEFINED FOUND_COMPONENTS_MSG) - set(FOUND_COMPONENTS_MSG "found components:") - endif() - string(APPEND FOUND_COMPONENTS_MSG " ${comp}") - - else() - - if(NOT DEFINED MISSING_COMPONENTS_MSG) - set(MISSING_COMPONENTS_MSG "missing components:") - endif() - string(APPEND MISSING_COMPONENTS_MSG " ${comp}") - - if(${_NAME}_FIND_REQUIRED_${comp}) - set(${_NAME}_FOUND FALSE) - string(APPEND MISSING_VARS " ${comp}") - endif() - - endif() - endforeach() - set(COMPONENT_MSG "${FOUND_COMPONENTS_MSG} ${MISSING_COMPONENTS_MSG}") - string(APPEND DETAILS "[c${COMPONENT_MSG}]") - endif() - - # version handling: - set(VERSION_MSG "") - set(VERSION_OK TRUE) - - # check that the version variable is not empty to avoid emitting a misleading - # message (i.e. `Found unsuitable version ""`) - if (DEFINED ${_NAME}_FIND_VERSION) - if(DEFINED ${FPHSA_VERSION_VAR}) - if(NOT "${${FPHSA_VERSION_VAR}}" STREQUAL "") - set(_FOUND_VERSION ${${FPHSA_VERSION_VAR}}) - if (FPHSA_HANDLE_VERSION_RANGE) - set (FPCV_HANDLE_VERSION_RANGE HANDLE_VERSION_RANGE) - else() - set(FPCV_HANDLE_VERSION_RANGE NO_AUTHOR_WARNING_VERSION_RANGE) - endif() - find_package_check_version ("${_FOUND_VERSION}" VERSION_OK RESULT_MESSAGE_VARIABLE VERSION_MSG - ${FPCV_HANDLE_VERSION_RANGE}) - else() - set(VERSION_OK FALSE) - endif() - endif() - if("${${FPHSA_VERSION_VAR}}" STREQUAL "") - # if the package was not found, but a version was given, add that to the output: - if(${_NAME}_FIND_VERSION_EXACT) - set(VERSION_MSG "(Required is exact version \"${${_NAME}_FIND_VERSION}\")") - elseif (FPHSA_HANDLE_VERSION_RANGE AND ${_NAME}_FIND_VERSION_RANGE) - set(VERSION_MSG "(Required is version range \"${${_NAME}_FIND_VERSION_RANGE}\")") - else() - set(VERSION_MSG "(Required is at least version \"${${_NAME}_FIND_VERSION}\")") - endif() - endif() - else () - # Check with DEFINED as the found version may be 0. - if(DEFINED ${FPHSA_VERSION_VAR}) - set(VERSION_MSG "(found version \"${${FPHSA_VERSION_VAR}}\")") - endif() - endif () - - if(VERSION_OK) - string(APPEND DETAILS "[v${${FPHSA_VERSION_VAR}}(${${_NAME}_FIND_VERSION})]") - else() - set(${_NAME}_FOUND FALSE) - endif() - - - # print the result: - if (${_NAME}_FOUND) - FIND_PACKAGE_MESSAGE(${_NAME} "Found ${_NAME}: ${${_FIRST_REQUIRED_VAR}} ${VERSION_MSG} ${COMPONENT_MSG}" "${DETAILS}") - else () - - if(FPHSA_CONFIG_MODE) - _FPHSA_HANDLE_FAILURE_CONFIG_MODE() - else() - if(NOT VERSION_OK) - set(RESULT_MSG) - if (_FIRST_REQUIRED_VAR) - string (APPEND RESULT_MSG "found ${${_FIRST_REQUIRED_VAR}}") - endif() - if (COMPONENT_MSG) - if (RESULT_MSG) - string (APPEND RESULT_MSG ", ") - endif() - string (APPEND RESULT_MSG "${FOUND_COMPONENTS_MSG}") - endif() - _FPHSA_FAILURE_MESSAGE("${FPHSA_FAIL_MESSAGE}: ${VERSION_MSG} (${RESULT_MSG})") - else() - _FPHSA_FAILURE_MESSAGE("${FPHSA_FAIL_MESSAGE} (missing:${MISSING_VARS}) ${VERSION_MSG}") - endif() - endif() - - endif () - - set(${_NAME}_FOUND ${${_NAME}_FOUND} PARENT_SCOPE) - set(${_NAME_UPPER}_FOUND ${${_NAME}_FOUND} PARENT_SCOPE) -endfunction() - - -cmake_policy(POP) diff --git a/build/FindPackageMessage.cmake b/build/FindPackageMessage.cmake deleted file mode 100644 index 7efbe18..0000000 --- a/build/FindPackageMessage.cmake +++ /dev/null @@ -1,49 +0,0 @@ -# Distributed under the OSI-approved BSD 3-Clause License. See accompanying -# file Copyright.txt or https://cmake.org/licensing for details. - -#[=======================================================================[.rst: -FindPackageMessage ------------------- - -.. code-block:: cmake - - find_package_message( "message for user" "find result details") - -This function is intended to be used in FindXXX.cmake modules files. -It will print a message once for each unique find result. This is -useful for telling the user where a package was found. The first -argument specifies the name (XXX) of the package. The second argument -specifies the message to display. The third argument lists details -about the find result so that if they change the message will be -displayed again. The macro also obeys the QUIET argument to the -find_package command. - -Example: - -.. code-block:: cmake - - if(X11_FOUND) - find_package_message(X11 "Found X11: ${X11_X11_LIB}" - "[${X11_X11_LIB}][${X11_INCLUDE_DIR}]") - else() - ... - endif() -#]=======================================================================] - -function(find_package_message pkg msg details) - # Avoid printing a message repeatedly for the same find result. - if(NOT ${pkg}_FIND_QUIETLY) - string(REPLACE "\n" "" details "${details}") - set(DETAILS_VAR FIND_PACKAGE_MESSAGE_DETAILS_${pkg}) - if(NOT "${details}" STREQUAL "${${DETAILS_VAR}}") - # The message has not yet been printed. - string(STRIP "${msg}" msg) - message(STATUS "${msg}") - - # Save the find details in the cache to avoid printing the same - # message again. - set("${DETAILS_VAR}" "${details}" - CACHE INTERNAL "Details about finding ${pkg}") - endif() - endif() -endfunction() diff --git a/build/FindwxWidgets.cmake b/build/FindwxWidgets.cmake deleted file mode 100644 index b42a85e..0000000 --- a/build/FindwxWidgets.cmake +++ /dev/null @@ -1,1245 +0,0 @@ -# Distributed under the OSI-approved BSD 3-Clause License. See accompanying -# file Copyright.txt or https://cmake.org/licensing for details. - -#[=======================================================================[.rst: -FindwxWidgets -------------- - -Find a wxWidgets (a.k.a., wxWindows) installation. - -This module finds if wxWidgets is installed and selects a default -configuration to use. wxWidgets is a modular library. To specify the -modules that you will use, you need to name them as components to the -package: - -find_package(wxWidgets COMPONENTS core base ... OPTIONAL_COMPONENTS net ...) - -.. versionadded:: 3.4 - Support for :command:`find_package` version argument; ``webview`` component. - -.. versionadded:: 3.14 - ``OPTIONAL_COMPONENTS`` support. - -There are two search branches: a windows style and a unix style. For -windows, the following variables are searched for and set to defaults -in case of multiple choices. Change them if the defaults are not -desired (i.e., these are the only variables you should change to -select a configuration): - -:: - - wxWidgets_ROOT_DIR - Base wxWidgets directory - (e.g., C:/wxWidgets-3.2.0). - wxWidgets_LIB_DIR - Path to wxWidgets libraries - (e.g., C:/wxWidgets-3.2.0/lib/vc_x64_lib). - wxWidgets_CONFIGURATION - Configuration to use - (e.g., msw, mswd, mswu, mswunivud, etc.) - wxWidgets_EXCLUDE_COMMON_LIBRARIES - - Set to TRUE to exclude linking of - commonly required libs (e.g., png tiff - jpeg zlib regex expat scintilla lexilla). - - - -For unix style it uses the wx-config utility. You can select between -debug/release, unicode/ansi, universal/non-universal, and -static/shared in the QtDialog or ccmake interfaces by turning ON/OFF -the following variables: - -:: - - wxWidgets_USE_DEBUG - wxWidgets_USE_UNICODE - wxWidgets_USE_UNIVERSAL - wxWidgets_USE_STATIC - -There is also a wxWidgets_CONFIG_OPTIONS variable for all other -options that need to be passed to the wx-config utility. For example, -to use the base toolkit found in the /usr/local path, set the variable -(before calling the FIND_PACKAGE command) as such: - -:: - - set(wxWidgets_CONFIG_OPTIONS --toolkit=base --prefix=/usr) - - - -The following are set after the configuration is done for both windows -and unix style: - -:: - - wxWidgets_FOUND - Set to TRUE if wxWidgets was found. - wxWidgets_INCLUDE_DIRS - Include directories for WIN32 - i.e., where to find "wx/wx.h" and - "wx/setup.h"; possibly empty for unices. - wxWidgets_LIBRARIES - Path to the wxWidgets libraries. - wxWidgets_LIBRARY_DIRS - compile time link dirs, useful for - rpath on UNIX. Typically an empty string - in WIN32 environment. - wxWidgets_DEFINITIONS - Contains defines required to compile/link - against WX, e.g. WXUSINGDLL - wxWidgets_DEFINITIONS_DEBUG- Contains defines required to compile/link - against WX debug builds, e.g. __WXDEBUG__ - wxWidgets_CXX_FLAGS - Include dirs and compiler flags for - unices, empty on WIN32. Essentially - "`wx-config --cxxflags`". - wxWidgets_USE_FILE - Convenience include file. - -.. versionadded:: 3.11 - The following environment variables can be used as hints: ``WX_CONFIG``, - ``WXRC_CMD``. - - -Sample usage: - -:: - - # Note that for MinGW users the order of libs is important! - find_package(wxWidgets COMPONENTS gl core base OPTIONAL_COMPONENTS net) - if(wxWidgets_FOUND) - include(${wxWidgets_USE_FILE}) - # and for each of your dependent executable/library targets: - target_link_libraries( ${wxWidgets_LIBRARIES}) - endif() - - - -If wxWidgets is required (i.e., not an optional part): - -:: - - find_package(wxWidgets REQUIRED gl core base OPTIONAL_COMPONENTS net) - include(${wxWidgets_USE_FILE}) - # and for each of your dependent executable/library targets: - target_link_libraries( ${wxWidgets_LIBRARIES}) - -Imported targets -^^^^^^^^^^^^^^^^ - -.. versionadded:: 3.27 - -This module defines the following :prop_tgt:`IMPORTED` targets: - -``wxWidgets::wxWidgets`` - An interface library providing usage requirements for the found components. -#]=======================================================================] - -# -# FIXME: check this and provide a correct sample usage... -# Remember to connect back to the upper text. -# Sample usage with monolithic wx build: -# -# find_package(wxWidgets COMPONENTS mono) -# ... - -# NOTES -# -# This module has been tested on the WIN32 platform with wxWidgets -# 2.6.2, 2.6.3, and 2.5.3. However, it has been designed to -# easily extend support to all possible builds, e.g., static/shared, -# debug/release, unicode, universal, multilib/monolithic, etc.. -# -# If you want to use the module and your build type is not supported -# out-of-the-box, please contact me to exchange information on how -# your system is setup and I'll try to add support for it. -# -# AUTHOR -# -# Miguel A. Figueroa-Villanueva (miguelf at ieee dot org). -# Jan Woetzel (jw at mip.informatik.uni-kiel.de). -# -# Based on previous works of: -# Jan Woetzel (FindwxWindows.cmake), -# Jorgen Bodde and Jerry Fath (FindwxWin.cmake). - -# TODO/ideas -# -# (1) Option/Setting to use all available wx libs -# In contrast to expert developer who lists the -# minimal set of required libs in wxWidgets_USE_LIBS -# there is the newbie user: -# - who just wants to link against WX with more 'magic' -# - doesn't know the internal structure of WX or how it was built, -# in particular if it is monolithic or not -# - want to link against all available WX libs -# Basically, the intent here is to mimic what wx-config would do by -# default (i.e., `wx-config --libs`). -# -# Possible solution: -# Add a reserved keyword "std" that initializes to what wx-config -# would default to. If the user has not set the wxWidgets_USE_LIBS, -# default to "std" instead of "base core" as it is now. To implement -# "std" will basically boil down to a FOR_EACH lib-FOUND, but maybe -# checking whether a minimal set was found. - - -# FIXME: This and all the DBG_MSG calls should be removed after the -# module stabilizes. -# -# Helper macro to control the debugging output globally. There are -# two versions for controlling how verbose your output should be. -macro(DBG_MSG _MSG) -# message(STATUS -# "${CMAKE_CURRENT_LIST_FILE}(${CMAKE_CURRENT_LIST_LINE}): ${_MSG}") -endmacro() -macro(DBG_MSG_V _MSG) -# message(STATUS -# "${CMAKE_CURRENT_LIST_FILE}(${CMAKE_CURRENT_LIST_LINE}): ${_MSG}") -endmacro() - -cmake_policy(PUSH) -cmake_policy(SET CMP0057 NEW) # if IN_LIST - -# Clear return values in case the module is loaded more than once. -set(wxWidgets_FOUND FALSE) -set(wxWidgets_INCLUDE_DIRS "") -set(wxWidgets_LIBRARIES "") -set(wxWidgets_LIBRARY_DIRS "") -set(wxWidgets_CXX_FLAGS "") - -# DEPRECATED: This is a patch to support the DEPRECATED use of -# wxWidgets_USE_LIBS. -# -# If wxWidgets_USE_LIBS is set: -# - if using , then override wxWidgets_USE_LIBS -# - else set wxWidgets_FIND_COMPONENTS to wxWidgets_USE_LIBS -if(wxWidgets_USE_LIBS AND NOT wxWidgets_FIND_COMPONENTS) - set(wxWidgets_FIND_COMPONENTS ${wxWidgets_USE_LIBS}) -endif() -DBG_MSG("wxWidgets_FIND_COMPONENTS : ${wxWidgets_FIND_COMPONENTS}") - -# Add the convenience use file if available. -# -# Get dir of this file which may reside in: -# - CMAKE_MAKE_ROOT/Modules on CMake installation -# - CMAKE_MODULE_PATH if user prefers his own specialized version -set(wxWidgets_USE_FILE "") -get_filename_component( - wxWidgets_CURRENT_LIST_DIR ${CMAKE_CURRENT_LIST_FILE} PATH) -# Prefer an existing customized version, but the user might override -# the FindwxWidgets module and not the UsewxWidgets one. -if(EXISTS "${wxWidgets_CURRENT_LIST_DIR}/UsewxWidgets.cmake") - set(wxWidgets_USE_FILE - "${wxWidgets_CURRENT_LIST_DIR}/UsewxWidgets.cmake") -else() - set(wxWidgets_USE_FILE UsewxWidgets) -endif() - -# Known wxWidgets versions. -set(wx_versions 3.3 3.2 3.1 3.0 2.9 2.8 2.7 2.6 2.5) - -macro(wx_extract_version) - unset(_wx_filename) - find_file(_wx_filename wx/version.h PATHS ${wxWidgets_INCLUDE_DIRS} NO_DEFAULT_PATH) - dbg_msg("_wx_filename: ${_wx_filename}") - - if(NOT _wx_filename) - message(FATAL_ERROR "wxWidgets wx/version.h file not found in ${wxWidgets_INCLUDE_DIRS}.") - endif() - - file(READ "${_wx_filename}" _wx_version_h) - unset(_wx_filename CACHE) - - string(REGEX REPLACE "^(.*\n)?#define +wxMAJOR_VERSION +([0-9]+).*" - "\\2" wxWidgets_VERSION_MAJOR "${_wx_version_h}" ) - string(REGEX REPLACE "^(.*\n)?#define +wxMINOR_VERSION +([0-9]+).*" - "\\2" wxWidgets_VERSION_MINOR "${_wx_version_h}" ) - string(REGEX REPLACE "^(.*\n)?#define +wxRELEASE_NUMBER +([0-9]+).*" - "\\2" wxWidgets_VERSION_PATCH "${_wx_version_h}" ) - string(REGEX REPLACE "^(.*\n)?#define +wxSUBRELEASE_NUMBER +([0-9]+).*" - "\\2" wxWidgets_VERSION_TWEAK "${_wx_version_h}" ) - - set(wxWidgets_VERSION_STRING - "${wxWidgets_VERSION_MAJOR}.${wxWidgets_VERSION_MINOR}.${wxWidgets_VERSION_PATCH}" ) - if(${wxWidgets_VERSION_TWEAK} GREATER 0) - string(APPEND wxWidgets_VERSION_STRING ".${wxWidgets_VERSION_TWEAK}") - endif() - dbg_msg("wxWidgets_VERSION_STRING: ${wxWidgets_VERSION_STRING}") -endmacro() - -#===================================================================== -# Determine whether unix or win32 paths should be used -#===================================================================== -if(WIN32 AND NOT CYGWIN AND NOT MSYS AND NOT CMAKE_CROSSCOMPILING) - set(wxWidgets_FIND_STYLE "win32") -else() - set(wxWidgets_FIND_STYLE "unix") -endif() - -#===================================================================== -# WIN32_FIND_STYLE -#===================================================================== -if(wxWidgets_FIND_STYLE STREQUAL "win32") - # Useful common wx libs needed by almost all components. - set(wxWidgets_COMMON_LIBRARIES png tiff jpeg zlib regex expat) - - # Libraries needed by stc component - set(wxWidgets_STC_LIBRARIES scintilla lexilla) - - # DEPRECATED: Use find_package(wxWidgets COMPONENTS mono) instead. - if(NOT wxWidgets_FIND_COMPONENTS) - if(wxWidgets_USE_MONOLITHIC) - set(wxWidgets_FIND_COMPONENTS mono) - else() - set(wxWidgets_FIND_COMPONENTS core base) # this is default - endif() - endif() - - # Add the common (usually required libs) unless - # wxWidgets_EXCLUDE_COMMON_LIBRARIES has been set. - if(NOT wxWidgets_EXCLUDE_COMMON_LIBRARIES) - if(stc IN_LIST wxWidgets_FIND_COMPONENTS) - list(APPEND wxWidgets_FIND_COMPONENTS ${wxWidgets_STC_LIBRARIES}) - endif() - list(APPEND wxWidgets_FIND_COMPONENTS ${wxWidgets_COMMON_LIBRARIES}) - endif() - - # Remove duplicates, for example when user has specified common libraries. - list(REMOVE_DUPLICATES wxWidgets_FIND_COMPONENTS) - - #------------------------------------------------------------------- - # WIN32: Helper MACROS - #------------------------------------------------------------------- - # - # Get filename components for a configuration. For example, - # if _CONFIGURATION = mswunivud, then _PF="msw", _UNV=univ, _UCD=u _DBG=d - # if _CONFIGURATION = mswu, then _PF="msw", _UNV="", _UCD=u _DBG="" - # - macro(WX_GET_NAME_COMPONENTS _CONFIGURATION _PF _UNV _UCD _DBG) - DBG_MSG_V(${_CONFIGURATION}) - string(REGEX MATCH "univ" ${_UNV} "${_CONFIGURATION}") - string(REGEX REPLACE "[msw|qt].*(u)[d]*$" "u" ${_UCD} "${_CONFIGURATION}") - if(${_UCD} STREQUAL ${_CONFIGURATION}) - set(${_UCD} "") - endif() - string(REGEX MATCH "d$" ${_DBG} "${_CONFIGURATION}") - string(REGEX MATCH "^[msw|qt]*" ${_PF} "${_CONFIGURATION}") - endmacro() - - # - # Find libraries associated to a configuration. - # - macro(WX_FIND_LIBS _PF _UNV _UCD _DBG _VER) - DBG_MSG_V("m_unv = ${_UNV}") - DBG_MSG_V("m_ucd = ${_UCD}") - DBG_MSG_V("m_dbg = ${_DBG}") - DBG_MSG_V("m_ver = ${_VER}") - - # FIXME: What if both regex libs are available. regex should be - # found outside the loop and only wx${LIB}${_UCD}${_DBG}. - # Find wxWidgets common libraries. - foreach(LIB ${wxWidgets_COMMON_LIBRARIES} ${wxWidgets_STC_LIBRARIES}) - find_library(WX_${LIB}${_DBG} - NAMES - wx${LIB}${_UCD}${_DBG} # for regex - wx${LIB}${_DBG} - PATHS ${WX_LIB_DIR} - NO_DEFAULT_PATH - ) - mark_as_advanced(WX_${LIB}${_DBG}) - endforeach() - - # Find wxWidgets multilib base libraries. - find_library(WX_base${_DBG} - NAMES wxbase${_VER}${_UCD}${_DBG} - PATHS ${WX_LIB_DIR} - NO_DEFAULT_PATH - ) - mark_as_advanced(WX_base${_DBG}) - foreach(LIB net odbc xml) - find_library(WX_${LIB}${_DBG} - NAMES wxbase${_VER}${_UCD}${_DBG}_${LIB} - PATHS ${WX_LIB_DIR} - NO_DEFAULT_PATH - ) - mark_as_advanced(WX_${LIB}${_DBG}) - endforeach() - - # Find wxWidgets monolithic library. - find_library(WX_mono${_DBG} - NAMES wx${_PF}${_UNV}${_VER}${_UCD}${_DBG} - PATHS ${WX_LIB_DIR} - NO_DEFAULT_PATH - ) - mark_as_advanced(WX_mono${_DBG}) - - # Find wxWidgets multilib libraries. - foreach(LIB core adv aui html media xrc dbgrid gl qa richtext - stc ribbon propgrid webview) - find_library(WX_${LIB}${_DBG} - NAMES wx${_PF}${_UNV}${_VER}${_UCD}${_DBG}_${LIB} - PATHS ${WX_LIB_DIR} - NO_DEFAULT_PATH - ) - mark_as_advanced(WX_${LIB}${_DBG}) - endforeach() - endmacro() - - # - # Clear all library paths, so that FIND_LIBRARY refinds them. - # - # Clear a lib, reset its found flag, and mark as advanced. - macro(WX_CLEAR_LIB _LIB) - set(${_LIB} "${_LIB}-NOTFOUND" CACHE FILEPATH "Cleared." FORCE) - set(${_LIB}_FOUND FALSE) - mark_as_advanced(${_LIB}) - endmacro() - # Clear all debug or release library paths (arguments are "d" or ""). - macro(WX_CLEAR_ALL_LIBS _DBG) - # Clear wxWidgets common libraries. - foreach(LIB ${wxWidgets_COMMON_LIBRARIES} ${wxWidgets_STC_LIBRARIES}) - WX_CLEAR_LIB(WX_${LIB}${_DBG}) - endforeach() - - # Clear wxWidgets multilib base libraries. - WX_CLEAR_LIB(WX_base${_DBG}) - foreach(LIB net odbc xml) - WX_CLEAR_LIB(WX_${LIB}${_DBG}) - endforeach() - - # Clear wxWidgets monolithic library. - WX_CLEAR_LIB(WX_mono${_DBG}) - - # Clear wxWidgets multilib libraries. - foreach(LIB core adv aui html media xrc dbgrid gl qa richtext - webview stc ribbon propgrid) - WX_CLEAR_LIB(WX_${LIB}${_DBG}) - endforeach() - endmacro() - # Clear all wxWidgets debug libraries. - macro(WX_CLEAR_ALL_DBG_LIBS) - WX_CLEAR_ALL_LIBS("d") - endmacro() - # Clear all wxWidgets release libraries. - macro(WX_CLEAR_ALL_REL_LIBS) - WX_CLEAR_ALL_LIBS("") - endmacro() - - # - # Set the wxWidgets_LIBRARIES variable. - # Also, Sets output variable wxWidgets_FOUND to FALSE if it fails. - # - macro(WX_SET_LIBRARIES _LIBS _DBG) - DBG_MSG_V("Looking for ${${_LIBS}}") - if(WX_USE_REL_AND_DBG) - foreach(LIB ${${_LIBS}}) - DBG_MSG_V("Searching for ${LIB} and ${LIB}d") - DBG_MSG_V("WX_${LIB} : ${WX_${LIB}}") - DBG_MSG_V("WX_${LIB}d : ${WX_${LIB}d}") - if(WX_${LIB} AND WX_${LIB}d) - DBG_MSG_V("Found ${LIB} and ${LIB}d") - list(APPEND wxWidgets_LIBRARIES - debug ${WX_${LIB}d} optimized ${WX_${LIB}} - ) - set(wxWidgets_${LIB}_FOUND TRUE) - elseif(NOT wxWidgets_FIND_REQUIRED_${LIB}) - DBG_MSG_V("- ignored optional missing WX_${LIB}=${WX_${LIB}} or WX_${LIB}d=${WX_${LIB}d}") - else() - DBG_MSG_V("- not found due to missing WX_${LIB}=${WX_${LIB}} or WX_${LIB}d=${WX_${LIB}d}") - set(wxWidgets_FOUND FALSE) - endif() - endforeach() - else() - foreach(LIB ${${_LIBS}}) - DBG_MSG_V("Searching for ${LIB}${_DBG}") - DBG_MSG_V("WX_${LIB}${_DBG} : ${WX_${LIB}${_DBG}}") - if(WX_${LIB}${_DBG}) - DBG_MSG_V("Found ${LIB}${_DBG}") - list(APPEND wxWidgets_LIBRARIES ${WX_${LIB}${_DBG}}) - set(wxWidgets_${LIB}_FOUND TRUE) - elseif(NOT wxWidgets_FIND_REQUIRED_${LIB}) - DBG_MSG_V("- ignored optional missing WX_${LIB}${_DBG}=${WX_${LIB}${_DBG}}") - else() - DBG_MSG_V("- not found due to missing WX_${LIB}${_DBG}=${WX_${LIB}${_DBG}}") - set(wxWidgets_FOUND FALSE) - endif() - endforeach() - endif() - - DBG_MSG_V("OpenGL") - if(gl IN_LIST ${_LIBS}) - DBG_MSG_V("- is required.") - list(APPEND wxWidgets_LIBRARIES opengl32 glu32) - endif() - - if(stc IN_LIST ${_LIBS}) - list(APPEND wxWidgets_LIBRARIES imm32) - endif() - - list(APPEND wxWidgets_LIBRARIES winmm comctl32 uuid oleacc uxtheme rpcrt4 shlwapi version wsock32) - endmacro() - - #------------------------------------------------------------------- - # WIN32: Start actual work. - #------------------------------------------------------------------- - - set(wx_paths "wxWidgets") - foreach(version ${wx_versions}) - foreach(patch RANGE 15 0 -1) - list(APPEND wx_paths "wxWidgets-${version}.${patch}") - foreach(tweak RANGE 3 1 -1) - list(APPEND wx_paths "wxWidgets-${version}.${patch}.${tweak}") - endforeach() - endforeach() - endforeach() - - # Look for an installation tree. - find_path(wxWidgets_ROOT_DIR - NAMES include/wx/wx.h - PATHS - ENV wxWidgets_ROOT_DIR - ENV WXWIN - "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\wxWidgets_is1;Inno Setup: App Path]" # WX 2.6.x - C:/ - D:/ - ENV ProgramFiles - PATH_SUFFIXES - ${wx_paths} - DOC "wxWidgets base/installation directory" - ) - - # If wxWidgets_ROOT_DIR changed, clear lib dir. - if(NOT WX_ROOT_DIR STREQUAL wxWidgets_ROOT_DIR) - if(NOT wxWidgets_LIB_DIR OR WX_ROOT_DIR) - set(wxWidgets_LIB_DIR "wxWidgets_LIB_DIR-NOTFOUND" - CACHE PATH "Cleared." FORCE) - endif() - set(WX_ROOT_DIR ${wxWidgets_ROOT_DIR} - CACHE INTERNAL "wxWidgets_ROOT_DIR") - endif() - - if(WX_ROOT_DIR) - # Select one default tree inside the already determined wx tree. - # Prefer static/shared order usually consistent with build - # settings. - set(_WX_TOOL "") - set(_WX_TOOLVER "") - set(_WX_ARCH "") - if(MINGW) - set(_WX_TOOL gcc) - elseif(MSVC) - set(_WX_TOOL vc) - set(_WX_TOOLVER ${MSVC_TOOLSET_VERSION}) - # support for a lib/vc14x_x64_dll/ path from wxW 3.1.3 distribution - string(REGEX REPLACE ".$" "x" _WX_TOOLVERx ${_WX_TOOLVER}) - if(CMAKE_SIZEOF_VOID_P EQUAL 8) - set(_WX_ARCH _x64) - endif() - endif() - if(BUILD_SHARED_LIBS) - find_path(wxWidgets_LIB_DIR - NAMES - qtu/wx/setup.h - qtud/wx/setup.h - msw/wx/setup.h - mswd/wx/setup.h - mswu/wx/setup.h - mswud/wx/setup.h - mswuniv/wx/setup.h - mswunivd/wx/setup.h - mswunivu/wx/setup.h - mswunivud/wx/setup.h - PATHS - ${WX_ROOT_DIR}/lib/${_WX_TOOL}${_WX_TOOLVER}_xp${_WX_ARCH}_dll # prefer shared - ${WX_ROOT_DIR}/lib/${_WX_TOOL}${_WX_TOOLVER}${_WX_ARCH}_dll # prefer shared - ${WX_ROOT_DIR}/lib/${_WX_TOOL}${_WX_TOOLVERx}_xp${_WX_ARCH}_dll # prefer shared - ${WX_ROOT_DIR}/lib/${_WX_TOOL}${_WX_TOOLVERx}${_WX_ARCH}_dll # prefer shared - ${WX_ROOT_DIR}/lib/${_WX_TOOL}${_WX_ARCH}_dll # prefer shared - ${WX_ROOT_DIR}/lib/${_WX_TOOL}${_WX_TOOLVER}_xp${_WX_ARCH}_lib - ${WX_ROOT_DIR}/lib/${_WX_TOOL}${_WX_TOOLVER}${_WX_ARCH}_lib - ${WX_ROOT_DIR}/lib/${_WX_TOOL}${_WX_TOOLVERx}_xp${_WX_ARCH}_lib - ${WX_ROOT_DIR}/lib/${_WX_TOOL}${_WX_TOOLVERx}${_WX_ARCH}_lib - ${WX_ROOT_DIR}/lib/${_WX_TOOL}${_WX_ARCH}_lib - DOC "Path to wxWidgets libraries" - NO_DEFAULT_PATH - ) - else() - find_path(wxWidgets_LIB_DIR - NAMES - qtu/wx/setup.h - qtud/wx/setup.h - msw/wx/setup.h - mswd/wx/setup.h - mswu/wx/setup.h - mswud/wx/setup.h - mswuniv/wx/setup.h - mswunivd/wx/setup.h - mswunivu/wx/setup.h - mswunivud/wx/setup.h - PATHS - ${WX_ROOT_DIR}/lib/${_WX_TOOL}${_WX_TOOLVER}_xp${_WX_ARCH}_lib # prefer static - ${WX_ROOT_DIR}/lib/${_WX_TOOL}${_WX_TOOLVER}${_WX_ARCH}_lib # prefer static - ${WX_ROOT_DIR}/lib/${_WX_TOOL}${_WX_TOOLVERx}_xp${_WX_ARCH}_lib # prefer static - ${WX_ROOT_DIR}/lib/${_WX_TOOL}${_WX_TOOLVERx}${_WX_ARCH}_lib # prefer static - ${WX_ROOT_DIR}/lib/${_WX_TOOL}${_WX_ARCH}_lib # prefer static - ${WX_ROOT_DIR}/lib/${_WX_TOOL}${_WX_TOOLVER}_xp${_WX_ARCH}_dll - ${WX_ROOT_DIR}/lib/${_WX_TOOL}${_WX_TOOLVER}${_WX_ARCH}_dll - ${WX_ROOT_DIR}/lib/${_WX_TOOL}${_WX_TOOLVERx}_xp${_WX_ARCH}_dll - ${WX_ROOT_DIR}/lib/${_WX_TOOL}${_WX_TOOLVERx}${_WX_ARCH}_dll - ${WX_ROOT_DIR}/lib/${_WX_TOOL}${_WX_ARCH}_dll - DOC "Path to wxWidgets libraries" - NO_DEFAULT_PATH - ) - endif() - unset(_WX_TOOL) - unset(_WX_TOOLVER) - unset(_WX_ARCH) - - # If wxWidgets_LIB_DIR changed, clear all libraries. - if(NOT WX_LIB_DIR STREQUAL wxWidgets_LIB_DIR) - set(WX_LIB_DIR ${wxWidgets_LIB_DIR} CACHE INTERNAL "wxWidgets_LIB_DIR") - WX_CLEAR_ALL_DBG_LIBS() - WX_CLEAR_ALL_REL_LIBS() - endif() - - if(WX_LIB_DIR) - # If building shared libs, define WXUSINGDLL to use dllimport. - if(WX_LIB_DIR MATCHES "[dD][lL][lL]") - set(wxWidgets_DEFINITIONS WXUSINGDLL) - DBG_MSG_V("detected SHARED/DLL tree WX_LIB_DIR=${WX_LIB_DIR}") - endif() - - # Search for available configuration types. - foreach(CFG mswunivud mswunivd mswud mswd mswunivu mswuniv mswu msw qt qtd qtu qtud) - set(WX_${CFG}_FOUND FALSE) - if(EXISTS ${WX_LIB_DIR}/${CFG}) - list(APPEND WX_CONFIGURATION_LIST ${CFG}) - set(WX_${CFG}_FOUND TRUE) - set(WX_CONFIGURATION ${CFG}) - endif() - endforeach() - DBG_MSG_V("WX_CONFIGURATION_LIST=${WX_CONFIGURATION_LIST}") - - if(WX_CONFIGURATION) - set(wxWidgets_FOUND TRUE) - - # If the selected configuration wasn't found force the default - # one. Otherwise, use it but still force a refresh for - # updating the doc string with the current list of available - # configurations. - if(NOT WX_${wxWidgets_CONFIGURATION}_FOUND) - set(wxWidgets_CONFIGURATION ${WX_CONFIGURATION} CACHE STRING - "Set wxWidgets configuration (${WX_CONFIGURATION_LIST})" FORCE) - else() - set(wxWidgets_CONFIGURATION ${wxWidgets_CONFIGURATION} CACHE STRING - "Set wxWidgets configuration (${WX_CONFIGURATION_LIST})" FORCE) - endif() - - # If release config selected, and both release/debug exist. - if(WX_${wxWidgets_CONFIGURATION}d_FOUND) - option(wxWidgets_USE_REL_AND_DBG - "Use release and debug configurations?" TRUE) - set(WX_USE_REL_AND_DBG ${wxWidgets_USE_REL_AND_DBG}) - else() - # If the option exists (already in cache), force it false. - if(wxWidgets_USE_REL_AND_DBG) - set(wxWidgets_USE_REL_AND_DBG FALSE CACHE BOOL - "No ${wxWidgets_CONFIGURATION}d found." FORCE) - endif() - set(WX_USE_REL_AND_DBG FALSE) - endif() - - # Get configuration parameters from the name. - WX_GET_NAME_COMPONENTS(${wxWidgets_CONFIGURATION} PF UNV UCD DBG) - - # Set wxWidgets lib setup include directory. - if(EXISTS ${WX_LIB_DIR}/${wxWidgets_CONFIGURATION}/wx/setup.h) - set(wxWidgets_INCLUDE_DIRS - ${WX_LIB_DIR}/${wxWidgets_CONFIGURATION}) - else() - DBG_MSG("wxWidgets_FOUND FALSE because ${WX_LIB_DIR}/${wxWidgets_CONFIGURATION}/wx/setup.h does not exist.") - set(wxWidgets_FOUND FALSE) - endif() - - # Set wxWidgets main include directory. - if(EXISTS ${WX_ROOT_DIR}/include/wx/wx.h) - list(APPEND wxWidgets_INCLUDE_DIRS ${WX_ROOT_DIR}/include) - else() - DBG_MSG("wxWidgets_FOUND FALSE because WX_ROOT_DIR=${WX_ROOT_DIR} has no ${WX_ROOT_DIR}/include/wx/wx.h") - set(wxWidgets_FOUND FALSE) - endif() - - # Get version number. - wx_extract_version() - set(VER "${wxWidgets_VERSION_MAJOR}${wxWidgets_VERSION_MINOR}") - - # Find wxWidgets libraries. - WX_FIND_LIBS("${PF}" "${UNV}" "${UCD}" "${DBG}" "${VER}") - if(WX_USE_REL_AND_DBG) - WX_FIND_LIBS("${PF}" "${UNV}" "${UCD}" "d" "${VER}") - endif() - - # Settings for requested libs (i.e., include dir, libraries, etc.). - WX_SET_LIBRARIES(wxWidgets_FIND_COMPONENTS "${DBG}") - - # Add necessary definitions for unicode builds - if("${UCD}" STREQUAL "u") - list(APPEND wxWidgets_DEFINITIONS UNICODE _UNICODE) - endif() - - # Add necessary definitions for debug builds - set(wxWidgets_DEFINITIONS_DEBUG _DEBUG __WXDEBUG__) - - endif() - endif() - endif() - - if(MINGW AND NOT wxWidgets_FOUND) - # Try unix search mode as well. - set(wxWidgets_FIND_STYLE "unix") - dbg_msg_v("wxWidgets_FIND_STYLE changed to unix") - endif() -endif() - -#===================================================================== -# UNIX_FIND_STYLE -#===================================================================== -if(wxWidgets_FIND_STYLE STREQUAL "unix") - #----------------------------------------------------------------- - # UNIX: Helper MACROS - #----------------------------------------------------------------- - # - # Set the default values based on "wx-config --selected-config". - # - macro(WX_CONFIG_SELECT_GET_DEFAULT) - execute_process( - COMMAND sh "${wxWidgets_CONFIG_EXECUTABLE}" - ${wxWidgets_CONFIG_OPTIONS} --selected-config - OUTPUT_VARIABLE _wx_selected_config - RESULT_VARIABLE _wx_result - ERROR_QUIET - ) - if(_wx_result EQUAL 0) - foreach(_opt_name debug static unicode universal) - string(TOUPPER ${_opt_name} _upper_opt_name) - if(_wx_selected_config MATCHES "${_opt_name}") - set(wxWidgets_DEFAULT_${_upper_opt_name} ON) - else() - set(wxWidgets_DEFAULT_${_upper_opt_name} OFF) - endif() - endforeach() - else() - foreach(_upper_opt_name DEBUG STATIC UNICODE UNIVERSAL) - set(wxWidgets_DEFAULT_${_upper_opt_name} OFF) - endforeach() - endif() - endmacro() - - # - # Query a boolean configuration option to determine if the system - # has both builds available. If so, provide the selection option - # to the user. - # - macro(WX_CONFIG_SELECT_QUERY_BOOL _OPT_NAME _OPT_HELP) - execute_process( - COMMAND sh "${wxWidgets_CONFIG_EXECUTABLE}" - ${wxWidgets_CONFIG_OPTIONS} --${_OPT_NAME}=yes - RESULT_VARIABLE _wx_result_yes - OUTPUT_QUIET - ERROR_QUIET - ) - execute_process( - COMMAND sh "${wxWidgets_CONFIG_EXECUTABLE}" - ${wxWidgets_CONFIG_OPTIONS} --${_OPT_NAME}=no - RESULT_VARIABLE _wx_result_no - OUTPUT_QUIET - ERROR_QUIET - ) - string(TOUPPER ${_OPT_NAME} _UPPER_OPT_NAME) - if(_wx_result_yes EQUAL 0 AND _wx_result_no EQUAL 0) - option(wxWidgets_USE_${_UPPER_OPT_NAME} - ${_OPT_HELP} ${wxWidgets_DEFAULT_${_UPPER_OPT_NAME}}) - else() - # If option exists (already in cache), force to available one. - if(DEFINED wxWidgets_USE_${_UPPER_OPT_NAME}) - if(_wx_result_yes EQUAL 0) - set(wxWidgets_USE_${_UPPER_OPT_NAME} ON CACHE BOOL ${_OPT_HELP} FORCE) - else() - set(wxWidgets_USE_${_UPPER_OPT_NAME} OFF CACHE BOOL ${_OPT_HELP} FORCE) - endif() - endif() - endif() - endmacro() - - # - # Set wxWidgets_SELECT_OPTIONS to wx-config options for selecting - # among multiple builds. - # - macro(WX_CONFIG_SELECT_SET_OPTIONS) - set(wxWidgets_SELECT_OPTIONS ${wxWidgets_CONFIG_OPTIONS}) - foreach(_opt_name debug static unicode universal) - string(TOUPPER ${_opt_name} _upper_opt_name) - if(DEFINED wxWidgets_USE_${_upper_opt_name}) - if(wxWidgets_USE_${_upper_opt_name}) - list(APPEND wxWidgets_SELECT_OPTIONS --${_opt_name}=yes) - else() - list(APPEND wxWidgets_SELECT_OPTIONS --${_opt_name}=no) - endif() - endif() - endforeach() - endmacro() - - #----------------------------------------------------------------- - # UNIX: Start actual work. - #----------------------------------------------------------------- - # Support cross-compiling, only search in the target platform. - # - # Look for wx-config -- this can be set in the environment, - # or try versioned and toolchain-versioned variants of the -config - # executable as well. - set(wx_config_names "wx-config") - foreach(version ${wx_versions}) - list(APPEND wx_config_names "wx-config-${version}" "wxgtk3u-${version}-config" "wxgtk2u-${version}-config") - endforeach() - find_program(wxWidgets_CONFIG_EXECUTABLE - NAMES - $ENV{WX_CONFIG} - ${wx_config_names} - DOC "Location of wxWidgets library configuration provider binary (wx-config)." - ONLY_CMAKE_FIND_ROOT_PATH - ) - - if(wxWidgets_CONFIG_EXECUTABLE) - set(wxWidgets_FOUND TRUE) - - # get defaults based on "wx-config --selected-config" - WX_CONFIG_SELECT_GET_DEFAULT() - - # for each option: if both builds are available, provide option - WX_CONFIG_SELECT_QUERY_BOOL(debug "Use debug build?") - WX_CONFIG_SELECT_QUERY_BOOL(unicode "Use unicode build?") - WX_CONFIG_SELECT_QUERY_BOOL(universal "Use universal build?") - WX_CONFIG_SELECT_QUERY_BOOL(static "Link libraries statically?") - - # process selection to set wxWidgets_SELECT_OPTIONS - WX_CONFIG_SELECT_SET_OPTIONS() - DBG_MSG("wxWidgets_SELECT_OPTIONS=${wxWidgets_SELECT_OPTIONS}") - - # run the wx-config program to get cxxflags - execute_process( - COMMAND sh "${wxWidgets_CONFIG_EXECUTABLE}" - ${wxWidgets_SELECT_OPTIONS} --cxxflags - OUTPUT_VARIABLE wxWidgets_CXX_FLAGS - RESULT_VARIABLE RET - ERROR_QUIET - ) - if(RET EQUAL 0) - string(STRIP "${wxWidgets_CXX_FLAGS}" wxWidgets_CXX_FLAGS) - separate_arguments(wxWidgets_CXX_FLAGS_LIST NATIVE_COMMAND "${wxWidgets_CXX_FLAGS}") - - DBG_MSG_V("wxWidgets_CXX_FLAGS=${wxWidgets_CXX_FLAGS}") - - # parse definitions and include dirs from cxxflags - # drop the -D and -I prefixes - set(wxWidgets_CXX_FLAGS) - foreach(arg IN LISTS wxWidgets_CXX_FLAGS_LIST) - if("${arg}" MATCHES "^-I(.*)$") - # include directory - list(APPEND wxWidgets_INCLUDE_DIRS "${CMAKE_MATCH_1}") - elseif("${arg}" MATCHES "^-D(.*)$") - # compile definition - list(APPEND wxWidgets_DEFINITIONS "${CMAKE_MATCH_1}") - else() - list(APPEND wxWidgets_CXX_FLAGS "${arg}") - endif() - endforeach() - - DBG_MSG_V("wxWidgets_DEFINITIONS=${wxWidgets_DEFINITIONS}") - DBG_MSG_V("wxWidgets_INCLUDE_DIRS=${wxWidgets_INCLUDE_DIRS}") - DBG_MSG_V("wxWidgets_CXX_FLAGS=${wxWidgets_CXX_FLAGS}") - - else() - set(wxWidgets_FOUND FALSE) - DBG_MSG_V( - "${wxWidgets_CONFIG_EXECUTABLE} --cxxflags FAILED with RET=${RET}") - endif() - - # run the wx-config program to get the libs - # - NOTE: wx-config doesn't verify that the libs requested exist - # it just produces the names. Maybe a TRY_COMPILE would - # be useful here... - unset(_cmp_req) - unset(_cmp_opt) - foreach(_cmp IN LISTS wxWidgets_FIND_COMPONENTS) - if(wxWidgets_FIND_REQUIRED_${_cmp}) - list(APPEND _cmp_req "${_cmp}") - else() - list(APPEND _cmp_opt "${_cmp}") - endif() - endforeach() - DBG_MSG_V("wxWidgets required components : ${_cmp_req}") - DBG_MSG_V("wxWidgets optional components : ${_cmp_opt}") - if(DEFINED _cmp_opt) - string(REPLACE ";" "," _cmp_opt "${_cmp_opt}") - set(_cmp_opt "--optional-libs" ${_cmp_opt}) - endif() - string(REPLACE ";" "," _cmp_req "${_cmp_req}") - execute_process( - COMMAND sh "${wxWidgets_CONFIG_EXECUTABLE}" - ${wxWidgets_SELECT_OPTIONS} --libs ${_cmp_req} ${_cmp_opt} - OUTPUT_VARIABLE wxWidgets_LIBRARIES - RESULT_VARIABLE RET - ERROR_QUIET - ) - if(RET EQUAL 0) - string(STRIP "${wxWidgets_LIBRARIES}" wxWidgets_LIBRARIES) - separate_arguments(wxWidgets_LIBRARIES) - string(REPLACE "-framework;" "-framework " - wxWidgets_LIBRARIES "${wxWidgets_LIBRARIES}") - string(REPLACE "-weak_framework;" "-weak_framework " - wxWidgets_LIBRARIES "${wxWidgets_LIBRARIES}") - string(REPLACE "-arch;" "-arch " - wxWidgets_LIBRARIES "${wxWidgets_LIBRARIES}") - string(REPLACE "-isysroot;" "-isysroot " - wxWidgets_LIBRARIES "${wxWidgets_LIBRARIES}") - - # extract linkdirs (-L) for rpath (i.e., LINK_DIRECTORIES) - string(REGEX MATCHALL "-L[^;]+" - wxWidgets_LIBRARY_DIRS "${wxWidgets_LIBRARIES}") - string(REGEX REPLACE "-L([^;]+)" "\\1" - wxWidgets_LIBRARY_DIRS "${wxWidgets_LIBRARY_DIRS}") - - DBG_MSG_V("wxWidgets_LIBRARIES=${wxWidgets_LIBRARIES}") - DBG_MSG_V("wxWidgets_LIBRARY_DIRS=${wxWidgets_LIBRARY_DIRS}") - - else() - set(wxWidgets_FOUND FALSE) - DBG_MSG("${wxWidgets_CONFIG_EXECUTABLE} --libs ${_cmp_req} ${_cmp_opt} FAILED with RET=${RET}") - endif() - unset(_cmp_req) - unset(_cmp_opt) - endif() - - # When using wx-config in MSYS, the include paths are UNIX style paths which may or may - # not work correctly depending on you MSYS/MinGW configuration. CMake expects native - # paths internally. - if(wxWidgets_FOUND AND MSYS) - find_program(_cygpath_exe cygpath ONLY_CMAKE_FIND_ROOT_PATH) - DBG_MSG_V("_cygpath_exe: ${_cygpath_exe}") - if(_cygpath_exe) - set(_tmp_path "") - foreach(_path ${wxWidgets_INCLUDE_DIRS}) - execute_process( - COMMAND cygpath -w ${_path} - OUTPUT_VARIABLE _native_path - RESULT_VARIABLE _retv - OUTPUT_STRIP_TRAILING_WHITESPACE - ERROR_QUIET - ) - if(_retv EQUAL 0) - file(TO_CMAKE_PATH ${_native_path} _native_path) - DBG_MSG_V("Path ${_path} converted to ${_native_path}") - string(APPEND _tmp_path " ${_native_path}") - endif() - endforeach() - DBG_MSG("Setting wxWidgets_INCLUDE_DIRS = ${_tmp_path}") - set(wxWidgets_INCLUDE_DIRS ${_tmp_path}) - separate_arguments(wxWidgets_INCLUDE_DIRS) - list(REMOVE_ITEM wxWidgets_INCLUDE_DIRS "") - - set(_tmp_path "") - foreach(_path ${wxWidgets_LIBRARY_DIRS}) - execute_process( - COMMAND cygpath -w ${_path} - OUTPUT_VARIABLE _native_path - RESULT_VARIABLE _retv - OUTPUT_STRIP_TRAILING_WHITESPACE - ERROR_QUIET - ) - if(_retv EQUAL 0) - file(TO_CMAKE_PATH ${_native_path} _native_path) - DBG_MSG_V("Path ${_path} converted to ${_native_path}") - string(APPEND _tmp_path " ${_native_path}") - endif() - endforeach() - DBG_MSG("Setting wxWidgets_LIBRARY_DIRS = ${_tmp_path}") - set(wxWidgets_LIBRARY_DIRS ${_tmp_path}) - separate_arguments(wxWidgets_LIBRARY_DIRS) - list(REMOVE_ITEM wxWidgets_LIBRARY_DIRS "") - endif() - unset(_cygpath_exe CACHE) - endif() - - # Check that all libraries are present, as wx-config does not check it - set(_wx_lib_missing "") - foreach(_wx_lib_ ${wxWidgets_LIBRARIES}) - if("${_wx_lib_}" MATCHES "^-l(.*)") - set(_wx_lib_name "${CMAKE_MATCH_1}") - unset(_wx_lib_found CACHE) - find_library(_wx_lib_found NAMES ${_wx_lib_name} HINTS ${wxWidgets_LIBRARY_DIRS}) - if(_wx_lib_found STREQUAL _wx_lib_found-NOTFOUND) - list(APPEND _wx_lib_missing ${_wx_lib_name}) - endif() - unset(_wx_lib_found CACHE) - endif() - endforeach() - - if (_wx_lib_missing) - string(REPLACE ";" " " _wx_lib_missing "${_wx_lib_missing}") - DBG_MSG_V("wxWidgets not found due to following missing libraries: ${_wx_lib_missing}") - set(wxWidgets_FOUND FALSE) - unset(wxWidgets_LIBRARIES) - endif() - unset(_wx_lib_missing) -endif() - -# Check if a specific version was requested by find_package(). -if(wxWidgets_FOUND) - wx_extract_version() -endif() - -file(TO_CMAKE_PATH "${wxWidgets_INCLUDE_DIRS}" wxWidgets_INCLUDE_DIRS) -file(TO_CMAKE_PATH "${wxWidgets_LIBRARY_DIRS}" wxWidgets_LIBRARY_DIRS) - -# Debug output: -DBG_MSG("wxWidgets_FOUND : ${wxWidgets_FOUND}") -DBG_MSG("wxWidgets_INCLUDE_DIRS : ${wxWidgets_INCLUDE_DIRS}") -DBG_MSG("wxWidgets_LIBRARY_DIRS : ${wxWidgets_LIBRARY_DIRS}") -DBG_MSG("wxWidgets_LIBRARIES : ${wxWidgets_LIBRARIES}") -DBG_MSG("wxWidgets_CXX_FLAGS : ${wxWidgets_CXX_FLAGS}") -DBG_MSG("wxWidgets_USE_FILE : ${wxWidgets_USE_FILE}") - -#===================================================================== -#===================================================================== - -include(${CMAKE_CURRENT_LIST_DIR}/FindPackageHandleStandardArgs.cmake) - -# FIXME: set wxWidgets__FOUND for wx-config branch -# and use HANDLE_COMPONENTS on Unix too -if(wxWidgets_FIND_STYLE STREQUAL "win32") - set(wxWidgets_HANDLE_COMPONENTS "HANDLE_COMPONENTS") -endif() - -find_package_handle_standard_args(wxWidgets - REQUIRED_VARS wxWidgets_LIBRARIES wxWidgets_INCLUDE_DIRS - VERSION_VAR wxWidgets_VERSION_STRING - ${wxWidgets_HANDLE_COMPONENTS} - ) -unset(wxWidgets_HANDLE_COMPONENTS) - -if(wxWidgets_FOUND AND NOT TARGET wxWidgets::wxWidgets) - add_library(wxWidgets::wxWidgets INTERFACE IMPORTED) - target_link_libraries(wxWidgets::wxWidgets INTERFACE ${wxWidgets_LIBRARIES}) - target_link_directories(wxWidgets::wxWidgets INTERFACE ${wxWidgets_LIBRARY_DIRS}) - target_include_directories(wxWidgets::wxWidgets INTERFACE ${wxWidgets_INCLUDE_DIRS}) - target_compile_options(wxWidgets::wxWidgets INTERFACE ${wxWidgets_CXX_FLAGS}) - target_compile_definitions(wxWidgets::wxWidgets INTERFACE ${wxWidgets_DEFINITIONS}) - # FIXME: Add "$<$:${wxWidgets_DEFINITIONS_DEBUG}>" - # if the debug library variant is available. -endif() - -#===================================================================== -# Macros for use in wxWidgets apps. -# - This module will not fail to find wxWidgets based on the code -# below. Hence, it's required to check for validity of: -# -# wxWidgets_wxrc_EXECUTABLE -#===================================================================== - -# Resource file compiler. -find_program(wxWidgets_wxrc_EXECUTABLE - NAMES $ENV{WXRC_CMD} wxrc - PATHS ${wxWidgets_ROOT_DIR}/utils/wxrc/vc_msw - DOC "Location of wxWidgets resource file compiler binary (wxrc)" - ) - -# -# WX_SPLIT_ARGUMENTS_ON( ...) -# -# Sets and to contain arguments to the left and right, -# respectively, of . -# -# Example usage: -# function(WXWIDGETS_ADD_RESOURCES outfiles) -# WX_SPLIT_ARGUMENTS_ON(OPTIONS wxrc_files wxrc_options ${ARGN}) -# ... -# endfunction() -# -# WXWIDGETS_ADD_RESOURCES(sources ${xrc_files} OPTIONS -e -o file.C) -# -# NOTE: This is a generic piece of code that should be renamed to -# SPLIT_ARGUMENTS_ON and put in a file serving the same purpose as -# FindPackageStandardArgs.cmake. At the time of this writing -# FindQt4.cmake has a QT4_EXTRACT_OPTIONS, which I basically copied -# here a bit more generalized. So, there are already two find modules -# using this approach. -# -function(WX_SPLIT_ARGUMENTS_ON _keyword _leftvar _rightvar) - # FIXME: Document that the input variables will be cleared. - #list(APPEND ${_leftvar} "") - #list(APPEND ${_rightvar} "") - set(${_leftvar} "") - set(${_rightvar} "") - - set(_doing_right FALSE) - foreach(element ${ARGN}) - if("${element}" STREQUAL "${_keyword}") - set(_doing_right TRUE) - else() - if(_doing_right) - list(APPEND ${_rightvar} "${element}") - else() - list(APPEND ${_leftvar} "${element}") - endif() - endif() - endforeach() - - set(${_leftvar} ${${_leftvar}} PARENT_SCOPE) - set(${_rightvar} ${${_rightvar}} PARENT_SCOPE) -endfunction() - -# -# WX_GET_DEPENDENCIES_FROM_XML( -# -# -# -# -# -# ) -# -# FIXME: Add documentation here... -# -function(WX_GET_DEPENDENCIES_FROM_XML - _depends - _match_patt - _clean_patt - _xml_contents - _depends_path - ) - - string(REGEX MATCHALL - ${_match_patt} - dep_file_list - "${${_xml_contents}}" - ) - foreach(dep_file ${dep_file_list}) - string(REGEX REPLACE ${_clean_patt} "" dep_file "${dep_file}") - - # make the file have an absolute path - if(NOT IS_ABSOLUTE "${dep_file}") - set(dep_file "${${_depends_path}}/${dep_file}") - endif() - - # append file to dependency list - list(APPEND ${_depends} "${dep_file}") - endforeach() - - set(${_depends} ${${_depends}} PARENT_SCOPE) -endfunction() - -# -# WXWIDGETS_ADD_RESOURCES( -# OPTIONS [NO_CPP_CODE]) -# -# Adds a custom command for resource file compilation of the -# and appends the output files to . -# -# Example usages: -# WXWIDGETS_ADD_RESOURCES(sources xrc/main_frame.xrc) -# WXWIDGETS_ADD_RESOURCES(sources ${xrc_files} OPTIONS -e -o altname.cxx) -# -function(WXWIDGETS_ADD_RESOURCES _outfiles) - WX_SPLIT_ARGUMENTS_ON(OPTIONS rc_file_list rc_options ${ARGN}) - - # Parse files for dependencies. - set(rc_file_list_abs "") - set(rc_depends "") - foreach(rc_file ${rc_file_list}) - get_filename_component(depends_path ${rc_file} PATH) - - get_filename_component(rc_file_abs ${rc_file} ABSOLUTE) - list(APPEND rc_file_list_abs "${rc_file_abs}") - - # All files have absolute paths or paths relative to the location - # of the rc file. - file(READ "${rc_file_abs}" rc_file_contents) - - # get bitmap/bitmap2 files - WX_GET_DEPENDENCIES_FROM_XML( - rc_depends - "]*>" - rc_file_contents - depends_path - ) - - # get url files - WX_GET_DEPENDENCIES_FROM_XML( - rc_depends - "]*>" - rc_file_contents - depends_path - ) - - # get wxIcon files - WX_GET_DEPENDENCIES_FROM_XML( - rc_depends - "]*class=\"wxIcon\"[^<]+" - "^]*>" - rc_file_contents - depends_path - ) - endforeach() - - # - # Parse options. - # - # If NO_CPP_CODE option specified, then produce .xrs file rather - # than a .cpp file (i.e., don't add the default --cpp-code option). - list(FIND rc_options NO_CPP_CODE index) - if(index EQUAL -1) - list(APPEND rc_options --cpp-code) - # wxrc's default output filename for cpp code. - set(outfile resource.cpp) - else() - list(REMOVE_AT rc_options ${index}) - # wxrc's default output filename for xrs file. - set(outfile resource.xrs) - endif() - - # Get output name for use in ADD_CUSTOM_COMMAND. - # - short option scanning - list(FIND rc_options -o index) - if(NOT index EQUAL -1) - math(EXPR filename_index "${index} + 1") - list(GET rc_options ${filename_index} outfile) - #list(REMOVE_AT rc_options ${index} ${filename_index}) - endif() - # - long option scanning - string(REGEX MATCH "--output=[^;]*" outfile_opt "${rc_options}") - if(outfile_opt) - string(REPLACE "--output=" "" outfile "${outfile_opt}") - endif() - #string(REGEX REPLACE "--output=[^;]*;?" "" rc_options "${rc_options}") - #string(REGEX REPLACE ";$" "" rc_options "${rc_options}") - - if(NOT IS_ABSOLUTE "${outfile}") - set(outfile "${CMAKE_CURRENT_BINARY_DIR}/${outfile}") - endif() - add_custom_command( - OUTPUT "${outfile}" - COMMAND ${wxWidgets_wxrc_EXECUTABLE} ${rc_options} ${rc_file_list_abs} - DEPENDS ${rc_file_list_abs} ${rc_depends} - ) - - # Add generated header to output file list. - list(FIND rc_options -e short_index) - list(FIND rc_options --extra-cpp-code long_index) - if(NOT short_index EQUAL -1 OR NOT long_index EQUAL -1) - get_filename_component(outfile_ext ${outfile} EXT) - string(REPLACE "${outfile_ext}" ".h" outfile_header "${outfile}") - list(APPEND ${_outfiles} "${outfile_header}") - set_source_files_properties( - "${outfile_header}" PROPERTIES GENERATED TRUE - ) - endif() - - # Add generated file to output file list. - list(APPEND ${_outfiles} "${outfile}") - - set(${_outfiles} ${${_outfiles}} PARENT_SCOPE) -endfunction() - -cmake_policy(POP) diff --git a/build/PCHSupport.cmake b/build/PCHSupport.cmake deleted file mode 100644 index 78d0e84..0000000 --- a/build/PCHSupport.cmake +++ /dev/null @@ -1,336 +0,0 @@ -# - Try to find precompiled headers support for GCC 3.4 and 4.x -# Once done this will define: -# -# Variable: -# PCHSupport_FOUND -# -# Macro: -# ADD_PRECOMPILED_HEADER _targetName _input _dowarn -# ADD_PRECOMPILED_HEADER_TO_TARGET _targetName _input _pch_output_to_use _dowarn -# ADD_NATIVE_PRECOMPILED_HEADER _targetName _input _dowarn -# GET_NATIVE_PRECOMPILED_HEADER _targetName _input - -# SET_PRECOMPILED_HEADER: -# this was written by Ronnie for a staright forward call to set pch in Visual Studio -# which requires setting a create with a cpp file -# for other platforms this simply falls back to ADD_NATIVE_PRECOMPILED_HEADER -# SET_PRECOMPILED_HEADER targetName hFileName cppFileName - -IF(CMAKE_COMPILER_IS_GNUCXX) - - EXEC_PROGRAM( - ${CMAKE_CXX_COMPILER} - ARGS ${CMAKE_CXX_COMPILER_ARG1} -dumpversion - OUTPUT_VARIABLE gcc_compiler_version) - #MESSAGE("GCC Version: ${gcc_compiler_version}") - IF(gcc_compiler_version MATCHES "4\\.[0-9]\\.[0-9]") - SET(PCHSupport_FOUND TRUE) - ELSE(gcc_compiler_version MATCHES "4\\.[0-9]\\.[0-9]") - IF(gcc_compiler_version MATCHES "3\\.4\\.[0-9]") - SET(PCHSupport_FOUND TRUE) - ENDIF(gcc_compiler_version MATCHES "3\\.4\\.[0-9]") - ENDIF(gcc_compiler_version MATCHES "4\\.[0-9]\\.[0-9]") - - SET(_PCH_include_prefix "-I") - -ELSE(CMAKE_COMPILER_IS_GNUCXX) - IF(WIN32) - SET(PCHSupport_FOUND TRUE) # for experimental msvc support - SET(_PCH_include_prefix "/I") - ELSE(WIN32) - SET(PCHSupport_FOUND FALSE) - ENDIF(WIN32) -ENDIF(CMAKE_COMPILER_IS_GNUCXX) - - -MACRO(_PCH_GET_COMPILE_FLAGS _out_compile_flags) - - - STRING(TOUPPER "CMAKE_CXX_FLAGS_${CMAKE_BUILD_TYPE}" _flags_var_name) - SET(${_out_compile_flags} ${${_flags_var_name}} ) - - IF(CMAKE_COMPILER_IS_GNUCXX) - - GET_TARGET_PROPERTY(_targetType ${_PCH_current_target} TYPE) - IF(${_targetType} STREQUAL SHARED_LIBRARY) - LIST(APPEND ${_out_compile_flags} "${${_out_compile_flags}} -fPIC") - ENDIF(${_targetType} STREQUAL SHARED_LIBRARY) - - ELSE(CMAKE_COMPILER_IS_GNUCXX) - ## TODO ... ? or does it work out of the box - ENDIF(CMAKE_COMPILER_IS_GNUCXX) - - GET_DIRECTORY_PROPERTY(DIRINC INCLUDE_DIRECTORIES ) - FOREACH(item ${DIRINC}) - LIST(APPEND ${_out_compile_flags} "${_PCH_include_prefix}${item}") - ENDFOREACH(item) - - GET_DIRECTORY_PROPERTY(_directory_flags DEFINITIONS) - #MESSAGE("_directory_flags ${_directory_flags}" ) - LIST(APPEND ${_out_compile_flags} ${_directory_flags}) - LIST(APPEND ${_out_compile_flags} ${CMAKE_CXX_FLAGS} ) - - SEPARATE_ARGUMENTS(${_out_compile_flags}) - -ENDMACRO(_PCH_GET_COMPILE_FLAGS) - - -MACRO(_PCH_WRITE_PCHDEP_CXX _targetName _include_file _dephelp) - - SET(${_dephelp} ${CMAKE_CURRENT_BINARY_DIR}/${_targetName}_pch_dephelp.cxx) - FILE(WRITE ${${_dephelp}} -"#include \"${_include_file}\" -int testfunction() -{ - return 0; -} -" - ) - -ENDMACRO(_PCH_WRITE_PCHDEP_CXX ) - -MACRO(_PCH_GET_COMPILE_COMMAND out_command _input _output) - - FILE(TO_NATIVE_PATH ${_input} _native_input) - FILE(TO_NATIVE_PATH ${_output} _native_output) - - - IF(CMAKE_COMPILER_IS_GNUCXX) - IF(CMAKE_CXX_COMPILER_ARG1) - # remove leading space in compiler argument - STRING(REGEX REPLACE "^ +" "" pchsupport_compiler_cxx_arg1 ${CMAKE_CXX_COMPILER_ARG1}) - - SET(${out_command} - ${CMAKE_CXX_COMPILER} ${pchsupport_compiler_cxx_arg1} ${_compile_FLAGS} -x c++-header -o ${_output} ${_input} - ) - ELSE(CMAKE_CXX_COMPILER_ARG1) - SET(${out_command} - ${CMAKE_CXX_COMPILER} ${_compile_FLAGS} -x c++-header -o ${_output} ${_input} - ) - ENDIF(CMAKE_CXX_COMPILER_ARG1) - ELSE(CMAKE_COMPILER_IS_GNUCXX) - - SET(_dummy_str "#include <${_input}>") - FILE(WRITE ${CMAKE_CURRENT_BINARY_DIR}/pch_dummy.cpp ${_dummy_str}) - - SET(${out_command} - ${CMAKE_CXX_COMPILER} ${_compile_FLAGS} /c /Fp${_native_output} /Yc${_native_input} pch_dummy.cpp - ) - #/out:${_output} - - ENDIF(CMAKE_COMPILER_IS_GNUCXX) - -ENDMACRO(_PCH_GET_COMPILE_COMMAND ) - - - -MACRO(_PCH_GET_TARGET_COMPILE_FLAGS _cflags _header_name _pch_path _dowarn ) - - FILE(TO_NATIVE_PATH ${_pch_path} _native_pch_path) - - IF(CMAKE_COMPILER_IS_GNUCXX) - # for use with distcc and gcc >4.0.1 if preprocessed files are accessible - # on all remote machines set - # PCH_ADDITIONAL_COMPILER_FLAGS to -fpch-preprocess - # if you want warnings for invalid header files (which is very inconvenient - # if you have different versions of the headers for different build types - # you may set _pch_dowarn - IF (_dowarn) - SET(${_cflags} "${PCH_ADDITIONAL_COMPILER_FLAGS} -include ${CMAKE_CURRENT_BINARY_DIR}/${_header_name} -Winvalid-pch " ) - ELSE (_dowarn) - SET(${_cflags} "${PCH_ADDITIONAL_COMPILER_FLAGS} -include ${CMAKE_CURRENT_BINARY_DIR}/${_header_name} " ) - ENDIF (_dowarn) - ELSE(CMAKE_COMPILER_IS_GNUCXX) - - set(${_cflags} "/Fp${_native_pch_path} /Yu${_header_name}" ) - - ENDIF(CMAKE_COMPILER_IS_GNUCXX) - -ENDMACRO(_PCH_GET_TARGET_COMPILE_FLAGS ) - -MACRO(GET_PRECOMPILED_HEADER_OUTPUT _targetName _input _output) - GET_FILENAME_COMPONENT(_name ${_input} NAME) - GET_FILENAME_COMPONENT(_path ${_input} PATH) - SET(_output "${CMAKE_CURRENT_BINARY_DIR}/${_name}.gch/${_targetName}_${CMAKE_BUILD_TYPE}.h++") -ENDMACRO(GET_PRECOMPILED_HEADER_OUTPUT _targetName _input) - - -MACRO(ADD_PRECOMPILED_HEADER_TO_TARGET _targetName _input _pch_output_to_use ) - - # to do: test whether compiler flags match between target _targetName - # and _pch_output_to_use - GET_FILENAME_COMPONENT(_name ${_input} NAME) - - IF( "${ARGN}" STREQUAL "0") - SET(_dowarn 0) - ELSE( "${ARGN}" STREQUAL "0") - SET(_dowarn 1) - ENDIF("${ARGN}" STREQUAL "0") - - - _PCH_GET_TARGET_COMPILE_FLAGS(_target_cflags ${_name} ${_pch_output_to_use} ${_dowarn}) - # MESSAGE("Add flags ${_target_cflags} to ${_targetName} " ) - SET_TARGET_PROPERTIES(${_targetName} - PROPERTIES - COMPILE_FLAGS ${_target_cflags} - ) - - ADD_CUSTOM_TARGET(pch_Generate_${_targetName} - DEPENDS ${_pch_output_to_use} - ) - - ADD_DEPENDENCIES(${_targetName} pch_Generate_${_targetName} ) - -ENDMACRO(ADD_PRECOMPILED_HEADER_TO_TARGET) - -MACRO(ADD_PRECOMPILED_HEADER _targetName _input) - - SET(_PCH_current_target ${_targetName}) - - IF(NOT CMAKE_BUILD_TYPE) - MESSAGE(FATAL_ERROR - "This is the ADD_PRECOMPILED_HEADER macro. " - "You must set CMAKE_BUILD_TYPE!" - ) - ENDIF(NOT CMAKE_BUILD_TYPE) - - IF( "${ARGN}" STREQUAL "0") - SET(_dowarn 0) - ELSE( "${ARGN}" STREQUAL "0") - SET(_dowarn 1) - ENDIF("${ARGN}" STREQUAL "0") - - - GET_FILENAME_COMPONENT(_name ${_input} NAME) - GET_FILENAME_COMPONENT(_path ${_input} PATH) - GET_PRECOMPILED_HEADER_OUTPUT( ${_targetName} ${_input} _output) - - GET_FILENAME_COMPONENT(_outdir ${_output} PATH ) - - GET_TARGET_PROPERTY(_targetType ${_PCH_current_target} TYPE) - _PCH_WRITE_PCHDEP_CXX(${_targetName} ${_input} _pch_dephelp_cxx) - - IF(${_targetType} STREQUAL SHARED_LIBRARY) - ADD_LIBRARY(${_targetName}_pch_dephelp SHARED ${_pch_dephelp_cxx} ) - ELSE(${_targetType} STREQUAL SHARED_LIBRARY) - ADD_LIBRARY(${_targetName}_pch_dephelp STATIC ${_pch_dephelp_cxx}) - ENDIF(${_targetType} STREQUAL SHARED_LIBRARY) - - FILE(MAKE_DIRECTORY ${_outdir}) - - - _PCH_GET_COMPILE_FLAGS(_compile_FLAGS) - - #MESSAGE("_compile_FLAGS: ${_compile_FLAGS}") - #message("COMMAND ${CMAKE_CXX_COMPILER} ${_compile_FLAGS} -x c++-header -o ${_output} ${_input}") - SET_SOURCE_FILES_PROPERTIES(${CMAKE_CURRENT_BINARY_DIR}/${_name} PROPERTIES GENERATED 1) - ADD_CUSTOM_COMMAND( - OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${_name} - COMMAND ${CMAKE_COMMAND} -E copy ${_input} ${CMAKE_CURRENT_BINARY_DIR}/${_name} # ensure same directory! Required by gcc - DEPENDS ${_input} - ) - - #message("_command ${_input} ${_output}") - _PCH_GET_COMPILE_COMMAND(_command ${CMAKE_CURRENT_BINARY_DIR}/${_name} ${_output} ) - - #message(${_input} ) - #message("_output ${_output}") - - ADD_CUSTOM_COMMAND( - OUTPUT ${_output} - COMMAND ${_command} - DEPENDS ${_input} ${CMAKE_CURRENT_BINARY_DIR}/${_name} ${_targetName}_pch_dephelp - ) - - - ADD_PRECOMPILED_HEADER_TO_TARGET(${_targetName} ${_input} ${_output} ${_dowarn}) -ENDMACRO(ADD_PRECOMPILED_HEADER) - - -# Generates the use of precompiled in a target, -# without using depency targets (2 extra for each target) -# Using Visual, must also add ${_targetName}_pch to sources -# Not needed by Xcode - -MACRO(GET_NATIVE_PRECOMPILED_HEADER _targetName _input) - - if(CMAKE_GENERATOR MATCHES Visual*) - - SET(_dummy_str "#include \"${_input}\"\n" - "// This is required to suppress LNK4221. Very annoying.\n" - "void *g_${_targetName}Dummy = 0\;\n") - - # Use of cxx extension for generated files (as Qt does) - SET(${_targetName}_pch ${CMAKE_CURRENT_BINARY_DIR}/${_targetName}_pch.cxx) - if(EXISTS ${${_targetName}_pch}) - # Check if contents is the same, if not rewrite - # todo - else(EXISTS ${${_targetName}_pch}) - FILE(WRITE ${${_targetName}_pch} ${_dummy_str}) - endif(EXISTS ${${_targetName}_pch}) - endif(CMAKE_GENERATOR MATCHES Visual*) - -ENDMACRO(GET_NATIVE_PRECOMPILED_HEADER) - - -MACRO(ADD_NATIVE_PRECOMPILED_HEADER _targetName _input) - - IF( "${ARGN}" STREQUAL "0") - SET(_dowarn 0) - ELSE( "${ARGN}" STREQUAL "0") - SET(_dowarn 1) - ENDIF("${ARGN}" STREQUAL "0") - - if(CMAKE_GENERATOR MATCHES Visual*) - # Auto include the precompile (useful for moc processing, since the use of - # precompiled is specified at the target level - # and I don't want to specifiy /F- for each moc/res/ui generated files (using Qt) - - GET_TARGET_PROPERTY(oldProps ${_targetName} COMPILE_FLAGS) - if (${oldProps} MATCHES NOTFOUND) - SET(oldProps "") - endif(${oldProps} MATCHES NOTFOUND) - - SET(newProperties "${oldProps} /Yu\"${_input}\" /FI\"${_input}\"") - SET_TARGET_PROPERTIES(${_targetName} PROPERTIES COMPILE_FLAGS "${newProperties}") - - #also inlude ${oldProps} to have the same compile options - SET_SOURCE_FILES_PROPERTIES(${${_targetName}_pch} PROPERTIES COMPILE_FLAGS "${oldProps} /Yc\"${_input}\"") - - else(CMAKE_GENERATOR MATCHES Visual*) - - if (CMAKE_GENERATOR MATCHES Xcode) - # For Xcode, cmake needs my patch to process - # GCC_PREFIX_HEADER and GCC_PRECOMPILE_PREFIX_HEADER as target properties - - GET_TARGET_PROPERTY(oldProps ${_targetName} COMPILE_FLAGS) - if (${oldProps} MATCHES NOTFOUND) - SET(oldProps "") - endif(${oldProps} MATCHES NOTFOUND) - - # When buiding out of the tree, precompiled may not be located - # Use full path instead. - GET_FILENAME_COMPONENT(fullPath ${_input} ABSOLUTE) - - SET_TARGET_PROPERTIES(${_targetName} PROPERTIES XCODE_ATTRIBUTE_GCC_PREFIX_HEADER "${fullPath}") - SET_TARGET_PROPERTIES(${_targetName} PROPERTIES XCODE_ATTRIBUTE_GCC_PRECOMPILE_PREFIX_HEADER "YES") - - else (CMAKE_GENERATOR MATCHES Xcode) - - #Fallback to the "old" precompiled suppport - #ADD_PRECOMPILED_HEADER(${_targetName} ${_input} ${_dowarn}) - endif(CMAKE_GENERATOR MATCHES Xcode) - endif(CMAKE_GENERATOR MATCHES Visual*) - -ENDMACRO(ADD_NATIVE_PRECOMPILED_HEADER) - -MACRO(SET_PRECOMPILED_HEADER targetName hFileName cppFileName) - ADD_NATIVE_PRECOMPILED_HEADER(${targetName} ${hFileName}) - if(WIN32) - GET_TARGET_PROPERTY(oldProps "${cppFileName}" COMPILE_FLAGS) - if (${oldProps} MATCHES NOTFOUND) - SET(oldProps "") - endif(${oldProps} MATCHES NOTFOUND) - SET_SOURCE_FILES_PROPERTIES(${cppFileName} PROPERTIES COMPILE_FLAGS "${oldprops} /Yc\"${hFileName}\"") - endif(WIN32) -ENDMACRO(SET_PRECOMPILED_HEADER) diff --git a/build/cm.bat b/build/cm.bat deleted file mode 100644 index 625ef98..0000000 --- a/build/cm.bat +++ /dev/null @@ -1,25 +0,0 @@ -rem @echo off -IF "%1" == "" GOTO NO_PARAMS -IF "%1" == "x86" GOTO CMAKE_86 -IF "%1" == "86" GOTO CMAKE_86 -IF "%1" == "32" GOTO CMAKE_86 -IF "%1" == "x64" GOTO CMAKE_64 -IF "%1" == "64" GOTO CMAKE_64 - -ECHO %1 -ECHO "Nothing to do" -GOTO End - -:CMAKE_86 - ECHO "Configuring for x86" - cm86.bat - GOTO End -:CMAKE_64 - ECHO "Configuring for x64" - cm64.bat - GOTO End -:NO_PARAMS - ECHO "No parameters specified" - IF EXIST "%ProgramW6432%" GOTO CMAKE_64 - GOTO CMAKE_86 -:End diff --git a/build/cm.sh b/build/cm.sh deleted file mode 100755 index 1de4a7a..0000000 --- a/build/cm.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/bash - -rm -dr Mac -mkdir Mac -cd Mac -cmake ../ -G "Xcode" -cd .. diff --git a/build/cm64.bat b/build/cm64.bat deleted file mode 100644 index 4dcbbe8..0000000 --- a/build/cm64.bat +++ /dev/null @@ -1,7 +0,0 @@ -rem @echo off -rmdir /S /Q Win -mkdir Win -cd Win -cmake ../ -G "Visual Studio 17 2022" -copy ..\wxModularHost.sln64 .\wxModularHost.sln -cd .. diff --git a/build/cm86.bat b/build/cm86.bat deleted file mode 100644 index 4bae862..0000000 --- a/build/cm86.bat +++ /dev/null @@ -1,7 +0,0 @@ -rem @echo off -rmdir /S /Q Win -mkdir Win -cd Win -cmake ../ -G "Visual Studio 17 2022" -A Win32 -copy ..\wxModularHost.sln32 .\wxModularHost.sln -cd .. diff --git a/build/cmAppleMac.sh b/build/cmAppleMac.sh deleted file mode 100644 index 9e57352..0000000 --- a/build/cmAppleMac.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/bash - -rm -dr Mac -mkdir Mac -cd Mac -cmake ../ -DCMAKE_CXX_STANDARD_LIBRARIES="-lwx_osx_cocoau_aui-3.2" -cd .. diff --git a/build/cmLinux.sh b/build/cmLinux.sh deleted file mode 100755 index c0d0e63..0000000 --- a/build/cmLinux.sh +++ /dev/null @@ -1,27 +0,0 @@ -#!/bin/bash -echo OS Type: $OSTYPE - -# ---------------------------------- -# build Debug configuration makefile -# ---------------------------------- -echo building Debug configuration makefile -echo directory "LinuxDebug" -rm -dr "LinuxDebug" -mkdir "LinuxDebug" -cd "LinuxDebug" -cmake -G "Eclipse CDT4 - Unix Makefiles" -DCMAKE_BUILD_TYPE:STRING=Debug ../ -# cmake -DCMAKE_BUILD_TYPE:STRING=Debug ../ -cd .. - -# ---------------------------------- -# build Release configuration makefile -# ---------------------------------- -echo building Release configuration makefile -echo directory "LinuxRelease" -rm -dr "LinuxRelease" -mkdir "LinuxRelease" -cd "LinuxRelease" -cmake -G "Eclipse CDT4 - Unix Makefiles" -DCMAKE_BUILD_TYPE:STRING=Release ../ -# cmake -DCMAKE_BUILD_TYPE:STRING=Release ../ -cd .. - diff --git a/build/wxModularHost.sln32 b/build/wxModularHost.sln32 deleted file mode 100644 index 18c872f..0000000 --- a/build/wxModularHost.sln32 +++ /dev/null @@ -1,89 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 17 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ALL_BUILD", "ALL_BUILD.vcxproj", "{2DE4A902-0D74-3DF1-BBB3-6F1E75BC5C87}" - ProjectSection(ProjectDependencies) = postProject - {85E2C45A-3E70-38FB-80FC-42B0F7BA4014} = {85E2C45A-3E70-38FB-80FC-42B0F7BA4014} - {DB9A1667-1D3A-3B2E-803D-86D7EB3E85C5} = {DB9A1667-1D3A-3B2E-803D-86D7EB3E85C5} - {A793772E-ACC6-4B21-80A4-F4A7952754AC} = {A793772E-ACC6-4B21-80A4-F4A7952754AC} - {B9B972FB-3C1F-3FE3-85BD-E714A07BC659} = {B9B972FB-3C1F-3FE3-85BD-E714A07BC659} - {E6CD9F9A-05BD-38E0-9F64-B3F8B7610FBD} = {E6CD9F9A-05BD-38E0-9F64-B3F8B7610FBD} - {ED4A979D-43E8-3D24-97E0-53E2D2C7B5BD} = {ED4A979D-43E8-3D24-97E0-53E2D2C7B5BD} - {47ABA340-A92C-3524-9020-D816EB794174} = {47ABA340-A92C-3524-9020-D816EB794174} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SampleGuiPlugin1", "..\..\SampleGuiPlugin1\Win\SampleGuiPlugin1.vcxproj", "{85E2C45A-3E70-38FB-80FC-42B0F7BA4014}" - ProjectSection(ProjectDependencies) = postProject - {B9B972FB-3C1F-3FE3-85BD-E714A07BC659} = {B9B972FB-3C1F-3FE3-85BD-E714A07BC659} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SampleGuiPlugin2", "..\..\SampleGuiPlugin2\Win\SampleGuiPlugin2.vcxproj", "{DB9A1667-1D3A-3B2E-803D-86D7EB3E85C5}" - ProjectSection(ProjectDependencies) = postProject - {B9B972FB-3C1F-3FE3-85BD-E714A07BC659} = {B9B972FB-3C1F-3FE3-85BD-E714A07BC659} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ZERO_CHECK", "ZERO_CHECK.vcxproj", "{B9B972FB-3C1F-3FE3-85BD-E714A07BC659}" - ProjectSection(ProjectDependencies) = postProject - EndProjectSection -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "mswplugin_csharp", "..\..\mswplugin_csharp\Win\mswplugin_csharp.csproj", "{E6CD9F9A-05BD-38E0-9F64-B3F8B7610FBD}" - ProjectSection(ProjectDependencies) = postProject - {B9B972FB-3C1F-3FE3-85BD-E714A07BC659} = {B9B972FB-3C1F-3FE3-85BD-E714A07BC659} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "wxModularCore", "..\..\wxModularCore\Win\wxModularCore.vcxproj", "{ED4A979D-43E8-3D24-97E0-53E2D2C7B5BD}" - ProjectSection(ProjectDependencies) = postProject - {B9B972FB-3C1F-3FE3-85BD-E714A07BC659} = {B9B972FB-3C1F-3FE3-85BD-E714A07BC659} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "wxModularHost", "..\..\wxModularHost\Win\wxModularHost.vcxproj", "{47ABA340-A92C-3524-9020-D816EB794174}" - ProjectSection(ProjectDependencies) = postProject - {B9B972FB-3C1F-3FE3-85BD-E714A07BC659} = {B9B972FB-3C1F-3FE3-85BD-E714A07BC659} - {ED4A979D-43E8-3D24-97E0-53E2D2C7B5BD} = {ED4A979D-43E8-3D24-97E0-53E2D2C7B5BD} - EndProjectSection -EndProject -Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "mswplugin_vb", "..\..\mswplugin_vb\mswplugin_vb.vbproj", "{A793772E-ACC6-4B21-80A4-F4A7952754AC}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Win32 = Debug|Win32 - Release|Win32 = Release|Win32 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {2DE4A902-0D74-3DF1-BBB3-6F1E75BC5C87}.Debug|Win32.ActiveCfg = Debug|Win32 - {2DE4A902-0D74-3DF1-BBB3-6F1E75BC5C87}.Release|Win32.ActiveCfg = Release|Win32 - {85E2C45A-3E70-38FB-80FC-42B0F7BA4014}.Debug|Win32.ActiveCfg = Debug|Win32 - {85E2C45A-3E70-38FB-80FC-42B0F7BA4014}.Debug|Win32.Build.0 = Debug|Win32 - {85E2C45A-3E70-38FB-80FC-42B0F7BA4014}.Release|Win32.ActiveCfg = Release|Win32 - {85E2C45A-3E70-38FB-80FC-42B0F7BA4014}.Release|Win32.Build.0 = Release|Win32 - {DB9A1667-1D3A-3B2E-803D-86D7EB3E85C5}.Debug|Win32.ActiveCfg = Debug|Win32 - {DB9A1667-1D3A-3B2E-803D-86D7EB3E85C5}.Debug|Win32.Build.0 = Debug|Win32 - {DB9A1667-1D3A-3B2E-803D-86D7EB3E85C5}.Release|Win32.ActiveCfg = Release|Win32 - {DB9A1667-1D3A-3B2E-803D-86D7EB3E85C5}.Release|Win32.Build.0 = Release|Win32 - {B9B972FB-3C1F-3FE3-85BD-E714A07BC659}.Debug|Win32.ActiveCfg = Debug|Win32 - {B9B972FB-3C1F-3FE3-85BD-E714A07BC659}.Debug|Win32.Build.0 = Debug|Win32 - {B9B972FB-3C1F-3FE3-85BD-E714A07BC659}.Release|Win32.ActiveCfg = Release|Win32 - {B9B972FB-3C1F-3FE3-85BD-E714A07BC659}.Release|Win32.Build.0 = Release|Win32 - {E6CD9F9A-05BD-38E0-9F64-B3F8B7610FBD}.Debug|Win32.ActiveCfg = Debug|Win32 - {E6CD9F9A-05BD-38E0-9F64-B3F8B7610FBD}.Debug|Win32.Build.0 = Debug|Win32 - {E6CD9F9A-05BD-38E0-9F64-B3F8B7610FBD}.Release|Win32.ActiveCfg = Release|Win32 - {E6CD9F9A-05BD-38E0-9F64-B3F8B7610FBD}.Release|Win32.Build.0 = Release|Win32 - {ED4A979D-43E8-3D24-97E0-53E2D2C7B5BD}.Debug|Win32.ActiveCfg = Debug|Win32 - {ED4A979D-43E8-3D24-97E0-53E2D2C7B5BD}.Debug|Win32.Build.0 = Debug|Win32 - {ED4A979D-43E8-3D24-97E0-53E2D2C7B5BD}.Release|Win32.ActiveCfg = Release|Win32 - {ED4A979D-43E8-3D24-97E0-53E2D2C7B5BD}.Release|Win32.Build.0 = Release|Win32 - {47ABA340-A92C-3524-9020-D816EB794174}.Debug|Win32.ActiveCfg = Debug|Win32 - {47ABA340-A92C-3524-9020-D816EB794174}.Debug|Win32.Build.0 = Debug|Win32 - {47ABA340-A92C-3524-9020-D816EB794174}.Release|Win32.ActiveCfg = Release|Win32 - {47ABA340-A92C-3524-9020-D816EB794174}.Release|Win32.Build.0 = Release|Win32 - {A793772E-ACC6-4B21-80A4-F4A7952754AC}.Debug|Win32.ActiveCfg = Debug|Win32 - {A793772E-ACC6-4B21-80A4-F4A7952754AC}.Debug|Win32.Build.0 = Debug|Win32 - {A793772E-ACC6-4B21-80A4-F4A7952754AC}.Release|Win32.ActiveCfg = Release|Win32 - {A793772E-ACC6-4B21-80A4-F4A7952754AC}.Release|Win32.Build.0 = Release|Win32 - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {FAC590C9-0DBB-386A-90AD-E57D0D314630} - EndGlobalSection - GlobalSection(ExtensibilityAddIns) = postSolution - EndGlobalSection -EndGlobal diff --git a/build/wxModularHost.sln64 b/build/wxModularHost.sln64 deleted file mode 100644 index 34f5d18..0000000 --- a/build/wxModularHost.sln64 +++ /dev/null @@ -1,89 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 17 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ALL_BUILD", "ALL_BUILD.vcxproj", "{2DE4A902-0D74-3DF1-BBB3-6F1E75BC5C87}" - ProjectSection(ProjectDependencies) = postProject - {85E2C45A-3E70-38FB-80FC-42B0F7BA4014} = {85E2C45A-3E70-38FB-80FC-42B0F7BA4014} - {DB9A1667-1D3A-3B2E-803D-86D7EB3E85C5} = {DB9A1667-1D3A-3B2E-803D-86D7EB3E85C5} - {A793772E-ACC6-4B21-80A4-F4A7952754AC} = {A793772E-ACC6-4B21-80A4-F4A7952754AC} - {B9B972FB-3C1F-3FE3-85BD-E714A07BC659} = {B9B972FB-3C1F-3FE3-85BD-E714A07BC659} - {E6CD9F9A-05BD-38E0-9F64-B3F8B7610FBD} = {E6CD9F9A-05BD-38E0-9F64-B3F8B7610FBD} - {ED4A979D-43E8-3D24-97E0-53E2D2C7B5BD} = {ED4A979D-43E8-3D24-97E0-53E2D2C7B5BD} - {47ABA340-A92C-3524-9020-D816EB794174} = {47ABA340-A92C-3524-9020-D816EB794174} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SampleGuiPlugin1", "..\..\SampleGuiPlugin1\Win\SampleGuiPlugin1.vcxproj", "{85E2C45A-3E70-38FB-80FC-42B0F7BA4014}" - ProjectSection(ProjectDependencies) = postProject - {B9B972FB-3C1F-3FE3-85BD-E714A07BC659} = {B9B972FB-3C1F-3FE3-85BD-E714A07BC659} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SampleGuiPlugin2", "..\..\SampleGuiPlugin2\Win\SampleGuiPlugin2.vcxproj", "{DB9A1667-1D3A-3B2E-803D-86D7EB3E85C5}" - ProjectSection(ProjectDependencies) = postProject - {B9B972FB-3C1F-3FE3-85BD-E714A07BC659} = {B9B972FB-3C1F-3FE3-85BD-E714A07BC659} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ZERO_CHECK", "ZERO_CHECK.vcxproj", "{B9B972FB-3C1F-3FE3-85BD-E714A07BC659}" - ProjectSection(ProjectDependencies) = postProject - EndProjectSection -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "mswplugin_csharp", "..\..\mswplugin_csharp\Win\mswplugin_csharp.csproj", "{E6CD9F9A-05BD-38E0-9F64-B3F8B7610FBD}" - ProjectSection(ProjectDependencies) = postProject - {B9B972FB-3C1F-3FE3-85BD-E714A07BC659} = {B9B972FB-3C1F-3FE3-85BD-E714A07BC659} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "wxModularCore", "..\..\wxModularCore\Win\wxModularCore.vcxproj", "{ED4A979D-43E8-3D24-97E0-53E2D2C7B5BD}" - ProjectSection(ProjectDependencies) = postProject - {B9B972FB-3C1F-3FE3-85BD-E714A07BC659} = {B9B972FB-3C1F-3FE3-85BD-E714A07BC659} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "wxModularHost", "..\..\wxModularHost\Win\wxModularHost.vcxproj", "{47ABA340-A92C-3524-9020-D816EB794174}" - ProjectSection(ProjectDependencies) = postProject - {B9B972FB-3C1F-3FE3-85BD-E714A07BC659} = {B9B972FB-3C1F-3FE3-85BD-E714A07BC659} - {ED4A979D-43E8-3D24-97E0-53E2D2C7B5BD} = {ED4A979D-43E8-3D24-97E0-53E2D2C7B5BD} - EndProjectSection -EndProject -Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "mswplugin_vb", "..\..\mswplugin_vb\mswplugin_vb.vbproj", "{A793772E-ACC6-4B21-80A4-F4A7952754AC}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|x64 = Debug|x64 - Release|x64 = Release|x64 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {2DE4A902-0D74-3DF1-BBB3-6F1E75BC5C87}.Debug|x64.ActiveCfg = Debug|x64 - {2DE4A902-0D74-3DF1-BBB3-6F1E75BC5C87}.Release|x64.ActiveCfg = Release|x64 - {85E2C45A-3E70-38FB-80FC-42B0F7BA4014}.Debug|x64.ActiveCfg = Debug|x64 - {85E2C45A-3E70-38FB-80FC-42B0F7BA4014}.Debug|x64.Build.0 = Debug|x64 - {85E2C45A-3E70-38FB-80FC-42B0F7BA4014}.Release|x64.ActiveCfg = Release|x64 - {85E2C45A-3E70-38FB-80FC-42B0F7BA4014}.Release|x64.Build.0 = Release|x64 - {DB9A1667-1D3A-3B2E-803D-86D7EB3E85C5}.Debug|x64.ActiveCfg = Debug|x64 - {DB9A1667-1D3A-3B2E-803D-86D7EB3E85C5}.Debug|x64.Build.0 = Debug|x64 - {DB9A1667-1D3A-3B2E-803D-86D7EB3E85C5}.Release|x64.ActiveCfg = Release|x64 - {DB9A1667-1D3A-3B2E-803D-86D7EB3E85C5}.Release|x64.Build.0 = Release|x64 - {B9B972FB-3C1F-3FE3-85BD-E714A07BC659}.Debug|x64.ActiveCfg = Debug|x64 - {B9B972FB-3C1F-3FE3-85BD-E714A07BC659}.Debug|x64.Build.0 = Debug|x64 - {B9B972FB-3C1F-3FE3-85BD-E714A07BC659}.Release|x64.ActiveCfg = Release|x64 - {B9B972FB-3C1F-3FE3-85BD-E714A07BC659}.Release|x64.Build.0 = Release|x64 - {E6CD9F9A-05BD-38E0-9F64-B3F8B7610FBD}.Debug|x64.ActiveCfg = Debug|x64 - {E6CD9F9A-05BD-38E0-9F64-B3F8B7610FBD}.Debug|x64.Build.0 = Debug|x64 - {E6CD9F9A-05BD-38E0-9F64-B3F8B7610FBD}.Release|x64.ActiveCfg = Release|x64 - {E6CD9F9A-05BD-38E0-9F64-B3F8B7610FBD}.Release|x64.Build.0 = Release|x64 - {ED4A979D-43E8-3D24-97E0-53E2D2C7B5BD}.Debug|x64.ActiveCfg = Debug|x64 - {ED4A979D-43E8-3D24-97E0-53E2D2C7B5BD}.Debug|x64.Build.0 = Debug|x64 - {ED4A979D-43E8-3D24-97E0-53E2D2C7B5BD}.Release|x64.ActiveCfg = Release|x64 - {ED4A979D-43E8-3D24-97E0-53E2D2C7B5BD}.Release|x64.Build.0 = Release|x64 - {47ABA340-A92C-3524-9020-D816EB794174}.Debug|x64.ActiveCfg = Debug|x64 - {47ABA340-A92C-3524-9020-D816EB794174}.Debug|x64.Build.0 = Debug|x64 - {47ABA340-A92C-3524-9020-D816EB794174}.Release|x64.ActiveCfg = Release|x64 - {47ABA340-A92C-3524-9020-D816EB794174}.Release|x64.Build.0 = Release|x64 - {A793772E-ACC6-4B21-80A4-F4A7952754AC}.Debug|x64.ActiveCfg = Debug|x64 - {A793772E-ACC6-4B21-80A4-F4A7952754AC}.Debug|x64.Build.0 = Debug|x64 - {A793772E-ACC6-4B21-80A4-F4A7952754AC}.Release|x64.ActiveCfg = Release|x64 - {A793772E-ACC6-4B21-80A4-F4A7952754AC}.Release|x64.Build.0 = Release|x64 - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {FAC590C9-0DBB-386A-90AD-E57D0D314630} - EndGlobalSection - GlobalSection(ExtensibilityAddIns) = postSolution - EndGlobalSection -EndGlobal diff --git a/include/qedit.h b/include/qedit.h new file mode 100644 index 0000000..169f07b --- /dev/null +++ b/include/qedit.h @@ -0,0 +1,89 @@ +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef __qedit_h__ +#define __qedit_h__ + +/////////////////////////////////////////////////////////////////////////////////// + +#pragma once + +/////////////////////////////////////////////////////////////////////////////////// + +interface +ISampleGrabberCB +: + public IUnknown +{ + virtual STDMETHODIMP SampleCB( double SampleTime, IMediaSample *pSample ) = 0; + virtual STDMETHODIMP BufferCB( double SampleTime, BYTE *pBuffer, long BufferLen ) = 0; +}; + +/////////////////////////////////////////////////////////////////////////////////// + +static +const +IID IID_ISampleGrabberCB = { 0x0579154A, 0x2B53, 0x4994, { 0xB0, 0xD0, 0xE7, 0x73, 0x14, 0x8E, 0xFF, 0x85 } }; + +/////////////////////////////////////////////////////////////////////////////////// + +interface +ISampleGrabber +: + public IUnknown +{ + virtual HRESULT STDMETHODCALLTYPE SetOneShot( BOOL OneShot ) = 0; + virtual HRESULT STDMETHODCALLTYPE SetMediaType( const AM_MEDIA_TYPE *pType ) = 0; + virtual HRESULT STDMETHODCALLTYPE GetConnectedMediaType( AM_MEDIA_TYPE *pType ) = 0; + virtual HRESULT STDMETHODCALLTYPE SetBufferSamples( BOOL BufferThem ) = 0; + virtual HRESULT STDMETHODCALLTYPE GetCurrentBuffer( long *pBufferSize, long *pBuffer ) = 0; + virtual HRESULT STDMETHODCALLTYPE GetCurrentSample( IMediaSample **ppSample ) = 0; + virtual HRESULT STDMETHODCALLTYPE SetCallback( ISampleGrabberCB *pCallback, long WhichMethodToCallback ) = 0; +}; + +/////////////////////////////////////////////////////////////////////////////////// + +static +const +IID IID_ISampleGrabber = { 0x6B652FFF, 0x11FE, 0x4fce, { 0x92, 0xAD, 0x02, 0x66, 0xB5, 0xD7, 0xC7, 0x8F } }; + +/////////////////////////////////////////////////////////////////////////////////// + +static +const +CLSID CLSID_SampleGrabber = { 0xC1F400A0, 0x3F08, 0x11d3, { 0x9F, 0x0B, 0x00, 0x60, 0x08, 0x03, 0x9E, 0x37 } }; + +/////////////////////////////////////////////////////////////////////////////////// + +static +const +CLSID CLSID_NullRenderer = { 0xC1F400A4, 0x3F08, 0x11d3, { 0x9F, 0x0B, 0x00, 0x60, 0x08, 0x03, 0x9E, 0x37 } }; + +/////////////////////////////////////////////////////////////////////////////////// + +static +const +CLSID CLSID_VideoEffects1Category = { 0xcc7bfb42, 0xf175, 0x11d1, { 0xa3, 0x92, 0x0, 0xe0, 0x29, 0x1f, 0x39, 0x59 } }; + +/////////////////////////////////////////////////////////////////////////////////// + +static +const +CLSID CLSID_VideoEffects2Category = { 0xcc7bfb43, 0xf175, 0x11d1, { 0xa3, 0x92, 0x0, 0xe0, 0x29, 0x1f, 0x39, 0x59 } }; + +/////////////////////////////////////////////////////////////////////////////////// + +static +const +CLSID CLSID_AudioEffects1Category = { 0xcc7bfb44, 0xf175, 0x11d1, { 0xa3, 0x92, 0x0, 0xe0, 0x29, 0x1f, 0x39, 0x59 } }; + +/////////////////////////////////////////////////////////////////////////////////// + +static +const +CLSID CLSID_AudioEffects2Category = { 0xcc7bfb45, 0xf175, 0x11d1, { 0xa3, 0x92, 0x0, 0xe0, 0x29, 0x1f, 0x39, 0x59 } }; + +/////////////////////////////////////////////////////////////////////////////////// + +#endif + +/////////////////////////////////////////////////////////////////////////////////// \ No newline at end of file diff --git a/include/stdwx.cpp b/include/stdwx.cpp index eae3e7a..82fa7ac 100644 --- a/include/stdwx.cpp +++ b/include/stdwx.cpp @@ -1 +1 @@ -#include "stdwx.h" +#include "stdwx.h" \ No newline at end of file diff --git a/include/stdwx.h b/include/stdwx.h index e803d0f..12201f6 100644 --- a/include/stdwx.h +++ b/include/stdwx.h @@ -1,50 +1,50 @@ -#ifndef IFLOOR_STDWX_H_ -#define IFLOOR_STDWX_H_ - -#if defined(WIN32) || defined(WINDOWS) -#include -#include -#define PLUGIN_EXPORTED_API WXEXPORT -#else -#define PLUGIN_EXPORTED_API extern "C" -#endif - -// SYSTEM INCLUDES -// For compilers that support precompilation, includes "wx/wx.h". -#include "wx/wxprec.h" -#ifdef __BORLANDC__ - #pragma hdrstop -#endif -//#ifndef WX_PRECOMP - #include "wx/wx.h" -//#endif -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -// APPLICATION INCLUDES -#endif +#ifndef IFLOOR_STDWX_H_ +#define IFLOOR_STDWX_H_ + +#if defined(WIN32) || defined(WINDOWS) +#include +#include +#define PLUGIN_EXPORTED_API WXEXPORT +#else +#define PLUGIN_EXPORTED_API extern "C" +#endif + +// SYSTEM INCLUDES +// For compilers that support precompilation, includes "wx/wx.h". +#include "wx/wxprec.h" +#ifdef __BORLANDC__ + #pragma hdrstop +#endif +//#ifndef WX_PRECOMP + #include "wx/wx.h" +//#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// APPLICATION INCLUDES +#endif diff --git a/wxGuiPluginBase/CMakeLists.txt b/wxGuiPluginBase/CMakeLists.txt index 7dfc7db..bbe9d2a 100644 --- a/wxGuiPluginBase/CMakeLists.txt +++ b/wxGuiPluginBase/CMakeLists.txt @@ -1,56 +1,30 @@ -set (SRCS - wxGuiPluginBase.cpp - wxGuiPluginWindowBase.cpp) -set (HEADERS - Declarations.h - wxGuiPluginBase.h - wxGuiPluginWindowBase.h) - -set(LIBRARY_NAME wxGuiPluginBase) - -if(WIN32) - set(SRCS ${SRCS} ${LIBRARY_NAME}.rc) - # Only for Windows: - # we add additional preprocessor definitons - set(PREPROCESSOR_DEFINITIONS ${PREPROCESSOR_DEFINITIONS}; - /D_USRDLL;/DDEMO_PLUGIN_EXPORTS;/D__STDC_CONSTANT_MACROS) -endif(WIN32) - -# Add 2 files for precompiled headers -set(SRCS ${SRCS} ${HEADERS} - ${PROJECT_ROOT_DIR}/include/stdwx.h - ${PROJECT_ROOT_DIR}/include/stdwx.cpp) - -# Set preprocessor definitions -add_definitions(${PREPROCESSOR_DEFINITIONS}) -# Set include directories -include_directories(${INCLUDE_DIRECTORIES} ${BASE_INCLUDE_DIRECTORIES}) -# Set library search paths -link_directories(${LINK_DIRECTORIES}) -# Setup the project name and assign the source files for this project -add_library(${LIBRARY_NAME} SHARED ${SRCS}) - -#Setup the output folder -set(DLL_DIR bin) -set(TARGET_LOCATION ${PROJECT_SOURCE_DIR}/${DLL_DIR}${LIB_SUFFIX}) -if(APPLE) - set(TARGET_LOCATION - ${TARGET_LOCATION}/$(CONFIGURATION)/${PROJECT_NAME}.app/Contents/Frameworks) -endif(APPLE) -if(LINUX OR APPLE) - get_target_property(RESULT_FULL_PATH ${LIBRARY_NAME} LOCATION) - get_filename_component(RESULT_FILE_NAME ${RESULT_FULL_PATH} NAME) -endif(LINUX OR APPLE) -set_target_properties(${LIBRARY_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${TARGET_LOCATION}) - -# Set additional dependencies -target_link_libraries(${LIBRARY_NAME} ${wxWidgets_LIBRARIES}) - -# Setup precompiled headers -target_precompile_headers(${LIBRARY_NAME} PRIVATE ${PROJECT_ROOT_DIR}/include/stdwx.h ${PROJECT_ROOT_DIR}/include/stdwx.cpp) - -if(LINUX OR APPLE) - add_custom_command(TARGET ${LIBRARY_NAME} POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy $ ${TARGET_LOCATION}/${RESULT_FILE_NAME} - ) -endif(LINUX OR APPLE) +set(SRCS + wxGuiPluginBase.cpp + wxGuiPluginWindowBase.cpp +) +set(HEADERS + Declarations.h + wxGuiPluginBase.h + wxGuiPluginWindowBase.h +) + +set(LIBRARY_NAME wxGuiPluginBase) + +if(WIN32) + set(SRCS ${SRCS} ${LIBRARY_NAME}.rc) + set(PREPROCESSOR_DEFINITIONS ${PREPROCESSOR_DEFINITIONS}; + /D_USRDLL;/DDEMO_PLUGIN_EXPORTS;/D__STDC_CONSTANT_MACROS) +endif(WIN32) + +set(SRCS ${SRCS} ${HEADERS} + ${PROJECT_ROOT_DIR}/include/stdwx.h + ${PROJECT_ROOT_DIR}/include/stdwx.cpp) + +add_definitions(${PREPROCESSOR_DEFINITIONS}) +include_directories(${INCLUDE_DIRECTORIES} ${BASE_INCLUDE_DIRECTORIES}) + +add_library(${LIBRARY_NAME} SHARED ${SRCS}) + +target_link_libraries(${LIBRARY_NAME} ${wxWidgets_LIBRARIES}) + +target_precompile_headers(${LIBRARY_NAME} PRIVATE ${PROJECT_ROOT_DIR}/include/stdwx.h) \ No newline at end of file diff --git a/wxGuiPluginBase/Declarations.h b/wxGuiPluginBase/Declarations.h index 13dd0d3..fd08279 100644 --- a/wxGuiPluginBase/Declarations.h +++ b/wxGuiPluginBase/Declarations.h @@ -1,14 +1,14 @@ -#ifndef _DECLARATIONS_H -#define _DECLARATIONS_H - -#if defined(__WXMSW__) -#ifdef DEMO_PLUGIN_EXPORTS -#define DEMO_API __declspec(dllexport) -#else -#define DEMO_API __declspec(dllimport) -#endif -#else -#define DEMO_API -#endif - -#endif // _DECLARATIONS_H +#ifndef _DECLARATIONS_H +#define _DECLARATIONS_H + +#if defined(__WXMSW__) +#ifdef DEMO_PLUGIN_EXPORTS +#define DEMO_API __declspec(dllexport) +#else +#define DEMO_API __declspec(dllimport) +#endif +#else +#define DEMO_API +#endif + +#endif // _DECLARATIONS_H diff --git a/wxGuiPluginBase/Win/wxGuiPluginBase.vcxproj b/wxGuiPluginBase/Win/wxGuiPluginBase.vcxproj new file mode 100644 index 0000000..85f1e0c --- /dev/null +++ b/wxGuiPluginBase/Win/wxGuiPluginBase.vcxproj @@ -0,0 +1,256 @@ + + + + x64 + + + + Debug + x64 + + + Release + x64 + + + + {016D40A8-D2BA-3C2B-8741-718BD5CE7B51} + Win32Proj + 10.0.26100.0 + x64 + wxGuiPluginBase + NoUpgrade + + + + DynamicLibrary + Unicode + v143 + + + DynamicLibrary + Unicode + v143 + + + + + + + + + + <_ProjectFileVersion>10.0.20506.1 + C:\Users\Admin\IFloor\projects\wxVideoProcessing\bin\ + wxGuiPluginBase.dir\Debug\ + wxGuiPluginBase + .dll + true + true + C:\Users\Admin\IFloor\projects\wxVideoProcessing\bin\ + wxGuiPluginBase.dir\Release\ + wxGuiPluginBase + .dll + false + true + + + + + C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\mswu;C:\Users\Admin\IFloor\libs\wxWidgets\include;C:\Users\Admin\IFloor\libs\opencv\build\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\Utils;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\MotionDetectorCore;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxJSON\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxxmlserializer\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxxmlserializer;%(AdditionalIncludeDirectories) + $(IntDir) + EnableFastChecks + + + ProgramDatabase + 4996 + Sync + + + Disabled + stdcpp17 + + true + Disabled + Use + C:/Users/Admin/IFloor/projects/wxVideoProcessing/wxGuiPluginBase/Win/CMakeFiles/wxGuiPluginBase.dir/Debug/cmake_pch.hxx + C:/Users/Admin/IFloor/projects/wxVideoProcessing/wxGuiPluginBase/Win/wxGuiPluginBase.dir/Debug//cmake_pch.pch + + MultiThreadedDebugDLL + true + + + false + %(PreprocessorDefinitions);WIN32;_WINDOWS;__WXDEBUG__=1;WIN32_LEAN_AND_MEAN;WXUSINGDLL;UNICODE;_UNICODE;_CRT_SECURE_NO_DEPRECATE;_USRDLL;DEMO_PLUGIN_EXPORTS;__STDC_CONSTANT_MACROS;CMAKE_INTDIR="Debug";wxGuiPluginBase_EXPORTS + $(IntDir) + false + + + %(PreprocessorDefinitions);WIN32;_DEBUG;_WINDOWS;__WXDEBUG__=1;WIN32_LEAN_AND_MEAN;WXUSINGDLL;UNICODE;_UNICODE;_CRT_SECURE_NO_DEPRECATE;_USRDLL;DEMO_PLUGIN_EXPORTS;__STDC_CONSTANT_MACROS;CMAKE_INTDIR=\"Debug\";wxGuiPluginBase_EXPORTS + C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\mswu;C:\Users\Admin\IFloor\libs\wxWidgets\include;C:\Users\Admin\IFloor\libs\opencv\build\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\Utils;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\MotionDetectorCore;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxJSON\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxxmlserializer\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxxmlserializer;%(AdditionalIncludeDirectories) + + + C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\mswu;C:\Users\Admin\IFloor\libs\wxWidgets\include;C:\Users\Admin\IFloor\libs\opencv\build\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\Utils;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\MotionDetectorCore;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxJSON\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxxmlserializer\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxxmlserializer;%(AdditionalIncludeDirectories) + $(ProjectDir)/$(IntDir) + %(Filename).h + %(Filename).tlb + %(Filename)_i.c + %(Filename)_p.c + + + C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxbase33ud.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33ud_core.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33ud_adv.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33ud_aui.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxbase33ud_net.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33ud_gl.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxbase33ud_xml.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33ud_propgrid.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33ud_html.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxpngd.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxtiffd.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxjpegd.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxzlibd.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxregexud.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxexpatd.lib;opengl32.lib;glu32.lib;winmm.lib;comctl32.lib;uuid.lib;oleacc.lib;uxtheme.lib;rpcrt4.lib;shlwapi.lib;version.lib;wsock32.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;comdlg32.lib;advapi32.lib + C:/Users/Admin/IFloor/projects/wxVideoProcessing/build/Win/lib;C:/Users/Admin/IFloor/projects/wxVideoProcessing/build/Win/lib/$(Configuration);%(AdditionalLibraryDirectories) + %(AdditionalOptions) /machine:x64 + + true + %(IgnoreSpecificDefaultLibraries) + + C:/Users/Admin/IFloor/projects/wxVideoProcessing/bin/lib/Debug/wxGuiPluginBase.lib + + C:/Users/Admin/IFloor/projects/wxVideoProcessing/bin/wxGuiPluginBase.pdb + + Console + + + false + + + + + C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\mswu;C:\Users\Admin\IFloor\libs\wxWidgets\include;C:\Users\Admin\IFloor\libs\opencv\build\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\Utils;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\MotionDetectorCore;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxJSON\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxxmlserializer\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxxmlserializer;%(AdditionalIncludeDirectories) + $(IntDir) + Default + + + 4996 + Sync + + + AnySuitable + stdcpp17 + + true + MaxSpeed + Use + C:/Users/Admin/IFloor/projects/wxVideoProcessing/wxGuiPluginBase/Win/CMakeFiles/wxGuiPluginBase.dir/Release/cmake_pch.hxx + C:/Users/Admin/IFloor/projects/wxVideoProcessing/wxGuiPluginBase/Win/wxGuiPluginBase.dir/Release//cmake_pch.pch + + MultiThreadedDLL + true + + + false + %(PreprocessorDefinitions);WIN32;_WINDOWS;NDEBUG;WIN32_LEAN_AND_MEAN;WXUSINGDLL;UNICODE;_UNICODE;_CRT_SECURE_NO_DEPRECATE;_USRDLL;DEMO_PLUGIN_EXPORTS;__STDC_CONSTANT_MACROS;CMAKE_INTDIR="Release";wxGuiPluginBase_EXPORTS + $(IntDir) + + + false + + + %(PreprocessorDefinitions);WIN32;_WINDOWS;NDEBUG;WIN32_LEAN_AND_MEAN;WXUSINGDLL;UNICODE;_UNICODE;_CRT_SECURE_NO_DEPRECATE;_USRDLL;DEMO_PLUGIN_EXPORTS;__STDC_CONSTANT_MACROS;CMAKE_INTDIR=\"Release\";wxGuiPluginBase_EXPORTS + C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\mswu;C:\Users\Admin\IFloor\libs\wxWidgets\include;C:\Users\Admin\IFloor\libs\opencv\build\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\Utils;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\MotionDetectorCore;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxJSON\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxxmlserializer\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxxmlserializer;%(AdditionalIncludeDirectories) + + + C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\mswu;C:\Users\Admin\IFloor\libs\wxWidgets\include;C:\Users\Admin\IFloor\libs\opencv\build\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\Utils;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\MotionDetectorCore;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxJSON\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxxmlserializer\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxxmlserializer;%(AdditionalIncludeDirectories) + $(ProjectDir)/$(IntDir) + %(Filename).h + %(Filename).tlb + %(Filename)_i.c + %(Filename)_p.c + + + C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxbase33u.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33u_core.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33u_adv.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33u_aui.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxbase33u_net.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33u_gl.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxbase33u_xml.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33u_propgrid.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33u_html.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxpng.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxtiff.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxjpeg.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxzlib.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxregexu.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxexpat.lib;opengl32.lib;glu32.lib;winmm.lib;comctl32.lib;uuid.lib;oleacc.lib;uxtheme.lib;rpcrt4.lib;shlwapi.lib;version.lib;wsock32.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;comdlg32.lib;advapi32.lib + C:/Users/Admin/IFloor/projects/wxVideoProcessing/build/Win/lib;C:/Users/Admin/IFloor/projects/wxVideoProcessing/build/Win/lib/$(Configuration);%(AdditionalLibraryDirectories) + %(AdditionalOptions) /machine:x64 + + false + %(IgnoreSpecificDefaultLibraries) + + C:/Users/Admin/IFloor/projects/wxVideoProcessing/bin/lib/Release/wxGuiPluginBase.lib + + C:/Users/Admin/IFloor/projects/wxVideoProcessing/bin/wxGuiPluginBase.pdb + + Console + + + false + + + + + Always + Building Custom Rule C:/Users/Admin/IFloor/projects/wxVideoProcessing/wxGuiPluginBase/CMakeLists.txt + setlocal +"C:\Program Files\CMake\bin\cmake.exe" -SC:/Users/Admin/IFloor/projects/wxVideoProcessing/build -BC:/Users/Admin/IFloor/projects/wxVideoProcessing/build/Win --check-stamp-file C:/Users/Admin/IFloor/projects/wxVideoProcessing/wxGuiPluginBase/Win/CMakeFiles/generate.stamp +if %errorlevel% neq 0 goto :cmEnd +:cmEnd +endlocal & call :cmErrorLevel %errorlevel% & goto :cmDone +:cmErrorLevel +exit /b %1 +:cmDone +if %errorlevel% neq 0 goto :VCEnd + %(AdditionalInputs) + C:\Users\Admin\IFloor\projects\wxVideoProcessing\wxGuiPluginBase\Win\CMakeFiles\generate.stamp + false + Building Custom Rule C:/Users/Admin/IFloor/projects/wxVideoProcessing/wxGuiPluginBase/CMakeLists.txt + setlocal +"C:\Program Files\CMake\bin\cmake.exe" -SC:/Users/Admin/IFloor/projects/wxVideoProcessing/build -BC:/Users/Admin/IFloor/projects/wxVideoProcessing/build/Win --check-stamp-file C:/Users/Admin/IFloor/projects/wxVideoProcessing/wxGuiPluginBase/Win/CMakeFiles/generate.stamp +if %errorlevel% neq 0 goto :cmEnd +:cmEnd +endlocal & call :cmErrorLevel %errorlevel% & goto :cmDone +:cmErrorLevel +exit /b %1 +:cmDone +if %errorlevel% neq 0 goto :VCEnd + %(AdditionalInputs) + C:\Users\Admin\IFloor\projects\wxVideoProcessing\wxGuiPluginBase\Win\CMakeFiles\generate.stamp + false + + + + + C:/Users/Admin/IFloor/projects/wxVideoProcessing/wxGuiPluginBase/Win/CMakeFiles/wxGuiPluginBase.dir/Debug/cmake_pch.hxx;%(ForcedIncludeFiles) + Create + C:/Users/Admin/IFloor/projects/wxVideoProcessing/wxGuiPluginBase/Win/CMakeFiles/wxGuiPluginBase.dir/Debug/cmake_pch.hxx + C:/Users/Admin/IFloor/projects/wxVideoProcessing/wxGuiPluginBase/Win/wxGuiPluginBase.dir/Debug//cmake_pch.pch + C:/Users/Admin/IFloor/projects/wxVideoProcessing/wxGuiPluginBase/Win/CMakeFiles/wxGuiPluginBase.dir/Release/cmake_pch.hxx;%(ForcedIncludeFiles) + Create + C:/Users/Admin/IFloor/projects/wxVideoProcessing/wxGuiPluginBase/Win/CMakeFiles/wxGuiPluginBase.dir/Release/cmake_pch.hxx + C:/Users/Admin/IFloor/projects/wxVideoProcessing/wxGuiPluginBase/Win/wxGuiPluginBase.dir/Release//cmake_pch.pch + + + C:/Users/Admin/IFloor/projects/wxVideoProcessing/wxGuiPluginBase/Win/CMakeFiles/wxGuiPluginBase.dir/Debug/cmake_pch.hxx;%(ForcedIncludeFiles) + C:/Users/Admin/IFloor/projects/wxVideoProcessing/wxGuiPluginBase/Win/CMakeFiles/wxGuiPluginBase.dir/Release/cmake_pch.hxx;%(ForcedIncludeFiles) + + + C:/Users/Admin/IFloor/projects/wxVideoProcessing/wxGuiPluginBase/Win/CMakeFiles/wxGuiPluginBase.dir/Debug/cmake_pch.hxx;%(ForcedIncludeFiles) + C:/Users/Admin/IFloor/projects/wxVideoProcessing/wxGuiPluginBase/Win/CMakeFiles/wxGuiPluginBase.dir/Release/cmake_pch.hxx;%(ForcedIncludeFiles) + + + NotUsing + NotUsing + + + + + + + C:/Users/Admin/IFloor/projects/wxVideoProcessing/wxGuiPluginBase/Win/CMakeFiles/wxGuiPluginBase.dir/Debug/cmake_pch.hxx;%(ForcedIncludeFiles) + C:/Users/Admin/IFloor/projects/wxVideoProcessing/wxGuiPluginBase/Win/CMakeFiles/wxGuiPluginBase.dir/Release/cmake_pch.hxx;%(ForcedIncludeFiles) + + + + + + + + {6F4BDD20-5B99-3029-BDF0-7D719B283AF5} + ZERO_CHECK + false + Never + + + + + + \ No newline at end of file diff --git a/wxGuiPluginBase/Win/wxGuiPluginBase.vcxproj.filters b/wxGuiPluginBase/Win/wxGuiPluginBase.vcxproj.filters new file mode 100644 index 0000000..c46bcbe --- /dev/null +++ b/wxGuiPluginBase/Win/wxGuiPluginBase.vcxproj.filters @@ -0,0 +1,56 @@ + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Precompile Header File + + + Precompile Header File + + + + + + + + Source Files + + + + + {4951AEE1-75F2-3C38-B593-C6BC8C809FB0} + + + {112D4227-BB51-3360-BB5B-5EA5DBA5A65F} + + + {05450AB2-CB7B-34EA-83DF-1EE86424EDF0} + + + diff --git a/wxGuiPluginBase/wxGuiPluginBase.cpp b/wxGuiPluginBase/wxGuiPluginBase.cpp index 653a8ab..9eda281 100644 --- a/wxGuiPluginBase/wxGuiPluginBase.cpp +++ b/wxGuiPluginBase/wxGuiPluginBase.cpp @@ -1,25 +1,25 @@ -#include "stdwx.h" -#include "wxGuiPluginBase.h" - -DEFINE_EVENT_TYPE(wxEVT_GUI_PLUGIN_INTEROP) - -IMPLEMENT_ABSTRACT_CLASS(wxGuiPluginBase, wxObject) - -wxGuiPluginBase::wxGuiPluginBase(wxEvtHandler * handler) -: m_Handler(handler) -{ -} - -wxGuiPluginBase::~wxGuiPluginBase() -{ -} - -wxEvtHandler * wxGuiPluginBase::GetEventHandler() -{ - return m_Handler; -} - -void wxGuiPluginBase::SetEventHandler(wxEvtHandler * handler) -{ - m_Handler = handler; +#include "stdwx.h" +#include "wxGuiPluginBase.h" + +DEFINE_EVENT_TYPE(wxEVT_GUI_PLUGIN_INTEROP) + +IMPLEMENT_ABSTRACT_CLASS(wxGuiPluginBase, wxObject) + +wxGuiPluginBase::wxGuiPluginBase(wxEvtHandler * handler) +: m_Handler(handler) +{ +} + +wxGuiPluginBase::~wxGuiPluginBase() +{ +} + +wxEvtHandler * wxGuiPluginBase::GetEventHandler() +{ + return m_Handler; +} + +void wxGuiPluginBase::SetEventHandler(wxEvtHandler * handler) +{ + m_Handler = handler; } \ No newline at end of file diff --git a/wxGuiPluginBase/wxGuiPluginBase.h b/wxGuiPluginBase/wxGuiPluginBase.h index 82cb7da..72b7f39 100644 --- a/wxGuiPluginBase/wxGuiPluginBase.h +++ b/wxGuiPluginBase/wxGuiPluginBase.h @@ -1,25 +1,25 @@ -#pragma once - -#include "Declarations.h" - -class DEMO_API wxGuiPluginBase : public wxObject -{ - DECLARE_ABSTRACT_CLASS(wxGuiPluginBase) -public: - wxGuiPluginBase(wxEvtHandler * handler); - virtual ~wxGuiPluginBase(); - - virtual wxString GetName() const = 0; - virtual wxString GetId() const = 0; - virtual wxWindow * CreatePanel(wxWindow * parent) = 0; - - wxEvtHandler * GetEventHandler(); - virtual void SetEventHandler(wxEvtHandler * handler); -protected: - wxEvtHandler * m_Handler; -}; - -DECLARE_EXPORTED_EVENT_TYPE(DEMO_API, wxEVT_GUI_PLUGIN_INTEROP, wxEVT_USER_FIRST + 100) - -typedef wxGuiPluginBase * (*CreateGuiPlugin_function)(); +#pragma once + +#include "Declarations.h" + +class DEMO_API wxGuiPluginBase : public wxObject +{ + DECLARE_ABSTRACT_CLASS(wxGuiPluginBase) +public: + wxGuiPluginBase(wxEvtHandler * handler); + virtual ~wxGuiPluginBase(); + + virtual wxString GetName() const = 0; + virtual wxString GetId() const = 0; + virtual wxWindow * CreatePanel(wxWindow * parent) = 0; + + wxEvtHandler * GetEventHandler(); + virtual void SetEventHandler(wxEvtHandler * handler); +protected: + wxEvtHandler * m_Handler; +}; + +DECLARE_EXPORTED_EVENT_TYPE(DEMO_API, wxEVT_GUI_PLUGIN_INTEROP, wxEVT_USER_FIRST + 100) + +typedef wxGuiPluginBase * (*CreateGuiPlugin_function)(); typedef void (*DeleteGuiPlugin_function)(wxGuiPluginBase * plugin); \ No newline at end of file diff --git a/wxGuiPluginBase/wxGuiPluginBase.pjd b/wxGuiPluginBase/wxGuiPluginBase.pjd index 8c19101..4b5e088 100644 --- a/wxGuiPluginBase/wxGuiPluginBase.pjd +++ b/wxGuiPluginBase/wxGuiPluginBase.pjd @@ -1,279 +1,279 @@ - - -

- 0 - "" - "" - "" - "" - "" - 0 - 0 - 0 - 1 - 1 - 1 - 1 - 0 - "Volodymyr (T-Rex) Triapichko" - "Volodymyr (T-Rex) Triapichko, 2013" - "" - 0 - 0 - 0 - 0 - "<All platforms>" - "2.9.5" - "Standard" - "///////////////////////////////////////////////////////////////////////////// -// Name: %HEADER-FILENAME% -// Purpose: -// Author: %AUTHOR% -// Modified by: -// Created: %DATE% -// RCS-ID: -// Copyright: %COPYRIGHT% -// Licence: -///////////////////////////////////////////////////////////////////////////// - -" - "///////////////////////////////////////////////////////////////////////////// -// Name: %SOURCE-FILENAME% -// Purpose: -// Author: %AUTHOR% -// Modified by: -// Created: %DATE% -// RCS-ID: -// Copyright: %COPYRIGHT% -// Licence: -///////////////////////////////////////////////////////////////////////////// - -" - "///////////////////////////////////////////////////////////////////////////// -// Name: %SYMBOLS-FILENAME% -// Purpose: Symbols file -// Author: %AUTHOR% -// Modified by: -// Created: %DATE% -// RCS-ID: -// Copyright: %COPYRIGHT% -// Licence: -///////////////////////////////////////////////////////////////////////////// - -" - "" - "// For compilers that support precompilation, includes "wx/wx.h". -#include "wx/wxprec.h" - -#ifdef __BORLANDC__ -#pragma hdrstop -#endif - -#ifndef WX_PRECOMP -#include "wx/wx.h" -#endif - -" - " /// %BODY% -" - " -/* - * %BODY% - */ - -" - "app_resources.h" - "app_resources.cpp" - "AppResources" - "app.h" - "app.cpp" - "Application" - 0 - "" - "<None>" - "iso-8859-1" - "utf-8" - "utf-8" - "" - 0 - 0 - 4 - " " - "" - 0 - 0 - 1 - 0 - 1 - 1 - 0 - 1 - 0 - 0 -
- - - "" - "data-document" - "" - "" - 0 - 1 - 0 - 0 - - "Configurations" - "config-data-document" - "" - "" - 0 - 1 - 0 - 0 - "" - 1 - 0 - - - - - - - "Projects" - "root-document" - "" - "project" - 1 - 1 - 0 - 1 - - "Windows" - "html-document" - "" - "dialogsfolder" - 1 - 1 - 0 - 1 - - "wxGuiPluginWindowBase: ID_WXGUIPLUGINWINDOWBASE" - "dialog-document" - "" - "panel" - 0 - 1 - 0 - 0 - "wbPanelProxy" - 10000 - 0 - "" - 0 - "" - "Standard" - 0 - 0 - "m_Plugin|wxGuiPluginBase *|Plugin||0|0|" - "ID_WXGUIPLUGINWINDOWBASE" - 10000 - "" - "wxGuiPluginWindowBase" - "wxPanel" - 0 - 1 - "wxGuiPluginWindowBase.cpp" - "wxGuiPluginWindowBase.h" - "" - "" - "" - "" - "" - "" - 0 - "" - "" - "" - "" - "" - 0 - 1 - "<Any platform>" - "" - "Tiled" - 1 - 0 - 0 - 0 - 0 - 0 - 0 - 0 - 0 - 0 - 0 - 0 - 0 - 1 - 0 - 0 - 0 - "" - 1 - -1 - -1 - -1 - -1 - "Centre" - "Centre" - 0 - 5 - 1 - 1 - 1 - 1 - 0 - 0 - 0 - 0 - "" - "" - "" - - - - "Sources" - "html-document" - "" - "sourcesfolder" - 1 - 1 - 0 - 1 - - "wxGuiPluginBase.rc" - "source-editor-document" - "wxGuiPluginBase.rc" - "source-editor" - 0 - 0 - 1 - 0 - "9/9/2013" - "" - - - - "Images" - "html-document" - "" - "bitmapsfolder" - 1 - 1 - 0 - 1 - - - - - + + +
+ 0 + "" + "" + "" + "" + "" + 0 + 0 + 0 + 1 + 1 + 1 + 1 + 0 + "Volodymyr (T-Rex) Triapichko" + "Volodymyr (T-Rex) Triapichko, 2013" + "" + 0 + 0 + 0 + 0 + "<All platforms>" + "2.9.5" + "Standard" + "///////////////////////////////////////////////////////////////////////////// +// Name: %HEADER-FILENAME% +// Purpose: +// Author: %AUTHOR% +// Modified by: +// Created: %DATE% +// RCS-ID: +// Copyright: %COPYRIGHT% +// Licence: +///////////////////////////////////////////////////////////////////////////// + +" + "///////////////////////////////////////////////////////////////////////////// +// Name: %SOURCE-FILENAME% +// Purpose: +// Author: %AUTHOR% +// Modified by: +// Created: %DATE% +// RCS-ID: +// Copyright: %COPYRIGHT% +// Licence: +///////////////////////////////////////////////////////////////////////////// + +" + "///////////////////////////////////////////////////////////////////////////// +// Name: %SYMBOLS-FILENAME% +// Purpose: Symbols file +// Author: %AUTHOR% +// Modified by: +// Created: %DATE% +// RCS-ID: +// Copyright: %COPYRIGHT% +// Licence: +///////////////////////////////////////////////////////////////////////////// + +" + "" + "// For compilers that support precompilation, includes "wx/wx.h". +#include "wx/wxprec.h" + +#ifdef __BORLANDC__ +#pragma hdrstop +#endif + +#ifndef WX_PRECOMP +#include "wx/wx.h" +#endif + +" + " /// %BODY% +" + " +/* + * %BODY% + */ + +" + "app_resources.h" + "app_resources.cpp" + "AppResources" + "app.h" + "app.cpp" + "Application" + 0 + "" + "<None>" + "iso-8859-1" + "utf-8" + "utf-8" + "" + 0 + 0 + 4 + " " + "" + 0 + 0 + 1 + 0 + 1 + 1 + 0 + 1 + 0 + 0 +
+ + + "" + "data-document" + "" + "" + 0 + 1 + 0 + 0 + + "Configurations" + "config-data-document" + "" + "" + 0 + 1 + 0 + 0 + "" + 1 + 0 + + + + + + + "Projects" + "root-document" + "" + "project" + 1 + 1 + 0 + 1 + + "Windows" + "html-document" + "" + "dialogsfolder" + 1 + 1 + 0 + 1 + + "wxGuiPluginWindowBase: ID_WXGUIPLUGINWINDOWBASE" + "dialog-document" + "" + "panel" + 0 + 1 + 0 + 0 + "wbPanelProxy" + 10000 + 0 + "" + 0 + "" + "Standard" + 0 + 0 + "m_Plugin|wxGuiPluginBase *|Plugin||0|0|" + "ID_WXGUIPLUGINWINDOWBASE" + 10000 + "" + "wxGuiPluginWindowBase" + "wxPanel" + 0 + 1 + "wxGuiPluginWindowBase.cpp" + "wxGuiPluginWindowBase.h" + "" + "" + "" + "" + "" + "" + 0 + "" + "" + "" + "" + "" + 0 + 1 + "<Any platform>" + "" + "Tiled" + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 1 + 0 + 0 + 0 + "" + 1 + -1 + -1 + -1 + -1 + "Centre" + "Centre" + 0 + 5 + 1 + 1 + 1 + 1 + 0 + 0 + 0 + 0 + "" + "" + "" + + + + "Sources" + "html-document" + "" + "sourcesfolder" + 1 + 1 + 0 + 1 + + "wxGuiPluginBase.rc" + "source-editor-document" + "wxGuiPluginBase.rc" + "source-editor" + 0 + 0 + 1 + 0 + "9/9/2013" + "" + + + + "Images" + "html-document" + "" + "bitmapsfolder" + 1 + 1 + 0 + 1 + + + + +
diff --git a/wxGuiPluginBase/wxGuiPluginBase.rc b/wxGuiPluginBase/wxGuiPluginBase.rc old mode 100755 new mode 100644 index f63e693..b86c4e2 --- a/wxGuiPluginBase/wxGuiPluginBase.rc +++ b/wxGuiPluginBase/wxGuiPluginBase.rc @@ -1 +1 @@ -#include "wx/msw/wx.rc" +#include "wx/msw/wx.rc" diff --git a/wxGuiPluginBase/wxGuiPluginWindowBase.cpp b/wxGuiPluginBase/wxGuiPluginWindowBase.cpp old mode 100755 new mode 100644 index b052eef..d0cc9f3 --- a/wxGuiPluginBase/wxGuiPluginWindowBase.cpp +++ b/wxGuiPluginBase/wxGuiPluginWindowBase.cpp @@ -1,149 +1,149 @@ -///////////////////////////////////////////////////////////////////////////// -// Name: wxGuiPluginWindowBase.cpp -// Purpose: -// Author: Volodymyr (T-Rex) Triapichko -// Modified by: -// Created: 09/09/2013 23:54:21 -// RCS-ID: -// Copyright: Volodymyr (T-Rex) Triapichko, 2013 -// Licence: -///////////////////////////////////////////////////////////////////////////// - -// For compilers that support precompilation, includes "wx/wx.h". -#include "wx/wxprec.h" - -#ifdef __BORLANDC__ -#pragma hdrstop -#endif - -#ifndef WX_PRECOMP -#include "wx/wx.h" -#endif - -////@begin includes -////@end includes - -#include "wxGuiPluginWindowBase.h" -#include "wxGuiPluginBase.h" - -////@begin XPM images -////@end XPM images - - -/* - * wxGuiPluginWindowBase type definition - */ - -IMPLEMENT_DYNAMIC_CLASS( wxGuiPluginWindowBase, wxPanel ) - - -/* - * wxGuiPluginWindowBase event table definition - */ - -BEGIN_EVENT_TABLE( wxGuiPluginWindowBase, wxPanel ) - -////@begin wxGuiPluginWindowBase event table entries -////@end wxGuiPluginWindowBase event table entries - -END_EVENT_TABLE() - - -/* - * wxGuiPluginWindowBase constructors - */ - -wxGuiPluginWindowBase::wxGuiPluginWindowBase() -{ - Init(); -} - -wxGuiPluginWindowBase::wxGuiPluginWindowBase(wxGuiPluginBase * plugin, wxWindow* parent, wxWindowID id, const wxPoint& pos, const wxSize& size, long style) -{ - Init(); - Create(plugin, parent, id, pos, size, style); -} - - -/* - * wxGuiPluginWindowBase creator - */ - -bool wxGuiPluginWindowBase::Create(wxGuiPluginBase * plugin, wxWindow* parent, wxWindowID id, const wxPoint& pos, const wxSize& size, long style) -{ - m_Plugin = plugin; -////@begin wxGuiPluginWindowBase creation - wxPanel::Create(parent, id, pos, size, style); - CreateControls(); -////@end wxGuiPluginWindowBase creation - return true; -} - - -/* - * wxGuiPluginWindowBase destructor - */ - -wxGuiPluginWindowBase::~wxGuiPluginWindowBase() -{ -////@begin wxGuiPluginWindowBase destruction -////@end wxGuiPluginWindowBase destruction -} - - -/* - * Member initialisation - */ - -void wxGuiPluginWindowBase::Init() -{ -////@begin wxGuiPluginWindowBase member initialisation -////@end wxGuiPluginWindowBase member initialisation -} - - -/* - * Control creation for wxGuiPluginWindowBase - */ - -void wxGuiPluginWindowBase::CreateControls() -{ -////@begin wxGuiPluginWindowBase content construction -////@end wxGuiPluginWindowBase content construction -} - - -/* - * Should we show tooltips? - */ - -bool wxGuiPluginWindowBase::ShowToolTips() -{ - return true; -} - -/* - * Get bitmap resources - */ - -wxBitmap wxGuiPluginWindowBase::GetBitmapResource( const wxString& name ) -{ - // Bitmap retrieval -////@begin wxGuiPluginWindowBase bitmap retrieval - wxUnusedVar(name); - return wxNullBitmap; -////@end wxGuiPluginWindowBase bitmap retrieval -} - -/* - * Get icon resources - */ - -wxIcon wxGuiPluginWindowBase::GetIconResource( const wxString& name ) -{ - // Icon retrieval -////@begin wxGuiPluginWindowBase icon retrieval - wxUnusedVar(name); - return wxNullIcon; -////@end wxGuiPluginWindowBase icon retrieval -} +///////////////////////////////////////////////////////////////////////////// +// Name: wxGuiPluginWindowBase.cpp +// Purpose: +// Author: Volodymyr (T-Rex) Triapichko +// Modified by: +// Created: 09/09/2013 23:54:21 +// RCS-ID: +// Copyright: Volodymyr (T-Rex) Triapichko, 2013 +// Licence: +///////////////////////////////////////////////////////////////////////////// + +// For compilers that support precompilation, includes "wx/wx.h". +#include "wx/wxprec.h" + +#ifdef __BORLANDC__ +#pragma hdrstop +#endif + +#ifndef WX_PRECOMP +#include "wx/wx.h" +#endif + +////@begin includes +////@end includes + +#include "wxGuiPluginWindowBase.h" +#include "wxGuiPluginBase.h" + +////@begin XPM images +////@end XPM images + + +/* + * wxGuiPluginWindowBase type definition + */ + +IMPLEMENT_DYNAMIC_CLASS( wxGuiPluginWindowBase, wxPanel ) + + +/* + * wxGuiPluginWindowBase event table definition + */ + +BEGIN_EVENT_TABLE( wxGuiPluginWindowBase, wxPanel ) + +////@begin wxGuiPluginWindowBase event table entries +////@end wxGuiPluginWindowBase event table entries + +END_EVENT_TABLE() + + +/* + * wxGuiPluginWindowBase constructors + */ + +wxGuiPluginWindowBase::wxGuiPluginWindowBase() +{ + Init(); +} + +wxGuiPluginWindowBase::wxGuiPluginWindowBase(wxGuiPluginBase * plugin, wxWindow* parent, wxWindowID id, const wxPoint& pos, const wxSize& size, long style) +{ + Init(); + Create(plugin, parent, id, pos, size, style); +} + + +/* + * wxGuiPluginWindowBase creator + */ + +bool wxGuiPluginWindowBase::Create(wxGuiPluginBase * plugin, wxWindow* parent, wxWindowID id, const wxPoint& pos, const wxSize& size, long style) +{ + m_Plugin = plugin; +////@begin wxGuiPluginWindowBase creation + wxPanel::Create(parent, id, pos, size, style); + CreateControls(); +////@end wxGuiPluginWindowBase creation + return true; +} + + +/* + * wxGuiPluginWindowBase destructor + */ + +wxGuiPluginWindowBase::~wxGuiPluginWindowBase() +{ +////@begin wxGuiPluginWindowBase destruction +////@end wxGuiPluginWindowBase destruction +} + + +/* + * Member initialisation + */ + +void wxGuiPluginWindowBase::Init() +{ +////@begin wxGuiPluginWindowBase member initialisation +////@end wxGuiPluginWindowBase member initialisation +} + + +/* + * Control creation for wxGuiPluginWindowBase + */ + +void wxGuiPluginWindowBase::CreateControls() +{ +////@begin wxGuiPluginWindowBase content construction +////@end wxGuiPluginWindowBase content construction +} + + +/* + * Should we show tooltips? + */ + +bool wxGuiPluginWindowBase::ShowToolTips() +{ + return true; +} + +/* + * Get bitmap resources + */ + +wxBitmap wxGuiPluginWindowBase::GetBitmapResource( const wxString& name ) +{ + // Bitmap retrieval +////@begin wxGuiPluginWindowBase bitmap retrieval + wxUnusedVar(name); + return wxNullBitmap; +////@end wxGuiPluginWindowBase bitmap retrieval +} + +/* + * Get icon resources + */ + +wxIcon wxGuiPluginWindowBase::GetIconResource( const wxString& name ) +{ + // Icon retrieval +////@begin wxGuiPluginWindowBase icon retrieval + wxUnusedVar(name); + return wxNullIcon; +////@end wxGuiPluginWindowBase icon retrieval +} diff --git a/wxGuiPluginBase/wxGuiPluginWindowBase.h b/wxGuiPluginBase/wxGuiPluginWindowBase.h old mode 100755 new mode 100644 index 11034be..cef6fb4 --- a/wxGuiPluginBase/wxGuiPluginWindowBase.h +++ b/wxGuiPluginBase/wxGuiPluginWindowBase.h @@ -1,107 +1,107 @@ -///////////////////////////////////////////////////////////////////////////// -// Name: wxGuiPluginWindowBase.h -// Purpose: -// Author: Volodymyr (T-Rex) Triapichko -// Modified by: -// Created: 09/09/2013 23:54:21 -// RCS-ID: -// Copyright: Volodymyr (T-Rex) Triapichko, 2013 -// Licence: -///////////////////////////////////////////////////////////////////////////// - -#ifndef _WXGUIPLUGINWINDOWBASE_H_ -#define _WXGUIPLUGINWINDOWBASE_H_ - - -/*! - * Includes - */ - -////@begin includes -////@end includes -#include "Declarations.h" - -/*! - * Forward declarations - */ - -////@begin forward declarations -class wxGuiPluginWindowBase; -////@end forward declarations -class wxGuiPluginBase; - -/*! - * Control identifiers - */ - -////@begin control identifiers -#define ID_WXGUIPLUGINWINDOWBASE 10000 -#define SYMBOL_WXGUIPLUGINWINDOWBASE_STYLE wxNO_BORDER|wxTAB_TRAVERSAL -#define SYMBOL_WXGUIPLUGINWINDOWBASE_IDNAME ID_WXGUIPLUGINWINDOWBASE -#define SYMBOL_WXGUIPLUGINWINDOWBASE_SIZE wxDefaultSize -#define SYMBOL_WXGUIPLUGINWINDOWBASE_POSITION wxDefaultPosition -////@end control identifiers - - -/*! - * wxGuiPluginWindowBase class declaration - */ - -class DEMO_API wxGuiPluginWindowBase: public wxPanel -{ - DECLARE_DYNAMIC_CLASS( wxGuiPluginWindowBase ) - DECLARE_EVENT_TABLE() - -public: - /// Constructors - wxGuiPluginWindowBase(); - wxGuiPluginWindowBase(wxGuiPluginBase * plugin, - wxWindow* parent, - wxWindowID id = ID_WXGUIPLUGINWINDOWBASE, - const wxPoint& pos = wxDefaultPosition, - const wxSize& size = wxDefaultSize, - long style = wxNO_BORDER|wxTAB_TRAVERSAL); - - /// Creation - bool Create(wxGuiPluginBase * plugin, - wxWindow* parent, - wxWindowID id = ID_WXGUIPLUGINWINDOWBASE, - const wxPoint& pos = wxDefaultPosition, - const wxSize& size = wxDefaultSize, - long style = wxNO_BORDER|wxTAB_TRAVERSAL); - - /// Destructor - ~wxGuiPluginWindowBase(); - - /// Initialises member variables - void Init(); - - /// Creates the controls and sizers - void CreateControls(); - -////@begin wxGuiPluginWindowBase event handler declarations - -////@end wxGuiPluginWindowBase event handler declarations - -////@begin wxGuiPluginWindowBase member function declarations - - wxGuiPluginBase * GetPlugin() const { return m_Plugin ; } - void SetPlugin(wxGuiPluginBase * value) { m_Plugin = value ; } - - /// Retrieves bitmap resources - wxBitmap GetBitmapResource( const wxString& name ); - - /// Retrieves icon resources - wxIcon GetIconResource( const wxString& name ); -////@end wxGuiPluginWindowBase member function declarations - - /// Should we show tooltips? - static bool ShowToolTips(); - -////@begin wxGuiPluginWindowBase member variables - wxGuiPluginBase * m_Plugin; -////@end wxGuiPluginWindowBase member variables -}; - -#endif - // _WXGUIPLUGINWINDOWBASE_H_ +///////////////////////////////////////////////////////////////////////////// +// Name: wxGuiPluginWindowBase.h +// Purpose: +// Author: Volodymyr (T-Rex) Triapichko +// Modified by: +// Created: 09/09/2013 23:54:21 +// RCS-ID: +// Copyright: Volodymyr (T-Rex) Triapichko, 2013 +// Licence: +///////////////////////////////////////////////////////////////////////////// + +#ifndef _WXGUIPLUGINWINDOWBASE_H_ +#define _WXGUIPLUGINWINDOWBASE_H_ + + +/*! + * Includes + */ + +////@begin includes +////@end includes +#include "Declarations.h" + +/*! + * Forward declarations + */ + +////@begin forward declarations +class wxGuiPluginWindowBase; +////@end forward declarations +class wxGuiPluginBase; + +/*! + * Control identifiers + */ + +////@begin control identifiers +#define ID_WXGUIPLUGINWINDOWBASE 10000 +#define SYMBOL_WXGUIPLUGINWINDOWBASE_STYLE wxNO_BORDER|wxTAB_TRAVERSAL +#define SYMBOL_WXGUIPLUGINWINDOWBASE_IDNAME ID_WXGUIPLUGINWINDOWBASE +#define SYMBOL_WXGUIPLUGINWINDOWBASE_SIZE wxDefaultSize +#define SYMBOL_WXGUIPLUGINWINDOWBASE_POSITION wxDefaultPosition +////@end control identifiers + + +/*! + * wxGuiPluginWindowBase class declaration + */ + +class DEMO_API wxGuiPluginWindowBase: public wxPanel +{ + DECLARE_DYNAMIC_CLASS( wxGuiPluginWindowBase ) + DECLARE_EVENT_TABLE() + +public: + /// Constructors + wxGuiPluginWindowBase(); + wxGuiPluginWindowBase(wxGuiPluginBase * plugin, + wxWindow* parent, + wxWindowID id = ID_WXGUIPLUGINWINDOWBASE, + const wxPoint& pos = wxDefaultPosition, + const wxSize& size = wxDefaultSize, + long style = wxNO_BORDER|wxTAB_TRAVERSAL); + + /// Creation + bool Create(wxGuiPluginBase * plugin, + wxWindow* parent, + wxWindowID id = ID_WXGUIPLUGINWINDOWBASE, + const wxPoint& pos = wxDefaultPosition, + const wxSize& size = wxDefaultSize, + long style = wxNO_BORDER|wxTAB_TRAVERSAL); + + /// Destructor + ~wxGuiPluginWindowBase(); + + /// Initialises member variables + void Init(); + + /// Creates the controls and sizers + void CreateControls(); + +////@begin wxGuiPluginWindowBase event handler declarations + +////@end wxGuiPluginWindowBase event handler declarations + +////@begin wxGuiPluginWindowBase member function declarations + + wxGuiPluginBase * GetPlugin() const { return m_Plugin ; } + void SetPlugin(wxGuiPluginBase * value) { m_Plugin = value ; } + + /// Retrieves bitmap resources + wxBitmap GetBitmapResource( const wxString& name ); + + /// Retrieves icon resources + wxIcon GetIconResource( const wxString& name ); +////@end wxGuiPluginWindowBase member function declarations + + /// Should we show tooltips? + static bool ShowToolTips(); + +////@begin wxGuiPluginWindowBase member variables + wxGuiPluginBase * m_Plugin; +////@end wxGuiPluginWindowBase member variables +}; + +#endif + // _WXGUIPLUGINWINDOWBASE_H_ diff --git a/wxModularCore/CMakeLists.txt b/wxModularCore/CMakeLists.txt index 1e6a04b..9d3c97a 100644 --- a/wxModularCore/CMakeLists.txt +++ b/wxModularCore/CMakeLists.txt @@ -1,30 +1,30 @@ -set (SRCS - wxModularCore.cpp - wxModularCoreSettings.cpp) -set (HEADERS - wxModularCore.h - wxModularCoreSettings.h) - -set(LIBRARY_NAME wxModularCore) - -if(WIN32) - set(PREPROCESSOR_DEFINITIONS ${PREPROCESSOR_DEFINITIONS};/D__STDC_CONSTANT_MACROS) -endif(WIN32) - -set(SRCS ${SRCS} ${HEADERS} - ${PROJECT_ROOT_DIR}/include/stdwx.h - ${PROJECT_ROOT_DIR}/include/stdwx.cpp) - -add_definitions(${PREPROCESSOR_DEFINITIONS}) - -include_directories(${INCLUDE_DIRECTORIES} ${BASE_INCLUDE_DIRECTORIES}) - -link_directories(${LINK_DIRECTORIES}) - -add_library(${LIBRARY_NAME} STATIC ${SRCS}) - -set(DLL_DIR bin) -set(TARGET_LOCATION ${PROJECT_SOURCE_DIR}/${DLL_DIR}/${CMAKE_CFG_INTDIR}) -set_target_properties(${LIBRARY_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${TARGET_LOCATION}) - -target_precompile_headers(${LIBRARY_NAME} PRIVATE ${PROJECT_ROOT_DIR}/include/stdwx.h ${PROJECT_ROOT_DIR}/include/stdwx.cpp) \ No newline at end of file +set (SRCS + wxModularCore.cpp + wxModularCoreSettings.cpp) +set (HEADERS + wxModularCore.h + wxModularCoreSettings.h) + +set(LIBRARY_NAME wxModularCore) + +if(WIN32) + set(PREPROCESSOR_DEFINITIONS ${PREPROCESSOR_DEFINITIONS};/D__STDC_CONSTANT_MACROS) +endif(WIN32) + +set(SRCS ${SRCS} ${HEADERS} + ${PROJECT_ROOT_DIR}/include/stdwx.h + ${PROJECT_ROOT_DIR}/include/stdwx.cpp) + +add_definitions(${PREPROCESSOR_DEFINITIONS}) + +include_directories(${INCLUDE_DIRECTORIES} ${BASE_INCLUDE_DIRECTORIES}) + +link_directories(${LINK_DIRECTORIES}) + +add_library(${LIBRARY_NAME} STATIC ${SRCS}) + +set(DLL_DIR bin) +set(TARGET_LOCATION ${PROJECT_SOURCE_DIR}/${DLL_DIR}/${CMAKE_CFG_INTDIR}) +set_target_properties(${LIBRARY_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${TARGET_LOCATION}) + +target_precompile_headers(${LIBRARY_NAME} PRIVATE ${PROJECT_ROOT_DIR}/include/stdwx.h) \ No newline at end of file diff --git a/wxModularCore/Win/wxModularCore.vcxproj b/wxModularCore/Win/wxModularCore.vcxproj new file mode 100644 index 0000000..ed9edcd --- /dev/null +++ b/wxModularCore/Win/wxModularCore.vcxproj @@ -0,0 +1,218 @@ + + + + x64 + + + + Debug + x64 + + + Release + x64 + + + + {746FA12D-51CE-3AA5-90F0-43DD3C32BDA9} + Win32Proj + 10.0.26100.0 + x64 + wxModularCore + NoUpgrade + + + + StaticLibrary + Unicode + v143 + + + StaticLibrary + Unicode + v143 + + + + + + + + + + <_ProjectFileVersion>10.0.20506.1 + C:\Users\Admin\IFloor\projects\wxVideoProcessing\bin\lib\Debug\ + wxModularCore.dir\Debug\ + wxModularCore + .lib + C:\Users\Admin\IFloor\projects\wxVideoProcessing\bin\lib\Release\ + wxModularCore.dir\Release\ + wxModularCore + .lib + + + + C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\mswu;C:\Users\Admin\IFloor\libs\wxWidgets\include;C:\Users\Admin\IFloor\libs\opencv\build\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\Utils;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\MotionDetectorCore;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxJSON\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxxmlserializer\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxxmlserializer;%(AdditionalIncludeDirectories) + $(IntDir) + EnableFastChecks + + + ProgramDatabase + 4996 + Sync + + + Disabled + stdcpp17 + + true + Disabled + Use + C:/Users/Admin/IFloor/projects/wxVideoProcessing/wxModularCore/Win/CMakeFiles/wxModularCore.dir/Debug/cmake_pch.hxx + C:/Users/Admin/IFloor/projects/wxVideoProcessing/wxModularCore/Win/wxModularCore.dir/Debug//cmake_pch.pch + + MultiThreadedDebugDLL + true + + + false + %(PreprocessorDefinitions);WIN32;_WINDOWS;__WXDEBUG__=1;WIN32_LEAN_AND_MEAN;WXUSINGDLL;UNICODE;_UNICODE;_CRT_SECURE_NO_DEPRECATE;__STDC_CONSTANT_MACROS;CMAKE_INTDIR="Debug" + $(IntDir) + false + + + %(PreprocessorDefinitions);WIN32;_DEBUG;_WINDOWS;__WXDEBUG__=1;WIN32_LEAN_AND_MEAN;WXUSINGDLL;UNICODE;_UNICODE;_CRT_SECURE_NO_DEPRECATE;__STDC_CONSTANT_MACROS;CMAKE_INTDIR=\"Debug\" + C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\mswu;C:\Users\Admin\IFloor\libs\wxWidgets\include;C:\Users\Admin\IFloor\libs\opencv\build\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\Utils;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\MotionDetectorCore;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxJSON\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxxmlserializer\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxxmlserializer;%(AdditionalIncludeDirectories) + + + C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\mswu;C:\Users\Admin\IFloor\libs\wxWidgets\include;C:\Users\Admin\IFloor\libs\opencv\build\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\Utils;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\MotionDetectorCore;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxJSON\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxxmlserializer\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxxmlserializer;%(AdditionalIncludeDirectories) + $(ProjectDir)/$(IntDir) + %(Filename).h + %(Filename).tlb + %(Filename)_i.c + %(Filename)_p.c + + + %(AdditionalOptions) /machine:x64 + + + + + C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\mswu;C:\Users\Admin\IFloor\libs\wxWidgets\include;C:\Users\Admin\IFloor\libs\opencv\build\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\Utils;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\MotionDetectorCore;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxJSON\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxxmlserializer\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxxmlserializer;%(AdditionalIncludeDirectories) + $(IntDir) + Default + + + 4996 + Sync + + + AnySuitable + stdcpp17 + + true + MaxSpeed + Use + C:/Users/Admin/IFloor/projects/wxVideoProcessing/wxModularCore/Win/CMakeFiles/wxModularCore.dir/Release/cmake_pch.hxx + C:/Users/Admin/IFloor/projects/wxVideoProcessing/wxModularCore/Win/wxModularCore.dir/Release//cmake_pch.pch + + MultiThreadedDLL + true + + + false + %(PreprocessorDefinitions);WIN32;_WINDOWS;NDEBUG;WIN32_LEAN_AND_MEAN;WXUSINGDLL;UNICODE;_UNICODE;_CRT_SECURE_NO_DEPRECATE;__STDC_CONSTANT_MACROS;CMAKE_INTDIR="Release" + $(IntDir) + + + false + + + %(PreprocessorDefinitions);WIN32;_WINDOWS;NDEBUG;WIN32_LEAN_AND_MEAN;WXUSINGDLL;UNICODE;_UNICODE;_CRT_SECURE_NO_DEPRECATE;__STDC_CONSTANT_MACROS;CMAKE_INTDIR=\"Release\" + C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\mswu;C:\Users\Admin\IFloor\libs\wxWidgets\include;C:\Users\Admin\IFloor\libs\opencv\build\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\Utils;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\MotionDetectorCore;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxJSON\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxxmlserializer\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxxmlserializer;%(AdditionalIncludeDirectories) + + + C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\mswu;C:\Users\Admin\IFloor\libs\wxWidgets\include;C:\Users\Admin\IFloor\libs\opencv\build\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\Utils;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\MotionDetectorCore;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxJSON\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxxmlserializer\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxxmlserializer;%(AdditionalIncludeDirectories) + $(ProjectDir)/$(IntDir) + %(Filename).h + %(Filename).tlb + %(Filename)_i.c + %(Filename)_p.c + + + %(AdditionalOptions) /machine:x64 + + + + + Always + Building Custom Rule C:/Users/Admin/IFloor/projects/wxVideoProcessing/wxModularCore/CMakeLists.txt + setlocal +"C:\Program Files\CMake\bin\cmake.exe" -SC:/Users/Admin/IFloor/projects/wxVideoProcessing/build -BC:/Users/Admin/IFloor/projects/wxVideoProcessing/build/Win --check-stamp-file C:/Users/Admin/IFloor/projects/wxVideoProcessing/wxModularCore/Win/CMakeFiles/generate.stamp +if %errorlevel% neq 0 goto :cmEnd +:cmEnd +endlocal & call :cmErrorLevel %errorlevel% & goto :cmDone +:cmErrorLevel +exit /b %1 +:cmDone +if %errorlevel% neq 0 goto :VCEnd + %(AdditionalInputs) + C:\Users\Admin\IFloor\projects\wxVideoProcessing\wxModularCore\Win\CMakeFiles\generate.stamp + false + Building Custom Rule C:/Users/Admin/IFloor/projects/wxVideoProcessing/wxModularCore/CMakeLists.txt + setlocal +"C:\Program Files\CMake\bin\cmake.exe" -SC:/Users/Admin/IFloor/projects/wxVideoProcessing/build -BC:/Users/Admin/IFloor/projects/wxVideoProcessing/build/Win --check-stamp-file C:/Users/Admin/IFloor/projects/wxVideoProcessing/wxModularCore/Win/CMakeFiles/generate.stamp +if %errorlevel% neq 0 goto :cmEnd +:cmEnd +endlocal & call :cmErrorLevel %errorlevel% & goto :cmDone +:cmErrorLevel +exit /b %1 +:cmDone +if %errorlevel% neq 0 goto :VCEnd + %(AdditionalInputs) + C:\Users\Admin\IFloor\projects\wxVideoProcessing\wxModularCore\Win\CMakeFiles\generate.stamp + false + + + + + C:/Users/Admin/IFloor/projects/wxVideoProcessing/wxModularCore/Win/CMakeFiles/wxModularCore.dir/Debug/cmake_pch.hxx;%(ForcedIncludeFiles) + Create + C:/Users/Admin/IFloor/projects/wxVideoProcessing/wxModularCore/Win/CMakeFiles/wxModularCore.dir/Debug/cmake_pch.hxx + C:/Users/Admin/IFloor/projects/wxVideoProcessing/wxModularCore/Win/wxModularCore.dir/Debug//cmake_pch.pch + C:/Users/Admin/IFloor/projects/wxVideoProcessing/wxModularCore/Win/CMakeFiles/wxModularCore.dir/Release/cmake_pch.hxx;%(ForcedIncludeFiles) + Create + C:/Users/Admin/IFloor/projects/wxVideoProcessing/wxModularCore/Win/CMakeFiles/wxModularCore.dir/Release/cmake_pch.hxx + C:/Users/Admin/IFloor/projects/wxVideoProcessing/wxModularCore/Win/wxModularCore.dir/Release//cmake_pch.pch + + + C:/Users/Admin/IFloor/projects/wxVideoProcessing/wxModularCore/Win/CMakeFiles/wxModularCore.dir/Debug/cmake_pch.hxx;%(ForcedIncludeFiles) + C:/Users/Admin/IFloor/projects/wxVideoProcessing/wxModularCore/Win/CMakeFiles/wxModularCore.dir/Release/cmake_pch.hxx;%(ForcedIncludeFiles) + + + C:/Users/Admin/IFloor/projects/wxVideoProcessing/wxModularCore/Win/CMakeFiles/wxModularCore.dir/Debug/cmake_pch.hxx;%(ForcedIncludeFiles) + C:/Users/Admin/IFloor/projects/wxVideoProcessing/wxModularCore/Win/CMakeFiles/wxModularCore.dir/Release/cmake_pch.hxx;%(ForcedIncludeFiles) + + + + + + C:/Users/Admin/IFloor/projects/wxVideoProcessing/wxModularCore/Win/CMakeFiles/wxModularCore.dir/Debug/cmake_pch.hxx;%(ForcedIncludeFiles) + C:/Users/Admin/IFloor/projects/wxVideoProcessing/wxModularCore/Win/CMakeFiles/wxModularCore.dir/Release/cmake_pch.hxx;%(ForcedIncludeFiles) + + + + + + + + {6F4BDD20-5B99-3029-BDF0-7D719B283AF5} + ZERO_CHECK + false + Never + + + + + + \ No newline at end of file diff --git a/wxModularCore/Win/wxModularCore.vcxproj.filters b/wxModularCore/Win/wxModularCore.vcxproj.filters new file mode 100644 index 0000000..f1b6bd4 --- /dev/null +++ b/wxModularCore/Win/wxModularCore.vcxproj.filters @@ -0,0 +1,48 @@ + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Header Files + + + Header Files + + + Header Files + + + Precompile Header File + + + Precompile Header File + + + + + + + + {4951AEE1-75F2-3C38-B593-C6BC8C809FB0} + + + {112D4227-BB51-3360-BB5B-5EA5DBA5A65F} + + + {05450AB2-CB7B-34EA-83DF-1EE86424EDF0} + + + diff --git a/wxModularCore/wxModularCore.cpp b/wxModularCore/wxModularCore.cpp index 7dc009f..d3cea07 100644 --- a/wxModularCore/wxModularCore.cpp +++ b/wxModularCore/wxModularCore.cpp @@ -1,57 +1,57 @@ -#include "stdwx.h" -#include "wxModularCore.h" -#include "wxModularCoreSettings.h" -#include - -WX_DEFINE_LIST(wxDynamicLibraryList); - - -wxModularCore::wxModularCore() - : m_Settings(new wxModularCoreSettings), m_Handler(new wxEvtHandler) -{ - // This will allow to delete all objects from this list automatically - m_DllList.DeleteContents(true); -} - -wxModularCore::~wxModularCore() -{ - wxDELETE(m_Handler); - wxDELETE(m_Settings); -} - -void wxModularCore::Clear() -{ - UnloadAllPlugins(); - // TODO: Add the code which resets the object to initial state -} - -wxString wxModularCore::GetPluginsPath(bool forceProgramPath) const -{ -#if defined(__WXMAC__) - return wxStandardPaths::Get().GetPluginsDir(); -#else - wxString path; - if (m_Settings->GetStoreInAppData() && !forceProgramPath) - path = wxStandardPaths::Get().GetConfigDir(); - else - path = wxPathOnly(wxStandardPaths::Get().GetExecutablePath()); - wxFileName fn; - fn.AssignDir(path); - fn.AppendDir(wxT("plugins")); - return fn.GetFullPath(); -#endif -} - -wxString wxModularCore::GetPluginExt() -{ - return -#if defined(__WXMSW__) - wxT("dll"); -#elif defined(__WXGTK__) - wxT("so"); -#elif defined(__WXMAC__) - wxT("dylib"); -#else - wxEmptyString; -#endif -} +#include "stdwx.h" +#include "wxModularCore.h" +#include "wxModularCoreSettings.h" +#include + +WX_DEFINE_LIST(wxDynamicLibraryList); + + +wxModularCore::wxModularCore() + : m_Settings(new wxModularCoreSettings), m_Handler(new wxEvtHandler) +{ + // This will allow to delete all objects from this list automatically + m_DllList.DeleteContents(true); +} + +wxModularCore::~wxModularCore() +{ + wxDELETE(m_Handler); + wxDELETE(m_Settings); +} + +void wxModularCore::Clear() +{ + UnloadAllPlugins(); + // TODO: Add the code which resets the object to initial state +} + +wxString wxModularCore::GetPluginsPath(bool forceProgramPath) const +{ +#if defined(__WXMAC__) + return wxStandardPaths::Get().GetPluginsDir(); +#else + wxString path; + if (m_Settings->GetStoreInAppData() && !forceProgramPath) + path = wxStandardPaths::Get().GetConfigDir(); + else + path = wxPathOnly(wxStandardPaths::Get().GetExecutablePath()); + wxFileName fn; + fn.AssignDir(path); + fn.AppendDir(wxT("plugins")); + return fn.GetFullPath(); +#endif +} + +wxString wxModularCore::GetPluginExt() +{ + return +#if defined(__WXMSW__) + wxT("dll"); +#elif defined(__WXGTK__) + wxT("so"); +#elif defined(__WXMAC__) + wxT("dylib"); +#else + wxEmptyString; +#endif +} diff --git a/wxModularCore/wxModularCore.h b/wxModularCore/wxModularCore.h index cd6701d..943f15d 100644 --- a/wxModularCore/wxModularCore.h +++ b/wxModularCore/wxModularCore.h @@ -1,140 +1,140 @@ -#pragma once - -// We need to keep the list of loaded DLLs -WX_DECLARE_LIST(wxDynamicLibrary, wxDynamicLibraryList); - -class wxModularCoreSettings; - -class wxModularCore -{ -public: - wxModularCore(); - virtual ~wxModularCore(); - - virtual wxString GetPluginsPath(bool forceProgramPath) const; - virtual wxString GetPluginExt(); - - virtual bool LoadAllPlugins(bool forceProgramPath) = 0; - virtual bool UnloadAllPlugins() = 0; - virtual void Clear(); -protected: - - wxDynamicLibraryList m_DllList; - - wxModularCoreSettings * m_Settings; - wxEvtHandler * m_Handler; - - template - bool RegisterPlugin(PluginType * plugin, - PluginListType & list) - { - list.Append(plugin); - return true; - } - - template - bool UnRegisterPlugin( - PluginType * plugin, - PluginListType & container, - PluginToDllDictionaryType & pluginMap) - { - typename PluginListType::compatibility_iterator it = - container.Find(plugin); - if (it == NULL) - return false; - - do - { - wxDynamicLibrary * dll = (wxDynamicLibrary *)pluginMap[plugin]; - if (!dll) // Probably plugin was not loaded from dll - break; - - wxDYNLIB_FUNCTION(DeletePluginFunctionType, - DeletePlugin, *dll); - if (pfnDeletePlugin) - { - pfnDeletePlugin(plugin); - container.Erase(it); - pluginMap.erase(plugin); - return true; - } - } while (false); - - // If plugin is not loaded from DLL (e.g. embedded into executable) - wxDELETE(plugin); - container.Erase(it); - - return true; - } - - template - bool UnloadPlugins(PluginListType & list, - PluginToDllDictionaryType & pluginDictoonary) - { - bool result = true; - PluginType * plugin = NULL; - while (list.GetFirst() && (plugin = - list.GetFirst()->GetData())) - { - result &= UnRegisterPlugin(plugin, - list, pluginDictoonary); - } - return result; - } - - template - bool LoadPlugins(const wxString & pluginsDirectory, - PluginListType & list, - PluginToDllDictionaryType & pluginDictionary, - const wxString & subFolder) - { - wxFileName fn; - fn.AssignDir(pluginsDirectory); - wxLogDebug(wxT("%s"), fn.GetFullPath().data()); - fn.AppendDir(subFolder); - wxLogDebug(wxT("%s"), fn.GetFullPath().data()); - if (!fn.DirExists()) - return false; - - if(!wxDirExists(fn.GetFullPath())) return false; - wxString wildcard = wxString::Format(wxT("*.%s"), - GetPluginExt().GetData()); - wxArrayString pluginPaths; - wxDir::GetAllFiles(fn.GetFullPath(), - &pluginPaths, wildcard); - for(size_t i = 0; i < pluginPaths.GetCount(); ++i) - { - wxString fileName = pluginPaths[i]; - wxDynamicLibrary * dll = new wxDynamicLibrary(fileName); - if (dll->IsLoaded()) - { - wxDYNLIB_FUNCTION(CreatePluginFunctionType, - CreatePlugin, *dll); - if (pfnCreatePlugin) - { - PluginType * plugin = pfnCreatePlugin(); - RegisterPlugin(plugin, list); - m_DllList.Append(dll); - pluginDictionary[plugin] = dll; - } - else - wxDELETE(dll); - } - } - return true; - } - -}; +#pragma once + +// We need to keep the list of loaded DLLs +WX_DECLARE_LIST(wxDynamicLibrary, wxDynamicLibraryList); + +class wxModularCoreSettings; + +class wxModularCore +{ +public: + wxModularCore(); + virtual ~wxModularCore(); + + virtual wxString GetPluginsPath(bool forceProgramPath) const; + virtual wxString GetPluginExt(); + + virtual bool LoadAllPlugins(bool forceProgramPath) = 0; + virtual bool UnloadAllPlugins() = 0; + virtual void Clear(); +protected: + + wxDynamicLibraryList m_DllList; + + wxModularCoreSettings * m_Settings; + wxEvtHandler * m_Handler; + + template + bool RegisterPlugin(PluginType * plugin, + PluginListType & list) + { + list.Append(plugin); + return true; + } + + template + bool UnRegisterPlugin( + PluginType * plugin, + PluginListType & container, + PluginToDllDictionaryType & pluginMap) + { + typename PluginListType::compatibility_iterator it = + container.Find(plugin); + if (it == nullptr) + return false; + + do + { + wxDynamicLibrary * dll = (wxDynamicLibrary *)pluginMap[plugin]; + if (!dll) // Probably plugin was not loaded from dll + break; + + wxDYNLIB_FUNCTION(DeletePluginFunctionType, + DeletePlugin, *dll); + if (pfnDeletePlugin) + { + pfnDeletePlugin(plugin); + container.Erase(it); + pluginMap.erase(plugin); + return true; + } + } while (false); + + // If plugin is not loaded from DLL (e.g. embedded into executable) + wxDELETE(plugin); + container.Erase(it); + + return true; + } + + template + bool UnloadPlugins(PluginListType & list, + PluginToDllDictionaryType & pluginDictoonary) + { + bool result = true; + PluginType * plugin = NULL; + while (list.GetFirst() && (plugin = + list.GetFirst()->GetData())) + { + result &= UnRegisterPlugin(plugin, + list, pluginDictoonary); + } + return result; + } + + template + bool LoadPlugins(const wxString & pluginsDirectory, + PluginListType & list, + PluginToDllDictionaryType & pluginDictionary, + const wxString & subFolder) + { + wxFileName fn; + fn.AssignDir(pluginsDirectory); + wxLogDebug(wxT("%s"), fn.GetFullPath().data()); + fn.AppendDir(subFolder); + wxLogDebug(wxT("%s"), fn.GetFullPath().data()); + if (!fn.DirExists()) + return false; + + if(!wxDirExists(fn.GetFullPath())) return false; + wxString wildcard = wxString::Format(wxT("*.%s"), + GetPluginExt().GetData()); + wxArrayString pluginPaths; + wxDir::GetAllFiles(fn.GetFullPath(), + &pluginPaths, wildcard); + for(size_t i = 0; i < pluginPaths.GetCount(); ++i) + { + wxString fileName = pluginPaths[i]; + wxDynamicLibrary * dll = new wxDynamicLibrary(fileName); + if (dll->IsLoaded()) + { + wxDYNLIB_FUNCTION(CreatePluginFunctionType, + CreatePlugin, *dll); + if (pfnCreatePlugin) + { + PluginType * plugin = pfnCreatePlugin(); + RegisterPlugin(plugin, list); + m_DllList.Append(dll); + pluginDictionary[plugin] = dll; + } + else + wxDELETE(dll); + } + } + return true; + } + +}; diff --git a/wxModularCore/wxModularCoreSettings.cpp b/wxModularCore/wxModularCoreSettings.cpp index 4761361..5987986 100644 --- a/wxModularCore/wxModularCoreSettings.cpp +++ b/wxModularCore/wxModularCoreSettings.cpp @@ -1,42 +1,42 @@ -#include "stdwx.h" -#include "wxModularCoreSettings.h" - -wxModularCoreSettings::wxModularCoreSettings() - : m_bStoreInAppData(false) -{ - -} - -wxModularCoreSettings::wxModularCoreSettings(const wxModularCoreSettings & settings) -{ - CopyFrom(settings); -} - -wxModularCoreSettings & wxModularCoreSettings::operator = (const wxModularCoreSettings & settings) -{ - if (this != &settings) - { - CopyFrom(settings); - } - return *this; -} - -wxModularCoreSettings::~wxModularCoreSettings() -{ - -} - -void wxModularCoreSettings::CopyFrom(const wxModularCoreSettings & settings) -{ - m_bStoreInAppData = settings.m_bStoreInAppData; -} - -void wxModularCoreSettings::SetStoreInAppData(const bool & value) -{ - m_bStoreInAppData = value; -} - -bool wxModularCoreSettings::GetStoreInAppData() const -{ - return m_bStoreInAppData; +#include "stdwx.h" +#include "wxModularCoreSettings.h" + +wxModularCoreSettings::wxModularCoreSettings() + : m_bStoreInAppData(false) +{ + +} + +wxModularCoreSettings::wxModularCoreSettings(const wxModularCoreSettings & settings) +{ + CopyFrom(settings); +} + +wxModularCoreSettings & wxModularCoreSettings::operator = (const wxModularCoreSettings & settings) +{ + if (this != &settings) + { + CopyFrom(settings); + } + return *this; +} + +wxModularCoreSettings::~wxModularCoreSettings() +{ + +} + +void wxModularCoreSettings::CopyFrom(const wxModularCoreSettings & settings) +{ + m_bStoreInAppData = settings.m_bStoreInAppData; +} + +void wxModularCoreSettings::SetStoreInAppData(const bool & value) +{ + m_bStoreInAppData = value; +} + +bool wxModularCoreSettings::GetStoreInAppData() const +{ + return m_bStoreInAppData; } \ No newline at end of file diff --git a/wxModularCore/wxModularCoreSettings.h b/wxModularCore/wxModularCoreSettings.h index baf7c5f..b970eb4 100644 --- a/wxModularCore/wxModularCoreSettings.h +++ b/wxModularCore/wxModularCoreSettings.h @@ -1,17 +1,17 @@ -#pragma once - -class wxModularCoreSettings -{ -public: - wxModularCoreSettings(); - wxModularCoreSettings(const wxModularCoreSettings & settings); - wxModularCoreSettings & operator = (const wxModularCoreSettings & settings); - virtual ~wxModularCoreSettings(); - - void SetStoreInAppData(const bool & val); - bool GetStoreInAppData() const; -protected: - virtual void CopyFrom(const wxModularCoreSettings & settings); -private: - bool m_bStoreInAppData; // Should we store data in Application Data folder or in .exe folder +#pragma once + +class wxModularCoreSettings +{ +public: + wxModularCoreSettings(); + wxModularCoreSettings(const wxModularCoreSettings & settings); + wxModularCoreSettings & operator = (const wxModularCoreSettings & settings); + virtual ~wxModularCoreSettings(); + + void SetStoreInAppData(const bool & val); + bool GetStoreInAppData() const; +protected: + virtual void CopyFrom(const wxModularCoreSettings & settings); +private: + bool m_bStoreInAppData; // Should we store data in Application Data folder or in .exe folder }; \ No newline at end of file diff --git a/wxModularHost/CMakeLists.txt b/wxModularHost/CMakeLists.txt index a43a247..035ab4c 100644 --- a/wxModularHost/CMakeLists.txt +++ b/wxModularHost/CMakeLists.txt @@ -1,102 +1,106 @@ -set(SRCS - MainFrame.cpp - SampleModularCore.cpp - wxModularHostApp.cpp) -set(HEADERS - MainFrame.h - SampleModularCore.h - wxModularHostApp.h) - - -execute_process( - COMMAND wx-config --libs aui - RESULT_VARIABLE wxWidgets_aui_lib_result - OUTPUT_VARIABLE wxWidgets_aui_lib_output - ERROR_VARIABLE wxWidgets_aui_lib_error - OUTPUT_STRIP_TRAILING_WHITESPACE -) - -if(NOT ${wxWidgets_aui_lib_result} EQUAL 0) - message(FATAL_ERROR "wx-config failed: ${wxWidgets_aui_lib_error}") -else() - message(STATUS "wx-config output: ${wxWidgets_aui_lib_output}") -endif() - -set(INCLUDE_DIRECTORIES ${BASE_INCLUDE_DIRECTORIES} - ${PROJECT_ROOT_DIR}/wxModularCore - ${PROJECT_ROOT_DIR}/wxNonGuiPluginBase - ${PROJECT_ROOT_DIR}/wxGuiPluginBase) - -if(WIN32) - set(SRCS ${SRCS} wxModularHost.rc) - set(PREPROCESSOR_DEFINITIONS ${PREPROCESSOR_DEFINITIONS}; - /D_USRDLL; - /DwxUSE_NO_MANIFEST=1; - /D__STDC_CONSTANT_MACROS) - set(LINK_DIRECTORIES - ${PROJECT_ROOT_DIR}/wxNonGuiPluginBase/${OS_BASE_NAME}${LIB_SUFFIX}/$(ConfigurationName) - ${PROJECT_ROOT_DIR}/wxGuiPluginBase/${OS_BASE_NAME}${LIB_SUFFIX}/$(ConfigurationName) - ${PROJECT_ROOT_DIR}/wxModularCore/${OS_BASE_NAME}${LIB_SUFFIX}/$(ConfigurationName)) - set(DEMO_LIBS wxModularCore.lib wxNonGuiPluginBase.lib wxGuiPluginBase.lib) -endif(WIN32) -if(LINUX OR APPLE) - set(LINK_DIRECTORIES - ${PROJECT_ROOT_DIR}/wxNonGuiPluginBase/${OS_BASE_NAME}${LIB_SUFFIX} - ${PROJECT_ROOT_DIR}/wxGuiPluginBase/${OS_BASE_NAME}${LIB_SUFFIX} - ${PROJECT_ROOT_DIR}/wxModularCore/${OS_BASE_NAME}${LIB_SUFFIX}) - set(DEMO_LIBS_SHARED wxNonGuiPluginBase wxGuiPluginBase) - set(DEMO_LIBS wxModularCore ${DEMO_LIBS_SHARED}) -endif(LINUX OR APPLE) - -set(LIBS ${DEMO_LIBS} ${wxWidgets_LIBRARIES} ${wxWidgets_aui_lib_output}) - -set(EXECUTABLE_NAME wxModularHost) - -add_definitions(${PREPROCESSOR_DEFINITIONS}) -include_directories(${INCLUDE_DIRECTORIES} ${BASE_INCLUDE_DIRECTORIES}) -link_directories(${LINK_DIRECTORIES}) - -if(WIN32) - set(EXECUTABLE_TYPE WIN32) -endif(WIN32) -if(APPLE) - set(MACOSX_BUNDLE YES) - set(EXECUTABLE_TYPE MACOSX_BUNDLE) - set(CMAKE_INSTALL_PATH "@loader_path") -endif(APPLE) -if(LINUX) - set(EXECUTABLE_TYPE "") -endif(LINUX) - -set(SRCS ${SRCS} ${HEADERS} - ${PROJECT_ROOT_DIR}/include/stdwx.h - ${PROJECT_ROOT_DIR}/include/stdwx.cpp) - -set(PROJECT_FILES ${SRCS}) -add_executable(${EXECUTABLE_NAME} ${EXECUTABLE_TYPE} ${PROJECT_FILES}) - -set(EXE_DIR bin) -set(TARGET_LOCATION ${PROJECT_SOURCE_DIR}/${EXE_DIR}${LIB_SUFFIX}) -set_target_properties(${EXECUTABLE_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${TARGET_LOCATION}) -if(APPLE) - set(CMAKE_MACOSX_RPATH 1) - set(MACOSX_RPATH TRUE) - set_target_properties( - ${EXECUTABLE_NAME} PROPERTIES - INSTALL_RPATH "@loader_path/../Frameworks") -endif(APPLE) - -target_link_libraries(${EXECUTABLE_NAME} ${LIBS}) - -add_dependencies(${EXECUTABLE_NAME} wxModularCore) - -target_precompile_headers(${EXECUTABLE_NAME} PRIVATE ${PROJECT_ROOT_DIR}/include/stdwx.h ${PROJECT_ROOT_DIR}/include/stdwx.cpp) - -if(APPLE) - FOREACH(DEP_LIB ${DEMO_LIBS_SHARED}) - get_filename_component(ABS_ROOT_DIR ${PROJECT_ROOT_DIR} ABSOLUTE) - set(LIBNAME_FULL "${ABS_ROOT_DIR}/${DEP_LIB}/${OS_BASE_NAME}${LIB_SUFFIX}/$(CONFIGURATION)/lib${DEP_LIB}.dylib") - add_custom_command(TARGET ${EXECUTABLE_NAME} POST_BUILD - COMMAND install_name_tool -change "${LIBNAME_FULL}" "@executable_path/../Frameworks/lib${DEP_LIB}.dylib" $) - ENDFOREACH(DEP_LIB) -endif(APPLE) +set(SRCS + MainFrame.cpp + SampleModularCore.cpp + wxModularHostApp.cpp) +set(HEADERS + MainFrame.h + SampleModularCore.h + wxModularHostApp.h) + + +if(NOT WIN32) + execute_process( + COMMAND wx-config --libs aui + RESULT_VARIABLE wxWidgets_aui_lib_result + OUTPUT_VARIABLE wxWidgets_aui_lib_output + ERROR_VARIABLE wxWidgets_aui_lib_error + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + + if(NOT ${wxWidgets_aui_lib_result} EQUAL 0) + message(FATAL_ERROR "wx-config failed: ${wxWidgets_aui_lib_error}") + else() + message(STATUS "wx-config output: ${wxWidgets_aui_lib_output}") + endif() +endif(NOT WIN32) + +set(INCLUDE_DIRECTORIES ${BASE_INCLUDE_DIRECTORIES} + ${PROJECT_ROOT_DIR}/wxModularCore + ${PROJECT_ROOT_DIR}/wxNonGuiPluginBase + ${PROJECT_ROOT_DIR}/wxGuiPluginBase) + +if(WIN32) + set(SRCS ${SRCS} wxModularHost.rc) + set(PREPROCESSOR_DEFINITIONS ${PREPROCESSOR_DEFINITIONS}; + /D_USRDLL; + /DwxUSE_NO_MANIFEST=1; + /D__STDC_CONSTANT_MACROS) + set(LINK_DIRECTORIES + ${PROJECT_ROOT_DIR}/wxNonGuiPluginBase/${OS_BASE_NAME}${LIB_SUFFIX}/$(ConfigurationName) + ${PROJECT_ROOT_DIR}/wxGuiPluginBase/${OS_BASE_NAME}${LIB_SUFFIX}/$(ConfigurationName) + ${PROJECT_ROOT_DIR}/wxModularCore/${OS_BASE_NAME}${LIB_SUFFIX}/$(ConfigurationName)) + set(DEMO_LIBS wxModularCore.lib wxNonGuiPluginBase.lib wxGuiPluginBase.lib) +endif(WIN32) +if(LINUX OR APPLE) + set(LINK_DIRECTORIES + ${PROJECT_ROOT_DIR}/wxNonGuiPluginBase/${OS_BASE_NAME}${LIB_SUFFIX} + ${PROJECT_ROOT_DIR}/wxGuiPluginBase/${OS_BASE_NAME}${LIB_SUFFIX} + ${PROJECT_ROOT_DIR}/wxModularCore/${OS_BASE_NAME}${LIB_SUFFIX}) + set(DEMO_LIBS_SHARED wxNonGuiPluginBase wxGuiPluginBase) + set(DEMO_LIBS wxModularCore ${DEMO_LIBS_SHARED}) +endif(LINUX OR APPLE) + +find_package(OpenCV REQUIRED) + +set(LIBS ${DEMO_LIBS} ${wxWidgets_LIBRARIES} ${wxWidgets_aui_lib_output} ${OpenCV_LIBS}) + +set(EXECUTABLE_NAME wxModularHost) + +add_definitions(${PREPROCESSOR_DEFINITIONS}) +include_directories(${INCLUDE_DIRECTORIES} ${BASE_INCLUDE_DIRECTORIES}) +link_directories(${LINK_DIRECTORIES}) + +if(WIN32) + set(EXECUTABLE_TYPE WIN32) +endif(WIN32) +if(APPLE) + set(MACOSX_BUNDLE YES) + set(EXECUTABLE_TYPE MACOSX_BUNDLE) + set(CMAKE_INSTALL_PATH "@loader_path") +endif(APPLE) +if(LINUX) + set(EXECUTABLE_TYPE "") +endif(LINUX) + +set(SRCS ${SRCS} ${HEADERS} + ${PROJECT_ROOT_DIR}/include/stdwx.h + ${PROJECT_ROOT_DIR}/include/stdwx.cpp) + +set(PROJECT_FILES ${SRCS}) +add_executable(${EXECUTABLE_NAME} ${EXECUTABLE_TYPE} ${PROJECT_FILES}) + +set(EXE_DIR bin) +set(TARGET_LOCATION ${PROJECT_SOURCE_DIR}/${EXE_DIR}${LIB_SUFFIX}) +set_target_properties(${EXECUTABLE_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${TARGET_LOCATION}) +if(APPLE) + set(CMAKE_MACOSX_RPATH 1) + set(MACOSX_RPATH TRUE) + set_target_properties( + ${EXECUTABLE_NAME} PROPERTIES + INSTALL_RPATH "@loader_path/../Frameworks") +endif(APPLE) + +target_link_libraries(${EXECUTABLE_NAME} ${LIBS}) + +add_dependencies(${EXECUTABLE_NAME} wxModularCore) + +target_precompile_headers(${EXECUTABLE_NAME} PRIVATE ${PROJECT_ROOT_DIR}/include/stdwx.h) + +if(APPLE) + FOREACH(DEP_LIB ${DEMO_LIBS_SHARED}) + get_filename_component(ABS_ROOT_DIR ${PROJECT_ROOT_DIR} ABSOLUTE) + set(LIBNAME_FULL "${ABS_ROOT_DIR}/${DEP_LIB}/${OS_BASE_NAME}${LIB_SUFFIX}/$(CONFIGURATION)/lib${DEP_LIB}.dylib") + add_custom_command(TARGET ${EXECUTABLE_NAME} POST_BUILD + COMMAND install_name_tool -change "${LIBNAME_FULL}" "@executable_path/../Frameworks/lib${DEP_LIB}.dylib" $) + ENDFOREACH(DEP_LIB) +endif(APPLE) diff --git a/wxModularHost/MainFrame.cpp b/wxModularHost/MainFrame.cpp index 6c8d050..24ec667 100644 --- a/wxModularHost/MainFrame.cpp +++ b/wxModularHost/MainFrame.cpp @@ -1,195 +1,195 @@ -///////////////////////////////////////////////////////////////////////////// -// Name: MainFrame.cpp -// Purpose: -// Author: Volodymyr (T-Rex) Triapichko -// Modified by: -// Created: 02/08/2013 21:20:05 -// RCS-ID: -// Copyright: Volodymyr (T-Rex) Triapichko, 2013 -// Licence: -///////////////////////////////////////////////////////////////////////////// - -// For compilers that support precompilation, includes "wx/wx.h". -#include "stdwx.h" - -#ifdef __BORLANDC__ -#pragma hdrstop -#endif - -#ifndef WX_PRECOMP -#include "wx/wx.h" -#endif - -////@begin includes -#include "wx/imaglist.h" -////@end includes - -#include "MainFrame.h" -#include "wxModularHostApp.h" -#include "SampleModularCore.h" - -////@begin XPM images -////@end XPM images - - -/* - * MainFrame type definition - */ - -IMPLEMENT_CLASS( MainFrame, wxFrame ) - - -/* - * MainFrame event table definition - */ - -BEGIN_EVENT_TABLE( MainFrame, wxFrame ) - -////@begin MainFrame event table entries -////@end MainFrame event table entries - -END_EVENT_TABLE() - - -/* - * MainFrame constructors - */ - -MainFrame::MainFrame() -{ - Init(); -} - -MainFrame::MainFrame( wxWindow* parent, wxWindowID id, const wxString& caption, const wxPoint& pos, const wxSize& size, long style ) -{ - Init(); - Create( parent, id, caption, pos, size, style ); -} - - -/* - * MainFrame creator - */ - -bool MainFrame::Create( wxWindow* parent, wxWindowID id, const wxString& caption, const wxPoint& pos, const wxSize& size, long style ) -{ -////@begin MainFrame creation - wxFrame::Create( parent, id, caption, pos, size, style ); - - CreateControls(); - Centre(); -////@end MainFrame creation - return true; -} - - -/* - * MainFrame destructor - */ - -MainFrame::~MainFrame() -{ -////@begin MainFrame destruction - GetAuiManager().UnInit(); -////@end MainFrame destruction -} - - -/* - * Member initialisation - */ - -void MainFrame::Init() -{ -////@begin MainFrame member initialisation - m_Notebook = NULL; -////@end MainFrame member initialisation -} - - -/* - * Control creation for MainFrame - */ - -void MainFrame::CreateControls() -{ -////@begin MainFrame content construction - MainFrame* itemFrame1 = this; - - GetAuiManager().SetManagedWindow(this); - - wxMenuBar* menuBar = new wxMenuBar; - wxMenu* itemMenu4 = new wxMenu; - itemMenu4->Append(wxID_EXIT, _("Exit\tAlt+F4"), wxEmptyString, wxITEM_NORMAL); - menuBar->Append(itemMenu4, _("File")); - itemFrame1->SetMenuBar(menuBar); - - wxStatusBar* itemStatusBar2 = new wxStatusBar( itemFrame1, ID_STATUSBAR, wxST_SIZEGRIP|wxNO_BORDER ); - itemStatusBar2->SetFieldsCount(2); - itemFrame1->SetStatusBar(itemStatusBar2); - - m_Notebook = new wxAuiNotebook( itemFrame1, ID_AUINOTEBOOK, wxDefaultPosition, wxDefaultSize, wxAUI_NB_DEFAULT_STYLE|wxAUI_NB_TOP|wxNO_BORDER ); - - itemFrame1->GetAuiManager().AddPane(m_Notebook, wxAuiPaneInfo() - .Name(_T("Pane1")).Centre().CaptionVisible(false).CloseButton(false).DestroyOnClose(false).Resizable(true).Floatable(false)); - - GetAuiManager().Update(); - -////@end MainFrame content construction - AddPagesFromGuiPlugins(); - -} - - -/* - * Should we show tooltips? - */ - -bool MainFrame::ShowToolTips() -{ - return true; -} - -/* - * Get bitmap resources - */ - -wxBitmap MainFrame::GetBitmapResource( const wxString& name ) -{ - // Bitmap retrieval -////@begin MainFrame bitmap retrieval - wxUnusedVar(name); - return wxNullBitmap; -////@end MainFrame bitmap retrieval -} - -/* - * Get icon resources - */ - -wxIcon MainFrame::GetIconResource( const wxString& name ) -{ - // Icon retrieval -////@begin MainFrame icon retrieval - wxUnusedVar(name); - return wxNullIcon; -////@end MainFrame icon retrieval -} - -void MainFrame::AddPagesFromGuiPlugins() -{ - SampleModularCore * pluginManager = wxGetApp().GetPluginManager(); - for(wxGuiPluginBaseList::Node * node = pluginManager->GetGuiPlugins().GetFirst(); - node; node = node->GetNext()) - { - wxGuiPluginBase * plugin = node->GetData(); - if(plugin) - { - wxWindow * page = plugin->CreatePanel(m_Notebook); - if(page) - { - m_Notebook->AddPage(page, plugin->GetName()); - } - } - } -} +///////////////////////////////////////////////////////////////////////////// +// Name: MainFrame.cpp +// Purpose: +// Author: Volodymyr (T-Rex) Triapichko +// Modified by: +// Created: 02/08/2013 21:20:05 +// RCS-ID: +// Copyright: Volodymyr (T-Rex) Triapichko, 2013 +// Licence: +///////////////////////////////////////////////////////////////////////////// + +// For compilers that support precompilation, includes "wx/wx.h". +#include "stdwx.h" + +#ifdef __BORLANDC__ +#pragma hdrstop +#endif + +#ifndef WX_PRECOMP +#include "wx/wx.h" +#endif + +////@begin includes +#include "wx/imaglist.h" +////@end includes + +#include "MainFrame.h" +#include "wxModularHostApp.h" +#include "SampleModularCore.h" + +////@begin XPM images +////@end XPM images + + +/* + * MainFrame type definition + */ + +IMPLEMENT_CLASS( MainFrame, wxFrame ) + + +/* + * MainFrame event table definition + */ + +BEGIN_EVENT_TABLE( MainFrame, wxFrame ) + +////@begin MainFrame event table entries +////@end MainFrame event table entries + +END_EVENT_TABLE() + + +/* + * MainFrame constructors + */ + +MainFrame::MainFrame() +{ + Init(); +} + +MainFrame::MainFrame( wxWindow* parent, wxWindowID id, const wxString& caption, const wxPoint& pos, const wxSize& size, long style ) +{ + Init(); + Create( parent, id, caption, pos, size, style ); +} + + +/* + * MainFrame creator + */ + +bool MainFrame::Create( wxWindow* parent, wxWindowID id, const wxString& caption, const wxPoint& pos, const wxSize& size, long style ) +{ +////@begin MainFrame creation + wxFrame::Create( parent, id, caption, pos, size, style ); + + CreateControls(); + Centre(); +////@end MainFrame creation + return true; +} + + +/* + * MainFrame destructor + */ + +MainFrame::~MainFrame() +{ +////@begin MainFrame destruction + GetAuiManager().UnInit(); +////@end MainFrame destruction +} + + +/* + * Member initialisation + */ + +void MainFrame::Init() +{ +////@begin MainFrame member initialisation + m_Notebook = NULL; +////@end MainFrame member initialisation +} + + +/* + * Control creation for MainFrame + */ + +void MainFrame::CreateControls() +{ +////@begin MainFrame content construction + MainFrame* itemFrame1 = this; + + GetAuiManager().SetManagedWindow(this); + + wxMenuBar* menuBar = new wxMenuBar; + wxMenu* itemMenu4 = new wxMenu; + itemMenu4->Append(wxID_EXIT, _("Exit\tAlt+F4"), wxEmptyString, wxITEM_NORMAL); + menuBar->Append(itemMenu4, _("File")); + itemFrame1->SetMenuBar(menuBar); + + wxStatusBar* itemStatusBar2 = new wxStatusBar( itemFrame1, ID_STATUSBAR, wxST_SIZEGRIP|wxNO_BORDER ); + itemStatusBar2->SetFieldsCount(2); + itemFrame1->SetStatusBar(itemStatusBar2); + + m_Notebook = new wxAuiNotebook( itemFrame1, ID_AUINOTEBOOK, wxDefaultPosition, wxDefaultSize, wxAUI_NB_DEFAULT_STYLE|wxAUI_NB_TOP|wxNO_BORDER ); + + itemFrame1->GetAuiManager().AddPane(m_Notebook, wxAuiPaneInfo() + .Name(_T("Pane1")).Centre().CaptionVisible(false).CloseButton(false).DestroyOnClose(false).Resizable(true).Floatable(false)); + + GetAuiManager().Update(); + +////@end MainFrame content construction + AddPagesFromGuiPlugins(); + +} + + +/* + * Should we show tooltips? + */ + +bool MainFrame::ShowToolTips() +{ + return true; +} + +/* + * Get bitmap resources + */ + +wxBitmap MainFrame::GetBitmapResource( const wxString& name ) +{ + // Bitmap retrieval +////@begin MainFrame bitmap retrieval + wxUnusedVar(name); + return wxNullBitmap; +////@end MainFrame bitmap retrieval +} + +/* + * Get icon resources + */ + +wxIcon MainFrame::GetIconResource( const wxString& name ) +{ + // Icon retrieval +////@begin MainFrame icon retrieval + wxUnusedVar(name); + return wxNullIcon; +////@end MainFrame icon retrieval +} + +void MainFrame::AddPagesFromGuiPlugins() +{ + SampleModularCore * pluginManager = wxGetApp().GetPluginManager(); + for(wxGuiPluginBaseList::compatibility_iterator node = pluginManager->GetGuiPlugins().GetFirst(); + node; node = node->GetNext()) + { + wxGuiPluginBase* plugin = node->GetData(); + if (plugin) + { + wxWindow* page = plugin->CreatePanel(m_Notebook); + if (page) + { + m_Notebook->AddPage(page, plugin->GetName()); + } + } + } +} diff --git a/wxModularHost/MainFrame.h b/wxModularHost/MainFrame.h index 1c28b12..a3335d1 100644 --- a/wxModularHost/MainFrame.h +++ b/wxModularHost/MainFrame.h @@ -1,107 +1,107 @@ -///////////////////////////////////////////////////////////////////////////// -// Name: MainFrame.h -// Purpose: -// Author: Volodymyr (T-Rex) Triapichko -// Modified by: -// Created: 02/08/2013 21:20:05 -// RCS-ID: -// Copyright: Volodymyr (T-Rex) Triapichko, 2013 -// Licence: -///////////////////////////////////////////////////////////////////////////// - -#ifndef _MAINFRAME_H_ -#define _MAINFRAME_H_ - - -/*! - * Includes - */ - -////@begin includes -#include "wx/aui/framemanager.h" -#include "wx/frame.h" -#include "wx/statusbr.h" -#include "wx/aui/auibook.h" -////@end includes - -/*! - * Forward declarations - */ - -////@begin forward declarations -class wxAuiNotebook; -////@end forward declarations - -/*! - * Control identifiers - */ - -////@begin control identifiers -#define SYMBOL_MAINFRAME_STYLE wxCAPTION|wxRESIZE_BORDER|wxSYSTEM_MENU|wxCLOSE_BOX -#define SYMBOL_MAINFRAME_TITLE _("MainFrame") -#define SYMBOL_MAINFRAME_IDNAME ID_MAINFRAME -#define SYMBOL_MAINFRAME_SIZE wxSize(600, 450) -#define SYMBOL_MAINFRAME_POSITION wxDefaultPosition -////@end control identifiers - - -/*! - * MainFrame class declaration - */ - -class MainFrame: public wxFrame -{ - DECLARE_CLASS( MainFrame ) - DECLARE_EVENT_TABLE() - -public: - /// Constructors - MainFrame(); - MainFrame( wxWindow* parent, wxWindowID id = SYMBOL_MAINFRAME_IDNAME, const wxString& caption = SYMBOL_MAINFRAME_TITLE, const wxPoint& pos = SYMBOL_MAINFRAME_POSITION, const wxSize& size = SYMBOL_MAINFRAME_SIZE, long style = SYMBOL_MAINFRAME_STYLE ); - - bool Create( wxWindow* parent, wxWindowID id = SYMBOL_MAINFRAME_IDNAME, const wxString& caption = SYMBOL_MAINFRAME_TITLE, const wxPoint& pos = SYMBOL_MAINFRAME_POSITION, const wxSize& size = SYMBOL_MAINFRAME_SIZE, long style = SYMBOL_MAINFRAME_STYLE ); - - /// Destructor - ~MainFrame(); - - /// Initialises member variables - void Init(); - - /// Creates the controls and sizers - void CreateControls(); - - void AddPagesFromGuiPlugins(); - -////@begin MainFrame event handler declarations - -////@end MainFrame event handler declarations - -////@begin MainFrame member function declarations - - /// Returns the AUI manager object - wxAuiManager& GetAuiManager() { return m_auiManager; } - - /// Retrieves bitmap resources - wxBitmap GetBitmapResource( const wxString& name ); - - /// Retrieves icon resources - wxIcon GetIconResource( const wxString& name ); -////@end MainFrame member function declarations - - /// Should we show tooltips? - static bool ShowToolTips(); - -////@begin MainFrame member variables - wxAuiManager m_auiManager; - wxAuiNotebook* m_Notebook; - /// Control identifiers - enum { - ID_MAINFRAME = 10000, - ID_STATUSBAR = 10001, - ID_AUINOTEBOOK = 10003 - }; -////@end MainFrame member variables -}; - -#endif - // _MAINFRAME_H_ +///////////////////////////////////////////////////////////////////////////// +// Name: MainFrame.h +// Purpose: +// Author: Volodymyr (T-Rex) Triapichko +// Modified by: +// Created: 02/08/2013 21:20:05 +// RCS-ID: +// Copyright: Volodymyr (T-Rex) Triapichko, 2013 +// Licence: +///////////////////////////////////////////////////////////////////////////// + +#ifndef _MAINFRAME_H_ +#define _MAINFRAME_H_ + + +/*! + * Includes + */ + +////@begin includes +#include "wx/aui/framemanager.h" +#include "wx/frame.h" +#include "wx/statusbr.h" +#include "wx/aui/auibook.h" +////@end includes + +/*! + * Forward declarations + */ + +////@begin forward declarations +class wxAuiNotebook; +////@end forward declarations + +/*! + * Control identifiers + */ + +////@begin control identifiers +#define SYMBOL_MAINFRAME_STYLE wxCAPTION|wxRESIZE_BORDER|wxSYSTEM_MENU|wxCLOSE_BOX +#define SYMBOL_MAINFRAME_TITLE _("MainFrame") +#define SYMBOL_MAINFRAME_IDNAME ID_MAINFRAME +#define SYMBOL_MAINFRAME_SIZE wxSize(600, 450) +#define SYMBOL_MAINFRAME_POSITION wxDefaultPosition +////@end control identifiers + + +/*! + * MainFrame class declaration + */ + +class MainFrame: public wxFrame +{ + DECLARE_CLASS( MainFrame ) + DECLARE_EVENT_TABLE() + +public: + /// Constructors + MainFrame(); + MainFrame( wxWindow* parent, wxWindowID id = SYMBOL_MAINFRAME_IDNAME, const wxString& caption = SYMBOL_MAINFRAME_TITLE, const wxPoint& pos = SYMBOL_MAINFRAME_POSITION, const wxSize& size = SYMBOL_MAINFRAME_SIZE, long style = SYMBOL_MAINFRAME_STYLE ); + + bool Create( wxWindow* parent, wxWindowID id = SYMBOL_MAINFRAME_IDNAME, const wxString& caption = SYMBOL_MAINFRAME_TITLE, const wxPoint& pos = SYMBOL_MAINFRAME_POSITION, const wxSize& size = SYMBOL_MAINFRAME_SIZE, long style = SYMBOL_MAINFRAME_STYLE ); + + /// Destructor + ~MainFrame(); + + /// Initialises member variables + void Init(); + + /// Creates the controls and sizers + void CreateControls(); + + void AddPagesFromGuiPlugins(); + +////@begin MainFrame event handler declarations + +////@end MainFrame event handler declarations + +////@begin MainFrame member function declarations + + /// Returns the AUI manager object + wxAuiManager& GetAuiManager() { return m_auiManager; } + + /// Retrieves bitmap resources + wxBitmap GetBitmapResource( const wxString& name ); + + /// Retrieves icon resources + wxIcon GetIconResource( const wxString& name ); +////@end MainFrame member function declarations + + /// Should we show tooltips? + static bool ShowToolTips(); + +////@begin MainFrame member variables + wxAuiManager m_auiManager; + wxAuiNotebook* m_Notebook; + /// Control identifiers + enum { + ID_MAINFRAME = 10000, + ID_STATUSBAR = 10001, + ID_AUINOTEBOOK = 10003 + }; +////@end MainFrame member variables +}; + +#endif + // _MAINFRAME_H_ diff --git a/wxModularHost/SampleModularCore.cpp b/wxModularHost/SampleModularCore.cpp index cbc8510..0e5d4d3 100644 --- a/wxModularHost/SampleModularCore.cpp +++ b/wxModularHost/SampleModularCore.cpp @@ -1,65 +1,65 @@ -#include "stdwx.h" -#include "SampleModularCore.h" -#include - -WX_DEFINE_LIST(wxNonGuiPluginBaseList); -WX_DEFINE_LIST(wxGuiPluginBaseList); - -SampleModularCore::~SampleModularCore() -{ - Clear(); -} - -bool SampleModularCore::LoadAllPlugins(bool forceProgramPath) -{ - wxString pluginsRootDir = GetPluginsPath(forceProgramPath); - bool result = true; - result &= LoadPlugins(pluginsRootDir, - m_NonGuiPlugins, - m_MapNonGuiPluginsDll, - wxT("nongui")); - result &= LoadPlugins(pluginsRootDir, - m_GuiPlugins, - m_MapGuiPluginsDll, - wxT("gui")); - // You can implement other logic which takes in account - // the result of LoadPlugins() calls - for(wxGuiPluginBaseList::Node * node = m_GuiPlugins.GetFirst(); - node; node = node->GetNext()) - { - wxGuiPluginBase * plugin = node->GetData(); - plugin->SetEventHandler(m_Handler); - } - return true; -} - -bool SampleModularCore::UnloadAllPlugins() -{ - return - UnloadPlugins(m_NonGuiPlugins, - m_MapNonGuiPluginsDll) && - UnloadPlugins(m_GuiPlugins, - m_MapGuiPluginsDll); -} - -const wxNonGuiPluginBaseList & SampleModularCore::GetNonGuiPlugins() const -{ - return m_NonGuiPlugins; -} - -const wxGuiPluginBaseList & SampleModularCore::GetGuiPlugins() const -{ - return m_GuiPlugins; +#include "stdwx.h" +#include "SampleModularCore.h" +#include + +WX_DEFINE_LIST(wxNonGuiPluginBaseList); +WX_DEFINE_LIST(wxGuiPluginBaseList); + +SampleModularCore::~SampleModularCore() +{ + Clear(); +} + +bool SampleModularCore::LoadAllPlugins(bool forceProgramPath) +{ + wxString pluginsRootDir = GetPluginsPath(forceProgramPath); + bool result = true; + result &= LoadPlugins(pluginsRootDir, + m_NonGuiPlugins, + m_MapNonGuiPluginsDll, + wxT("nongui")); + result &= LoadPlugins(pluginsRootDir, + m_GuiPlugins, + m_MapGuiPluginsDll, + wxT("gui")); + // You can implement other logic which takes in account + // the result of LoadPlugins() calls + for(wxGuiPluginBaseList::compatibility_iterator node = m_GuiPlugins.GetFirst(); + node; node = node->GetNext()) + { + wxGuiPluginBase * plugin = node->GetData(); + plugin->SetEventHandler(m_Handler); + } + return true; +} + +bool SampleModularCore::UnloadAllPlugins() +{ + return + UnloadPlugins(m_NonGuiPlugins, + m_MapNonGuiPluginsDll) && + UnloadPlugins(m_GuiPlugins, + m_MapGuiPluginsDll); +} + +const wxNonGuiPluginBaseList & SampleModularCore::GetNonGuiPlugins() const +{ + return m_NonGuiPlugins; +} + +const wxGuiPluginBaseList & SampleModularCore::GetGuiPlugins() const +{ + return m_GuiPlugins; } \ No newline at end of file diff --git a/wxModularHost/SampleModularCore.h b/wxModularHost/SampleModularCore.h index 2048b85..55c2609 100644 --- a/wxModularHost/SampleModularCore.h +++ b/wxModularHost/SampleModularCore.h @@ -1,32 +1,32 @@ -#pragma once - -#include -#include -#include - -// We need to know which DLL produced the specific plugin object. -WX_DECLARE_HASH_MAP(wxNonGuiPluginBase*, wxDynamicLibrary*, \ - wxPointerHash, wxPointerEqual, \ - wxNonGuiPluginToDllDictionary); -WX_DECLARE_HASH_MAP(wxGuiPluginBase*, wxDynamicLibrary*, \ - wxPointerHash, wxPointerEqual, \ - wxGuiPluginToDllDictionary); -// And separate list of loaded plugins for faster access. -WX_DECLARE_LIST(wxNonGuiPluginBase, wxNonGuiPluginBaseList); -WX_DECLARE_LIST(wxGuiPluginBase, wxGuiPluginBaseList); - -class SampleModularCore : public wxModularCore -{ -public: - virtual ~SampleModularCore(); - virtual bool LoadAllPlugins(bool forceProgramPath); - virtual bool UnloadAllPlugins(); - - const wxNonGuiPluginBaseList & GetNonGuiPlugins() const; - const wxGuiPluginBaseList & GetGuiPlugins() const; -private: - wxNonGuiPluginToDllDictionary m_MapNonGuiPluginsDll; - wxNonGuiPluginBaseList m_NonGuiPlugins; - wxGuiPluginToDllDictionary m_MapGuiPluginsDll; - wxGuiPluginBaseList m_GuiPlugins; +#pragma once + +#include +#include +#include + +// We need to know which DLL produced the specific plugin object. +WX_DECLARE_HASH_MAP(wxNonGuiPluginBase*, wxDynamicLibrary*, \ + wxPointerHash, wxPointerEqual, \ + wxNonGuiPluginToDllDictionary); +WX_DECLARE_HASH_MAP(wxGuiPluginBase*, wxDynamicLibrary*, \ + wxPointerHash, wxPointerEqual, \ + wxGuiPluginToDllDictionary); +// And separate list of loaded plugins for faster access. +WX_DECLARE_LIST(wxNonGuiPluginBase, wxNonGuiPluginBaseList); +WX_DECLARE_LIST(wxGuiPluginBase, wxGuiPluginBaseList); + +class SampleModularCore : public wxModularCore +{ +public: + virtual ~SampleModularCore(); + virtual bool LoadAllPlugins(bool forceProgramPath); + virtual bool UnloadAllPlugins(); + + const wxNonGuiPluginBaseList & GetNonGuiPlugins() const; + const wxGuiPluginBaseList & GetGuiPlugins() const; +private: + wxNonGuiPluginToDllDictionary m_MapNonGuiPluginsDll; + wxNonGuiPluginBaseList m_NonGuiPlugins; + wxGuiPluginToDllDictionary m_MapGuiPluginsDll; + wxGuiPluginBaseList m_GuiPlugins; }; \ No newline at end of file diff --git a/wxModularHost/Win/wxModularHost.vcxproj b/wxModularHost/Win/wxModularHost.vcxproj new file mode 100644 index 0000000..6bbaa1e --- /dev/null +++ b/wxModularHost/Win/wxModularHost.vcxproj @@ -0,0 +1,275 @@ + + + + x64 + + + + Debug + x64 + + + Release + x64 + + + + {B4520BB8-638C-30A5-B13E-9F8B09B91B37} + Win32Proj + 10.0.26100.0 + x64 + wxModularHost + NoUpgrade + + + + Application + Unicode + v143 + + + Application + Unicode + v143 + + + + + + + + + + <_ProjectFileVersion>10.0.20506.1 + C:\Users\Admin\IFloor\projects\wxVideoProcessing\bin\ + wxModularHost.dir\Debug\ + wxModularHost + .exe + true + true + C:\Users\Admin\IFloor\projects\wxVideoProcessing\bin\ + wxModularHost.dir\Release\ + wxModularHost + .exe + false + true + + + + C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\mswu;C:\Users\Admin\IFloor\libs\wxWidgets\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\Utils;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\MotionDetectorCore;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxJSON\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxxmlserializer\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxxmlserializer;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\wxModularCore;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\wxNonGuiPluginBase;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\wxGuiPluginBase;%(AdditionalIncludeDirectories) + %(AdditionalOptions) /external:I "C:/Users/Admin/IFloor/libs/opencv/build/include" + $(IntDir) + EnableFastChecks + + + ProgramDatabase + 4996 + Sync + TurnOffAllWarnings + + + Disabled + stdcpp17 + + true + Disabled + Use + C:/Users/Admin/IFloor/projects/wxVideoProcessing/wxModularHost/Win/CMakeFiles/wxModularHost.dir/Debug/cmake_pch.hxx + C:/Users/Admin/IFloor/projects/wxVideoProcessing/wxModularHost/Win/wxModularHost.dir/Debug//cmake_pch.pch + + MultiThreadedDebugDLL + true + + + false + %(PreprocessorDefinitions);WIN32;_WINDOWS;__WXDEBUG__=1;WIN32_LEAN_AND_MEAN;WXUSINGDLL;UNICODE;_UNICODE;_CRT_SECURE_NO_DEPRECATE;_USRDLL;wxUSE_NO_MANIFEST=1;__STDC_CONSTANT_MACROS;CMAKE_INTDIR="Debug" + $(IntDir) + false + + + %(PreprocessorDefinitions);WIN32;_DEBUG;_WINDOWS;__WXDEBUG__=1;WIN32_LEAN_AND_MEAN;WXUSINGDLL;UNICODE;_UNICODE;_CRT_SECURE_NO_DEPRECATE;_USRDLL;wxUSE_NO_MANIFEST=1;__STDC_CONSTANT_MACROS;CMAKE_INTDIR=\"Debug\" + C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\mswu;C:\Users\Admin\IFloor\libs\wxWidgets\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\Utils;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\MotionDetectorCore;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxJSON\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxxmlserializer\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxxmlserializer;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\wxModularCore;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\wxNonGuiPluginBase;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\wxGuiPluginBase;C:\Users\Admin\IFloor\libs\opencv\build\include;%(AdditionalIncludeDirectories) + + + C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\mswu;C:\Users\Admin\IFloor\libs\wxWidgets\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\Utils;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\MotionDetectorCore;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxJSON\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxxmlserializer\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxxmlserializer;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\wxModularCore;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\wxNonGuiPluginBase;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\wxGuiPluginBase;C:\Users\Admin\IFloor\libs\opencv\build\include;%(AdditionalIncludeDirectories) + $(ProjectDir)/$(IntDir) + %(Filename).h + %(Filename).tlb + %(Filename)_i.c + %(Filename)_p.c + + + wxModularCore.lib;wxNonGuiPluginBase.lib;wxGuiPluginBase.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxbase33ud.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33ud_core.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33ud_adv.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33ud_aui.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxbase33ud_net.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33ud_gl.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxbase33ud_xml.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33ud_propgrid.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33ud_html.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxpngd.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxtiffd.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxjpegd.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxzlibd.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxregexud.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxexpatd.lib;opengl32.lib;glu32.lib;winmm.lib;comctl32.lib;uuid.lib;oleacc.lib;uxtheme.lib;rpcrt4.lib;shlwapi.lib;version.lib;wsock32.lib;C:\Users\Admin\IFloor\libs\opencv\build\x64\vc16\lib\opencv_world4120d.lib;C:\Users\Admin\IFloor\libs\opencv\build\x64\vc16\lib\opencv_world4120d.lib;C:\Users\Admin\IFloor\libs\opencv\build\x64\vc16\lib\opencv_world4120d.lib;C:\Users\Admin\IFloor\libs\opencv\build\x64\vc16\lib\opencv_world4120d.lib;C:\Users\Admin\IFloor\libs\opencv\build\x64\vc16\lib\opencv_world4120d.lib;C:\Users\Admin\IFloor\libs\opencv\build\x64\vc16\lib\opencv_world4120d.lib;C:\Users\Admin\IFloor\libs\opencv\build\x64\vc16\lib\opencv_world4120d.lib;C:\Users\Admin\IFloor\libs\opencv\build\x64\vc16\lib\opencv_world4120d.lib;C:\Users\Admin\IFloor\libs\opencv\build\x64\vc16\lib\opencv_world4120d.lib;C:\Users\Admin\IFloor\libs\opencv\build\x64\vc16\lib\opencv_world4120d.lib;C:\Users\Admin\IFloor\libs\opencv\build\x64\vc16\lib\opencv_world4120d.lib;C:\Users\Admin\IFloor\libs\opencv\build\x64\vc16\lib\opencv_world4120d.lib;C:\Users\Admin\IFloor\libs\opencv\build\x64\vc16\lib\opencv_world4120d.lib;C:\Users\Admin\IFloor\libs\opencv\build\x64\vc16\lib\opencv_world4120d.lib;C:\Users\Admin\IFloor\libs\opencv\build\x64\vc16\lib\opencv_world4120d.lib;C:\Users\Admin\IFloor\libs\opencv\build\x64\vc16\lib\opencv_world4120d.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;comdlg32.lib;advapi32.lib + C:/Users/Admin/IFloor/projects/wxVideoProcessing/build/Win/lib;C:/Users/Admin/IFloor/projects/wxVideoProcessing/build/Win/lib/$(Configuration);C:/Users/Admin/IFloor/projects/wxVideoProcessing/build/../wxNonGuiPluginBase/Win/$(ConfigurationName);C:/Users/Admin/IFloor/projects/wxVideoProcessing/build/../wxNonGuiPluginBase/Win/$(ConfigurationName)/$(Configuration);C:/Users/Admin/IFloor/projects/wxVideoProcessing/build/../wxGuiPluginBase/Win/$(ConfigurationName);C:/Users/Admin/IFloor/projects/wxVideoProcessing/build/../wxGuiPluginBase/Win/$(ConfigurationName)/$(Configuration);C:/Users/Admin/IFloor/projects/wxVideoProcessing/build/../wxModularCore/Win/$(ConfigurationName);C:/Users/Admin/IFloor/projects/wxVideoProcessing/build/../wxModularCore/Win/$(ConfigurationName)/$(Configuration);%(AdditionalLibraryDirectories) + %(AdditionalOptions) /machine:x64 + + true + %(IgnoreSpecificDefaultLibraries) + + C:/Users/Admin/IFloor/projects/wxVideoProcessing/bin/lib/Debug/wxModularHost.lib + + C:/Users/Admin/IFloor/projects/wxVideoProcessing/bin/wxModularHost.pdb + + Windows + + + false + + + + + C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\mswu;C:\Users\Admin\IFloor\libs\wxWidgets\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\Utils;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\MotionDetectorCore;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxJSON\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxxmlserializer\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxxmlserializer;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\wxModularCore;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\wxNonGuiPluginBase;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\wxGuiPluginBase;%(AdditionalIncludeDirectories) + %(AdditionalOptions) /external:I "C:/Users/Admin/IFloor/libs/opencv/build/include" + $(IntDir) + Default + + + 4996 + Sync + TurnOffAllWarnings + + + AnySuitable + stdcpp17 + + true + MaxSpeed + Use + C:/Users/Admin/IFloor/projects/wxVideoProcessing/wxModularHost/Win/CMakeFiles/wxModularHost.dir/Release/cmake_pch.hxx + C:/Users/Admin/IFloor/projects/wxVideoProcessing/wxModularHost/Win/wxModularHost.dir/Release//cmake_pch.pch + + MultiThreadedDLL + true + + + false + %(PreprocessorDefinitions);WIN32;_WINDOWS;NDEBUG;WIN32_LEAN_AND_MEAN;WXUSINGDLL;UNICODE;_UNICODE;_CRT_SECURE_NO_DEPRECATE;_USRDLL;wxUSE_NO_MANIFEST=1;__STDC_CONSTANT_MACROS;CMAKE_INTDIR="Release" + $(IntDir) + + + false + + + %(PreprocessorDefinitions);WIN32;_WINDOWS;NDEBUG;WIN32_LEAN_AND_MEAN;WXUSINGDLL;UNICODE;_UNICODE;_CRT_SECURE_NO_DEPRECATE;_USRDLL;wxUSE_NO_MANIFEST=1;__STDC_CONSTANT_MACROS;CMAKE_INTDIR=\"Release\" + C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\mswu;C:\Users\Admin\IFloor\libs\wxWidgets\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\Utils;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\MotionDetectorCore;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxJSON\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxxmlserializer\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxxmlserializer;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\wxModularCore;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\wxNonGuiPluginBase;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\wxGuiPluginBase;C:\Users\Admin\IFloor\libs\opencv\build\include;%(AdditionalIncludeDirectories) + + + C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\mswu;C:\Users\Admin\IFloor\libs\wxWidgets\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\Utils;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\MotionDetectorCore;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxJSON\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxxmlserializer\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxxmlserializer;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\wxModularCore;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\wxNonGuiPluginBase;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\wxGuiPluginBase;C:\Users\Admin\IFloor\libs\opencv\build\include;%(AdditionalIncludeDirectories) + $(ProjectDir)/$(IntDir) + %(Filename).h + %(Filename).tlb + %(Filename)_i.c + %(Filename)_p.c + + + wxModularCore.lib;wxNonGuiPluginBase.lib;wxGuiPluginBase.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxbase33u.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33u_core.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33u_adv.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33u_aui.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxbase33u_net.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33u_gl.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxbase33u_xml.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33u_propgrid.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33u_html.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxpng.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxtiff.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxjpeg.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxzlib.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxregexu.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxexpat.lib;opengl32.lib;glu32.lib;winmm.lib;comctl32.lib;uuid.lib;oleacc.lib;uxtheme.lib;rpcrt4.lib;shlwapi.lib;version.lib;wsock32.lib;C:\Users\Admin\IFloor\libs\opencv\build\x64\vc16\lib\opencv_world4120.lib;C:\Users\Admin\IFloor\libs\opencv\build\x64\vc16\lib\opencv_world4120.lib;C:\Users\Admin\IFloor\libs\opencv\build\x64\vc16\lib\opencv_world4120.lib;C:\Users\Admin\IFloor\libs\opencv\build\x64\vc16\lib\opencv_world4120.lib;C:\Users\Admin\IFloor\libs\opencv\build\x64\vc16\lib\opencv_world4120.lib;C:\Users\Admin\IFloor\libs\opencv\build\x64\vc16\lib\opencv_world4120.lib;C:\Users\Admin\IFloor\libs\opencv\build\x64\vc16\lib\opencv_world4120.lib;C:\Users\Admin\IFloor\libs\opencv\build\x64\vc16\lib\opencv_world4120.lib;C:\Users\Admin\IFloor\libs\opencv\build\x64\vc16\lib\opencv_world4120.lib;C:\Users\Admin\IFloor\libs\opencv\build\x64\vc16\lib\opencv_world4120.lib;C:\Users\Admin\IFloor\libs\opencv\build\x64\vc16\lib\opencv_world4120.lib;C:\Users\Admin\IFloor\libs\opencv\build\x64\vc16\lib\opencv_world4120.lib;C:\Users\Admin\IFloor\libs\opencv\build\x64\vc16\lib\opencv_world4120.lib;C:\Users\Admin\IFloor\libs\opencv\build\x64\vc16\lib\opencv_world4120.lib;C:\Users\Admin\IFloor\libs\opencv\build\x64\vc16\lib\opencv_world4120.lib;C:\Users\Admin\IFloor\libs\opencv\build\x64\vc16\lib\opencv_world4120.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;comdlg32.lib;advapi32.lib + C:/Users/Admin/IFloor/projects/wxVideoProcessing/build/Win/lib;C:/Users/Admin/IFloor/projects/wxVideoProcessing/build/Win/lib/$(Configuration);C:/Users/Admin/IFloor/projects/wxVideoProcessing/build/../wxNonGuiPluginBase/Win/$(ConfigurationName);C:/Users/Admin/IFloor/projects/wxVideoProcessing/build/../wxNonGuiPluginBase/Win/$(ConfigurationName)/$(Configuration);C:/Users/Admin/IFloor/projects/wxVideoProcessing/build/../wxGuiPluginBase/Win/$(ConfigurationName);C:/Users/Admin/IFloor/projects/wxVideoProcessing/build/../wxGuiPluginBase/Win/$(ConfigurationName)/$(Configuration);C:/Users/Admin/IFloor/projects/wxVideoProcessing/build/../wxModularCore/Win/$(ConfigurationName);C:/Users/Admin/IFloor/projects/wxVideoProcessing/build/../wxModularCore/Win/$(ConfigurationName)/$(Configuration);%(AdditionalLibraryDirectories) + %(AdditionalOptions) /machine:x64 + + false + %(IgnoreSpecificDefaultLibraries) + + C:/Users/Admin/IFloor/projects/wxVideoProcessing/bin/lib/Release/wxModularHost.lib + + C:/Users/Admin/IFloor/projects/wxVideoProcessing/bin/wxModularHost.pdb + + Windows + + + false + + + + + Always + Building Custom Rule C:/Users/Admin/IFloor/projects/wxVideoProcessing/wxModularHost/CMakeLists.txt + setlocal +"C:\Program Files\CMake\bin\cmake.exe" -SC:/Users/Admin/IFloor/projects/wxVideoProcessing/build -BC:/Users/Admin/IFloor/projects/wxVideoProcessing/build/Win --check-stamp-file C:/Users/Admin/IFloor/projects/wxVideoProcessing/wxModularHost/Win/CMakeFiles/generate.stamp +if %errorlevel% neq 0 goto :cmEnd +:cmEnd +endlocal & call :cmErrorLevel %errorlevel% & goto :cmDone +:cmErrorLevel +exit /b %1 +:cmDone +if %errorlevel% neq 0 goto :VCEnd + C:\Users\Admin\IFloor\libs\opencv\build\OpenCVConfig-version.cmake;C:\Users\Admin\IFloor\libs\opencv\build\OpenCVConfig.cmake;C:\Users\Admin\IFloor\libs\opencv\build\x64\vc16\lib\OpenCVConfig.cmake;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\FindPackageHandleStandardArgs.cmake;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\FindPackageMessage.cmake;%(AdditionalInputs) + C:\Users\Admin\IFloor\projects\wxVideoProcessing\wxModularHost\Win\CMakeFiles\generate.stamp + false + Building Custom Rule C:/Users/Admin/IFloor/projects/wxVideoProcessing/wxModularHost/CMakeLists.txt + setlocal +"C:\Program Files\CMake\bin\cmake.exe" -SC:/Users/Admin/IFloor/projects/wxVideoProcessing/build -BC:/Users/Admin/IFloor/projects/wxVideoProcessing/build/Win --check-stamp-file C:/Users/Admin/IFloor/projects/wxVideoProcessing/wxModularHost/Win/CMakeFiles/generate.stamp +if %errorlevel% neq 0 goto :cmEnd +:cmEnd +endlocal & call :cmErrorLevel %errorlevel% & goto :cmDone +:cmErrorLevel +exit /b %1 +:cmDone +if %errorlevel% neq 0 goto :VCEnd + C:\Users\Admin\IFloor\libs\opencv\build\OpenCVConfig-version.cmake;C:\Users\Admin\IFloor\libs\opencv\build\OpenCVConfig.cmake;C:\Users\Admin\IFloor\libs\opencv\build\x64\vc16\lib\OpenCVConfig.cmake;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\FindPackageHandleStandardArgs.cmake;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\FindPackageMessage.cmake;%(AdditionalInputs) + C:\Users\Admin\IFloor\projects\wxVideoProcessing\wxModularHost\Win\CMakeFiles\generate.stamp + false + + + + + C:/Users/Admin/IFloor/projects/wxVideoProcessing/wxModularHost/Win/CMakeFiles/wxModularHost.dir/Debug/cmake_pch.hxx;%(ForcedIncludeFiles) + Create + C:/Users/Admin/IFloor/projects/wxVideoProcessing/wxModularHost/Win/CMakeFiles/wxModularHost.dir/Debug/cmake_pch.hxx + C:/Users/Admin/IFloor/projects/wxVideoProcessing/wxModularHost/Win/wxModularHost.dir/Debug//cmake_pch.pch + C:/Users/Admin/IFloor/projects/wxVideoProcessing/wxModularHost/Win/CMakeFiles/wxModularHost.dir/Release/cmake_pch.hxx;%(ForcedIncludeFiles) + Create + C:/Users/Admin/IFloor/projects/wxVideoProcessing/wxModularHost/Win/CMakeFiles/wxModularHost.dir/Release/cmake_pch.hxx + C:/Users/Admin/IFloor/projects/wxVideoProcessing/wxModularHost/Win/wxModularHost.dir/Release//cmake_pch.pch + + + C:/Users/Admin/IFloor/projects/wxVideoProcessing/wxModularHost/Win/CMakeFiles/wxModularHost.dir/Debug/cmake_pch.hxx;%(ForcedIncludeFiles) + C:/Users/Admin/IFloor/projects/wxVideoProcessing/wxModularHost/Win/CMakeFiles/wxModularHost.dir/Release/cmake_pch.hxx;%(ForcedIncludeFiles) + + + C:/Users/Admin/IFloor/projects/wxVideoProcessing/wxModularHost/Win/CMakeFiles/wxModularHost.dir/Debug/cmake_pch.hxx;%(ForcedIncludeFiles) + C:/Users/Admin/IFloor/projects/wxVideoProcessing/wxModularHost/Win/CMakeFiles/wxModularHost.dir/Release/cmake_pch.hxx;%(ForcedIncludeFiles) + + + C:/Users/Admin/IFloor/projects/wxVideoProcessing/wxModularHost/Win/CMakeFiles/wxModularHost.dir/Debug/cmake_pch.hxx;%(ForcedIncludeFiles) + C:/Users/Admin/IFloor/projects/wxVideoProcessing/wxModularHost/Win/CMakeFiles/wxModularHost.dir/Release/cmake_pch.hxx;%(ForcedIncludeFiles) + + + NotUsing + NotUsing + + + + + + + C:/Users/Admin/IFloor/projects/wxVideoProcessing/wxModularHost/Win/CMakeFiles/wxModularHost.dir/Debug/cmake_pch.hxx;%(ForcedIncludeFiles) + C:/Users/Admin/IFloor/projects/wxVideoProcessing/wxModularHost/Win/CMakeFiles/wxModularHost.dir/Release/cmake_pch.hxx;%(ForcedIncludeFiles) + + + + + + + + {6F4BDD20-5B99-3029-BDF0-7D719B283AF5} + ZERO_CHECK + false + Never + + + {751A15C0-76C2-3506-AB9D-0CD3545003FA} + CommonPluginBase + + + {14D82890-BB7F-3748-8713-43304E91CFA1} + Utils + + + {746FA12D-51CE-3AA5-90F0-43DD3C32BDA9} + wxModularCore + + + + + + \ No newline at end of file diff --git a/wxModularHost/Win/wxModularHost.vcxproj.filters b/wxModularHost/Win/wxModularHost.vcxproj.filters new file mode 100644 index 0000000..d993b28 --- /dev/null +++ b/wxModularHost/Win/wxModularHost.vcxproj.filters @@ -0,0 +1,59 @@ + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Precompile Header File + + + Precompile Header File + + + + + + + + Source Files + + + + + {4951AEE1-75F2-3C38-B593-C6BC8C809FB0} + + + {112D4227-BB51-3360-BB5B-5EA5DBA5A65F} + + + {05450AB2-CB7B-34EA-83DF-1EE86424EDF0} + + + diff --git a/wxModularHost/wxModularHost.pjd b/wxModularHost/wxModularHost.pjd index 8637447..e27afad 100644 --- a/wxModularHost/wxModularHost.pjd +++ b/wxModularHost/wxModularHost.pjd @@ -1,587 +1,587 @@ - - -
- 0 - "" - "" - "" - "" - "" - 0 - 0 - 0 - 1 - 1 - 1 - 1 - 0 - "Volodymyr (T-Rex) Triapichko" - "Volodymyr (T-Rex) Triapichko, 2013" - "" - 0 - 1 - 0 - 0 - "<All platforms>" - "2.9.5" - "Standard" - "///////////////////////////////////////////////////////////////////////////// -// Name: %HEADER-FILENAME% -// Purpose: -// Author: %AUTHOR% -// Modified by: -// Created: %DATE% -// RCS-ID: -// Copyright: %COPYRIGHT% -// Licence: -///////////////////////////////////////////////////////////////////////////// - -" - "///////////////////////////////////////////////////////////////////////////// -// Name: %SOURCE-FILENAME% -// Purpose: -// Author: %AUTHOR% -// Modified by: -// Created: %DATE% -// RCS-ID: -// Copyright: %COPYRIGHT% -// Licence: -///////////////////////////////////////////////////////////////////////////// - -" - "///////////////////////////////////////////////////////////////////////////// -// Name: %SYMBOLS-FILENAME% -// Purpose: Symbols file -// Author: %AUTHOR% -// Modified by: -// Created: %DATE% -// RCS-ID: -// Copyright: %COPYRIGHT% -// Licence: -///////////////////////////////////////////////////////////////////////////// - -" - "" - "// For compilers that support precompilation, includes "wx/wx.h". -#include "wx/wxprec.h" - -#ifdef __BORLANDC__ -#pragma hdrstop -#endif - -#ifndef WX_PRECOMP -#include "wx/wx.h" -#endif - -" - " /// %BODY% -" - " -/* - * %BODY% - */ - -" - "app_resources.h" - "app_resources.cpp" - "AppResources" - "app.h" - "app.cpp" - "Application" - 0 - "" - "<None>" - "iso-8859-1" - "utf-8" - "utf-8" - "" - 0 - 0 - 4 - " " - "" - 0 - 0 - 1 - 0 - 1 - 1 - 0 - 1 - 0 - 0 -
- - - "" - "data-document" - "" - "" - 0 - 1 - 0 - 0 - - "Configurations" - "config-data-document" - "" - "" - 0 - 1 - 0 - 0 - "" - 1 - -8519680 - "" - "Debug" - "Unicode" - "Static" - "Modular" - "GUI" - "wxMSW" - "Default" - "Dynamic" - "Yes" - "No" - "Yes" - "No" - "No" - "Yes" - "Yes" - "Yes" - "Yes" - "Yes" - "builtin" - "Yes" - "%EXECUTABLE%" - "" - "%AUTO%" - "%AUTO%" - "%AUTO%" - "%AUTO%" - "%WXVERSION%" - "%AUTO%" - "%AUTO%" - "%AUTO%" - "%AUTO%" - "%AUTO%" - "" - "%AUTO%" - "%AUTO%" - "%AUTO%" - "%AUTO%" - "%AUTO%" - "%AUTO%" - "%AUTO%" - "%AUTO%" - "%AUTO%" - "%AUTO%" - "%AUTO%" - "%AUTO%" - "%AUTO%" - "%AUTO%" - "%AUTO%" - "%AUTO%" - "%AUTO%" - "%AUTO%" - "%AUTO%" - "%AUTO%" - "%AUTO%" - "%AUTO%" - 0 - 1 - - - - - - - "Projects" - "root-document" - "" - "project" - 1 - 1 - 0 - 1 - - "Windows" - "html-document" - "" - "dialogsfolder" - 1 - 1 - 0 - 1 - - "wxModularHostApp" - "dialog-document" - "" - "app" - 0 - 1 - 0 - 0 - "wbAppProxy" - 10000 - 0 - "" - 0 - "" - "Standard" - 0 - 0 - "m_PluginManager|SampleModularCore *|PluginManager|new SampleModularCore|0|0|" - "wxModularHostApp" - "wxApp" - "wxModularHostApp.cpp" - "wxModularHostApp.h" - "" - "ID_MAINFRAME" - "" - - - "MainFrame" - "dialog-document" - "" - "frame" - 0 - 1 - 0 - 0 - "wbFrameProxy" - 10000 - 0 - "" - 0 - "" - "Standard" - 0 - 0 - "ID_MAINFRAME" - 10000 - "MainFrame" - "wxFrame" - "MainFrame.cpp" - "MainFrame.h" - "" - "MainFrame" - 1 - "" - 0 - "" - "" - "" - "" - "" - 0 - 1 - "<Any platform>" - "" - "" - "" - "" - "" - 0 - 1 - 1 - 1 - 0 - 0 - 0 - 0 - 1 - 0 - 0 - 0 - 0 - 0 - 0 - 0 - 0 - 0 - 0 - 0 - 0 - 0 - 0 - 0 - 0 - 0 - 0 - 0 - "" - 0 - 0 - -1 - -1 - 600 - 450 - 1 - "" - - "wxStatusBar: ID_STATUSBAR" - "dialog-control-document" - "" - "statusbar" - 0 - 1 - 0 - 0 - "wbStatusBarProxy" - "ID_STATUSBAR" - 10001 - "" - "wxStatusBar" - "wxStatusBar" - 1 - 0 - "" - "" - "" - "" - "" - "" - "" - "" - 0 - 1 - "<Any platform>" - 2 - "" - "" - 1 - 1 - 0 - 0 - 0 - 0 - 0 - 0 - 0 - 0 - 0 - 0 - "" - -1 - -1 - -1 - -1 - "Centre" - "Centre" - 0 - 5 - 1 - 1 - 1 - 1 - 0 - 0 - 0 - 0 - "" - "" - "" - - - "wxMenuBar: ID_MENUBAR" - "dialog-control-document" - "" - "menubar" - 0 - 1 - 0 - 0 - "wbMenuBarProxy" - "ID_MENUBAR" - "<Any platform>" - 10002 - - "File" - "dialog-control-document" - "" - "menu" - 0 - 1 - 0 - 0 - "wbMenuProxy" - "File" - 1 - "" - "<Any platform>" - - "Exit\tAlt+F4: wxID_EXIT" - "dialog-control-document" - "" - "menuitem" - 0 - 1 - 0 - 0 - "wbMenuItemProxy" - "wxID_EXIT" - 5006 - "Exit\tAlt+F4" - "Normal" - 0 - 1 - "" - "" - "" - "<Any platform>" - - - - - "wxAuiNotebook: ID_AUINOTEBOOK" - "dialog-control-document" - "" - "notebook" - 0 - 1 - 0 - 0 - "wbAuiNotebookProxy" - "ID_AUINOTEBOOK" - 10003 - "" - "wxAuiNotebook" - "wxAuiNotebook" - 1 - 0 - "" - "" - "m_Notebook" - "" - "" - "" - "" - "" - 0 - 1 - "<Any platform>" - "" - "" - "" - "" - "" - "" - "" - 1 - 1 - 0 - 0 - 0 - 0 - 0 - 0 - 0 - 0 - 0 - 0 - 1 - 0 - 0 - 0 - 0 - 0 - 0 - 0 - 0 - 0 - 0 - 0 - "" - -1 - -1 - -1 - -1 - "Centre" - "Centre" - 0 - 5 - 1 - 1 - 1 - 1 - 0 - 0 - 0 - 0 - "" - "" - 1 - "Pane1" - "" - 0 - "Centre" - 0 - 0 - 0 - 0 - 1 - 1 - 1 - 1 - 0 - 0 - 1 - 0 - 0 - 1 - 1 - 0 - 0 - 0 - 0 - -1 - -1 - -1 - -1 - -1 - -1 - -1 - -1 - -1 - -1 - - - - - "Sources" - "html-document" - "" - "sourcesfolder" - 1 - 1 - 0 - 1 - - "wxModularHost.rc" - "source-editor-document" - "wxModularHost.rc" - "source-editor" - 0 - 0 - 1 - 0 - "2/8/2013" - "" - - - - "Images" - "html-document" - "" - "bitmapsfolder" - 1 - 1 - 0 - 1 - - - - -
+ + +
+ 0 + "" + "" + "" + "" + "" + 0 + 0 + 0 + 1 + 1 + 1 + 1 + 0 + "Volodymyr (T-Rex) Triapichko" + "Volodymyr (T-Rex) Triapichko, 2013" + "" + 0 + 1 + 0 + 0 + "<All platforms>" + "2.9.5" + "Standard" + "///////////////////////////////////////////////////////////////////////////// +// Name: %HEADER-FILENAME% +// Purpose: +// Author: %AUTHOR% +// Modified by: +// Created: %DATE% +// RCS-ID: +// Copyright: %COPYRIGHT% +// Licence: +///////////////////////////////////////////////////////////////////////////// + +" + "///////////////////////////////////////////////////////////////////////////// +// Name: %SOURCE-FILENAME% +// Purpose: +// Author: %AUTHOR% +// Modified by: +// Created: %DATE% +// RCS-ID: +// Copyright: %COPYRIGHT% +// Licence: +///////////////////////////////////////////////////////////////////////////// + +" + "///////////////////////////////////////////////////////////////////////////// +// Name: %SYMBOLS-FILENAME% +// Purpose: Symbols file +// Author: %AUTHOR% +// Modified by: +// Created: %DATE% +// RCS-ID: +// Copyright: %COPYRIGHT% +// Licence: +///////////////////////////////////////////////////////////////////////////// + +" + "" + "// For compilers that support precompilation, includes "wx/wx.h". +#include "wx/wxprec.h" + +#ifdef __BORLANDC__ +#pragma hdrstop +#endif + +#ifndef WX_PRECOMP +#include "wx/wx.h" +#endif + +" + " /// %BODY% +" + " +/* + * %BODY% + */ + +" + "app_resources.h" + "app_resources.cpp" + "AppResources" + "app.h" + "app.cpp" + "Application" + 0 + "" + "<None>" + "iso-8859-1" + "utf-8" + "utf-8" + "" + 0 + 0 + 4 + " " + "" + 0 + 0 + 1 + 0 + 1 + 1 + 0 + 1 + 0 + 0 +
+ + + "" + "data-document" + "" + "" + 0 + 1 + 0 + 0 + + "Configurations" + "config-data-document" + "" + "" + 0 + 1 + 0 + 0 + "" + 1 + -8519680 + "" + "Debug" + "Unicode" + "Static" + "Modular" + "GUI" + "wxMSW" + "Default" + "Dynamic" + "Yes" + "No" + "Yes" + "No" + "No" + "Yes" + "Yes" + "Yes" + "Yes" + "Yes" + "builtin" + "Yes" + "%EXECUTABLE%" + "" + "%AUTO%" + "%AUTO%" + "%AUTO%" + "%AUTO%" + "%WXVERSION%" + "%AUTO%" + "%AUTO%" + "%AUTO%" + "%AUTO%" + "%AUTO%" + "" + "%AUTO%" + "%AUTO%" + "%AUTO%" + "%AUTO%" + "%AUTO%" + "%AUTO%" + "%AUTO%" + "%AUTO%" + "%AUTO%" + "%AUTO%" + "%AUTO%" + "%AUTO%" + "%AUTO%" + "%AUTO%" + "%AUTO%" + "%AUTO%" + "%AUTO%" + "%AUTO%" + "%AUTO%" + "%AUTO%" + "%AUTO%" + "%AUTO%" + 0 + 1 + + + + + + + "Projects" + "root-document" + "" + "project" + 1 + 1 + 0 + 1 + + "Windows" + "html-document" + "" + "dialogsfolder" + 1 + 1 + 0 + 1 + + "wxModularHostApp" + "dialog-document" + "" + "app" + 0 + 1 + 0 + 0 + "wbAppProxy" + 10000 + 0 + "" + 0 + "" + "Standard" + 0 + 0 + "m_PluginManager|SampleModularCore *|PluginManager|new SampleModularCore|0|0|" + "wxModularHostApp" + "wxApp" + "wxModularHostApp.cpp" + "wxModularHostApp.h" + "" + "ID_MAINFRAME" + "" + + + "MainFrame" + "dialog-document" + "" + "frame" + 0 + 1 + 0 + 0 + "wbFrameProxy" + 10000 + 0 + "" + 0 + "" + "Standard" + 0 + 0 + "ID_MAINFRAME" + 10000 + "MainFrame" + "wxFrame" + "MainFrame.cpp" + "MainFrame.h" + "" + "MainFrame" + 1 + "" + 0 + "" + "" + "" + "" + "" + 0 + 1 + "<Any platform>" + "" + "" + "" + "" + "" + 0 + 1 + 1 + 1 + 0 + 0 + 0 + 0 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + "" + 0 + 0 + -1 + -1 + 600 + 450 + 1 + "" + + "wxStatusBar: ID_STATUSBAR" + "dialog-control-document" + "" + "statusbar" + 0 + 1 + 0 + 0 + "wbStatusBarProxy" + "ID_STATUSBAR" + 10001 + "" + "wxStatusBar" + "wxStatusBar" + 1 + 0 + "" + "" + "" + "" + "" + "" + "" + "" + 0 + 1 + "<Any platform>" + 2 + "" + "" + 1 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + "" + -1 + -1 + -1 + -1 + "Centre" + "Centre" + 0 + 5 + 1 + 1 + 1 + 1 + 0 + 0 + 0 + 0 + "" + "" + "" + + + "wxMenuBar: ID_MENUBAR" + "dialog-control-document" + "" + "menubar" + 0 + 1 + 0 + 0 + "wbMenuBarProxy" + "ID_MENUBAR" + "<Any platform>" + 10002 + + "File" + "dialog-control-document" + "" + "menu" + 0 + 1 + 0 + 0 + "wbMenuProxy" + "File" + 1 + "" + "<Any platform>" + + "Exit\tAlt+F4: wxID_EXIT" + "dialog-control-document" + "" + "menuitem" + 0 + 1 + 0 + 0 + "wbMenuItemProxy" + "wxID_EXIT" + 5006 + "Exit\tAlt+F4" + "Normal" + 0 + 1 + "" + "" + "" + "<Any platform>" + + + + + "wxAuiNotebook: ID_AUINOTEBOOK" + "dialog-control-document" + "" + "notebook" + 0 + 1 + 0 + 0 + "wbAuiNotebookProxy" + "ID_AUINOTEBOOK" + 10003 + "" + "wxAuiNotebook" + "wxAuiNotebook" + 1 + 0 + "" + "" + "m_Notebook" + "" + "" + "" + "" + "" + 0 + 1 + "<Any platform>" + "" + "" + "" + "" + "" + "" + "" + 1 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + "" + -1 + -1 + -1 + -1 + "Centre" + "Centre" + 0 + 5 + 1 + 1 + 1 + 1 + 0 + 0 + 0 + 0 + "" + "" + 1 + "Pane1" + "" + 0 + "Centre" + 0 + 0 + 0 + 0 + 1 + 1 + 1 + 1 + 0 + 0 + 1 + 0 + 0 + 1 + 1 + 0 + 0 + 0 + 0 + -1 + -1 + -1 + -1 + -1 + -1 + -1 + -1 + -1 + -1 + + + + + "Sources" + "html-document" + "" + "sourcesfolder" + 1 + 1 + 0 + 1 + + "wxModularHost.rc" + "source-editor-document" + "wxModularHost.rc" + "source-editor" + 0 + 0 + 1 + 0 + "2/8/2013" + "" + + + + "Images" + "html-document" + "" + "bitmapsfolder" + 1 + 1 + 0 + 1 + + + + +
diff --git a/wxModularHost/wxModularHost.rc b/wxModularHost/wxModularHost.rc old mode 100755 new mode 100644 index f63e693..b86c4e2 --- a/wxModularHost/wxModularHost.rc +++ b/wxModularHost/wxModularHost.rc @@ -1 +1 @@ -#include "wx/msw/wx.rc" +#include "wx/msw/wx.rc" diff --git a/wxModularHost/wxModularHostApp.cpp b/wxModularHost/wxModularHostApp.cpp index 3c6d9cf..fd648a9 100644 --- a/wxModularHost/wxModularHostApp.cpp +++ b/wxModularHost/wxModularHostApp.cpp @@ -1,138 +1,138 @@ -///////////////////////////////////////////////////////////////////////////// -// Name: wxModularHostApp.cpp -// Purpose: -// Author: Volodymyr (T-Rex) Triapichko -// Modified by: -// Created: 02/08/2013 21:14:33 -// RCS-ID: -// Copyright: Volodymyr (T-Rex) Triapichko, 2013 -// Licence: -///////////////////////////////////////////////////////////////////////////// - -// For compilers that support precompilation, includes "wx/wx.h". -#include "stdwx.h" - -#ifdef __BORLANDC__ -#pragma hdrstop -#endif - -#ifndef WX_PRECOMP -#include "wx/wx.h" -#endif - -////@begin includes -////@end includes - -#include "wxModularHostApp.h" -#include "SampleModularCore.h" - -////@begin XPM images -////@end XPM images - - -/* - * Application instance implementation - */ - -////@begin implement app -IMPLEMENT_APP( wxModularHostApp ) -////@end implement app - - -/* - * wxModularHostApp type definition - */ - -IMPLEMENT_CLASS( wxModularHostApp, wxApp ) - - -/* - * wxModularHostApp event table definition - */ - -BEGIN_EVENT_TABLE( wxModularHostApp, wxApp ) - -////@begin wxModularHostApp event table entries -////@end wxModularHostApp event table entries - -END_EVENT_TABLE() - - -/* - * Constructor for wxModularHostApp - */ - -wxModularHostApp::wxModularHostApp() -{ - Init(); -} - - -/* - * Member initialisation - */ - -void wxModularHostApp::Init() -{ -////@begin wxModularHostApp member initialisation - m_PluginManager = new SampleModularCore; -////@end wxModularHostApp member initialisation -} - -/* - * Initialisation for wxModularHostApp - */ - -bool wxModularHostApp::OnInit() -{ -#if wxUSE_XPM - wxImage::AddHandler(new wxXPMHandler); -#endif -#if wxUSE_LIBPNG - wxImage::AddHandler(new wxPNGHandler); -#endif -#if wxUSE_LIBJPEG - wxImage::AddHandler(new wxJPEGHandler); -#endif -#if wxUSE_GIF - wxImage::AddHandler(new wxGIFHandler); -#endif - TestNonGuiPlugins(); - - MainFrame* mainWindow = new MainFrame( NULL ); - mainWindow->Show(true); - - return true; -} - - -/* - * Cleanup for wxModularHostApp - */ - -int wxModularHostApp::OnExit() -{ - wxDELETE(m_PluginManager); -////@begin wxModularHostApp cleanup - return wxApp::OnExit(); -////@end wxModularHostApp cleanup -} - -void wxModularHostApp::TestNonGuiPlugins() -{ - if(m_PluginManager) - { - if(m_PluginManager->LoadAllPlugins(true)) - { - for(wxNonGuiPluginBaseList::Node * node = - m_PluginManager->GetNonGuiPlugins().GetFirst(); node; node = node->GetNext()) - { - wxNonGuiPluginBase * plugin = node->GetData(); - if(plugin) - { - wxLogDebug(wxT("Non-GUI plugin returns %i"), plugin->Work()); - } - } - } - } -} +///////////////////////////////////////////////////////////////////////////// +// Name: wxModularHostApp.cpp +// Purpose: +// Author: Volodymyr (T-Rex) Triapichko +// Modified by: +// Created: 02/08/2013 21:14:33 +// RCS-ID: +// Copyright: Volodymyr (T-Rex) Triapichko, 2013 +// Licence: +///////////////////////////////////////////////////////////////////////////// + +// For compilers that support precompilation, includes "wx/wx.h". +#include "stdwx.h" + +#ifdef __BORLANDC__ +#pragma hdrstop +#endif + +#ifndef WX_PRECOMP +#include "wx/wx.h" +#endif + +////@begin includes +////@end includes + +#include "wxModularHostApp.h" +#include "SampleModularCore.h" + +////@begin XPM images +////@end XPM images + + +/* + * Application instance implementation + */ + +////@begin implement app +IMPLEMENT_APP( wxModularHostApp ) +////@end implement app + + +/* + * wxModularHostApp type definition + */ + +IMPLEMENT_CLASS( wxModularHostApp, wxApp ) + + +/* + * wxModularHostApp event table definition + */ + +BEGIN_EVENT_TABLE( wxModularHostApp, wxApp ) + +////@begin wxModularHostApp event table entries +////@end wxModularHostApp event table entries + +END_EVENT_TABLE() + + +/* + * Constructor for wxModularHostApp + */ + +wxModularHostApp::wxModularHostApp() +{ + Init(); +} + + +/* + * Member initialisation + */ + +void wxModularHostApp::Init() +{ +////@begin wxModularHostApp member initialisation + m_PluginManager = new SampleModularCore; +////@end wxModularHostApp member initialisation +} + +/* + * Initialisation for wxModularHostApp + */ + +bool wxModularHostApp::OnInit() +{ +#if wxUSE_XPM + wxImage::AddHandler(new wxXPMHandler); +#endif +#if wxUSE_LIBPNG + wxImage::AddHandler(new wxPNGHandler); +#endif +#if wxUSE_LIBJPEG + wxImage::AddHandler(new wxJPEGHandler); +#endif +#if wxUSE_GIF + wxImage::AddHandler(new wxGIFHandler); +#endif + TestNonGuiPlugins(); + + MainFrame* mainWindow = new MainFrame( NULL ); + mainWindow->Show(true); + + return true; +} + + +/* + * Cleanup for wxModularHostApp + */ + +int wxModularHostApp::OnExit() +{ + wxDELETE(m_PluginManager); +////@begin wxModularHostApp cleanup + return wxApp::OnExit(); +////@end wxModularHostApp cleanup +} + +void wxModularHostApp::TestNonGuiPlugins() +{ + if(m_PluginManager) + { + if(m_PluginManager->LoadAllPlugins(true)) + { + for(wxNonGuiPluginBaseList::compatibility_iterator node = + m_PluginManager->GetNonGuiPlugins().GetFirst(); node; node = node->GetNext()) + { + wxNonGuiPluginBase * plugin = node->GetData(); + if(plugin) + { + wxLogDebug(wxT("Non-GUI plugin returns %i"), plugin->Work()); + } + } + } + } +} diff --git a/wxModularHost/wxModularHostApp.h b/wxModularHost/wxModularHostApp.h index ac05af6..1f02682 100644 --- a/wxModularHost/wxModularHostApp.h +++ b/wxModularHost/wxModularHostApp.h @@ -1,88 +1,88 @@ -///////////////////////////////////////////////////////////////////////////// -// Name: wxModularHostApp.h -// Purpose: -// Author: Volodymyr (T-Rex) Triapichko -// Modified by: -// Created: 02/08/2013 21:14:33 -// RCS-ID: -// Copyright: Volodymyr (T-Rex) Triapichko, 2013 -// Licence: -///////////////////////////////////////////////////////////////////////////// - -#ifndef _WXMODULARHOSTAPP_H_ -#define _WXMODULARHOSTAPP_H_ - - -/*! - * Includes - */ - -////@begin includes -#include "wx/image.h" -#include "MainFrame.h" -////@end includes - -/*! - * Forward declarations - */ - -////@begin forward declarations -////@end forward declarations -class SampleModularCore; - -/*! - * Control identifiers - */ - -////@begin control identifiers -////@end control identifiers - -/*! - * wxModularHostApp class declaration - */ - -class wxModularHostApp: public wxApp -{ - DECLARE_CLASS( wxModularHostApp ) - DECLARE_EVENT_TABLE() - -public: - /// Constructor - wxModularHostApp(); - - void Init(); - - /// Initialises the application - virtual bool OnInit(); - - void TestNonGuiPlugins(); - - /// Called on exit - virtual int OnExit(); - -////@begin wxModularHostApp event handler declarations - -////@end wxModularHostApp event handler declarations - -////@begin wxModularHostApp member function declarations - - SampleModularCore * GetPluginManager() const { return m_PluginManager ; } - void SetPluginManager(SampleModularCore * value) { m_PluginManager = value ; } - -////@end wxModularHostApp member function declarations - -////@begin wxModularHostApp member variables - SampleModularCore * m_PluginManager; -////@end wxModularHostApp member variables -}; - -/*! - * Application instance declaration - */ - -////@begin declare app -DECLARE_APP(wxModularHostApp) -////@end declare app - -#endif - // _WXMODULARHOSTAPP_H_ +///////////////////////////////////////////////////////////////////////////// +// Name: wxModularHostApp.h +// Purpose: +// Author: Volodymyr (T-Rex) Triapichko +// Modified by: +// Created: 02/08/2013 21:14:33 +// RCS-ID: +// Copyright: Volodymyr (T-Rex) Triapichko, 2013 +// Licence: +///////////////////////////////////////////////////////////////////////////// + +#ifndef _WXMODULARHOSTAPP_H_ +#define _WXMODULARHOSTAPP_H_ + + +/*! + * Includes + */ + +////@begin includes +#include "wx/image.h" +#include "MainFrame.h" +////@end includes + +/*! + * Forward declarations + */ + +////@begin forward declarations +////@end forward declarations +class SampleModularCore; + +/*! + * Control identifiers + */ + +////@begin control identifiers +////@end control identifiers + +/*! + * wxModularHostApp class declaration + */ + +class wxModularHostApp: public wxApp +{ + DECLARE_CLASS( wxModularHostApp ) + DECLARE_EVENT_TABLE() + +public: + /// Constructor + wxModularHostApp(); + + void Init(); + + /// Initialises the application + virtual bool OnInit(); + + void TestNonGuiPlugins(); + + /// Called on exit + virtual int OnExit(); + +////@begin wxModularHostApp event handler declarations + +////@end wxModularHostApp event handler declarations + +////@begin wxModularHostApp member function declarations + + SampleModularCore * GetPluginManager() const { return m_PluginManager ; } + void SetPluginManager(SampleModularCore * value) { m_PluginManager = value ; } + +////@end wxModularHostApp member function declarations + +////@begin wxModularHostApp member variables + SampleModularCore * m_PluginManager; +////@end wxModularHostApp member variables +}; + +/*! + * Application instance declaration + */ + +////@begin declare app +DECLARE_APP(wxModularHostApp) +////@end declare app + +#endif + // _WXMODULARHOSTAPP_H_ diff --git a/wxNonGuiPluginBase/CMakeLists.txt b/wxNonGuiPluginBase/CMakeLists.txt index 4bffebc..a917c4a 100644 --- a/wxNonGuiPluginBase/CMakeLists.txt +++ b/wxNonGuiPluginBase/CMakeLists.txt @@ -1,61 +1,27 @@ -set (SRCS - wxNonGuiPluginBase.cpp) -set (HEADERS - Declarations.h - wxNonGuiPluginBase.h) - -set(LIBRARY_NAME wxNonGuiPluginBase) - -if(WIN32) - # Only for Windows: - # we add additional preprocessor definitons - set(PREPROCESSOR_DEFINITIONS ${PREPROCESSOR_DEFINITIONS}; - /D_USRDLL;/DDEMO_PLUGIN_EXPORTS;/D__STDC_CONSTANT_MACROS) -endif(WIN32) - -# Add 2 files for precompiled headers -set(SRCS ${SRCS} ${HEADERS} - ${PROJECT_ROOT_DIR}/include/stdwx.h - ${PROJECT_ROOT_DIR}/include/stdwx.cpp) - -# Set preprocessor definitions -add_definitions(${PREPROCESSOR_DEFINITIONS}) -# Set include directories -include_directories(${INCLUDE_DIRECTORIES} ${BASE_INCLUDE_DIRECTORIES}) -# Set library search paths -link_directories(${LINK_DIRECTORIES}) -# Setup the project name and assign the source files for this project -add_library(${LIBRARY_NAME} SHARED ${SRCS}) - -#Setup the output folder -set(DLL_DIR bin) -set(TARGET_LOCATION ${PROJECT_SOURCE_DIR}/${DLL_DIR}${LIB_SUFFIX}) -if(APPLE) - set(TARGET_LOCATION - ${TARGET_LOCATION}/$(CONFIGURATION)/${PROJECT_NAME}.app/Contents/Frameworks) - set(CMAKE_INSTALL_PATH "@loader_path/../Frameworks") -endif(APPLE) -if(LINUX OR APPLE) - get_target_property(RESULT_FULL_PATH ${LIBRARY_NAME} LOCATION) - get_filename_component(RESULT_FILE_NAME ${RESULT_FULL_PATH} NAME) -endif(LINUX OR APPLE) -set_target_properties(${LIBRARY_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${TARGET_LOCATION}) - -# Set additional dependencies -target_link_libraries(${LIBRARY_NAME} ${wxWidgets_LIBRARIES}) - -# Setup precompiled headers -target_precompile_headers(${LIBRARY_NAME} PRIVATE ${PROJECT_ROOT_DIR}/include/stdwx.h ${PROJECT_ROOT_DIR}/include/stdwx.cpp) - -if(APPLE) - get_filename_component(ABSOLUTE_PATH "${RESULT_FULL_PATH}" ABSOLUTE) - add_custom_command(TARGET ${LIBRARY_NAME} POST_BUILD - COMMAND install_name_tool -change "${ABSOLUTE_PATH}" "@loader_path/../Frameworks/${RESULT_FILE_NAME}" $ - ) -endif(APPLE) - -if(LINUX OR APPLE) - add_custom_command(TARGET ${LIBRARY_NAME} POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy $ ${TARGET_LOCATION}/${RESULT_FILE_NAME} - ) -endif(LINUX OR APPLE) +set(SRCS + wxNonGuiPluginBase.cpp +) +set(HEADERS + Declarations.h + wxNonGuiPluginBase.h +) + +set(LIBRARY_NAME wxNonGuiPluginBase) + +if(WIN32) + set(PREPROCESSOR_DEFINITIONS ${PREPROCESSOR_DEFINITIONS}; + /D_USRDLL;/DDEMO_PLUGIN_EXPORTS;/D__STDC_CONSTANT_MACROS) +endif(WIN32) + +set(SRCS ${SRCS} ${HEADERS} + ${PROJECT_ROOT_DIR}/include/stdwx.h + ${PROJECT_ROOT_DIR}/include/stdwx.cpp) + +add_definitions(${PREPROCESSOR_DEFINITIONS}) +include_directories(${INCLUDE_DIRECTORIES} ${BASE_INCLUDE_DIRECTORIES}) + +add_library(${LIBRARY_NAME} SHARED ${SRCS}) + +target_link_libraries(${LIBRARY_NAME} ${wxWidgets_LIBRARIES}) + +target_precompile_headers(${LIBRARY_NAME} PRIVATE ${PROJECT_ROOT_DIR}/include/stdwx.h) \ No newline at end of file diff --git a/wxNonGuiPluginBase/Declarations.h b/wxNonGuiPluginBase/Declarations.h index 13dd0d3..fd08279 100644 --- a/wxNonGuiPluginBase/Declarations.h +++ b/wxNonGuiPluginBase/Declarations.h @@ -1,14 +1,14 @@ -#ifndef _DECLARATIONS_H -#define _DECLARATIONS_H - -#if defined(__WXMSW__) -#ifdef DEMO_PLUGIN_EXPORTS -#define DEMO_API __declspec(dllexport) -#else -#define DEMO_API __declspec(dllimport) -#endif -#else -#define DEMO_API -#endif - -#endif // _DECLARATIONS_H +#ifndef _DECLARATIONS_H +#define _DECLARATIONS_H + +#if defined(__WXMSW__) +#ifdef DEMO_PLUGIN_EXPORTS +#define DEMO_API __declspec(dllexport) +#else +#define DEMO_API __declspec(dllimport) +#endif +#else +#define DEMO_API +#endif + +#endif // _DECLARATIONS_H diff --git a/wxNonGuiPluginBase/Win/wxNonGuiPluginBase.vcxproj b/wxNonGuiPluginBase/Win/wxNonGuiPluginBase.vcxproj new file mode 100644 index 0000000..32c4a2a --- /dev/null +++ b/wxNonGuiPluginBase/Win/wxNonGuiPluginBase.vcxproj @@ -0,0 +1,247 @@ + + + + x64 + + + + Debug + x64 + + + Release + x64 + + + + {0DC8318A-07F9-3AB6-8E65-2642EE273101} + Win32Proj + 10.0.26100.0 + x64 + wxNonGuiPluginBase + NoUpgrade + + + + DynamicLibrary + Unicode + v143 + + + DynamicLibrary + Unicode + v143 + + + + + + + + + + <_ProjectFileVersion>10.0.20506.1 + C:\Users\Admin\IFloor\projects\wxVideoProcessing\bin\ + wxNonGuiPluginBase.dir\Debug\ + wxNonGuiPluginBase + .dll + true + true + C:\Users\Admin\IFloor\projects\wxVideoProcessing\bin\ + wxNonGuiPluginBase.dir\Release\ + wxNonGuiPluginBase + .dll + false + true + + + + + C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\mswu;C:\Users\Admin\IFloor\libs\wxWidgets\include;C:\Users\Admin\IFloor\libs\opencv\build\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\Utils;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\MotionDetectorCore;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxJSON\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxxmlserializer\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxxmlserializer;%(AdditionalIncludeDirectories) + $(IntDir) + EnableFastChecks + + + ProgramDatabase + 4996 + Sync + + + Disabled + stdcpp17 + + true + Disabled + Use + C:/Users/Admin/IFloor/projects/wxVideoProcessing/wxNonGuiPluginBase/Win/CMakeFiles/wxNonGuiPluginBase.dir/Debug/cmake_pch.hxx + C:/Users/Admin/IFloor/projects/wxVideoProcessing/wxNonGuiPluginBase/Win/wxNonGuiPluginBase.dir/Debug//cmake_pch.pch + + MultiThreadedDebugDLL + true + + + false + %(PreprocessorDefinitions);WIN32;_WINDOWS;__WXDEBUG__=1;WIN32_LEAN_AND_MEAN;WXUSINGDLL;UNICODE;_UNICODE;_CRT_SECURE_NO_DEPRECATE;_USRDLL;DEMO_PLUGIN_EXPORTS;__STDC_CONSTANT_MACROS;CMAKE_INTDIR="Debug";wxNonGuiPluginBase_EXPORTS + $(IntDir) + false + + + %(PreprocessorDefinitions);WIN32;_DEBUG;_WINDOWS;__WXDEBUG__=1;WIN32_LEAN_AND_MEAN;WXUSINGDLL;UNICODE;_UNICODE;_CRT_SECURE_NO_DEPRECATE;_USRDLL;DEMO_PLUGIN_EXPORTS;__STDC_CONSTANT_MACROS;CMAKE_INTDIR=\"Debug\";wxNonGuiPluginBase_EXPORTS + C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\mswu;C:\Users\Admin\IFloor\libs\wxWidgets\include;C:\Users\Admin\IFloor\libs\opencv\build\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\Utils;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\MotionDetectorCore;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxJSON\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxxmlserializer\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxxmlserializer;%(AdditionalIncludeDirectories) + + + C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\mswu;C:\Users\Admin\IFloor\libs\wxWidgets\include;C:\Users\Admin\IFloor\libs\opencv\build\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\Utils;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\MotionDetectorCore;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxJSON\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxxmlserializer\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxxmlserializer;%(AdditionalIncludeDirectories) + $(ProjectDir)/$(IntDir) + %(Filename).h + %(Filename).tlb + %(Filename)_i.c + %(Filename)_p.c + + + C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxbase33ud.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33ud_core.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33ud_adv.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33ud_aui.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxbase33ud_net.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33ud_gl.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxbase33ud_xml.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33ud_propgrid.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33ud_html.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxpngd.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxtiffd.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxjpegd.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxzlibd.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxregexud.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxexpatd.lib;opengl32.lib;glu32.lib;winmm.lib;comctl32.lib;uuid.lib;oleacc.lib;uxtheme.lib;rpcrt4.lib;shlwapi.lib;version.lib;wsock32.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;comdlg32.lib;advapi32.lib + C:/Users/Admin/IFloor/projects/wxVideoProcessing/build/Win/lib;C:/Users/Admin/IFloor/projects/wxVideoProcessing/build/Win/lib/$(Configuration);%(AdditionalLibraryDirectories) + %(AdditionalOptions) /machine:x64 + + true + %(IgnoreSpecificDefaultLibraries) + + C:/Users/Admin/IFloor/projects/wxVideoProcessing/bin/lib/Debug/wxNonGuiPluginBase.lib + + C:/Users/Admin/IFloor/projects/wxVideoProcessing/bin/wxNonGuiPluginBase.pdb + + Console + + + false + + + + + C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\mswu;C:\Users\Admin\IFloor\libs\wxWidgets\include;C:\Users\Admin\IFloor\libs\opencv\build\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\Utils;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\MotionDetectorCore;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxJSON\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxxmlserializer\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxxmlserializer;%(AdditionalIncludeDirectories) + $(IntDir) + Default + + + 4996 + Sync + + + AnySuitable + stdcpp17 + + true + MaxSpeed + Use + C:/Users/Admin/IFloor/projects/wxVideoProcessing/wxNonGuiPluginBase/Win/CMakeFiles/wxNonGuiPluginBase.dir/Release/cmake_pch.hxx + C:/Users/Admin/IFloor/projects/wxVideoProcessing/wxNonGuiPluginBase/Win/wxNonGuiPluginBase.dir/Release//cmake_pch.pch + + MultiThreadedDLL + true + + + false + %(PreprocessorDefinitions);WIN32;_WINDOWS;NDEBUG;WIN32_LEAN_AND_MEAN;WXUSINGDLL;UNICODE;_UNICODE;_CRT_SECURE_NO_DEPRECATE;_USRDLL;DEMO_PLUGIN_EXPORTS;__STDC_CONSTANT_MACROS;CMAKE_INTDIR="Release";wxNonGuiPluginBase_EXPORTS + $(IntDir) + + + false + + + %(PreprocessorDefinitions);WIN32;_WINDOWS;NDEBUG;WIN32_LEAN_AND_MEAN;WXUSINGDLL;UNICODE;_UNICODE;_CRT_SECURE_NO_DEPRECATE;_USRDLL;DEMO_PLUGIN_EXPORTS;__STDC_CONSTANT_MACROS;CMAKE_INTDIR=\"Release\";wxNonGuiPluginBase_EXPORTS + C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\mswu;C:\Users\Admin\IFloor\libs\wxWidgets\include;C:\Users\Admin\IFloor\libs\opencv\build\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\Utils;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\MotionDetectorCore;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxJSON\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxxmlserializer\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxxmlserializer;%(AdditionalIncludeDirectories) + + + C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\mswu;C:\Users\Admin\IFloor\libs\wxWidgets\include;C:\Users\Admin\IFloor\libs\opencv\build\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\Utils;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\MotionDetectorCore;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxJSON\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxxmlserializer\include;C:\Users\Admin\IFloor\projects\wxVideoProcessing\build\..\ThirdParty\wxxmlserializer;%(AdditionalIncludeDirectories) + $(ProjectDir)/$(IntDir) + %(Filename).h + %(Filename).tlb + %(Filename)_i.c + %(Filename)_p.c + + + C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxbase33u.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33u_core.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33u_adv.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33u_aui.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxbase33u_net.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33u_gl.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxbase33u_xml.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33u_propgrid.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxmsw33u_html.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxpng.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxtiff.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxjpeg.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxzlib.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxregexu.lib;C:\Users\Admin\IFloor\libs\wxWidgets\lib\vc_x64_dll\wxexpat.lib;opengl32.lib;glu32.lib;winmm.lib;comctl32.lib;uuid.lib;oleacc.lib;uxtheme.lib;rpcrt4.lib;shlwapi.lib;version.lib;wsock32.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;comdlg32.lib;advapi32.lib + C:/Users/Admin/IFloor/projects/wxVideoProcessing/build/Win/lib;C:/Users/Admin/IFloor/projects/wxVideoProcessing/build/Win/lib/$(Configuration);%(AdditionalLibraryDirectories) + %(AdditionalOptions) /machine:x64 + + false + %(IgnoreSpecificDefaultLibraries) + + C:/Users/Admin/IFloor/projects/wxVideoProcessing/bin/lib/Release/wxNonGuiPluginBase.lib + + C:/Users/Admin/IFloor/projects/wxVideoProcessing/bin/wxNonGuiPluginBase.pdb + + Console + + + false + + + + + Always + Building Custom Rule C:/Users/Admin/IFloor/projects/wxVideoProcessing/wxNonGuiPluginBase/CMakeLists.txt + setlocal +"C:\Program Files\CMake\bin\cmake.exe" -SC:/Users/Admin/IFloor/projects/wxVideoProcessing/build -BC:/Users/Admin/IFloor/projects/wxVideoProcessing/build/Win --check-stamp-file C:/Users/Admin/IFloor/projects/wxVideoProcessing/wxNonGuiPluginBase/Win/CMakeFiles/generate.stamp +if %errorlevel% neq 0 goto :cmEnd +:cmEnd +endlocal & call :cmErrorLevel %errorlevel% & goto :cmDone +:cmErrorLevel +exit /b %1 +:cmDone +if %errorlevel% neq 0 goto :VCEnd + %(AdditionalInputs) + C:\Users\Admin\IFloor\projects\wxVideoProcessing\wxNonGuiPluginBase\Win\CMakeFiles\generate.stamp + false + Building Custom Rule C:/Users/Admin/IFloor/projects/wxVideoProcessing/wxNonGuiPluginBase/CMakeLists.txt + setlocal +"C:\Program Files\CMake\bin\cmake.exe" -SC:/Users/Admin/IFloor/projects/wxVideoProcessing/build -BC:/Users/Admin/IFloor/projects/wxVideoProcessing/build/Win --check-stamp-file C:/Users/Admin/IFloor/projects/wxVideoProcessing/wxNonGuiPluginBase/Win/CMakeFiles/generate.stamp +if %errorlevel% neq 0 goto :cmEnd +:cmEnd +endlocal & call :cmErrorLevel %errorlevel% & goto :cmDone +:cmErrorLevel +exit /b %1 +:cmDone +if %errorlevel% neq 0 goto :VCEnd + %(AdditionalInputs) + C:\Users\Admin\IFloor\projects\wxVideoProcessing\wxNonGuiPluginBase\Win\CMakeFiles\generate.stamp + false + + + + + C:/Users/Admin/IFloor/projects/wxVideoProcessing/wxNonGuiPluginBase/Win/CMakeFiles/wxNonGuiPluginBase.dir/Debug/cmake_pch.hxx;%(ForcedIncludeFiles) + Create + C:/Users/Admin/IFloor/projects/wxVideoProcessing/wxNonGuiPluginBase/Win/CMakeFiles/wxNonGuiPluginBase.dir/Debug/cmake_pch.hxx + C:/Users/Admin/IFloor/projects/wxVideoProcessing/wxNonGuiPluginBase/Win/wxNonGuiPluginBase.dir/Debug//cmake_pch.pch + C:/Users/Admin/IFloor/projects/wxVideoProcessing/wxNonGuiPluginBase/Win/CMakeFiles/wxNonGuiPluginBase.dir/Release/cmake_pch.hxx;%(ForcedIncludeFiles) + Create + C:/Users/Admin/IFloor/projects/wxVideoProcessing/wxNonGuiPluginBase/Win/CMakeFiles/wxNonGuiPluginBase.dir/Release/cmake_pch.hxx + C:/Users/Admin/IFloor/projects/wxVideoProcessing/wxNonGuiPluginBase/Win/wxNonGuiPluginBase.dir/Release//cmake_pch.pch + + + C:/Users/Admin/IFloor/projects/wxVideoProcessing/wxNonGuiPluginBase/Win/CMakeFiles/wxNonGuiPluginBase.dir/Debug/cmake_pch.hxx;%(ForcedIncludeFiles) + C:/Users/Admin/IFloor/projects/wxVideoProcessing/wxNonGuiPluginBase/Win/CMakeFiles/wxNonGuiPluginBase.dir/Release/cmake_pch.hxx;%(ForcedIncludeFiles) + + + + + + C:/Users/Admin/IFloor/projects/wxVideoProcessing/wxNonGuiPluginBase/Win/CMakeFiles/wxNonGuiPluginBase.dir/Debug/cmake_pch.hxx;%(ForcedIncludeFiles) + C:/Users/Admin/IFloor/projects/wxVideoProcessing/wxNonGuiPluginBase/Win/CMakeFiles/wxNonGuiPluginBase.dir/Release/cmake_pch.hxx;%(ForcedIncludeFiles) + + + + + + + + {6F4BDD20-5B99-3029-BDF0-7D719B283AF5} + ZERO_CHECK + false + Never + + + + + + \ No newline at end of file diff --git a/wxNonGuiPluginBase/Win/wxNonGuiPluginBase.vcxproj.filters b/wxNonGuiPluginBase/Win/wxNonGuiPluginBase.vcxproj.filters new file mode 100644 index 0000000..f2c2fab --- /dev/null +++ b/wxNonGuiPluginBase/Win/wxNonGuiPluginBase.vcxproj.filters @@ -0,0 +1,45 @@ + + + + + Source Files + + + Source Files + + + Source Files + + + + + Header Files + + + Header Files + + + Header Files + + + Precompile Header File + + + Precompile Header File + + + + + + + + {4951AEE1-75F2-3C38-B593-C6BC8C809FB0} + + + {112D4227-BB51-3360-BB5B-5EA5DBA5A65F} + + + {05450AB2-CB7B-34EA-83DF-1EE86424EDF0} + + + diff --git a/wxNonGuiPluginBase/wxNonGuiPluginBase.cpp b/wxNonGuiPluginBase/wxNonGuiPluginBase.cpp index 78a26ed..18a9db8 100644 --- a/wxNonGuiPluginBase/wxNonGuiPluginBase.cpp +++ b/wxNonGuiPluginBase/wxNonGuiPluginBase.cpp @@ -1,12 +1,12 @@ -#include "stdwx.h" -#include "wxNonGuiPluginBase.h" - -IMPLEMENT_ABSTRACT_CLASS(wxNonGuiPluginBase, wxObject) - -wxNonGuiPluginBase::wxNonGuiPluginBase() -{ -} - -wxNonGuiPluginBase::~wxNonGuiPluginBase() -{ -} +#include "stdwx.h" +#include "wxNonGuiPluginBase.h" + +IMPLEMENT_ABSTRACT_CLASS(wxNonGuiPluginBase, wxObject) + +wxNonGuiPluginBase::wxNonGuiPluginBase() +{ +} + +wxNonGuiPluginBase::~wxNonGuiPluginBase() +{ +} diff --git a/wxNonGuiPluginBase/wxNonGuiPluginBase.h b/wxNonGuiPluginBase/wxNonGuiPluginBase.h index 5d31011..123f12e 100644 --- a/wxNonGuiPluginBase/wxNonGuiPluginBase.h +++ b/wxNonGuiPluginBase/wxNonGuiPluginBase.h @@ -1,16 +1,16 @@ -#pragma once - -#include "Declarations.h" - -class DEMO_API wxNonGuiPluginBase : public wxObject -{ - DECLARE_ABSTRACT_CLASS(wxNonGuiPluginBase) -public: - wxNonGuiPluginBase(); - virtual ~wxNonGuiPluginBase(); - - virtual int Work() = 0; -}; - -typedef wxNonGuiPluginBase * (*CreatePlugin_function)(); +#pragma once + +#include "Declarations.h" + +class DEMO_API wxNonGuiPluginBase : public wxObject +{ + DECLARE_ABSTRACT_CLASS(wxNonGuiPluginBase) +public: + wxNonGuiPluginBase(); + virtual ~wxNonGuiPluginBase(); + + virtual int Work() = 0; +}; + +typedef wxNonGuiPluginBase * (*CreatePlugin_function)(); typedef void (*DeletePlugin_function)(wxNonGuiPluginBase * plugin); \ No newline at end of file

nPE(7(m^HlF_Ic@{Z|av^%D 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�!|?){KE}eeEEu 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 literal 0 HcmV?d00001 diff --git a/bin/MotionDetectorMultiCam.dll b/bin/MotionDetectorMultiCam.dll new file mode 100644 index 0000000000000000000000000000000000000000..d3c26796db8dd69c6e1b367cc6b4879af7738e0e GIT binary patch literal 294912 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≶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 literal 0 HcmV?d00001 diff --git a/bin/MotionDetectorMultiCamGui.dll b/bin/MotionDetectorMultiCamGui.dll new file mode 100644 index 0000000000000000000000000000000000000000..ae7fc182ca9216df53db4662fc55db810a74b656 GIT binary patch literal 305152 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