diff --git a/.github/workflows/Ubuntu_CI.yml b/.github/workflows/Ubuntu_CI.yml
index 931b5ff6c3..f1fedc4a90 100644
--- a/.github/workflows/Ubuntu_CI.yml
+++ b/.github/workflows/Ubuntu_CI.yml
@@ -60,7 +60,7 @@ jobs:
with:
submodules: 'true'
- name: Install dependencies
- run: sudo apt-get update && sudo apt-get install g++ gcc build-essential libgtk-3-dev libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev freeglut3-dev libavcodec-dev libavformat-dev libswscale-dev libsdl2-dev libswresample-dev libavutil-dev libavresample-dev libportmidi-dev libzstd-dev libcurl4-openssl-dev libltc-dev liblua5.3-dev wget git cbp2make
+ run: sudo apt-get update && sudo apt-get install g++ gcc build-essential libgtk-3-dev libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev freeglut3-dev libavcodec-dev libavformat-dev libswscale-dev libsdl2-dev libswresample-dev libavutil-dev libavresample-dev libportmidi-dev libzstd-dev libwebp-dev libcurl4-openssl-dev libltc-dev liblua5.3-dev wget git cbp2make
- name: Make
run: make -j 2
diff --git a/.github/workflows/Ubuntu_Window_Release.yml b/.github/workflows/Ubuntu_Window_Release.yml
index 86b9172318..ae09bb10f7 100644
--- a/.github/workflows/Ubuntu_Window_Release.yml
+++ b/.github/workflows/Ubuntu_Window_Release.yml
@@ -14,8 +14,8 @@ jobs:
- uses: actions/checkout@v2
with:
submodules: 'true'
- - run: docker pull debenham/xlights
- - run: docker run --name buildvm debenham/xlights /bin/bash Recipe.appimage
+ - run: docker pull ghcr.io/xlightssequencer/xlights-build-docker:master
+ - run: docker run --name buildvm ghcr.io/xlightssequencer/xlights-build-docker /bin/bash Recipe.appimage
- run: 'docker cp buildvm:/xLights/xLights/AppImage/ /tmp/'
- run: ls -lh /tmp/AppImage
diff --git a/.github/workflows/docker_manual.yml b/.github/workflows/docker_manual.yml
index 7e17696c72..8e92a4e17f 100644
--- a/.github/workflows/docker_manual.yml
+++ b/.github/workflows/docker_manual.yml
@@ -10,8 +10,8 @@ jobs:
- uses: actions/checkout@v2
with:
submodules: 'true'
- - run: docker pull debenham/xlights
- - run: docker run --name buildvm debenham/xlights /bin/bash Recipe.appimage
+ - run: docker pull ghcr.io/xlightssequencer/xlights-build-docker:master
+ - run: docker run --name buildvm ghcr.io/xlightssequencer/xlights-build-docker:master /bin/bash Recipe.appimage
- run: 'docker cp buildvm:/xLights/xLights/AppImage/ /tmp/'
- run: ls -lh /tmp/AppImage
diff --git a/.travis.yml b/.travis.yml
index 90269488d8..f805ea6774 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -6,8 +6,8 @@ services:
git:
depth: 3
script:
- - docker pull debenham/xlights
- - docker run --name buildvm debenham/xlights /bin/bash Recipe.appimage
+ - docker pull docker pull ghcr.io/xlightssequencer/xlights-build-docker:master
+ - docker run --name buildvm ghcr.io/xlightssequencer/xlights-build-docker /bin/bash Recipe.appimage
after_success:
- docker cp buildvm:/xLights/xLights/AppImage/ /tmp/
- ls -lh /tmp/*
diff --git a/README.linux b/README.linux
index d2ff670025..26c74c90d7 100644
--- a/README.linux
+++ b/README.linux
@@ -65,11 +65,12 @@ Instructions for other Linux distributions will vary.
libcurl4-openssl-dev
libltc-dev
liblua5.3-dev
+ libwebp-dev
cbp2make (optional but recommended if compiling from git)
Example command to install packages on Ubuntu
- sudo apt-get install g++ gcc build-essential libgtk-3-dev libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev freeglut3-dev libavcodec-dev libavformat-dev libswscale-dev libsdl2-dev libavutil-dev libportmidi-dev libzstd-dev libcurl4-openssl-dev libltc-dev liblua5.3-dev wget git cbp2make
+ sudo apt-get install g++ gcc build-essential libgtk-3-dev libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev freeglut3-dev libavcodec-dev libavformat-dev libswscale-dev libsdl2-dev libavutil-dev libportmidi-dev libzstd-dev libwebp-dev libcurl4-openssl-dev libltc-dev liblua5.3-dev wget git cbp2make
Example commands to install packages on Fedora 38
@@ -88,7 +89,7 @@ Instructions for other Linux distributions will vary.
Install other packages:
sudo dnf install https://download1.rpmfusion.org/free/fedora/rpmfusion-free-release-$(rpm -E %fedora).noarch.rpm https://download1.rpmfusion.org/nonfree/fedora/rpmfusion-nonfree-release-$(rpm -E %fedora).noarch.rpm
- sudo dnf install gcc-c++ gtk3-devel gstreamer1-devel gstreamer1-plugins-base-devel freeglut-devel gstreamer1-plugins-bad-free-devel ffmpeg-devel SDL2-devel portmidi-devel libzstd-devel curl-devel libltc-devel lua-devel
+ sudo dnf install gcc-c++ gtk3-devel gstreamer1-devel gstreamer1-plugins-base-devel freeglut-devel gstreamer1-plugins-bad-free-devel ffmpeg-devel SDL2-devel portmidi-devel libzstd-devel libwebp-devel curl-devel libltc-devel lua-devel
Note: newer versions of libportmidi combined the .so files. Easiest workaround is to add a sym link to the combine .so file:
sudo ln -s /usr/lib64/libportmidi.so /usr/lib64/libporttime.so
diff --git a/README.txt b/README.txt
index 1a77469cf6..cc3c362e7e 100644
--- a/README.txt
+++ b/README.txt
@@ -11,6 +11,83 @@ Issue Tracker is found here: www.github.com/smeighan/xLights/issues
XLIGHTS/NUTCRACKER RELEASE NOTES
---------------------------------
+ -- bug (scott) Fix Import Dialog Groups Blue turning white.
+2023.22 Dec 5, 2023
+ -- enh (claudio) Add virtual matrix to xSchedule using RGBW data
+ -- enh (lutz) Add palette shift buttons
+ -- enh (scott) Add cycles and scale to ripple effect
+ -- enh (scott) Add set smart remote and increment to visualiser
+ -- enh (scott) Add importing submodels as states
+ -- enh (scott) Add more controller connection property setting to the visualiser
+ -- enh (keith) Show progress in batch render including number left to render
+ -- enh (keith) Add model aliases used to improve automap and handling opening sequences with model names that have changed
+ -- enh (keith) Add an adjust effect - primarily for use on DMX effects to modify results without changing the underlying effects
+ -- enh (keith) Add support for non-zig zagging trees with multiple strands but one string
+ -- enh (dartunder) Add bulk edit to clear shadow models
+ -- bug (dartunder) Fix 3 point model dimensions incorrect
+ -- bug (keith) Fix Falcon V4 upload without full xlights control should not override port brightness
+ -- bug (keith) Fix layout model locking menu items inconsistently available
+ -- bug (keith) Fix Falcon V4/V5 reversed not uploading correctly
+ -- bug (keith) Fix click on import effect count column does not sort by effect count
+ -- bug (keith) Fix servo effect incompletely resets parameters
+ -- bug (keith) Fix Eseq playback on xSchedule
+ -- bug (keith) Fix default superstring to RGB handling
+ -- bug (scott) Fix FPP compatible audio file types incomplete
+ -- bug (scott) Fix missing bulk edit from some face effect properties
+ -- bug (dkulp) Fix 4GB+ uncompressed FSEQ files
+ -- bug (dkulp) Fix crash on player only controllers
+ -- bug (dkulp) Fix OSX font crash
+2023.21 Nov 25, 2023
+ -- enh (dkulp) Allow upload of config to inactive controllers
+ -- enh (scott) Add More Right Click Options to Multi-Controller Upload, Save/Load Selected
+ -- enh (scott) FPP Connect - Add Falcon V5 Support
+ -- enh (keith) Downgrade sketch effect image missing to warning and allow suppression of the warning in preferences.
+ -- enh (scott) FPP Connect - add Genius FSEQ upload
+ -- bug (dkulp) FPP Connect - fix crash with discovery/fpp connect if "Player Only" controller configured
+ -- bug (billnapier) Remove invisible unicode character in AlphxPix 4 settings
+ -- bug (dkulp) Fix Faces on models from Base show directory will always trigger the "dirty" flag
+ -- bug (keith) Fix copy on empty cell overwrites clipboard
+ -- bug (dkulp) Retry FSEQ upload on errors (up to three times)
+ -- bug (scott) HinkxPix won't allow assignment to smart receiver 3-9
+ -- bug (dartunder) Fix face blink effect
+ -- bug (keith) Fix eyes closed not mapping correctly
+ -- bug (keith) Fix PixLite config retrieval
+2023.20 Nov 15, 2023
+ -- enh (scott) FPP Connect - Remove ancient FPP 1.x upload via mounted USB
+ -- enh (scott) FPP Connect - Add ability to upload Proxy Controller IPs
+ -- enh (MrPierreB) Selectively Offset parameters on remap of dmx channels
+ -- enh (keith) Add a quick way to make a block of ports a dumb receiver
+ -- enh (kylegrymonprez) Adding ability to adjust the frequency of the "Auto" eye blink
+ -- enh (degracode ) Allow the Setup tab's Activate, Activate in xLights, Deactivate, and Unlink From Base menu items to be used when multiple controllers are selected
+ -- enh (keith) Add option to remove placeholders from submodels
+ -- enh (scott) Make ModelChainDialog growable
+ -- enh (dartunder) Show fpp connect results in status bar
+ -- bug (djulien) Allow Preview to be selected when showing 3D models
+ -- bug (dartunder) Fix delete warning for locked models, delete all models including locked after confirmation dialog.
+ -- bug (dkulp) FPP String upload: don't upload "DUMMY" entries
+ -- bug (keith) Show smart remotes as 0-15 on HinksPix
+ -- bug (dartunder) Fix undo for replace model command
+ -- bug (scott) Fix alphapix upload
+ -- bug (keith) Fix crash in Check Sequence in liquid effect
+2023.19 Nov 4, 2023
+ -- enh (keith) Add webp image support
+ -- enh (dartunder) Add warning and make it easier to see that a model had a shadow model in visualizer
+ -- bug (Daniel Hacquebord) Fix dark mode for shadow models in visualizer
+ -- bug (scott) Fix AlphaPix upload
+ -- bug (keith) Fix crash with liquid effect on large models
+ -- bug (scott) LUA Script: Select Sequences and deselect highlighted does nothing
+ -- bug (dkulp) FPP Connect: not uploading serial outputs from FPP Connect (worked fine from controller page)
+ -- bug (dkulp) FPP Connect: not able to upload to FPP instances with UI password set
+ -- bug (dkulp) Fix crash on MacOS with RotoZoom if GPU is busy
+ -- bug (dkulp) Workaround some other common crashes, but underlying cause of crash still under investigation
+2023.18 Oct 21, 2023
+ -- enh (dkulp) FPP Connect - query and media in parallel
+ -- bug (scott) Custom Model Export - Export Default Render Buffer as Submodel
+ -- bug (scott) Fix Submodel Dialog Sizing Issues
+ -- bug (dartunder) FPP upload - fix e1.31 priority not being set if universes are all the same size
+ -- bug (scott) HinksPix - fix folder and filter selector
+ -- bug (dkulp) FPP Connect - attempt to fix some FPP Connect issues by remove "Expect: Continue" headers
+ -- bug (robfallone) Fix OpenGL crash on startup with some mesh objects
2023.17 Oct 16, 2023
-- enh (dkulp) Add monitor attribute for controllers, FPP upload of co-universes will set priority/monitoring/deDup settings
-- enh (Kyle Grymonprez) Add Node Count to model tooltips
diff --git a/build_scripts/msw/xLights_common.iss b/build_scripts/msw/xLights_common.iss
index f098991508..7c19779a54 100644
--- a/build_scripts/msw/xLights_common.iss
+++ b/build_scripts/msw/xLights_common.iss
@@ -3,5 +3,5 @@
#define MyTitleName "xLights"
#define Year 2023
-#define Version 17
+#define Version 22
#define Other ""
diff --git a/controllers/fpp.xcontroller b/controllers/fpp.xcontroller
index 263b37ad82..dc4d756a59 100644
--- a/controllers/fpp.xcontroller
+++ b/controllers/fpp.xcontroller
@@ -229,6 +229,7 @@
24
1
+
ttyAMA0
diff --git a/controllers/holidaycoro.xcontroller b/controllers/holidaycoro.xcontroller
index 9601814792..541e69b108 100644
--- a/controllers/holidaycoro.xcontroller
+++ b/controllers/holidaycoro.xcontroller
@@ -10,7 +10,7 @@
4
1
512
- 2040
+ 2040
diff --git a/controllers/scott.xcontroller b/controllers/scott.xcontroller
index 09261cde55..d04f23d2f6 100644
--- a/controllers/scott.xcontroller
+++ b/controllers/scott.xcontroller
@@ -10,6 +10,13 @@
1
17,16
+
+ 16
+ 1ttyS1
+ ttyS1
+ 1
+ 17,-1
+
@@ -28,6 +35,14 @@
ttyS1
ttyS2
+
+ 20
+ 1ttyS1
+ ttyS1
+ 1
+ 17,4
+ 21,-1
+
@@ -87,4 +102,49 @@
ttyS2
+
+
+ 0
+ 8
+
+
+
+
+ 24
+ 0
+ 1
+ 17,4
+ 21,4
+ 25,-1
+
+
+
+
+ 24
+ 0
+ 1
+ 5,4
+ 9,4
+ 13,4
+ 17,4
+ 21,4
+ 25,-1
+
+
+
+
+ 16
+ 0
+ 1
+ 17,-1
+
+
+
+
+ 16
+ 0
+ 1
+ 17,-1
+
+
diff --git a/dependencies.txt b/dependencies.txt
index 7d1ef829a3..81bfa53017 100644
--- a/dependencies.txt
+++ b/dependencies.txt
@@ -179,7 +179,7 @@ Usage: read/write of v2 FSEQ files
Link: https://github.com/facebook/zstd
Source: https://github.com/facebook/zstd
Last Pulled:
-Version: [latest version available as at Jan 2021 is 1.4.8 released Dec 2020 ... I doubt this is the version we are using]
+Version: [latest version available as at Dec 2023 is 1.5.5]
License: BSD
Strategy: zstd.h header added to xLights repository, static libraries added to lib for OSX/Windows
diff --git a/include/adjust16.xpm b/include/adjust16.xpm
new file mode 100644
index 0000000000..b0b6aa8951
--- /dev/null
+++ b/include/adjust16.xpm
@@ -0,0 +1,21 @@
+/* XPM */
+static const char *adjust16_xpm[] = {
+"16 16 2 1",
+" c #FFFFFF",
+". c #000000",
+" ",
+" . ",
+".. .. ",
+" .. .. ",
+" .... ",
+" ... ",
+" .. ",
+" .. ",
+" .. ",
+" .. ",
+" .. ",
+" .. ",
+" .. ",
+" ",
+" ",
+" "};
diff --git a/include/adjust24.xpm b/include/adjust24.xpm
new file mode 100644
index 0000000000..36c26d5d95
--- /dev/null
+++ b/include/adjust24.xpm
@@ -0,0 +1,29 @@
+/* XPM */
+static const char *adjust24_xpm[] = {
+"24 24 2 1",
+" c #FFFFFF",
+". c #000000",
+" ",
+" ",
+" . . .. ",
+" . . .. ",
+" . . . . ",
+" .. . . . ",
+" . .. . . .. ",
+" . . . . ",
+" . . . . ",
+" .... . . . ",
+" . ... ",
+" . .. ",
+" . . . ",
+" . . . ",
+" . . . ",
+" . ... . ",
+" . . . . . ",
+" . . . . . . ",
+" . . . . . . ",
+" . . . . . ",
+" . . . .",
+" . . . ",
+" ",
+" "};
diff --git a/include/adjust32.xpm b/include/adjust32.xpm
new file mode 100644
index 0000000000..a0aeb8ae12
--- /dev/null
+++ b/include/adjust32.xpm
@@ -0,0 +1,37 @@
+/* XPM */
+static const char *adjust32_xpm[] = {
+"32 32 2 1",
+" c #FFFFFF",
+". c #000000",
+" ",
+" ",
+" .... .. ",
+" . .. .. ..",
+" . . .. . ",
+" . .. . .. ",
+" .. .. . . . ",
+" ... .. . . . ",
+" . .. . . .. .. ",
+" . .. . . . ",
+" . .. .. . ",
+" .. .. . . ",
+" ...... .. .. . ",
+" . .. . ",
+" . ... ",
+" .. .. ",
+" . . .. ",
+" . . .. ",
+" ... . .. ",
+" . . .. ",
+" . .... .. ",
+" . . . . .. ",
+" . . . . .. ",
+" . . .. .. . .. ",
+" . . .. .. . .. ",
+" . .. .. . .. ",
+" . .. .. . . ",
+" . .. . .",
+" . .. . . ",
+" ... ... ",
+" ",
+" "};
diff --git a/include/adjust48.xpm b/include/adjust48.xpm
new file mode 100644
index 0000000000..0dc912a633
--- /dev/null
+++ b/include/adjust48.xpm
@@ -0,0 +1,53 @@
+/* XPM */
+static const char *adjust48_xpm[] = {
+"48 48 2 1",
+" c #FFFFFF",
+". c #000000",
+" ",
+" ",
+" ",
+" ...... .. ",
+" ........ ..... ",
+" .. .. ... . ",
+" .. .. ... .. ",
+" .. .. .. . ",
+" . .. .. . .. ",
+" .. .. . .. . ",
+" ... .. . .. .. ",
+" . .. .. .. .. ... ",
+" . .. .. . .. .... ",
+" . .... . .. .. ",
+" .. .. .. .. .. ",
+" . .. .. .. ",
+" .. .. .. .. ",
+" ... .. .. .. ",
+" ......... .. .. .. ",
+" ........ .. .. .. ",
+" .. ... .. ",
+" .. .. .. ",
+" .. ... ",
+" .... .. ",
+" .. .. .. ",
+" . .. .. ",
+" . .. .. ",
+" ..... .. .. ",
+" .. . .. .. ",
+" .. .. .. ",
+" .. ...... .. ",
+" .. .. ..... .. .. ",
+" .. .. .. .. .. ",
+" .. .. .. .. .. ",
+" .. .. .. .. .. .. ",
+" .. .. .. .. .. .. ",
+" .. .. .. .. .. .. ",
+" . .. .. .. .. .. ",
+" . .. .. .. .. ",
+" . .. .. .. .. ",
+" . .. .. .. . ",
+" .. .. .. ..",
+" .. .. .. . ",
+" ........ ...... ",
+" ..... .... ",
+" ",
+" ",
+" "};
diff --git a/include/adjust64.xpm b/include/adjust64.xpm
new file mode 100644
index 0000000000..122bfa7e04
--- /dev/null
+++ b/include/adjust64.xpm
@@ -0,0 +1,69 @@
+/* XPM */
+static const char *adjust64_xpm[] = {
+"64 64 2 1",
+" c #FFFFFF",
+". c #000000",
+" ",
+" ",
+" ",
+" ",
+" ....... .. ",
+" .......... .... ",
+" ... .... .... .. ",
+" ... ... .... .. ",
+" ... ... .... ... ",
+" ... .. ... .. ",
+" ... .. .. ... ",
+" .. ... .. .. .. ",
+" ... ... .. .. .. ",
+" .... .. .. .. .. ",
+" ...... .. .. .. .. ",
+" .. ... .. .. .. ..... ",
+" .. ... .. .. .. ...... ",
+" .. ...... .. .. .... ",
+" .. .... ... .. ... ",
+" .. .. ... .. ... ",
+" .. ... .. ... ",
+" ... ... .. ... ",
+" ... ... .. ... ",
+" ... ... .. ... ",
+" ...... .... ... .. ... ",
+" ........... ... .. ... ",
+" ... ... ..... ... ",
+" ... .... ... ",
+" ... ... ... ",
+" ... ..... ",
+" .... ... ",
+" ...... ... ",
+" ... ... ... ",
+" .. ... ... ",
+" .. ... ... ",
+" . .. ... ... ",
+" ....... ... ... ",
+" ....... ... ... ",
+" ... ... ... ",
+" ... ... ... ",
+" ... .. ..... ... ",
+" ... . ....... ... ... ",
+" ... .. .. .. ... ... ",
+" ... ... .. ... ... ",
+" ... ... .. ... ... ",
+" ... ... .. .. ... ... ",
+" ... ... ... ... ... ... ",
+" ... ... ... ... ... ... ",
+" ... ... ... ... ... ... ",
+" .. ... ... ... ... ... ",
+" .. ... ... ... ... ... ",
+" .. ... ... ... ... ",
+" .. ... ... ... ... ",
+" .. ... ... ... .. ",
+" .. .. ... ... .. ",
+" .. ... ... .. ",
+" .. ... ... .. ",
+" .... .... ... ... ",
+" ......... ....... ",
+" ...... ..... ",
+" ",
+" ",
+" ",
+" "};
diff --git a/include/left_shift.xpm b/include/left_shift.xpm
new file mode 100644
index 0000000000..081469da1a
--- /dev/null
+++ b/include/left_shift.xpm
@@ -0,0 +1,45 @@
+/* XPM */
+static const char * left_shift_xpm[] = {
+"26 16 26 1",
+" c None",
+". c #A3A3A3",
+"+ c #C6C6C6",
+"@ c #C4C4C4",
+"# c #BBBBBB",
+"$ c #8A8A8A",
+"% c #393939",
+"& c #B7B7B7",
+"* c #696969",
+"= c #0E0E0E",
+"- c #0A0A0A",
+"; c #010101",
+"> c #C3C3C3",
+", c #B9B9B9",
+"' c #626262",
+") c #101010",
+"! c #000000",
+"~ c #B3B3B3",
+"{ c #262626",
+"] c #B4B4B4",
+"^ c #636363",
+"/ c #C5C5C5",
+"( c #B6B6B6",
+"_ c #0F0F0F",
+": c #878787",
+"< c #383838",
+"..........................",
+".++++++++++++++++++++++++.",
+".++++++++++++++++++++++++.",
+".++++++++++++++++++++++++.",
+".++++@#$%++++++++++++++++.",
+".+++&*=-;++++++++++++++++.",
+".>,')!!!!++++++++++++++++.",
+".~{!!!!!!!!!!!!!!!!!!!!!+.",
+".]{!!!!!!!!!!!!!!!!!!!!!+.",
+".+,^)!!!!++++++++++++++++.",
+".++/(*_-!++++++++++++++++.",
+".++++/#:<++++++++++++++++.",
+".++++++++++++++++++++++++.",
+".++++++++++++++++++++++++.",
+".++++++++++++++++++++++++.",
+".........................."};
diff --git a/include/reverse.xpm b/include/reverse.xpm
new file mode 100644
index 0000000000..e3fccf7657
--- /dev/null
+++ b/include/reverse.xpm
@@ -0,0 +1,84 @@
+/* XPM */
+static const char * reverse_xpm[] = {
+"26 16 65 1",
+" c None",
+". c #A3A3A3",
+"+ c #C6C6C6",
+"@ c #959595",
+"# c #7E7E7E",
+"$ c #C1C1C1",
+"% c #C3C3C3",
+"& c #8D8D8D",
+"* c #828282",
+"= c #808080",
+"- c #8A8A8A",
+"; c #9D9D9D",
+"> c #BCBCBC",
+", c #B5B5B5",
+"' c #484848",
+") c #171717",
+"! c #989898",
+"~ c #BABABA",
+"{ c #797979",
+"] c #343434",
+"^ c #070707",
+"/ c #000000",
+"( c #030303",
+"_ c #181818",
+": c #060606",
+"< c #010101",
+"[ c #3B3B3B",
+"} c #B6B6B6",
+"| c #BBBBBB",
+"1 c #626262",
+"2 c #0C0C0C",
+"3 c #1B1B1B",
+"4 c #383838",
+"5 c #434343",
+"6 c #454545",
+"7 c #3A3A3A",
+"8 c #1F1F1F",
+"9 c #050505",
+"0 c #6D6D6D",
+"a c #141414",
+"b c #313131",
+"c c #7C7C7C",
+"d c #B9B9B9",
+"e c #020202",
+"f c #898989",
+"g c #B2B2B2",
+"h c #3E3E3E",
+"i c #2D2D2D",
+"j c #979797",
+"k c #7A7A7A",
+"l c #929292",
+"m c #ADADAD",
+"n c #B1B1B1",
+"o c #BEBEBE",
+"p c #C2C2C2",
+"q c #B0B0B0",
+"r c #131313",
+"s c #0E0E0E",
+"t c #323232",
+"u c #3F3F3F",
+"v c #535353",
+"w c #5E5E5E",
+"x c #C5C5C5",
+"y c #080808",
+"z c #AAAAAA",
+"..........................",
+".+++++++++++++++++@#$++++.",
+".++++++++%.&*=-;>,')!++++.",
+".+++++~{]^//////(_:<[}+++.",
+".+++|12///345678////90$++.",
+".++*a//bcd+++++>6e///:f++.",
+".gh//ij++++++++!3/////(k%.",
+".+llmn,~op++++++++++++q-+.",
+".or/stu'vwj+++++++++xk2yz.",
+".%k(/////3!++++++++ji//hg.",
+".++f:///e6>+++++dcb//a*++.",
+".++$09////876543///21|+++.",
+".+++}[<:_(//////^]{~+++++.",
+".++++!)',>;-=*&.%++++++++.",
+".++++$#@+++++++++++++++++.",
+".........................."};
diff --git a/include/right_shift.xpm b/include/right_shift.xpm
new file mode 100644
index 0000000000..49c389f06b
--- /dev/null
+++ b/include/right_shift.xpm
@@ -0,0 +1,45 @@
+/* XPM */
+static const char * right_shift_xpm[] = {
+"26 16 26 1",
+" c None",
+". c #A3A3A3",
+"+ c #C6C6C6",
+"@ c #393939",
+"# c #8A8A8A",
+"$ c #BBBBBB",
+"% c #C4C4C4",
+"& c #010101",
+"* c #0A0A0A",
+"= c #0E0E0E",
+"- c #696969",
+"; c #B7B7B7",
+"> c #000000",
+", c #101010",
+"' c #626262",
+") c #B9B9B9",
+"! c #C3C3C3",
+"~ c #262626",
+"{ c #B3B3B3",
+"] c #B4B4B4",
+"^ c #636363",
+"/ c #0F0F0F",
+"( c #B6B6B6",
+"_ c #C5C5C5",
+": c #383838",
+"< c #878787",
+"..........................",
+".++++++++++++++++++++++++.",
+".++++++++++++++++++++++++.",
+".++++++++++++++++++++++++.",
+".++++++++++++++++@#$%++++.",
+".++++++++++++++++&*=-;+++.",
+".++++++++++++++++>>>>,')!.",
+".+>>>>>>>>>>>>>>>>>>>>>~{.",
+".+>>>>>>>>>>>>>>>>>>>>>~].",
+".++++++++++++++++>>>>,^)+.",
+".++++++++++++++++>*/-(_++.",
+".++++++++++++++++:<$_++++.",
+".++++++++++++++++++++++++.",
+".++++++++++++++++++++++++.",
+".++++++++++++++++++++++++.",
+".........................."};
diff --git a/include/switch.xpm b/include/switch.xpm
deleted file mode 100644
index 7deeebd941..0000000000
--- a/include/switch.xpm
+++ /dev/null
@@ -1,39 +0,0 @@
-/* XPM */
-static const char * switch_xpm[] = {
-"24 24 12 1",
-" c #EAEAEA",
-". c #507090",
-"+ c #A0A8C0",
-"@ c #F6F9F6",
-"# c #C0C8E0",
-"$ c #C1C6D5",
-"% c #F4F8F4",
-"& c #F3F8F3",
-"* c #AAB1C7",
-"= c #A5ACC3",
-"- c #F7F9F7",
-"; c #BBC1D1",
-" ",
-" ",
-" ...... ",
-" +.@#..#@.$ ",
-" +.%#. .#%.$ ",
-" .%@. ++&** ",
-" ++&== .&-.; ",
-" .&&. ==%** ",
-" ...@@... .@@. ",
-" #.@@@@.# .@@. ",
-" #.@@.# .... ",
-" #..# ",
-" #..# ",
-" .... #.@@.# ",
-" .@@. #.@@@@.# ",
-" .@@. ...@@... ",
-" **%== .&&. ",
-" ;.-&. ==&++ ",
-" **&++ .@%. ",
-" $.%#. .#%.+ ",
-" $.@#..#@.+ ",
-" ...... ",
-" ",
-" "};
diff --git a/include/zstd.h b/include/zstd.h
index 6c873544b4..e5c3f8b68b 100644
--- a/include/zstd.h
+++ b/include/zstd.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016-present, Yann Collet, Facebook, Inc.
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
* All rights reserved.
*
* This source code is licensed under both the BSD-style license (found in the
@@ -14,26 +14,61 @@ extern "C" {
#ifndef ZSTD_H_235446
#define ZSTD_H_235446
-/* ====== Dependency ======*/
+/* ====== Dependencies ======*/
+#include /* INT_MAX */
#include /* size_t */
/* ===== ZSTDLIB_API : control library symbols visibility ===== */
-#ifndef ZSTDLIB_VISIBILITY
-# if defined(__GNUC__) && (__GNUC__ >= 4)
-# define ZSTDLIB_VISIBILITY __attribute__ ((visibility ("default")))
+#ifndef ZSTDLIB_VISIBLE
+ /* Backwards compatibility with old macro name */
+# ifdef ZSTDLIB_VISIBILITY
+# define ZSTDLIB_VISIBLE ZSTDLIB_VISIBILITY
+# elif defined(__GNUC__) && (__GNUC__ >= 4) && !defined(__MINGW32__)
+# define ZSTDLIB_VISIBLE __attribute__ ((visibility ("default")))
# else
-# define ZSTDLIB_VISIBILITY
+# define ZSTDLIB_VISIBLE
# endif
#endif
+
+#ifndef ZSTDLIB_HIDDEN
+# if defined(__GNUC__) && (__GNUC__ >= 4) && !defined(__MINGW32__)
+# define ZSTDLIB_HIDDEN __attribute__ ((visibility ("hidden")))
+# else
+# define ZSTDLIB_HIDDEN
+# endif
+#endif
+
#if defined(ZSTD_DLL_EXPORT) && (ZSTD_DLL_EXPORT==1)
-# define ZSTDLIB_API __declspec(dllexport) ZSTDLIB_VISIBILITY
+# define ZSTDLIB_API __declspec(dllexport) ZSTDLIB_VISIBLE
#elif defined(ZSTD_DLL_IMPORT) && (ZSTD_DLL_IMPORT==1)
-# define ZSTDLIB_API __declspec(dllimport) ZSTDLIB_VISIBILITY /* It isn't required but allows to generate better code, saving a function pointer load from the IAT and an indirect jump.*/
+# define ZSTDLIB_API __declspec(dllimport) ZSTDLIB_VISIBLE /* It isn't required but allows to generate better code, saving a function pointer load from the IAT and an indirect jump.*/
#else
-# define ZSTDLIB_API ZSTDLIB_VISIBILITY
+# define ZSTDLIB_API ZSTDLIB_VISIBLE
#endif
+/* Deprecation warnings :
+ * Should these warnings be a problem, it is generally possible to disable them,
+ * typically with -Wno-deprecated-declarations for gcc or _CRT_SECURE_NO_WARNINGS in Visual.
+ * Otherwise, it's also possible to define ZSTD_DISABLE_DEPRECATE_WARNINGS.
+ */
+#ifdef ZSTD_DISABLE_DEPRECATE_WARNINGS
+# define ZSTD_DEPRECATED(message) /* disable deprecation warnings */
+#else
+# if defined (__cplusplus) && (__cplusplus >= 201402) /* C++14 or greater */
+# define ZSTD_DEPRECATED(message) [[deprecated(message)]]
+# elif (defined(GNUC) && (GNUC > 4 || (GNUC == 4 && GNUC_MINOR >= 5))) || defined(__clang__)
+# define ZSTD_DEPRECATED(message) __attribute__((deprecated(message)))
+# elif defined(__GNUC__) && (__GNUC__ >= 3)
+# define ZSTD_DEPRECATED(message) __attribute__((deprecated))
+# elif defined(_MSC_VER)
+# define ZSTD_DEPRECATED(message) __declspec(deprecated(message))
+# else
+# pragma message("WARNING: You need to implement ZSTD_DEPRECATED for this compiler")
+# define ZSTD_DEPRECATED(message)
+# endif
+#endif /* ZSTD_DISABLE_DEPRECATE_WARNINGS */
+
/*******************************************************************************
Introduction
@@ -70,31 +105,51 @@ extern "C" {
/*------ Version ------*/
#define ZSTD_VERSION_MAJOR 1
-#define ZSTD_VERSION_MINOR 3
-#define ZSTD_VERSION_RELEASE 8
-
+#define ZSTD_VERSION_MINOR 5
+#define ZSTD_VERSION_RELEASE 5
#define ZSTD_VERSION_NUMBER (ZSTD_VERSION_MAJOR *100*100 + ZSTD_VERSION_MINOR *100 + ZSTD_VERSION_RELEASE)
-ZSTDLIB_API unsigned ZSTD_versionNumber(void); /**< to check runtime library version */
+
+/*! ZSTD_versionNumber() :
+ * Return runtime library version, the value is (MAJOR*100*100 + MINOR*100 + RELEASE). */
+ZSTDLIB_API unsigned ZSTD_versionNumber(void);
#define ZSTD_LIB_VERSION ZSTD_VERSION_MAJOR.ZSTD_VERSION_MINOR.ZSTD_VERSION_RELEASE
#define ZSTD_QUOTE(str) #str
#define ZSTD_EXPAND_AND_QUOTE(str) ZSTD_QUOTE(str)
#define ZSTD_VERSION_STRING ZSTD_EXPAND_AND_QUOTE(ZSTD_LIB_VERSION)
-ZSTDLIB_API const char* ZSTD_versionString(void); /* requires v1.3.0+ */
-/***************************************
-* Default constant
-***************************************/
+/*! ZSTD_versionString() :
+ * Return runtime library version, like "1.4.5". Requires v1.3.0+. */
+ZSTDLIB_API const char* ZSTD_versionString(void);
+
+/* *************************************
+ * Default constant
+ ***************************************/
#ifndef ZSTD_CLEVEL_DEFAULT
# define ZSTD_CLEVEL_DEFAULT 3
#endif
+/* *************************************
+ * Constants
+ ***************************************/
+
+/* All magic numbers are supposed read/written to/from files/memory using little-endian convention */
+#define ZSTD_MAGICNUMBER 0xFD2FB528 /* valid since v0.8.0 */
+#define ZSTD_MAGIC_DICTIONARY 0xEC30A437 /* valid since v0.7.0 */
+#define ZSTD_MAGIC_SKIPPABLE_START 0x184D2A50 /* all 16 values, from 0x184D2A50 to 0x184D2A5F, signal the beginning of a skippable frame */
+#define ZSTD_MAGIC_SKIPPABLE_MASK 0xFFFFFFF0
+
+#define ZSTD_BLOCKSIZELOG_MAX 17
+#define ZSTD_BLOCKSIZE_MAX (1<= `ZSTD_compressBound(srcSize)`.
+ * NOTE: Providing `dstCapacity >= ZSTD_compressBound(srcSize)` guarantees that zstd will have
+ * enough space to successfully compress the data.
* @return : compressed size written into `dst` (<= `dstCapacity),
* or an error code if it fails (which can be tested using ZSTD_isError()). */
ZSTDLIB_API size_t ZSTD_compress( void* dst, size_t dstCapacity,
@@ -143,15 +198,49 @@ ZSTDLIB_API unsigned long long ZSTD_getFrameContentSize(const void *src, size_t
* "empty", "unknown" and "error" results to the same return value (0),
* while ZSTD_getFrameContentSize() gives them separate return values.
* @return : decompressed size of `src` frame content _if known and not empty_, 0 otherwise. */
-ZSTDLIB_API unsigned long long ZSTD_getDecompressedSize(const void* src, size_t srcSize);
+ZSTD_DEPRECATED("Replaced by ZSTD_getFrameContentSize")
+ZSTDLIB_API
+unsigned long long ZSTD_getDecompressedSize(const void* src, size_t srcSize);
+
+/*! ZSTD_findFrameCompressedSize() : Requires v1.4.0+
+ * `src` should point to the start of a ZSTD frame or skippable frame.
+ * `srcSize` must be >= first frame size
+ * @return : the compressed size of the first frame starting at `src`,
+ * suitable to pass as `srcSize` to `ZSTD_decompress` or similar,
+ * or an error code if input is invalid */
+ZSTDLIB_API size_t ZSTD_findFrameCompressedSize(const void* src, size_t srcSize);
/*====== Helper functions ======*/
-#define ZSTD_COMPRESSBOUND(srcSize) ((srcSize) + ((srcSize)>>8) + (((srcSize) < (128<<10)) ? (((128<<10) - (srcSize)) >> 11) /* margin, from 64 to 0 */ : 0)) /* this formula ensures that bound(A) + bound(B) <= bound(A+B) as long as A and B >= 128 KB */
-ZSTDLIB_API size_t ZSTD_compressBound(size_t srcSize); /*!< maximum compressed size in worst case single-pass scenario */
+/* ZSTD_compressBound() :
+ * maximum compressed size in worst case single-pass scenario.
+ * When invoking `ZSTD_compress()` or any other one-pass compression function,
+ * it's recommended to provide @dstCapacity >= ZSTD_compressBound(srcSize)
+ * as it eliminates one potential failure scenario,
+ * aka not enough room in dst buffer to write the compressed frame.
+ * Note : ZSTD_compressBound() itself can fail, if @srcSize > ZSTD_MAX_INPUT_SIZE .
+ * In which case, ZSTD_compressBound() will return an error code
+ * which can be tested using ZSTD_isError().
+ *
+ * ZSTD_COMPRESSBOUND() :
+ * same as ZSTD_compressBound(), but as a macro.
+ * It can be used to produce constants, which can be useful for static allocation,
+ * for example to size a static array on stack.
+ * Will produce constant value 0 if srcSize too large.
+ */
+#define ZSTD_MAX_INPUT_SIZE ((sizeof(size_t)==8) ? 0xFF00FF00FF00FF00LLU : 0xFF00FF00U)
+#define ZSTD_COMPRESSBOUND(srcSize) (((size_t)(srcSize) >= ZSTD_MAX_INPUT_SIZE) ? 0 : (srcSize) + ((srcSize)>>8) + (((srcSize) < (128<<10)) ? (((128<<10) - (srcSize)) >> 11) /* margin, from 64 to 0 */ : 0)) /* this formula ensures that bound(A) + bound(B) <= bound(A+B) as long as A and B >= 128 KB */
+ZSTDLIB_API size_t ZSTD_compressBound(size_t srcSize); /*!< maximum compressed size in worst case single-pass scenario */
+/* ZSTD_isError() :
+ * Most ZSTD_* functions returning a size_t value can be tested for error,
+ * using ZSTD_isError().
+ * @return 1 if error, 0 otherwise
+ */
ZSTDLIB_API unsigned ZSTD_isError(size_t code); /*!< tells if a `size_t` function result is an error code */
ZSTDLIB_API const char* ZSTD_getErrorName(size_t code); /*!< provides readable string from an error code */
+ZSTDLIB_API int ZSTD_minCLevel(void); /*!< minimum negative compression level allowed, requires v1.4.0+ */
ZSTDLIB_API int ZSTD_maxCLevel(void); /*!< maximum compression level available */
+ZSTDLIB_API int ZSTD_defaultCLevel(void); /*!< default compression level, specified by ZSTD_CLEVEL_DEFAULT, requires v1.5.0+ */
/***************************************
@@ -159,17 +248,26 @@ ZSTDLIB_API int ZSTD_maxCLevel(void); /*!< maximum compres
***************************************/
/*= Compression context
* When compressing many times,
- * it is recommended to allocate a context just once, and re-use it for each successive compression operation.
+ * it is recommended to allocate a context just once,
+ * and re-use it for each successive compression operation.
* This will make workload friendlier for system's memory.
- * Use one context per thread for parallel execution in multi-threaded environments. */
+ * Note : re-using context is just a speed / resource optimization.
+ * It doesn't change the compression ratio, which remains identical.
+ * Note 2 : In multi-threaded environments,
+ * use one different context per thread for parallel execution.
+ */
typedef struct ZSTD_CCtx_s ZSTD_CCtx;
ZSTDLIB_API ZSTD_CCtx* ZSTD_createCCtx(void);
-ZSTDLIB_API size_t ZSTD_freeCCtx(ZSTD_CCtx* cctx);
+ZSTDLIB_API size_t ZSTD_freeCCtx(ZSTD_CCtx* cctx); /* accept NULL pointer */
/*! ZSTD_compressCCtx() :
- * Same as ZSTD_compress(), using an explicit ZSTD_CCtx
- * The function will compress at requested compression level,
- * ignoring any other parameter */
+ * Same as ZSTD_compress(), using an explicit ZSTD_CCtx.
+ * Important : in order to behave similarly to `ZSTD_compress()`,
+ * this function compresses at requested compression level,
+ * __ignoring any other parameter__ .
+ * If any advanced parameter was set using the advanced API,
+ * they will all be reset. Only `compressionLevel` remains.
+ */
ZSTDLIB_API size_t ZSTD_compressCCtx(ZSTD_CCtx* cctx,
void* dst, size_t dstCapacity,
const void* src, size_t srcSize,
@@ -183,7 +281,7 @@ ZSTDLIB_API size_t ZSTD_compressCCtx(ZSTD_CCtx* cctx,
* Use one context per thread for parallel execution. */
typedef struct ZSTD_DCtx_s ZSTD_DCtx;
ZSTDLIB_API ZSTD_DCtx* ZSTD_createDCtx(void);
-ZSTDLIB_API size_t ZSTD_freeDCtx(ZSTD_DCtx* dctx);
+ZSTDLIB_API size_t ZSTD_freeDCtx(ZSTD_DCtx* dctx); /* accept NULL pointer */
/*! ZSTD_decompressDCtx() :
* Same as ZSTD_decompress(),
@@ -195,93 +293,378 @@ ZSTDLIB_API size_t ZSTD_decompressDCtx(ZSTD_DCtx* dctx,
const void* src, size_t srcSize);
-/**************************
-* Simple dictionary API
-***************************/
-/*! ZSTD_compress_usingDict() :
- * Compression at an explicit compression level using a Dictionary.
- * A dictionary can be any arbitrary data segment (also called a prefix),
- * or a buffer with specified information (see dictBuilder/zdict.h).
- * Note : This function loads the dictionary, resulting in significant startup delay.
- * It's intended for a dictionary used only once.
- * Note 2 : When `dict == NULL || dictSize < 8` no dictionary is used. */
-ZSTDLIB_API size_t ZSTD_compress_usingDict(ZSTD_CCtx* ctx,
- void* dst, size_t dstCapacity,
- const void* src, size_t srcSize,
- const void* dict,size_t dictSize,
- int compressionLevel);
-
-/*! ZSTD_decompress_usingDict() :
- * Decompression using a known Dictionary.
- * Dictionary must be identical to the one used during compression.
- * Note : This function loads the dictionary, resulting in significant startup delay.
- * It's intended for a dictionary used only once.
- * Note : When `dict == NULL || dictSize < 8` no dictionary is used. */
-ZSTDLIB_API size_t ZSTD_decompress_usingDict(ZSTD_DCtx* dctx,
- void* dst, size_t dstCapacity,
- const void* src, size_t srcSize,
- const void* dict,size_t dictSize);
-
+/*********************************************
+* Advanced compression API (Requires v1.4.0+)
+**********************************************/
-/***********************************
- * Bulk processing dictionary API
- **********************************/
-typedef struct ZSTD_CDict_s ZSTD_CDict;
+/* API design :
+ * Parameters are pushed one by one into an existing context,
+ * using ZSTD_CCtx_set*() functions.
+ * Pushed parameters are sticky : they are valid for next compressed frame, and any subsequent frame.
+ * "sticky" parameters are applicable to `ZSTD_compress2()` and `ZSTD_compressStream*()` !
+ * __They do not apply to "simple" one-shot variants such as ZSTD_compressCCtx()__ .
+ *
+ * It's possible to reset all parameters to "default" using ZSTD_CCtx_reset().
+ *
+ * This API supersedes all other "advanced" API entry points in the experimental section.
+ * In the future, we expect to remove from experimental API entry points which are redundant with this API.
+ */
-/*! ZSTD_createCDict() :
- * When compressing multiple messages / blocks using the same dictionary, it's recommended to load it only once.
- * ZSTD_createCDict() will create a digested dictionary, ready to start future compression operations without startup cost.
- * ZSTD_CDict can be created once and shared by multiple threads concurrently, since its usage is read-only.
- * `dictBuffer` can be released after ZSTD_CDict creation, because its content is copied within CDict.
- * Consider experimental function `ZSTD_createCDict_byReference()` if you prefer to not duplicate `dictBuffer` content.
- * Note : A ZSTD_CDict can be created from an empty dictBuffer, but it is inefficient when used to compress small data. */
-ZSTDLIB_API ZSTD_CDict* ZSTD_createCDict(const void* dictBuffer, size_t dictSize,
- int compressionLevel);
-/*! ZSTD_freeCDict() :
- * Function frees memory allocated by ZSTD_createCDict(). */
-ZSTDLIB_API size_t ZSTD_freeCDict(ZSTD_CDict* CDict);
+/* Compression strategies, listed from fastest to strongest */
+typedef enum { ZSTD_fast=1,
+ ZSTD_dfast=2,
+ ZSTD_greedy=3,
+ ZSTD_lazy=4,
+ ZSTD_lazy2=5,
+ ZSTD_btlazy2=6,
+ ZSTD_btopt=7,
+ ZSTD_btultra=8,
+ ZSTD_btultra2=9
+ /* note : new strategies _might_ be added in the future.
+ Only the order (from fast to strong) is guaranteed */
+} ZSTD_strategy;
-/*! ZSTD_compress_usingCDict() :
- * Compression using a digested Dictionary.
- * Recommended when same dictionary is used multiple times.
- * Note : compression level is _decided at dictionary creation time_,
- * and frame parameters are hardcoded (dictID=yes, contentSize=yes, checksum=no) */
-ZSTDLIB_API size_t ZSTD_compress_usingCDict(ZSTD_CCtx* cctx,
- void* dst, size_t dstCapacity,
- const void* src, size_t srcSize,
- const ZSTD_CDict* cdict);
+typedef enum {
+ /* compression parameters
+ * Note: When compressing with a ZSTD_CDict these parameters are superseded
+ * by the parameters used to construct the ZSTD_CDict.
+ * See ZSTD_CCtx_refCDict() for more info (superseded-by-cdict). */
+ ZSTD_c_compressionLevel=100, /* Set compression parameters according to pre-defined cLevel table.
+ * Note that exact compression parameters are dynamically determined,
+ * depending on both compression level and srcSize (when known).
+ * Default level is ZSTD_CLEVEL_DEFAULT==3.
+ * Special: value 0 means default, which is controlled by ZSTD_CLEVEL_DEFAULT.
+ * Note 1 : it's possible to pass a negative compression level.
+ * Note 2 : setting a level does not automatically set all other compression parameters
+ * to default. Setting this will however eventually dynamically impact the compression
+ * parameters which have not been manually set. The manually set
+ * ones will 'stick'. */
+ /* Advanced compression parameters :
+ * It's possible to pin down compression parameters to some specific values.
+ * In which case, these values are no longer dynamically selected by the compressor */
+ ZSTD_c_windowLog=101, /* Maximum allowed back-reference distance, expressed as power of 2.
+ * This will set a memory budget for streaming decompression,
+ * with larger values requiring more memory
+ * and typically compressing more.
+ * Must be clamped between ZSTD_WINDOWLOG_MIN and ZSTD_WINDOWLOG_MAX.
+ * Special: value 0 means "use default windowLog".
+ * Note: Using a windowLog greater than ZSTD_WINDOWLOG_LIMIT_DEFAULT
+ * requires explicitly allowing such size at streaming decompression stage. */
+ ZSTD_c_hashLog=102, /* Size of the initial probe table, as a power of 2.
+ * Resulting memory usage is (1 << (hashLog+2)).
+ * Must be clamped between ZSTD_HASHLOG_MIN and ZSTD_HASHLOG_MAX.
+ * Larger tables improve compression ratio of strategies <= dFast,
+ * and improve speed of strategies > dFast.
+ * Special: value 0 means "use default hashLog". */
+ ZSTD_c_chainLog=103, /* Size of the multi-probe search table, as a power of 2.
+ * Resulting memory usage is (1 << (chainLog+2)).
+ * Must be clamped between ZSTD_CHAINLOG_MIN and ZSTD_CHAINLOG_MAX.
+ * Larger tables result in better and slower compression.
+ * This parameter is useless for "fast" strategy.
+ * It's still useful when using "dfast" strategy,
+ * in which case it defines a secondary probe table.
+ * Special: value 0 means "use default chainLog". */
+ ZSTD_c_searchLog=104, /* Number of search attempts, as a power of 2.
+ * More attempts result in better and slower compression.
+ * This parameter is useless for "fast" and "dFast" strategies.
+ * Special: value 0 means "use default searchLog". */
+ ZSTD_c_minMatch=105, /* Minimum size of searched matches.
+ * Note that Zstandard can still find matches of smaller size,
+ * it just tweaks its search algorithm to look for this size and larger.
+ * Larger values increase compression and decompression speed, but decrease ratio.
+ * Must be clamped between ZSTD_MINMATCH_MIN and ZSTD_MINMATCH_MAX.
+ * Note that currently, for all strategies < btopt, effective minimum is 4.
+ * , for all strategies > fast, effective maximum is 6.
+ * Special: value 0 means "use default minMatchLength". */
+ ZSTD_c_targetLength=106, /* Impact of this field depends on strategy.
+ * For strategies btopt, btultra & btultra2:
+ * Length of Match considered "good enough" to stop search.
+ * Larger values make compression stronger, and slower.
+ * For strategy fast:
+ * Distance between match sampling.
+ * Larger values make compression faster, and weaker.
+ * Special: value 0 means "use default targetLength". */
+ ZSTD_c_strategy=107, /* See ZSTD_strategy enum definition.
+ * The higher the value of selected strategy, the more complex it is,
+ * resulting in stronger and slower compression.
+ * Special: value 0 means "use default strategy". */
+ /* LDM mode parameters */
+ ZSTD_c_enableLongDistanceMatching=160, /* Enable long distance matching.
+ * This parameter is designed to improve compression ratio
+ * for large inputs, by finding large matches at long distance.
+ * It increases memory usage and window size.
+ * Note: enabling this parameter increases default ZSTD_c_windowLog to 128 MB
+ * except when expressly set to a different value.
+ * Note: will be enabled by default if ZSTD_c_windowLog >= 128 MB and
+ * compression strategy >= ZSTD_btopt (== compression level 16+) */
+ ZSTD_c_ldmHashLog=161, /* Size of the table for long distance matching, as a power of 2.
+ * Larger values increase memory usage and compression ratio,
+ * but decrease compression speed.
+ * Must be clamped between ZSTD_HASHLOG_MIN and ZSTD_HASHLOG_MAX
+ * default: windowlog - 7.
+ * Special: value 0 means "automatically determine hashlog". */
+ ZSTD_c_ldmMinMatch=162, /* Minimum match size for long distance matcher.
+ * Larger/too small values usually decrease compression ratio.
+ * Must be clamped between ZSTD_LDM_MINMATCH_MIN and ZSTD_LDM_MINMATCH_MAX.
+ * Special: value 0 means "use default value" (default: 64). */
+ ZSTD_c_ldmBucketSizeLog=163, /* Log size of each bucket in the LDM hash table for collision resolution.
+ * Larger values improve collision resolution but decrease compression speed.
+ * The maximum value is ZSTD_LDM_BUCKETSIZELOG_MAX.
+ * Special: value 0 means "use default value" (default: 3). */
+ ZSTD_c_ldmHashRateLog=164, /* Frequency of inserting/looking up entries into the LDM hash table.
+ * Must be clamped between 0 and (ZSTD_WINDOWLOG_MAX - ZSTD_HASHLOG_MIN).
+ * Default is MAX(0, (windowLog - ldmHashLog)), optimizing hash table usage.
+ * Larger values improve compression speed.
+ * Deviating far from default value will likely result in a compression ratio decrease.
+ * Special: value 0 means "automatically determine hashRateLog". */
-typedef struct ZSTD_DDict_s ZSTD_DDict;
+ /* frame parameters */
+ ZSTD_c_contentSizeFlag=200, /* Content size will be written into frame header _whenever known_ (default:1)
+ * Content size must be known at the beginning of compression.
+ * This is automatically the case when using ZSTD_compress2(),
+ * For streaming scenarios, content size must be provided with ZSTD_CCtx_setPledgedSrcSize() */
+ ZSTD_c_checksumFlag=201, /* A 32-bits checksum of content is written at end of frame (default:0) */
+ ZSTD_c_dictIDFlag=202, /* When applicable, dictionary's ID is written into frame header (default:1) */
-/*! ZSTD_createDDict() :
- * Create a digested dictionary, ready to start decompression operation without startup delay.
- * dictBuffer can be released after DDict creation, as its content is copied inside DDict. */
-ZSTDLIB_API ZSTD_DDict* ZSTD_createDDict(const void* dictBuffer, size_t dictSize);
+ /* multi-threading parameters */
+ /* These parameters are only active if multi-threading is enabled (compiled with build macro ZSTD_MULTITHREAD).
+ * Otherwise, trying to set any other value than default (0) will be a no-op and return an error.
+ * In a situation where it's unknown if the linked library supports multi-threading or not,
+ * setting ZSTD_c_nbWorkers to any value >= 1 and consulting the return value provides a quick way to check this property.
+ */
+ ZSTD_c_nbWorkers=400, /* Select how many threads will be spawned to compress in parallel.
+ * When nbWorkers >= 1, triggers asynchronous mode when invoking ZSTD_compressStream*() :
+ * ZSTD_compressStream*() consumes input and flush output if possible, but immediately gives back control to caller,
+ * while compression is performed in parallel, within worker thread(s).
+ * (note : a strong exception to this rule is when first invocation of ZSTD_compressStream2() sets ZSTD_e_end :
+ * in which case, ZSTD_compressStream2() delegates to ZSTD_compress2(), which is always a blocking call).
+ * More workers improve speed, but also increase memory usage.
+ * Default value is `0`, aka "single-threaded mode" : no worker is spawned,
+ * compression is performed inside Caller's thread, and all invocations are blocking */
+ ZSTD_c_jobSize=401, /* Size of a compression job. This value is enforced only when nbWorkers >= 1.
+ * Each compression job is completed in parallel, so this value can indirectly impact the nb of active threads.
+ * 0 means default, which is dynamically determined based on compression parameters.
+ * Job size must be a minimum of overlap size, or ZSTDMT_JOBSIZE_MIN (= 512 KB), whichever is largest.
+ * The minimum size is automatically and transparently enforced. */
+ ZSTD_c_overlapLog=402, /* Control the overlap size, as a fraction of window size.
+ * The overlap size is an amount of data reloaded from previous job at the beginning of a new job.
+ * It helps preserve compression ratio, while each job is compressed in parallel.
+ * This value is enforced only when nbWorkers >= 1.
+ * Larger values increase compression ratio, but decrease speed.
+ * Possible values range from 0 to 9 :
+ * - 0 means "default" : value will be determined by the library, depending on strategy
+ * - 1 means "no overlap"
+ * - 9 means "full overlap", using a full window size.
+ * Each intermediate rank increases/decreases load size by a factor 2 :
+ * 9: full window; 8: w/2; 7: w/4; 6: w/8; 5:w/16; 4: w/32; 3:w/64; 2:w/128; 1:no overlap; 0:default
+ * default value varies between 6 and 9, depending on strategy */
-/*! ZSTD_freeDDict() :
- * Function frees memory allocated with ZSTD_createDDict() */
-ZSTDLIB_API size_t ZSTD_freeDDict(ZSTD_DDict* ddict);
+ /* note : additional experimental parameters are also available
+ * within the experimental section of the API.
+ * At the time of this writing, they include :
+ * ZSTD_c_rsyncable
+ * ZSTD_c_format
+ * ZSTD_c_forceMaxWindow
+ * ZSTD_c_forceAttachDict
+ * ZSTD_c_literalCompressionMode
+ * ZSTD_c_targetCBlockSize
+ * ZSTD_c_srcSizeHint
+ * ZSTD_c_enableDedicatedDictSearch
+ * ZSTD_c_stableInBuffer
+ * ZSTD_c_stableOutBuffer
+ * ZSTD_c_blockDelimiters
+ * ZSTD_c_validateSequences
+ * ZSTD_c_useBlockSplitter
+ * ZSTD_c_useRowMatchFinder
+ * ZSTD_c_prefetchCDictTables
+ * ZSTD_c_enableSeqProducerFallback
+ * ZSTD_c_maxBlockSize
+ * Because they are not stable, it's necessary to define ZSTD_STATIC_LINKING_ONLY to access them.
+ * note : never ever use experimentalParam? names directly;
+ * also, the enums values themselves are unstable and can still change.
+ */
+ ZSTD_c_experimentalParam1=500,
+ ZSTD_c_experimentalParam2=10,
+ ZSTD_c_experimentalParam3=1000,
+ ZSTD_c_experimentalParam4=1001,
+ ZSTD_c_experimentalParam5=1002,
+ ZSTD_c_experimentalParam6=1003,
+ ZSTD_c_experimentalParam7=1004,
+ ZSTD_c_experimentalParam8=1005,
+ ZSTD_c_experimentalParam9=1006,
+ ZSTD_c_experimentalParam10=1007,
+ ZSTD_c_experimentalParam11=1008,
+ ZSTD_c_experimentalParam12=1009,
+ ZSTD_c_experimentalParam13=1010,
+ ZSTD_c_experimentalParam14=1011,
+ ZSTD_c_experimentalParam15=1012,
+ ZSTD_c_experimentalParam16=1013,
+ ZSTD_c_experimentalParam17=1014,
+ ZSTD_c_experimentalParam18=1015,
+ ZSTD_c_experimentalParam19=1016
+} ZSTD_cParameter;
-/*! ZSTD_decompress_usingDDict() :
- * Decompression using a digested Dictionary.
- * Recommended when same dictionary is used multiple times. */
-ZSTDLIB_API size_t ZSTD_decompress_usingDDict(ZSTD_DCtx* dctx,
- void* dst, size_t dstCapacity,
- const void* src, size_t srcSize,
- const ZSTD_DDict* ddict);
+typedef struct {
+ size_t error;
+ int lowerBound;
+ int upperBound;
+} ZSTD_bounds;
+/*! ZSTD_cParam_getBounds() :
+ * All parameters must belong to an interval with lower and upper bounds,
+ * otherwise they will either trigger an error or be automatically clamped.
+ * @return : a structure, ZSTD_bounds, which contains
+ * - an error status field, which must be tested using ZSTD_isError()
+ * - lower and upper bounds, both inclusive
+ */
+ZSTDLIB_API ZSTD_bounds ZSTD_cParam_getBounds(ZSTD_cParameter cParam);
-/****************************
-* Streaming
-****************************/
+/*! ZSTD_CCtx_setParameter() :
+ * Set one compression parameter, selected by enum ZSTD_cParameter.
+ * All parameters have valid bounds. Bounds can be queried using ZSTD_cParam_getBounds().
+ * Providing a value beyond bound will either clamp it, or trigger an error (depending on parameter).
+ * Setting a parameter is generally only possible during frame initialization (before starting compression).
+ * Exception : when using multi-threading mode (nbWorkers >= 1),
+ * the following parameters can be updated _during_ compression (within same frame):
+ * => compressionLevel, hashLog, chainLog, searchLog, minMatch, targetLength and strategy.
+ * new parameters will be active for next job only (after a flush()).
+ * @return : an error code (which can be tested using ZSTD_isError()).
+ */
+ZSTDLIB_API size_t ZSTD_CCtx_setParameter(ZSTD_CCtx* cctx, ZSTD_cParameter param, int value);
-typedef struct ZSTD_inBuffer_s {
- const void* src; /**< start of input buffer */
- size_t size; /**< size of input buffer */
- size_t pos; /**< position where reading stopped. Will be updated. Necessarily 0 <= pos <= size */
-} ZSTD_inBuffer;
+/*! ZSTD_CCtx_setPledgedSrcSize() :
+ * Total input data size to be compressed as a single frame.
+ * Value will be written in frame header, unless if explicitly forbidden using ZSTD_c_contentSizeFlag.
+ * This value will also be controlled at end of frame, and trigger an error if not respected.
+ * @result : 0, or an error code (which can be tested with ZSTD_isError()).
+ * Note 1 : pledgedSrcSize==0 actually means zero, aka an empty frame.
+ * In order to mean "unknown content size", pass constant ZSTD_CONTENTSIZE_UNKNOWN.
+ * ZSTD_CONTENTSIZE_UNKNOWN is default value for any new frame.
+ * Note 2 : pledgedSrcSize is only valid once, for the next frame.
+ * It's discarded at the end of the frame, and replaced by ZSTD_CONTENTSIZE_UNKNOWN.
+ * Note 3 : Whenever all input data is provided and consumed in a single round,
+ * for example with ZSTD_compress2(),
+ * or invoking immediately ZSTD_compressStream2(,,,ZSTD_e_end),
+ * this value is automatically overridden by srcSize instead.
+ */
+ZSTDLIB_API size_t ZSTD_CCtx_setPledgedSrcSize(ZSTD_CCtx* cctx, unsigned long long pledgedSrcSize);
+
+typedef enum {
+ ZSTD_reset_session_only = 1,
+ ZSTD_reset_parameters = 2,
+ ZSTD_reset_session_and_parameters = 3
+} ZSTD_ResetDirective;
+
+/*! ZSTD_CCtx_reset() :
+ * There are 2 different things that can be reset, independently or jointly :
+ * - The session : will stop compressing current frame, and make CCtx ready to start a new one.
+ * Useful after an error, or to interrupt any ongoing compression.
+ * Any internal data not yet flushed is cancelled.
+ * Compression parameters and dictionary remain unchanged.
+ * They will be used to compress next frame.
+ * Resetting session never fails.
+ * - The parameters : changes all parameters back to "default".
+ * This also removes any reference to any dictionary or external sequence producer.
+ * Parameters can only be changed between 2 sessions (i.e. no compression is currently ongoing)
+ * otherwise the reset fails, and function returns an error value (which can be tested using ZSTD_isError())
+ * - Both : similar to resetting the session, followed by resetting parameters.
+ */
+ZSTDLIB_API size_t ZSTD_CCtx_reset(ZSTD_CCtx* cctx, ZSTD_ResetDirective reset);
+
+/*! ZSTD_compress2() :
+ * Behave the same as ZSTD_compressCCtx(), but compression parameters are set using the advanced API.
+ * ZSTD_compress2() always starts a new frame.
+ * Should cctx hold data from a previously unfinished frame, everything about it is forgotten.
+ * - Compression parameters are pushed into CCtx before starting compression, using ZSTD_CCtx_set*()
+ * - The function is always blocking, returns when compression is completed.
+ * NOTE: Providing `dstCapacity >= ZSTD_compressBound(srcSize)` guarantees that zstd will have
+ * enough space to successfully compress the data, though it is possible it fails for other reasons.
+ * @return : compressed size written into `dst` (<= `dstCapacity),
+ * or an error code if it fails (which can be tested using ZSTD_isError()).
+ */
+ZSTDLIB_API size_t ZSTD_compress2( ZSTD_CCtx* cctx,
+ void* dst, size_t dstCapacity,
+ const void* src, size_t srcSize);
+
+
+/***********************************************
+* Advanced decompression API (Requires v1.4.0+)
+************************************************/
+
+/* The advanced API pushes parameters one by one into an existing DCtx context.
+ * Parameters are sticky, and remain valid for all following frames
+ * using the same DCtx context.
+ * It's possible to reset parameters to default values using ZSTD_DCtx_reset().
+ * Note : This API is compatible with existing ZSTD_decompressDCtx() and ZSTD_decompressStream().
+ * Therefore, no new decompression function is necessary.
+ */
+
+typedef enum {
+
+ ZSTD_d_windowLogMax=100, /* Select a size limit (in power of 2) beyond which
+ * the streaming API will refuse to allocate memory buffer
+ * in order to protect the host from unreasonable memory requirements.
+ * This parameter is only useful in streaming mode, since no internal buffer is allocated in single-pass mode.
+ * By default, a decompression context accepts window sizes <= (1 << ZSTD_WINDOWLOG_LIMIT_DEFAULT).
+ * Special: value 0 means "use default maximum windowLog". */
+
+ /* note : additional experimental parameters are also available
+ * within the experimental section of the API.
+ * At the time of this writing, they include :
+ * ZSTD_d_format
+ * ZSTD_d_stableOutBuffer
+ * ZSTD_d_forceIgnoreChecksum
+ * ZSTD_d_refMultipleDDicts
+ * ZSTD_d_disableHuffmanAssembly
+ * Because they are not stable, it's necessary to define ZSTD_STATIC_LINKING_ONLY to access them.
+ * note : never ever use experimentalParam? names directly
+ */
+ ZSTD_d_experimentalParam1=1000,
+ ZSTD_d_experimentalParam2=1001,
+ ZSTD_d_experimentalParam3=1002,
+ ZSTD_d_experimentalParam4=1003,
+ ZSTD_d_experimentalParam5=1004
+
+} ZSTD_dParameter;
+
+/*! ZSTD_dParam_getBounds() :
+ * All parameters must belong to an interval with lower and upper bounds,
+ * otherwise they will either trigger an error or be automatically clamped.
+ * @return : a structure, ZSTD_bounds, which contains
+ * - an error status field, which must be tested using ZSTD_isError()
+ * - both lower and upper bounds, inclusive
+ */
+ZSTDLIB_API ZSTD_bounds ZSTD_dParam_getBounds(ZSTD_dParameter dParam);
+
+/*! ZSTD_DCtx_setParameter() :
+ * Set one compression parameter, selected by enum ZSTD_dParameter.
+ * All parameters have valid bounds. Bounds can be queried using ZSTD_dParam_getBounds().
+ * Providing a value beyond bound will either clamp it, or trigger an error (depending on parameter).
+ * Setting a parameter is only possible during frame initialization (before starting decompression).
+ * @return : 0, or an error code (which can be tested using ZSTD_isError()).
+ */
+ZSTDLIB_API size_t ZSTD_DCtx_setParameter(ZSTD_DCtx* dctx, ZSTD_dParameter param, int value);
+
+/*! ZSTD_DCtx_reset() :
+ * Return a DCtx to clean state.
+ * Session and parameters can be reset jointly or separately.
+ * Parameters can only be reset when no active frame is being decompressed.
+ * @return : 0, or an error code, which can be tested with ZSTD_isError()
+ */
+ZSTDLIB_API size_t ZSTD_DCtx_reset(ZSTD_DCtx* dctx, ZSTD_ResetDirective reset);
+
+
+/****************************
+* Streaming
+****************************/
+
+typedef struct ZSTD_inBuffer_s {
+ const void* src; /**< start of input buffer */
+ size_t size; /**< size of input buffer */
+ size_t pos; /**< position where reading stopped. Will be updated. Necessarily 0 <= pos <= size */
+} ZSTD_inBuffer;
typedef struct ZSTD_outBuffer_s {
void* dst; /**< start of output buffer */
@@ -306,34 +689,46 @@ typedef struct ZSTD_outBuffer_s {
* Parameters are sticky : when starting a new compression on the same context,
* it will re-use the same sticky parameters as previous compression session.
* When in doubt, it's recommended to fully initialize the context before usage.
-* Use ZSTD_initCStream() to set the parameter to a selected compression level.
-* Use advanced API (ZSTD_CCtx_setParameter(), etc.) to set more specific parameters.
+* Use ZSTD_CCtx_reset() to reset the context and ZSTD_CCtx_setParameter(),
+* ZSTD_CCtx_setPledgedSrcSize(), or ZSTD_CCtx_loadDictionary() and friends to
+* set more specific parameters, the pledged source size, or load a dictionary.
*
-* Use ZSTD_compressStream() as many times as necessary to consume input stream.
-* The function will automatically update both `pos` fields within `input` and `output`.
-* Note that the function may not consume the entire input,
-* for example, because the output buffer is already full,
-* in which case `input.pos < input.size`.
+* Use ZSTD_compressStream2() with ZSTD_e_continue as many times as necessary to
+* consume input stream. The function will automatically update both `pos`
+* fields within `input` and `output`.
+* Note that the function may not consume the entire input, for example, because
+* the output buffer is already full, in which case `input.pos < input.size`.
* The caller must check if input has been entirely consumed.
* If not, the caller must make some room to receive more compressed data,
* and then present again remaining input data.
-* @return : a size hint, preferred nb of bytes to use as input for next function call
+* note: ZSTD_e_continue is guaranteed to make some forward progress when called,
+* but doesn't guarantee maximal forward progress. This is especially relevant
+* when compressing with multiple threads. The call won't block if it can
+* consume some input, but if it can't it will wait for some, but not all,
+* output to be flushed.
+* @return : provides a minimum amount of data remaining to be flushed from internal buffers
* or an error code, which can be tested using ZSTD_isError().
-* Note 1 : it's just a hint, to help latency a little, any value will work fine.
-* Note 2 : size hint is guaranteed to be <= ZSTD_CStreamInSize()
*
* At any moment, it's possible to flush whatever data might remain stuck within internal buffer,
-* using ZSTD_flushStream(). `output->pos` will be updated.
-* Note that, if `output->size` is too small, a single invocation of ZSTD_flushStream() might not be enough (return code > 0).
-* In which case, make some room to receive more compressed data, and call again ZSTD_flushStream().
+* using ZSTD_compressStream2() with ZSTD_e_flush. `output->pos` will be updated.
+* Note that, if `output->size` is too small, a single invocation with ZSTD_e_flush might not be enough (return code > 0).
+* In which case, make some room to receive more compressed data, and call again ZSTD_compressStream2() with ZSTD_e_flush.
+* You must continue calling ZSTD_compressStream2() with ZSTD_e_flush until it returns 0, at which point you can change the
+* operation.
+* note: ZSTD_e_flush will flush as much output as possible, meaning when compressing with multiple threads, it will
+* block until the flush is complete or the output buffer is full.
* @return : 0 if internal buffers are entirely flushed,
* >0 if some data still present within internal buffer (the value is minimal estimation of remaining size),
* or an error code, which can be tested using ZSTD_isError().
*
-* ZSTD_endStream() instructs to finish a frame.
+* Calling ZSTD_compressStream2() with ZSTD_e_end instructs to finish a frame.
* It will perform a flush and write frame epilogue.
* The epilogue is required for decoders to consider a frame completed.
-* flush() operation is the same, and follows same rules as ZSTD_flushStream().
+* flush operation is the same, and follows same rules as calling ZSTD_compressStream2() with ZSTD_e_flush.
+* You must continue calling ZSTD_compressStream2() with ZSTD_e_end until it returns 0, at which point you are free to
+* start a new frame.
+* note: ZSTD_e_end will flush as much output as possible, meaning when compressing with multiple threads, it will
+* block until the flush is complete or the output buffer is full.
* @return : 0 if frame fully completed and fully flushed,
* >0 if some data still present within internal buffer (the value is minimal estimation of remaining size),
* or an error code, which can be tested using ZSTD_isError().
@@ -344,18 +739,96 @@ typedef ZSTD_CCtx ZSTD_CStream; /**< CCtx and CStream are now effectively same
/* Continue to distinguish them for compatibility with older versions <= v1.2.0 */
/*===== ZSTD_CStream management functions =====*/
ZSTDLIB_API ZSTD_CStream* ZSTD_createCStream(void);
-ZSTDLIB_API size_t ZSTD_freeCStream(ZSTD_CStream* zcs);
+ZSTDLIB_API size_t ZSTD_freeCStream(ZSTD_CStream* zcs); /* accept NULL pointer */
/*===== Streaming compression functions =====*/
+typedef enum {
+ ZSTD_e_continue=0, /* collect more data, encoder decides when to output compressed result, for optimal compression ratio */
+ ZSTD_e_flush=1, /* flush any data provided so far,
+ * it creates (at least) one new block, that can be decoded immediately on reception;
+ * frame will continue: any future data can still reference previously compressed data, improving compression.
+ * note : multithreaded compression will block to flush as much output as possible. */
+ ZSTD_e_end=2 /* flush any remaining data _and_ close current frame.
+ * note that frame is only closed after compressed data is fully flushed (return value == 0).
+ * After that point, any additional data starts a new frame.
+ * note : each frame is independent (does not reference any content from previous frame).
+ : note : multithreaded compression will block to flush as much output as possible. */
+} ZSTD_EndDirective;
+
+/*! ZSTD_compressStream2() : Requires v1.4.0+
+ * Behaves about the same as ZSTD_compressStream, with additional control on end directive.
+ * - Compression parameters are pushed into CCtx before starting compression, using ZSTD_CCtx_set*()
+ * - Compression parameters cannot be changed once compression is started (save a list of exceptions in multi-threading mode)
+ * - output->pos must be <= dstCapacity, input->pos must be <= srcSize
+ * - output->pos and input->pos will be updated. They are guaranteed to remain below their respective limit.
+ * - endOp must be a valid directive
+ * - When nbWorkers==0 (default), function is blocking : it completes its job before returning to caller.
+ * - When nbWorkers>=1, function is non-blocking : it copies a portion of input, distributes jobs to internal worker threads, flush to output whatever is available,
+ * and then immediately returns, just indicating that there is some data remaining to be flushed.
+ * The function nonetheless guarantees forward progress : it will return only after it reads or write at least 1+ byte.
+ * - Exception : if the first call requests a ZSTD_e_end directive and provides enough dstCapacity, the function delegates to ZSTD_compress2() which is always blocking.
+ * - @return provides a minimum amount of data remaining to be flushed from internal buffers
+ * or an error code, which can be tested using ZSTD_isError().
+ * if @return != 0, flush is not fully completed, there is still some data left within internal buffers.
+ * This is useful for ZSTD_e_flush, since in this case more flushes are necessary to empty all buffers.
+ * For ZSTD_e_end, @return == 0 when internal buffers are fully flushed and frame is completed.
+ * - after a ZSTD_e_end directive, if internal buffer is not fully flushed (@return != 0),
+ * only ZSTD_e_end or ZSTD_e_flush operations are allowed.
+ * Before starting a new compression job, or changing compression parameters,
+ * it is required to fully flush internal buffers.
+ */
+ZSTDLIB_API size_t ZSTD_compressStream2( ZSTD_CCtx* cctx,
+ ZSTD_outBuffer* output,
+ ZSTD_inBuffer* input,
+ ZSTD_EndDirective endOp);
+
+
+/* These buffer sizes are softly recommended.
+ * They are not required : ZSTD_compressStream*() happily accepts any buffer size, for both input and output.
+ * Respecting the recommended size just makes it a bit easier for ZSTD_compressStream*(),
+ * reducing the amount of memory shuffling and buffering, resulting in minor performance savings.
+ *
+ * However, note that these recommendations are from the perspective of a C caller program.
+ * If the streaming interface is invoked from some other language,
+ * especially managed ones such as Java or Go, through a foreign function interface such as jni or cgo,
+ * a major performance rule is to reduce crossing such interface to an absolute minimum.
+ * It's not rare that performance ends being spent more into the interface, rather than compression itself.
+ * In which cases, prefer using large buffers, as large as practical,
+ * for both input and output, to reduce the nb of roundtrips.
+ */
+ZSTDLIB_API size_t ZSTD_CStreamInSize(void); /**< recommended size for input buffer */
+ZSTDLIB_API size_t ZSTD_CStreamOutSize(void); /**< recommended size for output buffer. Guarantee to successfully flush at least one complete compressed block. */
+
+
+/* *****************************************************************************
+ * This following is a legacy streaming API, available since v1.0+ .
+ * It can be replaced by ZSTD_CCtx_reset() and ZSTD_compressStream2().
+ * It is redundant, but remains fully supported.
+ ******************************************************************************/
+
+/*!
+ * Equivalent to:
+ *
+ * ZSTD_CCtx_reset(zcs, ZSTD_reset_session_only);
+ * ZSTD_CCtx_refCDict(zcs, NULL); // clear the dictionary (if any)
+ * ZSTD_CCtx_setParameter(zcs, ZSTD_c_compressionLevel, compressionLevel);
+ *
+ * Note that ZSTD_initCStream() clears any previously set dictionary. Use the new API
+ * to compress with a dictionary.
+ */
ZSTDLIB_API size_t ZSTD_initCStream(ZSTD_CStream* zcs, int compressionLevel);
+/*!
+ * Alternative for ZSTD_compressStream2(zcs, output, input, ZSTD_e_continue).
+ * NOTE: The return value is different. ZSTD_compressStream() returns a hint for
+ * the next read size (if non-zero and not an error). ZSTD_compressStream2()
+ * returns the minimum nb of bytes left to flush (if non-zero and not an error).
+ */
ZSTDLIB_API size_t ZSTD_compressStream(ZSTD_CStream* zcs, ZSTD_outBuffer* output, ZSTD_inBuffer* input);
+/*! Equivalent to ZSTD_compressStream2(zcs, output, &emptyInput, ZSTD_e_flush). */
ZSTDLIB_API size_t ZSTD_flushStream(ZSTD_CStream* zcs, ZSTD_outBuffer* output);
+/*! Equivalent to ZSTD_compressStream2(zcs, output, &emptyInput, ZSTD_e_end). */
ZSTDLIB_API size_t ZSTD_endStream(ZSTD_CStream* zcs, ZSTD_outBuffer* output);
-ZSTDLIB_API size_t ZSTD_CStreamInSize(void); /**< recommended size for input buffer */
-ZSTDLIB_API size_t ZSTD_CStreamOutSize(void); /**< recommended size for output buffer. Guarantee to successfully flush at least one complete compressed block in all circumstances. */
-
-
/*-***************************************************************************
* Streaming decompression - HowTo
@@ -388,302 +861,182 @@ typedef ZSTD_DCtx ZSTD_DStream; /**< DCtx and DStream are now effectively same
/* For compatibility with versions <= v1.2.0, prefer differentiating them. */
/*===== ZSTD_DStream management functions =====*/
ZSTDLIB_API ZSTD_DStream* ZSTD_createDStream(void);
-ZSTDLIB_API size_t ZSTD_freeDStream(ZSTD_DStream* zds);
+ZSTDLIB_API size_t ZSTD_freeDStream(ZSTD_DStream* zds); /* accept NULL pointer */
/*===== Streaming decompression functions =====*/
+
+/*! ZSTD_initDStream() :
+ * Initialize/reset DStream state for new decompression operation.
+ * Call before new decompression operation using same DStream.
+ *
+ * Note : This function is redundant with the advanced API and equivalent to:
+ * ZSTD_DCtx_reset(zds, ZSTD_reset_session_only);
+ * ZSTD_DCtx_refDDict(zds, NULL);
+ */
ZSTDLIB_API size_t ZSTD_initDStream(ZSTD_DStream* zds);
+
+/*! ZSTD_decompressStream() :
+ * Streaming decompression function.
+ * Call repetitively to consume full input updating it as necessary.
+ * Function will update both input and output `pos` fields exposing current state via these fields:
+ * - `input.pos < input.size`, some input remaining and caller should provide remaining input
+ * on the next call.
+ * - `output.pos < output.size`, decoder finished and flushed all remaining buffers.
+ * - `output.pos == output.size`, potentially uncflushed data present in the internal buffers,
+ * call ZSTD_decompressStream() again to flush remaining data to output.
+ * Note : with no additional input, amount of data flushed <= ZSTD_BLOCKSIZE_MAX.
+ *
+ * @return : 0 when a frame is completely decoded and fully flushed,
+ * or an error code, which can be tested using ZSTD_isError(),
+ * or any other value > 0, which means there is some decoding or flushing to do to complete current frame.
+ */
ZSTDLIB_API size_t ZSTD_decompressStream(ZSTD_DStream* zds, ZSTD_outBuffer* output, ZSTD_inBuffer* input);
ZSTDLIB_API size_t ZSTD_DStreamInSize(void); /*!< recommended size for input buffer */
ZSTDLIB_API size_t ZSTD_DStreamOutSize(void); /*!< recommended size for output buffer. Guarantee to successfully flush at least one complete block in all circumstances. */
-#endif /* ZSTD_H_235446 */
-
-
+/**************************
+* Simple dictionary API
+***************************/
+/*! ZSTD_compress_usingDict() :
+ * Compression at an explicit compression level using a Dictionary.
+ * A dictionary can be any arbitrary data segment (also called a prefix),
+ * or a buffer with specified information (see zdict.h).
+ * Note : This function loads the dictionary, resulting in significant startup delay.
+ * It's intended for a dictionary used only once.
+ * Note 2 : When `dict == NULL || dictSize < 8` no dictionary is used. */
+ZSTDLIB_API size_t ZSTD_compress_usingDict(ZSTD_CCtx* ctx,
+ void* dst, size_t dstCapacity,
+ const void* src, size_t srcSize,
+ const void* dict,size_t dictSize,
+ int compressionLevel);
-/****************************************************************************************
- * ADVANCED AND EXPERIMENTAL FUNCTIONS
- ****************************************************************************************
- * The definitions in the following section are considered experimental.
- * They are provided for advanced scenarios.
- * They should never be used with a dynamic library, as prototypes may change in the future.
- * Use them only in association with static linking.
- * ***************************************************************************************/
-
-#if defined(ZSTD_STATIC_LINKING_ONLY) && !defined(ZSTD_H_ZSTD_STATIC_LINKING_ONLY)
-#define ZSTD_H_ZSTD_STATIC_LINKING_ONLY
-
-
-/****************************************************************************************
- * Candidate API for promotion to stable status
- ****************************************************************************************
- * The following symbols and constants form the "staging area" :
- * they are considered to join "stable API" by v1.4.0.
- * The proposal is written so that it can be made stable "as is",
- * though it's still possible to suggest improvements.
- * Staging is in fact last chance for changes,
- * the API is locked once reaching "stable" status.
- * ***************************************************************************************/
-
-
-/* === Constants === */
-
-/* all magic numbers are supposed read/written to/from files/memory using little-endian convention */
-#define ZSTD_MAGICNUMBER 0xFD2FB528 /* valid since v0.8.0 */
-#define ZSTD_MAGIC_DICTIONARY 0xEC30A437 /* valid since v0.7.0 */
-#define ZSTD_MAGIC_SKIPPABLE_START 0x184D2A50 /* all 16 values, from 0x184D2A50 to 0x184D2A5F, signal the beginning of a skippable frame */
-#define ZSTD_MAGIC_SKIPPABLE_MASK 0xFFFFFFF0
-
-#define ZSTD_BLOCKSIZELOG_MAX 17
-#define ZSTD_BLOCKSIZE_MAX (1<= first frame size
- * @return : the compressed size of the first frame starting at `src`,
- * suitable to pass as `srcSize` to `ZSTD_decompress` or similar,
- * or an error code if input is invalid */
-ZSTDLIB_API size_t ZSTD_findFrameCompressedSize(const void* src, size_t srcSize);
-
+/*! ZSTD_decompress_usingDict() :
+ * Decompression using a known Dictionary.
+ * Dictionary must be identical to the one used during compression.
+ * Note : This function loads the dictionary, resulting in significant startup delay.
+ * It's intended for a dictionary used only once.
+ * Note : When `dict == NULL || dictSize < 8` no dictionary is used. */
+ZSTDLIB_API size_t ZSTD_decompress_usingDict(ZSTD_DCtx* dctx,
+ void* dst, size_t dstCapacity,
+ const void* src, size_t srcSize,
+ const void* dict,size_t dictSize);
-/* === Memory management === */
-/*! ZSTD_sizeof_*() :
- * These functions give the _current_ memory usage of selected object.
- * Note that object memory usage can evolve (increase or decrease) over time. */
-ZSTDLIB_API size_t ZSTD_sizeof_CCtx(const ZSTD_CCtx* cctx);
-ZSTDLIB_API size_t ZSTD_sizeof_DCtx(const ZSTD_DCtx* dctx);
-ZSTDLIB_API size_t ZSTD_sizeof_CStream(const ZSTD_CStream* zcs);
-ZSTDLIB_API size_t ZSTD_sizeof_DStream(const ZSTD_DStream* zds);
-ZSTDLIB_API size_t ZSTD_sizeof_CDict(const ZSTD_CDict* cdict);
-ZSTDLIB_API size_t ZSTD_sizeof_DDict(const ZSTD_DDict* ddict);
+/***********************************
+ * Bulk processing dictionary API
+ **********************************/
+typedef struct ZSTD_CDict_s ZSTD_CDict;
+/*! ZSTD_createCDict() :
+ * When compressing multiple messages or blocks using the same dictionary,
+ * it's recommended to digest the dictionary only once, since it's a costly operation.
+ * ZSTD_createCDict() will create a state from digesting a dictionary.
+ * The resulting state can be used for future compression operations with very limited startup cost.
+ * ZSTD_CDict can be created once and shared by multiple threads concurrently, since its usage is read-only.
+ * @dictBuffer can be released after ZSTD_CDict creation, because its content is copied within CDict.
+ * Note 1 : Consider experimental function `ZSTD_createCDict_byReference()` if you prefer to not duplicate @dictBuffer content.
+ * Note 2 : A ZSTD_CDict can be created from an empty @dictBuffer,
+ * in which case the only thing that it transports is the @compressionLevel.
+ * This can be useful in a pipeline featuring ZSTD_compress_usingCDict() exclusively,
+ * expecting a ZSTD_CDict parameter with any data, including those without a known dictionary. */
+ZSTDLIB_API ZSTD_CDict* ZSTD_createCDict(const void* dictBuffer, size_t dictSize,
+ int compressionLevel);
-/***************************************
-* Advanced compression API
-***************************************/
+/*! ZSTD_freeCDict() :
+ * Function frees memory allocated by ZSTD_createCDict().
+ * If a NULL pointer is passed, no operation is performed. */
+ZSTDLIB_API size_t ZSTD_freeCDict(ZSTD_CDict* CDict);
-/* API design :
- * Parameters are pushed one by one into an existing context,
- * using ZSTD_CCtx_set*() functions.
- * Pushed parameters are sticky : they are valid for next compressed frame, and any subsequent frame.
- * "sticky" parameters are applicable to `ZSTD_compress2()` and `ZSTD_compressStream*()` !
- * They do not apply to "simple" one-shot variants such as ZSTD_compressCCtx()
- *
- * It's possible to reset all parameters to "default" using ZSTD_CCtx_reset().
- *
- * This API supercedes all other "advanced" API entry points in the experimental section.
- * In the future, we expect to remove from experimental API entry points which are redundant with this API.
- */
+/*! ZSTD_compress_usingCDict() :
+ * Compression using a digested Dictionary.
+ * Recommended when same dictionary is used multiple times.
+ * Note : compression level is _decided at dictionary creation time_,
+ * and frame parameters are hardcoded (dictID=yes, contentSize=yes, checksum=no) */
+ZSTDLIB_API size_t ZSTD_compress_usingCDict(ZSTD_CCtx* cctx,
+ void* dst, size_t dstCapacity,
+ const void* src, size_t srcSize,
+ const ZSTD_CDict* cdict);
-/* Compression strategies, listed from fastest to strongest */
-typedef enum { ZSTD_fast=1,
- ZSTD_dfast=2,
- ZSTD_greedy=3,
- ZSTD_lazy=4,
- ZSTD_lazy2=5,
- ZSTD_btlazy2=6,
- ZSTD_btopt=7,
- ZSTD_btultra=8,
- ZSTD_btultra2=9
- /* note : new strategies _might_ be added in the future.
- Only the order (from fast to strong) is guaranteed */
-} ZSTD_strategy;
+typedef struct ZSTD_DDict_s ZSTD_DDict;
+/*! ZSTD_createDDict() :
+ * Create a digested dictionary, ready to start decompression operation without startup delay.
+ * dictBuffer can be released after DDict creation, as its content is copied inside DDict. */
+ZSTDLIB_API ZSTD_DDict* ZSTD_createDDict(const void* dictBuffer, size_t dictSize);
-typedef enum {
+/*! ZSTD_freeDDict() :
+ * Function frees memory allocated with ZSTD_createDDict()
+ * If a NULL pointer is passed, no operation is performed. */
+ZSTDLIB_API size_t ZSTD_freeDDict(ZSTD_DDict* ddict);
- /* compression parameters */
- ZSTD_c_compressionLevel=100, /* Update all compression parameters according to pre-defined cLevel table
- * Default level is ZSTD_CLEVEL_DEFAULT==3.
- * Special: value 0 means default, which is controlled by ZSTD_CLEVEL_DEFAULT.
- * Note 1 : it's possible to pass a negative compression level.
- * Note 2 : setting a level sets all default values of other compression parameters */
- ZSTD_c_windowLog=101, /* Maximum allowed back-reference distance, expressed as power of 2.
- * Must be clamped between ZSTD_WINDOWLOG_MIN and ZSTD_WINDOWLOG_MAX.
- * Special: value 0 means "use default windowLog".
- * Note: Using a windowLog greater than ZSTD_WINDOWLOG_LIMIT_DEFAULT
- * requires explicitly allowing such window size at decompression stage if using streaming. */
- ZSTD_c_hashLog=102, /* Size of the initial probe table, as a power of 2.
- * Resulting memory usage is (1 << (hashLog+2)).
- * Must be clamped between ZSTD_HASHLOG_MIN and ZSTD_HASHLOG_MAX.
- * Larger tables improve compression ratio of strategies <= dFast,
- * and improve speed of strategies > dFast.
- * Special: value 0 means "use default hashLog". */
- ZSTD_c_chainLog=103, /* Size of the multi-probe search table, as a power of 2.
- * Resulting memory usage is (1 << (chainLog+2)).
- * Must be clamped between ZSTD_CHAINLOG_MIN and ZSTD_CHAINLOG_MAX.
- * Larger tables result in better and slower compression.
- * This parameter is useless when using "fast" strategy.
- * It's still useful when using "dfast" strategy,
- * in which case it defines a secondary probe table.
- * Special: value 0 means "use default chainLog". */
- ZSTD_c_searchLog=104, /* Number of search attempts, as a power of 2.
- * More attempts result in better and slower compression.
- * This parameter is useless when using "fast" and "dFast" strategies.
- * Special: value 0 means "use default searchLog". */
- ZSTD_c_minMatch=105, /* Minimum size of searched matches.
- * Note that Zstandard can still find matches of smaller size,
- * it just tweaks its search algorithm to look for this size and larger.
- * Larger values increase compression and decompression speed, but decrease ratio.
- * Must be clamped between ZSTD_MINMATCH_MIN and ZSTD_MINMATCH_MAX.
- * Note that currently, for all strategies < btopt, effective minimum is 4.
- * , for all strategies > fast, effective maximum is 6.
- * Special: value 0 means "use default minMatchLength". */
- ZSTD_c_targetLength=106, /* Impact of this field depends on strategy.
- * For strategies btopt, btultra & btultra2:
- * Length of Match considered "good enough" to stop search.
- * Larger values make compression stronger, and slower.
- * For strategy fast:
- * Distance between match sampling.
- * Larger values make compression faster, and weaker.
- * Special: value 0 means "use default targetLength". */
- ZSTD_c_strategy=107, /* See ZSTD_strategy enum definition.
- * The higher the value of selected strategy, the more complex it is,
- * resulting in stronger and slower compression.
- * Special: value 0 means "use default strategy". */
+/*! ZSTD_decompress_usingDDict() :
+ * Decompression using a digested Dictionary.
+ * Recommended when same dictionary is used multiple times. */
+ZSTDLIB_API size_t ZSTD_decompress_usingDDict(ZSTD_DCtx* dctx,
+ void* dst, size_t dstCapacity,
+ const void* src, size_t srcSize,
+ const ZSTD_DDict* ddict);
- /* LDM mode parameters */
- ZSTD_c_enableLongDistanceMatching=160, /* Enable long distance matching.
- * This parameter is designed to improve compression ratio
- * for large inputs, by finding large matches at long distance.
- * It increases memory usage and window size.
- * Note: enabling this parameter increases default ZSTD_c_windowLog to 128 MB
- * except when expressly set to a different value. */
- ZSTD_c_ldmHashLog=161, /* Size of the table for long distance matching, as a power of 2.
- * Larger values increase memory usage and compression ratio,
- * but decrease compression speed.
- * Must be clamped between ZSTD_HASHLOG_MIN and ZSTD_HASHLOG_MAX
- * default: windowlog - 7.
- * Special: value 0 means "automatically determine hashlog". */
- ZSTD_c_ldmMinMatch=162, /* Minimum match size for long distance matcher.
- * Larger/too small values usually decrease compression ratio.
- * Must be clamped between ZSTD_LDM_MINMATCH_MIN and ZSTD_LDM_MINMATCH_MAX.
- * Special: value 0 means "use default value" (default: 64). */
- ZSTD_c_ldmBucketSizeLog=163, /* Log size of each bucket in the LDM hash table for collision resolution.
- * Larger values improve collision resolution but decrease compression speed.
- * The maximum value is ZSTD_LDM_BUCKETSIZELOG_MAX.
- * Special: value 0 means "use default value" (default: 3). */
- ZSTD_c_ldmHashRateLog=164, /* Frequency of inserting/looking up entries into the LDM hash table.
- * Must be clamped between 0 and (ZSTD_WINDOWLOG_MAX - ZSTD_HASHLOG_MIN).
- * Default is MAX(0, (windowLog - ldmHashLog)), optimizing hash table usage.
- * Larger values improve compression speed.
- * Deviating far from default value will likely result in a compression ratio decrease.
- * Special: value 0 means "automatically determine hashRateLog". */
- /* frame parameters */
- ZSTD_c_contentSizeFlag=200, /* Content size will be written into frame header _whenever known_ (default:1)
- * Content size must be known at the beginning of compression.
- * This is automatically the case when using ZSTD_compress2(),
- * For streaming variants, content size must be provided with ZSTD_CCtx_setPledgedSrcSize() */
- ZSTD_c_checksumFlag=201, /* A 32-bits checksum of content is written at end of frame (default:0) */
- ZSTD_c_dictIDFlag=202, /* When applicable, dictionary's ID is written into frame header (default:1) */
+/********************************
+ * Dictionary helper functions
+ *******************************/
- /* multi-threading parameters */
- /* These parameters are only useful if multi-threading is enabled (compiled with build macro ZSTD_MULTITHREAD).
- * They return an error otherwise. */
- ZSTD_c_nbWorkers=400, /* Select how many threads will be spawned to compress in parallel.
- * When nbWorkers >= 1, triggers asynchronous mode when used with ZSTD_compressStream*() :
- * ZSTD_compressStream*() consumes input and flush output if possible, but immediately gives back control to caller,
- * while compression work is performed in parallel, within worker threads.
- * (note : a strong exception to this rule is when first invocation of ZSTD_compressStream2() sets ZSTD_e_end :
- * in which case, ZSTD_compressStream2() delegates to ZSTD_compress2(), which is always a blocking call).
- * More workers improve speed, but also increase memory usage.
- * Default value is `0`, aka "single-threaded mode" : no worker is spawned, compression is performed inside Caller's thread, all invocations are blocking */
- ZSTD_c_jobSize=401, /* Size of a compression job. This value is enforced only when nbWorkers >= 1.
- * Each compression job is completed in parallel, so this value can indirectly impact the nb of active threads.
- * 0 means default, which is dynamically determined based on compression parameters.
- * Job size must be a minimum of overlapSize, or 1 MB, whichever is largest.
- * The minimum size is automatically and transparently enforced */
- ZSTD_c_overlapSizeLog=402, /* Size of previous job reloaded at the beginning of each job, as a fraction of window size.
- * This value is enforced only when nbWorkers >= 1.
- * Larger values increase compression ratio, but decrease speed.
- * Values range from 0 (no overlap) to 9 (overlap a full windowSize).
- * Each rank (except 0) increase/decrease load size by a factor 2
- * 9: full window; 8: w/2; 7: w/4; 6: w/8; 5:w/16; 4: w/32; 3:w/64; 2:w/128; 1:w/256;
- * default value is 6 : use 1/8th of windowSize */
+/*! ZSTD_getDictID_fromDict() : Requires v1.4.0+
+ * Provides the dictID stored within dictionary.
+ * if @return == 0, the dictionary is not conformant with Zstandard specification.
+ * It can still be loaded, but as a content-only dictionary. */
+ZSTDLIB_API unsigned ZSTD_getDictID_fromDict(const void* dict, size_t dictSize);
- /* note : additional experimental parameters are also available
- * within the experimental section of the API.
- * At the time of this writing, they include :
- * ZSTD_c_rsyncable
- * ZSTD_c_format
- * ZSTD_c_forceMaxWindow
- * ZSTD_c_forceAttachDict
- * Because they are not stable, it's necessary to define ZSTD_STATIC_LINKING_ONLY to access them.
- * note : never ever use experimentalParam? names directly;
- * also, the enums values themselves are unstable and can still change.
- */
- ZSTD_c_experimentalParam1=500,
- ZSTD_c_experimentalParam2=10,
- ZSTD_c_experimentalParam3=1000,
- ZSTD_c_experimentalParam4=1001
-} ZSTD_cParameter;
+/*! ZSTD_getDictID_fromCDict() : Requires v1.5.0+
+ * Provides the dictID of the dictionary loaded into `cdict`.
+ * If @return == 0, the dictionary is not conformant to Zstandard specification, or empty.
+ * Non-conformant dictionaries can still be loaded, but as content-only dictionaries. */
+ZSTDLIB_API unsigned ZSTD_getDictID_fromCDict(const ZSTD_CDict* cdict);
+/*! ZSTD_getDictID_fromDDict() : Requires v1.4.0+
+ * Provides the dictID of the dictionary loaded into `ddict`.
+ * If @return == 0, the dictionary is not conformant to Zstandard specification, or empty.
+ * Non-conformant dictionaries can still be loaded, but as content-only dictionaries. */
+ZSTDLIB_API unsigned ZSTD_getDictID_fromDDict(const ZSTD_DDict* ddict);
-typedef struct {
- size_t error;
- int lowerBound;
- int upperBound;
-} ZSTD_bounds;
+/*! ZSTD_getDictID_fromFrame() : Requires v1.4.0+
+ * Provides the dictID required to decompressed the frame stored within `src`.
+ * If @return == 0, the dictID could not be decoded.
+ * This could for one of the following reasons :
+ * - The frame does not require a dictionary to be decoded (most common case).
+ * - The frame was built with dictID intentionally removed. Whatever dictionary is necessary is a hidden piece of information.
+ * Note : this use case also happens when using a non-conformant dictionary.
+ * - `srcSize` is too small, and as a result, the frame header could not be decoded (only possible if `srcSize < ZSTD_FRAMEHEADERSIZE_MAX`).
+ * - This is not a Zstandard frame.
+ * When identifying the exact failure cause, it's possible to use ZSTD_getFrameHeader(), which will provide a more precise error code. */
+ZSTDLIB_API unsigned ZSTD_getDictID_fromFrame(const void* src, size_t srcSize);
-/*! ZSTD_cParam_getBounds() :
- * All parameters must belong to an interval with lower and upper bounds,
- * otherwise they will either trigger an error or be automatically clamped.
- * @return : a structure, ZSTD_bounds, which contains
- * - an error status field, which must be tested using ZSTD_isError()
- * - lower and upper bounds, both inclusive
- */
-ZSTDLIB_API ZSTD_bounds ZSTD_cParam_getBounds(ZSTD_cParameter cParam);
-/*! ZSTD_CCtx_setParameter() :
- * Set one compression parameter, selected by enum ZSTD_cParameter.
- * All parameters have valid bounds. Bounds can be queried using ZSTD_cParam_getBounds().
- * Providing a value beyond bound will either clamp it, or trigger an error (depending on parameter).
- * Setting a parameter is generally only possible during frame initialization (before starting compression).
- * Exception : when using multi-threading mode (nbWorkers >= 1),
- * the following parameters can be updated _during_ compression (within same frame):
- * => compressionLevel, hashLog, chainLog, searchLog, minMatch, targetLength and strategy.
- * new parameters will be active for next job only (after a flush()).
- * @return : an error code (which can be tested using ZSTD_isError()).
- */
-ZSTDLIB_API size_t ZSTD_CCtx_setParameter(ZSTD_CCtx* cctx, ZSTD_cParameter param, int value);
+/*******************************************************************************
+ * Advanced dictionary and prefix API (Requires v1.4.0+)
+ *
+ * This API allows dictionaries to be used with ZSTD_compress2(),
+ * ZSTD_compressStream2(), and ZSTD_decompressDCtx().
+ * Dictionaries are sticky, they remain valid when same context is re-used,
+ * they only reset when the context is reset
+ * with ZSTD_reset_parameters or ZSTD_reset_session_and_parameters.
+ * In contrast, Prefixes are single-use.
+ ******************************************************************************/
-/*! ZSTD_CCtx_setPledgedSrcSize() :
- * Total input data size to be compressed as a single frame.
- * Value will be written in frame header, unless if explicitly forbidden using ZSTD_c_contentSizeFlag.
- * This value will also be controlled at end of frame, and trigger an error if not respected.
- * @result : 0, or an error code (which can be tested with ZSTD_isError()).
- * Note 1 : pledgedSrcSize==0 actually means zero, aka an empty frame.
- * In order to mean "unknown content size", pass constant ZSTD_CONTENTSIZE_UNKNOWN.
- * ZSTD_CONTENTSIZE_UNKNOWN is default value for any new frame.
- * Note 2 : pledgedSrcSize is only valid once, for the next frame.
- * It's discarded at the end of the frame, and replaced by ZSTD_CONTENTSIZE_UNKNOWN.
- * Note 3 : Whenever all input data is provided and consumed in a single round,
- * for example with ZSTD_compress2(),
- * or invoking immediately ZSTD_compressStream2(,,,ZSTD_e_end),
- * this value is automatically overriden by srcSize instead.
- */
-ZSTDLIB_API size_t ZSTD_CCtx_setPledgedSrcSize(ZSTD_CCtx* cctx, unsigned long long pledgedSrcSize);
-/*! ZSTD_CCtx_loadDictionary() :
+/*! ZSTD_CCtx_loadDictionary() : Requires v1.4.0+
* Create an internal CDict from `dict` buffer.
* Decompression will have to use same dictionary.
* @result : 0, or an error code (which can be tested with ZSTD_isError()).
* Special: Loading a NULL (or 0-size) dictionary invalidates previous dictionary,
* meaning "return to no-dictionary mode".
- * Note 1 : Dictionary is sticky, it will be used for all future compressed frames.
- * To return to "no-dictionary" situation, load a NULL dictionary (or reset parameters).
+ * Note 1 : Dictionary is sticky, it will be used for all future compressed frames,
+ * until parameters are reset, a new dictionary is loaded, or the dictionary
+ * is explicitly invalidated by loading a NULL dictionary.
* Note 2 : Loading a dictionary involves building tables.
* It's also a CPU consuming operation, with non-negligible impact on latency.
* Tables are dependent on compression parameters, and for this reason,
@@ -692,13 +1045,19 @@ ZSTDLIB_API size_t ZSTD_CCtx_setPledgedSrcSize(ZSTD_CCtx* cctx, unsigned long lo
* Use experimental ZSTD_CCtx_loadDictionary_byReference() to reference content instead.
* In such a case, dictionary buffer must outlive its users.
* Note 4 : Use ZSTD_CCtx_loadDictionary_advanced()
- * to precisely select how dictionary content must be interpreted. */
+ * to precisely select how dictionary content must be interpreted.
+ * Note 5 : This method does not benefit from LDM (long distance mode).
+ * If you want to employ LDM on some large dictionary content,
+ * prefer employing ZSTD_CCtx_refPrefix() described below.
+ */
ZSTDLIB_API size_t ZSTD_CCtx_loadDictionary(ZSTD_CCtx* cctx, const void* dict, size_t dictSize);
-/*! ZSTD_CCtx_refCDict() :
- * Reference a prepared dictionary, to be used for all next compressed frames.
+/*! ZSTD_CCtx_refCDict() : Requires v1.4.0+
+ * Reference a prepared dictionary, to be used for all future compressed frames.
* Note that compression parameters are enforced from within CDict,
- * and supercede any compression parameter previously set within CCtx.
+ * and supersede any compression parameter previously set within CCtx.
+ * The parameters ignored are labelled as "superseded-by-cdict" in the ZSTD_cParameter enum docs.
+ * The ignored parameters will be used again if the CCtx is returned to no-dictionary mode.
* The dictionary will remain valid for future compressed frames using same CCtx.
* @result : 0, or an error code (which can be tested with ZSTD_isError()).
* Special : Referencing a NULL CDict means "return to no-dictionary mode".
@@ -707,12 +1066,13 @@ ZSTDLIB_API size_t ZSTD_CCtx_loadDictionary(ZSTD_CCtx* cctx, const void* dict, s
* Note 2 : CDict is just referenced, its lifetime must outlive its usage within CCtx. */
ZSTDLIB_API size_t ZSTD_CCtx_refCDict(ZSTD_CCtx* cctx, const ZSTD_CDict* cdict);
-/*! ZSTD_CCtx_refPrefix() :
+/*! ZSTD_CCtx_refPrefix() : Requires v1.4.0+
* Reference a prefix (single-usage dictionary) for next compressed frame.
* A prefix is **only used once**. Tables are discarded at end of frame (ZSTD_e_end).
* Decompression will need same prefix to properly regenerate data.
* Compressing with a prefix is similar in outcome as performing a diff and compressing it,
* but performs much faster, especially during decompression (compression speed is tunable with compression level).
+ * This method is compatible with LDM (long distance mode).
* @result : 0, or an error code (which can be tested with ZSTD_isError()).
* Special: Adding any prefix (including NULL) invalidates any previous prefix or dictionary
* Note 1 : Prefix buffer is referenced. It **must** outlive compression.
@@ -723,145 +1083,15 @@ ZSTDLIB_API size_t ZSTD_CCtx_refCDict(ZSTD_CCtx* cctx, const ZSTD_CDict* cdict);
* Note 3 : Referencing a prefix involves building tables, which are dependent on compression parameters.
* It's a CPU consuming operation, with non-negligible impact on latency.
* If there is a need to use the same prefix multiple times, consider loadDictionary instead.
- * Note 4 : By default, the prefix is interpreted as raw content (ZSTD_dm_rawContent).
+ * Note 4 : By default, the prefix is interpreted as raw content (ZSTD_dct_rawContent).
* Use experimental ZSTD_CCtx_refPrefix_advanced() to alter dictionary interpretation. */
ZSTDLIB_API size_t ZSTD_CCtx_refPrefix(ZSTD_CCtx* cctx,
const void* prefix, size_t prefixSize);
-
-typedef enum {
- ZSTD_reset_session_only = 1,
- ZSTD_reset_parameters = 2,
- ZSTD_reset_session_and_parameters = 3
-} ZSTD_ResetDirective;
-
-/*! ZSTD_CCtx_reset() :
- * There are 2 different things that can be reset, independently or jointly :
- * - The session : will stop compressing current frame, and make CCtx ready to start a new one.
- * Useful after an error, or to interrupt any ongoing compression.
- * Any internal data not yet flushed is cancelled.
- * Compression parameters and dictionary remain unchanged.
- * They will be used to compress next frame.
- * Resetting session never fails.
- * - The parameters : changes all parameters back to "default".
- * This removes any reference to any dictionary too.
- * Parameters can only be changed between 2 sessions (i.e. no compression is currently ongoing)
- * otherwise the reset fails, and function returns an error value (which can be tested using ZSTD_isError())
- * - Both : similar to resetting the session, followed by resetting parameters.
- */
-ZSTDLIB_API size_t ZSTD_CCtx_reset(ZSTD_CCtx* cctx, ZSTD_ResetDirective reset);
-
-
-
-/*! ZSTD_compress2() :
- * Behave the same as ZSTD_compressCCtx(), but compression parameters are set using the advanced API.
- * ZSTD_compress2() always starts a new frame.
- * Should cctx hold data from a previously unfinished frame, everything about it is forgotten.
- * - Compression parameters are pushed into CCtx before starting compression, using ZSTD_CCtx_set*()
- * - The function is always blocking, returns when compression is completed.
- * Hint : compression runs faster if `dstCapacity` >= `ZSTD_compressBound(srcSize)`.
- * @return : compressed size written into `dst` (<= `dstCapacity),
- * or an error code if it fails (which can be tested using ZSTD_isError()).
- */
-ZSTDLIB_API size_t ZSTD_compress2( ZSTD_CCtx* cctx,
- void* dst, size_t dstCapacity,
- const void* src, size_t srcSize);
-
-typedef enum {
- ZSTD_e_continue=0, /* collect more data, encoder decides when to output compressed result, for optimal compression ratio */
- ZSTD_e_flush=1, /* flush any data provided so far,
- * it creates (at least) one new block, that can be decoded immediately on reception;
- * frame will continue: any future data can still reference previously compressed data, improving compression. */
- ZSTD_e_end=2 /* flush any remaining data _and_ close current frame.
- * note that frame is only closed after compressed data is fully flushed (return value == 0).
- * After that point, any additional data starts a new frame.
- * note : each frame is independent (does not reference any content from previous frame). */
-} ZSTD_EndDirective;
-
-/*! ZSTD_compressStream2() :
- * Behaves about the same as ZSTD_compressStream, with additional control on end directive.
- * - Compression parameters are pushed into CCtx before starting compression, using ZSTD_CCtx_set*()
- * - Compression parameters cannot be changed once compression is started (save a list of exceptions in multi-threading mode)
- * - outpot->pos must be <= dstCapacity, input->pos must be <= srcSize
- * - outpot->pos and input->pos will be updated. They are guaranteed to remain below their respective limit.
- * - When nbWorkers==0 (default), function is blocking : it completes its job before returning to caller.
- * - When nbWorkers>=1, function is non-blocking : it just acquires a copy of input, and distributes jobs to internal worker threads, flush whatever is available,
- * and then immediately returns, just indicating that there is some data remaining to be flushed.
- * The function nonetheless guarantees forward progress : it will return only after it reads or write at least 1+ byte.
- * - Exception : if the first call requests a ZSTD_e_end directive and provides enough dstCapacity, the function delegates to ZSTD_compress2() which is always blocking.
- * - @return provides a minimum amount of data remaining to be flushed from internal buffers
- * or an error code, which can be tested using ZSTD_isError().
- * if @return != 0, flush is not fully completed, there is still some data left within internal buffers.
- * This is useful for ZSTD_e_flush, since in this case more flushes are necessary to empty all buffers.
- * For ZSTD_e_end, @return == 0 when internal buffers are fully flushed and frame is completed.
- * - after a ZSTD_e_end directive, if internal buffer is not fully flushed (@return != 0),
- * only ZSTD_e_end or ZSTD_e_flush operations are allowed.
- * Before starting a new compression job, or changing compression parameters,
- * it is required to fully flush internal buffers.
- */
-ZSTDLIB_API size_t ZSTD_compressStream2( ZSTD_CCtx* cctx,
- ZSTD_outBuffer* output,
- ZSTD_inBuffer* input,
- ZSTD_EndDirective endOp);
-
-
-
-/* ============================== */
-/* Advanced decompression API */
-/* ============================== */
-
-/* The advanced API pushes parameters one by one into an existing DCtx context.
- * Parameters are sticky, and remain valid for all following frames
- * using the same DCtx context.
- * It's possible to reset parameters to default values using ZSTD_DCtx_reset().
- * Note : This API is compatible with existing ZSTD_decompressDCtx() and ZSTD_decompressStream().
- * Therefore, no new decompression function is necessary.
- */
-
-
-typedef enum {
-
- ZSTD_d_windowLogMax=100, /* Select a size limit (in power of 2) beyond which
- * the streaming API will refuse to allocate memory buffer
- * in order to protect the host from unreasonable memory requirements.
- * This parameter is only useful in streaming mode, since no internal buffer is allocated in single-pass mode.
- * By default, a decompression context accepts window sizes <= (1 << ZSTD_WINDOWLOG_LIMIT_DEFAULT) */
-
- /* note : additional experimental parameters are also available
- * within the experimental section of the API.
- * At the time of this writing, they include :
- * ZSTD_c_format
- * Because they are not stable, it's necessary to define ZSTD_STATIC_LINKING_ONLY to access them.
- * note : never ever use experimentalParam? names directly
- */
- ZSTD_d_experimentalParam1=1000
-
-} ZSTD_dParameter;
-
-
-/*! ZSTD_dParam_getBounds() :
- * All parameters must belong to an interval with lower and upper bounds,
- * otherwise they will either trigger an error or be automatically clamped.
- * @return : a structure, ZSTD_bounds, which contains
- * - an error status field, which must be tested using ZSTD_isError()
- * - both lower and upper bounds, inclusive
- */
-ZSTDLIB_API ZSTD_bounds ZSTD_dParam_getBounds(ZSTD_dParameter dParam);
-
-/*! ZSTD_DCtx_setParameter() :
- * Set one compression parameter, selected by enum ZSTD_dParameter.
- * All parameters have valid bounds. Bounds can be queried using ZSTD_dParam_getBounds().
- * Providing a value beyond bound will either clamp it, or trigger an error (depending on parameter).
- * Setting a parameter is only possible during frame initialization (before starting decompression).
- * @return : 0, or an error code (which can be tested using ZSTD_isError()).
- */
-ZSTDLIB_API size_t ZSTD_DCtx_setParameter(ZSTD_DCtx* dctx, ZSTD_dParameter param, int value);
-
-
-/*! ZSTD_DCtx_loadDictionary() :
- * Create an internal DDict from dict buffer,
- * to be used to decompress next frames.
- * The dictionary remains valid for all future frames, until explicitly invalidated.
+/*! ZSTD_DCtx_loadDictionary() : Requires v1.4.0+
+ * Create an internal DDict from dict buffer, to be used to decompress all future frames.
+ * The dictionary remains valid for all future frames, until explicitly invalidated, or
+ * a new dictionary is loaded.
* @result : 0, or an error code (which can be tested with ZSTD_isError()).
* Special : Adding a NULL (or 0-size) dictionary invalidates any previous dictionary,
* meaning "return to no-dictionary mode".
@@ -875,18 +1105,26 @@ ZSTDLIB_API size_t ZSTD_DCtx_setParameter(ZSTD_DCtx* dctx, ZSTD_dParameter param
*/
ZSTDLIB_API size_t ZSTD_DCtx_loadDictionary(ZSTD_DCtx* dctx, const void* dict, size_t dictSize);
-/*! ZSTD_DCtx_refDDict() :
+/*! ZSTD_DCtx_refDDict() : Requires v1.4.0+
* Reference a prepared dictionary, to be used to decompress next frames.
* The dictionary remains active for decompression of future frames using same DCtx.
+ *
+ * If called with ZSTD_d_refMultipleDDicts enabled, repeated calls of this function
+ * will store the DDict references in a table, and the DDict used for decompression
+ * will be determined at decompression time, as per the dict ID in the frame.
+ * The memory for the table is allocated on the first call to refDDict, and can be
+ * freed with ZSTD_freeDCtx().
+ *
+ * If called with ZSTD_d_refMultipleDDicts disabled (the default), only one dictionary
+ * will be managed, and referencing a dictionary effectively "discards" any previous one.
+ *
* @result : 0, or an error code (which can be tested with ZSTD_isError()).
- * Note 1 : Currently, only one dictionary can be managed.
- * Referencing a new dictionary effectively "discards" any previous one.
* Special: referencing a NULL DDict means "return to no-dictionary mode".
* Note 2 : DDict is just referenced, its lifetime must outlive its usage from DCtx.
*/
ZSTDLIB_API size_t ZSTD_DCtx_refDDict(ZSTD_DCtx* dctx, const ZSTD_DDict* ddict);
-/*! ZSTD_DCtx_refPrefix() :
+/*! ZSTD_DCtx_refPrefix() : Requires v1.4.0+
* Reference a prefix (single-usage dictionary) to decompress next frame.
* This is the reverse operation of ZSTD_CCtx_refPrefix(),
* and must use the same prefix as the one used during compression.
@@ -897,7 +1135,7 @@ ZSTDLIB_API size_t ZSTD_DCtx_refDDict(ZSTD_DCtx* dctx, const ZSTD_DDict* ddict);
* Note 2 : Prefix buffer is referenced. It **must** outlive decompression.
* Prefix buffer must remain unmodified up to the end of frame,
* reached when ZSTD_decompressStream() returns 0.
- * Note 3 : By default, the prefix is treated as raw content (ZSTD_dm_rawContent).
+ * Note 3 : By default, the prefix is treated as raw content (ZSTD_dct_rawContent).
* Use ZSTD_CCtx_refPrefix_advanced() to alter dictMode (Experimental section)
* Note 4 : Referencing a raw content prefix has almost no cpu nor memory cost.
* A full dictionary is more costly, as it requires building tables.
@@ -905,15 +1143,43 @@ ZSTDLIB_API size_t ZSTD_DCtx_refDDict(ZSTD_DCtx* dctx, const ZSTD_DDict* ddict);
ZSTDLIB_API size_t ZSTD_DCtx_refPrefix(ZSTD_DCtx* dctx,
const void* prefix, size_t prefixSize);
-/*! ZSTD_DCtx_reset() :
- * Return a DCtx to clean state.
- * Session and parameters can be reset jointly or separately.
- * Parameters can only be reset when no active frame is being decompressed.
- * @return : 0, or an error code, which can be tested with ZSTD_isError()
- */
-ZSTDLIB_API size_t ZSTD_DCtx_reset(ZSTD_DCtx* dctx, ZSTD_ResetDirective reset);
+/* === Memory management === */
+
+/*! ZSTD_sizeof_*() : Requires v1.4.0+
+ * These functions give the _current_ memory usage of selected object.
+ * Note that object memory usage can evolve (increase or decrease) over time. */
+ZSTDLIB_API size_t ZSTD_sizeof_CCtx(const ZSTD_CCtx* cctx);
+ZSTDLIB_API size_t ZSTD_sizeof_DCtx(const ZSTD_DCtx* dctx);
+ZSTDLIB_API size_t ZSTD_sizeof_CStream(const ZSTD_CStream* zcs);
+ZSTDLIB_API size_t ZSTD_sizeof_DStream(const ZSTD_DStream* zds);
+ZSTDLIB_API size_t ZSTD_sizeof_CDict(const ZSTD_CDict* cdict);
+ZSTDLIB_API size_t ZSTD_sizeof_DDict(const ZSTD_DDict* ddict);
+
+#endif /* ZSTD_H_235446 */
+
+
+/* **************************************************************************************
+ * ADVANCED AND EXPERIMENTAL FUNCTIONS
+ ****************************************************************************************
+ * The definitions in the following section are considered experimental.
+ * They are provided for advanced scenarios.
+ * They should never be used with a dynamic library, as prototypes may change in the future.
+ * Use them only in association with static linking.
+ * ***************************************************************************************/
+#if defined(ZSTD_STATIC_LINKING_ONLY) && !defined(ZSTD_H_ZSTD_STATIC_LINKING_ONLY)
+#define ZSTD_H_ZSTD_STATIC_LINKING_ONLY
+/* This can be overridden externally to hide static symbols. */
+#ifndef ZSTDLIB_STATIC_API
+# if defined(ZSTD_DLL_EXPORT) && (ZSTD_DLL_EXPORT==1)
+# define ZSTDLIB_STATIC_API __declspec(dllexport) ZSTDLIB_VISIBLE
+# elif defined(ZSTD_DLL_IMPORT) && (ZSTD_DLL_IMPORT==1)
+# define ZSTDLIB_STATIC_API __declspec(dllimport) ZSTDLIB_VISIBLE
+# else
+# define ZSTDLIB_STATIC_API ZSTDLIB_VISIBLE
+# endif
+#endif
/****************************************************************************************
* experimental API (static linking only)
@@ -925,8 +1191,8 @@ ZSTDLIB_API size_t ZSTD_DCtx_reset(ZSTD_DCtx* dctx, ZSTD_ResetDirective reset);
* Some of them might be removed in the future (especially when redundant with existing stable functions)
* ***************************************************************************************/
-#define ZSTD_FRAMEHEADERSIZE_PREFIX 5 /* minimum input size required to query frame header size */
-#define ZSTD_FRAMEHEADERSIZE_MIN 6
+#define ZSTD_FRAMEHEADERSIZE_PREFIX(format) ((format) == ZSTD_f_zstd1 ? 5 : 1) /* minimum input size required to query frame header size */
+#define ZSTD_FRAMEHEADERSIZE_MIN(format) ((format) == ZSTD_f_zstd1 ? 6 : 2)
#define ZSTD_FRAMEHEADERSIZE_MAX 18 /* can be useful for static allocation */
#define ZSTD_SKIPPABLEHEADERSIZE 8
@@ -949,6 +1215,7 @@ ZSTDLIB_API size_t ZSTD_DCtx_reset(ZSTD_DCtx* dctx, ZSTD_ResetDirective reset);
#define ZSTD_TARGETLENGTH_MIN 0 /* note : comparing this constant to an unsigned results in a tautological test */
#define ZSTD_STRATEGY_MIN ZSTD_fast
#define ZSTD_STRATEGY_MAX ZSTD_btultra2
+#define ZSTD_BLOCKSIZE_MAX_MIN (1 << 10) /* The minimum valid max blocksize. Maximum blocksizes smaller than this make compressBound() inaccurate. */
#define ZSTD_OVERLAPLOG_MIN 0
@@ -957,7 +1224,7 @@ ZSTDLIB_API size_t ZSTD_DCtx_reset(ZSTD_DCtx* dctx, ZSTD_ResetDirective reset);
#define ZSTD_WINDOWLOG_LIMIT_DEFAULT 27 /* by default, the streaming decoder will refuse any frame
* requiring larger than (1< 0:
+ * If litLength != 0:
+ * rep == 1 --> offset == repeat_offset_1
+ * rep == 2 --> offset == repeat_offset_2
+ * rep == 3 --> offset == repeat_offset_3
+ * If litLength == 0:
+ * rep == 1 --> offset == repeat_offset_2
+ * rep == 2 --> offset == repeat_offset_3
+ * rep == 3 --> offset == repeat_offset_1 - 1
+ *
+ * Note: This field is optional. ZSTD_generateSequences() will calculate the value of
+ * 'rep', but repeat offsets do not necessarily need to be calculated from an external
+ * sequence provider's perspective. For example, ZSTD_compressSequences() does not
+ * use this 'rep' field at all (as of now).
+ */
+} ZSTD_Sequence;
+
typedef struct {
unsigned windowLog; /**< largest match distance : larger == more compression, more memory needed during decompression */
unsigned chainLog; /**< fully searched segment : larger == more compression, slower, more memory (useless for fast) */
@@ -1008,32 +1315,35 @@ typedef enum {
typedef enum {
ZSTD_dlm_byCopy = 0, /**< Copy dictionary content internally */
- ZSTD_dlm_byRef = 1, /**< Reference dictionary content -- the dictionary buffer must outlive its users. */
+ ZSTD_dlm_byRef = 1 /**< Reference dictionary content -- the dictionary buffer must outlive its users. */
} ZSTD_dictLoadMethod_e;
typedef enum {
- /* Opened question : should we have a format ZSTD_f_auto ?
- * Today, it would mean exactly the same as ZSTD_f_zstd1.
- * But, in the future, should several formats become supported,
- * on the compression side, it would mean "default format".
- * On the decompression side, it would mean "automatic format detection",
- * so that ZSTD_f_zstd1 would mean "accept *only* zstd frames".
- * Since meaning is a little different, another option could be to define different enums for compression and decompression.
- * This question could be kept for later, when there are actually multiple formats to support,
- * but there is also the question of pinning enum values, and pinning value `0` is especially important */
ZSTD_f_zstd1 = 0, /* zstd frame format, specified in zstd_compression_format.md (default) */
- ZSTD_f_zstd1_magicless = 1, /* Variant of zstd frame format, without initial 4-bytes magic number.
+ ZSTD_f_zstd1_magicless = 1 /* Variant of zstd frame format, without initial 4-bytes magic number.
* Useful to save 4 bytes per generated frame.
* Decoder cannot recognise automatically this format, requiring this instruction. */
} ZSTD_format_e;
+typedef enum {
+ /* Note: this enum controls ZSTD_d_forceIgnoreChecksum */
+ ZSTD_d_validateChecksum = 0,
+ ZSTD_d_ignoreChecksum = 1
+} ZSTD_forceIgnoreChecksum_e;
+
+typedef enum {
+ /* Note: this enum controls ZSTD_d_refMultipleDDicts */
+ ZSTD_rmd_refSingleDDict = 0,
+ ZSTD_rmd_refMultipleDDicts = 1
+} ZSTD_refMultipleDDicts_e;
+
typedef enum {
/* Note: this enum and the behavior it controls are effectively internal
* implementation details of the compressor. They are expected to continue
* to evolve and should be considered only in the context of extremely
* advanced performance tuning.
*
- * Zstd currently supports the use of a CDict in two ways:
+ * Zstd currently supports the use of a CDict in three ways:
*
* - The contents of the CDict can be copied into the working context. This
* means that the compression can search both the dictionary and input
@@ -1049,6 +1359,12 @@ typedef enum {
* working context's tables can be reused). For small inputs, this can be
* faster than copying the CDict's tables.
*
+ * - The CDict's tables are not used at all, and instead we use the working
+ * context alone to reload the dictionary and use params based on the source
+ * size. See ZSTD_compress_insertDictionary() and ZSTD_compress_usingDict().
+ * This method is effective when the dictionary sizes are very small relative
+ * to the input size, and the input size is fairly large to begin with.
+ *
* Zstd has a simple internal heuristic that selects which strategy to use
* at the beginning of a compression. However, if experimentation shows that
* Zstd is making poor choices, it is possible to override that choice with
@@ -1057,41 +1373,264 @@ typedef enum {
ZSTD_dictDefaultAttach = 0, /* Use the default heuristic. */
ZSTD_dictForceAttach = 1, /* Never copy the dictionary. */
ZSTD_dictForceCopy = 2, /* Always copy the dictionary. */
+ ZSTD_dictForceLoad = 3 /* Always reload the dictionary */
} ZSTD_dictAttachPref_e;
+typedef enum {
+ ZSTD_lcm_auto = 0, /**< Automatically determine the compression mode based on the compression level.
+ * Negative compression levels will be uncompressed, and positive compression
+ * levels will be compressed. */
+ ZSTD_lcm_huffman = 1, /**< Always attempt Huffman compression. Uncompressed literals will still be
+ * emitted if Huffman compression is not profitable. */
+ ZSTD_lcm_uncompressed = 2 /**< Always emit uncompressed literals. */
+} ZSTD_literalCompressionMode_e;
+
+typedef enum {
+ /* Note: This enum controls features which are conditionally beneficial. Zstd typically will make a final
+ * decision on whether or not to enable the feature (ZSTD_ps_auto), but setting the switch to ZSTD_ps_enable
+ * or ZSTD_ps_disable allow for a force enable/disable the feature.
+ */
+ ZSTD_ps_auto = 0, /* Let the library automatically determine whether the feature shall be enabled */
+ ZSTD_ps_enable = 1, /* Force-enable the feature */
+ ZSTD_ps_disable = 2 /* Do not use the feature */
+} ZSTD_paramSwitch_e;
+
+/***************************************
+* Frame header and size functions
+***************************************/
+
+/*! ZSTD_findDecompressedSize() :
+ * `src` should point to the start of a series of ZSTD encoded and/or skippable frames
+ * `srcSize` must be the _exact_ size of this series
+ * (i.e. there should be a frame boundary at `src + srcSize`)
+ * @return : - decompressed size of all data in all successive frames
+ * - if the decompressed size cannot be determined: ZSTD_CONTENTSIZE_UNKNOWN
+ * - if an error occurred: ZSTD_CONTENTSIZE_ERROR
+ *
+ * note 1 : decompressed size is an optional field, that may not be present, especially in streaming mode.
+ * When `return==ZSTD_CONTENTSIZE_UNKNOWN`, data to decompress could be any size.
+ * In which case, it's necessary to use streaming mode to decompress data.
+ * note 2 : decompressed size is always present when compression is done with ZSTD_compress()
+ * note 3 : decompressed size can be very large (64-bits value),
+ * potentially larger than what local system can handle as a single memory segment.
+ * In which case, it's necessary to use streaming mode to decompress data.
+ * note 4 : If source is untrusted, decompressed size could be wrong or intentionally modified.
+ * Always ensure result fits within application's authorized limits.
+ * Each application can set its own limits.
+ * note 5 : ZSTD_findDecompressedSize handles multiple frames, and so it must traverse the input to
+ * read each contained frame header. This is fast as most of the data is skipped,
+ * however it does mean that all frame data must be present and valid. */
+ZSTDLIB_STATIC_API unsigned long long ZSTD_findDecompressedSize(const void* src, size_t srcSize);
+
+/*! ZSTD_decompressBound() :
+ * `src` should point to the start of a series of ZSTD encoded and/or skippable frames
+ * `srcSize` must be the _exact_ size of this series
+ * (i.e. there should be a frame boundary at `src + srcSize`)
+ * @return : - upper-bound for the decompressed size of all data in all successive frames
+ * - if an error occurred: ZSTD_CONTENTSIZE_ERROR
+ *
+ * note 1 : an error can occur if `src` contains an invalid or incorrectly formatted frame.
+ * note 2 : the upper-bound is exact when the decompressed size field is available in every ZSTD encoded frame of `src`.
+ * in this case, `ZSTD_findDecompressedSize` and `ZSTD_decompressBound` return the same value.
+ * note 3 : when the decompressed size field isn't available, the upper-bound for that frame is calculated by:
+ * upper-bound = # blocks * min(128 KB, Window_Size)
+ */
+ZSTDLIB_STATIC_API unsigned long long ZSTD_decompressBound(const void* src, size_t srcSize);
+
+/*! ZSTD_frameHeaderSize() :
+ * srcSize must be >= ZSTD_FRAMEHEADERSIZE_PREFIX.
+ * @return : size of the Frame Header,
+ * or an error code (if srcSize is too small) */
+ZSTDLIB_STATIC_API size_t ZSTD_frameHeaderSize(const void* src, size_t srcSize);
+
+typedef enum { ZSTD_frame, ZSTD_skippableFrame } ZSTD_frameType_e;
+typedef struct {
+ unsigned long long frameContentSize; /* if == ZSTD_CONTENTSIZE_UNKNOWN, it means this field is not available. 0 means "empty" */
+ unsigned long long windowSize; /* can be very large, up to <= frameContentSize */
+ unsigned blockSizeMax;
+ ZSTD_frameType_e frameType; /* if == ZSTD_skippableFrame, frameContentSize is the size of skippable content */
+ unsigned headerSize;
+ unsigned dictID;
+ unsigned checksumFlag;
+ unsigned _reserved1;
+ unsigned _reserved2;
+} ZSTD_frameHeader;
+
+/*! ZSTD_getFrameHeader() :
+ * decode Frame Header, or requires larger `srcSize`.
+ * @return : 0, `zfhPtr` is correctly filled,
+ * >0, `srcSize` is too small, value is wanted `srcSize` amount,
+ * or an error code, which can be tested using ZSTD_isError() */
+ZSTDLIB_STATIC_API size_t ZSTD_getFrameHeader(ZSTD_frameHeader* zfhPtr, const void* src, size_t srcSize); /**< doesn't consume input */
+/*! ZSTD_getFrameHeader_advanced() :
+ * same as ZSTD_getFrameHeader(),
+ * with added capability to select a format (like ZSTD_f_zstd1_magicless) */
+ZSTDLIB_STATIC_API size_t ZSTD_getFrameHeader_advanced(ZSTD_frameHeader* zfhPtr, const void* src, size_t srcSize, ZSTD_format_e format);
+
+/*! ZSTD_decompressionMargin() :
+ * Zstd supports in-place decompression, where the input and output buffers overlap.
+ * In this case, the output buffer must be at least (Margin + Output_Size) bytes large,
+ * and the input buffer must be at the end of the output buffer.
+ *
+ * _______________________ Output Buffer ________________________
+ * | |
+ * | ____ Input Buffer ____|
+ * | | |
+ * v v v
+ * |---------------------------------------|-----------|----------|
+ * ^ ^ ^
+ * |___________________ Output_Size ___________________|_ Margin _|
+ *
+ * NOTE: See also ZSTD_DECOMPRESSION_MARGIN().
+ * NOTE: This applies only to single-pass decompression through ZSTD_decompress() or
+ * ZSTD_decompressDCtx().
+ * NOTE: This function supports multi-frame input.
+ *
+ * @param src The compressed frame(s)
+ * @param srcSize The size of the compressed frame(s)
+ * @returns The decompression margin or an error that can be checked with ZSTD_isError().
+ */
+ZSTDLIB_STATIC_API size_t ZSTD_decompressionMargin(const void* src, size_t srcSize);
+
+/*! ZSTD_DECOMPRESS_MARGIN() :
+ * Similar to ZSTD_decompressionMargin(), but instead of computing the margin from
+ * the compressed frame, compute it from the original size and the blockSizeLog.
+ * See ZSTD_decompressionMargin() for details.
+ *
+ * WARNING: This macro does not support multi-frame input, the input must be a single
+ * zstd frame. If you need that support use the function, or implement it yourself.
+ *
+ * @param originalSize The original uncompressed size of the data.
+ * @param blockSize The block size == MIN(windowSize, ZSTD_BLOCKSIZE_MAX).
+ * Unless you explicitly set the windowLog smaller than
+ * ZSTD_BLOCKSIZELOG_MAX you can just use ZSTD_BLOCKSIZE_MAX.
+ */
+#define ZSTD_DECOMPRESSION_MARGIN(originalSize, blockSize) ((size_t)( \
+ ZSTD_FRAMEHEADERSIZE_MAX /* Frame header */ + \
+ 4 /* checksum */ + \
+ ((originalSize) == 0 ? 0 : 3 * (((originalSize) + (blockSize) - 1) / blockSize)) /* 3 bytes per block */ + \
+ (blockSize) /* One block of margin */ \
+ ))
+
+typedef enum {
+ ZSTD_sf_noBlockDelimiters = 0, /* Representation of ZSTD_Sequence has no block delimiters, sequences only */
+ ZSTD_sf_explicitBlockDelimiters = 1 /* Representation of ZSTD_Sequence contains explicit block delimiters */
+} ZSTD_sequenceFormat_e;
+
+/*! ZSTD_sequenceBound() :
+ * `srcSize` : size of the input buffer
+ * @return : upper-bound for the number of sequences that can be generated
+ * from a buffer of srcSize bytes
+ *
+ * note : returns number of sequences - to get bytes, multiply by sizeof(ZSTD_Sequence).
+ */
+ZSTDLIB_STATIC_API size_t ZSTD_sequenceBound(size_t srcSize);
+
+/*! ZSTD_generateSequences() :
+ * Generate sequences using ZSTD_compress2(), given a source buffer.
+ *
+ * Each block will end with a dummy sequence
+ * with offset == 0, matchLength == 0, and litLength == length of last literals.
+ * litLength may be == 0, and if so, then the sequence of (of: 0 ml: 0 ll: 0)
+ * simply acts as a block delimiter.
+ *
+ * @zc can be used to insert custom compression params.
+ * This function invokes ZSTD_compress2().
+ *
+ * The output of this function can be fed into ZSTD_compressSequences() with CCtx
+ * setting of ZSTD_c_blockDelimiters as ZSTD_sf_explicitBlockDelimiters
+ * @return : number of sequences generated
+ */
+
+ZSTDLIB_STATIC_API size_t
+ZSTD_generateSequences( ZSTD_CCtx* zc,
+ ZSTD_Sequence* outSeqs, size_t outSeqsSize,
+ const void* src, size_t srcSize);
+
+/*! ZSTD_mergeBlockDelimiters() :
+ * Given an array of ZSTD_Sequence, remove all sequences that represent block delimiters/last literals
+ * by merging them into the literals of the next sequence.
+ *
+ * As such, the final generated result has no explicit representation of block boundaries,
+ * and the final last literals segment is not represented in the sequences.
+ *
+ * The output of this function can be fed into ZSTD_compressSequences() with CCtx
+ * setting of ZSTD_c_blockDelimiters as ZSTD_sf_noBlockDelimiters
+ * @return : number of sequences left after merging
+ */
+ZSTDLIB_STATIC_API size_t ZSTD_mergeBlockDelimiters(ZSTD_Sequence* sequences, size_t seqsSize);
+
+/*! ZSTD_compressSequences() :
+ * Compress an array of ZSTD_Sequence, associated with @src buffer, into dst.
+ * @src contains the entire input (not just the literals).
+ * If @srcSize > sum(sequence.length), the remaining bytes are considered all literals
+ * If a dictionary is included, then the cctx should reference the dict. (see: ZSTD_CCtx_refCDict(), ZSTD_CCtx_loadDictionary(), etc.)
+ * The entire source is compressed into a single frame.
+ *
+ * The compression behavior changes based on cctx params. In particular:
+ * If ZSTD_c_blockDelimiters == ZSTD_sf_noBlockDelimiters, the array of ZSTD_Sequence is expected to contain
+ * no block delimiters (defined in ZSTD_Sequence). Block boundaries are roughly determined based on
+ * the block size derived from the cctx, and sequences may be split. This is the default setting.
+ *
+ * If ZSTD_c_blockDelimiters == ZSTD_sf_explicitBlockDelimiters, the array of ZSTD_Sequence is expected to contain
+ * block delimiters (defined in ZSTD_Sequence). Behavior is undefined if no block delimiters are provided.
+ *
+ * If ZSTD_c_validateSequences == 0, this function will blindly accept the sequences provided. Invalid sequences cause undefined
+ * behavior. If ZSTD_c_validateSequences == 1, then if sequence is invalid (see doc/zstd_compression_format.md for
+ * specifics regarding offset/matchlength requirements) then the function will bail out and return an error.
+ *
+ * In addition to the two adjustable experimental params, there are other important cctx params.
+ * - ZSTD_c_minMatch MUST be set as less than or equal to the smallest match generated by the match finder. It has a minimum value of ZSTD_MINMATCH_MIN.
+ * - ZSTD_c_compressionLevel accordingly adjusts the strength of the entropy coder, as it would in typical compression.
+ * - ZSTD_c_windowLog affects offset validation: this function will return an error at higher debug levels if a provided offset
+ * is larger than what the spec allows for a given window log and dictionary (if present). See: doc/zstd_compression_format.md
+ *
+ * Note: Repcodes are, as of now, always re-calculated within this function, so ZSTD_Sequence::rep is unused.
+ * Note 2: Once we integrate ability to ingest repcodes, the explicit block delims mode must respect those repcodes exactly,
+ * and cannot emit an RLE block that disagrees with the repcode history
+ * @return : final compressed size, or a ZSTD error code.
+ */
+ZSTDLIB_STATIC_API size_t
+ZSTD_compressSequences( ZSTD_CCtx* cctx, void* dst, size_t dstSize,
+ const ZSTD_Sequence* inSeqs, size_t inSeqsSize,
+ const void* src, size_t srcSize);
+
-/***************************************
-* Frame size functions
-***************************************/
+/*! ZSTD_writeSkippableFrame() :
+ * Generates a zstd skippable frame containing data given by src, and writes it to dst buffer.
+ *
+ * Skippable frames begin with a 4-byte magic number. There are 16 possible choices of magic number,
+ * ranging from ZSTD_MAGIC_SKIPPABLE_START to ZSTD_MAGIC_SKIPPABLE_START+15.
+ * As such, the parameter magicVariant controls the exact skippable frame magic number variant used, so
+ * the magic number used will be ZSTD_MAGIC_SKIPPABLE_START + magicVariant.
+ *
+ * Returns an error if destination buffer is not large enough, if the source size is not representable
+ * with a 4-byte unsigned int, or if the parameter magicVariant is greater than 15 (and therefore invalid).
+ *
+ * @return : number of bytes written or a ZSTD error.
+ */
+ZSTDLIB_STATIC_API size_t ZSTD_writeSkippableFrame(void* dst, size_t dstCapacity,
+ const void* src, size_t srcSize, unsigned magicVariant);
-/*! ZSTD_findDecompressedSize() :
- * `src` should point the start of a series of ZSTD encoded and/or skippable frames
- * `srcSize` must be the _exact_ size of this series
- * (i.e. there should be a frame boundary exactly at `srcSize` bytes after `src`)
- * @return : - decompressed size of all data in all successive frames
- * - if the decompressed size cannot be determined: ZSTD_CONTENTSIZE_UNKNOWN
- * - if an error occurred: ZSTD_CONTENTSIZE_ERROR
+/*! ZSTD_readSkippableFrame() :
+ * Retrieves a zstd skippable frame containing data given by src, and writes it to dst buffer.
*
- * note 1 : decompressed size is an optional field, that may not be present, especially in streaming mode.
- * When `return==ZSTD_CONTENTSIZE_UNKNOWN`, data to decompress could be any size.
- * In which case, it's necessary to use streaming mode to decompress data.
- * note 2 : decompressed size is always present when compression is done with ZSTD_compress()
- * note 3 : decompressed size can be very large (64-bits value),
- * potentially larger than what local system can handle as a single memory segment.
- * In which case, it's necessary to use streaming mode to decompress data.
- * note 4 : If source is untrusted, decompressed size could be wrong or intentionally modified.
- * Always ensure result fits within application's authorized limits.
- * Each application can set its own limits.
- * note 5 : ZSTD_findDecompressedSize handles multiple frames, and so it must traverse the input to
- * read each contained frame header. This is fast as most of the data is skipped,
- * however it does mean that all frame data must be present and valid. */
-ZSTDLIB_API unsigned long long ZSTD_findDecompressedSize(const void* src, size_t srcSize);
+ * The parameter magicVariant will receive the magicVariant that was supplied when the frame was written,
+ * i.e. magicNumber - ZSTD_MAGIC_SKIPPABLE_START. This can be NULL if the caller is not interested
+ * in the magicVariant.
+ *
+ * Returns an error if destination buffer is not large enough, or if the frame is not skippable.
+ *
+ * @return : number of bytes written or a ZSTD error.
+ */
+ZSTDLIB_API size_t ZSTD_readSkippableFrame(void* dst, size_t dstCapacity, unsigned* magicVariant,
+ const void* src, size_t srcSize);
+
+/*! ZSTD_isSkippableFrame() :
+ * Tells if the content of `buffer` starts with a valid Frame Identifier for a skippable frame.
+ */
+ZSTDLIB_API unsigned ZSTD_isSkippableFrame(const void* buffer, size_t size);
-/*! ZSTD_frameHeaderSize() :
- * srcSize must be >= ZSTD_FRAMEHEADERSIZE_PREFIX.
- * @return : size of the Frame Header,
- * or an error code (if srcSize is too small) */
-ZSTDLIB_API size_t ZSTD_frameHeaderSize(const void* src, size_t srcSize);
/***************************************
@@ -1101,44 +1640,66 @@ ZSTDLIB_API size_t ZSTD_frameHeaderSize(const void* src, size_t srcSize);
/*! ZSTD_estimate*() :
* These functions make it possible to estimate memory usage
* of a future {D,C}Ctx, before its creation.
- * ZSTD_estimateCCtxSize() will provide a budget large enough for any compression level up to selected one.
- * It will also consider src size to be arbitrarily "large", which is worst case.
- * If srcSize is known to always be small, ZSTD_estimateCCtxSize_usingCParams() can provide a tighter estimation.
- * ZSTD_estimateCCtxSize_usingCParams() can be used in tandem with ZSTD_getCParams() to create cParams from compressionLevel.
- * ZSTD_estimateCCtxSize_usingCCtxParams() can be used in tandem with ZSTD_CCtxParam_setParameter(). Only single-threaded compression is supported. This function will return an error code if ZSTD_c_nbWorkers is >= 1.
- * Note : CCtx size estimation is only correct for single-threaded compression. */
-ZSTDLIB_API size_t ZSTD_estimateCCtxSize(int compressionLevel);
-ZSTDLIB_API size_t ZSTD_estimateCCtxSize_usingCParams(ZSTD_compressionParameters cParams);
-ZSTDLIB_API size_t ZSTD_estimateCCtxSize_usingCCtxParams(const ZSTD_CCtx_params* params);
-ZSTDLIB_API size_t ZSTD_estimateDCtxSize(void);
+ *
+ * ZSTD_estimateCCtxSize() will provide a memory budget large enough
+ * for any compression level up to selected one.
+ * Note : Unlike ZSTD_estimateCStreamSize*(), this estimate
+ * does not include space for a window buffer.
+ * Therefore, the estimation is only guaranteed for single-shot compressions, not streaming.
+ * The estimate will assume the input may be arbitrarily large,
+ * which is the worst case.
+ *
+ * When srcSize can be bound by a known and rather "small" value,
+ * this fact can be used to provide a tighter estimation
+ * because the CCtx compression context will need less memory.
+ * This tighter estimation can be provided by more advanced functions
+ * ZSTD_estimateCCtxSize_usingCParams(), which can be used in tandem with ZSTD_getCParams(),
+ * and ZSTD_estimateCCtxSize_usingCCtxParams(), which can be used in tandem with ZSTD_CCtxParams_setParameter().
+ * Both can be used to estimate memory using custom compression parameters and arbitrary srcSize limits.
+ *
+ * Note : only single-threaded compression is supported.
+ * ZSTD_estimateCCtxSize_usingCCtxParams() will return an error code if ZSTD_c_nbWorkers is >= 1.
+ *
+ * Note 2 : ZSTD_estimateCCtxSize* functions are not compatible with the Block-Level Sequence Producer API at this time.
+ * Size estimates assume that no external sequence producer is registered.
+ */
+ZSTDLIB_STATIC_API size_t ZSTD_estimateCCtxSize(int compressionLevel);
+ZSTDLIB_STATIC_API size_t ZSTD_estimateCCtxSize_usingCParams(ZSTD_compressionParameters cParams);
+ZSTDLIB_STATIC_API size_t ZSTD_estimateCCtxSize_usingCCtxParams(const ZSTD_CCtx_params* params);
+ZSTDLIB_STATIC_API size_t ZSTD_estimateDCtxSize(void);
/*! ZSTD_estimateCStreamSize() :
* ZSTD_estimateCStreamSize() will provide a budget large enough for any compression level up to selected one.
* It will also consider src size to be arbitrarily "large", which is worst case.
* If srcSize is known to always be small, ZSTD_estimateCStreamSize_usingCParams() can provide a tighter estimation.
* ZSTD_estimateCStreamSize_usingCParams() can be used in tandem with ZSTD_getCParams() to create cParams from compressionLevel.
- * ZSTD_estimateCStreamSize_usingCCtxParams() can be used in tandem with ZSTD_CCtxParam_setParameter(). Only single-threaded compression is supported. This function will return an error code if ZSTD_c_nbWorkers is >= 1.
+ * ZSTD_estimateCStreamSize_usingCCtxParams() can be used in tandem with ZSTD_CCtxParams_setParameter(). Only single-threaded compression is supported. This function will return an error code if ZSTD_c_nbWorkers is >= 1.
* Note : CStream size estimation is only correct for single-threaded compression.
* ZSTD_DStream memory budget depends on window Size.
* This information can be passed manually, using ZSTD_estimateDStreamSize,
* or deducted from a valid frame Header, using ZSTD_estimateDStreamSize_fromFrame();
* Note : if streaming is init with function ZSTD_init?Stream_usingDict(),
* an internal ?Dict will be created, which additional size is not estimated here.
- * In this case, get total size by adding ZSTD_estimate?DictSize */
-ZSTDLIB_API size_t ZSTD_estimateCStreamSize(int compressionLevel);
-ZSTDLIB_API size_t ZSTD_estimateCStreamSize_usingCParams(ZSTD_compressionParameters cParams);
-ZSTDLIB_API size_t ZSTD_estimateCStreamSize_usingCCtxParams(const ZSTD_CCtx_params* params);
-ZSTDLIB_API size_t ZSTD_estimateDStreamSize(size_t windowSize);
-ZSTDLIB_API size_t ZSTD_estimateDStreamSize_fromFrame(const void* src, size_t srcSize);
+ * In this case, get total size by adding ZSTD_estimate?DictSize
+ * Note 2 : only single-threaded compression is supported.
+ * ZSTD_estimateCStreamSize_usingCCtxParams() will return an error code if ZSTD_c_nbWorkers is >= 1.
+ * Note 3 : ZSTD_estimateCStreamSize* functions are not compatible with the Block-Level Sequence Producer API at this time.
+ * Size estimates assume that no external sequence producer is registered.
+ */
+ZSTDLIB_STATIC_API size_t ZSTD_estimateCStreamSize(int compressionLevel);
+ZSTDLIB_STATIC_API size_t ZSTD_estimateCStreamSize_usingCParams(ZSTD_compressionParameters cParams);
+ZSTDLIB_STATIC_API size_t ZSTD_estimateCStreamSize_usingCCtxParams(const ZSTD_CCtx_params* params);
+ZSTDLIB_STATIC_API size_t ZSTD_estimateDStreamSize(size_t windowSize);
+ZSTDLIB_STATIC_API size_t ZSTD_estimateDStreamSize_fromFrame(const void* src, size_t srcSize);
/*! ZSTD_estimate?DictSize() :
* ZSTD_estimateCDictSize() will bet that src size is relatively "small", and content is copied, like ZSTD_createCDict().
* ZSTD_estimateCDictSize_advanced() makes it possible to control compression parameters precisely, like ZSTD_createCDict_advanced().
* Note : dictionaries created by reference (`ZSTD_dlm_byRef`) are logically smaller.
*/
-ZSTDLIB_API size_t ZSTD_estimateCDictSize(size_t dictSize, int compressionLevel);
-ZSTDLIB_API size_t ZSTD_estimateCDictSize_advanced(size_t dictSize, ZSTD_compressionParameters cParams, ZSTD_dictLoadMethod_e dictLoadMethod);
-ZSTDLIB_API size_t ZSTD_estimateDDictSize(size_t dictSize, ZSTD_dictLoadMethod_e dictLoadMethod);
+ZSTDLIB_STATIC_API size_t ZSTD_estimateCDictSize(size_t dictSize, int compressionLevel);
+ZSTDLIB_STATIC_API size_t ZSTD_estimateCDictSize_advanced(size_t dictSize, ZSTD_compressionParameters cParams, ZSTD_dictLoadMethod_e dictLoadMethod);
+ZSTDLIB_STATIC_API size_t ZSTD_estimateDDictSize(size_t dictSize, ZSTD_dictLoadMethod_e dictLoadMethod);
/*! ZSTD_initStatic*() :
* Initialize an object using a pre-allocated fixed-size buffer.
@@ -1161,20 +1722,20 @@ ZSTDLIB_API size_t ZSTD_estimateDDictSize(size_t dictSize, ZSTD_dictLoadMethod_e
* Limitation 2 : static cctx currently not compatible with multi-threading.
* Limitation 3 : static dctx is incompatible with legacy support.
*/
-ZSTDLIB_API ZSTD_CCtx* ZSTD_initStaticCCtx(void* workspace, size_t workspaceSize);
-ZSTDLIB_API ZSTD_CStream* ZSTD_initStaticCStream(void* workspace, size_t workspaceSize); /**< same as ZSTD_initStaticCCtx() */
+ZSTDLIB_STATIC_API ZSTD_CCtx* ZSTD_initStaticCCtx(void* workspace, size_t workspaceSize);
+ZSTDLIB_STATIC_API ZSTD_CStream* ZSTD_initStaticCStream(void* workspace, size_t workspaceSize); /**< same as ZSTD_initStaticCCtx() */
-ZSTDLIB_API ZSTD_DCtx* ZSTD_initStaticDCtx(void* workspace, size_t workspaceSize);
-ZSTDLIB_API ZSTD_DStream* ZSTD_initStaticDStream(void* workspace, size_t workspaceSize); /**< same as ZSTD_initStaticDCtx() */
+ZSTDLIB_STATIC_API ZSTD_DCtx* ZSTD_initStaticDCtx(void* workspace, size_t workspaceSize);
+ZSTDLIB_STATIC_API ZSTD_DStream* ZSTD_initStaticDStream(void* workspace, size_t workspaceSize); /**< same as ZSTD_initStaticDCtx() */
-ZSTDLIB_API const ZSTD_CDict* ZSTD_initStaticCDict(
+ZSTDLIB_STATIC_API const ZSTD_CDict* ZSTD_initStaticCDict(
void* workspace, size_t workspaceSize,
const void* dict, size_t dictSize,
ZSTD_dictLoadMethod_e dictLoadMethod,
ZSTD_dictContentType_e dictContentType,
ZSTD_compressionParameters cParams);
-ZSTDLIB_API const ZSTD_DDict* ZSTD_initStaticDDict(
+ZSTDLIB_STATIC_API const ZSTD_DDict* ZSTD_initStaticDDict(
void* workspace, size_t workspaceSize,
const void* dict, size_t dictSize,
ZSTD_dictLoadMethod_e dictLoadMethod,
@@ -1189,24 +1750,54 @@ ZSTDLIB_API const ZSTD_DDict* ZSTD_initStaticDDict(
typedef void* (*ZSTD_allocFunction) (void* opaque, size_t size);
typedef void (*ZSTD_freeFunction) (void* opaque, void* address);
typedef struct { ZSTD_allocFunction customAlloc; ZSTD_freeFunction customFree; void* opaque; } ZSTD_customMem;
-static ZSTD_customMem const ZSTD_defaultCMem = { NULL, NULL, NULL }; /**< this constant defers to stdlib's functions */
+static
+#ifdef __GNUC__
+__attribute__((__unused__))
+#endif
+ZSTD_customMem const ZSTD_defaultCMem = { NULL, NULL, NULL }; /**< this constant defers to stdlib's functions */
-ZSTDLIB_API ZSTD_CCtx* ZSTD_createCCtx_advanced(ZSTD_customMem customMem);
-ZSTDLIB_API ZSTD_CStream* ZSTD_createCStream_advanced(ZSTD_customMem customMem);
-ZSTDLIB_API ZSTD_DCtx* ZSTD_createDCtx_advanced(ZSTD_customMem customMem);
-ZSTDLIB_API ZSTD_DStream* ZSTD_createDStream_advanced(ZSTD_customMem customMem);
+ZSTDLIB_STATIC_API ZSTD_CCtx* ZSTD_createCCtx_advanced(ZSTD_customMem customMem);
+ZSTDLIB_STATIC_API ZSTD_CStream* ZSTD_createCStream_advanced(ZSTD_customMem customMem);
+ZSTDLIB_STATIC_API ZSTD_DCtx* ZSTD_createDCtx_advanced(ZSTD_customMem customMem);
+ZSTDLIB_STATIC_API ZSTD_DStream* ZSTD_createDStream_advanced(ZSTD_customMem customMem);
-ZSTDLIB_API ZSTD_CDict* ZSTD_createCDict_advanced(const void* dict, size_t dictSize,
+ZSTDLIB_STATIC_API ZSTD_CDict* ZSTD_createCDict_advanced(const void* dict, size_t dictSize,
ZSTD_dictLoadMethod_e dictLoadMethod,
ZSTD_dictContentType_e dictContentType,
ZSTD_compressionParameters cParams,
ZSTD_customMem customMem);
-ZSTDLIB_API ZSTD_DDict* ZSTD_createDDict_advanced(const void* dict, size_t dictSize,
- ZSTD_dictLoadMethod_e dictLoadMethod,
- ZSTD_dictContentType_e dictContentType,
- ZSTD_customMem customMem);
+/*! Thread pool :
+ * These prototypes make it possible to share a thread pool among multiple compression contexts.
+ * This can limit resources for applications with multiple threads where each one uses
+ * a threaded compression mode (via ZSTD_c_nbWorkers parameter).
+ * ZSTD_createThreadPool creates a new thread pool with a given number of threads.
+ * Note that the lifetime of such pool must exist while being used.
+ * ZSTD_CCtx_refThreadPool assigns a thread pool to a context (use NULL argument value
+ * to use an internal thread pool).
+ * ZSTD_freeThreadPool frees a thread pool, accepts NULL pointer.
+ */
+typedef struct POOL_ctx_s ZSTD_threadPool;
+ZSTDLIB_STATIC_API ZSTD_threadPool* ZSTD_createThreadPool(size_t numThreads);
+ZSTDLIB_STATIC_API void ZSTD_freeThreadPool (ZSTD_threadPool* pool); /* accept NULL pointer */
+ZSTDLIB_STATIC_API size_t ZSTD_CCtx_refThreadPool(ZSTD_CCtx* cctx, ZSTD_threadPool* pool);
+
+
+/*
+ * This API is temporary and is expected to change or disappear in the future!
+ */
+ZSTDLIB_STATIC_API ZSTD_CDict* ZSTD_createCDict_advanced2(
+ const void* dict, size_t dictSize,
+ ZSTD_dictLoadMethod_e dictLoadMethod,
+ ZSTD_dictContentType_e dictContentType,
+ const ZSTD_CCtx_params* cctxParams,
+ ZSTD_customMem customMem);
+ZSTDLIB_STATIC_API ZSTD_DDict* ZSTD_createDDict_advanced(
+ const void* dict, size_t dictSize,
+ ZSTD_dictLoadMethod_e dictLoadMethod,
+ ZSTD_dictContentType_e dictContentType,
+ ZSTD_customMem customMem);
/***************************************
@@ -1217,39 +1808,73 @@ ZSTDLIB_API ZSTD_DDict* ZSTD_createDDict_advanced(const void* dict, size_t dictS
* Create a digested dictionary for compression
* Dictionary content is just referenced, not duplicated.
* As a consequence, `dictBuffer` **must** outlive CDict,
- * and its content must remain unmodified throughout the lifetime of CDict. */
-ZSTDLIB_API ZSTD_CDict* ZSTD_createCDict_byReference(const void* dictBuffer, size_t dictSize, int compressionLevel);
+ * and its content must remain unmodified throughout the lifetime of CDict.
+ * note: equivalent to ZSTD_createCDict_advanced(), with dictLoadMethod==ZSTD_dlm_byRef */
+ZSTDLIB_STATIC_API ZSTD_CDict* ZSTD_createCDict_byReference(const void* dictBuffer, size_t dictSize, int compressionLevel);
/*! ZSTD_getCParams() :
-* @return ZSTD_compressionParameters structure for a selected compression level and estimated srcSize.
-* `estimatedSrcSize` value is optional, select 0 if not known */
-ZSTDLIB_API ZSTD_compressionParameters ZSTD_getCParams(int compressionLevel, unsigned long long estimatedSrcSize, size_t dictSize);
+ * @return ZSTD_compressionParameters structure for a selected compression level and estimated srcSize.
+ * `estimatedSrcSize` value is optional, select 0 if not known */
+ZSTDLIB_STATIC_API ZSTD_compressionParameters ZSTD_getCParams(int compressionLevel, unsigned long long estimatedSrcSize, size_t dictSize);
/*! ZSTD_getParams() :
-* same as ZSTD_getCParams(), but @return a full `ZSTD_parameters` object instead of sub-component `ZSTD_compressionParameters`.
-* All fields of `ZSTD_frameParameters` are set to default : contentSize=1, checksum=0, noDictID=0 */
-ZSTDLIB_API ZSTD_parameters ZSTD_getParams(int compressionLevel, unsigned long long estimatedSrcSize, size_t dictSize);
+ * same as ZSTD_getCParams(), but @return a full `ZSTD_parameters` object instead of sub-component `ZSTD_compressionParameters`.
+ * All fields of `ZSTD_frameParameters` are set to default : contentSize=1, checksum=0, noDictID=0 */
+ZSTDLIB_STATIC_API ZSTD_parameters ZSTD_getParams(int compressionLevel, unsigned long long estimatedSrcSize, size_t dictSize);
/*! ZSTD_checkCParams() :
-* Ensure param values remain within authorized range */
-ZSTDLIB_API size_t ZSTD_checkCParams(ZSTD_compressionParameters params);
+ * Ensure param values remain within authorized range.
+ * @return 0 on success, or an error code (can be checked with ZSTD_isError()) */
+ZSTDLIB_STATIC_API size_t ZSTD_checkCParams(ZSTD_compressionParameters params);
/*! ZSTD_adjustCParams() :
* optimize params for a given `srcSize` and `dictSize`.
- * both values are optional, select `0` if unknown. */
-ZSTDLIB_API ZSTD_compressionParameters ZSTD_adjustCParams(ZSTD_compressionParameters cPar, unsigned long long srcSize, size_t dictSize);
+ * `srcSize` can be unknown, in which case use ZSTD_CONTENTSIZE_UNKNOWN.
+ * `dictSize` must be `0` when there is no dictionary.
+ * cPar can be invalid : all parameters will be clamped within valid range in the @return struct.
+ * This function never fails (wide contract) */
+ZSTDLIB_STATIC_API ZSTD_compressionParameters ZSTD_adjustCParams(ZSTD_compressionParameters cPar, unsigned long long srcSize, size_t dictSize);
+
+/*! ZSTD_CCtx_setCParams() :
+ * Set all parameters provided within @p cparams into the working @p cctx.
+ * Note : if modifying parameters during compression (MT mode only),
+ * note that changes to the .windowLog parameter will be ignored.
+ * @return 0 on success, or an error code (can be checked with ZSTD_isError()).
+ * On failure, no parameters are updated.
+ */
+ZSTDLIB_STATIC_API size_t ZSTD_CCtx_setCParams(ZSTD_CCtx* cctx, ZSTD_compressionParameters cparams);
+
+/*! ZSTD_CCtx_setFParams() :
+ * Set all parameters provided within @p fparams into the working @p cctx.
+ * @return 0 on success, or an error code (can be checked with ZSTD_isError()).
+ */
+ZSTDLIB_STATIC_API size_t ZSTD_CCtx_setFParams(ZSTD_CCtx* cctx, ZSTD_frameParameters fparams);
+
+/*! ZSTD_CCtx_setParams() :
+ * Set all parameters provided within @p params into the working @p cctx.
+ * @return 0 on success, or an error code (can be checked with ZSTD_isError()).
+ */
+ZSTDLIB_STATIC_API size_t ZSTD_CCtx_setParams(ZSTD_CCtx* cctx, ZSTD_parameters params);
/*! ZSTD_compress_advanced() :
- * Same as ZSTD_compress_usingDict(), with fine-tune control over compression parameters (by structure) */
-ZSTDLIB_API size_t ZSTD_compress_advanced(ZSTD_CCtx* cctx,
- void* dst, size_t dstCapacity,
- const void* src, size_t srcSize,
- const void* dict,size_t dictSize,
- ZSTD_parameters params);
+ * Note : this function is now DEPRECATED.
+ * It can be replaced by ZSTD_compress2(), in combination with ZSTD_CCtx_setParameter() and other parameter setters.
+ * This prototype will generate compilation warnings. */
+ZSTD_DEPRECATED("use ZSTD_compress2")
+ZSTDLIB_STATIC_API
+size_t ZSTD_compress_advanced(ZSTD_CCtx* cctx,
+ void* dst, size_t dstCapacity,
+ const void* src, size_t srcSize,
+ const void* dict,size_t dictSize,
+ ZSTD_parameters params);
/*! ZSTD_compress_usingCDict_advanced() :
- * Same as ZSTD_compress_usingCDict(), with fine-tune control over frame parameters */
-ZSTDLIB_API size_t ZSTD_compress_usingCDict_advanced(ZSTD_CCtx* cctx,
+ * Note : this function is now DEPRECATED.
+ * It can be replaced by ZSTD_compress2(), in combination with ZSTD_CCtx_loadDictionary() and other parameter setters.
+ * This prototype will generate compilation warnings. */
+ZSTD_DEPRECATED("use ZSTD_compress2 with ZSTD_CCtx_loadDictionary")
+ZSTDLIB_STATIC_API
+size_t ZSTD_compress_usingCDict_advanced(ZSTD_CCtx* cctx,
void* dst, size_t dstCapacity,
const void* src, size_t srcSize,
const ZSTD_CDict* cdict,
@@ -1259,18 +1884,18 @@ ZSTDLIB_API size_t ZSTD_compress_usingCDict_advanced(ZSTD_CCtx* cctx,
/*! ZSTD_CCtx_loadDictionary_byReference() :
* Same as ZSTD_CCtx_loadDictionary(), but dictionary content is referenced, instead of being copied into CCtx.
* It saves some memory, but also requires that `dict` outlives its usage within `cctx` */
-ZSTDLIB_API size_t ZSTD_CCtx_loadDictionary_byReference(ZSTD_CCtx* cctx, const void* dict, size_t dictSize);
+ZSTDLIB_STATIC_API size_t ZSTD_CCtx_loadDictionary_byReference(ZSTD_CCtx* cctx, const void* dict, size_t dictSize);
/*! ZSTD_CCtx_loadDictionary_advanced() :
* Same as ZSTD_CCtx_loadDictionary(), but gives finer control over
* how to load the dictionary (by copy ? by reference ?)
* and how to interpret it (automatic ? force raw mode ? full mode only ?) */
-ZSTDLIB_API size_t ZSTD_CCtx_loadDictionary_advanced(ZSTD_CCtx* cctx, const void* dict, size_t dictSize, ZSTD_dictLoadMethod_e dictLoadMethod, ZSTD_dictContentType_e dictContentType);
+ZSTDLIB_STATIC_API size_t ZSTD_CCtx_loadDictionary_advanced(ZSTD_CCtx* cctx, const void* dict, size_t dictSize, ZSTD_dictLoadMethod_e dictLoadMethod, ZSTD_dictContentType_e dictContentType);
/*! ZSTD_CCtx_refPrefix_advanced() :
* Same as ZSTD_CCtx_refPrefix(), but gives finer control over
* how to interpret prefix content (automatic ? force raw mode (default) ? full mode only ?) */
-ZSTDLIB_API size_t ZSTD_CCtx_refPrefix_advanced(ZSTD_CCtx* cctx, const void* prefix, size_t prefixSize, ZSTD_dictContentType_e dictContentType);
+ZSTDLIB_STATIC_API size_t ZSTD_CCtx_refPrefix_advanced(ZSTD_CCtx* cctx, const void* prefix, size_t prefixSize, ZSTD_dictContentType_e dictContentType);
/* === experimental parameters === */
/* these parameters can be used with ZSTD_setParameter()
@@ -1309,65 +1934,343 @@ ZSTDLIB_API size_t ZSTD_CCtx_refPrefix_advanced(ZSTD_CCtx* cctx, const void* pre
* See the comments on that enum for an explanation of the feature. */
#define ZSTD_c_forceAttachDict ZSTD_c_experimentalParam4
+/* Controlled with ZSTD_paramSwitch_e enum.
+ * Default is ZSTD_ps_auto.
+ * Set to ZSTD_ps_disable to never compress literals.
+ * Set to ZSTD_ps_enable to always compress literals. (Note: uncompressed literals
+ * may still be emitted if huffman is not beneficial to use.)
+ *
+ * By default, in ZSTD_ps_auto, the library will decide at runtime whether to use
+ * literals compression based on the compression parameters - specifically,
+ * negative compression levels do not use literal compression.
+ */
+#define ZSTD_c_literalCompressionMode ZSTD_c_experimentalParam5
+
+/* Tries to fit compressed block size to be around targetCBlockSize.
+ * No target when targetCBlockSize == 0.
+ * There is no guarantee on compressed block size (default:0) */
+#define ZSTD_c_targetCBlockSize ZSTD_c_experimentalParam6
+
+/* User's best guess of source size.
+ * Hint is not valid when srcSizeHint == 0.
+ * There is no guarantee that hint is close to actual source size,
+ * but compression ratio may regress significantly if guess considerably underestimates */
+#define ZSTD_c_srcSizeHint ZSTD_c_experimentalParam7
+
+/* Controls whether the new and experimental "dedicated dictionary search
+ * structure" can be used. This feature is still rough around the edges, be
+ * prepared for surprising behavior!
+ *
+ * How to use it:
+ *
+ * When using a CDict, whether to use this feature or not is controlled at
+ * CDict creation, and it must be set in a CCtxParams set passed into that
+ * construction (via ZSTD_createCDict_advanced2()). A compression will then
+ * use the feature or not based on how the CDict was constructed; the value of
+ * this param, set in the CCtx, will have no effect.
+ *
+ * However, when a dictionary buffer is passed into a CCtx, such as via
+ * ZSTD_CCtx_loadDictionary(), this param can be set on the CCtx to control
+ * whether the CDict that is created internally can use the feature or not.
+ *
+ * What it does:
+ *
+ * Normally, the internal data structures of the CDict are analogous to what
+ * would be stored in a CCtx after compressing the contents of a dictionary.
+ * To an approximation, a compression using a dictionary can then use those
+ * data structures to simply continue what is effectively a streaming
+ * compression where the simulated compression of the dictionary left off.
+ * Which is to say, the search structures in the CDict are normally the same
+ * format as in the CCtx.
+ *
+ * It is possible to do better, since the CDict is not like a CCtx: the search
+ * structures are written once during CDict creation, and then are only read
+ * after that, while the search structures in the CCtx are both read and
+ * written as the compression goes along. This means we can choose a search
+ * structure for the dictionary that is read-optimized.
+ *
+ * This feature enables the use of that different structure.
+ *
+ * Note that some of the members of the ZSTD_compressionParameters struct have
+ * different semantics and constraints in the dedicated search structure. It is
+ * highly recommended that you simply set a compression level in the CCtxParams
+ * you pass into the CDict creation call, and avoid messing with the cParams
+ * directly.
+ *
+ * Effects:
+ *
+ * This will only have any effect when the selected ZSTD_strategy
+ * implementation supports this feature. Currently, that's limited to
+ * ZSTD_greedy, ZSTD_lazy, and ZSTD_lazy2.
+ *
+ * Note that this means that the CDict tables can no longer be copied into the
+ * CCtx, so the dict attachment mode ZSTD_dictForceCopy will no longer be
+ * usable. The dictionary can only be attached or reloaded.
+ *
+ * In general, you should expect compression to be faster--sometimes very much
+ * so--and CDict creation to be slightly slower. Eventually, we will probably
+ * make this mode the default.
+ */
+#define ZSTD_c_enableDedicatedDictSearch ZSTD_c_experimentalParam8
+
+/* ZSTD_c_stableInBuffer
+ * Experimental parameter.
+ * Default is 0 == disabled. Set to 1 to enable.
+ *
+ * Tells the compressor that input data presented with ZSTD_inBuffer
+ * will ALWAYS be the same between calls.
+ * Technically, the @src pointer must never be changed,
+ * and the @pos field can only be updated by zstd.
+ * However, it's possible to increase the @size field,
+ * allowing scenarios where more data can be appended after compressions starts.
+ * These conditions are checked by the compressor,
+ * and compression will fail if they are not respected.
+ * Also, data in the ZSTD_inBuffer within the range [src, src + pos)
+ * MUST not be modified during compression or it will result in data corruption.
+ *
+ * When this flag is enabled zstd won't allocate an input window buffer,
+ * because the user guarantees it can reference the ZSTD_inBuffer until
+ * the frame is complete. But, it will still allocate an output buffer
+ * large enough to fit a block (see ZSTD_c_stableOutBuffer). This will also
+ * avoid the memcpy() from the input buffer to the input window buffer.
+ *
+ * NOTE: So long as the ZSTD_inBuffer always points to valid memory, using
+ * this flag is ALWAYS memory safe, and will never access out-of-bounds
+ * memory. However, compression WILL fail if conditions are not respected.
+ *
+ * WARNING: The data in the ZSTD_inBuffer in the range [src, src + pos) MUST
+ * not be modified during compression or it will result in data corruption.
+ * This is because zstd needs to reference data in the ZSTD_inBuffer to find
+ * matches. Normally zstd maintains its own window buffer for this purpose,
+ * but passing this flag tells zstd to rely on user provided buffer instead.
+ */
+#define ZSTD_c_stableInBuffer ZSTD_c_experimentalParam9
+
+/* ZSTD_c_stableOutBuffer
+ * Experimental parameter.
+ * Default is 0 == disabled. Set to 1 to enable.
+ *
+ * Tells he compressor that the ZSTD_outBuffer will not be resized between
+ * calls. Specifically: (out.size - out.pos) will never grow. This gives the
+ * compressor the freedom to say: If the compressed data doesn't fit in the
+ * output buffer then return ZSTD_error_dstSizeTooSmall. This allows us to
+ * always decompress directly into the output buffer, instead of decompressing
+ * into an internal buffer and copying to the output buffer.
+ *
+ * When this flag is enabled zstd won't allocate an output buffer, because
+ * it can write directly to the ZSTD_outBuffer. It will still allocate the
+ * input window buffer (see ZSTD_c_stableInBuffer).
+ *
+ * Zstd will check that (out.size - out.pos) never grows and return an error
+ * if it does. While not strictly necessary, this should prevent surprises.
+ */
+#define ZSTD_c_stableOutBuffer ZSTD_c_experimentalParam10
+
+/* ZSTD_c_blockDelimiters
+ * Default is 0 == ZSTD_sf_noBlockDelimiters.
+ *
+ * For use with sequence compression API: ZSTD_compressSequences().
+ *
+ * Designates whether or not the given array of ZSTD_Sequence contains block delimiters
+ * and last literals, which are defined as sequences with offset == 0 and matchLength == 0.
+ * See the definition of ZSTD_Sequence for more specifics.
+ */
+#define ZSTD_c_blockDelimiters ZSTD_c_experimentalParam11
+
+/* ZSTD_c_validateSequences
+ * Default is 0 == disabled. Set to 1 to enable sequence validation.
+ *
+ * For use with sequence compression API: ZSTD_compressSequences().
+ * Designates whether or not we validate sequences provided to ZSTD_compressSequences()
+ * during function execution.
+ *
+ * Without validation, providing a sequence that does not conform to the zstd spec will cause
+ * undefined behavior, and may produce a corrupted block.
+ *
+ * With validation enabled, if sequence is invalid (see doc/zstd_compression_format.md for
+ * specifics regarding offset/matchlength requirements) then the function will bail out and
+ * return an error.
+ *
+ */
+#define ZSTD_c_validateSequences ZSTD_c_experimentalParam12
+
+/* ZSTD_c_useBlockSplitter
+ * Controlled with ZSTD_paramSwitch_e enum.
+ * Default is ZSTD_ps_auto.
+ * Set to ZSTD_ps_disable to never use block splitter.
+ * Set to ZSTD_ps_enable to always use block splitter.
+ *
+ * By default, in ZSTD_ps_auto, the library will decide at runtime whether to use
+ * block splitting based on the compression parameters.
+ */
+#define ZSTD_c_useBlockSplitter ZSTD_c_experimentalParam13
+
+/* ZSTD_c_useRowMatchFinder
+ * Controlled with ZSTD_paramSwitch_e enum.
+ * Default is ZSTD_ps_auto.
+ * Set to ZSTD_ps_disable to never use row-based matchfinder.
+ * Set to ZSTD_ps_enable to force usage of row-based matchfinder.
+ *
+ * By default, in ZSTD_ps_auto, the library will decide at runtime whether to use
+ * the row-based matchfinder based on support for SIMD instructions and the window log.
+ * Note that this only pertains to compression strategies: greedy, lazy, and lazy2
+ */
+#define ZSTD_c_useRowMatchFinder ZSTD_c_experimentalParam14
+
+/* ZSTD_c_deterministicRefPrefix
+ * Default is 0 == disabled. Set to 1 to enable.
+ *
+ * Zstd produces different results for prefix compression when the prefix is
+ * directly adjacent to the data about to be compressed vs. when it isn't.
+ * This is because zstd detects that the two buffers are contiguous and it can
+ * use a more efficient match finding algorithm. However, this produces different
+ * results than when the two buffers are non-contiguous. This flag forces zstd
+ * to always load the prefix in non-contiguous mode, even if it happens to be
+ * adjacent to the data, to guarantee determinism.
+ *
+ * If you really care about determinism when using a dictionary or prefix,
+ * like when doing delta compression, you should select this option. It comes
+ * at a speed penalty of about ~2.5% if the dictionary and data happened to be
+ * contiguous, and is free if they weren't contiguous. We don't expect that
+ * intentionally making the dictionary and data contiguous will be worth the
+ * cost to memcpy() the data.
+ */
+#define ZSTD_c_deterministicRefPrefix ZSTD_c_experimentalParam15
+
+/* ZSTD_c_prefetchCDictTables
+ * Controlled with ZSTD_paramSwitch_e enum. Default is ZSTD_ps_auto.
+ *
+ * In some situations, zstd uses CDict tables in-place rather than copying them
+ * into the working context. (See docs on ZSTD_dictAttachPref_e above for details).
+ * In such situations, compression speed is seriously impacted when CDict tables are
+ * "cold" (outside CPU cache). This parameter instructs zstd to prefetch CDict tables
+ * when they are used in-place.
+ *
+ * For sufficiently small inputs, the cost of the prefetch will outweigh the benefit.
+ * For sufficiently large inputs, zstd will by default memcpy() CDict tables
+ * into the working context, so there is no need to prefetch. This parameter is
+ * targeted at a middle range of input sizes, where a prefetch is cheap enough to be
+ * useful but memcpy() is too expensive. The exact range of input sizes where this
+ * makes sense is best determined by careful experimentation.
+ *
+ * Note: for this parameter, ZSTD_ps_auto is currently equivalent to ZSTD_ps_disable,
+ * but in the future zstd may conditionally enable this feature via an auto-detection
+ * heuristic for cold CDicts.
+ * Use ZSTD_ps_disable to opt out of prefetching under any circumstances.
+ */
+#define ZSTD_c_prefetchCDictTables ZSTD_c_experimentalParam16
+
+/* ZSTD_c_enableSeqProducerFallback
+ * Allowed values are 0 (disable) and 1 (enable). The default setting is 0.
+ *
+ * Controls whether zstd will fall back to an internal sequence producer if an
+ * external sequence producer is registered and returns an error code. This fallback
+ * is block-by-block: the internal sequence producer will only be called for blocks
+ * where the external sequence producer returns an error code. Fallback parsing will
+ * follow any other cParam settings, such as compression level, the same as in a
+ * normal (fully-internal) compression operation.
+ *
+ * The user is strongly encouraged to read the full Block-Level Sequence Producer API
+ * documentation (below) before setting this parameter. */
+#define ZSTD_c_enableSeqProducerFallback ZSTD_c_experimentalParam17
+
+/* ZSTD_c_maxBlockSize
+ * Allowed values are between 1KB and ZSTD_BLOCKSIZE_MAX (128KB).
+ * The default is ZSTD_BLOCKSIZE_MAX, and setting to 0 will set to the default.
+ *
+ * This parameter can be used to set an upper bound on the blocksize
+ * that overrides the default ZSTD_BLOCKSIZE_MAX. It cannot be used to set upper
+ * bounds greater than ZSTD_BLOCKSIZE_MAX or bounds lower than 1KB (will make
+ * compressBound() inaccurate). Only currently meant to be used for testing.
+ *
+ */
+#define ZSTD_c_maxBlockSize ZSTD_c_experimentalParam18
+
+/* ZSTD_c_searchForExternalRepcodes
+ * This parameter affects how zstd parses external sequences, such as sequences
+ * provided through the compressSequences() API or from an external block-level
+ * sequence producer.
+ *
+ * If set to ZSTD_ps_enable, the library will check for repeated offsets in
+ * external sequences, even if those repcodes are not explicitly indicated in
+ * the "rep" field. Note that this is the only way to exploit repcode matches
+ * while using compressSequences() or an external sequence producer, since zstd
+ * currently ignores the "rep" field of external sequences.
+ *
+ * If set to ZSTD_ps_disable, the library will not exploit repeated offsets in
+ * external sequences, regardless of whether the "rep" field has been set. This
+ * reduces sequence compression overhead by about 25% while sacrificing some
+ * compression ratio.
+ *
+ * The default value is ZSTD_ps_auto, for which the library will enable/disable
+ * based on compression level.
+ *
+ * Note: for now, this param only has an effect if ZSTD_c_blockDelimiters is
+ * set to ZSTD_sf_explicitBlockDelimiters. That may change in the future.
+ */
+#define ZSTD_c_searchForExternalRepcodes ZSTD_c_experimentalParam19
+
/*! ZSTD_CCtx_getParameter() :
* Get the requested compression parameter value, selected by enum ZSTD_cParameter,
* and store it into int* value.
* @return : 0, or an error code (which can be tested with ZSTD_isError()).
*/
-ZSTDLIB_API size_t ZSTD_CCtx_getParameter(ZSTD_CCtx* cctx, ZSTD_cParameter param, int* value);
+ZSTDLIB_STATIC_API size_t ZSTD_CCtx_getParameter(const ZSTD_CCtx* cctx, ZSTD_cParameter param, int* value);
/*! ZSTD_CCtx_params :
* Quick howto :
* - ZSTD_createCCtxParams() : Create a ZSTD_CCtx_params structure
- * - ZSTD_CCtxParam_setParameter() : Push parameters one by one into
- * an existing ZSTD_CCtx_params structure.
- * This is similar to
- * ZSTD_CCtx_setParameter().
+ * - ZSTD_CCtxParams_setParameter() : Push parameters one by one into
+ * an existing ZSTD_CCtx_params structure.
+ * This is similar to
+ * ZSTD_CCtx_setParameter().
* - ZSTD_CCtx_setParametersUsingCCtxParams() : Apply parameters to
* an existing CCtx.
* These parameters will be applied to
* all subsequent frames.
* - ZSTD_compressStream2() : Do compression using the CCtx.
- * - ZSTD_freeCCtxParams() : Free the memory.
+ * - ZSTD_freeCCtxParams() : Free the memory, accept NULL pointer.
*
* This can be used with ZSTD_estimateCCtxSize_advanced_usingCCtxParams()
* for static allocation of CCtx for single-threaded compression.
*/
-ZSTDLIB_API ZSTD_CCtx_params* ZSTD_createCCtxParams(void);
-ZSTDLIB_API size_t ZSTD_freeCCtxParams(ZSTD_CCtx_params* params);
+ZSTDLIB_STATIC_API ZSTD_CCtx_params* ZSTD_createCCtxParams(void);
+ZSTDLIB_STATIC_API size_t ZSTD_freeCCtxParams(ZSTD_CCtx_params* params); /* accept NULL pointer */
/*! ZSTD_CCtxParams_reset() :
* Reset params to default values.
*/
-ZSTDLIB_API size_t ZSTD_CCtxParams_reset(ZSTD_CCtx_params* params);
+ZSTDLIB_STATIC_API size_t ZSTD_CCtxParams_reset(ZSTD_CCtx_params* params);
/*! ZSTD_CCtxParams_init() :
* Initializes the compression parameters of cctxParams according to
* compression level. All other parameters are reset to their default values.
*/
-ZSTDLIB_API size_t ZSTD_CCtxParams_init(ZSTD_CCtx_params* cctxParams, int compressionLevel);
+ZSTDLIB_STATIC_API size_t ZSTD_CCtxParams_init(ZSTD_CCtx_params* cctxParams, int compressionLevel);
/*! ZSTD_CCtxParams_init_advanced() :
* Initializes the compression and frame parameters of cctxParams according to
* params. All other parameters are reset to their default values.
*/
-ZSTDLIB_API size_t ZSTD_CCtxParams_init_advanced(ZSTD_CCtx_params* cctxParams, ZSTD_parameters params);
+ZSTDLIB_STATIC_API size_t ZSTD_CCtxParams_init_advanced(ZSTD_CCtx_params* cctxParams, ZSTD_parameters params);
-/*! ZSTD_CCtxParam_setParameter() :
+/*! ZSTD_CCtxParams_setParameter() : Requires v1.4.0+
* Similar to ZSTD_CCtx_setParameter.
* Set one compression parameter, selected by enum ZSTD_cParameter.
- * Parameters must be applied to a ZSTD_CCtx using ZSTD_CCtx_setParametersUsingCCtxParams().
- * @result : 0, or an error code (which can be tested with ZSTD_isError()).
+ * Parameters must be applied to a ZSTD_CCtx using
+ * ZSTD_CCtx_setParametersUsingCCtxParams().
+ * @result : a code representing success or failure (which can be tested with
+ * ZSTD_isError()).
*/
-ZSTDLIB_API size_t ZSTD_CCtxParam_setParameter(ZSTD_CCtx_params* params, ZSTD_cParameter param, int value);
+ZSTDLIB_STATIC_API size_t ZSTD_CCtxParams_setParameter(ZSTD_CCtx_params* params, ZSTD_cParameter param, int value);
-/*! ZSTD_CCtxParam_getParameter() :
+/*! ZSTD_CCtxParams_getParameter() :
* Similar to ZSTD_CCtx_getParameter.
* Get the requested value of one compression parameter, selected by enum ZSTD_cParameter.
* @result : 0, or an error code (which can be tested with ZSTD_isError()).
*/
-ZSTDLIB_API size_t ZSTD_CCtxParam_getParameter(ZSTD_CCtx_params* params, ZSTD_cParameter param, int* value);
+ZSTDLIB_STATIC_API size_t ZSTD_CCtxParams_getParameter(const ZSTD_CCtx_params* params, ZSTD_cParameter param, int* value);
/*! ZSTD_CCtx_setParametersUsingCCtxParams() :
* Apply a set of ZSTD_CCtx_params to the compression context.
@@ -1376,7 +2279,7 @@ ZSTDLIB_API size_t ZSTD_CCtxParam_getParameter(ZSTD_CCtx_params* params, ZSTD_cP
* if nbWorkers>=1, new parameters will be picked up at next job,
* with a few restrictions (windowLog, pledgedSrcSize, nbWorkers, jobSize, and overlapLog are not updated).
*/
-ZSTDLIB_API size_t ZSTD_CCtx_setParametersUsingCCtxParams(
+ZSTDLIB_STATIC_API size_t ZSTD_CCtx_setParametersUsingCCtxParams(
ZSTD_CCtx* cctx, const ZSTD_CCtx_params* params);
/*! ZSTD_compressStream2_simpleArgs() :
@@ -1385,7 +2288,7 @@ ZSTDLIB_API size_t ZSTD_CCtx_setParametersUsingCCtxParams(
* This variant might be helpful for binders from dynamic languages
* which have troubles handling structures containing memory pointers.
*/
-ZSTDLIB_API size_t ZSTD_compressStream2_simpleArgs (
+ZSTDLIB_STATIC_API size_t ZSTD_compressStream2_simpleArgs (
ZSTD_CCtx* cctx,
void* dst, size_t dstCapacity, size_t* dstPos,
const void* src, size_t srcSize, size_t* srcPos,
@@ -1401,58 +2304,33 @@ ZSTDLIB_API size_t ZSTD_compressStream2_simpleArgs (
* Note : Frame Identifier is 4 bytes. If `size < 4`, @return will always be 0.
* Note 2 : Legacy Frame Identifiers are considered valid only if Legacy Support is enabled.
* Note 3 : Skippable Frame Identifiers are considered valid. */
-ZSTDLIB_API unsigned ZSTD_isFrame(const void* buffer, size_t size);
+ZSTDLIB_STATIC_API unsigned ZSTD_isFrame(const void* buffer, size_t size);
/*! ZSTD_createDDict_byReference() :
* Create a digested dictionary, ready to start decompression operation without startup delay.
* Dictionary content is referenced, and therefore stays in dictBuffer.
* It is important that dictBuffer outlives DDict,
* it must remain read accessible throughout the lifetime of DDict */
-ZSTDLIB_API ZSTD_DDict* ZSTD_createDDict_byReference(const void* dictBuffer, size_t dictSize);
-
-
-/*! ZSTD_getDictID_fromDict() :
- * Provides the dictID stored within dictionary.
- * if @return == 0, the dictionary is not conformant with Zstandard specification.
- * It can still be loaded, but as a content-only dictionary. */
-ZSTDLIB_API unsigned ZSTD_getDictID_fromDict(const void* dict, size_t dictSize);
-
-/*! ZSTD_getDictID_fromDDict() :
- * Provides the dictID of the dictionary loaded into `ddict`.
- * If @return == 0, the dictionary is not conformant to Zstandard specification, or empty.
- * Non-conformant dictionaries can still be loaded, but as content-only dictionaries. */
-ZSTDLIB_API unsigned ZSTD_getDictID_fromDDict(const ZSTD_DDict* ddict);
-
-/*! ZSTD_getDictID_fromFrame() :
- * Provides the dictID required to decompressed the frame stored within `src`.
- * If @return == 0, the dictID could not be decoded.
- * This could for one of the following reasons :
- * - The frame does not require a dictionary to be decoded (most common case).
- * - The frame was built with dictID intentionally removed. Whatever dictionary is necessary is a hidden information.
- * Note : this use case also happens when using a non-conformant dictionary.
- * - `srcSize` is too small, and as a result, the frame header could not be decoded (only possible if `srcSize < ZSTD_FRAMEHEADERSIZE_MAX`).
- * - This is not a Zstandard frame.
- * When identifying the exact failure cause, it's possible to use ZSTD_getFrameHeader(), which will provide a more precise error code. */
-ZSTDLIB_API unsigned ZSTD_getDictID_fromFrame(const void* src, size_t srcSize);
+ZSTDLIB_STATIC_API ZSTD_DDict* ZSTD_createDDict_byReference(const void* dictBuffer, size_t dictSize);
/*! ZSTD_DCtx_loadDictionary_byReference() :
* Same as ZSTD_DCtx_loadDictionary(),
* but references `dict` content instead of copying it into `dctx`.
* This saves memory if `dict` remains around.,
* However, it's imperative that `dict` remains accessible (and unmodified) while being used, so it must outlive decompression. */
-ZSTDLIB_API size_t ZSTD_DCtx_loadDictionary_byReference(ZSTD_DCtx* dctx, const void* dict, size_t dictSize);
+ZSTDLIB_STATIC_API size_t ZSTD_DCtx_loadDictionary_byReference(ZSTD_DCtx* dctx, const void* dict, size_t dictSize);
/*! ZSTD_DCtx_loadDictionary_advanced() :
* Same as ZSTD_DCtx_loadDictionary(),
* but gives direct control over
* how to load the dictionary (by copy ? by reference ?)
* and how to interpret it (automatic ? force raw mode ? full mode only ?). */
-ZSTDLIB_API size_t ZSTD_DCtx_loadDictionary_advanced(ZSTD_DCtx* dctx, const void* dict, size_t dictSize, ZSTD_dictLoadMethod_e dictLoadMethod, ZSTD_dictContentType_e dictContentType);
+ZSTDLIB_STATIC_API size_t ZSTD_DCtx_loadDictionary_advanced(ZSTD_DCtx* dctx, const void* dict, size_t dictSize, ZSTD_dictLoadMethod_e dictLoadMethod, ZSTD_dictContentType_e dictContentType);
/*! ZSTD_DCtx_refPrefix_advanced() :
* Same as ZSTD_DCtx_refPrefix(), but gives finer control over
* how to interpret prefix content (automatic ? force raw mode (default) ? full mode only ?) */
-ZSTDLIB_API size_t ZSTD_DCtx_refPrefix_advanced(ZSTD_DCtx* dctx, const void* prefix, size_t prefixSize, ZSTD_dictContentType_e dictContentType);
+ZSTDLIB_STATIC_API size_t ZSTD_DCtx_refPrefix_advanced(ZSTD_DCtx* dctx, const void* prefix, size_t prefixSize, ZSTD_dictContentType_e dictContentType);
/*! ZSTD_DCtx_setMaxWindowSize() :
* Refuses allocating internal buffers for frames requiring a window size larger than provided limit.
@@ -1461,20 +2339,107 @@ ZSTDLIB_API size_t ZSTD_DCtx_refPrefix_advanced(ZSTD_DCtx* dctx, const void* pre
* By default, a decompression context accepts all window sizes <= (1 << ZSTD_WINDOWLOG_LIMIT_DEFAULT)
* @return : 0, or an error code (which can be tested using ZSTD_isError()).
*/
-ZSTDLIB_API size_t ZSTD_DCtx_setMaxWindowSize(ZSTD_DCtx* dctx, size_t maxWindowSize);
+ZSTDLIB_STATIC_API size_t ZSTD_DCtx_setMaxWindowSize(ZSTD_DCtx* dctx, size_t maxWindowSize);
+
+/*! ZSTD_DCtx_getParameter() :
+ * Get the requested decompression parameter value, selected by enum ZSTD_dParameter,
+ * and store it into int* value.
+ * @return : 0, or an error code (which can be tested with ZSTD_isError()).
+ */
+ZSTDLIB_STATIC_API size_t ZSTD_DCtx_getParameter(ZSTD_DCtx* dctx, ZSTD_dParameter param, int* value);
/* ZSTD_d_format
* experimental parameter,
* allowing selection between ZSTD_format_e input compression formats
*/
#define ZSTD_d_format ZSTD_d_experimentalParam1
+/* ZSTD_d_stableOutBuffer
+ * Experimental parameter.
+ * Default is 0 == disabled. Set to 1 to enable.
+ *
+ * Tells the decompressor that the ZSTD_outBuffer will ALWAYS be the same
+ * between calls, except for the modifications that zstd makes to pos (the
+ * caller must not modify pos). This is checked by the decompressor, and
+ * decompression will fail if it ever changes. Therefore the ZSTD_outBuffer
+ * MUST be large enough to fit the entire decompressed frame. This will be
+ * checked when the frame content size is known. The data in the ZSTD_outBuffer
+ * in the range [dst, dst + pos) MUST not be modified during decompression
+ * or you will get data corruption.
+ *
+ * When this flag is enabled zstd won't allocate an output buffer, because
+ * it can write directly to the ZSTD_outBuffer, but it will still allocate
+ * an input buffer large enough to fit any compressed block. This will also
+ * avoid the memcpy() from the internal output buffer to the ZSTD_outBuffer.
+ * If you need to avoid the input buffer allocation use the buffer-less
+ * streaming API.
+ *
+ * NOTE: So long as the ZSTD_outBuffer always points to valid memory, using
+ * this flag is ALWAYS memory safe, and will never access out-of-bounds
+ * memory. However, decompression WILL fail if you violate the preconditions.
+ *
+ * WARNING: The data in the ZSTD_outBuffer in the range [dst, dst + pos) MUST
+ * not be modified during decompression or you will get data corruption. This
+ * is because zstd needs to reference data in the ZSTD_outBuffer to regenerate
+ * matches. Normally zstd maintains its own buffer for this purpose, but passing
+ * this flag tells zstd to use the user provided buffer.
+ */
+#define ZSTD_d_stableOutBuffer ZSTD_d_experimentalParam2
+
+/* ZSTD_d_forceIgnoreChecksum
+ * Experimental parameter.
+ * Default is 0 == disabled. Set to 1 to enable
+ *
+ * Tells the decompressor to skip checksum validation during decompression, regardless
+ * of whether checksumming was specified during compression. This offers some
+ * slight performance benefits, and may be useful for debugging.
+ * Param has values of type ZSTD_forceIgnoreChecksum_e
+ */
+#define ZSTD_d_forceIgnoreChecksum ZSTD_d_experimentalParam3
+
+/* ZSTD_d_refMultipleDDicts
+ * Experimental parameter.
+ * Default is 0 == disabled. Set to 1 to enable
+ *
+ * If enabled and dctx is allocated on the heap, then additional memory will be allocated
+ * to store references to multiple ZSTD_DDict. That is, multiple calls of ZSTD_refDDict()
+ * using a given ZSTD_DCtx, rather than overwriting the previous DDict reference, will instead
+ * store all references. At decompression time, the appropriate dictID is selected
+ * from the set of DDicts based on the dictID in the frame.
+ *
+ * Usage is simply calling ZSTD_refDDict() on multiple dict buffers.
+ *
+ * Param has values of byte ZSTD_refMultipleDDicts_e
+ *
+ * WARNING: Enabling this parameter and calling ZSTD_DCtx_refDDict(), will trigger memory
+ * allocation for the hash table. ZSTD_freeDCtx() also frees this memory.
+ * Memory is allocated as per ZSTD_DCtx::customMem.
+ *
+ * Although this function allocates memory for the table, the user is still responsible for
+ * memory management of the underlying ZSTD_DDict* themselves.
+ */
+#define ZSTD_d_refMultipleDDicts ZSTD_d_experimentalParam4
+
+/* ZSTD_d_disableHuffmanAssembly
+ * Set to 1 to disable the Huffman assembly implementation.
+ * The default value is 0, which allows zstd to use the Huffman assembly
+ * implementation if available.
+ *
+ * This parameter can be used to disable Huffman assembly at runtime.
+ * If you want to disable it at compile time you can define the macro
+ * ZSTD_DISABLE_ASM.
+ */
+#define ZSTD_d_disableHuffmanAssembly ZSTD_d_experimentalParam5
+
/*! ZSTD_DCtx_setFormat() :
+ * This function is REDUNDANT. Prefer ZSTD_DCtx_setParameter().
* Instruct the decoder context about what kind of data to decode next.
* This instruction is mandatory to decode data without a fully-formed header,
* such ZSTD_f_zstd1_magicless for example.
* @return : 0, or an error code (which can be tested using ZSTD_isError()). */
-ZSTDLIB_API size_t ZSTD_DCtx_setFormat(ZSTD_DCtx* dctx, ZSTD_format_e format);
+ZSTD_DEPRECATED("use ZSTD_DCtx_setParameter() instead")
+ZSTDLIB_STATIC_API
+size_t ZSTD_DCtx_setFormat(ZSTD_DCtx* dctx, ZSTD_format_e format);
/*! ZSTD_decompressStream_simpleArgs() :
* Same as ZSTD_decompressStream(),
@@ -1482,7 +2447,7 @@ ZSTDLIB_API size_t ZSTD_DCtx_setFormat(ZSTD_DCtx* dctx, ZSTD_format_e format);
* This can be helpful for binders from dynamic languages
* which have troubles handling structures containing memory pointers.
*/
-ZSTDLIB_API size_t ZSTD_decompressStream_simpleArgs (
+ZSTDLIB_STATIC_API size_t ZSTD_decompressStream_simpleArgs (
ZSTD_DCtx* dctx,
void* dst, size_t dstCapacity, size_t* dstPos,
const void* src, size_t srcSize, size_t* srcPos);
@@ -1496,14 +2461,101 @@ ZSTDLIB_API size_t ZSTD_decompressStream_simpleArgs (
********************************************************************/
/*===== Advanced Streaming compression functions =====*/
-ZSTDLIB_API size_t ZSTD_initCStream_srcSize(ZSTD_CStream* zcs, int compressionLevel, unsigned long long pledgedSrcSize); /**< pledgedSrcSize must be correct. If it is not known at init time, use ZSTD_CONTENTSIZE_UNKNOWN. Note that, for compatibility with older programs, "0" also disables frame content size field. It may be enabled in the future. */
-ZSTDLIB_API size_t ZSTD_initCStream_usingDict(ZSTD_CStream* zcs, const void* dict, size_t dictSize, int compressionLevel); /**< creates of an internal CDict (incompatible with static CCtx), except if dict == NULL or dictSize < 8, in which case no dict is used. Note: dict is loaded with ZSTD_dm_auto (treated as a full zstd dictionary if it begins with ZSTD_MAGIC_DICTIONARY, else as raw content) and ZSTD_dlm_byCopy.*/
-ZSTDLIB_API size_t ZSTD_initCStream_advanced(ZSTD_CStream* zcs, const void* dict, size_t dictSize,
- ZSTD_parameters params, unsigned long long pledgedSrcSize); /**< pledgedSrcSize must be correct. If srcSize is not known at init time, use value ZSTD_CONTENTSIZE_UNKNOWN. dict is loaded with ZSTD_dm_auto and ZSTD_dlm_byCopy. */
-ZSTDLIB_API size_t ZSTD_initCStream_usingCDict(ZSTD_CStream* zcs, const ZSTD_CDict* cdict); /**< note : cdict will just be referenced, and must outlive compression session */
-ZSTDLIB_API size_t ZSTD_initCStream_usingCDict_advanced(ZSTD_CStream* zcs, const ZSTD_CDict* cdict, ZSTD_frameParameters fParams, unsigned long long pledgedSrcSize); /**< same as ZSTD_initCStream_usingCDict(), with control over frame parameters. pledgedSrcSize must be correct. If srcSize is not known at init time, use value ZSTD_CONTENTSIZE_UNKNOWN. */
+
+/*! ZSTD_initCStream_srcSize() :
+ * This function is DEPRECATED, and equivalent to:
+ * ZSTD_CCtx_reset(zcs, ZSTD_reset_session_only);
+ * ZSTD_CCtx_refCDict(zcs, NULL); // clear the dictionary (if any)
+ * ZSTD_CCtx_setParameter(zcs, ZSTD_c_compressionLevel, compressionLevel);
+ * ZSTD_CCtx_setPledgedSrcSize(zcs, pledgedSrcSize);
+ *
+ * pledgedSrcSize must be correct. If it is not known at init time, use
+ * ZSTD_CONTENTSIZE_UNKNOWN. Note that, for compatibility with older programs,
+ * "0" also disables frame content size field. It may be enabled in the future.
+ * This prototype will generate compilation warnings.
+ */
+ZSTD_DEPRECATED("use ZSTD_CCtx_reset, see zstd.h for detailed instructions")
+ZSTDLIB_STATIC_API
+size_t ZSTD_initCStream_srcSize(ZSTD_CStream* zcs,
+ int compressionLevel,
+ unsigned long long pledgedSrcSize);
+
+/*! ZSTD_initCStream_usingDict() :
+ * This function is DEPRECATED, and is equivalent to:
+ * ZSTD_CCtx_reset(zcs, ZSTD_reset_session_only);
+ * ZSTD_CCtx_setParameter(zcs, ZSTD_c_compressionLevel, compressionLevel);
+ * ZSTD_CCtx_loadDictionary(zcs, dict, dictSize);
+ *
+ * Creates of an internal CDict (incompatible with static CCtx), except if
+ * dict == NULL or dictSize < 8, in which case no dict is used.
+ * Note: dict is loaded with ZSTD_dct_auto (treated as a full zstd dictionary if
+ * it begins with ZSTD_MAGIC_DICTIONARY, else as raw content) and ZSTD_dlm_byCopy.
+ * This prototype will generate compilation warnings.
+ */
+ZSTD_DEPRECATED("use ZSTD_CCtx_reset, see zstd.h for detailed instructions")
+ZSTDLIB_STATIC_API
+size_t ZSTD_initCStream_usingDict(ZSTD_CStream* zcs,
+ const void* dict, size_t dictSize,
+ int compressionLevel);
+
+/*! ZSTD_initCStream_advanced() :
+ * This function is DEPRECATED, and is equivalent to:
+ * ZSTD_CCtx_reset(zcs, ZSTD_reset_session_only);
+ * ZSTD_CCtx_setParams(zcs, params);
+ * ZSTD_CCtx_setPledgedSrcSize(zcs, pledgedSrcSize);
+ * ZSTD_CCtx_loadDictionary(zcs, dict, dictSize);
+ *
+ * dict is loaded with ZSTD_dct_auto and ZSTD_dlm_byCopy.
+ * pledgedSrcSize must be correct.
+ * If srcSize is not known at init time, use value ZSTD_CONTENTSIZE_UNKNOWN.
+ * This prototype will generate compilation warnings.
+ */
+ZSTD_DEPRECATED("use ZSTD_CCtx_reset, see zstd.h for detailed instructions")
+ZSTDLIB_STATIC_API
+size_t ZSTD_initCStream_advanced(ZSTD_CStream* zcs,
+ const void* dict, size_t dictSize,
+ ZSTD_parameters params,
+ unsigned long long pledgedSrcSize);
+
+/*! ZSTD_initCStream_usingCDict() :
+ * This function is DEPRECATED, and equivalent to:
+ * ZSTD_CCtx_reset(zcs, ZSTD_reset_session_only);
+ * ZSTD_CCtx_refCDict(zcs, cdict);
+ *
+ * note : cdict will just be referenced, and must outlive compression session
+ * This prototype will generate compilation warnings.
+ */
+ZSTD_DEPRECATED("use ZSTD_CCtx_reset and ZSTD_CCtx_refCDict, see zstd.h for detailed instructions")
+ZSTDLIB_STATIC_API
+size_t ZSTD_initCStream_usingCDict(ZSTD_CStream* zcs, const ZSTD_CDict* cdict);
+
+/*! ZSTD_initCStream_usingCDict_advanced() :
+ * This function is DEPRECATED, and is equivalent to:
+ * ZSTD_CCtx_reset(zcs, ZSTD_reset_session_only);
+ * ZSTD_CCtx_setFParams(zcs, fParams);
+ * ZSTD_CCtx_setPledgedSrcSize(zcs, pledgedSrcSize);
+ * ZSTD_CCtx_refCDict(zcs, cdict);
+ *
+ * same as ZSTD_initCStream_usingCDict(), with control over frame parameters.
+ * pledgedSrcSize must be correct. If srcSize is not known at init time, use
+ * value ZSTD_CONTENTSIZE_UNKNOWN.
+ * This prototype will generate compilation warnings.
+ */
+ZSTD_DEPRECATED("use ZSTD_CCtx_reset and ZSTD_CCtx_refCDict, see zstd.h for detailed instructions")
+ZSTDLIB_STATIC_API
+size_t ZSTD_initCStream_usingCDict_advanced(ZSTD_CStream* zcs,
+ const ZSTD_CDict* cdict,
+ ZSTD_frameParameters fParams,
+ unsigned long long pledgedSrcSize);
/*! ZSTD_resetCStream() :
+ * This function is DEPRECATED, and is equivalent to:
+ * ZSTD_CCtx_reset(zcs, ZSTD_reset_session_only);
+ * ZSTD_CCtx_setPledgedSrcSize(zcs, pledgedSrcSize);
+ * Note: ZSTD_resetCStream() interprets pledgedSrcSize == 0 as ZSTD_CONTENTSIZE_UNKNOWN, but
+ * ZSTD_CCtx_setPledgedSrcSize() does not do the same, so ZSTD_CONTENTSIZE_UNKNOWN must be
+ * explicitly specified.
+ *
* start a new frame, using same parameters from previous frame.
* This is typically useful to skip dictionary loading stage, since it will re-use it in-place.
* Note that zcs must be init at least once before using ZSTD_resetCStream().
@@ -1512,8 +2564,11 @@ ZSTDLIB_API size_t ZSTD_initCStream_usingCDict_advanced(ZSTD_CStream* zcs, const
* For the time being, pledgedSrcSize==0 is interpreted as "srcSize unknown" for compatibility with older programs,
* but it will change to mean "empty" in future version, so use macro ZSTD_CONTENTSIZE_UNKNOWN instead.
* @return : 0, or an error code (which can be tested using ZSTD_isError())
+ * This prototype will generate compilation warnings.
*/
-ZSTDLIB_API size_t ZSTD_resetCStream(ZSTD_CStream* zcs, unsigned long long pledgedSrcSize);
+ZSTD_DEPRECATED("use ZSTD_CCtx_reset, see zstd.h for detailed instructions")
+ZSTDLIB_STATIC_API
+size_t ZSTD_resetCStream(ZSTD_CStream* zcs, unsigned long long pledgedSrcSize);
typedef struct {
@@ -1531,7 +2586,7 @@ typedef struct {
* Note : (ingested - consumed) is amount of input data buffered internally, not yet compressed.
* Aggregates progression inside active worker threads.
*/
-ZSTDLIB_API ZSTD_frameProgression ZSTD_getFrameProgression(const ZSTD_CCtx* cctx);
+ZSTDLIB_STATIC_API ZSTD_frameProgression ZSTD_getFrameProgression(const ZSTD_CCtx* cctx);
/*! ZSTD_toFlushNow() :
* Tell how many bytes are ready to be flushed immediately.
@@ -1546,21 +2601,218 @@ ZSTDLIB_API ZSTD_frameProgression ZSTD_getFrameProgression(const ZSTD_CCtx* cctx
* therefore flush speed is limited by production speed of oldest job
* irrespective of the speed of concurrent (and newer) jobs.
*/
-ZSTDLIB_API size_t ZSTD_toFlushNow(ZSTD_CCtx* cctx);
+ZSTDLIB_STATIC_API size_t ZSTD_toFlushNow(ZSTD_CCtx* cctx);
/*===== Advanced Streaming decompression functions =====*/
-ZSTDLIB_API size_t ZSTD_initDStream_usingDict(ZSTD_DStream* zds, const void* dict, size_t dictSize); /**< note: no dictionary will be used if dict == NULL or dictSize < 8 */
-ZSTDLIB_API size_t ZSTD_initDStream_usingDDict(ZSTD_DStream* zds, const ZSTD_DDict* ddict); /**< note : ddict is referenced, it must outlive decompression session */
-ZSTDLIB_API size_t ZSTD_resetDStream(ZSTD_DStream* zds); /**< re-use decompression parameters from previous init; saves dictionary loading */
+
+/*!
+ * This function is deprecated, and is equivalent to:
+ *
+ * ZSTD_DCtx_reset(zds, ZSTD_reset_session_only);
+ * ZSTD_DCtx_loadDictionary(zds, dict, dictSize);
+ *
+ * note: no dictionary will be used if dict == NULL or dictSize < 8
+ */
+ZSTD_DEPRECATED("use ZSTD_DCtx_reset + ZSTD_DCtx_loadDictionary, see zstd.h for detailed instructions")
+ZSTDLIB_STATIC_API size_t ZSTD_initDStream_usingDict(ZSTD_DStream* zds, const void* dict, size_t dictSize);
+
+/*!
+ * This function is deprecated, and is equivalent to:
+ *
+ * ZSTD_DCtx_reset(zds, ZSTD_reset_session_only);
+ * ZSTD_DCtx_refDDict(zds, ddict);
+ *
+ * note : ddict is referenced, it must outlive decompression session
+ */
+ZSTD_DEPRECATED("use ZSTD_DCtx_reset + ZSTD_DCtx_refDDict, see zstd.h for detailed instructions")
+ZSTDLIB_STATIC_API size_t ZSTD_initDStream_usingDDict(ZSTD_DStream* zds, const ZSTD_DDict* ddict);
+
+/*!
+ * This function is deprecated, and is equivalent to:
+ *
+ * ZSTD_DCtx_reset(zds, ZSTD_reset_session_only);
+ *
+ * re-use decompression parameters from previous init; saves dictionary loading
+ */
+ZSTD_DEPRECATED("use ZSTD_DCtx_reset, see zstd.h for detailed instructions")
+ZSTDLIB_STATIC_API size_t ZSTD_resetDStream(ZSTD_DStream* zds);
+
+
+/* ********************* BLOCK-LEVEL SEQUENCE PRODUCER API *********************
+ *
+ * *** OVERVIEW ***
+ * The Block-Level Sequence Producer API allows users to provide their own custom
+ * sequence producer which libzstd invokes to process each block. The produced list
+ * of sequences (literals and matches) is then post-processed by libzstd to produce
+ * valid compressed blocks.
+ *
+ * This block-level offload API is a more granular complement of the existing
+ * frame-level offload API compressSequences() (introduced in v1.5.1). It offers
+ * an easier migration story for applications already integrated with libzstd: the
+ * user application continues to invoke the same compression functions
+ * ZSTD_compress2() or ZSTD_compressStream2() as usual, and transparently benefits
+ * from the specific advantages of the external sequence producer. For example,
+ * the sequence producer could be tuned to take advantage of known characteristics
+ * of the input, to offer better speed / ratio, or could leverage hardware
+ * acceleration not available within libzstd itself.
+ *
+ * See contrib/externalSequenceProducer for an example program employing the
+ * Block-Level Sequence Producer API.
+ *
+ * *** USAGE ***
+ * The user is responsible for implementing a function of type
+ * ZSTD_sequenceProducer_F. For each block, zstd will pass the following
+ * arguments to the user-provided function:
+ *
+ * - sequenceProducerState: a pointer to a user-managed state for the sequence
+ * producer.
+ *
+ * - outSeqs, outSeqsCapacity: an output buffer for the sequence producer.
+ * outSeqsCapacity is guaranteed >= ZSTD_sequenceBound(srcSize). The memory
+ * backing outSeqs is managed by the CCtx.
+ *
+ * - src, srcSize: an input buffer for the sequence producer to parse.
+ * srcSize is guaranteed to be <= ZSTD_BLOCKSIZE_MAX.
+ *
+ * - dict, dictSize: a history buffer, which may be empty, which the sequence
+ * producer may reference as it parses the src buffer. Currently, zstd will
+ * always pass dictSize == 0 into external sequence producers, but this will
+ * change in the future.
+ *
+ * - compressionLevel: a signed integer representing the zstd compression level
+ * set by the user for the current operation. The sequence producer may choose
+ * to use this information to change its compression strategy and speed/ratio
+ * tradeoff. Note: the compression level does not reflect zstd parameters set
+ * through the advanced API.
+ *
+ * - windowSize: a size_t representing the maximum allowed offset for external
+ * sequences. Note that sequence offsets are sometimes allowed to exceed the
+ * windowSize if a dictionary is present, see doc/zstd_compression_format.md
+ * for details.
+ *
+ * The user-provided function shall return a size_t representing the number of
+ * sequences written to outSeqs. This return value will be treated as an error
+ * code if it is greater than outSeqsCapacity. The return value must be non-zero
+ * if srcSize is non-zero. The ZSTD_SEQUENCE_PRODUCER_ERROR macro is provided
+ * for convenience, but any value greater than outSeqsCapacity will be treated as
+ * an error code.
+ *
+ * If the user-provided function does not return an error code, the sequences
+ * written to outSeqs must be a valid parse of the src buffer. Data corruption may
+ * occur if the parse is not valid. A parse is defined to be valid if the
+ * following conditions hold:
+ * - The sum of matchLengths and literalLengths must equal srcSize.
+ * - All sequences in the parse, except for the final sequence, must have
+ * matchLength >= ZSTD_MINMATCH_MIN. The final sequence must have
+ * matchLength >= ZSTD_MINMATCH_MIN or matchLength == 0.
+ * - All offsets must respect the windowSize parameter as specified in
+ * doc/zstd_compression_format.md.
+ * - If the final sequence has matchLength == 0, it must also have offset == 0.
+ *
+ * zstd will only validate these conditions (and fail compression if they do not
+ * hold) if the ZSTD_c_validateSequences cParam is enabled. Note that sequence
+ * validation has a performance cost.
+ *
+ * If the user-provided function returns an error, zstd will either fall back
+ * to an internal sequence producer or fail the compression operation. The user can
+ * choose between the two behaviors by setting the ZSTD_c_enableSeqProducerFallback
+ * cParam. Fallback compression will follow any other cParam settings, such as
+ * compression level, the same as in a normal compression operation.
+ *
+ * The user shall instruct zstd to use a particular ZSTD_sequenceProducer_F
+ * function by calling
+ * ZSTD_registerSequenceProducer(cctx,
+ * sequenceProducerState,
+ * sequenceProducer)
+ * This setting will persist until the next parameter reset of the CCtx.
+ *
+ * The sequenceProducerState must be initialized by the user before calling
+ * ZSTD_registerSequenceProducer(). The user is responsible for destroying the
+ * sequenceProducerState.
+ *
+ * *** LIMITATIONS ***
+ * This API is compatible with all zstd compression APIs which respect advanced parameters.
+ * However, there are three limitations:
+ *
+ * First, the ZSTD_c_enableLongDistanceMatching cParam is not currently supported.
+ * COMPRESSION WILL FAIL if it is enabled and the user tries to compress with a block-level
+ * external sequence producer.
+ * - Note that ZSTD_c_enableLongDistanceMatching is auto-enabled by default in some
+ * cases (see its documentation for details). Users must explicitly set
+ * ZSTD_c_enableLongDistanceMatching to ZSTD_ps_disable in such cases if an external
+ * sequence producer is registered.
+ * - As of this writing, ZSTD_c_enableLongDistanceMatching is disabled by default
+ * whenever ZSTD_c_windowLog < 128MB, but that's subject to change. Users should
+ * check the docs on ZSTD_c_enableLongDistanceMatching whenever the Block-Level Sequence
+ * Producer API is used in conjunction with advanced settings (like ZSTD_c_windowLog).
+ *
+ * Second, history buffers are not currently supported. Concretely, zstd will always pass
+ * dictSize == 0 to the external sequence producer (for now). This has two implications:
+ * - Dictionaries are not currently supported. Compression will *not* fail if the user
+ * references a dictionary, but the dictionary won't have any effect.
+ * - Stream history is not currently supported. All advanced compression APIs, including
+ * streaming APIs, work with external sequence producers, but each block is treated as
+ * an independent chunk without history from previous blocks.
+ *
+ * Third, multi-threading within a single compression is not currently supported. In other words,
+ * COMPRESSION WILL FAIL if ZSTD_c_nbWorkers > 0 and an external sequence producer is registered.
+ * Multi-threading across compressions is fine: simply create one CCtx per thread.
+ *
+ * Long-term, we plan to overcome all three limitations. There is no technical blocker to
+ * overcoming them. It is purely a question of engineering effort.
+ */
+
+#define ZSTD_SEQUENCE_PRODUCER_ERROR ((size_t)(-1))
+
+typedef size_t ZSTD_sequenceProducer_F (
+ void* sequenceProducerState,
+ ZSTD_Sequence* outSeqs, size_t outSeqsCapacity,
+ const void* src, size_t srcSize,
+ const void* dict, size_t dictSize,
+ int compressionLevel,
+ size_t windowSize
+);
+
+/*! ZSTD_registerSequenceProducer() :
+ * Instruct zstd to use a block-level external sequence producer function.
+ *
+ * The sequenceProducerState must be initialized by the caller, and the caller is
+ * responsible for managing its lifetime. This parameter is sticky across
+ * compressions. It will remain set until the user explicitly resets compression
+ * parameters.
+ *
+ * Sequence producer registration is considered to be an "advanced parameter",
+ * part of the "advanced API". This means it will only have an effect on compression
+ * APIs which respect advanced parameters, such as compress2() and compressStream2().
+ * Older compression APIs such as compressCCtx(), which predate the introduction of
+ * "advanced parameters", will ignore any external sequence producer setting.
+ *
+ * The sequence producer can be "cleared" by registering a NULL function pointer. This
+ * removes all limitations described above in the "LIMITATIONS" section of the API docs.
+ *
+ * The user is strongly encouraged to read the full API documentation (above) before
+ * calling this function. */
+ZSTDLIB_STATIC_API void
+ZSTD_registerSequenceProducer(
+ ZSTD_CCtx* cctx,
+ void* sequenceProducerState,
+ ZSTD_sequenceProducer_F* sequenceProducer
+);
/*********************************************************************
-* Buffer-less and synchronous inner streaming functions
+* Buffer-less and synchronous inner streaming functions (DEPRECATED)
*
-* This is an advanced API, giving full control over buffer management, for users which need direct control over memory.
-* But it's also a complex one, with several restrictions, documented below.
-* Prefer normal streaming API for an easier experience.
+* This API is deprecated, and will be removed in a future version.
+* It allows streaming (de)compression with user allocated buffers.
+* However, it is hard to use, and not as well tested as the rest of
+* our API.
+*
+* Please use the normal streaming API instead: ZSTD_compressStream2,
+* and ZSTD_decompressStream.
+* If there is functionality that you need, but it doesn't provide,
+* please open an issue on our GitHub.
********************************************************************* */
/**
@@ -1571,9 +2823,7 @@ ZSTDLIB_API size_t ZSTD_resetDStream(ZSTD_DStream* zds); /**< re-use decompress
ZSTD_CCtx object can be re-used multiple times within successive compression operations.
Start by initializing a context.
- Use ZSTD_compressBegin(), or ZSTD_compressBegin_usingDict() for dictionary compression,
- or ZSTD_compressBegin_advanced(), for finer parameter control.
- It's also possible to duplicate a reference context which has already been initialized, using ZSTD_copyCCtx()
+ Use ZSTD_compressBegin(), or ZSTD_compressBegin_usingDict() for dictionary compression.
Then, consume your input using ZSTD_compressContinue().
There are some important considerations to keep in mind when using this advanced function :
@@ -1595,18 +2845,30 @@ ZSTDLIB_API size_t ZSTD_resetDStream(ZSTD_DStream* zds); /**< re-use decompress
*/
/*===== Buffer-less streaming compression functions =====*/
-ZSTDLIB_API size_t ZSTD_compressBegin(ZSTD_CCtx* cctx, int compressionLevel);
-ZSTDLIB_API size_t ZSTD_compressBegin_usingDict(ZSTD_CCtx* cctx, const void* dict, size_t dictSize, int compressionLevel);
-ZSTDLIB_API size_t ZSTD_compressBegin_advanced(ZSTD_CCtx* cctx, const void* dict, size_t dictSize, ZSTD_parameters params, unsigned long long pledgedSrcSize); /**< pledgedSrcSize : If srcSize is not known at init time, use ZSTD_CONTENTSIZE_UNKNOWN */
-ZSTDLIB_API size_t ZSTD_compressBegin_usingCDict(ZSTD_CCtx* cctx, const ZSTD_CDict* cdict); /**< note: fails if cdict==NULL */
-ZSTDLIB_API size_t ZSTD_compressBegin_usingCDict_advanced(ZSTD_CCtx* const cctx, const ZSTD_CDict* const cdict, ZSTD_frameParameters const fParams, unsigned long long const pledgedSrcSize); /* compression parameters are already set within cdict. pledgedSrcSize must be correct. If srcSize is not known, use macro ZSTD_CONTENTSIZE_UNKNOWN */
-ZSTDLIB_API size_t ZSTD_copyCCtx(ZSTD_CCtx* cctx, const ZSTD_CCtx* preparedCCtx, unsigned long long pledgedSrcSize); /**< note: if pledgedSrcSize is not known, use ZSTD_CONTENTSIZE_UNKNOWN */
-
-ZSTDLIB_API size_t ZSTD_compressContinue(ZSTD_CCtx* cctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize);
-ZSTDLIB_API size_t ZSTD_compressEnd(ZSTD_CCtx* cctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize);
-
-
-/*-
+ZSTD_DEPRECATED("The buffer-less API is deprecated in favor of the normal streaming API. See docs.")
+ZSTDLIB_STATIC_API size_t ZSTD_compressBegin(ZSTD_CCtx* cctx, int compressionLevel);
+ZSTD_DEPRECATED("The buffer-less API is deprecated in favor of the normal streaming API. See docs.")
+ZSTDLIB_STATIC_API size_t ZSTD_compressBegin_usingDict(ZSTD_CCtx* cctx, const void* dict, size_t dictSize, int compressionLevel);
+ZSTD_DEPRECATED("The buffer-less API is deprecated in favor of the normal streaming API. See docs.")
+ZSTDLIB_STATIC_API size_t ZSTD_compressBegin_usingCDict(ZSTD_CCtx* cctx, const ZSTD_CDict* cdict); /**< note: fails if cdict==NULL */
+
+ZSTD_DEPRECATED("This function will likely be removed in a future release. It is misleading and has very limited utility.")
+ZSTDLIB_STATIC_API
+size_t ZSTD_copyCCtx(ZSTD_CCtx* cctx, const ZSTD_CCtx* preparedCCtx, unsigned long long pledgedSrcSize); /**< note: if pledgedSrcSize is not known, use ZSTD_CONTENTSIZE_UNKNOWN */
+
+ZSTD_DEPRECATED("The buffer-less API is deprecated in favor of the normal streaming API. See docs.")
+ZSTDLIB_STATIC_API size_t ZSTD_compressContinue(ZSTD_CCtx* cctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize);
+ZSTD_DEPRECATED("The buffer-less API is deprecated in favor of the normal streaming API. See docs.")
+ZSTDLIB_STATIC_API size_t ZSTD_compressEnd(ZSTD_CCtx* cctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize);
+
+/* The ZSTD_compressBegin_advanced() and ZSTD_compressBegin_usingCDict_advanced() are now DEPRECATED and will generate a compiler warning */
+ZSTD_DEPRECATED("use advanced API to access custom parameters")
+ZSTDLIB_STATIC_API
+size_t ZSTD_compressBegin_advanced(ZSTD_CCtx* cctx, const void* dict, size_t dictSize, ZSTD_parameters params, unsigned long long pledgedSrcSize); /**< pledgedSrcSize : If srcSize is not known at init time, use ZSTD_CONTENTSIZE_UNKNOWN */
+ZSTD_DEPRECATED("use advanced API to access custom parameters")
+ZSTDLIB_STATIC_API
+size_t ZSTD_compressBegin_usingCDict_advanced(ZSTD_CCtx* const cctx, const ZSTD_CDict* const cdict, ZSTD_frameParameters const fParams, unsigned long long const pledgedSrcSize); /* compression parameters are already set within cdict. pledgedSrcSize must be correct. If srcSize is not known, use macro ZSTD_CONTENTSIZE_UNKNOWN */
+/**
Buffer-less streaming decompression (synchronous mode)
A ZSTD_DCtx object is required to track streaming operations.
@@ -1617,8 +2879,8 @@ ZSTDLIB_API size_t ZSTD_compressEnd(ZSTD_CCtx* cctx, void* dst, size_t dstCapaci
Frame header is extracted from the beginning of compressed frame, so providing only the frame's beginning is enough.
Data fragment must be large enough to ensure successful decoding.
`ZSTD_frameHeaderSize_max` bytes is guaranteed to always be large enough.
- @result : 0 : successful decoding, the `ZSTD_frameHeader` structure is correctly filled.
- >0 : `srcSize` is too small, please provide at least @result bytes on next attempt.
+ result : 0 : successful decoding, the `ZSTD_frameHeader` structure is correctly filled.
+ >0 : `srcSize` is too small, please provide at least result bytes on next attempt.
errorCode, which can be tested using ZSTD_isError().
It fills a ZSTD_frameHeader structure with important information to correctly decode the frame,
@@ -1637,7 +2899,7 @@ ZSTDLIB_API size_t ZSTD_compressEnd(ZSTD_CCtx* cctx, void* dst, size_t dstCapaci
The most memory efficient way is to use a round buffer of sufficient size.
Sufficient size is determined by invoking ZSTD_decodingBufferSize_min(),
- which can @return an error code if required value is too large for current system (in 32-bits mode).
+ which can return an error code if required value is too large for current system (in 32-bits mode).
In a round buffer methodology, ZSTD_decompressContinue() decompresses each block next to previous one,
up to the moment there is not enough room left in the buffer to guarantee decoding another full block,
which maximum size is provided in `ZSTD_frameHeader` structure, field `blockSizeMax`.
@@ -1657,7 +2919,7 @@ ZSTDLIB_API size_t ZSTD_compressEnd(ZSTD_CCtx* cctx, void* dst, size_t dstCapaci
ZSTD_nextSrcSizeToDecompress() tells how many bytes to provide as 'srcSize' to ZSTD_decompressContinue().
ZSTD_decompressContinue() requires this _exact_ amount of bytes, or it will fail.
- @result of ZSTD_decompressContinue() is the number of bytes regenerated within 'dst' (necessarily <= dstCapacity).
+ result of ZSTD_decompressContinue() is the number of bytes regenerated within 'dst' (necessarily <= dstCapacity).
It can be zero : it just means ZSTD_decompressContinue() has decoded some metadata item.
It can also be an error code, which can be tested with ZSTD_isError().
@@ -1680,52 +2942,45 @@ ZSTDLIB_API size_t ZSTD_compressEnd(ZSTD_CCtx* cctx, void* dst, size_t dstCapaci
*/
/*===== Buffer-less streaming decompression functions =====*/
-typedef enum { ZSTD_frame, ZSTD_skippableFrame } ZSTD_frameType_e;
-typedef struct {
- unsigned long long frameContentSize; /* if == ZSTD_CONTENTSIZE_UNKNOWN, it means this field is not available. 0 means "empty" */
- unsigned long long windowSize; /* can be very large, up to <= frameContentSize */
- unsigned blockSizeMax;
- ZSTD_frameType_e frameType; /* if == ZSTD_skippableFrame, frameContentSize is the size of skippable content */
- unsigned headerSize;
- unsigned dictID;
- unsigned checksumFlag;
-} ZSTD_frameHeader;
-/** ZSTD_getFrameHeader() :
- * decode Frame Header, or requires larger `srcSize`.
- * @return : 0, `zfhPtr` is correctly filled,
- * >0, `srcSize` is too small, value is wanted `srcSize` amount,
- * or an error code, which can be tested using ZSTD_isError() */
-ZSTDLIB_API size_t ZSTD_getFrameHeader(ZSTD_frameHeader* zfhPtr, const void* src, size_t srcSize); /**< doesn't consume input */
-/*! ZSTD_getFrameHeader_advanced() :
- * same as ZSTD_getFrameHeader(),
- * with added capability to select a format (like ZSTD_f_zstd1_magicless) */
-ZSTDLIB_API size_t ZSTD_getFrameHeader_advanced(ZSTD_frameHeader* zfhPtr, const void* src, size_t srcSize, ZSTD_format_e format);
-ZSTDLIB_API size_t ZSTD_decodingBufferSize_min(unsigned long long windowSize, unsigned long long frameContentSize); /**< when frame content size is not known, pass in frameContentSize == ZSTD_CONTENTSIZE_UNKNOWN */
+ZSTDLIB_STATIC_API size_t ZSTD_decodingBufferSize_min(unsigned long long windowSize, unsigned long long frameContentSize); /**< when frame content size is not known, pass in frameContentSize == ZSTD_CONTENTSIZE_UNKNOWN */
-ZSTDLIB_API size_t ZSTD_decompressBegin(ZSTD_DCtx* dctx);
-ZSTDLIB_API size_t ZSTD_decompressBegin_usingDict(ZSTD_DCtx* dctx, const void* dict, size_t dictSize);
-ZSTDLIB_API size_t ZSTD_decompressBegin_usingDDict(ZSTD_DCtx* dctx, const ZSTD_DDict* ddict);
+ZSTDLIB_STATIC_API size_t ZSTD_decompressBegin(ZSTD_DCtx* dctx);
+ZSTDLIB_STATIC_API size_t ZSTD_decompressBegin_usingDict(ZSTD_DCtx* dctx, const void* dict, size_t dictSize);
+ZSTDLIB_STATIC_API size_t ZSTD_decompressBegin_usingDDict(ZSTD_DCtx* dctx, const ZSTD_DDict* ddict);
-ZSTDLIB_API size_t ZSTD_nextSrcSizeToDecompress(ZSTD_DCtx* dctx);
-ZSTDLIB_API size_t ZSTD_decompressContinue(ZSTD_DCtx* dctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize);
+ZSTDLIB_STATIC_API size_t ZSTD_nextSrcSizeToDecompress(ZSTD_DCtx* dctx);
+ZSTDLIB_STATIC_API size_t ZSTD_decompressContinue(ZSTD_DCtx* dctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize);
/* misc */
-ZSTDLIB_API void ZSTD_copyDCtx(ZSTD_DCtx* dctx, const ZSTD_DCtx* preparedDCtx);
+ZSTD_DEPRECATED("This function will likely be removed in the next minor release. It is misleading and has very limited utility.")
+ZSTDLIB_STATIC_API void ZSTD_copyDCtx(ZSTD_DCtx* dctx, const ZSTD_DCtx* preparedDCtx);
typedef enum { ZSTDnit_frameHeader, ZSTDnit_blockHeader, ZSTDnit_block, ZSTDnit_lastBlock, ZSTDnit_checksum, ZSTDnit_skippableFrame } ZSTD_nextInputType_e;
-ZSTDLIB_API ZSTD_nextInputType_e ZSTD_nextInputType(ZSTD_DCtx* dctx);
+ZSTDLIB_STATIC_API ZSTD_nextInputType_e ZSTD_nextInputType(ZSTD_DCtx* dctx);
-/* ============================ */
-/** Block level API */
-/* ============================ */
+/* ========================================= */
+/** Block level API (DEPRECATED) */
+/* ========================================= */
/*!
+
+ This API is deprecated in favor of the regular compression API.
+ You can get the frame header down to 2 bytes by setting:
+ - ZSTD_c_format = ZSTD_f_zstd1_magicless
+ - ZSTD_c_contentSizeFlag = 0
+ - ZSTD_c_checksumFlag = 0
+ - ZSTD_c_dictIDFlag = 0
+
+ This API is not as well tested as our normal API, so we recommend not using it.
+ We will be removing it in a future version. If the normal API doesn't provide
+ the functionality you need, please open a GitHub issue.
+
Block functions produce and decode raw zstd blocks, without frame metadata.
- Frame metadata cost is typically ~18 bytes, which can be non-negligible for very small blocks (< 100 bytes).
- User will have to take in charge required information to regenerate data, such as compressed and content sizes.
+ Frame metadata cost is typically ~12 bytes, which can be non-negligible for very small blocks (< 100 bytes).
+ But users will have to take in charge needed metadata to regenerate data, such as compressed and content sizes.
A few rules to respect :
- Compressing and decompressing require a context structure
@@ -1733,26 +2988,30 @@ ZSTDLIB_API ZSTD_nextInputType_e ZSTD_nextInputType(ZSTD_DCtx* dctx);
- It is necessary to init context before starting
+ compression : any ZSTD_compressBegin*() variant, including with dictionary
+ decompression : any ZSTD_decompressBegin*() variant, including with dictionary
- + copyCCtx() and copyDCtx() can be used too
- Block size is limited, it must be <= ZSTD_getBlockSize() <= ZSTD_BLOCKSIZE_MAX == 128 KB
+ If input is larger than a block size, it's necessary to split input data into multiple blocks
- + For inputs larger than a single block, really consider using regular ZSTD_compress() instead.
- Frame metadata is not that costly, and quickly becomes negligible as source size grows larger.
- - When a block is considered not compressible enough, ZSTD_compressBlock() result will be zero.
- In which case, nothing is produced into `dst` !
- + User must test for such outcome and deal directly with uncompressed data
- + ZSTD_decompressBlock() doesn't accept uncompressed data as input !!!
+ + For inputs larger than a single block, consider using regular ZSTD_compress() instead.
+ Frame metadata is not that costly, and quickly becomes negligible as source size grows larger than a block.
+ - When a block is considered not compressible enough, ZSTD_compressBlock() result will be 0 (zero) !
+ ===> In which case, nothing is produced into `dst` !
+ + User __must__ test for such outcome and deal directly with uncompressed data
+ + A block cannot be declared incompressible if ZSTD_compressBlock() return value was != 0.
+ Doing so would mess up with statistics history, leading to potential data corruption.
+ + ZSTD_decompressBlock() _doesn't accept uncompressed data as input_ !!
+ In case of multiple successive blocks, should some of them be uncompressed,
decoder must be informed of their existence in order to follow proper history.
Use ZSTD_insertBlock() for such a case.
*/
/*===== Raw zstd block functions =====*/
-ZSTDLIB_API size_t ZSTD_getBlockSize (const ZSTD_CCtx* cctx);
-ZSTDLIB_API size_t ZSTD_compressBlock (ZSTD_CCtx* cctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize);
-ZSTDLIB_API size_t ZSTD_decompressBlock(ZSTD_DCtx* dctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize);
-ZSTDLIB_API size_t ZSTD_insertBlock (ZSTD_DCtx* dctx, const void* blockStart, size_t blockSize); /**< insert uncompressed block into `dctx` history. Useful for multi-blocks decompression. */
-
+ZSTD_DEPRECATED("The block API is deprecated in favor of the normal compression API. See docs.")
+ZSTDLIB_STATIC_API size_t ZSTD_getBlockSize (const ZSTD_CCtx* cctx);
+ZSTD_DEPRECATED("The block API is deprecated in favor of the normal compression API. See docs.")
+ZSTDLIB_STATIC_API size_t ZSTD_compressBlock (ZSTD_CCtx* cctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize);
+ZSTD_DEPRECATED("The block API is deprecated in favor of the normal compression API. See docs.")
+ZSTDLIB_STATIC_API size_t ZSTD_decompressBlock(ZSTD_DCtx* dctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize);
+ZSTD_DEPRECATED("The block API is deprecated in favor of the normal compression API. See docs.")
+ZSTDLIB_STATIC_API size_t ZSTD_insertBlock (ZSTD_DCtx* dctx, const void* blockStart, size_t blockSize); /**< insert uncompressed block into `dctx` history. Useful for multi-blocks decompression. */
#endif /* ZSTD_H_ZSTD_STATIC_LINKING_ONLY */
diff --git a/lib/windows/libzstd_static.lib b/lib/windows/libzstd_static.lib
index e8ec8a8fc1..ea5b9a0bd0 100755
Binary files a/lib/windows/libzstd_static.lib and b/lib/windows/libzstd_static.lib differ
diff --git a/lib/windows/libzstd_static_VS.lib b/lib/windows/libzstd_static_VS.lib
index 07b189d8f9..ddeb05c500 100644
Binary files a/lib/windows/libzstd_static_VS.lib and b/lib/windows/libzstd_static_VS.lib differ
diff --git a/lib/windows/libzstdd_static_VS.lib b/lib/windows/libzstdd_static_VS.lib
index 5a1192a787..4e39cddfba 100644
Binary files a/lib/windows/libzstdd_static_VS.lib and b/lib/windows/libzstdd_static_VS.lib differ
diff --git a/lib/windows64/libwebp.lib b/lib/windows64/libwebp.lib
new file mode 100644
index 0000000000..466ea99304
Binary files /dev/null and b/lib/windows64/libwebp.lib differ
diff --git a/lib/windows64/libwebpdecoder.lib b/lib/windows64/libwebpdecoder.lib
new file mode 100644
index 0000000000..8c8c4f39ba
Binary files /dev/null and b/lib/windows64/libwebpdecoder.lib differ
diff --git a/lib/windows64/libwebpdemux.lib b/lib/windows64/libwebpdemux.lib
new file mode 100644
index 0000000000..684987edca
Binary files /dev/null and b/lib/windows64/libwebpdemux.lib differ
diff --git a/lib/windows64/libzstd_static.lib b/lib/windows64/libzstd_static.lib
index 545b7de107..3308c117d4 100755
Binary files a/lib/windows64/libzstd_static.lib and b/lib/windows64/libzstd_static.lib differ
diff --git a/lib/windows64/libzstd_static_VS.lib b/lib/windows64/libzstd_static_VS.lib
index ed26567906..acf05498cb 100644
Binary files a/lib/windows64/libzstd_static_VS.lib and b/lib/windows64/libzstd_static_VS.lib differ
diff --git a/lib/windows64/libzstdd_static_VS.lib b/lib/windows64/libzstdd_static_VS.lib
index 0274f2c03e..ac839fdabb 100644
Binary files a/lib/windows64/libzstdd_static_VS.lib and b/lib/windows64/libzstdd_static_VS.lib differ
diff --git a/macOS b/macOS
index 01b6edda3a..0b35e0e386 160000
--- a/macOS
+++ b/macOS
@@ -1 +1 @@
-Subproject commit 01b6edda3ab35602dea5fafeb91bd8e08df9a3f1
+Subproject commit 0b35e0e386bae1d3901c36f8ac2d0a24ee9c274d
diff --git a/xLights/BatchRenderDialog.cpp b/xLights/BatchRenderDialog.cpp
index 8859eea06d..dbb4055b94 100644
--- a/xLights/BatchRenderDialog.cpp
+++ b/xLights/BatchRenderDialog.cpp
@@ -164,7 +164,7 @@ void BatchRenderDialog::OnPopupCommand(wxCommandEvent &event)
CheckListBox_Sequences->UncheckItem(item);
} else if (id == ID_MNU_SELECTHIGH && !isChecked && isSelected) {
CheckListBox_Sequences->CheckItem(item);
- } else if (id == ID_MNU_SELECTHIGH && isChecked && isSelected) {
+ } else if (id == ID_MNU_DESELECTHIGH && isChecked && isSelected) {
CheckListBox_Sequences->UncheckItem(item);
}
item = CheckListBox_Sequences->GetNextItem(item);
diff --git a/xLights/BitmapCache.cpp b/xLights/BitmapCache.cpp
index 567128793e..f891b6ec1f 100644
--- a/xLights/BitmapCache.cpp
+++ b/xLights/BitmapCache.cpp
@@ -110,7 +110,9 @@
#include "../include/cc_radialout.xpm"
#include "../include/save.xpm"
#include "../include/delete.xpm"
-#include "../include/switch.xpm"
+#include "../include/reverse.xpm"
+#include "../include/left_shift.xpm"
+#include "../include/right_shift.xpm"
#include "../include/valuecurvenotselected.xpm"
@@ -509,8 +511,12 @@ wxBitmapBundle xlArtProvider::CreateBitmapBundle(const wxArtID& id,
return CreateBitmapBundleFromXPMs(sz, "cc_radialin_xpm", {cc_radialin_xpm, cc_radialin_xpm});
} else if ("xlART_cc_radialout_xpm" == id) {
return CreateBitmapBundleFromXPMs(sz, "cc_radialout_xpm", {cc_radialout_xpm, cc_radialout_xpm});
- } else if ("xlART_colorpanel_switch_xpm" == id) {
- return CreateBitmapBundleFromXPMs(sz, "colorpanel_switch_xpm", {switch_xpm, switch_xpm});
+ } else if ("xlART_colorpanel_reverse_xpm" == id) {
+ return CreateBitmapBundleFromXPMs(sz, "colorpanel_switch_xpm", {reverse_xpm, reverse_xpm});
+ } else if ("xlART_colorpanel_left_shift_xpm" == id) {
+ return CreateBitmapBundleFromXPMs(sz, "colorpanel_left_shift_xpm", { left_shift_xpm, left_shift_xpm });
+ } else if ("xlART_colorpanel_right_shift_xpm" == id) {
+ return CreateBitmapBundleFromXPMs(sz, "colorpanel_right_shift_xpm", { right_shift_xpm, right_shift_xpm });
} else if ("xlART_colorpanel_delete_xpm" == id) {
return CreateBitmapBundleFromXPMs(sz, "colorpanel_delete_xpm", {delete_xpm, delete_xpm});
} else if ("xlART_colorpanel_save_xpm" == id) {
diff --git a/xLights/CachedFileDownloader.cpp b/xLights/CachedFileDownloader.cpp
index 7210b5e9bb..fc5b9821f4 100644
--- a/xLights/CachedFileDownloader.cpp
+++ b/xLights/CachedFileDownloader.cpp
@@ -144,6 +144,7 @@ void CachedFileDownloader::SaveCache()
{
static log4cpp::Category &logger_base = log4cpp::Category::getInstance(std::string("log_base"));
+ std::lock_guard lock(_cacheItemsLock);
if (!Initialize())
{
return;
@@ -184,6 +185,7 @@ void CachedFileDownloader::LoadCache()
{
static log4cpp::Category &logger_base = log4cpp::Category::getInstance(std::string("log_base"));
+ std::lock_guard lock(_cacheItemsLock);
_cacheItems.clear();
if (!Initialize())
@@ -224,6 +226,7 @@ void CachedFileDownloader::LoadCache()
FileCacheItem* CachedFileDownloader::Find(wxURI url)
{
+ std::lock_guard lock(_cacheItemsLock);
for (const auto& it : _cacheItems)
{
if (*it == url)
@@ -236,7 +239,9 @@ FileCacheItem* CachedFileDownloader::Find(wxURI url)
}
static std::unique_ptr _defaultCache(nullptr);
+static std::mutex _defaultCacheLock;
CachedFileDownloader& CachedFileDownloader::GetDefaultCache() {
+ std::unique_lock lock(_defaultCacheLock);
if (_defaultCache.get() == nullptr) {
std::unique_ptr tmp(new CachedFileDownloader());
_defaultCache = std::move(tmp);
@@ -244,11 +249,12 @@ CachedFileDownloader& CachedFileDownloader::GetDefaultCache() {
return *_defaultCache.get();
}
-CachedFileDownloader::CachedFileDownloader(const std::string cacheDir) : _cacheDir(cacheDir)
+CachedFileDownloader::CachedFileDownloader() : _cacheDir("")
{
_initialised = false;
}
bool CachedFileDownloader::Initialize() {
+ std::lock_guard lock(_cacheItemsLock);
if (_initialised) {
return _enabled;
}
@@ -293,6 +299,7 @@ void CachedFileDownloader::ClearCache()
static log4cpp::Category &logger_base = log4cpp::Category::getInstance(std::string("log_base"));
logger_base.debug("File Cache cleared.");
+ std::lock_guard lock(_cacheItemsLock);
for (const auto& it : _cacheItems)
{
it->Delete();
@@ -304,6 +311,7 @@ void CachedFileDownloader::PurgeAgedItems()
static log4cpp::Category &logger_base = log4cpp::Category::getInstance(std::string("log_base"));
logger_base.debug("File Cache purging aged items.");
+ std::lock_guard lock(_cacheItemsLock);
for (const auto& it : _cacheItems)
{
it->PurgeIfAged();
@@ -311,6 +319,7 @@ void CachedFileDownloader::PurgeAgedItems()
}
int CachedFileDownloader::size() {
+ std::lock_guard lock(_cacheItemsLock);
if (!Initialize()) {
return 0;
}
@@ -320,7 +329,7 @@ int CachedFileDownloader::size() {
std::string CachedFileDownloader::GetFile(wxURI url, CACHEFOR cacheFor, const wxString& forceType, wxProgressDialog* prog, int low, int high, bool keepProgress)
{
static log4cpp::Category &logger_base = log4cpp::Category::getInstance(std::string("log_base"));
-
+ std::lock_guard lock(_cacheItemsLock);
if (!Initialize())
{
// because we dont have a valid place to save the cache we cant cache anything beyond this session
diff --git a/xLights/CachedFileDownloader.h b/xLights/CachedFileDownloader.h
index 4b4745c5de..2b7adcf28f 100644
--- a/xLights/CachedFileDownloader.h
+++ b/xLights/CachedFileDownloader.h
@@ -14,6 +14,7 @@
#include
#include
#include
+#include
class wxProgressDialog;
class wxXmlNode;
@@ -53,6 +54,7 @@ class CachedFileDownloader
{
std::string _cacheDir;
std::list _cacheItems;
+ std::recursive_mutex _cacheItemsLock;
std::string _cacheFile;
bool _initialised;
bool _enabled;
@@ -63,9 +65,9 @@ class CachedFileDownloader
bool Initialize();
FileCacheItem* Find(wxURI url);
+ CachedFileDownloader();
public:
- CachedFileDownloader(const std::string cacheDir = "");
virtual ~CachedFileDownloader();
// erase everything from cache
void ClearCache();
diff --git a/xLights/ColorPanel.cpp b/xLights/ColorPanel.cpp
index 1b1488df26..a40c98c412 100644
--- a/xLights/ColorPanel.cpp
+++ b/xLights/ColorPanel.cpp
@@ -44,10 +44,12 @@
#define PALETTE_SIZE 8
//(*IdInit(ColorPanel)
-const long ColorPanel::ID_BITMAPBUTTON4 = wxNewId();
+const long ColorPanel::ID_BITMAPBUTTON_ReverseColours = wxNewId();
+const long ColorPanel::ID_BITMAPBUTTON_LeftShiftColours = wxNewId();
+const long ColorPanel::ID_BITMAPBUTTON_RightShiftColours = wxNewId();
const long ColorPanel::ID_CUSTOM1 = wxNewId();
-const long ColorPanel::ID_BITMAPBUTTON3 = wxNewId();
const long ColorPanel::ID_BUTTON1 = wxNewId();
+const long ColorPanel::ID_BITMAPBUTTON3 = wxNewId();
const long ColorPanel::ID_BITMAPBUTTON2 = wxNewId();
const long ColorPanel::ID_CHECKBOX_ResetColorPanel = wxNewId();
const long ColorPanel::ID_STATICTEXT1 = wxNewId();
@@ -251,6 +253,9 @@ ColorPanel::ColorPanel(wxWindow* parent, wxWindowID id,const wxPoint& pos,const
_supportsradial = false;
//(*Initialize(ColorPanel)
+ wxBoxSizer* BoxSizer1;
+ wxBoxSizer* BoxSizer2;
+ wxBoxSizer* BoxSizer3;
wxButton* ButtonColor1;
wxFlexGridSizer* FlexGridSizer10;
wxFlexGridSizer* FlexGridSizer11;
@@ -290,17 +295,26 @@ ColorPanel::ColorPanel(wxWindow* parent, wxWindowID id,const wxPoint& pos,const
FlexGridSizer9->Add(FlexGridSizer10, 1, wxALL|wxALIGN_RIGHT, 2);
FlexGridSizer11 = new wxFlexGridSizer(0, 3, 0, 0);
FlexGridSizer11->AddGrowableCol(1);
- BitmapButton_ShuffleColours = new xlSizedBitmapButton(ColorScrollWindow, ID_BITMAPBUTTON4, wxArtProvider::GetBitmapBundle("xlART_colorpanel_switch_xpm", wxART_BUTTON), wxDefaultPosition, wxSize(24,24), wxBU_AUTODRAW|wxBORDER_NONE, wxDefaultValidator, _T("ID_BITMAPBUTTON4"));
- FlexGridSizer11->Add(BitmapButton_ShuffleColours, 1, wxALL|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5);
+ BoxSizer1 = new wxBoxSizer(wxVERTICAL);
+ BitmapButton_ReverseColours = new xlSizedBitmapButton(ColorScrollWindow, ID_BITMAPBUTTON_ReverseColours, wxArtProvider::GetBitmapBundle("xlART_colorpanel_reverse_xpm", wxART_BUTTON), wxDefaultPosition, wxSize(26,16), wxBU_AUTODRAW|wxBORDER_NONE, wxDefaultValidator, _T("ID_BITMAPBUTTON_ReverseColours"));
+ BoxSizer1->Add(BitmapButton_ReverseColours, 1, wxALL|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 2);
+ BitmapButton_LeftShiftColours = new xlSizedBitmapButton(ColorScrollWindow, ID_BITMAPBUTTON_LeftShiftColours, wxArtProvider::GetBitmapBundle("xlART_colorpanel_left_shift_xpm", wxART_BUTTON), wxDefaultPosition, wxSize(26,16), wxBU_AUTODRAW, wxDefaultValidator, _T("ID_BITMAPBUTTON_LeftShiftColours"));
+ BoxSizer1->Add(BitmapButton_LeftShiftColours, 1, wxALL|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 2);
+ BitmapButton_RightShiftColours = new xlSizedBitmapButton(ColorScrollWindow, ID_BITMAPBUTTON_RightShiftColours, wxArtProvider::GetBitmapBundle("xlART_colorpanel_right_shift_xpm", wxART_BUTTON), wxDefaultPosition, wxSize(26,16), wxBU_AUTODRAW, wxDefaultValidator, _T("ID_BITMAPBUTTON_RightShiftColours"));
+ BoxSizer1->Add(BitmapButton_RightShiftColours, 1, wxALL|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 2);
+ FlexGridSizer11->Add(BoxSizer1, 1, wxALL|wxALIGN_TOP|wxALIGN_CENTER_HORIZONTAL, 5);
+ BoxSizer2 = new wxBoxSizer(wxVERTICAL);
BitmapButton_ColourChoice = new ColourList(ColorScrollWindow,ID_CUSTOM1,wxDefaultPosition,wxDefaultSize,ZERO,wxDefaultValidator,_T("ID_CUSTOM1"));
- FlexGridSizer11->Add(BitmapButton_ColourChoice, 1, wxALL|wxEXPAND, 2);
- BitmapButton_SavePalette = new xlSizedBitmapButton(ColorScrollWindow, ID_BITMAPBUTTON3, wxArtProvider::GetBitmapBundle("xlART_colorpanel_save_xpm", wxART_BUTTON), wxDefaultPosition, wxSize(24,24), wxBU_AUTODRAW|wxBORDER_NONE, wxDefaultValidator, _T("ID_BITMAPBUTTON3"));
- FlexGridSizer11->Add(BitmapButton_SavePalette, 1, wxALL|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5);
- FlexGridSizer11->Add(-1,-1,1, wxALL|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5);
+ BoxSizer2->Add(BitmapButton_ColourChoice, 1, wxALL|wxEXPAND, 2);
ButtonColor1 = new wxButton(ColorScrollWindow, ID_BUTTON1, _("Update"), wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator, _T("ID_BUTTON1"));
- FlexGridSizer11->Add(ButtonColor1, 1, wxALL|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5);
+ BoxSizer2->Add(ButtonColor1, 1, wxALL|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5);
+ FlexGridSizer11->Add(BoxSizer2, 1, wxALL|wxALIGN_TOP|wxALIGN_CENTER_HORIZONTAL, 5);
+ BoxSizer3 = new wxBoxSizer(wxVERTICAL);
+ BitmapButton_SavePalette = new xlSizedBitmapButton(ColorScrollWindow, ID_BITMAPBUTTON3, wxArtProvider::GetBitmapBundle("xlART_colorpanel_save_xpm", wxART_BUTTON), wxDefaultPosition, wxSize(24,24), wxBU_AUTODRAW|wxBORDER_NONE, wxDefaultValidator, _T("ID_BITMAPBUTTON3"));
+ BoxSizer3->Add(BitmapButton_SavePalette, 1, wxALL|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5);
BitmapButton_DeletePalette = new xlSizedBitmapButton(ColorScrollWindow, ID_BITMAPBUTTON2, wxArtProvider::GetBitmapBundle("xlART_colorpanel_delete_xpm", wxART_BUTTON), wxDefaultPosition, wxSize(24,24), wxBU_AUTODRAW|wxBORDER_NONE, wxDefaultValidator, _T("ID_BITMAPBUTTON2"));
- FlexGridSizer11->Add(BitmapButton_DeletePalette, 1, wxALL|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5);
+ BoxSizer3->Add(BitmapButton_DeletePalette, 1, wxALL|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5);
+ FlexGridSizer11->Add(BoxSizer3, 1, wxALL|wxALIGN_TOP|wxALIGN_CENTER_HORIZONTAL, 5);
FlexGridSizer9->Add(FlexGridSizer11, 1, wxALL|wxALIGN_LEFT, 2);
FlexGridSizer9->Add(-1,-1,1, wxALL|wxEXPAND, 5);
FlexGridSizer5->Add(FlexGridSizer9, 1, wxALL|wxEXPAND, 0);
@@ -412,14 +426,22 @@ ColorPanel::ColorPanel(wxWindow* parent, wxWindowID id,const wxPoint& pos,const
FlexGridSizer12->Add(TextCtrl_Color_ValueAdjust, 1, wxALL|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 2);
FlexGridSizer4->Add(FlexGridSizer12, 1, wxALL|wxEXPAND, 2);
ColorScrollWindow->SetSizer(FlexGridSizer4);
+ FlexGridSizer4->Fit(ColorScrollWindow);
+ FlexGridSizer4->SetSizeHints(ColorScrollWindow);
FlexGridSizer3->Add(ColorScrollWindow, 1, wxALL|wxEXPAND, 0);
Panel_Sizer->SetSizer(FlexGridSizer3);
+ FlexGridSizer3->Fit(Panel_Sizer);
+ FlexGridSizer3->SetSizeHints(Panel_Sizer);
FlexGridSizer1->Add(Panel_Sizer, 1, wxALL|wxEXPAND, 0);
SetSizer(FlexGridSizer1);
+ FlexGridSizer1->Fit(this);
+ FlexGridSizer1->SetSizeHints(this);
- Connect(ID_BITMAPBUTTON4,wxEVT_COMMAND_BUTTON_CLICKED,(wxObjectEventFunction)&ColorPanel::OnBitmapButton_ShuffleColoursClick);
- Connect(ID_BITMAPBUTTON3,wxEVT_COMMAND_BUTTON_CLICKED,(wxObjectEventFunction)&ColorPanel::OnBitmapButton_SavePaletteClick);
+ Connect(ID_BITMAPBUTTON_ReverseColours, wxEVT_COMMAND_BUTTON_CLICKED, (wxObjectEventFunction)&ColorPanel::OnBitmapButton_ReverseColoursClick);
+ Connect(ID_BITMAPBUTTON_LeftShiftColours, wxEVT_COMMAND_BUTTON_CLICKED, (wxObjectEventFunction)&ColorPanel::OnBitmapButton_ShiftColoursLeftClick);
+ Connect(ID_BITMAPBUTTON_RightShiftColours,wxEVT_COMMAND_BUTTON_CLICKED,(wxObjectEventFunction)&ColorPanel::OnBitmapButton_ShiftColoursRightClick);
Connect(ID_BUTTON1,wxEVT_COMMAND_BUTTON_CLICKED,(wxObjectEventFunction)&ColorPanel::OnUpdateColorClick);
+ Connect(ID_BITMAPBUTTON3,wxEVT_COMMAND_BUTTON_CLICKED,(wxObjectEventFunction)&ColorPanel::OnBitmapButton_SavePaletteClick);
Connect(ID_BITMAPBUTTON2,wxEVT_COMMAND_BUTTON_CLICKED,(wxObjectEventFunction)&ColorPanel::OnBitmapButton_DeletePaletteClick);
Connect(ID_CHECKBOX_ResetColorPanel,wxEVT_COMMAND_CHECKBOX_CLICKED,(wxObjectEventFunction)&ColorPanel::OnCheckBox_ResetColorPanelClick);
Connect(ID_CHECKBOX_Chroma,wxEVT_COMMAND_CHECKBOX_CLICKED,(wxObjectEventFunction)&ColorPanel::OnCheckBox_EnableChromakeyClick);
@@ -1374,7 +1396,7 @@ void ColorPanel::OnBitmapButton_DeletePaletteClick(wxCommandEvent& event)
ValidateWindow();
}
-void ColorPanel::OnBitmapButton_ShuffleColoursClick(wxCommandEvent& event)
+void ColorPanel::OnBitmapButton_ReverseColoursClick(wxCommandEvent& event)
{
std::string pal = GetCurrentPalette();
@@ -1398,6 +1420,82 @@ void ColorPanel::OnBitmapButton_ShuffleColoursClick(wxCommandEvent& event)
ValidateWindow();
}
+void ColorPanel::OnBitmapButton_ShiftColoursLeftClick(wxCommandEvent& event)
+{
+ std::string pal = GetCurrentPalette();
+
+ wxArrayString as = wxSplit(pal, ',');
+
+ bool zeroActive = as[0].Contains("Active");
+ std::string zeroItem = as[0].ToStdString();
+
+ for (size_t i = 0; i < PALETTE_SIZE - 1; ++i)
+ {
+ if (as[i + 1].Contains("Active"))
+ {
+ buttons[i]->GetValue()->Deserialise(as[i + 1].ToStdString());
+ buttons[i]->SetActive(true);
+ buttons[i]->Refresh();
+ }
+ else
+ {
+ buttons[i]->SetColor(as[i + 1].ToStdString());
+ buttons[i]->Refresh();
+ }
+ }
+ if (zeroActive)
+ {
+ buttons[PALETTE_SIZE - 1]->GetValue()->Deserialise(zeroItem);
+ buttons[PALETTE_SIZE - 1]->SetActive(true);
+ buttons[PALETTE_SIZE - 1]->Refresh();
+ }
+ else
+ {
+ buttons[PALETTE_SIZE - 1]->SetColor(zeroItem);
+ buttons[PALETTE_SIZE - 1]->Refresh();
+ }
+ FireChangeEvent();
+ ValidateWindow();
+}
+
+void ColorPanel::OnBitmapButton_ShiftColoursRightClick(wxCommandEvent& event)
+{
+ std::string pal = GetCurrentPalette();
+
+ wxArrayString as = wxSplit(pal, ',');
+
+ bool lastActive = as[PALETTE_SIZE - 1].Contains("Active");
+ std::string lastItem = as[PALETTE_SIZE - 1].ToStdString();
+
+ for (size_t i = PALETTE_SIZE - 1; i > 0; --i)
+ {
+ if (as[i - 1].Contains("Active"))
+ {
+ buttons[i]->GetValue()->Deserialise(as[i - 1].ToStdString());
+ buttons[i]->SetActive(true);
+ buttons[i]->Refresh();
+ }
+ else
+ {
+ buttons[i]->SetColor(as[i - 1].ToStdString());
+ buttons[i]->Refresh();
+ }
+ }
+ if (lastActive)
+ {
+ buttons[0]->GetValue()->Deserialise(lastItem);
+ buttons[0]->SetActive(true);
+ buttons[0]->Refresh();
+ }
+ else
+ {
+ buttons[0]->SetColor(lastItem);
+ buttons[0]->Refresh();
+ }
+ FireChangeEvent();
+ ValidateWindow();
+}
+
void ColorPanel::OnCCButtonClick(wxCommandEvent& event)
{
wxBitmapButton* bb = static_cast(event.GetEventObject());
diff --git a/xLights/ColorPanel.h b/xLights/ColorPanel.h
index e86b8c1940..df951f79ac 100644
--- a/xLights/ColorPanel.h
+++ b/xLights/ColorPanel.h
@@ -77,9 +77,9 @@ class ColorPanel: public xlEffectPanel
bool PaletteChanged;
bool EffectChanged;
-
+
void SetDefaultPalette();
-
+
static double GetSettingVCMin(const std::string& name)
{
if (name == "C_VALUECURVE_Brightness")
@@ -175,17 +175,21 @@ class ColorPanel: public xlEffectPanel
xlLockButton* BitmapButton_MusicSparkles;
xlLockButton* BitmapButton_SparkleFrequency;
xlSizedBitmapButton* BitmapButton_DeletePalette;
+ xlSizedBitmapButton* BitmapButton_LeftShiftColours;
+ xlSizedBitmapButton* BitmapButton_ReverseColours;
+ xlSizedBitmapButton* BitmapButton_RightShiftColours;
xlSizedBitmapButton* BitmapButton_SavePalette;
- xlSizedBitmapButton* BitmapButton_ShuffleColours;
//*)
protected:
//(*Identifiers(ColorPanel)
- static const long ID_BITMAPBUTTON4;
+ static const long ID_BITMAPBUTTON_ReverseColours;
+ static const long ID_BITMAPBUTTON_LeftShiftColours;
+ static const long ID_BITMAPBUTTON_RightShiftColours;
static const long ID_CUSTOM1;
- static const long ID_BITMAPBUTTON3;
static const long ID_BUTTON1;
+ static const long ID_BITMAPBUTTON3;
static const long ID_BITMAPBUTTON2;
static const long ID_CHECKBOX_ResetColorPanel;
static const long ID_STATICTEXT1;
@@ -235,10 +239,12 @@ class ColorPanel: public xlEffectPanel
void OnCheckBox_MusicSparklesClick(wxCommandEvent& event);
void OnBitmapButton_SavePaletteClick(wxCommandEvent& event);
void OnBitmapButton_DeletePaletteClick(wxCommandEvent& event);
- void OnBitmapButton_ShuffleColoursClick(wxCommandEvent& event);
+ void OnBitmapButton_ReverseColoursClick(wxCommandEvent& event);
void UpdateTouchBarSlider(wxScrollEvent& event);
void OnCheckBox_ResetColorPanelClick(wxCommandEvent& event);
void OnCheckBox_EnableChromakeyClick(wxCommandEvent& event);
+ void OnBitmapButton_ShiftColoursLeftClick(wxCommandEvent& event);
+ void OnBitmapButton_ShiftColoursRightClick(wxCommandEvent& event);
//*)
void OnCCButtonClick(wxCommandEvent& event);
@@ -247,7 +253,7 @@ class ColorPanel: public xlEffectPanel
void OnColourChoiceDropDown(wxCommandEvent& event);
void OnColourChoiceSelect(wxCommandEvent& event);
wxColourData colorData;
-
+
std::vector buttons;
std::vector checkBoxes;
std::map lastColors;
diff --git a/xLights/ControllerModelDialog.cpp b/xLights/ControllerModelDialog.cpp
index 1b8cfcb587..f24ff1fe15 100644
--- a/xLights/ControllerModelDialog.cpp
+++ b/xLights/ControllerModelDialog.cpp
@@ -40,6 +40,7 @@
#include "outputs/Output.h"
#include
+#include
//(*IdInit(ControllerModelDialog)
const long ControllerModelDialog::ID_PANEL1 = wxNewId();
@@ -70,7 +71,13 @@ const long ControllerModelDialog::CONTROLLER_BRIGHTNESS = wxNewId();
const long ControllerModelDialog::CONTROLLER_BRIGHTNESSCLEAR = wxNewId();
const long ControllerModelDialog::CONTROLLER_REMOVEALLMODELS = wxNewId();
const long ControllerModelDialog::CONTROLLER_SMARTREMOTETYPE = wxNewId();
+const long ControllerModelDialog::CONTROLLER_REMOVESMARTREMOTE = wxNewId();
+const long ControllerModelDialog::CONTROLLER_SETSMARTREMOTE = wxNewId();
const long ControllerModelDialog::CONTROLLER_MODEL_STRINGS = wxNewId();
+const long ControllerModelDialog::CONTROLLER_STARTNULLS = wxNewId();
+const long ControllerModelDialog::CONTROLLER_ENDNULLS = wxNewId();
+const long ControllerModelDialog::CONTROLLER_COLORORDER = wxNewId();
+const long ControllerModelDialog::CONTROLLER_GROUPCOUNT = wxNewId();
BEGIN_EVENT_TABLE(ControllerModelDialog, wxDialog)
//(*EventTable(ControllerModelDialog)
@@ -117,6 +124,8 @@ wxColour __darkYellow(60, 60, 0, wxALPHA_OPAQUE);
wxColour __lightOrange(255, 201, 150, wxALPHA_OPAQUE);
wxColour __darkOrange(150, 54, 3, wxALPHA_OPAQUE);
wxColour __magenta(255, 0, 255, wxALPHA_OPAQUE);
+wxColour __lightShadow(211, 211, 211);
+wxColour __darkShadow(30, 24, 24);
wxColour __textForeground;
wxBrush __invalidBrush;
wxBrush __dropTargetBrush;
@@ -135,6 +144,7 @@ wxBrush __modelSRCBrush;
wxBrush __modelSRDBrush;
wxBrush __modelSREBrush;
wxBrush __modelSRFBrush;
+wxBrush __modelShadowBrush;
wxColour __modelSRAText;
wxColour __modelSRBText;
wxColour __modelSRCText;
@@ -189,6 +199,7 @@ void SetColours(bool printing)
__modelSRDBrush.SetColour(__darkAqua);
__modelSREBrush.SetColour(__darkPink);
__modelSRFBrush.SetColour(__darkYellow);
+ __modelShadowBrush.SetColour(__darkShadow);
__backgroundPen.SetColour(__charcoal);
__backgroundBrush.SetColour(__charcoal);
__modelSRAText = __lightGreen;
@@ -212,6 +223,7 @@ void SetColours(bool printing)
__modelSRDBrush.SetColour(__lightAqua);
__modelSREBrush.SetColour(__lightPink);
__modelSRFBrush.SetColour(__lightYellow);
+ __modelShadowBrush.SetColour(__lightShadow);
__backgroundPen.SetColour(*wxWHITE);
__backgroundBrush.SetColour(*wxWHITE);
__modelSRAText = *wxBLACK;
@@ -599,6 +611,12 @@ class PortCMObject : public BaseCMObject
if (_caps != nullptr && (_type == PORTTYPE::PIXEL) && _caps->SupportsSmartRemotes() && (_caps->GetSmartRemoteTypes().size() > 1)) {
mnu.Append(ControllerModelDialog::CONTROLLER_SMARTREMOTETYPE, "Set Smart Remote Type");
}
+ if (_caps != nullptr && (_type == PORTTYPE::PIXEL) && _caps->SupportsSmartRemotes()) {
+ mnu.Append(ControllerModelDialog::CONTROLLER_SETSMARTREMOTE, "Set Smart Remote ID and Increment");
+ }
+ if (_caps != nullptr && (_type == PORTTYPE::PIXEL) && _caps->SupportsSmartRemotes() && GetSmartRemoteCount() > 0) {
+ mnu.Append(ControllerModelDialog::CONTROLLER_REMOVESMARTREMOTE, "Remove Smart Remote");
+ }
mnu.Append(ControllerModelDialog::CONTROLLER_REMOVEPORTMODELS, "Remove All Models From Port");
if (_caps != nullptr && ((_type == PORTTYPE::PIXEL && _caps->GetMaxPixelPort() > 1) || (_type == PORTTYPE::SERIAL && _caps->GetMaxSerialPort() > 1))) {
mnu.Append(ControllerModelDialog::CONTROLLER_MOVEMODELSTOPORT, "Move All Models To Port");
@@ -740,6 +758,50 @@ class PortCMObject : public BaseCMObject
}
return true;
}
+ } else if (id == ControllerModelDialog::CONTROLLER_REMOVESMARTREMOTE) {
+ int basePort = GetBasePort();
+ for (uint8_t p = 0; p < 4; ++p) {
+ _cud->GetControllerPixelPort(basePort + p)->ClearSmartRemoteOnAllModels();
+ }
+ return true;
+ } else if (id == ControllerModelDialog::CONTROLLER_SETSMARTREMOTE) {
+ wxArrayString choices;
+ int sr_count = _caps->GetSmartRemoteCount();
+ if (_caps->GetVendor() == "HinksPix") {
+ for (int i = 0; i < sr_count; i++) {
+ choices.Add(wxString::Format("%d", i));
+ }
+ } else {
+ for (int i = 0; i < sr_count; i++) {
+ choices.Add(wxString(char(65 + i)));
+ }
+ }
+ int selection{ -1 };
+ if (GetUDPort()->GetModels().size() != 0 && nullptr != GetUDPort()->GetModels().front()) {
+ selection = GetUDPort()->GetModels().front()->GetSmartRemote() - 1;//0=none, 1=A,Falcon/FPP, 1=0,HinksPix
+ }
+ wxSingleChoiceDialog dlg(parent, "Port Smart Remote ID", "Smart Remote ID", choices);
+ if (selection >= 0 && selection < choices.size()) {
+ dlg.SetSelection(selection);
+ }
+ if (dlg.ShowModal() == wxID_OK) {
+ int startId = dlg.GetSelection();
+ std::string lastName;
+ for (const auto& it : GetUDPort()->GetModels()) {
+ if (lastName == it->GetModel()->Name()) {//skip multistring models sequentuial ports
+ continue;
+ }
+ it->GetModel()->SetSmartRemote(startId + 1);
+ int max_cas = std::min(it->GetModel()->GetSRMaxCascade(), (int)std::ceil(it->GetModel()->GetNumStrings() / 4.0));
+ max_cas = std::max(max_cas, 1);
+ startId += max_cas;
+ if (startId >= sr_count) {
+ startId = (sr_count - 1);
+ }
+ lastName = it->GetModel()->Name();
+ }
+ }
+ return true;
}
return false;
}
@@ -1117,10 +1179,18 @@ class SRCMObject : public BaseCMObject
auto mi = srMenu->AppendRadioItem(wxNewId(), "None");
if (_smartRemote == 0)
mi->Check();
- for (int i = 0; i < srcount; i++) {
- mi = srMenu->AppendRadioItem(wxNewId(), wxString(char(65 + i)));
- if (_smartRemote == i + 1)
- mi->Check();
+ if (_caps->GetVendor() == "HinksPix") {
+ for (int i = 0; i < srcount; i++) {
+ mi = srMenu->AppendRadioItem(wxNewId(), wxString::Format("%d", i));
+ if (_smartRemote == i + 1)
+ mi->Check();
+ }
+ } else {
+ for (int i = 0; i < srcount; i++) {
+ mi = srMenu->AppendRadioItem(wxNewId(), wxString(char(65 + i)));
+ if (_smartRemote == i + 1)
+ mi->Check();
+ }
}
srMenu->Connect(wxEVT_MENU, (wxObjectEventFunction)&ControllerModelDialog::OnPopupCommand, nullptr, cmd);
@@ -1134,6 +1204,9 @@ class SRCMObject : public BaseCMObject
if (label == "None") {
SetAllModelsToReceiver(_port, _smartRemote, 0);
return true;
+ } else if ((label >= "0" && label <= "9") || (label >= "10" && label <= "19")) {
+ SetAllModelsToReceiver(_port, _smartRemote, wxAtoi(label) + 1);
+ return true;
} else if (label >= "A" && label <= "Z") {
SetAllModelsToReceiver(_port, _smartRemote, int(label[0]) - 64);
return true;
@@ -1153,7 +1226,8 @@ class ModelCMObject : public BaseCMObject
int _string = 0;
UDControllerPort* _port = nullptr;
int _virtualString;
- bool _isShadow = false;
+ bool _isShadowed = false;
+ bool _isShadowFor = false;
public:
ModelCMObject(UDControllerPort* port, int virtualString, const std::string& name, const std::string displayName, ModelManager* mm, UDController* cud, ControllerCaps* caps, wxPoint location, wxSize size, int style, double scale) :
@@ -1170,10 +1244,12 @@ class ModelCMObject : public BaseCMObject
}
auto cmn = displayName.substr(0, displayName.find("-str-"));
if (GetModel() != nullptr) {
- _isShadow = GetModel()->GetShadowModelFor() != "" || GetModel()->GetModelManager().IsModelShadowing(GetModel());
+ _isShadowed = GetModel()->GetModelManager().IsModelShadowing(GetModel());
+ _isShadowFor = GetModel()->GetShadowModelFor() != "";
} else if (cmn != "" && (*mm)[cmn] != nullptr) {
auto m = (*mm)[cmn];
- _isShadow = m->GetShadowModelFor() != "" || mm->IsModelShadowing(m);
+ _isShadowed = mm->IsModelShadowing(m);
+ _isShadowFor = m->GetShadowModelFor() != "";
}
}
@@ -1295,7 +1371,11 @@ class ModelCMObject : public BaseCMObject
wxSize sz = _size;
sz = sz.Scale(scale, scale);
- if (_isShadow) {
+ if (_isShadowed) {
+ dc.SetPen(wxPen(dc.GetPen().GetColour(), dc.GetPen().GetWidth(), wxPENSTYLE_LONG_DASH));
+ dc.SetBrush(__modelShadowBrush);
+ }
+ if (_isShadowFor) {
dc.SetPen(wxPen(dc.GetPen().GetColour(), dc.GetPen().GetWidth(), wxPENSTYLE_LONG_DASH));
}
dc.DrawRectangle(location + offset, sz);
@@ -1366,21 +1446,28 @@ class ModelCMObject : public BaseCMObject
if (!GetModel()->HasSingleNode(GetModel()->GetStringType()) && GetModel()->SupportsChangingStringCount()) {
mnu.AppendSeparator();
mnu.Append(ControllerModelDialog::CONTROLLER_MODEL_STRINGS, "Change String Count");
- mnu.AppendSeparator();
}
if (_caps->SupportsSmartRemotes()) {
wxMenu* srMenu = new wxMenu();
+ mnu.AppendSeparator();
int srcount = _caps->GetSmartRemoteCount();
auto mi = srMenu->AppendRadioItem(wxNewId(), "None");
- if (GetModel()->GetSmartRemote() == 0)
+ if (GetModel()->GetSmartRemote() == 0){
mi->Check();
+ }
+
for (int i = 0; i < srcount; i++) {
- mi = srMenu->AppendRadioItem(wxNewId(), wxString(char(65 + i)));
- if (GetModel()->GetSmartRemote() == i + 1)
+ if (_caps->GetVendor() == "HinksPix") {
+ mi = srMenu->AppendRadioItem(wxNewId(), wxString::Format("%d", i), "SR Port");
+ } else {
+ mi = srMenu->AppendRadioItem(wxNewId(), wxString(char(65 + i)), "SR Port");
+ }
+ if (GetModel()->GetSmartRemote() == i + 1) {
mi->Check();
+ }
}
if (_caps->GetSmartRemoteTypes().size() > 1) {
@@ -1401,9 +1488,10 @@ class ModelCMObject : public BaseCMObject
wxMenu* srMax = new wxMenu();
for (int i = 0; i < srcount; i++) {
- mi = srMax->AppendRadioItem(wxNewId(), wxString::Format("%d", i + 1));
- if (GetModel()->GetSRMaxCascade() == i + 1)
+ mi = srMax->AppendRadioItem(wxNewId(), wxString::Format("%d", i + 1), "Cascade");
+ if (GetModel()->GetSRMaxCascade() == i + 1) {
mi->Check();
+ }
}
srMenu->AppendSubMenu(srMax, "Cascaded Remotes");
@@ -1420,6 +1508,19 @@ class ModelCMObject : public BaseCMObject
mnu.Append(ControllerModelDialog::CONTROLLER_BRIGHTNESSCLEAR, "Clear Brightness");
}
}
+ if (_caps->SupportsPixelPortNullPixels()) {
+ mnu.Append(ControllerModelDialog::CONTROLLER_STARTNULLS, "Set Start Nulls");
+ }
+ if (_caps->SupportsPixelPortEndNullPixels()) {
+ mnu.Append(ControllerModelDialog::CONTROLLER_ENDNULLS, "Set End Nulls");
+ }
+ if (_caps->SupportsPixelPortColourOrder()) {
+ mnu.Append(ControllerModelDialog::CONTROLLER_COLORORDER, "Set Color Order");
+ }
+ if (_caps->SupportsPixelPortGrouping()) {
+ mnu.Append(ControllerModelDialog::CONTROLLER_GROUPCOUNT, "Set Group Count");
+ }
+
} else if (GetModel() != nullptr && GetModel()->IsSerialProtocol()) {
mnu.AppendSeparator();
mnu.Append(ControllerModelDialog::CONTROLLER_DMXCHANNEL, "Set Channel");
@@ -1462,6 +1563,35 @@ class ModelCMObject : public BaseCMObject
GetModel()->SetControllerBrightness(dlg.GetValue());
}
return true;
+ } else if (id == ControllerModelDialog::CONTROLLER_STARTNULLS) {
+ wxNumberEntryDialog dlg(parent, "Enter the Model Start Nulls", "Start Nulls", "Start Nulls", GetModel()->GetControllerStartNulls(), 0, 100);
+ if (dlg.ShowModal() == wxID_OK) {
+ GetModel()->SetControllerStartNulls(dlg.GetValue());
+ }
+ return true;
+ } else if (id == ControllerModelDialog::CONTROLLER_ENDNULLS) {
+ wxNumberEntryDialog dlg(parent, "Enter the End Nulls", "End Nulls", "Model End Nulls", GetModel()->GetControllerEndNulls(), 0, 100);
+ if (dlg.ShowModal() == wxID_OK) {
+ GetModel()->SetControllerEndNulls(dlg.GetValue());
+ }
+ return true;
+ } else if (id == ControllerModelDialog::CONTROLLER_COLORORDER) {
+ auto choices = Model::CONTROLLER_COLORORDER;
+ int selection = choices.Index(GetModel()->GetControllerColorOrder());
+ wxSingleChoiceDialog dlg(parent, "Model Color Order", "Color Order", choices);
+ if (selection >= 0 && selection < choices.size()) {
+ dlg.SetSelection(selection);
+ }
+ if (dlg.ShowModal() == wxID_OK) {
+ GetModel()->SetControllerColorOrder(choices[dlg.GetSelection()]);
+ }
+ return true;
+ } else if (id == ControllerModelDialog::CONTROLLER_GROUPCOUNT) {
+ wxNumberEntryDialog dlg(parent, "Enter the Group Count", "Group Count", "Model Group Count", GetModel()->GetControllerGroupCount(), 1, 500);
+ if (dlg.ShowModal() == wxID_OK) {
+ GetModel()->SetControllerGroupCount(dlg.GetValue());
+ }
+ return true;
} else if (id == ControllerModelDialog::CONTROLLER_BRIGHTNESSCLEAR) {
GetModel()->ClearControllerBrightness();
return true;
@@ -1476,6 +1606,7 @@ class ModelCMObject : public BaseCMObject
return true;
} else {
wxString label = ((wxMenu*)event.GetEventObject())->GetLabelText(id);
+ wxString title = ((wxMenu*)event.GetEventObject())->GetHelpString(id);
auto const types = GetModel()->GetSmartRemoteTypes();
if (std::find(types.begin(), types.end(), label.ToStdString()) != types.end()) {
int const port = GetModel()->GetControllerPort();
@@ -1493,10 +1624,14 @@ class ModelCMObject : public BaseCMObject
} else if (label == "None") {
GetModel()->SetSmartRemote(0);
return true;
- } else if (label >= "A" && label <= "Z") {
- GetModel()->SetSmartRemote(int(label[0]) - 64);
+ } else if (title == "SR Port") {
+ if (label >= "A" && label <= "Z") {
+ GetModel()->SetSmartRemote(int(label[0]) - 64);
+ return true;
+ }
+ GetModel()->SetSmartRemote(wxAtoi(label) + 1);
return true;
- } else {
+ } else if (title == "Cascade") {
int max = wxAtoi(label);
if (max >= 1) {
GetModel()->SetSRMaxCascade(max);
@@ -1978,7 +2113,6 @@ void ControllerModelDialog::ReloadModels()
std::string check;
if (_caps != nullptr) {
_cud->Check(_caps, check);
- TextCtrl_Check->SetValue(check);
}
while (_models.size() > 0) {
@@ -1993,6 +2127,25 @@ void ControllerModelDialog::ReloadModels()
FixDMXChannels();
+ for (const auto& it : *_mm) {
+ if (it.second->GetDisplayAs() != "ModelGroup") {
+ if (_controller->ContainsChannels(it.second->GetFirstChannel(), it.second->GetLastChannel())) {
+ auto shadows = it.second->GetShadowedBy();
+ if (shadows.size() > 0) {
+ std::string sh;
+ for (const auto& it : shadows) {
+ if (sh != "")
+ sh += ", ";
+ sh += it;
+ }
+ check += "WARN: " + it.second->Name() + " is shadowed by " + sh + ".\n ";
+ }
+ }
+ }
+ }
+
+ TextCtrl_Check->SetValue(check);
+
for (const auto& it : *_mm) {
if (it.second->GetDisplayAs() != "ModelGroup") {
if (_cud->GetControllerPortModel(it.second->GetName(), 0) == nullptr &&
@@ -3666,9 +3819,7 @@ std::string ControllerModelDialog::GetModelTooltip(ModelCMObject* mob)
if (!isSubsequentString) {
sr += wxString::Format("\nSmart Remote Type: %s", m->GetSmartRemoteType());
sr += wxString::Format("\nSmart Remote Cascade Down Port: %s", toStr(m->GetSRCascadeOnPort()));
- if (m->GetSRCascadeOnPort()) {
- sr += wxString::Format("\nSmart Remote Cascade Length: %d", m->GetSRMaxCascade());
- }
+ sr += wxString::Format("\nSmart Remote Cascade Length: %d", m->GetSRMaxCascade());
}
sr += "\n";
}
diff --git a/xLights/ControllerModelDialog.h b/xLights/ControllerModelDialog.h
index 5f0eb8860a..55a140f9f3 100644
--- a/xLights/ControllerModelDialog.h
+++ b/xLights/ControllerModelDialog.h
@@ -127,12 +127,18 @@ class ControllerModelDialog: public wxDialog
static const long CONTROLLER_CASCADEDOWNPORT;
static const long CONTROLLER_DMXCHANNELCHAIN;
static const long CONTROLLER_PROTOCOL;
+ static const long CONTROLLER_SETSMARTREMOTE;
static const long CONTROLLER_REMOVEPORTMODELS;
static const long CONTROLLER_MOVEMODELSTOPORT;
static const long CONTROLLER_BRIGHTNESS;
static const long CONTROLLER_BRIGHTNESSCLEAR;
static const long CONTROLLER_SMARTREMOTETYPE;
+ static const long CONTROLLER_REMOVESMARTREMOTE;
static const long CONTROLLER_MODEL_STRINGS;
+ static const long CONTROLLER_STARTNULLS;
+ static const long CONTROLLER_ENDNULLS;
+ static const long CONTROLLER_COLORORDER;
+ static const long CONTROLLER_GROUPCOUNT;
wxBitmap RenderPicture(int startY, int startX, int width, int height, wxString const& pageName);
void DropFromModels(const wxPoint& location, const std::string& name, wxPanel* target);
diff --git a/xLights/EditAliasesDialog.cpp b/xLights/EditAliasesDialog.cpp
new file mode 100644
index 0000000000..dd482f0a9b
--- /dev/null
+++ b/xLights/EditAliasesDialog.cpp
@@ -0,0 +1,135 @@
+#include "EditAliasesDialog.h"
+#include "models/Model.h"
+
+//(*InternalHeaders(EditAliasesDialog)
+#include
+#include
+//*)
+
+//(*IdInit(EditAliasesDialog)
+const long EditAliasesDialog::ID_LISTBOX1 = wxNewId();
+const long EditAliasesDialog::ID_BUTTON1 = wxNewId();
+const long EditAliasesDialog::ID_BUTTON2 = wxNewId();
+const long EditAliasesDialog::ID_BUTTON3 = wxNewId();
+const long EditAliasesDialog::ID_BUTTON4 = wxNewId();
+//*)
+
+BEGIN_EVENT_TABLE(EditAliasesDialog,wxDialog)
+ //(*EventTable(EditAliasesDialog)
+ //*)
+END_EVENT_TABLE()
+
+EditAliasesDialog::EditAliasesDialog(wxWindow* parent, Model* m, wxWindowID id, const wxPoint& pos, const wxSize& size) :
+ _m(m)
+{
+ //(*Initialize(EditAliasesDialog)
+ wxFlexGridSizer* FlexGridSizer1;
+ wxFlexGridSizer* FlexGridSizer2;
+ wxFlexGridSizer* FlexGridSizer3;
+
+ Create(parent, wxID_ANY, _("Model Aliases"), wxDefaultPosition, wxDefaultSize, wxCAPTION|wxRESIZE_BORDER|wxMAXIMIZE_BOX, _T("wxID_ANY"));
+ FlexGridSizer1 = new wxFlexGridSizer(0, 2, 0, 0);
+ FlexGridSizer1->AddGrowableCol(0);
+ FlexGridSizer1->AddGrowableRow(0);
+ ListBoxAliases = new wxListBox(this, ID_LISTBOX1, wxDefaultPosition, wxDefaultSize, 0, 0, wxLB_SINGLE|wxLB_SORT, wxDefaultValidator, _T("ID_LISTBOX1"));
+ FlexGridSizer1->Add(ListBoxAliases, 1, wxALL|wxEXPAND, 5);
+ FlexGridSizer2 = new wxFlexGridSizer(0, 1, 0, 0);
+ ButtonAdd = new wxButton(this, ID_BUTTON1, _("Add"), wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator, _T("ID_BUTTON1"));
+ FlexGridSizer2->Add(ButtonAdd, 1, wxALL|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5);
+ ButtonDelete = new wxButton(this, ID_BUTTON2, _("Delete"), wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator, _T("ID_BUTTON2"));
+ FlexGridSizer2->Add(ButtonDelete, 1, wxALL|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5);
+ FlexGridSizer1->Add(FlexGridSizer2, 1, wxALL|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5);
+ FlexGridSizer3 = new wxFlexGridSizer(0, 3, 0, 0);
+ ButtonOk = new wxButton(this, ID_BUTTON3, _("Ok"), wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator, _T("ID_BUTTON3"));
+ ButtonOk->SetDefault();
+ FlexGridSizer3->Add(ButtonOk, 1, wxALL|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5);
+ ButtonCancel = new wxButton(this, ID_BUTTON4, _("Cancel"), wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator, _T("ID_BUTTON4"));
+ FlexGridSizer3->Add(ButtonCancel, 1, wxALL|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5);
+ FlexGridSizer1->Add(FlexGridSizer3, 1, wxALL|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5);
+ SetSizer(FlexGridSizer1);
+ FlexGridSizer1->Fit(this);
+ FlexGridSizer1->SetSizeHints(this);
+
+ Connect(ID_LISTBOX1,wxEVT_COMMAND_LISTBOX_SELECTED,(wxObjectEventFunction)&EditAliasesDialog::OnListBoxAliasesSelect);
+ Connect(ID_BUTTON1,wxEVT_COMMAND_BUTTON_CLICKED,(wxObjectEventFunction)&EditAliasesDialog::OnButtonAddClick);
+ Connect(ID_BUTTON2,wxEVT_COMMAND_BUTTON_CLICKED,(wxObjectEventFunction)&EditAliasesDialog::OnButtonDeleteClick);
+ Connect(ID_BUTTON3,wxEVT_COMMAND_BUTTON_CLICKED,(wxObjectEventFunction)&EditAliasesDialog::OnButtonOkClick);
+ Connect(ID_BUTTON4,wxEVT_COMMAND_BUTTON_CLICKED,(wxObjectEventFunction)&EditAliasesDialog::OnButtonCancelClick);
+ //*)
+
+ for (const auto& it : _m->GetAliases())
+ {
+ ListBoxAliases->Append(it);
+ }
+
+ SetEscapeId(ID_BUTTON4);
+
+ ValidateWindow();
+}
+
+EditAliasesDialog::~EditAliasesDialog()
+{
+ //(*Destroy(EditAliasesDialog)
+ //*)
+}
+
+void EditAliasesDialog::ValidateWindow()
+{
+ if (ListBoxAliases->GetSelection()>= 0)
+ {
+ ButtonDelete->Enable();
+ }
+ else
+ {
+ ButtonDelete->Disable();
+ }
+}
+
+void EditAliasesDialog::OnButtonAddClick(wxCommandEvent& event)
+{
+ wxTextEntryDialog te(this, "Alias to add", "Add an alias");
+
+ if (te.ShowModal() == wxID_OK) {
+ auto add = te.GetValue().Lower();
+
+ bool found = false;
+ for (int i = 0; !found && i < ListBoxAliases->GetCount(); ++i) {
+ if (ListBoxAliases->GetString(i) == add)
+ found = true;
+ }
+
+ if (!found) {
+ ListBoxAliases->Append(add);
+ }
+ }
+ ValidateWindow();
+}
+
+void EditAliasesDialog::OnButtonDeleteClick(wxCommandEvent& event)
+{
+ if (ListBoxAliases->GetSelection() >= 0) {
+ ListBoxAliases->Delete(ListBoxAliases->GetSelection());
+ }
+ ValidateWindow();
+}
+
+void EditAliasesDialog::OnButtonOkClick(wxCommandEvent& event)
+{
+ std::list aliases;
+ for (int i = 0; i < ListBoxAliases->GetCount(); ++i)
+ {
+ aliases.push_back(ListBoxAliases->GetString(i));
+ }
+ _m->SetAliases(aliases);
+ EndDialog(wxID_OK);
+}
+
+void EditAliasesDialog::OnButtonCancelClick(wxCommandEvent& event)
+{
+ EndDialog(wxID_CANCEL);
+}
+
+void EditAliasesDialog::OnListBoxAliasesSelect(wxCommandEvent& event)
+{
+ ValidateWindow();
+}
diff --git a/xLights/EditAliasesDialog.h b/xLights/EditAliasesDialog.h
new file mode 100644
index 0000000000..adce72c4f9
--- /dev/null
+++ b/xLights/EditAliasesDialog.h
@@ -0,0 +1,54 @@
+#ifndef EDITALIASESDIALOG_H
+#define EDITALIASESDIALOG_H
+
+//(*Headers(EditAliasesDialog)
+#include
+#include
+#include
+#include
+//*)
+
+class Model;
+
+class EditAliasesDialog: public wxDialog
+{
+ public:
+
+ EditAliasesDialog(wxWindow* parent, Model* mg, wxWindowID id=wxID_ANY,const wxPoint& pos=wxDefaultPosition,const wxSize& size=wxDefaultSize);
+ virtual ~EditAliasesDialog();
+
+ //(*Declarations(EditAliasesDialog)
+ wxButton* ButtonAdd;
+ wxButton* ButtonCancel;
+ wxButton* ButtonDelete;
+ wxButton* ButtonOk;
+ wxListBox* ListBoxAliases;
+ //*)
+
+ protected:
+
+ Model* _m = nullptr;
+ void ValidateWindow();
+
+ //(*Identifiers(EditAliasesDialog)
+ static const long ID_LISTBOX1;
+ static const long ID_BUTTON1;
+ static const long ID_BUTTON2;
+ static const long ID_BUTTON3;
+ static const long ID_BUTTON4;
+ //*)
+
+ private:
+
+ //(*Handlers(EditAliasesDialog)
+ void OnButtonAddClick(wxCommandEvent& event);
+ void OnButtonDeleteClick(wxCommandEvent& event);
+ void OnButtonOkClick(wxCommandEvent& event);
+ void OnButtonCancelClick(wxCommandEvent& event);
+ void OnListBoxAliasesSelect(wxCommandEvent& event);
+ //*)
+
+ DECLARE_EVENT_TABLE()
+};
+
+#endif
diff --git a/xLights/FSEQFile.cpp b/xLights/FSEQFile.cpp
index e33574fac9..e0cd7b3d0a 100644
--- a/xLights/FSEQFile.cpp
+++ b/xLights/FSEQFile.cpp
@@ -101,7 +101,35 @@ inline void AddSlowStorageWarning() {
#endif
#ifndef NO_ZSTD
+#ifndef LINUX
+//zstd on Debian 11 doesn't have the thread pool stuff
+#define ZSTD_STATIC_LINKING_ONLY
+#endif
#include
+#include
+
+#ifdef ZSTD_STATIC_LINKING_ONLY
+class ZSTDThreadPoolHolder {
+ struct POOL_ctx_s* pool = nullptr;
+public:
+ ZSTDThreadPoolHolder() {}
+ ~ZSTDThreadPoolHolder() {
+ if (pool) {
+ ZSTD_freeThreadPool(pool);
+ }
+ }
+ ZSTD_threadPool* getPool() {
+ if (pool == nullptr) {
+ pool = ZSTD_createThreadPool(std::thread::hardware_concurrency());
+ }
+ return pool;
+ }
+
+ static ZSTDThreadPoolHolder INSTANCE;
+};
+ZSTDThreadPoolHolder ZSTDThreadPoolHolder::INSTANCE;
+#endif
+
#endif
#ifndef NO_ZLIB
#include
@@ -671,6 +699,14 @@ class UncompressedFrameData : public FSEQFile::FrameData {
return true;
}
+ [[nodiscard]] virtual size_t GetSize() const override {
+ return m_size;
+ }
+
+ [[nodiscard]] virtual uint8_t* GetData() const override {
+ return m_data;
+ }
+
uint32_t m_size;
uint8_t* m_data;
std::vector> m_ranges;
@@ -745,8 +781,8 @@ static const int V2FSEQ_HEADER_SIZE = 32;
static const int V2FSEQ_SPARSE_RANGE_SIZE = 6;
static const int V2FSEQ_COMPRESSION_BLOCK_SIZE = 8;
#if !defined(NO_ZLIB) || !defined(NO_ZSTD)
-static const int V2FSEQ_OUT_BUFFER_SIZE = 1024 * 1024; // 1MB output buffer
-static const int V2FSEQ_OUT_BUFFER_FLUSH_SIZE = 900 * 1024; // 90% full, flush it
+static const int V2FSEQ_OUT_BUFFER_SIZE = 32 * 1024 * 1024; // 32MB output buffer
+static const int V2FSEQ_OUT_BUFFER_FLUSH_SIZE = 16 * 1024 * 1024; // 50% full, flush it
static const int V2FSEQ_OUT_COMPRESSION_BLOCK_SIZE = 64 * 1024; // 64KB blocks
#endif
@@ -882,7 +918,9 @@ class V2CompressedHandler : public V2Handler {
return m_maxBlocks;
}
//determine a good number of compression blocks
- uint64_t datasize = m_file->getChannelCount() * m_file->getNumFrames();
+ uint64_t datasize = m_file->getChannelCount();
+ uint64_t numFrames = m_file->getNumFrames();
+ datasize *= numFrames;
uint64_t numBlocks = datasize / V2FSEQ_OUT_COMPRESSION_BLOCK_SIZE;
if (numBlocks > maxNumBlocks) {
//need a lot of blocks, use as many as we can
@@ -890,7 +928,7 @@ class V2CompressedHandler : public V2Handler {
} else if (numBlocks < 1) {
numBlocks = 1;
}
- m_framesPerBlock = m_file->getNumFrames() / numBlocks;
+ m_framesPerBlock = numFrames / numBlocks;
if (m_framesPerBlock < 10)
m_framesPerBlock = 10;
m_curFrameInBlock = 0;
@@ -963,6 +1001,7 @@ class V2CompressedHandler : public V2Handler {
#ifndef NO_ZSTD
class V2ZSTDCompressionHandler : public V2CompressedHandler {
public:
+
V2ZSTDCompressionHandler(V2FSEQFile* f) :
V2CompressedHandler(f),
m_cctx(nullptr),
@@ -1011,7 +1050,8 @@ class V2ZSTDCompressionHandler : public V2CompressedHandler {
uint64_t len = m_file->m_frameOffsets[m_curBlock + 1].second;
len -= m_file->m_frameOffsets[m_curBlock].second;
- uint64_t max = m_file->getNumFrames() * m_file->getChannelCount();
+ uint64_t max = m_file->getNumFrames();
+ max *= (uint64_t)m_file->getChannelCount();
if (len > max) {
len = max;
}
@@ -1099,7 +1139,7 @@ class V2ZSTDCompressionHandler : public V2CompressedHandler {
return data;
}
void compressData(ZSTD_CStream* m_cctx, ZSTD_inBuffer_s& input, ZSTD_outBuffer_s& output) {
- ZSTD_compressStream(m_cctx, &output, &input);
+ ZSTD_compressStream2(m_cctx, &output, &input, ZSTD_e_continue);
int count = input.pos;
int total = input.size;
uint8_t* curData = (uint8_t*)input.src;
@@ -1113,7 +1153,7 @@ class V2ZSTDCompressionHandler : public V2CompressedHandler {
write(output.dst, output.pos);
output.pos = 0;
}
- ZSTD_compressStream(m_cctx, &output, &input);
+ ZSTD_compressStream2(m_cctx, &output, &input, ZSTD_e_continue);
count += input.pos;
}
}
@@ -1141,6 +1181,14 @@ class V2ZSTDCompressionHandler : public V2CompressedHandler {
clevel = 0;
}
ZSTD_initCStream(m_cctx, clevel);
+ //ZSTD_CCtx_reset(m_cctx, ZSTD_reset_session_only);
+ //ZSTD_CCtx_refCDict(m_cctx, NULL);
+ //ZSTD_CCtx_setParameter(m_cctx, ZSTD_c_compressionLevel, clevel);
+
+#ifdef ZSTD_STATIC_LINKING_ONLY
+ ZSTD_CCtx_setParameter(m_cctx, ZSTD_c_nbWorkers, std::thread::hardware_concurrency());
+ ZSTD_CCtx_refThreadPool(m_cctx, ZSTDThreadPoolHolder::INSTANCE.getPool());
+#endif
}
uint8_t* curData = (uint8_t*)data;
@@ -1173,7 +1221,10 @@ class V2ZSTDCompressionHandler : public V2CompressedHandler {
//we'll start a new block. We want the first block to be small so startup is
//quicker and we can get the first few frames as fast as possible.
if ((m_curBlock == 0 && m_curFrameInBlock == 10) || (m_curFrameInBlock >= m_framesPerBlock && m_file->m_frameOffsets.size() < m_maxBlocks)) {
- while (ZSTD_endStream(m_cctx, &m_outBuffer) > 0) {
+ ZSTD_inBuffer_s input = {
+ 0, 0, 0
+ };
+ while(ZSTD_compressStream2(m_cctx, &m_outBuffer, &input, ZSTD_e_end) > 0) {
write(m_outBuffer.dst, m_outBuffer.pos);
m_outBuffer.pos = 0;
}
@@ -1186,7 +1237,10 @@ class V2ZSTDCompressionHandler : public V2CompressedHandler {
}
virtual void finalize() override {
if (m_curFrameInBlock) {
- while (ZSTD_endStream(m_cctx, &m_outBuffer) > 0) {
+ ZSTD_inBuffer_s input = {
+ 0, 0, 0
+ };
+ while(ZSTD_compressStream2(m_cctx, &m_outBuffer, &input, ZSTD_e_end) > 0) {
write(m_outBuffer.dst, m_outBuffer.pos);
m_outBuffer.pos = 0;
}
@@ -1199,7 +1253,7 @@ class V2ZSTDCompressionHandler : public V2CompressedHandler {
V2CompressedHandler::finalize();
}
- ZSTD_CStream* m_cctx = nullptr;
+ ZSTD_CCtx* m_cctx = nullptr;
ZSTD_DStream* m_dctx = nullptr;
ZSTD_outBuffer_s m_outBuffer;
ZSTD_inBuffer_s m_inBuffer;
diff --git a/xLights/FSEQFile.h b/xLights/FSEQFile.h
index fd12b21644..13d60a16de 100644
--- a/xLights/FSEQFile.h
+++ b/xLights/FSEQFile.h
@@ -28,7 +28,8 @@ class FSEQFile {
virtual ~FrameData() {};
virtual bool readFrame(uint8_t *data, uint32_t maxChannels) = 0;
-
+ [[nodiscard]] virtual uint8_t* GetData() const = 0;
+ [[nodiscard]] virtual size_t GetSize() const = 0;
uint32_t frame;
};
@@ -83,9 +84,9 @@ class FSEQFile {
virtual void dumpInfo(bool indent = false);
- uint32_t getNumFrames() const { return m_seqNumFrames; }
+ uint64_t getNumFrames() const { return m_seqNumFrames; }
int getStepTime() const { return m_seqStepTime; }
- uint32_t getChannelCount() const { return m_seqChannelCount; }
+ uint64_t getChannelCount() const { return m_seqChannelCount; }
int getVersionMajor() const { return m_seqVersionMajor; }
int getVersionMinor() const { return m_seqVersionMinor; }
uint64_t getUniqueId() const { return m_uniqueId; }
diff --git a/xLights/LayoutPanel.cpp b/xLights/LayoutPanel.cpp
index 154f638c56..99bffdfeb5 100644
--- a/xLights/LayoutPanel.cpp
+++ b/xLights/LayoutPanel.cpp
@@ -157,6 +157,7 @@ const long LayoutPanel::ID_PREVIEW_BULKEDIT_PIXELSIZE = wxNewId();
const long LayoutPanel::ID_PREVIEW_BULKEDIT_PIXELSTYLE = wxNewId();
const long LayoutPanel::ID_PREVIEW_BULKEDIT_TRANSPARENCY = wxNewId();
const long LayoutPanel::ID_PREVIEW_BULKEDIT_BLACKTRANSPARENCY = wxNewId();
+const long LayoutPanel::ID_PREVIEW_BULKEDIT_SHADOWMODELFOR = wxNewId();
const long LayoutPanel::ID_PREVIEW_BULKEDIT_CONTROLLERGAMMA = wxNewId();
const long LayoutPanel::ID_PREVIEW_BULKEDIT_CONTROLLERCOLOURORDER = wxNewId();
const long LayoutPanel::ID_PREVIEW_BULKEDIT_CONTROLLERBRIGHTNESS = wxNewId();
@@ -474,11 +475,12 @@ LayoutPanel::LayoutPanel(wxWindow* parent, xLightsFrame *xl, wxPanel* sequencer)
int msp = config->Read("LayoutModelSplitterSash", -1);
int sp = config->Read("LayoutMainSplitterSash", -1);
is_3d = config->ReadBool("LayoutMode3D", false);
+ bool allow_3d_previews = true; //false; //set to false for previous behavior
CheckBox_3D->SetValue(is_3d);
xlights->GetHousePreview()->Set3D(is_3d);
- if (is_3d)
+ if (!allow_3d_previews && is_3d)
{
ChoiceLayoutGroups->Disable();
ChoiceLayoutGroups->SetToolTip("3D is only supported in the Default preview.");
@@ -877,6 +879,7 @@ void LayoutPanel::OnPropertyGridChange(wxPropertyGridEvent& event) {
}
else {
if (editing_models) {
+ xlights->AbortRender();
if (selectedBaseObject != nullptr) {
Model* selectedModel = dynamic_cast(selectedBaseObject);
//model property
@@ -946,6 +949,7 @@ void LayoutPanel::OnPropertyGridChanging(wxPropertyGridEvent& event) {
xlights->AddTraceMessage("LayoutPanel::OnPropertyGridChanging Property: " + name);
if (selectedBaseObject != nullptr) {
if( editing_models ) {
+ xlights->AbortRender();
Model* selectedModel = dynamic_cast(selectedBaseObject);
if ("ModelName" == name) {
std::string safename = Model::SafeModelName(event.GetValue().GetString().ToStdString());
@@ -1172,6 +1176,7 @@ std::string LayoutPanel::TreeModelName(const Model* model, bool fullname)
return "<" + name + ">";
}
}
+
void LayoutPanel::FreezeTreeListView() {
TreeListViewModels->Freeze();
//turn off the column width auto-resize. Makes it REALLY slow to populate the tree
@@ -1204,8 +1209,10 @@ void LayoutPanel::SetTreeListViewItemText(wxTreeListItem &item, int col, const w
void LayoutPanel::refreshModelList() {
+ static log4cpp::Category& logger_base = log4cpp::Category::getInstance(std::string("log_base"));
static log4cpp::Category& logger_work = log4cpp::Category::getInstance(std::string("log_work"));
logger_work.debug(" refreshModelList.");
+ wxStopWatch sw;
FreezeTreeListView();
@@ -1246,6 +1253,9 @@ void LayoutPanel::refreshModelList() {
}
}
ThawTreeListView();
+
+ if (sw.Time() > 500)
+ logger_base.debug(" LayoutPanel::refreshModelList took %lums", sw.Time());
}
void LayoutPanel::RenameModelInTree(Model *model, const std::string& new_name)
@@ -1344,7 +1354,9 @@ void LayoutPanel::UpdateModelList(bool full_refresh) {
void LayoutPanel::UpdateModelList(bool full_refresh, std::vector &models) {
- //static log4cpp::Category& logger_base = log4cpp::Category::getInstance(std::string("log_base"));
+ static log4cpp::Category& logger_base = log4cpp::Category::getInstance(std::string("log_base"));
+ wxStopWatch sw;
+
FreezeTreeListView();
unsigned sortcol;
bool ascending;
@@ -1453,11 +1465,15 @@ void LayoutPanel::UpdateModelList(bool full_refresh, std::vector &models
xlights->GetOutputModelManager()->AddASAPWork(OutputModelManager::WORK_REDRAW_LAYOUTPREVIEW, "LayoutPanel::UpdateModelList");
ThawTreeListView();
+
+ if (sw.Time() > 500)
+ logger_base.debug(" LayoutPanel::UpdateModelList took %lums", sw.Time());
}
void LayoutPanel::UpdateModelsForPreview(const std::string &group, LayoutGroup* layout_grp, std::vector &prev_models, bool filtering)
{
- //static log4cpp::Category& logger_base = log4cpp::Category::getInstance(std::string("log_base"));
+ static log4cpp::Category& logger_base = log4cpp::Category::getInstance(std::string("log_base"));
+ wxStopWatch sw;
//logger_base.debug("Updated models for preview: %s.", (const char*)group.c_str());
std::set modelsAdded;
@@ -1584,6 +1600,9 @@ void LayoutPanel::UpdateModelsForPreview(const std::string &group, LayoutGroup*
}
}
}
+
+ if (sw.Time() > 500)
+ logger_base.debug(" LayoutPanel::UpdateModelsForPreview took %lums", sw.Time());
}
void LayoutPanel::BulkEditDimmingCurves()
@@ -1867,6 +1886,7 @@ void LayoutPanel::BulkEditControllerName()
ReselectTreeModels(selectedModelPaths);
}
}
+
void LayoutPanel::BulkEditPixelSize() {
std::vector modelsToEdit = GetSelectedModelsForEdit();
// remember the selected models
@@ -1893,6 +1913,7 @@ void LayoutPanel::BulkEditPixelSize() {
ReselectTreeModels(selectedModelPaths);
}
}
+
void LayoutPanel::BulkEditPixelStyle() {
std::vector modelsToEdit = GetSelectedModelsForEdit();
// remember the selected models
@@ -1923,6 +1944,7 @@ void LayoutPanel::BulkEditPixelStyle() {
ReselectTreeModels(selectedModelPaths);
}
}
+
void LayoutPanel::BulkEditTransparency() {
std::vector modelsToEdit = GetSelectedModelsForEdit();
// remember the selected models
@@ -1949,6 +1971,7 @@ void LayoutPanel::BulkEditTransparency() {
ReselectTreeModels(selectedModelPaths);
}
}
+
void LayoutPanel::BulkEditBlackTranparency() {
std::vector modelsToEdit = GetSelectedModelsForEdit();
// remember the selected models
@@ -1975,6 +1998,39 @@ void LayoutPanel::BulkEditBlackTranparency() {
ReselectTreeModels(selectedModelPaths);
}
}
+
+void LayoutPanel::BulkEditShadowModelFor()
+{
+ std::vector modelsToEdit = GetSelectedModelsForEdit();
+ // remember the selected models
+ std::vector> selectedModelPaths = GetSelectedTreeModelPaths();
+
+ wxArrayString choices;
+ choices.Add("");
+
+ for (size_t i = 0; i < modelPreview->GetModels().size(); i++) {
+ if (modelPreview->GetModels()[i]->GetName() != selectedBaseObject->GetName()) {
+ choices.Add(modelPreview->GetModels()[i]->GetName());
+ }
+ }
+
+ wxSingleChoiceDialog dlg(this, "", "Select the model to shadow.", choices);
+ dlg.SetSelection(0);
+ OptimiseDialogPosition(&dlg);
+
+ if (dlg.ShowModal() == wxID_OK) {
+ for (Model* model : modelsToEdit) {
+ model->SetShadowModelFor(dlg.GetStringSelection());
+ }
+
+ // see comment in BulkEditActive()
+ xlights->GetOutputModelManager()->ClearSelectedModel();
+ xlights->GetOutputModelManager()->AddImmediateWork(OutputModelManager::WORK_RELOAD_ALLMODELS, "BulkEditBlackShadowModelFor");
+ // reselect all the models
+ ReselectTreeModels(selectedModelPaths);
+ }
+}
+
void LayoutPanel::BulkEditTagColour()
{
std::vector modelsToEdit = GetSelectedModelsForEdit();
@@ -2219,10 +2275,12 @@ class xlImageProperty : public wxImageFileProperty {
xlImageProperty(const wxString& label,
const wxString& name,
const wxString& value,
- const wxImage *img)
- : wxImageFileProperty(label, name, ""), lastFileName(value)
+ const wxImage* img) :
+ wxImageFileProperty(label, name, ""), lastFileName(value)
{
- SetAttribute(wxPG_FILE_WILDCARD, "Image files|*.png;*.bmp;*.jpg;*.gif;*.jpeg|All files (*.*)|*.*");
+ SetAttribute(wxPG_FILE_WILDCARD, "Image files|*.png;*.bmp;*.jpg;*.gif;*.jpeg"
+ ";*.webp"
+ "|All files (*.*)|*.*");
SetValueFromString(value);
if (img != nullptr) {
setImage(*img);
@@ -2264,6 +2322,8 @@ class xlImageProperty : public wxImageFileProperty {
void LayoutPanel::UnSelectAllModels(bool addBkgProps)
{
static log4cpp::Category& logger_base = log4cpp::Category::getInstance(std::string("log_base"));
+ wxStopWatch sw;
+
highlightedBaseObject = nullptr;
selectedBaseObject = nullptr;
selectionLatched = false;
@@ -2319,6 +2379,9 @@ void LayoutPanel::UnSelectAllModels(bool addBkgProps)
if (!updatingProperty && addBkgProps) {
showBackgroundProperties();
}
+
+ if (sw.Time() > 500)
+ logger_base.debug(" LayoutPanel::UnSelectAllModels took %lums", sw.Time());
}
void LayoutPanel::showBackgroundProperties()
@@ -2422,6 +2485,8 @@ void LayoutPanel::SelectAllModels()
void LayoutPanel::SetupPropGrid(BaseObject *base_object) {
+ // static log4cpp::Category& logger_base = log4cpp::Category::getInstance(std::string("log_base"));
+
if (base_object == nullptr || propertyEditor == nullptr) return;
if (dynamic_cast(base_object) != nullptr) {
//groups don't use the property grid
@@ -2502,6 +2567,7 @@ void LayoutPanel::SetupPropGrid(BaseObject *base_object) {
}
}
}
+
if (!frozen) propertyEditor->Thaw();
if (_lastSelProp != "") {
@@ -4411,12 +4477,30 @@ void LayoutPanel::AddSingleModelOptionsToBaseMenu(wxMenu &menu) {
}
if (editing_models)
{
+ bool anySelectedModelLocked = false;
+ bool anySelectedModelUnlocked = false;
+ bool allSelectedModelsFromBase = true;
+ bool anySelectedModelFromBase = false;
+
+ std::vector selectedModels = GetSelectedModelsForEdit();
+ for (const auto& it : selectedModels) {
+ if (it->IsLocked())
+ anySelectedModelLocked = true;
+ else
+ anySelectedModelUnlocked = true;
+
+ if (!it->IsFromBase())
+ allSelectedModelsFromBase = false;
+ else
+ anySelectedModelFromBase = true;
+ }
+
auto lm = menu.Append(ID_PREVIEW_MODEL_LOCK, "Lock");
- lm->Enable(!selectedBaseObject->IsLocked() && !selectedBaseObject->IsFromBase());
+ lm->Enable(anySelectedModelUnlocked && !allSelectedModelsFromBase);
auto um = menu.Append(ID_PREVIEW_MODEL_UNLOCK, "Unlock");
- um->Enable(selectedBaseObject->IsLocked() && !selectedBaseObject->IsFromBase());
+ um->Enable(anySelectedModelLocked && !allSelectedModelsFromBase);
auto ul = menu.Append(ID_PREVIEW_MODEL_UNLINKFROMBASE, "Unlink from base show folder");
- ul->Enable(selectedBaseObject->IsFromBase());
+ ul->Enable(anySelectedModelFromBase);
Model* model = dynamic_cast(selectedBaseObject);
if (model != nullptr && model->GetDisplayAs() != "ModelGroup" && model->GetDisplayAs() != "SubModel") {
@@ -4470,6 +4554,7 @@ void LayoutPanel::AddBulkEditOptionsToMenu(wxMenu* mnuBulkEdit) {
mnuBulkEdit->Append(ID_PREVIEW_BULKEDIT_PIXELSTYLE, "Pixel Style");
mnuBulkEdit->Append(ID_PREVIEW_BULKEDIT_TRANSPARENCY, "Transparency");
mnuBulkEdit->Append(ID_PREVIEW_BULKEDIT_BLACKTRANSPARENCY, "Black Transparency");
+ mnuBulkEdit->Append(ID_PREVIEW_BULKEDIT_SHADOWMODELFOR, "Shadow Model For");
mnuBulkEdit->AppendSeparator();
mnuBulkEdit->Append(ID_PREVIEW_BULKEDIT_CONTROLLERCONNECTION, "Controller Port");
@@ -4690,6 +4775,8 @@ void LayoutPanel::OnPreviewModelPopup(wxCommandEvent& event)
BulkEditTransparency();
} else if (event.GetId() == ID_PREVIEW_BULKEDIT_BLACKTRANSPARENCY) {
BulkEditBlackTranparency();
+ } else if (event.GetId() == ID_PREVIEW_BULKEDIT_SHADOWMODELFOR) {
+ BulkEditShadowModelFor();
} else if (event.GetId() == ID_PREVIEW_BULKEDIT_PREVIEW) {
BulkEditControllerPreview();
} else if (event.GetId() == ID_PREVIEW_BULKEDIT_DIMMINGCURVES) {
@@ -6582,7 +6669,7 @@ void LayoutPanel::RemoveSelectedModelsFromGroup() {
void LayoutPanel::DeleteSelectedModels()
{
// I deliberately allow objects that come from base to be deleted.
- if (selectedBaseObject != nullptr && !selectedBaseObject->GetBaseObjectScreenLocation().IsLocked()) {
+ if (selectedBaseObject != nullptr) {
xlights->AddTraceMessage("LayoutPanel::Delete Selected Model");
wxArrayString modelsToDelete;
@@ -6608,13 +6695,9 @@ void LayoutPanel::DeleteSelectedModels()
for (const auto& it : modelsToDelete) {
auto model = xlights->AllModels[it];
if (model != nullptr) {
- if (!model->IsLocked()) {
- xlights->GetDisplayElementsPanel()->RemoveModelFromLists(it);
- allDeleted = xlights->AllModels.Delete(it) && allDeleted;
- xlights->AddTraceMessage(wxString::Format("LayoutPanel::Delete Selected Model : %s", it));
- } else {
- allDeleted = false;
- }
+ xlights->GetDisplayElementsPanel()->RemoveModelFromLists(it);
+ allDeleted = xlights->AllModels.Delete(it) && allDeleted;
+ xlights->AddTraceMessage(wxString::Format("LayoutPanel::Delete Selected Model : %s", it));
}
else {
allDeleted = false;
@@ -6623,7 +6706,7 @@ void LayoutPanel::DeleteSelectedModels()
if (!allDeleted) {
wxBell();
- wxMessageBox("One or models unable to be deleted. They may be locked or have effects on them.", "Delete failed", 5L, this);
+ wxMessageBox("One or more models cannot be deleted. They may have effects on them.", "Delete failed", 5L, this);
}
selectedBaseObject = nullptr;
@@ -6713,6 +6796,8 @@ void LayoutPanel::ReplaceModel()
if (replaceModel == nullptr) return;
+ CreateUndoPoint("All", "", "");
+
// Prompt user to copy the target models start channel ...but only if
// they are not already the same and the new model uses a chaining start
// channel ... the theory being if you took time to set the start channel
@@ -6749,7 +6834,10 @@ void LayoutPanel::ReplaceModel()
}
}
- if (wxMessageBox("Use original size and position", "Use original size and position", wxYES_NO) == wxYES) {
+ auto rmn = replaceModel->GetName();
+ auto riw = modelToReplaceItWith->GetName();
+
+ if (wxMessageBox("Use original size and position of " + rmn, "Use original size and position", wxYES_NO) == wxYES) {
modelToReplaceItWith->GetModelScreenLocation().SetRotation(replaceModel->GetModelScreenLocation().GetRotation());
modelToReplaceItWith->SetHcenterPos(replaceModel->GetHcenterPos());
modelToReplaceItWith->SetVcenterPos(replaceModel->GetVcenterPos());
@@ -6759,8 +6847,6 @@ void LayoutPanel::ReplaceModel()
modelToReplaceItWith->SetDepth(replaceModel->GetDepth());
}
- auto rmn = replaceModel->GetName();
- auto riw = modelToReplaceItWith->GetName();
xlights->AllModels.RenameInListOnly(dlg.GetStringSelection().ToStdString(), "Iamgoingtodeletethismodel");
replaceModel->Rename("Iamgoingtodeletethismodel");
xlights->AllModels.RenameInListOnly(modelToReplaceItWith->GetName(), dlg.GetStringSelection().ToStdString());
@@ -7418,6 +7504,8 @@ void LayoutPanel::OnModelsPopup(wxCommandEvent& event) {
BulkEditTransparency();
} else if (event.GetId() == ID_PREVIEW_BULKEDIT_BLACKTRANSPARENCY) {
BulkEditBlackTranparency();
+ } else if (event.GetId() == ID_PREVIEW_BULKEDIT_SHADOWMODELFOR) {
+ BulkEditShadowModelFor();
} else if (event.GetId() == ID_PREVIEW_BULKEDIT_PREVIEW) {
BulkEditControllerPreview();
} else if (event.GetId() == ID_PREVIEW_BULKEDIT_DIMMINGCURVES) {
@@ -8423,6 +8511,8 @@ void LayoutPanel::HandleSelectionChanged() {
return;
}
+ wxStopWatch sw;
+
BaseObject* lastSelectedBaseObject = selectedBaseObject;
Model* lastSelectedModel = dynamic_cast(lastSelectedBaseObject);
wxTreeListItems selectedItems;
@@ -8431,6 +8521,9 @@ void LayoutPanel::HandleSelectionChanged() {
UnSelectAllModels(false);
resetPropertyGrid();
+ if (sw.Time() > 500)
+ logger_base.debug(" LayoutPanel::HandleSelectionChanged after reset of property grid %lums", sw.Time());
+
if (selectedItems.size() > 0) {
bool isPrimary = false;
if (selectedItems.size() == 1) {
@@ -8473,6 +8566,8 @@ void LayoutPanel::HandleSelectionChanged() {
}
}
}
+ if (sw.Time() > 500)
+ logger_base.debug(" LayoutPanel::HandleSelectionChanged after select in tree %lums", sw.Time());
// if we still don't have a primary model selected then force one if we can
if (selectedPrimaryTreeItem == nullptr) {
@@ -8489,6 +8584,9 @@ void LayoutPanel::HandleSelectionChanged() {
}
}
+ if (sw.Time() > 500)
+ logger_base.debug(" LayoutPanel::HandleSelectionChanged after force select %lums", sw.Time());
+
// determine which panel and tooltip to show if any
int mSize = selectedTreeModels.size();
int gSize = selectedTreeGroups.size();
@@ -8533,6 +8631,9 @@ void LayoutPanel::HandleSelectionChanged() {
SetToolTipForTreeList(TreeListViewModels, tooltip);
+ if (sw.Time() > 500)
+ logger_base.debug(" LayoutPanel::HandleSelectionChanged after tooltip %lums", sw.Time());
+
// removing below or Keyboard Cut/Copy/Paste/etc will not fire when making selections in preview
// #ifndef LINUX
// TreeListViewModels->SetFocus();
@@ -8547,6 +8648,7 @@ void LayoutPanel::HandleSelectionChanged() {
}
xlights->GetOutputModelManager()->AddASAPWork(OutputModelManager::WORK_REDRAW_LAYOUTPREVIEW, "LayoutPanel::HandleSelectionChanged");
+
} else {
selectedBaseObject = nullptr;
UnSelectAllModels(true);
@@ -8554,6 +8656,9 @@ void LayoutPanel::HandleSelectionChanged() {
SetToolTipForTreeList(TreeListViewModels, "");
xlights->SetStatusText("");
}
+
+ if (sw.Time() > 500)
+ logger_base.debug(" LayoutPanel::HandleSelectionChanged took %lums", sw.Time());
}
void LayoutPanel::ModelGroupUpdated(ModelGroup *grp, bool full_refresh) {
@@ -8732,8 +8837,9 @@ std::string CopyPasteBaseObject::Serialise() const
void LayoutPanel::OnCheckBox_3DClick(wxCommandEvent& event)
{
is_3d = CheckBox_3D->GetValue();
+ bool allow_3d_previews = true; //false; //set to false for previous behavior
- if (is_3d) {
+ if (!allow_3d_previews && is_3d) {
if (ChoiceLayoutGroups->GetStringSelection() != "Default") {
ChoiceLayoutGroups->SetStringSelection("Default");
wxCommandEvent e;
diff --git a/xLights/LayoutPanel.h b/xLights/LayoutPanel.h
index 7def4902ce..ab564a65ac 100644
--- a/xLights/LayoutPanel.h
+++ b/xLights/LayoutPanel.h
@@ -165,6 +165,7 @@ class LayoutPanel: public wxPanel
static const long ID_PREVIEW_BULKEDIT_PIXELSTYLE;
static const long ID_PREVIEW_BULKEDIT_TRANSPARENCY;
static const long ID_PREVIEW_BULKEDIT_BLACKTRANSPARENCY;
+ static const long ID_PREVIEW_BULKEDIT_SHADOWMODELFOR;
static const long ID_PREVIEW_BULKEDIT_CONTROLLERDIRECTION;
static const long ID_PREVIEW_BULKEDIT_CONTROLLERSTARTNULLNODES;
static const long ID_PREVIEW_BULKEDIT_CONTROLLERENDNULLNODES;
@@ -345,6 +346,7 @@ class LayoutPanel: public wxPanel
void BulkEditPixelStyle();
void BulkEditTransparency();
void BulkEditBlackTranparency();
+ void BulkEditShadowModelFor();
void BulkEditControllerConnection(int type);
void BulkEditControllerPreview();
void BulkEditDimmingCurves();
diff --git a/xLights/ModelChainDialog.cpp b/xLights/ModelChainDialog.cpp
index abf26b5968..c7d8c0a347 100644
--- a/xLights/ModelChainDialog.cpp
+++ b/xLights/ModelChainDialog.cpp
@@ -39,18 +39,19 @@ ModelChainDialog::ModelChainDialog(wxWindow* parent,wxWindowID id,const wxPoint&
wxFlexGridSizer* FlexGridSizer2;
wxStdDialogButtonSizer* StdDialogButtonSizer1;
- Create(parent, id, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE, _T("id"));
+ Create(parent, id, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER, _T("id"));
SetClientSize(wxDefaultSize);
Move(wxDefaultPosition);
FlexGridSizer1 = new wxFlexGridSizer(0, 1, 0, 0);
FlexGridSizer1->AddGrowableCol(0);
- FlexGridSizer2 = new wxFlexGridSizer(0, 3, 0, 0);
+ FlexGridSizer2 = new wxFlexGridSizer(1, 2, 0, 0);
+ FlexGridSizer2->AddGrowableCol(1);
StaticText1 = new wxStaticText(this, ID_STATICTEXT1, _("Chain after model:"), wxDefaultPosition, wxDefaultSize, 0, _T("ID_STATICTEXT1"));
FlexGridSizer2->Add(StaticText1, 1, wxALL|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5);
ModelChoice = new wxChoice(this, ID_CHOICE1, wxDefaultPosition, wxDefaultSize, 0, 0, 0, wxDefaultValidator, _T("ID_CHOICE1"));
ModelChoice->SetMinSize(wxDLG_UNIT(this,wxSize(100,-1)));
- FlexGridSizer2->Add(ModelChoice, 1, wxALL|wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL, 5);
- FlexGridSizer1->Add(FlexGridSizer2, 1, wxALL|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5);
+ FlexGridSizer2->Add(ModelChoice, 1, wxALL|wxEXPAND, 5);
+ FlexGridSizer1->Add(FlexGridSizer2, 1, wxALL|wxEXPAND, 5);
StdDialogButtonSizer1 = new wxStdDialogButtonSizer();
StdDialogButtonSizer1->AddButton(new wxButton(this, wxID_OK, wxEmptyString));
StdDialogButtonSizer1->AddButton(new wxButton(this, wxID_CANCEL, wxEmptyString));
diff --git a/xLights/ModelChainDialog.h b/xLights/ModelChainDialog.h
index 1cdd429bc6..80c3cf79e8 100644
--- a/xLights/ModelChainDialog.h
+++ b/xLights/ModelChainDialog.h
@@ -11,11 +11,11 @@
**************************************************************/
//(*Headers(ModelChainDialog)
-#include
-#include
-#include
-#include
-//*)
+ #include
+ #include
+ #include
+ #include
+ //*)
#include
diff --git a/xLights/ModelFaceDialog.cpp b/xLights/ModelFaceDialog.cpp
index 1cb96d64c0..b9c275340b 100644
--- a/xLights/ModelFaceDialog.cpp
+++ b/xLights/ModelFaceDialog.cpp
@@ -655,7 +655,9 @@ void ModelFaceDialog::OnMatrixModelsGridLabelLeftDClick(wxGridEvent& event)
SelectMatrixImage(event.GetRow(), 0);// update eyes open column
}
-static const wxString strSupportedImageTypes = "Image files|*.png;*.bmp;*.jpg;*.gif;*.jpeg|All files (*.*)|*.*";
+static const wxString strSupportedImageTypes = "Image files|*.png;*.bmp;*.jpg;*.gif;*.jpeg"
+ ";*.webp"
+ "|All files (*.*)|*.*";
void ModelFaceDialog::SelectMatrixImage(int r, int c)
{
std::string const name = NameChoice->GetString(NameChoice->GetSelection()).ToStdString();
diff --git a/xLights/ModelFaceDialog.h b/xLights/ModelFaceDialog.h
index 882ed1bd70..b66ac6e41c 100644
--- a/xLights/ModelFaceDialog.h
+++ b/xLights/ModelFaceDialog.h
@@ -12,6 +12,7 @@
#include
#include
+#include
//(*Headers(ModelFaceDialog)
#include
diff --git a/xLights/ModelGroupPanel.cpp b/xLights/ModelGroupPanel.cpp
index 5601a56727..653d486509 100644
--- a/xLights/ModelGroupPanel.cpp
+++ b/xLights/ModelGroupPanel.cpp
@@ -26,6 +26,7 @@
#include "OutputModelManager.h"
#include "xLightsMain.h"
#include "UtilFunctions.h"
+#include "EditAliasesDialog.h"
#include
@@ -99,6 +100,7 @@ const long ModelGroupPanel::ID_STATICTEXT11 = wxNewId();
const long ModelGroupPanel::ID_SPINCTRL3 = wxNewId();
const long ModelGroupPanel::ID_STATICTEXT8 = wxNewId();
const long ModelGroupPanel::ID_COLOURPICKERCTRL_MG_TAGCOLOUR = wxNewId();
+const long ModelGroupPanel::ID_BUTTON2 = wxNewId();
const long ModelGroupPanel::ID_CHECKBOX1 = wxNewId();
const long ModelGroupPanel::ID_CHECKBOX3 = wxNewId();
const long ModelGroupPanel::ID_CHECKBOX2 = wxNewId();
@@ -137,6 +139,7 @@ ModelGroupPanel::ModelGroupPanel(wxWindow* parent, ModelManager &Models, LayoutP
wxFlexGridSizer* FlexGridSizer2;
wxFlexGridSizer* FlexGridSizer3;
wxFlexGridSizer* FlexGridSizer4;
+ wxFlexGridSizer* FlexGridSizer5;
wxFlexGridSizer* FlexGridSizer6;
wxStaticText* StaticText4;
wxStaticText* StaticText6;
@@ -202,8 +205,12 @@ ModelGroupPanel::ModelGroupPanel(wxWindow* parent, ModelManager &Models, LayoutP
FlexGridSizer6->Add(FlexGridSizer4, 1, wxALL|wxEXPAND, 5);
StaticText8 = new wxStaticText(this, ID_STATICTEXT8, _("Tag Color:"), wxDefaultPosition, wxDefaultSize, 0, _T("ID_STATICTEXT8"));
FlexGridSizer6->Add(StaticText8, 1, wxALL|wxALIGN_RIGHT|wxALIGN_CENTER_VERTICAL, 2);
+ FlexGridSizer5 = new wxFlexGridSizer(0, 3, 0, 0);
ColourPickerCtrl_ModelGroupTagColour = new wxColourPickerCtrl(this, ID_COLOURPICKERCTRL_MG_TAGCOLOUR, wxColour(0,0,0), wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator, _T("ID_COLOURPICKERCTRL_MG_TAGCOLOUR"));
- FlexGridSizer6->Add(ColourPickerCtrl_ModelGroupTagColour, 1, wxALL|wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL, 5);
+ FlexGridSizer5->Add(ColourPickerCtrl_ModelGroupTagColour, 1, wxALL|wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL, 5);
+ ButtonAliases = new wxButton(this, ID_BUTTON2, _("Aliases"), wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator, _T("ID_BUTTON2"));
+ FlexGridSizer5->Add(ButtonAliases, 1, wxALL|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5);
+ FlexGridSizer6->Add(FlexGridSizer5, 1, wxALL|wxEXPAND, 5);
CheckBox_ShowSubmodels = new wxCheckBox(this, ID_CHECKBOX1, _("Show SubModels to Add"), wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator, _T("ID_CHECKBOX1"));
CheckBox_ShowSubmodels->SetValue(true);
FlexGridSizer6->Add(CheckBox_ShowSubmodels, 1, wxALL|wxEXPAND, 2);
@@ -272,6 +279,7 @@ ModelGroupPanel::ModelGroupPanel(wxWindow* parent, ModelManager &Models, LayoutP
Connect(ID_SPINCTRL2,wxEVT_COMMAND_SPINCTRL_UPDATED,(wxObjectEventFunction)&ModelGroupPanel::OnSpinCtrl_XCentreOffsetChange);
Connect(ID_SPINCTRL3,wxEVT_COMMAND_SPINCTRL_UPDATED,(wxObjectEventFunction)&ModelGroupPanel::OnSpinCtrl_YCentreOffsetChange);
Connect(ID_COLOURPICKERCTRL_MG_TAGCOLOUR,wxEVT_COMMAND_COLOURPICKER_CHANGED,(wxObjectEventFunction)&ModelGroupPanel::OnColourPickerCtrl_ModelGroupTagColourColourChanged);
+ Connect(ID_BUTTON2,wxEVT_COMMAND_BUTTON_CLICKED,(wxObjectEventFunction)&ModelGroupPanel::OnButtonAliasesClick);
Connect(ID_CHECKBOX1,wxEVT_COMMAND_CHECKBOX_CLICKED,(wxObjectEventFunction)&ModelGroupPanel::OnCheckBox_ShowSubmodelsClick);
Connect(ID_CHECKBOX3,wxEVT_COMMAND_CHECKBOX_CLICKED,(wxObjectEventFunction)&ModelGroupPanel::OnCheckBox_ShowInactiveModelsClick);
Connect(ID_CHECKBOX2,wxEVT_COMMAND_CHECKBOX_CLICKED,(wxObjectEventFunction)&ModelGroupPanel::OnCheckBox_ShowModelGroupsClick);
@@ -1366,3 +1374,13 @@ void ModelGroupPanel::OnChoice_DefaultCameraSelect(wxCommandEvent& event)
{
SaveGroupChanges();
}
+
+void ModelGroupPanel::OnButtonAliasesClick(wxCommandEvent& event)
+{
+ ModelGroup* g = (ModelGroup*)mModels[mGroup];
+ if (g == nullptr)
+ return;
+ EditAliasesDialog dlg(this, g);
+
+ dlg.ShowModal();
+}
diff --git a/xLights/ModelGroupPanel.h b/xLights/ModelGroupPanel.h
index 643755ca64..c75500aa68 100644
--- a/xLights/ModelGroupPanel.h
+++ b/xLights/ModelGroupPanel.h
@@ -65,6 +65,7 @@ class ModelGroupPanel : public wxPanel
wxBitmapButton* ButtonMoveDown;
wxBitmapButton* ButtonMoveUp;
wxBitmapButton* ButtonRemoveModel;
+ wxButton* ButtonAliases;
wxButton* ButtonClearFilter;
wxCheckBox* CheckBox_ShowInactiveModels;
wxCheckBox* CheckBox_ShowModelGroups;
@@ -113,6 +114,7 @@ class ModelGroupPanel : public wxPanel
static const long ID_SPINCTRL3;
static const long ID_STATICTEXT8;
static const long ID_COLOURPICKERCTRL_MG_TAGCOLOUR;
+ static const long ID_BUTTON2;
static const long ID_CHECKBOX1;
static const long ID_CHECKBOX3;
static const long ID_CHECKBOX2;
@@ -169,6 +171,7 @@ class ModelGroupPanel : public wxPanel
void OnCheckBox_ShowOnlyModelsInCurrentViewClick(wxCommandEvent& event);
void OnColourPickerCtrl_ModelGroupTagColourColourChanged(wxColourPickerEvent& event);
void OnChoice_DefaultCameraSelect(wxCommandEvent& event);
+ void OnButtonAliasesClick(wxCommandEvent& event);
//*)
DECLARE_EVENT_TABLE()
diff --git a/xLights/ModelRemap.cpp b/xLights/ModelRemap.cpp
index 8d1d274505..9cb56832aa 100644
--- a/xLights/ModelRemap.cpp
+++ b/xLights/ModelRemap.cpp
@@ -423,7 +423,7 @@ void RemapModelProperties::Remap(RemapModelProperties& original)
wxXmlNode* nn = new wxXmlNode(*n);
RemapNodes(nn, "Eyes-Closed", mapping);
- RemapNodes(nn, "Eyes-Closed", mapping);
+ RemapNodes(nn, "Eyes-Closed2", mapping);
RemapNodes(nn, "Eyes-Closed3", mapping);
RemapNodes(nn, "Eyes-Open", mapping);
RemapNodes(nn, "Eyes-Open2", mapping);
diff --git a/xLights/ModelStateDialog.cpp b/xLights/ModelStateDialog.cpp
index 215bc8ad0a..0eaa4c7eb1 100644
--- a/xLights/ModelStateDialog.cpp
+++ b/xLights/ModelStateDialog.cpp
@@ -21,6 +21,7 @@
#include "xLightsMain.h"
#include "support/VectorMath.h"
#include "models/CustomModel.h"
+#include "utils/string_utils.h"
#include
@@ -63,6 +64,7 @@ const long ModelStateDialog::ID_SPLITTERWINDOW1 = wxNewId();
const long ModelStateDialog::ID_TIMER1 = wxNewId();
const long ModelStateDialog::STATE_DIALOG_IMPORT_SUB = wxNewId();
+const long ModelStateDialog::STATE_DIALOG_IMPORT_ALL_SUB = wxNewId();
const long ModelStateDialog::STATE_DIALOG_COPY_STATES = wxNewId();
const long ModelStateDialog::STATE_DIALOG_IMPORT_MODEL = wxNewId();
const long ModelStateDialog::STATE_DIALOG_IMPORT_FILE = wxNewId();
@@ -854,6 +856,7 @@ void ModelStateDialog::OnButton_ImportClick(wxCommandEvent& event)
}
mnu.Append(STATE_DIALOG_IMPORT_MODEL, "Import From Model");
mnu.Append(STATE_DIALOG_IMPORT_FILE, "Import From File");
+ mnu.Append(STATE_DIALOG_IMPORT_ALL_SUB, "Import From SubModels");
mnu.AppendSeparator();
mnu.Append(STATE_DIALOG_SHIFT, "Shift Nodes");
mnu.Append(STATE_DIALOG_REVERSE, "Reverse Nodes");
@@ -1074,32 +1077,22 @@ wxString ModelStateDialog::getSubmodelNodes(Model* sm)
void ModelStateDialog::OnAddBtnPopup(wxCommandEvent& event)
{
- if (event.GetId() == STATE_DIALOG_IMPORT_MODEL)
- {
+ if (event.GetId() == STATE_DIALOG_IMPORT_MODEL) {
ImportStatesFromModel();
- }
- else if (event.GetId() == STATE_DIALOG_IMPORT_FILE)
- {
+ } else if (event.GetId() == STATE_DIALOG_IMPORT_FILE) {
const wxString filename = wxFileSelector(_("Choose Model file"), wxEmptyString, wxEmptyString, wxEmptyString, "xModel Files (*.xmodel)|*.xmodel", wxFD_OPEN);
if (filename.IsEmpty()) return;
-
ImportStates(filename);
- }
- else if (event.GetId() == STATE_DIALOG_COPY)
- {
+ } else if (event.GetId() == STATE_DIALOG_COPY) {
CopyStateData();
- }
- else if (event.GetId() == STATE_DIALOG_RENAME)
- {
+ } else if (event.GetId() == STATE_DIALOG_RENAME) {
RenameState();
- }
- else if(event.GetId() == STATE_DIALOG_SHIFT)
- {
+ } else if(event.GetId() == STATE_DIALOG_SHIFT) {
ShiftStateNodes();
- }
- else if(event.GetId() == STATE_DIALOG_REVERSE)
- {
+ } else if(event.GetId() == STATE_DIALOG_REVERSE) {
ReverseStateNodes();
+ } else if (event.GetId() == STATE_DIALOG_IMPORT_ALL_SUB) {
+ ImportStatesFromSubModels();
}
}
@@ -1180,6 +1173,58 @@ void ModelStateDialog::ImportStates(const wxString & filename)
}
}
+void ModelStateDialog::ImportStatesFromSubModels()
+{
+ if (model->GetSubModels().size() == 0) {
+ wxMessageBox("No SubModels Found.");
+ return;
+ }
+ wxTextEntryDialog dlg(this, "New State Model", "Enter name for new state model definition");
+ if (dlg.ShowModal() == wxID_OK) {
+ std::string name = dlg.GetValue().ToStdString();
+ if (NameChoice->FindString(name) == wxNOT_FOUND) {
+ NameChoice->Append(name);
+ NameChoice->SetStringSelection(name);
+ NameChoice->Enable();
+ DeleteButton->Enable();
+ StateTypeChoice->ChangeSelection(NODE_RANGE_STATE);
+ UpdateStateType();
+
+ int idx { 0 };
+ for (Model* sm : model->GetSubModels()) {
+ auto subname = cleanSubName(sm->Name());
+ const auto nodes = getSubmodelNodes(sm);
+ auto newNodeArrray = wxSplit(ExpandNodes(nodes), ',');
+
+ // sort
+ std::sort(newNodeArrray.begin(), newNodeArrray.end(),
+ [](const wxString& a, const wxString& b) {
+ return wxAtoi(a) < wxAtoi(b);
+ });
+
+ // make unique
+ newNodeArrray.erase(std::unique(newNodeArrray.begin(), newNodeArrray.end()), newNodeArrray.end());
+ NodeRangeGrid->SetCellValue(idx, NAME_COL, subname);
+ NodeRangeGrid->SetCellValue(idx, CHANNEL_COL, CompressNodes(wxJoin(newNodeArrray, ',')));
+ GetValue(NodeRangeGrid, idx, NAME_COL, stateData[name]);
+ GetValue(NodeRangeGrid, idx, CHANNEL_COL, stateData[name]);
+ ++idx;
+ if (idx >= 200) {//state max out at 200
+ break;
+ }
+ }
+ NodeRangeGrid->Refresh();
+ }
+ }
+}
+
+std::string ModelStateDialog::cleanSubName(std::string name)
+{
+ name = ::Lower(name);
+ Replace(name, " ", "_");
+ return name;
+}
+
void ModelStateDialog::AddStates(std::map > states)
{
bool overRide = false;
diff --git a/xLights/ModelStateDialog.h b/xLights/ModelStateDialog.h
index 25fb8f65d1..01b6832100 100644
--- a/xLights/ModelStateDialog.h
+++ b/xLights/ModelStateDialog.h
@@ -92,6 +92,7 @@ class ModelStateDialog : public wxDialog
static const long ID_TIMER1;
static const long STATE_DIALOG_IMPORT_SUB;
+ static const long STATE_DIALOG_IMPORT_ALL_SUB;
static const long STATE_DIALOG_COPY_STATES;
static const long STATE_DIALOG_IMPORT_MODEL;
static const long STATE_DIALOG_IMPORT_FILE;
@@ -165,6 +166,8 @@ class ModelStateDialog : public wxDialog
wxString getSubmodelNodes(Model* sm);
void ImportStates(const wxString& filename);
void ImportStatesFromModel();
+ void ImportStatesFromSubModels();
+ std::string cleanSubName(std::string name);
void AddStates(std::map> states);
wxArrayString getModelList(ModelManager* modelManager);
diff --git a/xLights/MultiControllerUploadDialog.cpp b/xLights/MultiControllerUploadDialog.cpp
index 8e1eace3f3..2cb517f0b0 100644
--- a/xLights/MultiControllerUploadDialog.cpp
+++ b/xLights/MultiControllerUploadDialog.cpp
@@ -21,6 +21,8 @@
#include "controllers/ControllerCaps.h"
#include "outputs/ControllerEthernet.h"
+#include "utils/ip_utils.h"
+
//(*IdInit(MultiControllerUploadDialog)
const long MultiControllerUploadDialog::ID_STATICTEXT1 = wxNewId();
const long MultiControllerUploadDialog::ID_CHECKLISTBOX1 = wxNewId();
@@ -31,6 +33,9 @@ const long MultiControllerUploadDialog::ID_TEXTCTRL1 = wxNewId();
const long MultiControllerUploadDialog::ID_MCU_SELECTALL = wxNewId();
const long MultiControllerUploadDialog::ID_MCU_SELECTNONE = wxNewId();
+const long MultiControllerUploadDialog::ID_MCU_SELECTACTIVE = wxNewId();
+const long MultiControllerUploadDialog::ID_MCU_DESELECTINACTIVE = wxNewId();
+const long MultiControllerUploadDialog::ID_MCU_SELECTAUTO = wxNewId();
BEGIN_EVENT_TABLE(MultiControllerUploadDialog, wxDialog)
//(*EventTable(MultiControllerUploadDialog)
@@ -92,9 +97,8 @@ MultiControllerUploadDialog::MultiControllerUploadDialog(wxWindow* parent, wxWin
}
}
}
-
+ LoadChecked();
Fit();
-
ValidateWindow();
}
@@ -130,6 +134,7 @@ void MultiControllerUploadDialog::OnButton_UploadClick(wxCommandEvent& event)
TextCtrl_Log->AppendText(message);
TextCtrl_Log->AppendText("\n");
TextCtrl_Log->AppendText(" Done.");
+ TextCtrl_Log->AppendText("\n");
}
CheckListBox_Controllers->Enable();
@@ -140,6 +145,7 @@ void MultiControllerUploadDialog::OnButton_UploadClick(wxCommandEvent& event)
void MultiControllerUploadDialog::OnButton_CancelClick(wxCommandEvent& event)
{
+ SaveChecked();
EndDialog(wxID_CLOSE);
}
@@ -160,6 +166,28 @@ void MultiControllerUploadDialog::OnListRClick(wxContextMenuEvent& event)
wxMenu mnu;
mnu.Append(ID_MCU_SELECTALL, "Select All");
mnu.Append(ID_MCU_SELECTNONE, "Select None");
+ mnu.Append(ID_MCU_SELECTACTIVE, "Select Active");
+ mnu.Append(ID_MCU_SELECTAUTO, "Select Auto Config");
+ mnu.Append(ID_MCU_DESELECTINACTIVE, "Deselect Inactive");
+
+ std::vector proxies;
+ for (auto* c : _controllers) {
+ auto controllerproxy = c->GetFPPProxy();
+ if (!controllerproxy.empty()) {
+ if (std::find(proxies.begin(), proxies.end(), controllerproxy) == proxies.end()) {
+ proxies.push_back(controllerproxy);
+ }
+ }
+ }
+ if (!proxies.empty()) {
+ std::sort(proxies.begin(), proxies.end());
+ wxMenu* srMenu = new wxMenu();
+ for (auto p : proxies) {
+ srMenu->Append(wxNewId(), wxString(p));
+ }
+ srMenu->Connect(wxEVT_MENU, (wxObjectEventFunction)&MultiControllerUploadDialog::OnProxyPopup, nullptr, this);
+ mnu.AppendSubMenu(srMenu, "Select with Proxy");
+ }
mnu.Connect(wxEVT_MENU, (wxObjectEventFunction)&MultiControllerUploadDialog::OnPopup, nullptr, this);
PopupMenu(&mnu);
@@ -172,11 +200,81 @@ void MultiControllerUploadDialog::OnPopup(wxCommandEvent& event)
CheckListBox_Controllers->Check(i);
}
ValidateWindow();
- }
- else if (event.GetId() == ID_MCU_SELECTNONE) {
+ } else if (event.GetId() == ID_MCU_SELECTNONE) {
for (size_t i = 0; i < CheckListBox_Controllers->GetCount(); i++) {
CheckListBox_Controllers->Check(i, false);
}
ValidateWindow();
+ } else if (event.GetId() == ID_MCU_SELECTACTIVE) {
+ for (size_t i = 0; i < CheckListBox_Controllers->GetCount(); i++) {
+ if (_controllers[i]->IsActive()) {
+ CheckListBox_Controllers->Check(i);
+ }
+
+ }
+ ValidateWindow();
+ } else if (event.GetId() == ID_MCU_DESELECTINACTIVE) {
+ for (size_t i = 0; i < CheckListBox_Controllers->GetCount(); i++) {
+ if (!_controllers[i]->IsActive()) {
+ CheckListBox_Controllers->Check(i, false);
+ }
+ }
+ ValidateWindow();
+ } else if (event.GetId() == ID_MCU_SELECTAUTO) {
+ for (size_t i = 0; i < CheckListBox_Controllers->GetCount(); i++) {
+ if (_controllers[i]->IsAutoLayout()) {
+ CheckListBox_Controllers->Check(i);
+ }
+ }
+ ValidateWindow();
+ }
+}
+
+void MultiControllerUploadDialog::OnProxyPopup(wxCommandEvent& event)
+{
+ auto id = event.GetId();
+ wxString label = ((wxMenu*)event.GetEventObject())->GetLabelText(id);
+ for (size_t i = 0; i < CheckListBox_Controllers->GetCount(); i++) {
+ if (!_controllers[i] || _controllers[i]->GetFPPProxy().empty()) {
+ continue;
+ }
+ if (label.compare(_controllers[i]->GetFPPProxy()) == 0) {
+ CheckListBox_Controllers->Check(i);
+ }
+ }
+ ValidateWindow();
+}
+
+void MultiControllerUploadDialog::SaveChecked()
+{
+ wxArrayInt ch;
+ CheckListBox_Controllers->GetCheckedItems(ch);
+ std::list fake;
+ std::vector selected_controllers;
+ for (int i = 0; i < ch.Count() ; i++) {
+ auto c = _controllers[ch[i]];
+ selected_controllers.push_back(c->GetIP());
+ }
+
+ wxConfigBase* config = wxConfigBase::Get();
+ config->Write("MultiControllerUploadSelection", wxString(Join(selected_controllers, ",")));
+ config->Flush();
+}
+
+void MultiControllerUploadDialog::LoadChecked()
+{
+ wxConfigBase* config = wxConfigBase::Get();
+
+ if (config != nullptr) {
+ wxString controllerSelect = "";
+
+ config->Read("MultiControllerUploadSelection", &controllerSelect);
+ std::vector selected_controllers = Split(controllerSelect, ',');
+ for (size_t i = 0; i < CheckListBox_Controllers->GetCount(); i++) {
+ auto c = _controllers[i];
+ if (std::find(selected_controllers.begin(), selected_controllers.end(), c->GetIP()) != selected_controllers.end()) {
+ CheckListBox_Controllers->Check(i);
+ }
+ }
}
}
diff --git a/xLights/MultiControllerUploadDialog.h b/xLights/MultiControllerUploadDialog.h
index cc8078e876..954222f46b 100644
--- a/xLights/MultiControllerUploadDialog.h
+++ b/xLights/MultiControllerUploadDialog.h
@@ -53,6 +53,9 @@ class MultiControllerUploadDialog : public wxDialog
static const long ID_MCU_SELECTALL;
static const long ID_MCU_SELECTNONE;
+ static const long ID_MCU_SELECTACTIVE;
+ static const long ID_MCU_DESELECTINACTIVE;
+ static const long ID_MCU_SELECTAUTO;
private:
@@ -64,6 +67,8 @@ class MultiControllerUploadDialog : public wxDialog
void OnListRClick(wxContextMenuEvent& event);
void OnPopup(wxCommandEvent& event);
-
+ void OnProxyPopup(wxCommandEvent& event);
+ void SaveChecked();
+ void LoadChecked();
DECLARE_EVENT_TABLE()
};
diff --git a/xLights/PathGenerationDialog.cpp b/xLights/PathGenerationDialog.cpp
index 94089e9b8e..debed1e214 100644
--- a/xLights/PathGenerationDialog.cpp
+++ b/xLights/PathGenerationDialog.cpp
@@ -64,7 +64,7 @@ PathGenerationDialog::PathGenerationDialog(wxWindow* parent, const std::string&
FlexGridSizer1->Add(Panel1, 1, wxALL|wxEXPAND, 5);
FlexGridSizer3 = new wxFlexGridSizer(0, 2, 0, 0);
FlexGridSizer3->AddGrowableCol(0);
- FilePickerCtrl1 = new wxFilePickerCtrl(this, ID_FILEPICKERCTRL1, wxEmptyString, _("Select an image file"), _T("*.jpg;*.gif;*.png;*.bmp;*.jpeg"), wxDefaultPosition, wxDefaultSize, wxFLP_FILE_MUST_EXIST|wxFLP_OPEN|wxFLP_USE_TEXTCTRL, wxDefaultValidator, _T("ID_FILEPICKERCTRL1"));
+ FilePickerCtrl1 = new wxFilePickerCtrl(this, ID_FILEPICKERCTRL1, wxEmptyString, _("Select an image file"), _T("*.jpg;*.gif;*.png;*.bmp;*.jpeg;*.webp"), wxDefaultPosition, wxDefaultSize, wxFLP_FILE_MUST_EXIST|wxFLP_OPEN|wxFLP_USE_TEXTCTRL, wxDefaultValidator, _T("ID_FILEPICKERCTRL1"));
FlexGridSizer3->Add(FilePickerCtrl1, 1, wxALL|wxEXPAND, 5);
Slider_Brightness = new wxSlider(this, ID_SLIDER1, 100, 0, 100, wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator, _T("ID_SLIDER1"));
FlexGridSizer3->Add(Slider_Brightness, 1, wxALL|wxEXPAND, 5);
diff --git a/xLights/RenderBuffer.cpp b/xLights/RenderBuffer.cpp
index 1c8baf87d9..90391c4973 100644
--- a/xLights/RenderBuffer.cpp
+++ b/xLights/RenderBuffer.cpp
@@ -226,6 +226,36 @@ void RenderBuffer::AlphaBlend(const RenderBuffer& src)
inline double DegToRad(double deg) { return (deg * M_PI) / 180.0; }
+// MoC - March 2023
+// The wx font map is not thread safe in some cases, effects using
+// it from background threads need to mutex each other (and ideally
+// the event loop thread but meh. This is not the best place (WX
+// would be a better place), but this is better than no place.
+//
+// The first step here was centralizing the access methods, putting a
+// lock around them then became possible.
+// Per dkulp, we could, in the future, pre-populate the cache from the
+// main thread, or we could use CallAfter or similar to do the font
+// lookup on the main thread, which may be incrementally better than
+// just a lock shared between background threads.
+std::mutex FONT_MAP_LOCK;
+
+std::map FONT_MAP_TXT;
+std::map FONT_MAP_SHP;
+
+class FontMapLock
+{
+ std::unique_lock lk;
+
+public:
+ FontMapLock() :
+ lk(FONT_MAP_LOCK)
+ {}
+
+ ~FontMapLock()
+ {}
+};
+
DrawingContext::DrawingContext(int BufferWi, int BufferHt, bool allowShared, bool alpha) : nullBitmap(wxNullBitmap)
{
gc = nullptr;
@@ -244,6 +274,7 @@ DrawingContext::DrawingContext(int BufferWi, int BufferHt, bool allowShared, boo
dc = new wxMemoryDC(*bitmap);
if (!allowShared) {
+ FontMapLock lk;
//make sure we UnShare everything that is being held onto
//also use "non-normal" defaults to avoid "==" issue that
//would keep it from using the non-shared versions
@@ -303,35 +334,6 @@ PathDrawingContext::PathDrawingContext(int BufferWi, int BufferHt, bool allowSha
PathDrawingContext::~PathDrawingContext() {}
-// MoC - March 2023
-// The wx font map is not thread safe in some cases, effects using
-// it from background threads need to mutex each other (and ideally
-// the event loop thread but meh. This is not the best place (WX
-// would be a better place), but this is better than no place.
-//
-// The first step here was centralizing the access methods, putting a
-// lock around them then became possible.
-// Per dkulp, we could, in the future, pre-populate the cache from the
-// main thread, or we could use CallAfter or similar to do the font
-// lookup on the main thread, which may be incrementally better than
-// just a lock shared between background threads.
-std::mutex FONT_MAP_LOCK;
-
-std::map FONT_MAP_TXT;
-std::map FONT_MAP_SHP;
-
-class FontMapLock
-{
- std::unique_lock lk;
-
-public:
- FontMapLock() :
- lk(FONT_MAP_LOCK)
- {}
-
- ~FontMapLock()
- {}
-};
TextDrawingContext::TextDrawingContext(int BufferWi, int BufferHt, bool allowShared)
@@ -538,6 +540,7 @@ void PathDrawingContext::FillPath(wxGraphicsPath& path, wxPolygonFillMode fillSt
void TextDrawingContext::SetFont(const wxFontInfo& font, const xlColor& color)
{
+ FontMapLock lk;
if (gc != nullptr) {
int style = wxFONTFLAG_NOT_ANTIALIASED;
if (font.GetWeight() == wxFONTWEIGHT_BOLD) {
@@ -602,7 +605,6 @@ void TextDrawingContext::SetFont(const wxFontInfo& font, const xlColor& color)
lf.lfPitchAndFamily,
lf.lfFaceName);*/
{
- FontMapLock lk;
wxString s = f.GetNativeFontInfoDesc();
s.Replace(";2;", ";3;", false);
f.SetNativeFontInfo(s);
diff --git a/xLights/SeqSettingsDialog.cpp b/xLights/SeqSettingsDialog.cpp
index 92a843c25f..7675f58fe4 100644
--- a/xLights/SeqSettingsDialog.cpp
+++ b/xLights/SeqSettingsDialog.cpp
@@ -1535,7 +1535,7 @@ void SeqSettingsDialog::MediaLoad(wxFileName name_and_path)
void SeqSettingsDialog::MediaChooser()
{
- wxFileDialog OpenDialog(this, "Choose Audio file", wxEmptyString, wxEmptyString, "FPP 2.x+ Audio Files|*.mp3;*.ogg;*.m4p;*.mp4;*.m4a|FPP 1.x Audio Files|*.mp3;*.ogg;*.m4p;*.mp4|xLights Audio Files|*.mp3;*.ogg;*.m4p;*.mp4;*.avi;*.wma;*.au;*.wav;*.m4a;*.mid;*.mkv;*.mov;*.mpg;*.asf;*.flv;*.mpeg;*.wmv;*.flac", wxFD_OPEN | wxFD_FILE_MUST_EXIST, wxDefaultPosition);
+ wxFileDialog OpenDialog(this, "Choose Audio file", wxEmptyString, wxEmptyString, "FPP Audio Files|*.mp3;*.ogg;*.m4p;*.mp4;*.m4a;*.aac;*.wav;*.flac;*.wma;*.au;*.mkv;*.mov|xLights Audio Files|*.mp3;*.ogg;*.m4p;*.mp4;*.avi;*.wma;*.au;*.wav;*.m4a;*.mid;*.mkv;*.mov;*.mpg;*.asf;*.flv;*.mpeg;*.wmv;*.flac", wxFD_OPEN | wxFD_FILE_MUST_EXIST, wxDefaultPosition);
std::string media_directory = media_directories.empty() ? "" : media_directories.front();
diff --git a/xLights/SequenceData.h b/xLights/SequenceData.h
index e46ab42004..e0ddd49b49 100644
--- a/xLights/SequenceData.h
+++ b/xLights/SequenceData.h
@@ -46,19 +46,23 @@ class SequenceData {
void Zero() {
memset(_data, 0x00, _numChannels);
}
+
void Zero(unsigned int start, unsigned int count) {
+ if (_data == nullptr) return;
if (start < 0) return;
if (count < 1) return;
if (start + count > _numChannels) return;
memset(&_data[start], 0x00, count);
}
- unsigned char &operator[](unsigned int channel) {
+ [[nodiscard]] unsigned char& operator[](unsigned int channel)
+ {
wxASSERT(_zero == 0);
return channel < _numChannels ? _data[channel] : _zero;
}
- const unsigned char *operator[](unsigned int channel) const {
+ [[nodiscard]] const unsigned char* operator[](unsigned int channel) const
+ {
const unsigned char* cdata = _data;
return channel < _numChannels ? &cdata[channel] : &_constzero;
}
@@ -106,24 +110,38 @@ class SequenceData {
unsigned int TotalTime() const { return _numFrames * _frameTime; }
bool OK(unsigned int frame, unsigned int channel) const { return frame < _numFrames && channel < _numChannels; }
- FrameData &operator[](unsigned int frame) {
+ [[nodiscard]] FrameData& operator[](unsigned int frame)
+ {
if (frame >= _numFrames) {
return _invalidFrame;
}
return _frames[frame];
}
- const FrameData &operator[](unsigned int frame) const {
+ [[nodiscard]] const FrameData& operator[](unsigned int frame) const
+ {
if (frame >= _numFrames) {
return _invalidFrame;
}
return _frames[frame];
}
- unsigned int NumChannels() const { return _numChannels;}
- unsigned int NumFrames() const { return _numFrames;}
- unsigned int FrameTime() const { return _frameTime;}
- bool IsValidData() const { return !_dataBlocks.empty(); }
+ [[nodiscard]] unsigned int NumChannels() const
+ {
+ return _numChannels;
+ }
+ [[nodiscard]] unsigned int NumFrames() const
+ {
+ return _numFrames;
+ }
+ [[nodiscard]] unsigned int FrameTime() const
+ {
+ return _frameTime;
+ }
+ [[nodiscard]] bool IsValidData() const
+ {
+ return !_dataBlocks.empty();
+ }
// encodes contents of SeqData in channel order
- wxString base64_encode();
+ [[nodiscard]] wxString base64_encode();
};
diff --git a/xLights/ShaderDownloadDialog.cpp b/xLights/ShaderDownloadDialog.cpp
index bd839ca114..868f734638 100644
--- a/xLights/ShaderDownloadDialog.cpp
+++ b/xLights/ShaderDownloadDialog.cpp
@@ -35,7 +35,6 @@
#undef min
#undef max
-CachedFileDownloader ShaderDownloadDialog::_cache;
class MShader
{
@@ -327,9 +326,6 @@ ShaderDownloadDialog::ShaderDownloadDialog(wxWindow* parent, wxWindowID id, cons
SetSize(800, 600);
- static log4cpp::Category &logger_base = log4cpp::Category::getInstance(std::string("log_base"));
- logger_base.debug("File cache size: %d", _cache.size());
-
ListView_Sites->AppendColumn("", wxListColumnFormat::wxLIST_FORMAT_LEFT);
ListView_Sites->InsertItem(0, "www.interactiveshaderformat.com/");
ListView_Sites->InsertItem(0, "www.shadertoy.com/");
@@ -426,7 +422,7 @@ ShaderDownloadDialog::~ShaderDownloadDialog()
//(*Destroy(ShaderDownloadDialog)
//*)
- _cache.Save();
+ GetCache().Save();
for (const auto& it : _shaders)
{
diff --git a/xLights/ShaderDownloadDialog.h b/xLights/ShaderDownloadDialog.h
index 01e2a18b2d..d9bcd95018 100644
--- a/xLights/ShaderDownloadDialog.h
+++ b/xLights/ShaderDownloadDialog.h
@@ -40,7 +40,6 @@ class ShaderDownloadDialog: public wxDialog
std::string _shaderFile;
int _currImage = -1;
wxImage _shaderImage;
- static CachedFileDownloader _cache;
wxXmlDocument* GetXMLFromURL(wxURI url, std::string& filename, wxProgressDialog* prog, int low, int high) const;
bool LoadTree(wxProgressDialog* prog, int low = 0, int high = 100);
@@ -57,7 +56,7 @@ class ShaderDownloadDialog: public wxDialog
virtual ~ShaderDownloadDialog();
std::string GetShaderFile() const { return _shaderFile; }
bool DlgInit(wxProgressDialog* prog, int low, int high);
- static CachedFileDownloader& GetCache() { return _cache; }
+ static CachedFileDownloader& GetCache() { return CachedFileDownloader::GetDefaultCache(); }
//(*Declarations(ShaderDownloadDialog)
wxButton* Button_Download;
diff --git a/xLights/SubModelsDialog.cpp b/xLights/SubModelsDialog.cpp
index 0f0a9ef8a8..fde6fbbf22 100644
--- a/xLights/SubModelsDialog.cpp
+++ b/xLights/SubModelsDialog.cpp
@@ -32,6 +32,8 @@
#include
#include
+#include
+
#include "SubModelsDialog.h"
#include "models/Model.h"
#include "SubBufferPanel.h"
@@ -122,6 +124,8 @@ const long SubModelsDialog::SUBMODEL_DIALOG_EXPAND_STRANDS_ALL = wxNewId();
const long SubModelsDialog::SUBMODEL_DIALOG_COMPRESS_STRANDS_ALL = wxNewId();
const long SubModelsDialog::SUBMODEL_DIALOG_BLANKS_AS_ZERO = wxNewId();
const long SubModelsDialog::SUBMODEL_DIALOG_BLANKS_AS_EMPTY = wxNewId();
+const long SubModelsDialog::SUBMODEL_DIALOG_REMOVE_BLANKS_ZEROS = wxNewId();
+
BEGIN_EVENT_TABLE(SubModelsDialog,wxDialog)
//(*EventTable(SubModelsDialog)
@@ -1027,6 +1031,7 @@ void SubModelsDialog::OnNodesGridCellRightClick(wxGridEvent& event)
mnu.Append(SUBMODEL_DIALOG_COMPRESS_STRANDS_ALL, "Compress All Strands");
mnu.Append(SUBMODEL_DIALOG_BLANKS_AS_ZERO, "Convert Blanks To Zeros");
mnu.Append(SUBMODEL_DIALOG_BLANKS_AS_EMPTY, "Convert Zeros To Empty");
+ mnu.Append(SUBMODEL_DIALOG_REMOVE_BLANKS_ZEROS, "Remove Blanks/Zeros");
mnu.Connect(wxEVT_MENU, (wxObjectEventFunction)&SubModelsDialog::OnNodesGridPopup, nullptr, this);
PopupMenu(&mnu);
@@ -1037,46 +1042,46 @@ void SubModelsDialog::OnNodesGridPopup(wxCommandEvent& event)
if (event.GetId() == SUBMODEL_DIALOG_REMOVE_DUPLICATE) {
RemoveDuplicates(false);
}
- if (event.GetId() == SUBMODEL_DIALOG_SUPPRESS_DUPLICATE) {
+ else if (event.GetId() == SUBMODEL_DIALOG_SUPPRESS_DUPLICATE) {
RemoveDuplicates(true);
}
- if (event.GetId() == SUBMODEL_DIALOG_REMOVE_ALL_DUPLICATE_LR) {
+ else if (event.GetId() == SUBMODEL_DIALOG_REMOVE_ALL_DUPLICATE_LR) {
RemoveAllDuplicates(true, false);
}
- if (event.GetId() == SUBMODEL_DIALOG_REMOVE_ALL_DUPLICATE_TB) {
+ else if (event.GetId() == SUBMODEL_DIALOG_REMOVE_ALL_DUPLICATE_TB) {
RemoveAllDuplicates(false, false);
}
- if (event.GetId() == SUBMODEL_DIALOG_SUPPRESS_ALL_DUPLICATE_LR) {
+ else if (event.GetId() == SUBMODEL_DIALOG_SUPPRESS_ALL_DUPLICATE_LR) {
RemoveAllDuplicates(true, true);
}
- if (event.GetId() == SUBMODEL_DIALOG_SUPPRESS_ALL_DUPLICATE_TB) {
+ else if (event.GetId() == SUBMODEL_DIALOG_SUPPRESS_ALL_DUPLICATE_TB) {
RemoveAllDuplicates(false, true);
}
- if (event.GetId() == SUBMODEL_DIALOG_EVEN_ROWS) {
+ else if (event.GetId() == SUBMODEL_DIALOG_EVEN_ROWS) {
MakeRowsUniform();
}
- if (event.GetId() == SUBMODEL_DIALOG_PIVOT_ROWS_COLUMNS) {
+ else if (event.GetId() == SUBMODEL_DIALOG_PIVOT_ROWS_COLUMNS) {
PivotRowsColumns();
}
- if (event.GetId() == SUBMODEL_DIALOG_SYMMETRIZE) {
+ else if (event.GetId() == SUBMODEL_DIALOG_SYMMETRIZE) {
Symmetrize();
}
- if (event.GetId() == SUBMODEL_DIALOG_SORT_POINTS) {
+ else if (event.GetId() == SUBMODEL_DIALOG_SORT_POINTS) {
OrderPoints(false);
}
- if (event.GetId() == SUBMODEL_DIALOG_SORT_POINTS_ALL) {
+ else if (event.GetId() == SUBMODEL_DIALOG_SORT_POINTS_ALL) {
OrderPoints(true);
}
- if (event.GetId() == SUBMODEL_DIALOG_COMBINE_STRANDS) {
+ else if (event.GetId() == SUBMODEL_DIALOG_COMBINE_STRANDS) {
CombineStrands();
}
- if (event.GetId() == SUBMODEL_DIALOG_EXPAND_STRANDS_ALL) {
+ else if (event.GetId() == SUBMODEL_DIALOG_EXPAND_STRANDS_ALL) {
processAllStrands([](wxString str) { return ExpandNodes(str); });
}
- if (event.GetId() == SUBMODEL_DIALOG_COMPRESS_STRANDS_ALL) {
+ else if (event.GetId() == SUBMODEL_DIALOG_COMPRESS_STRANDS_ALL) {
processAllStrands([](wxString str) { return CompressNodes(str); });
}
- if (event.GetId() == SUBMODEL_DIALOG_BLANKS_AS_ZERO) {
+ else if (event.GetId() == SUBMODEL_DIALOG_BLANKS_AS_ZERO) {
processAllStrands([](wxString str) {
auto ns = wxSplit(str, ',');
for (auto i = ns.begin(); i != ns.end(); ++i) {
@@ -1086,7 +1091,7 @@ void SubModelsDialog::OnNodesGridPopup(wxCommandEvent& event)
return wxJoin(ns, ',');
});
}
- if (event.GetId() == SUBMODEL_DIALOG_BLANKS_AS_EMPTY) {
+ else if (event.GetId() == SUBMODEL_DIALOG_BLANKS_AS_EMPTY) {
processAllStrands([](wxString str) {
auto ns = wxSplit(str, ',');
for (auto i = ns.begin(); i != ns.end(); ++i) {
@@ -1095,6 +1100,16 @@ void SubModelsDialog::OnNodesGridPopup(wxCommandEvent& event)
}
return wxJoin(ns, ',');
});
+ } else if (event.GetId() == SUBMODEL_DIALOG_REMOVE_BLANKS_ZEROS) {
+ processAllStrands([](wxString str) {
+ auto ns = wxSplit(str, ',');
+ ns.erase(std::remove_if(ns.begin(), ns.end(),
+ [](wxString& v) {
+ return (v == "0" || v == "");
+ }),
+ ns.end());
+ return wxJoin(ns, ',');
+ });
}
}
diff --git a/xLights/SubModelsDialog.h b/xLights/SubModelsDialog.h
index 6f0922d522..82cc141ad0 100644
--- a/xLights/SubModelsDialog.h
+++ b/xLights/SubModelsDialog.h
@@ -249,6 +249,7 @@ class SubModelsDialog : public wxDialog
static const long SUBMODEL_DIALOG_COMPRESS_STRANDS_ALL;
static const long SUBMODEL_DIALOG_BLANKS_AS_ZERO;
static const long SUBMODEL_DIALOG_BLANKS_AS_EMPTY;
+ static const long SUBMODEL_DIALOG_REMOVE_BLANKS_ZEROS;
void SaveXML(Model* m);
wxString GetSelectedName() const;
diff --git a/xLights/TabSequence.cpp b/xLights/TabSequence.cpp
index a312dd6379..6274ab722e 100644
--- a/xLights/TabSequence.cpp
+++ b/xLights/TabSequence.cpp
@@ -1162,6 +1162,7 @@ void xLightsFrame::OpenRenderAndSaveSequences(const wxArrayString &origFilenames
Destroy();
} else {
CloseSequence();
+ SetStatusText(_("Batch Render Done."));
}
return;
}
@@ -1186,6 +1187,16 @@ void xLightsFrame::OpenRenderAndSaveSequences(const wxArrayString &origFilenames
wxString seq = fileNames[0];
wxStopWatch sw; // start a stopwatch timer
+ auto b = _renderMode;
+ _renderMode = false;
+ if (fileNames.size() == 1)
+ {
+ SetStatusText(_("Batch Rendering " + seq + ". Last sequence."));
+ } else {
+ SetStatusText(_("Batch Rendering " + seq + ". " + wxString::Format("%d", (int)fileNames.size() - 1) + " sequences left to render."));
+ }
+ _renderMode = b;
+
printf("Processing file %s\n", (const char *)seq.c_str());
logger_base.debug("Batch Render Processing file %s\n", (const char *)seq.c_str());
OpenSequence(seq, nullptr);
diff --git a/xLights/TabSetup.cpp b/xLights/TabSetup.cpp
index e179dbebf3..0a0428b44d 100644
--- a/xLights/TabSetup.cpp
+++ b/xLights/TabSetup.cpp
@@ -529,16 +529,13 @@ void xLightsFrame::GetControllerDetailsForChannel(int32_t channel, std::string&
}
std::string xLightsFrame::GetChannelToControllerMapping(int32_t channel) {
-
int32_t stch;
Controller* c = _outputManager.GetController(channel, stch);
if (c != nullptr) {
return c->GetChannelMapping(channel);
}
- else {
- return wxString::Format("Channel %d could not be mapped to a controller.", channel).ToStdString();
- }
+ return wxString::Format("Channel %d could not be mapped to a controller.", channel).ToStdString();
}
// reset test channel listbox
@@ -2136,21 +2133,27 @@ void xLightsFrame::OnListControllersItemRClick(wxListEvent& event) {
if (SpecialOptions::GetOption("xxx") == "true") {
ethernet += "xxx";
}
- int count = List_Controllers->GetSelectedItemCount();
- Controller *controller = nullptr;
- if (count == 1) {
- auto name = Controllers_PropertyEditor->GetProperty("ControllerName")->GetValue().GetString();
- controller = _outputManager.GetController(name);
+
+ std::vector selectedControllers;
+ for (const auto& controllerName : GetSelectedControllerNames()) {
+ Controller* controller = _outputManager.GetController(controllerName);
+ if (controller)
+ selectedControllers.push_back(controller);
}
+ bool anySelectedControllersFromBase = std::any_of(selectedControllers.begin(), selectedControllers.end(), [](const Controller* controller) { return controller->IsFromBase(); });
+ bool allSelectedControllersFromBase = std::all_of(selectedControllers.begin(), selectedControllers.end(), [](const Controller* controller) { return controller->IsFromBase(); });
+ bool enableActivateMenuItems = selectedControllers.size() > 0 && !anySelectedControllersFromBase;
+ bool enableUnlinkFromBaseMenuItem = selectedControllers.size() > 0 && allSelectedControllersFromBase;
+
mnu.Append(ID_NETWORK_ADDETHERNET, ethernet)->Enable(ButtonAddControllerSerial->IsEnabled());
mnu.Append(ID_NETWORK_ADDNULL, "Insert NULL")->Enable(ButtonAddControllerSerial->IsEnabled());
mnu.Append(ID_NETWORK_ADDSERIAL, "Insert DMX/LOR/DLight/Renard")->Enable(ButtonAddControllerSerial->IsEnabled());
- mnu.Append(ID_NETWORK_ACTIVE, "Activate")->Enable(ButtonAddControllerSerial->IsEnabled() && controller != nullptr && !controller->IsFromBase());
- mnu.Append(ID_NETWORK_ACTIVEXLIGHTS, "Activate in xLights Only")->Enable(ButtonAddControllerSerial->IsEnabled() && controller != nullptr && !controller->IsFromBase());
- mnu.Append(ID_NETWORK_INACTIVE, "Inactivate")->Enable(ButtonAddControllerSerial->IsEnabled() && controller != nullptr && !controller->IsFromBase());
+ mnu.Append(ID_NETWORK_ACTIVE, "Activate")->Enable(ButtonAddControllerSerial->IsEnabled() && enableActivateMenuItems);
+ mnu.Append(ID_NETWORK_ACTIVEXLIGHTS, "Activate in xLights Only")->Enable(ButtonAddControllerSerial->IsEnabled() && enableActivateMenuItems);
+ mnu.Append(ID_NETWORK_INACTIVE, "Inactivate")->Enable(ButtonAddControllerSerial->IsEnabled() && enableActivateMenuItems);
mnu.Append(ID_NETWORK_DELETE, "Delete")->Enable(ButtonAddControllerSerial->IsEnabled());
- mnu.Append(ID_NETWORK_UNLINKFROMBASE, "Unlink from Base Show Folder")->Enable(ButtonAddControllerSerial->IsEnabled() && controller != nullptr && controller->IsFromBase());
+ mnu.Append(ID_NETWORK_UNLINKFROMBASE, "Unlink from Base Show Folder")->Enable(ButtonAddControllerSerial->IsEnabled() && enableUnlinkFromBaseMenuItem);
mnu.Connect(wxEVT_MENU, (wxObjectEventFunction)&xLightsFrame::OnListControllerPopup, nullptr, this);
PopupMenu(&mnu);
@@ -2361,7 +2364,7 @@ bool xLightsFrame::UploadInputToController(Controller* controller, wxString &mes
if (caps->SupportsInputOnlyUpload()) {
auto vendor = controller->GetVendor();
auto model = controller->GetModel();
- auto ip = controller->GetResolvedIP();
+ auto ip = controller->GetResolvedIP(true);
if (ip == "MULTICAST" || ip == "") {
wxTextEntryDialog dlg(this, "Controller IP Address", "IP Address", ip);
if (dlg.ShowModal() != wxID_OK) {
@@ -2426,7 +2429,7 @@ bool xLightsFrame::UploadOutputToController(Controller* controller, wxString& me
if (caps->SupportsUpload()) {
auto vendor = controller->GetVendor();
auto model = controller->GetModel();
- auto ip = controller->GetResolvedIP();
+ auto ip = controller->GetResolvedIP(true);
if (ip == "MULTICAST") {
wxTextEntryDialog dlg(this, "Controller IP Address", "IP Address", ip);
if (dlg.ShowModal() != wxID_OK) {
diff --git a/xLights/TipOfTheDayDialog.cpp b/xLights/TipOfTheDayDialog.cpp
index 6f60c4af91..baec01249d 100644
--- a/xLights/TipOfTheDayDialog.cpp
+++ b/xLights/TipOfTheDayDialog.cpp
@@ -86,8 +86,8 @@ class TipOfDayThread : public wxThread
virtual void* Entry() override
{
// download Tip of day content here
- CachedFileDownloader cache;
- auto file = cache.GetFile(wxURI(TOD_BASE_URL + "tod.xml"), CACHEFOR::CACHETIME_DAY);
+ auto file = CachedFileDownloader::GetDefaultCache().GetFile(wxURI(TOD_BASE_URL + "tod.xml"), CACHEFOR::CACHETIME_DAY);
+ CachedFileDownloader::GetDefaultCache().Save();
wxCommandEvent e(EVT_TIPOFDAY_READY);
e.SetString(file);
@@ -121,8 +121,9 @@ class xlCachedHtmlWindow : public wxHtmlWindow {
url = baseURL + url.substr(baseFileLocation.size());
}
wxURI uri(url);
- auto file = cache.GetFile(uri, CACHEFOR::CACHETIME_LONG);
+ auto file = CachedFileDownloader::GetDefaultCache().GetFile(uri, CACHEFOR::CACHETIME_LONG);
if (file != "") {
+ CachedFileDownloader::GetDefaultCache().Save();
*redirect = file;
if (baseFileLocation == "") {
baseFileLocation = file.substr(0, file.find_last_of(wxFileName::GetPathSeparator()));
@@ -131,7 +132,6 @@ class xlCachedHtmlWindow : public wxHtmlWindow {
}
return wxHtmlWindow::OnHTMLOpeningURL(type, url, redirect);
}
- mutable CachedFileDownloader cache;
mutable std::string baseURL;
mutable std::string baseFileLocation;
};
diff --git a/xLights/UtilClasses.h b/xLights/UtilClasses.h
index 171020434f..ee43b097f3 100644
--- a/xLights/UtilClasses.h
+++ b/xLights/UtilClasses.h
@@ -34,8 +34,7 @@ class MapStringString: public std::map {
}
int GetInt(const std::string &key, const int def = 0) const {
std::map::const_iterator i(find(key));
- size_t l = i->second.length();
- if (i == end() || l == 0 || i->second.at(0) == ' ') {
+ if (i == end() || i->second.length() == 0 || i->second.at(0) == ' ') {
return def;
}
try {
@@ -47,8 +46,7 @@ class MapStringString: public std::map {
float GetFloat(const std::string& key, const float def = 0.0) const
{
std::map::const_iterator i(find(key));
- size_t l = i->second.length();
- if (i == end() || l == 0 || i->second.at(0) == ' ') {
+ if (i == end() || i->second.length() == 0 || i->second.at(0) == ' ') {
return def;
}
try {
@@ -61,8 +59,7 @@ class MapStringString: public std::map {
double GetDouble(const std::string& key, const double def = 0.0) const
{
std::map::const_iterator i(find(key));
- size_t l = i->second.length();
- if (i == end() || l == 0 || i->second.at(0) == ' ') {
+ if (i == end() || i->second.length() == 0 || i->second.at(0) == ' ') {
return def;
}
try {
@@ -249,7 +246,10 @@ class ImageFilePickerCtrl : public wxFilePickerCtrl
{
public:
ImageFilePickerCtrl(wxWindow *parent, wxWindowID id, const wxString& path, const wxString& message, const wxString& wildcard, const wxPoint &pos, const wxSize &size, long style, const wxValidator &validator, const wxString &name) :
- wxFilePickerCtrl(parent, id, path, message, "Image files|*.png;*.bmp;*.jpg;*.gif;*.jpeg|All files (*.*)|*.*", pos, size, style, validator, name)
+ wxFilePickerCtrl(parent, id, path, message, "Image files|*.png;*.bmp;*.jpg;*.gif;*.jpeg"
+ ";*.webp"
+ "|All files (*.*)|*.*",
+ pos, size, style, validator, name)
{
}
};
diff --git a/xLights/ValueCurve.cpp b/xLights/ValueCurve.cpp
index a74e956da9..98104f3ae6 100644
--- a/xLights/ValueCurve.cpp
+++ b/xLights/ValueCurve.cpp
@@ -1679,6 +1679,16 @@ float ValueCurve::GetOutputValueAtDivided(float offset, long startMS, long endMS
return (_min + (_max - _min) * GetValueAt(offset, startMS, endMS)) / _divisor;
}
+float ValueCurve::GetMaxValueDivided()
+{
+ float max = GetMin() / _divisor;
+ for (int i = 0; i < 100; ++i)
+ {
+ max = std::max(max, GetOutputValueAtDivided(i * 50, 0, 100 * 50));
+ }
+ return max;
+}
+
float ValueCurve::ApplyGain(float value, int gain) const
{
float v = (100.0 + gain) * value / 100.0;
@@ -2186,16 +2196,47 @@ void ValueCurve::ScaleAndOffsetValues(float scale, int offset)
return (val * scale ) + offset;
};
- _parameter1 = ScaleVal(_parameter1);
- _parameter2 = ScaleVal(_parameter2);
- _parameter3 = ScaleVal(_parameter3);
- _parameter4 = ScaleVal(_parameter4);
+ std::vector parametersToScale;
- if (_type == "Custom")
- {
- for (auto& it : _values)
- {
+ if (_type == "Custom") {
+ for (auto& it : _values) {
it.y = ScaleVal(it.y);
}
- }
+ } else if (_type == "Flat") {
+ parametersToScale.push_back(1);
+ } else if (_type == "Ramp" || _type == "Ramp Up/Down Hold" || _type == "Saw Tooth" || _type == "Square" || _type == "Random" || _type == "Music" || _type == "Inverted Music" ||
+ _type == "Music Trigger Fade" || _type == "Timing Track Toggle" || _type == "Timing Track Fade Fixed" || _type == "Timing Track Fade Proportional") {
+ parametersToScale.push_back(1);
+ parametersToScale.push_back(2);
+ } else if (_type == "Ramp Up/Down") {
+ parametersToScale.push_back(1);
+ parametersToScale.push_back(2);
+ parametersToScale.push_back(3);
+ } else if (_type == "Parabolic Down" || _type == "Parabolic Up" || _type == "Logarithmic Up" || _type == "Logarithmic Down" || _type == "Exponential Up" || _type == "Exponential Down") {
+ parametersToScale.push_back(2);
+ } else if (_type == "Sine" || _type == "Abs Sine" || _type == "Decaying Sine") {
+ parametersToScale.push_back(4);
+ } else {
+ parametersToScale.push_back(1);
+ parametersToScale.push_back(2);
+ parametersToScale.push_back(3);
+ parametersToScale.push_back(4);
+ }
+
+ for (int param : parametersToScale) {
+ switch (param) {
+ case 1:
+ _parameter1 = ScaleVal(_parameter1);
+ break;
+ case 2:
+ _parameter2 = ScaleVal(_parameter2);
+ break;
+ case 3:
+ _parameter3 = ScaleVal(_parameter3);
+ break;
+ case 4:
+ _parameter4 = ScaleVal(_parameter4);
+ break;
+ }
+ }
}
diff --git a/xLights/ValueCurve.h b/xLights/ValueCurve.h
index e60f33250e..13fe35ae3a 100644
--- a/xLights/ValueCurve.h
+++ b/xLights/ValueCurve.h
@@ -146,6 +146,7 @@ class ValueCurve
float GetValueAt(float offset, long startMS, long endMS);
float GetOutputValueAt(float offset, long startMS, long endMS);
float GetOutputValueAtDivided(float offset, long startMS, long endMS);
+ float GetMaxValueDivided();
float GetScaledValue(float offset) const;
void SetActive(bool a) { _active = a; RenderType(); }
bool IsActive() const { return _active && IsOk(); }
diff --git a/xLights/VendorModelDialog.cpp b/xLights/VendorModelDialog.cpp
index 984236c2e3..cd9ef3a630 100644
--- a/xLights/VendorModelDialog.cpp
+++ b/xLights/VendorModelDialog.cpp
@@ -28,8 +28,6 @@
#include "UtilFunctions.h"
#include "ExternalHooks.h"
-CachedFileDownloader VendorModelDialog::_cache;
-
class MModel;
class MModelWiring
@@ -800,9 +798,6 @@ VendorModelDialog::VendorModelDialog(wxWindow* parent, const std::string& showFo
SetSize(800, 600);
- static log4cpp::Category& logger_base = log4cpp::Category::getInstance(std::string("log_base"));
- logger_base.debug("File cache size: %d", _cache.size());
-
PopulateModelPanel((MModel*)nullptr);
PopulateVendorPanel(nullptr);
@@ -1038,10 +1033,9 @@ VendorModelDialog::~VendorModelDialog()
//(*Destroy(VendorModelDialog)
//*)
- _cache.Save();
+ GetCache().Save();
- for (const auto& it : _vendors)
- {
+ for (const auto& it : _vendors) {
delete it;
}
}
diff --git a/xLights/VendorModelDialog.h b/xLights/VendorModelDialog.h
index 6d88b3d040..8677d9c777 100644
--- a/xLights/VendorModelDialog.h
+++ b/xLights/VendorModelDialog.h
@@ -46,7 +46,6 @@ class VendorModelDialog: public wxDialog
int _currImage = -1;
wxImage _vendorImage;
wxImage _modelImage;
- static CachedFileDownloader _cache;
wxXmlDocument* GetXMLFromURL(wxURI url, std::string& filename, wxProgressDialog* prog, int low, int high, bool keepProgress) const;
bool LoadTree(wxProgressDialog* prog, int low = 0, int high = 100);
@@ -70,7 +69,7 @@ class VendorModelDialog: public wxDialog
std::string GetModelFile() const { return _modelFile; }
bool DlgInit(wxProgressDialog* prog, int low, int high);
bool FindModelFile(const std::string &vendor, const std::string &model);
- static CachedFileDownloader& GetCache() { return _cache; }
+ static CachedFileDownloader& GetCache() { return CachedFileDownloader::GetDefaultCache(); }
//(*Declarations(VendorModelDialog)
wxButton* Button_InsertModel;
diff --git a/xLights/Xlights.vcxproj b/xLights/Xlights.vcxproj
index 2f2cda16c3..f6af9a964c 100644
--- a/xLights/Xlights.vcxproj
+++ b/xLights/Xlights.vcxproj
@@ -130,7 +130,7 @@
Level3
Disabled
- _DEBUG;_WINDOWS;%(PreprocessorDefinitions);WXDEBUG;__WXDEBUG__;_CRT_SECURE_NO_WARNINGS;IGNORE_VENDORS;$(DefineConstants);
+ _DEBUG;_WINDOWS;%(PreprocessorDefinitions);WXDEBUG;__WXDEBUG__;_CRT_SECURE_NO_WARNINGS;IGNORE_VENDORS;$(DefineConstants);wxUSE_WEBP
true
stdcpp17
/D "NOMINMAX"
@@ -292,6 +292,9 @@ xcopy "$(SolutionDir)..\bin64\Vamp\" "$(TargetDir)Vamp\" /e /y /i /r
+
+
+
@@ -707,6 +710,7 @@ xcopy "$(SolutionDir)..\bin64\Vamp\" "$(TargetDir)Vamp\" /e /y /i /r
+
@@ -779,6 +783,9 @@ xcopy "$(SolutionDir)..\bin64\Vamp\" "$(TargetDir)Vamp\" /e /y /i /r
+
+
+
@@ -1201,6 +1208,7 @@ xcopy "$(SolutionDir)..\bin64\Vamp\" "$(TargetDir)Vamp\" /e /y /i /r
+
diff --git a/xLights/Xlights.vcxproj.filters b/xLights/Xlights.vcxproj.filters
index 7fe0d1a247..f27e0a7c5c 100644
--- a/xLights/Xlights.vcxproj.filters
+++ b/xLights/Xlights.vcxproj.filters
@@ -1035,6 +1035,16 @@
Effects
+
+
+ Effects
+
+
+ Effects
+
+
+ Models
+
@@ -2083,6 +2093,16 @@
Effects
+
+
+ Effects
+
+
+ Effects
+
+
+ Models
+
diff --git a/xLights/automation/xLightsAutomations.cpp b/xLights/automation/xLightsAutomations.cpp
index 17cfa5e30e..26d5f048c0 100644
--- a/xLights/automation/xLightsAutomations.cpp
+++ b/xLights/automation/xLightsAutomations.cpp
@@ -456,7 +456,7 @@ bool xLightsFrame::ProcessAutomation(std::vector &paths,
}
fpp->FinalizeUploadSequence();
- if (fpp->fppType == FPP_TYPE::FALCONV4) {
+ if (fpp->fppType == FPP_TYPE::FALCONV4V5) {
// a falcon
std::string proxy = "";
auto c = _outputManager.GetControllers(fpp->ipAddress);
diff --git a/xLights/controllers/AlphaPix.cpp b/xLights/controllers/AlphaPix.cpp
index 2c973946ca..9355204a34 100644
--- a/xLights/controllers/AlphaPix.cpp
+++ b/xLights/controllers/AlphaPix.cpp
@@ -28,6 +28,8 @@
#include
+#include
+
#pragma region Output Classes
class AlphaPixOutput
{
@@ -107,7 +109,7 @@ AlphaPix::AlphaPix(const std::string& ip, const std::string &proxy) : BaseContro
static log4cpp::Category &logger_base = log4cpp::Category::getInstance(std::string("log_base"));
- _page = GetURL("/");
+ _page = APGetURL("/");
if (!_page.empty()) {
if (_page.Contains("Existing user login")) {
logger_base.error("AlphaPix Webpage locked out by another computer");
@@ -214,7 +216,7 @@ bool AlphaPix::ParseWebpage(const wxString& page, AlphaPixData& data) {
}
else {
//Load advance color order page
- const wxString colorPage = PutURL(GetColorOrderURL(), "RGBORD=1");
+ const wxString colorPage = APPutURL(GetColorOrderURL(), "RGBORD=1");
for (auto& pixelPort : _pixelOutputs) {
pixelPort->colorOrder = ExtractSingleColor(colorPage, pixelPort->output);
}
@@ -582,6 +584,79 @@ std::string AlphaPix::SafeDescription(const std::string description) const {
wxString desc(description);
return desc.Left(16).ToStdString();
}
+
+std::string AlphaPix::APGetURL(const std::string& url) const
+{
+ static log4cpp::Category& logger_base = log4cpp::Category::getInstance(std::string("log_base"));
+ std::string res;
+ std::string const baseIP = _fppProxy.empty() ? _ip : _fppProxy;
+
+ CURL* curl = curl_easy_init();
+ if (curl) {
+ auto u = std::string("http://" + baseIP + _baseUrl + url);
+ logger_base.debug("Curl GET: %s", (const char*)u.c_str());
+ curl_easy_setopt(curl, CURLOPT_URL, u.c_str());
+ curl_easy_setopt(curl, CURLOPT_TIMEOUT, 15);
+ curl_easy_setopt(curl, CURLOPT_HTTP09_ALLOWED, 1L);
+ curl_easy_setopt(curl, CURLOPT_ACCEPT_ENCODING, "");
+ std::string response_string;
+ curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writeFunction);
+ curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response_string);
+
+ /* Perform the request, res will get the return code */
+ CURLcode r = curl_easy_perform(curl);
+
+ if (r != CURLE_OK) {
+ logger_base.error("Failure to access %s: %s.", (const char*)url.c_str(), curl_easy_strerror(r));
+ } else {
+ res = response_string;
+ }
+ /* always cleanup */
+ curl_easy_cleanup(curl);
+ }
+ return res;
+}
+
+std::string AlphaPix::APPutURL(const std::string& url, const std::string& request) const
+{
+ static log4cpp::Category& logger_base = log4cpp::Category::getInstance(std::string("log_base"));
+
+ std::string const baseIP = _fppProxy.empty() ? _ip : _fppProxy;
+ logger_base.debug("Making request to Controller '%s'.", (const char*)url.c_str());
+ logger_base.debug(" With data '%s'.", (const char*)request.c_str());
+
+ CURL* curl = curl_easy_init();
+ if (curl != nullptr) {
+ curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "POST");
+ auto u = std::string("http://" + baseIP + _baseUrl + url);
+ logger_base.debug("Curl POST: %s", (const char*)u.c_str());
+ curl_easy_setopt(curl, CURLOPT_URL, u.c_str());
+ curl_easy_setopt(curl, CURLOPT_ACCEPT_ENCODING, "");
+
+ struct curl_slist* headers = NULL;
+ headers = curl_slist_append(headers, _("content-type: application/x-www-form-urlencoded").c_str());
+ curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
+ curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, (long)request.size());
+ curl_easy_setopt(curl, CURLOPT_POSTFIELDS, (const char*)request.c_str());
+ // curl_easy_setopt(curl, CURLOPT_FORBID_REUSE, 1);
+
+ curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writeFunction);
+ curl_easy_setopt(curl, CURLOPT_TIMEOUT, 30);
+ curl_easy_setopt(curl, CURLOPT_HTTP09_ALLOWED, 1L);
+ std::string buffer = "";
+ curl_easy_setopt(curl, CURLOPT_WRITEDATA, &buffer);
+
+ CURLcode ret = curl_easy_perform(curl);
+ curl_easy_cleanup(curl);
+
+ if (ret == CURLE_OK) {
+ return buffer;
+ }
+ logger_base.error("Failure to access %s: %s.", (const char*)url.c_str(), curl_easy_strerror(ret));
+ }
+
+ return "";
+}
#pragma endregion
#pragma region Getters and Setters
@@ -674,7 +749,7 @@ bool AlphaPix::SetOutputs(ModelManager* allmodels, OutputManager* outputManager,
if (serial->upload) {
if (_modelnum == 4) {
const std::string serialRequest = wxString::Format("Rever5=1&DMX512=%d", serial->universe);
- const wxString res = PutURL(GetDMXURL(), serialRequest);
+ const wxString res = APPutURL(GetDMXURL(), serialRequest);
if (res.empty())
worked = false;
wxMilliSleep(1000);
@@ -682,7 +757,7 @@ bool AlphaPix::SetOutputs(ModelManager* allmodels, OutputManager* outputManager,
else {
const std::string serialRequest = wxString::Format("Rever%d=1&DMX512_%d=%d",
serial->output, serial->output, serial->universe);
- const wxString res = PutURL(GetDMXURL(serial->output), serialRequest);
+ const wxString res = APPutURL(GetDMXURL(serial->output), serialRequest);
if (res.empty())
worked = false;
wxMilliSleep(1000);
@@ -694,7 +769,7 @@ bool AlphaPix::SetOutputs(ModelManager* allmodels, OutputManager* outputManager,
progress.Update(50, "Uploading Protocol Type.");
const int newProtocol = EncodeStringPortProtocol(pixelType);
if (newProtocol != -1 && controllerData.protocol != newProtocol) {
- const wxString res = PutURL(GetProtocolURL(), wxString::Format("IC=%d", newProtocol));
+ const wxString res = APPutURL(GetProtocolURL(), wxString::Format("IC=%d", newProtocol));
if (res.empty())
worked = false;
wxMilliSleep(1000);
@@ -708,14 +783,14 @@ bool AlphaPix::SetOutputs(ModelManager* allmodels, OutputManager* outputManager,
colorOrder.erase(std::unique(colorOrder.begin(), colorOrder.end()), colorOrder.end());
if (colorOrder.size() == 1) {
//all the same color order, "simple mode" will do
- const wxString res = PutURL(GetColorOrderURL(), wxString::Format("RGBORD=0&RGBS=%d", colorOrder[0]));
+ const wxString res = APPutURL(GetColorOrderURL(), wxString::Format("RGBORD=0&RGBS=%d", colorOrder[0]));
if (res.empty())
worked = false;
wxMilliSleep(1000);
}
else {
// different color orders, "advance mode" needed
- const wxString res = PutURL(GetColorOrderURL(), "RGBORD=1");
+ const wxString res = APPutURL(GetColorOrderURL(), "RGBORD=1");
if (res.empty())
worked = false;
wxMilliSleep(1000);
@@ -728,7 +803,7 @@ bool AlphaPix::SetOutputs(ModelManager* allmodels, OutputManager* outputManager,
pixelPort->output, pixelPort->colorOrder);
}
- const wxString res2 = PutURL(GetIndvColorOrderURL(), colorRequestString);
+ const wxString res2 = APPutURL(GetIndvColorOrderURL(), colorRequestString);
if (res2.empty())
worked = false;
wxMilliSleep(1000);
@@ -739,7 +814,7 @@ bool AlphaPix::SetOutputs(ModelManager* allmodels, OutputManager* outputManager,
progress.Update(70, "Uploading Output Description.");
const std::string outName = SafeDescription(controller->GetName());
if (!outName.empty() && !controllerData.name.IsSameAs(outName)) {
- const wxString res = PutURL(GetNameURL(), "name=" + outName);
+ const wxString res = APPutURL(GetNameURL(), "name=" + outName);
if (res.empty())
worked = false;
wxMilliSleep(1000);
@@ -760,7 +835,7 @@ bool AlphaPix::SetOutputs(ModelManager* allmodels, OutputManager* outputManager,
logger_base.info("Uploading Output Type.");
progress.Update(80, "Updating Output Type.");
if (!requestInputString.empty()) {
- const wxString res = PutURL(GetInputTypeURL(), requestInputString);
+ const wxString res = APPutURL(GetInputTypeURL(), requestInputString);
if (res.empty())
worked = false;
//wait for reboot
@@ -790,7 +865,7 @@ void AlphaPix::UploadPixelOutputs(bool& worked) {
logger_base.info("PUT String Output Information.");
if (!requestString.empty()) {
- const wxString res = PutURL(GetOutputURL(), requestString);
+ const wxString res = APPutURL(GetOutputURL(), requestString);
if (res.empty())
worked = false;
wxMilliSleep(2000);
@@ -820,7 +895,7 @@ void AlphaPix::UploadFlexPixelOutputs(bool& worked) {
logger_base.info("PUT String Output Information.");
if (!requestString.empty() && upload) {
- const wxString res = PutURL(GetOutputURL(i + 1), requestString);
+ const wxString res = APPutURL(GetOutputURL(i + 1), requestString);
if (res.empty())
worked = false;
wxMilliSleep(2000);
diff --git a/xLights/controllers/AlphaPix.h b/xLights/controllers/AlphaPix.h
index 61504a6d6a..0118673843 100644
--- a/xLights/controllers/AlphaPix.h
+++ b/xLights/controllers/AlphaPix.h
@@ -78,6 +78,9 @@ class AlphaPix : public BaseController
return 2;
return 0;
}
+
+ std::string APGetURL(const std::string& url) const;
+ std::string APPutURL(const std::string& url, const std::string& request) const;
#pragma endregion
#pragma region Private Static Functions
@@ -98,11 +101,22 @@ class AlphaPix : public BaseController
AlphaPix(const std::string& ip, const std::string &fppProxy);
virtual ~AlphaPix();
#pragma endregion
+
+#pragma region Static Functions
+ static size_t writeFunction(void* ptr, size_t size, size_t nmemb, std::string* data)
+ {
+ if (data == nullptr)
+ return 0;
+ data->append((char*)ptr, size * nmemb);
+ return size * nmemb;
+ }
+#pragma endregion
#pragma region Getters and Setters
#ifndef DISCOVERYONLY
virtual bool SetOutputs(ModelManager* allmodels, OutputManager* outputManager, Controller* controller, wxWindow* parent) override;
#endif
virtual bool UsesHTTP() const override { return true; }
+ virtual bool needsHTTP_0_9() const override { return true; }
#pragma endregion
};
diff --git a/xLights/controllers/BaseController.cpp b/xLights/controllers/BaseController.cpp
index 82f05c0b85..5585c3a15d 100644
--- a/xLights/controllers/BaseController.cpp
+++ b/xLights/controllers/BaseController.cpp
@@ -35,6 +35,7 @@
#include "Minleon.h"
#include "WLED.h"
#include "Experience.h"
+#include "utils/CurlManager.h"
#pragma region Constructors and Destructors
BaseController::BaseController(const std::string& ip, const std::string &proxy) : _ip(ip), _fppProxy(proxy), _baseUrl("") {
@@ -104,103 +105,46 @@ BaseController *BaseController::CreateBaseController(Controller *controller, con
#pragma region Protected Functions
std::string BaseController::GetURL(const std::string& url, const std::string& username, const std::string& password) const{
-
static log4cpp::Category& logger_base = log4cpp::Category::getInstance(std::string("log_base"));
- std::string res;
- std::string const baseIP = _fppProxy.empty() ? _ip : _fppProxy;
-
- CURL* curl = curl_easy_init();
- if (curl) {
- auto u = std::string("http://" + baseIP + _baseUrl + url);
- logger_base.debug("Curl GET: %s", (const char*)u.c_str());
- curl_easy_setopt(curl, CURLOPT_URL, u.c_str());
- curl_easy_setopt(curl, CURLOPT_TIMEOUT, 15);
- curl_easy_setopt(curl, CURLOPT_HTTP09_ALLOWED, 1L);
- curl_easy_setopt(curl, CURLOPT_ACCEPT_ENCODING, "");
- //curl_easy_setopt(curl, CURLOPT_FORBID_REUSE, 1);
-
- std::string response_string;
-
- if (!username.empty())
- {
- curl_easy_setopt(curl, CURLOPT_USERNAME, (const char*)username.c_str());
- curl_easy_setopt(curl, CURLOPT_PASSWORD, (const char*)password.c_str());
- }
-
-#ifdef __WXMSW__
- curl_easy_setopt(curl, CURLOPT_DEBUGFUNCTION, Curl::CurlDebug);
- curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
-#endif
- curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writeFunction);
- curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response_string);
-
- /* Perform the request, res will get the return code */
- CURLcode r = curl_easy_perform(curl);
-
- if (r != CURLE_OK) {
- logger_base.error("Failure to access %s: %s.", (const char*)url.c_str(), curl_easy_strerror(r));
- }
- else {
- res = response_string;
- }
-
- /* always cleanup */
- curl_easy_cleanup(curl);
+ std::string const baseIP = _fppProxy.empty() ? _ip : _fppProxy;
+ auto furl = std::string("http://" + baseIP + _baseUrl + url);
+ if (!username.empty()) {
+ CurlManager::INSTANCE.setHostUsernamePassword(baseIP, username, password);
+ }
+ if (needsHTTP_0_9() && _fppProxy.empty()) {
+ CurlManager::INSTANCE.setHostAllowHTTP_0_9(baseIP, true);
+ }
+ int rc = 0;
+ std::string res = CurlManager::INSTANCE.doGet(furl, rc);
+ if (rc == 0 && !needsHTTP_0_9()) {
+ logger_base.error("Failure to access %s: %s.", (const char*)furl.c_str(), res.c_str());
+ return "";
}
return res;
}
std::string BaseController::PutURL(const std::string& url, const std::string& request, const std::string& username, const std::string& password, const std::string& contentType) const
{
-
static log4cpp::Category& logger_base = log4cpp::Category::getInstance(std::string("log_base"));
std::string const baseIP = _fppProxy.empty() ? _ip : _fppProxy;
logger_base.debug("Making request to Controller '%s'.", (const char*)url.c_str());
logger_base.debug(" With data '%s'.", (const char*)request.c_str());
-
- CURL* curl = curl_easy_init();
- if (curl != nullptr) {
- curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "POST");
- auto u = std::string("http://" + baseIP + _baseUrl + url);
- logger_base.debug("Curl POST: %s", (const char*)u.c_str());
- curl_easy_setopt(curl, CURLOPT_URL, u.c_str());
- curl_easy_setopt(curl, CURLOPT_ACCEPT_ENCODING, "");
-
-#ifdef __WXMSW__
- curl_easy_setopt(curl, CURLOPT_DEBUGFUNCTION, Curl::CurlDebug);
- curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
-#endif
-
- struct curl_slist* headers = NULL;
-
- headers = curl_slist_append(headers, _("content-type: application/" + contentType).c_str());
- curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
- curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, (long)request.size());
- curl_easy_setopt(curl, CURLOPT_POSTFIELDS, (const char*)request.c_str());
- //curl_easy_setopt(curl, CURLOPT_FORBID_REUSE, 1);
-
- if (username != "")
- {
- curl_easy_setopt(curl, CURLOPT_USERNAME, (const char*)username.c_str());
- curl_easy_setopt(curl, CURLOPT_PASSWORD, (const char*)password.c_str());
- }
- curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writeFunction);
- curl_easy_setopt(curl, CURLOPT_TIMEOUT, 30);
- curl_easy_setopt(curl, CURLOPT_HTTP09_ALLOWED, 1L);
- std::string buffer = "";
- curl_easy_setopt(curl, CURLOPT_WRITEDATA, &buffer);
-
- CURLcode ret = curl_easy_perform(curl);
- curl_easy_cleanup(curl);
-
- if (ret == CURLE_OK) {
- return buffer;
- }
- logger_base.error("Failure to access %s: %s.", (const char*)url.c_str(), curl_easy_strerror(ret));
+
+ auto furl = std::string("http://" + baseIP + _baseUrl + url);
+ if (!username.empty()) {
+ CurlManager::INSTANCE.setHostUsernamePassword(baseIP, username, password);
}
-
- return "";
+ if (needsHTTP_0_9() && _fppProxy.empty()) {
+ CurlManager::INSTANCE.setHostAllowHTTP_0_9(baseIP, true);
+ }
+ int rc = 0;
+ std::string res = CurlManager::INSTANCE.doPost(furl, contentType, request, rc);
+ if (rc == 0 && !needsHTTP_0_9()) {
+ logger_base.error("Failure to post to %s: %s.", (const char*)furl.c_str(), res.c_str());
+ return "";
+ }
+ return res;
}
#pragma endregion
diff --git a/xLights/controllers/BaseController.h b/xLights/controllers/BaseController.h
index 620976702f..07c5d9399c 100644
--- a/xLights/controllers/BaseController.h
+++ b/xLights/controllers/BaseController.h
@@ -33,20 +33,13 @@ class BaseController
#pragma endregion
#pragma region Protected Functions
+ virtual bool needsHTTP_0_9() const { return false; }
std::string GetURL(const std::string& url, const std::string& username = "", const std::string& password = "") const;
std::string PutURL(const std::string& url, const std::string& request, const std::string& username = "", const std::string& password = "", const std::string& contentType = "x-www-form-urlencoded") const;
#pragma endregion
public:
-
- #pragma region Static Functions
- static size_t writeFunction(void* ptr, size_t size, size_t nmemb, std::string* data) {
- if (data == nullptr) return 0;
- data->append((char*)ptr, size * nmemb);
- return size * nmemb;
- }
- #pragma endregion
#pragma region Constructors and Destructors
BaseController() {}
diff --git a/xLights/controllers/ControllerUploadData.cpp b/xLights/controllers/ControllerUploadData.cpp
index ec335424a6..267d2d8ec8 100644
--- a/xLights/controllers/ControllerUploadData.cpp
+++ b/xLights/controllers/ControllerUploadData.cpp
@@ -571,6 +571,7 @@ void UDControllerPort::CreateVirtualStrings(bool mergeSequential) {
int32_t lastEndChannel = -1000;
UDVirtualString* current = nullptr;
+ int curRemote = 0;
for (const auto& it : _models) {
bool first = false;
int brightness = it->GetBrightness(NO_VALUE_INT);
@@ -586,12 +587,12 @@ void UDControllerPort::CreateVirtualStrings(bool mergeSequential) {
if (current == nullptr || !mergeSequential) {
if (smartRemote != 0) {
- int curRemote = current == nullptr ? (smartRemote < 100 ? 0 : 99): current->_smartRemote < 100;
curRemote++;
for (int sr = curRemote; sr < smartRemote; sr++) {
// we seem to have missed one so create a dummy
current = new UDVirtualString();
_virtualStrings.push_back(current);
+ curRemote++;
current->_endChannel = it->GetStartChannel() + 2;
current->_startChannel = it->GetStartChannel();
current->_description = "DUMMY";
diff --git a/xLights/controllers/Experience.cpp b/xLights/controllers/Experience.cpp
index 73ece5dc99..c03f767f15 100644
--- a/xLights/controllers/Experience.cpp
+++ b/xLights/controllers/Experience.cpp
@@ -23,6 +23,8 @@
#include
+#include "../utils/Curl.h"
+
#include
#include
#include
@@ -51,6 +53,18 @@ std::string Experience::PostJSONToURL(const std::string& url, const wxJSONValue&
}
#pragma endregion
+bool Experience::UploadSequence(const std::string& seq, const std::string& file,std::function progress)
+{
+ static log4cpp::Category& logger_base = log4cpp::Category::getInstance(std::string("log_base"));
+
+ std::string const baseIP = _fppProxy.empty() ? _ip : _fppProxy;
+ std::string url = "http://" + baseIP + _baseUrl + "/upload";
+ logger_base.debug("Uploading to URL: %s", (const char*)url.c_str());
+
+ wxFileName fn(file);
+ return Curl::HTTPUploadFile(url, seq, fn.GetFullName().ToStdString(), progress);
+}
+
#pragma region Encode and Decode
int Experience::EncodeBrightness(int brightness) const
{
diff --git a/xLights/controllers/Experience.h b/xLights/controllers/Experience.h
index a5e410ee00..0e936555a7 100644
--- a/xLights/controllers/Experience.h
+++ b/xLights/controllers/Experience.h
@@ -56,6 +56,8 @@ class Experience : public BaseController
virtual ~Experience(){};
#pragma endregion
+ bool UploadSequence(const std::string& seq, const std::string& file, std::function progress);
+
#pragma region Getters and Setters
#ifndef DISCOVERYONLY
int32_t SetInputUniverses(wxJSONValue& data, Controller* controller);
diff --git a/xLights/controllers/FPP.cpp b/xLights/controllers/FPP.cpp
index 7eeb91af41..b6116fc316 100644
--- a/xLights/controllers/FPP.cpp
+++ b/xLights/controllers/FPP.cpp
@@ -220,9 +220,15 @@ static size_t read_callback(void *ptr, size_t size, size_t nmemb, void *userp) {
return dt->readData(ptr, buffer_size);
}
+static size_t writeFunction(void* ptr, size_t size, size_t nmemb, std::string* data) {
+
+ if (data == nullptr) return 0;
+ data->append((char*)ptr, size * nmemb);
+ return size * nmemb;
+}
CURL *FPP::setupCurl(const std::string &url, bool isGet, int timeout) {
CURL* curl = curl_easy_init();
- curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, BaseController::writeFunction);
+ curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writeFunction);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &curlInputBuffer);
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT_MS, defaultConnectTimeout);
@@ -255,7 +261,7 @@ bool FPP::GetURLAsString(const std::string& url, std::string& val, bool recordEr
std::string fullUrl = ipAddress + url;
std::string ipAddForGet = ipAddress;
if (fppType == FPP_TYPE::ESPIXELSTICK) {
- fullUrl = ipAddress + "/fpp?path=" + url;
+ fullUrl = ipAddress + "/fpp?path=" + url;
}
if (!_fppProxy.empty()) {
fullUrl = "http://" + _fppProxy + "/proxy/" + fullUrl;
@@ -421,14 +427,12 @@ bool FPP::parseSysInfo(wxJSONValue& val) {
}
bool FPP::IsDDPInputEnabled() {
- if (!IsDrive()) {
- wxJSONValue origRoot;
- if (GetURLAsJSON("/api/configfile/ci-universes.json", origRoot, false)) {
- if (origRoot.HasMember("channelInputs") && origRoot.ItemAt("channelInputs").HasMember(0)
- && origRoot.ItemAt("channelInputs").ItemAt(0).HasMember("enabled") &&
- origRoot.ItemAt("channelInputs").ItemAt(0).ItemAt("enabled").AsInt() == 1) {
- return true;
- }
+ wxJSONValue origRoot;
+ if (GetURLAsJSON("/api/configfile/ci-universes.json", origRoot, false)) {
+ if (origRoot.HasMember("channelInputs") && origRoot.ItemAt("channelInputs").HasMember(0)
+ && origRoot.ItemAt("channelInputs").ItemAt(0).HasMember("enabled") &&
+ origRoot.ItemAt("channelInputs").ItemAt(0).ItemAt("enabled").AsInt() == 1) {
+ return true;
}
}
return false;
@@ -517,11 +521,6 @@ void FPP::parseConfig(const std::string& v) {
}
}
-
-bool FPP::IsDrive() {
- return ipAddress.find("/") != std::string::npos || ipAddress.find("\\") != std::string::npos;
-}
-
bool FPP::IsVersionAtLeast(uint32_t maj, uint32_t min, uint32_t patch) const{
if (majorVersion < maj) {
return false;
@@ -595,30 +594,6 @@ static inline void addString(wxMemoryBuffer &buffer, const std::string &str) {
buffer.AppendData(str.c_str(), str.length());
}
-bool FPP::GetPathAsJSON(const std::string &path, wxJSONValue &val) {
- wxFileName fn;
- fn = ToWXString(path);
- if (FileExists(fn)) {
- wxJSONReader reader;
- wxFile tf(fn.GetFullPath());
- wxString json;
- tf.ReadAll(&json);
- reader.Parse(json, &val);
- tf.Close();
- return true;
- }
- return false;
-}
-bool FPP::WriteJSONToPath(const std::string& path, const wxJSONValue& val) {
- wxFileName fn;
- fn = ToWXString(path);
- wxFileOutputStream ufile(fn.GetFullPath());
- wxJSONWriter writer(wxJSONWRITER_STYLED, 0, 3);
- writer.Write(val, ufile);
- ufile.Close();
- return true;
-}
-
int FPP::PostJSONToURL(const std::string& url, const wxJSONValue& val) {
wxString str;
wxJSONWriter writer(wxJSONWRITER_STYLED, 0, 3);
@@ -816,13 +791,15 @@ bool FPP::callMoveFile(const std::string &filename) {
return GetURLAsString("/fppxml.php?command=moveFile&file=" + URLEncode(filename), val);
}
-struct V7ProgressStruct {
+class V7ProgressStruct {
+public:
wxFile in;
FPP *instance;
size_t length;
size_t offset = 0;
int lastPct = 0;
+ int errorCount = 0;
std::string fullUrl;
std::string fileSizeHeader;
@@ -852,17 +829,19 @@ int progress_callback(void *clientp,
void prepareCurlForMulti(V7ProgressStruct *ps) {
static log4cpp::Category& logger_curl = log4cpp::Category::getInstance(std::string("log_curl"));
- constexpr uint64_t BLOCK_SIZE = 64*1024*1024;
+ constexpr uint64_t BLOCK_SIZE = 16*1024*1024;
CurlManager::CurlPrivateData *cpd = nullptr;
CURL *curl = CurlManager::INSTANCE.createCurl(ps->fullUrl, &cpd, true);
- //if we cannot upload it in 5 minutes, we have serious issues
- curl_easy_setopt(curl, CURLOPT_TIMEOUT_MS, 1000*5*60);
+ //if we cannot upload a single chunk in 3 minutes, we have serious issues
+ curl_easy_setopt(curl, CURLOPT_TIMEOUT_MS, 1000*3*60);
struct curl_slist *headers = nullptr;
- headers = curl_slist_append(headers, "Content-Type: application/octet-stream");
+ headers = curl_slist_append(headers, "Content-Type: application/offset+octet-stream");
headers = curl_slist_append(headers, "X-Requested-With: FPPConnect");
-
+ headers = curl_slist_append(headers, "Expect:");
+ headers = curl_slist_append(headers, "Connection: keep-alive");
+
std::string offsetHeader = "Upload-Offset: " + std::to_string(ps->offset);
headers = curl_slist_append(headers, offsetHeader.c_str());
headers = curl_slist_append(headers, ps->fileSizeHeader.c_str());
@@ -876,6 +855,7 @@ void prepareCurlForMulti(V7ProgressStruct *ps) {
cpd->req->resize(remaining);
uint64_t read = ps->in.Read(cpd->req->data(), remaining);
if (read != remaining) {
+ logger_curl.info("ERROR Uploading file: " + ps->filename + " Could not read source file.");
ps->instance->messages.push_back("ERROR Uploading file: " + ps->filename + " Could not read source file.");
}
std::string contentSizeHeader = "Content-Length: " + std::to_string(remaining);
@@ -898,9 +878,20 @@ void prepareCurlForMulti(V7ProgressStruct *ps) {
long response_code = 0;
curl_easy_getinfo(c, CURLINFO_RESPONSE_CODE, &response_code);
logger_curl.info(" FPPConnect CURL Callbak - URL: %s Response: %d", ps->fullUrl.c_str(), response_code);
- ps->offset += remaining;
+ bool cancelled = false;
+ if (response_code != 200 && ps->errorCount < 3) {
+ // strange error on upload, let's restart and try again (up to three attempts)
+ ps->offset = 0;
+ ps->in.Seek(0);
+ ++ps->errorCount;
+ } else if (response_code != 200) {
+ ps->instance->messages.push_back("ERROR Uploading file: " + ps->filename + ". Response code: " + std::to_string(response_code));
+ cancelled = true;
+ } else {
+ ps->offset += remaining;
+ }
uint64_t pct = (ps->offset * 1000) / ps->length;
- bool cancelled = ps->instance->updateProgress(pct, false);
+ cancelled |= ps->instance->updateProgress(pct, false);
if (cancelled || ps->offset >= ps->length) {
delete ps;
} else {
@@ -941,58 +932,10 @@ bool FPP::uploadFileV7(const std::string &filename,
return cancelled;
}
-bool FPP::copyFile(const std::string &filename,
- const std::string &file,
- const std::string &dir) {
- static log4cpp::Category &logger_base = log4cpp::Category::getInstance(std::string("log_base"));
- bool cancelled = false;
- cancelled |= updateProgress(0, true);
-
- wxFile in;
- in.Open(ToWXString(file));
-
- if (in.IsOpened()) {
- wxFile out;
- wxString target = ipAddress + wxFileName::GetPathSeparator() + dir + wxFileName::GetPathSeparator() + filename;
- out.Open(target, wxFile::write);
-
- if (out.IsOpened()) {
- wxFileOffset length = in.Length();
- wxFileOffset done = 0;
-
- uint8_t buffer[8192]; // 8KB at a time
- while (!in.Eof() && !cancelled) {
- size_t read = in.Read(&buffer[0], sizeof(buffer));
- size_t written = out.Write(&buffer[0], read);
- while (written < read) {
- written += out.Write(&buffer[written], read - written);
- }
- done += read;
-
- int prgs = done * 1000 / length;
- cancelled |= updateProgress(prgs, true);
- }
- cancelled |= updateProgress(1000, true);
- in.Close();
- out.Close();
- } else {
- cancelled |= updateProgress(1000, true);
- logger_base.warn(" Copy of file %s failed ... target file %s could not be opened.", (const char *)file.c_str(), (const char *)target.c_str());
- }
- } else {
- cancelled |= updateProgress(1000, true);
- logger_base.warn(" Copy of file %s failed ... file could not be opened.", (const char *)file.c_str());
- }
- return cancelled;
-}
-
bool FPP::uploadOrCopyFile(const std::string &filename,
const std::string &file,
const std::string &dir) {
- if (IsDrive()) {
- return copyFile(filename, file, dir);
- }
- if (IsVersionAtLeast(6, 3, 2)) {
+ if (fppType == FPP_TYPE::FPP && IsVersionAtLeast(6, 3, 2)) {
return uploadFileV7(filename, file, dir);
}
return uploadFile(filename, file);
@@ -1066,6 +1009,52 @@ static void FindHostSpecificMedia(const std::string &hostName, std::string &medi
}
}
}
+bool FPP::CheckUploadMedia(const std::string &media, std::string &mediaBaseName) {
+ bool cancelled = false;
+ wxFileName mfn(FromUTF8(media));
+ std::string mediaFile = media;
+ mediaBaseName = ToUTF8(mfn.GetFullName());
+
+ if (majorVersion >= 6) {
+ FindHostSpecificMedia(hostName, mediaBaseName, mediaFile, mfn);
+ }
+
+ std::string url = "/api/media/" + URLEncode(mediaBaseName) + "/meta";
+ std::string fullUrl = ipAddress + url;
+ std::string ipAddForGet = ipAddress;
+ if (!_fppProxy.empty()) {
+ fullUrl = "http://" + _fppProxy + "/proxy/" + fullUrl;
+ ipAddForGet = _fppProxy;
+ } else {
+ fullUrl = "http://" + fullUrl;
+ }
+ if (username != "") {
+ CurlManager::INSTANCE.setHostUsernamePassword(ipAddForGet, username, password);
+ }
+ CurlManager::INSTANCE.addGet(fullUrl, [this, mfn, mediaBaseName, mediaFile](int rc, const std::string &resp) {
+ bool doMediaUpload = true;
+ if (rc == 200) {
+ wxJSONValue currentMeta;
+ wxJSONReader reader;
+ reader.Parse(resp, ¤tMeta);
+ if (currentMeta.HasMember("format") && currentMeta["format"].HasMember("size") &&
+ (mfn.GetSize() == std::atoi(currentMeta["format"]["size"].AsString().c_str()))) {
+ doMediaUpload = false;
+ }
+ }
+ if (doMediaUpload) {
+ std::string dir = "music";
+ for (auto &a : FPP_VIDEO_EXT) {
+ if (mfn.GetExt() == a) {
+ dir = "videos";
+ }
+ }
+ uploadOrCopyFile(mediaBaseName, mediaFile, dir);
+ }
+ });
+
+ return cancelled;
+}
bool FPP::PrepareUploadSequence(FSEQFile *file,
const std::string &seq,
@@ -1086,46 +1075,17 @@ bool FPP::PrepareUploadSequence(FSEQFile *file,
std::string mediaBaseName = "";
bool cancelled = false;
if (media != "" && fppType == FPP_TYPE::FPP) {
- wxFileName mfn(FromUTF8(media));
- std::string mediaFile = media;
- mediaBaseName = ToUTF8(mfn.GetFullName());
-
- if (majorVersion >= 6) {
- FindHostSpecificMedia(hostName, mediaBaseName, mediaFile, mfn);
- }
-
- bool doMediaUpload = true;
- wxJSONValue currentMeta;
- if (GetURLAsJSON("/api/media/" + URLEncode(mediaBaseName) + "/meta", currentMeta, false)) {
- if (currentMeta.HasMember("format") && currentMeta["format"].HasMember("size") &&
- (mfn.GetSize() == std::atoi(currentMeta["format"]["size"].AsString().c_str()))) {
- doMediaUpload = false;
- }
- }
- if (doMediaUpload) {
- std::string dir = "music";
- for (auto &a : FPP_VIDEO_EXT) {
- if (mfn.GetExt() == a) {
- dir = "videos";
- }
- }
- cancelled |= uploadOrCopyFile(mediaBaseName, mediaFile, dir);
- }
+ cancelled = CheckUploadMedia(media, mediaBaseName);
if (cancelled) {
- return cancelled;
+ return true;
}
}
sequences[baseName].sequence = baseName;
sequences[baseName].media = mediaBaseName;
sequences[baseName].duration = ((float)(file->getStepTime() * file->getNumFrames())) / 1000.0f;
- std::string fileName;
- if (IsDrive()) {
- fileName = ipAddress + GetPathSeparator() + "sequences" + GetPathSeparator() + baseName;
- } else {
- tempFileName = ToStdString(wxFileName::CreateTempFileName(ToWXString(baseName)));
- fileName = tempFileName;
- }
+ tempFileName = ToStdString(wxFileName::CreateTempFileName(ToWXString(baseName)));
+ std::string fileName = tempFileName;
FSEQFile::CompressionType ctype = ::FSEQFile::CompressionType::zstd;
if (type == 3 || type == 4) {
@@ -1139,7 +1099,7 @@ bool FPP::PrepareUploadSequence(FSEQFile *file,
int currentChannelCount = 0;
std::vector> currentRanges;
std::vector> newRanges;
- if (!IsDrive() && fppType == FPP_TYPE::FPP) {
+ if (fppType == FPP_TYPE::FPP) {
wxJSONValue currentMeta;
if (GetURLAsJSON("/api/sequence/" + URLEncode(baseName) + "/meta", currentMeta, false)) {
doSeqUpload = false;
@@ -1335,13 +1295,7 @@ static bool PlaylistContainsEntry(wxJSONValue &pl, const std::string &media, con
bool FPP::UploadPlaylist(const std::string &name) {
wxJSONValue origJson;
- std::string fn;
- if (IsDrive()) {
- fn = (ipAddress + GetPathSeparator() + "playlists" +GetPathSeparator() + name + ".json");
- GetPathAsJSON(fn, origJson);
- } else {
- GetURLAsJSON("/api/playlist/" + URLEncode(name), origJson, false);
- }
+ GetURLAsJSON("/api/playlist/" + URLEncode(name), origJson, false);
for (const auto& info : sequences) {
if (!PlaylistContainsEntry(origJson["mainPlaylist"], info.second.media, info.first)) {
@@ -1367,55 +1321,34 @@ bool FPP::UploadPlaylist(const std::string &name) {
origJson.Remove(wxString("playlistInfo"));
origJson["name"] = name;
- if (IsDrive()) {
- WriteJSONToPath(fn, origJson);
- } else {
- PostJSONToURL("/api/playlist/" + URLEncode(name), origJson);
- }
+ PostJSONToURL("/api/playlist/" + URLEncode(name), origJson);
return false;
}
bool FPP::UploadModels(const wxJSONValue &models) {
- if (IsDrive()) {
- std::string fn = (ipAddress +GetPathSeparator() + "config" +GetPathSeparator() + "model-overlays.json");
- WriteJSONToPath(fn, models);
- } else {
- PostJSONToURL("/api/models", models);
- }
+ PostJSONToURL("/api/models", models);
return false;
}
bool FPP::UploadDisplayMap(const std::string &displayMap) {
- if (IsDrive()) {
- wxFileName fn = (ipAddress + wxFileName::GetPathSeparator() + "config/virtualdisplaymap");
- wxFile tf(fn.GetFullPath());
- tf.Write(displayMap);
- tf.Close();
- } else {
- PostToURL("/api/configfile/virtualdisplaymap", displayMap);
- }
+ PostToURL("/api/configfile/virtualdisplaymap", displayMap);
return false;
}
bool FPP::UploadUDPOut(const wxJSONValue &udp) {
- if (IsDrive()) {
- std::string fn = ipAddress +GetPathSeparator() + "config" +GetPathSeparator() + "co-universes.json";
- WriteJSONToPath(fn, udp);
- } else {
- wxJSONValue orig;
- wxJSONValue newudp = udp;
-
- if (GetURLAsJSON("/api/channel/output/universeOutputs", orig)) {
- if (orig.HasMember("channelOutputs")) {
- for (int x = 0; x < orig["channelOutputs"].Size(); x++) {
- if (orig["channelOutputs"][x]["type"].AsString() == "universes" && orig["channelOutputs"][x].HasMember("interface")) {
- newudp["channelOutputs"][0]["interface"] = orig["channelOutputs"][x]["interface"].AsString();
- }
+ wxJSONValue orig;
+ wxJSONValue newudp = udp;
+
+ if (GetURLAsJSON("/api/channel/output/universeOutputs", orig)) {
+ if (orig.HasMember("channelOutputs")) {
+ for (int x = 0; x < orig["channelOutputs"].Size(); x++) {
+ if (orig["channelOutputs"][x]["type"].AsString() == "universes" && orig["channelOutputs"][x].HasMember("interface")) {
+ newudp["channelOutputs"][0]["interface"] = orig["channelOutputs"][x]["interface"].AsString();
}
}
}
- PostJSONToURL("/api/channel/output/universeOutputs", newudp);
}
+ PostJSONToURL("/api/channel/output/universeOutputs", newudp);
return false;
}
@@ -1622,7 +1555,7 @@ std::string FPP::CreateVirtualDisplayMap(ModelManager* allmodels) {
modelPts.insert(std::make_tuple(x, y, z, ch));
}
}
- for (auto [x,y,z, ch] : modelPts) {
+ for (auto const&[x,y,z, ch] : modelPts) {
ret += ToUTF8(wxString::Format("%d,%d,%d,%d,%d,%s\n",
(int)std::round(x), (int)std::round(y), (int)std::round(z), ch,
model->GetChanCountPerNode(), stringType.c_str()));
@@ -1950,12 +1883,8 @@ wxJSONValue FPP::CreateUniverseFile(const std::list& selected, bool
}
bool FPP::SetRestartFlag() {
- std::string val;
- if (!IsDrive()) {
- restartNeeded = true;
- return PutToURL("/api/settings/restartFlag", "2", "text/plain");
- }
- return false;
+ restartNeeded = true;
+ return PutToURL("/api/settings/restartFlag", "2", "text/plain");
}
bool FPP::Restart( bool ifNeeded) {
@@ -1972,7 +1901,7 @@ bool FPP::Restart( bool ifNeeded) {
void FPP::UpdateChannelRanges()
{
// This probably should handle drives correctly but as is it doesnt bail for now until we add drive support
- if (fppType != FPP_TYPE::FPP || IsDrive()) {
+ if (fppType != FPP_TYPE::FPP) {
return;
}
wxJSONValue jval;
@@ -2008,9 +1937,7 @@ void FPP::UpdateChannelRanges()
}
void FPP::SetDescription(const std::string &st) {
- if (!IsDrive()) {
- PutToURL("/api/settings/HostDescription", st, "text/plain");
- }
+ PutToURL("/api/settings/HostDescription", st, "text/plain");
}
bool FPP::SetInputUniversesBridge(Controller* controller) {
@@ -2028,12 +1955,7 @@ bool FPP::SetInputUniversesBridge(Controller* controller) {
wxJSONValue udp = CreateUniverseFile(std::list({ controller }), true);
if (udp["channelInputs"][0]["universes"].Size() != 0 || forceUpload) {
- if (IsDrive()) {
- std::string fn = (c->GetResolvedIP() +GetPathSeparator() + "config" +GetPathSeparator() + "ci-universes.json");
- WriteJSONToPath(fn, udp);
- } else {
- PostJSONToURL("/api/channel/output/universeInputs", udp);
- }
+ PostJSONToURL("/api/channel/output/universeInputs", udp);
}
return false;
@@ -2224,11 +2146,7 @@ bool FPP::UploadPanelOutputs(ModelManager* allmodels,
startChannel--;
}
if (startChannel >= 0 || fullcontrol) {
- if (IsDrive()) {
- GetPathAsJSON(ipAddress +GetPathSeparator() + "config" +GetPathSeparator() + "channeloutputs.json", origJson);
- } else {
- GetURLAsJSON("/api/channel/output/channelOutputsJSON", origJson, false);
- }
+ GetURLAsJSON("/api/channel/output/channelOutputsJSON", origJson, false);
}
if (startChannel >= 0) {
std::map rngs;
@@ -2254,12 +2172,8 @@ bool FPP::UploadPanelOutputs(ModelManager* allmodels,
}
}
if (changed) {
- if (IsDrive()) {
- WriteJSONToPath(ipAddress +GetPathSeparator() + "config" +GetPathSeparator() + "channeloutputs.json", origJson);
- } else {
- PostJSONToURL("/api/channel/output/channelOutputsJSON", origJson);
- SetRestartFlag();
- }
+ PostJSONToURL("/api/channel/output/channelOutputsJSON", origJson);
+ SetRestartFlag();
}
return false;
}
@@ -2278,11 +2192,7 @@ bool FPP::UploadVirtualMatrixOutputs(ModelManager* allmodels,
bool changed = false;
wxJSONValue origJson;
if (fullcontrol || (rules->SupportsVirtualMatrix() && cud.GetMaxVirtualMatrixPort())) {
- if (IsDrive()) {
- GetPathAsJSON(ipAddress +GetPathSeparator() + "config" +GetPathSeparator() + "co-other.json", origJson);
- } else {
- GetURLAsJSON("/api/channel/output/co-other", origJson, false);
- }
+ GetURLAsJSON("/api/channel/output/co-other", origJson, false);
if (fullcontrol) {
for (int x = 0; x < origJson["channelOutputs"].Size(); x++) {
if (origJson["channelOutputs"][x]["type"].AsString() == "VirtualMatrix") {
@@ -2387,12 +2297,8 @@ bool FPP::UploadVirtualMatrixOutputs(ModelManager* allmodels,
}
}
if (changed) {
- if (IsDrive()) {
- WriteJSONToPath(ipAddress +GetPathSeparator() + "config" +GetPathSeparator() + "co-other.json", origJson);
- } else {
- PostJSONToURL("/api/channel/output/co-other", origJson);
- SetRestartFlag();
- }
+ PostJSONToURL("/api/channel/output/co-other", origJson);
+ SetRestartFlag();
}
return false;
@@ -2487,21 +2393,12 @@ bool FPP::UploadSerialOutputs(ModelManager* allmodels,
wxJSONValue otherOrigRoot = otherData;
bool changed = true;
- if (IsDrive()) {
- GetPathAsJSON(ipAddress +GetPathSeparator() + "config" +GetPathSeparator() + "co-other.json", otherOrigRoot);
+ if (GetURLAsJSON("/api/configfile/co-other.json", otherOrigRoot, false)) {
changed = mergeSerialInto(otherData, otherOrigRoot, false);
- } else {
- if (GetURLAsJSON("/api/configfile/co-other.json", otherOrigRoot, false)) {
- changed = mergeSerialInto(otherData, otherOrigRoot, false);
- }
}
if (changed) {
- if (IsDrive()) {
- WriteJSONToPath(ipAddress +GetPathSeparator() + "config" +GetPathSeparator() + "co-other.json", otherOrigRoot);
- } else {
- PostJSONToURL("/api/configfile/co-other.json", otherOrigRoot);
- SetRestartFlag();
- }
+ PostJSONToURL("/api/configfile/co-other.json", otherOrigRoot);
+ SetRestartFlag();
SetNewRanges(rngs);
}
}
@@ -2547,11 +2444,7 @@ bool FPP::UploadPixelOutputs(ModelManager* allmodels,
fnOrig.AssignTempFileName("pixelOutputs");
std::string file = fnOrig.GetFullPath().ToStdString();
wxJSONValue origJson;
- if (IsDrive()) {
- GetPathAsJSON(ipAddress +GetPathSeparator() + "config" +GetPathSeparator() + fppFileName +".json", origJson);
- } else {
- GetURLAsJSON("/api/channel/output/" + fppFileName, origJson, false);
- }
+ GetURLAsJSON("/api/channel/output/" + fppFileName, origJson, false);
logger_base.debug("Original JSON");
DumpJSON(origJson);
@@ -2641,13 +2534,19 @@ bool FPP::UploadPixelOutputs(ModelManager* allmodels,
port->CreateVirtualStrings(false);
for (const auto& pvs : port->GetVirtualStrings()) {
wxJSONValue vs;
- vs["description"] = pvs->_description;
- vs["startChannel"] = pvs->_startChannel - 1; // we need 0 based
- vs["pixelCount"] = pvs->Channels() / pvs->_channelsPerPixel;
+ if (pvs->_isDummy) {
+ vs["description"] = wxString("");
+ vs["startChannel"] = 0;
+ vs["pixelCount"] = 0;
+ } else {
+ vs["description"] = pvs->_description;
+ vs["startChannel"] = pvs->_startChannel - 1; // we need 0 based
+ vs["pixelCount"] = pvs->Channels() / pvs->_channelsPerPixel;
- rngs[pvs->_startChannel - 1] = pvs->Channels();
+ rngs[pvs->_startChannel - 1] = pvs->Channels();
+ }
- if (origStrings.find(vs["description"].AsString()) != origStrings.end()) {
+ if (!pvs->_isDummy && (origStrings.find(vs["description"].AsString()) != origStrings.end())) {
wxJSONValue &vo = origStrings[vs["description"].AsString()];
vs["groupCount"] = vo["groupCount"];
vs["reverse"] = vo["reverse"];
@@ -2908,21 +2807,12 @@ bool FPP::UploadPixelOutputs(ModelManager* allmodels,
} else {
wxJSONValue otherOrigRoot = otherDmxData;
bool changed = true;
- if (IsDrive()) {
- GetPathAsJSON(ipAddress +GetPathSeparator() + "config" +GetPathSeparator() + "co-other.json", otherOrigRoot);
+ if (GetURLAsJSON("/api/configfile/co-other.json", otherOrigRoot, false)) {
changed = mergeSerialInto(otherDmxData, otherOrigRoot, true);
- } else {
- if (GetURLAsJSON("/api/configfile/co-other.json", otherOrigRoot, false)) {
- changed = mergeSerialInto(otherDmxData, otherOrigRoot, true);
- }
}
if (changed) {
- if (IsDrive()) {
- WriteJSONToPath(ipAddress +GetPathSeparator() + "config" +GetPathSeparator() + "co-other.json", otherOrigRoot);
- } else {
- PostJSONToURL("/api/configfile/co-other.json", otherOrigRoot);
- SetRestartFlag();
- }
+ PostJSONToURL("/api/configfile/co-other.json", otherOrigRoot);
+ SetRestartFlag();
}
}
@@ -2939,18 +2829,39 @@ bool FPP::UploadPixelOutputs(ModelManager* allmodels,
writer.Write(root, ufile);
ufile.Close();
- if (IsDrive()) {
- WriteJSONToPath(ipAddress +GetPathSeparator() + "config" +GetPathSeparator() + fppFileName +".json", root);
- } else {
- PostJSONToURL("/api/channel/output/" + fppFileName, root);
- SetRestartFlag();
- }
+ PostJSONToURL("/api/channel/output/" + fppFileName, root);
+ SetRestartFlag();
} else {
logger_base.debug("Skipping JSON upload as it has not changed.");
}
SetNewRanges(rngs);
return false;
}
+
+bool FPP::UploadControllerProxies(OutputManager* outputManager)
+{
+ auto currentProxies = GetProxies();
+ std::vector newProxies;
+
+ for (const auto& it : outputManager->GetControllers()) {
+ auto c = dynamic_cast(it);
+ if (c != nullptr) {
+ std::string proxy_ip = ip_utils::ResolveIP(c->GetFPPProxy());
+ if (ipAddress.compare(proxy_ip) == 0) {
+ auto controllerip = c->GetIP();
+ if (std::find(currentProxies.begin(), currentProxies.end(), controllerip) == currentProxies.end()) {
+ newProxies.push_back(controllerip);
+ currentProxies.push_back(controllerip);
+ }
+ }
+ }
+ }
+
+ for (const auto& nprox : newProxies) {
+ PostToURL("/api/proxies/" + nprox, "", "text/plain");
+ }
+ return false;
+}
#endif
#define FPP_CTRL_PORT 32320
@@ -2965,7 +2876,7 @@ static void setRangesToChannelCount(DiscoveredData *inst) {
int min = 9999999; int max = 0;
if (inst->ranges != "") {
wxArrayString r1 = wxSplit(wxString(inst->ranges), ',');
- for (auto a : r1) {
+ for (auto const& a : r1) {
wxArrayString r = wxSplit(a, '-');
int start = wxAtoi(r[0]);
int len = 4; //at least 4
@@ -3687,8 +3598,14 @@ static bool supportedForFPPConnect(DiscoveredData* res, OutputManager* outputMan
return res->majorVersion >= 4 && res->mode == "remote";
}
- if (res->typeId == 0x88 || res->typeId == 0x89) {
- // F16V4 / F48V4
+ if (res->typeId == 0x88 || res->typeId == 0x89 ||
+ res->typeId == 0x90 || res->typeId == 0x91) {
+ // F16V4 / F48V4 / F16V5 / F48V5
+ return res->mode != "bridge";
+ }
+
+ if (res->typeId >= 0xA0 && res->typeId <= 0xAF) {
+ // Genius
return res->mode != "bridge";
}
@@ -3787,10 +3704,13 @@ void FPP::MapToFPPInstances(Discovery &discovery, std::list &instances, Ou
void FPP::TypeIDtoControllerType(int typeId, FPP* inst) {
if (typeId < 0x80) {
inst->fppType = FPP_TYPE::FPP;
- } else if (typeId == 0x88 || typeId == 0x89) {
- inst->fppType = FPP_TYPE::FALCONV4;
+ } else if (typeId == 0x88 || typeId == 0x89 ||
+ typeId == 0x90 || typeId == 0x91) {
+ inst->fppType = FPP_TYPE::FALCONV4V5;
} else if (typeId == 0xC2 || typeId == 0xC3) {
inst->fppType = FPP_TYPE::ESPIXELSTICK;
+ } else if (typeId >= 0xA0 && typeId <= 0xAF) {
+ inst->fppType = FPP_TYPE::GENIUS;
}
}
diff --git a/xLights/controllers/FPP.h b/xLights/controllers/FPP.h
index 69d3a6ccea..83ac9c0bd9 100644
--- a/xLights/controllers/FPP.h
+++ b/xLights/controllers/FPP.h
@@ -19,8 +19,9 @@ class FPPUploadProgressDialog;
class Discovery;
enum class FPP_TYPE { FPP,
- FALCONV4,
- ESPIXELSTICK };
+ FALCONV4V5,
+ ESPIXELSTICK,
+ GENIUS };
class FPP : public BaseController
{
@@ -77,13 +78,13 @@ class FPP : public BaseController
bool IsDDPInputEnabled();
bool IsVersionAtLeast(uint32_t maj, uint32_t min, uint32_t patch = 0) const;
- bool IsDrive();
#ifndef DISCOVERYONLY
bool PrepareUploadSequence(FSEQFile *file,
const std::string &seq,
const std::string &media,
int type);
+ bool CheckUploadMedia(const std::string &media, std::string &mediaBaseName);
bool WillUploadSequence() const;
bool NeedCustomSequence() const;
bool AddFrameToUpload(uint32_t frame, uint8_t *data);
@@ -114,6 +115,8 @@ class FPP : public BaseController
Controller* controller);
bool SetInputUniversesBridge(Controller* controller);
+ bool UploadControllerProxies(OutputManager* outputManager);
+
bool SetRestartFlag();
bool Restart(bool ifNeeded = false);
void SetDescription(const std::string &st);
@@ -148,15 +151,12 @@ class FPP : public BaseController
private:
FPPUploadProgressDialog *progressDialog = nullptr;
wxGauge *progress = nullptr;
-
void DumpJSON(const wxJSONValue& json);
- bool GetPathAsJSON(const std::string &path, wxJSONValue &val);
bool GetURLAsJSON(const std::string& url, wxJSONValue& val, bool recordError = true);
bool GetURLAsString(const std::string& url, std::string& val, bool recordError = true);
- bool WriteJSONToPath(const std::string& path, const wxJSONValue& val);
int PostJSONToURL(const std::string& url, const wxJSONValue& val);
int PostJSONToURLAsFormData(const std::string& url, const std::string &extra, const wxJSONValue& val);
int PostToURL(const std::string& url, const std::string &val, const std::string &contentType = "application/octet-stream");
@@ -174,9 +174,6 @@ class FPP : public BaseController
bool uploadFileV7(const std::string &filename,
const std::string &file,
const std::string &dir);
- bool copyFile(const std::string &filename,
- const std::string &file,
- const std::string &dir);
bool callMoveFile(const std::string &filename);
bool parseSysInfo(wxJSONValue& v);
diff --git a/xLights/controllers/FPPConnectDialog.cpp b/xLights/controllers/FPPConnectDialog.cpp
index 16f20f85bf..7a46b861e3 100644
--- a/xLights/controllers/FPPConnectDialog.cpp
+++ b/xLights/controllers/FPPConnectDialog.cpp
@@ -33,6 +33,7 @@
#include "../Parallel.h"
#include "../Discovery.h"
#include "Falcon.h"
+#include "Experience.h"
//(*IdInit(FPPConnectDialog)
const long FPPConnectDialog::ID_SCROLLEDWINDOW1 = wxNewId();
@@ -75,6 +76,7 @@ static const std::string FSEQ_COL = "ID_FSEQTYPE_";
static const std::string MEDIA_COL = "ID_MEDIA_";
static const std::string MODELS_COL = "ID_MODELS_";
static const std::string UDP_COL = "ID_UDPOUT_";
+static const std::string PROXY_COL = "ID_PROXY_";
static const std::string PLAYLIST_COL = "ID_PLAYLIST_";
static const std::string UPLOAD_CONTROLLER_COL = "ID_CONTROLLER_";
@@ -96,12 +98,14 @@ FPPConnectDialog::FPPConnectDialog(wxWindow* parent, OutputManager* outputManage
FlexGridSizer1->AddGrowableCol(0);
FlexGridSizer1->AddGrowableRow(0);
SplitterWindow1 = new wxSplitterWindow(this, ID_SPLITTERWINDOW1, wxDefaultPosition, wxDefaultSize, wxSP_3D|wxSP_3DSASH, _T("ID_SPLITTERWINDOW1"));
- SplitterWindow1->SetMinimumPaneSize(100);
+ SplitterWindow1->SetMinSize(wxSize(100,100));
SplitterWindow1->SetSashGravity(0.5);
FPPInstanceList = new wxScrolledWindow(SplitterWindow1, ID_SCROLLEDWINDOW1, wxDefaultPosition, wxDefaultSize, wxVSCROLL|wxHSCROLL, _T("ID_SCROLLEDWINDOW1"));
FPPInstanceList->SetMinSize(wxDLG_UNIT(SplitterWindow1,wxSize(800,100)));
- FPPInstanceSizer = new wxFlexGridSizer(0, 11, 0, 0);
+ FPPInstanceSizer = new wxFlexGridSizer(0, 12, 0, 0);
FPPInstanceList->SetSizer(FPPInstanceSizer);
+ FPPInstanceSizer->Fit(FPPInstanceList);
+ FPPInstanceSizer->SetSizeHints(FPPInstanceList);
Panel1 = new wxPanel(SplitterWindow1, ID_PANEL1, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL, _T("ID_PANEL1"));
FlexGridSizer2 = new wxFlexGridSizer(2, 1, 0, 0);
FlexGridSizer2->AddGrowableCol(0);
@@ -125,6 +129,8 @@ FPPConnectDialog::FPPConnectDialog(wxWindow* parent, OutputManager* outputManage
CheckListBoxHolder->SetMinSize(wxSize(-1,100));
FlexGridSizer2->Add(CheckListBoxHolder, 1, wxALL|wxEXPAND, 0);
Panel1->SetSizer(FlexGridSizer2);
+ FlexGridSizer2->Fit(Panel1);
+ FlexGridSizer2->SetSizeHints(Panel1);
SplitterWindow1->SplitHorizontally(FPPInstanceList, Panel1);
FlexGridSizer1->Add(SplitterWindow1, 1, wxALL|wxEXPAND, 5);
FlexGridSizer4 = new wxFlexGridSizer(0, 4, 0, 0);
@@ -140,7 +146,8 @@ FPPConnectDialog::FPPConnectDialog(wxWindow* parent, OutputManager* outputManage
FlexGridSizer4->Add(cancelButton, 1, wxALL|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5);
FlexGridSizer1->Add(FlexGridSizer4, 1, wxALL|wxEXPAND, 5);
SetSizer(FlexGridSizer1);
- Fit();
+ FlexGridSizer1->Fit(this);
+ FlexGridSizer1->SetSizeHints(this);
Connect(ID_CHOICE_FILTER,wxEVT_COMMAND_CHOICE_SELECTED,(wxObjectEventFunction)&FPPConnectDialog::OnChoiceFilterSelect);
Connect(ID_CHOICE_FOLDER,wxEVT_COMMAND_CHOICE_SELECTED,(wxObjectEventFunction)&FPPConnectDialog::OnChoiceFolderSelect);
@@ -185,9 +192,6 @@ FPPConnectDialog::FPPConnectDialog(wxWindow* parent, OutputManager* outputManage
instances = FPP::GetInstances(this, outputManager);
- prgs.Pulse("Checking for mounted media drives");
- CreateDriveList();
-
AddInstanceHeader("Upload", "Enable to Upload Files/Configs to this FPP Device.");
wxPanel *p = AddInstanceHeader("Location", "Host and IP Address.");
p->Connect(wxEVT_CONTEXT_MENU, (wxObjectEventFunction)& FPPConnectDialog::LocationPopupMenu, nullptr, this);
@@ -199,6 +203,7 @@ FPPConnectDialog::FPPConnectDialog(wxWindow* parent, OutputManager* outputManage
AddInstanceHeader("Media", "Enable to Upload MP3, MP4 and WAV Media Files.");
AddInstanceHeader("Models", "Enable to Upload Models for Display Testing.");
AddInstanceHeader("UDP Out", "'None'- Device is not going to send Pixel data across the network. \n \n 'All' This will send pixel data over your Show Network from FPP instance to all controllers marked as 'ACTIVE'. \n \n 'Proxied' will set UDP Out only for Controllers with a Proxy IP address set.");
+ AddInstanceHeader("Add Proxies", "Upload Proxy IP Adresses to FPP.");
AddInstanceHeader("Playlist","Select Playlist to Add Uploaded Sequences Too.");
AddInstanceHeader("Pixel Hat/Cape", "Display Hat or Hat Attached to FPP Device, If Found.");
@@ -306,8 +311,8 @@ void FPPConnectDialog::PopulateFPPInstanceList(wxProgressDialog *prgs) {
w->Destroy();
w = tmp;
}
- while (FPPInstanceSizer->GetItemCount () > 11) {
- FPPInstanceSizer->Remove(11);
+ while (FPPInstanceSizer->GetItemCount () > 12) {
+ FPPInstanceSizer->Remove(12);
}
int row = 0;
@@ -348,13 +353,13 @@ void FPPConnectDialog::PopulateFPPInstanceList(wxProgressDialog *prgs) {
font.SetPointSize(font.GetPointSize() - 2);
Choice1->SetFont(font);
Choice1->Append(_("V1"));
- Choice1->Append(_("V2"));
+ Choice1->Append(_("V2 zstd"));
Choice1->Append(_("V2 Sparse/zstd"));
Choice1->Append(_("V2 Sparse/Uncompressed"));
Choice1->SetSelection(inst->mode == "master" ? 1 : 2);
FPPInstanceSizer->Add(Choice1, 1, wxALL|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 0);
}
- } else if (inst->fppType == FPP_TYPE::FALCONV4) {
+ } else if (inst->fppType == FPP_TYPE::FALCONV4V5) {
wxChoice* Choice1 = new wxChoice(FPPInstanceList, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0, 0, 0, wxDefaultValidator, FSEQ_COL + rowStr);
wxFont font = Choice1->GetFont();
font.SetPointSize(font.GetPointSize() - 2);
@@ -366,9 +371,9 @@ void FPPConnectDialog::PopulateFPPInstanceList(wxProgressDialog *prgs) {
Choice1->Append(_("V2 Uncompressed"));
Choice1->SetSelection(2);
FPPInstanceSizer->Add(Choice1, 1, wxALL | wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_VERTICAL, 0);
- } else if (inst->fppType == FPP_TYPE::ESPIXELSTICK) {
+ } else if (inst->fppType == FPP_TYPE::ESPIXELSTICK || inst->fppType == FPP_TYPE::GENIUS) {
label = new wxStaticText(FPPInstanceList, wxID_ANY, "V2 Sparse/Uncompressed", wxDefaultPosition, wxDefaultSize, 0, _T("ID_STATIC_TEXT_FS_" + rowStr));
- FPPInstanceSizer->Add(label, 1, wxALL|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 1);
+ FPPInstanceSizer->Add(label, 1, wxALL | wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_VERTICAL, 1);
} else {
label = new wxStaticText(FPPInstanceList, wxID_ANY, "V1", wxDefaultPosition, wxDefaultSize, 0, _T("ID_STATIC_TEXT_FS_" + rowStr));
FPPInstanceSizer->Add(label, 1, wxALL|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 1);
@@ -402,20 +407,21 @@ void FPPConnectDialog::PopulateFPPInstanceList(wxProgressDialog *prgs) {
Choice1->Append(_("Proxied"));
Choice1->SetSelection(0);
FPPInstanceSizer->Add(Choice1, 1, wxALL|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 1);
+
+ wxCheckBox* CheckBoxProxy = new wxCheckBox(FPPInstanceList, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator, PROXY_COL + rowStr);
+ FPPInstanceSizer->Add(CheckBoxProxy, 1, wxALL | wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_VERTICAL, 1);
//playlist combo box
- if (!inst->IsDrive()) {
- wxComboBox *ComboBox1 = new wxComboBox(FPPInstanceList, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0, 0, wxTE_PROCESS_ENTER, wxDefaultValidator, PLAYLIST_COL + rowStr);
- ComboBox1->Append(_(""));
- for (const auto& pl : inst->playlists) {
- ComboBox1->Append(pl);
- }
- wxFont font = ComboBox1->GetFont();
- font.SetPointSize(font.GetPointSize() - 2);
- ComboBox1->SetFont(font);
- FPPInstanceSizer->Add(ComboBox1, 1, wxALL|wxEXPAND, 0);
+ wxComboBox *ComboBox1 = new wxComboBox(FPPInstanceList, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0, 0, wxTE_PROCESS_ENTER, wxDefaultValidator, PLAYLIST_COL + rowStr);
+ ComboBox1->Append(_(""));
+ for (const auto& pl : inst->playlists) {
+ ComboBox1->Append(pl);
}
- } else if (inst->fppType == FPP_TYPE::FALCONV4) {
+ font = ComboBox1->GetFont();
+ font.SetPointSize(font.GetPointSize() - 2);
+ ComboBox1->SetFont(font);
+ FPPInstanceSizer->Add(ComboBox1, 1, wxALL|wxEXPAND, 0);
+ } else if (inst->fppType == FPP_TYPE::FALCONV4V5) {
// this probably needs to be moved as this is not really a zlib thing but only the falcons end up here today so I am going to put it here for now
wxCheckBox *CheckBox1 = new wxCheckBox(FPPInstanceList, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator, MEDIA_COL + rowStr);
CheckBox1->SetValue(inst->mode != "remote");
@@ -424,11 +430,13 @@ void FPPConnectDialog::PopulateFPPInstanceList(wxProgressDialog *prgs) {
FPPInstanceSizer->Add(0, 0, 1, wxALL | wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_VERTICAL, 1);
FPPInstanceSizer->Add(0, 0, 1, wxALL | wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_VERTICAL, 1);
FPPInstanceSizer->Add(0, 0, 1, wxALL | wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_VERTICAL, 1);
+ FPPInstanceSizer->Add(0, 0, 1, wxALL | wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_VERTICAL, 1);
} else {
FPPInstanceSizer->Add(0,0,1, wxALL|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 1);
FPPInstanceSizer->Add(0,0,1, wxALL|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 1);
FPPInstanceSizer->Add(0,0,1, wxALL|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 1);
FPPInstanceSizer->Add(0,0,1, wxALL|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 1);
+ FPPInstanceSizer->Add(0, 0, 1, wxALL | wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_VERTICAL, 1);
}
auto c = _outputManager->GetControllers(inst->ipAddress);
@@ -452,7 +460,7 @@ void FPPConnectDialog::PopulateFPPInstanceList(wxProgressDialog *prgs) {
FPPInstanceSizer->Add(0,0,1, wxALL|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 1);
}
- row++;
+ ++row;
}
ApplySavedHostSettings();
@@ -576,7 +584,7 @@ void FPPConnectDialog::LoadSequencesFromFolder(wxString dir) const
case SP_XmlPullEvent::eStartTag: {
SP_XmlStartTagEvent * stagEvent = (SP_XmlStartTagEvent*)event;
wxString NodeName = wxString::FromAscii(stagEvent->getName());
- count++;
+ ++count;
if (NodeName == "xsequence") {
isSequence = true;
} else if (NodeName == "mediaFile") {
@@ -800,7 +808,7 @@ void FPPConnectDialog::OnButton_UploadClick(wxCommandEvent& event)
for (row = 0; row < doUpload.size(); ++row) {
std::string rowStr = std::to_string(row);
doUpload[row] = GetCheckValue(CHECK_COL + rowStr);
- uploadCount++;
+ ++uploadCount;
}
FPPUploadProgressDialog prgs(this);
@@ -817,7 +825,7 @@ void FPPConnectDialog::OnButton_UploadClick(wxCommandEvent& event)
} else {
inst->setProgress(nullptr, nullptr);
}
- row++;
+ ++row;
}
if (uploadCount) {
//prgs.SetSize(450, 400);
@@ -865,6 +873,9 @@ void FPPConnectDialog::doUpload(FPPUploadProgressDialog *prgs, std::vector
if (playlist != "") {
cancelled |= inst->UploadPlaylist(playlist);
}
+ if (GetCheckValue(PROXY_COL + rowStr)) {
+ cancelled |= inst->UploadControllerProxies(_outputManager);
+ }
if (GetChoiceValueIndex(UDP_COL + rowStr) == 1) {
cancelled |= inst->UploadUDPOut(outputs);
//add the UDP ranges into the list of ranges
@@ -877,14 +888,15 @@ void FPPConnectDialog::doUpload(FPPUploadProgressDialog *prgs, std::vector
inst->SetRestartFlag();
}
if (GetCheckValue(UPLOAD_CONTROLLER_COL + rowStr)) {
- auto vendor = FPP::GetVendor(inst->pixelControllerType);
- auto model = FPP::GetModel(inst->pixelControllerType);
+ //auto vendor = FPP::GetVendor(inst->pixelControllerType);
+ //auto model = FPP::GetModel(inst->pixelControllerType);
//auto caps = ControllerCaps::GetControllerConfig(vendor, model, "");
auto c = _outputManager->GetControllers(inst->ipAddress);
if (c.size() == 1) {
cancelled |= inst->UploadPanelOutputs(&frame->AllModels, _outputManager, c.front());
cancelled |= inst->UploadVirtualMatrixOutputs(&frame->AllModels, _outputManager, c.front());
cancelled |= inst->UploadPixelOutputs(&frame->AllModels, _outputManager, c.front());
+ cancelled |= inst->UploadSerialOutputs(&frame->AllModels, _outputManager, c.front());
}
}
if (GetChoiceValueIndex(MODELS_COL + rowStr) == 1) {
@@ -909,7 +921,7 @@ void FPPConnectDialog::doUpload(FPPUploadProgressDialog *prgs, std::vector
delete bc;
}
}
- row++;
+ ++row;
}
row = 0;
for (const auto& inst : instances) {
@@ -917,7 +929,7 @@ void FPPConnectDialog::doUpload(FPPUploadProgressDialog *prgs, std::vector
// update the channel ranges now that the config has been uploaded an fppd restarted
inst->UpdateChannelRanges();
}
- row++;
+ ++row;
}
row = 0;
wxTreeListItem item = CheckListBox_Sequences->GetFirstItem();
@@ -948,19 +960,26 @@ void FPPConnectDialog::doUpload(FPPUploadProgressDialog *prgs, std::vector
int fseqType = 0;
if (inst->fppType == FPP_TYPE::FPP) {
fseqType = GetChoiceValueIndex(FSEQ_COL + rowStr);
- } else if (inst->fppType == FPP_TYPE::FALCONV4) {
+ } else if (inst->fppType == FPP_TYPE::FALCONV4V5) {
fseqType = GetChoiceValueIndex(FSEQ_COL + rowStr);
// need to adjust so they are unique
if (fseqType == 1) fseqType = 5;
if (fseqType == 2) fseqType = 6;
- }
- else {
+ } else {
fseqType = 3;
}
cancelled |= inst->PrepareUploadSequence(seq,
fseq, m2,
fseqType);
-
+ }
+ row++;
+ }
+ while (CurlManager::INSTANCE.processCurls()) {
+ wxYield();
+ }
+ row = 0;
+ for (const auto& inst : instances) {
+ if (!cancelled && doUpload[row]) {
if (inst->WillUploadSequence()) {
uploadCount++;
if (inst->NeedCustomSequence()) {
@@ -1032,7 +1051,7 @@ void FPPConnectDialog::doUpload(FPPUploadProgressDialog *prgs, std::vector
if (!cancelled && doUpload[row]) {
cancelled |= inst->FinalizeUploadSequence();
- if (inst->fppType == FPP_TYPE::FALCONV4) {
+ if (inst->fppType == FPP_TYPE::FALCONV4V5) {
// a falcon
std::string proxy = "";
auto c = _outputManager->GetControllers(inst->ipAddress);
@@ -1061,6 +1080,26 @@ void FPPConnectDialog::doUpload(FPPUploadProgressDialog *prgs, std::vector
cancelled = true;
}
inst->ClearTempFile();
+ } else if (inst->fppType == FPP_TYPE::GENIUS) {
+ // a Genius
+ std::string proxy;
+ auto c = _outputManager->GetControllers(inst->ipAddress);
+ if (c.size() == 1) {
+ proxy = c.front()->GetFPPProxy();
+ }
+ Experience genius(inst->ipAddress, proxy);
+ if (genius.IsConnected()) {
+ std::function updateProg = [&prgs, inst](int val, std::string msg) {
+ prgs->setActionLabel(msg);
+ inst->updateProgress(val, true);
+ return true;
+ };
+ cancelled |= !genius.UploadSequence(inst->GetTempFile(), fseq, updateProg);
+ } else {
+ logger_base.debug("Upload failed as Genius is not connected.");
+ cancelled = true;
+ }
+ inst->ClearTempFile();
}
}
row++;
@@ -1105,125 +1144,21 @@ void FPPConnectDialog::doUpload(FPPUploadProgressDialog *prgs, std::vector
}
row++;
}
+ xLightsFrame* xlframe = static_cast(GetParent());
if (messages != "") {
+ xlframe->SetStatusText("FPP Connect Upload had errors or warnings", 0);
wxMessageBox(messages, "Problems Uploading", wxOK | wxCENTRE, this);
- }
- prgs->EndModal(cancelled ? 1 : 0);
-}
-
-void FPPConnectDialog::CreateDriveList()
-{
- wxArrayString drives;
-#ifdef __WXMSW__
- static log4cpp::Category& logger_base = log4cpp::Category::getInstance(std::string("log_base"));
- wxArrayString ud = wxFSVolume::GetVolumes(wxFS_VOL_REMOVABLE | wxFS_VOL_MOUNTED, 0);
- for (const auto &a : ud) {
- if (wxDir::Exists(a + "\\sequences")) {
- drives.push_back(a);
- logger_base.info("FPP Connect found drive %s with a \\sequences folder.", (const char*)a.c_str());
- }
- }
-#elif defined(__WXOSX__)
- wxDir d;
- d.Open("/Volumes");
- wxString dir;
- bool fcont = d.GetFirst(&dir, wxEmptyString, wxDIR_DIRS);
- while (fcont) {
- if ((dir[0] != '.')
- && (dir != "Recovery")
- && (dir != "Macintosh HD")
- && wxDir::Exists("/Volumes/" + dir + "/sequences")) { //raw USB drive mounted
-
- drives.push_back("/Volumes/" + dir + "/");
- }
- fcont = d.GetNext(&dir);
- }
-#else
- bool done = false;
- wxDir d;
- d.Open("/media");
- wxString dir;
- bool fcont = d.GetFirst(&dir, wxEmptyString, wxDIR_DIRS);
- while (fcont) {
- wxDir d2;
- d2.Open("/media/" + dir);
- wxString dir2;
- bool fcont2 = d2.GetFirst(&dir2, wxEmptyString, wxDIR_DIRS);
- while (fcont2) {
- if (dir2 == "sequences") {
- drives.push_back("/media/" + dir + "/" + dir2);
- } else if (wxDir::Exists("/media/" + dir + "/" + dir2 + "/sequences")) {
- drives.push_back("/media/" + dir + "/" + dir2);
- }
- fcont2 = d2.GetNext(&dir2);
- }
- fcont = d.GetNext(&dir);
- }
-#endif
-
- for (const auto& a : drives) {
- FPP *inst = new FPP();
- inst->hostName = "FPP";
- inst->ipAddress = ToUTF8(a);
- inst->minorVersion = 0;
- inst->majorVersion = 2;
- inst->fullVersion = "Unknown";
- inst->mode = "standalone";
- if (FileExists(a + "/fpp-info.json")) {
- //read version and hostname
- wxJSONValue system;
- wxJSONReader reader;
- wxString str;
- wxString drive = a;
- if (!ObtainAccessToURL(ToUTF8(drive))) {
- wxDirDialog dlg(this, "Select FPP Directory", drive,
- wxDD_DEFAULT_STYLE | wxDD_DIR_MUST_EXIST);
- if (dlg.ShowModal() == wxID_OK) {
- drive = dlg.GetPath();
- }
- if (!ObtainAccessToURL(ToUTF8(drive))) {
- continue;
- }
- }
- wxFile file(drive + "/fpp-info.json");
- if (!file.IsOpened()) {
- //could not open the file, likely not readable/writable
- continue;
- }
-
- file.ReadAll(&str);
- reader.Parse(str, &system);
-
- if (!system["hostname"].IsNull()) {
- inst->hostName = ToUTF8(system["hostname"].AsString());
- }
- if (!system["type"].IsNull()) {
- inst->platform = ToUTF8(system["type"].AsString());
- }
- if (!system["model"].IsNull()) {
- inst->model = ToUTF8(system["model"].AsString());
- }
- if (!system["version"].IsNull()) {
- inst->fullVersion = ToUTF8(system["version"].AsString());
- }
- if (system["minorVersion"].IsInt()) {
- inst->minorVersion = system["minorVersion"].AsInt();
- }
- if (system["majorVersion"].IsInt()) {
- inst->majorVersion = system["majorVersion"].AsInt();
- }
- if (!system["channelRanges"].IsNull()) {
- inst->ranges = ToUTF8(system["channelRanges"].AsString());
- }
- if (!system["HostDescription"].IsNull()) {
- inst->description = ToUTF8(system["HostDescription"].AsString());
- }
- if (!system["fppModeString"].IsNull()) {
- inst->mode = ToUTF8(system["fppModeString"].AsString());
- }
+ logger_base.warn("FPP Connect Upload had errors or warnings:\n" + messages);
+ prgs->EndModal(2);
+ } else {
+ if (cancelled) {
+ xlframe->SetStatusText("FPP Connect Upload Cancelled", 0);
+ prgs->EndModal(1);
+ } else {
+ xlframe->SetStatusText("FPP Connect Upload Complete", 0);
+ prgs->EndModal(0);
}
- instances.push_back(inst);
- }
+ };
}
bool FPPConnectDialog::GetCheckValue(const std::string &col) {
@@ -1319,6 +1254,7 @@ void FPPConnectDialog::SaveSettings(bool onlyInsts)
config->Write("FPPConnectUploadModels_" + keyPostfx, GetChoiceValueIndex(MODELS_COL + rowStr));
config->Write("FPPConnectUploadUDPOut_" + keyPostfx, GetChoiceValueIndex(UDP_COL + rowStr));
config->Write("FPPConnectUploadPixelOut_" + keyPostfx, GetCheckValue(UPLOAD_CONTROLLER_COL + rowStr));
+ config->Write("FPPConnectUploadProxy_" + keyPostfx, GetCheckValue(PROXY_COL + rowStr));
row++;
}
config->Flush();
@@ -1368,6 +1304,9 @@ void FPPConnectDialog::ApplySavedHostSettings()
if (config->Read("FPPConnectUploadPixelOut_" + keyPostfx, &bval)) {
SetCheckValue(UPLOAD_CONTROLLER_COL + rowStr, bval);
}
+ if (config->Read("FPPConnectUploadProxy_" + keyPostfx, &bval)) {
+ SetCheckValue(PROXY_COL + rowStr, bval);
+ }
row++;
}
}
diff --git a/xLights/controllers/FPPConnectDialog.h b/xLights/controllers/FPPConnectDialog.h
index c0c77d6edb..5fcb397ef1 100644
--- a/xLights/controllers/FPPConnectDialog.h
+++ b/xLights/controllers/FPPConnectDialog.h
@@ -89,7 +89,6 @@ class FPPConnectDialog: public wxDialog
void OnLocationPopupClick(wxCommandEvent &evt);
//*)
- void CreateDriveList();
void LoadSequencesFromFolder(wxString dir) const;
void LoadSequences();
void PopulateFPPInstanceList(wxProgressDialog *prgs = nullptr);
diff --git a/xLights/controllers/Falcon.cpp b/xLights/controllers/Falcon.cpp
index d527a071ab..917e7777f3 100644
--- a/xLights/controllers/Falcon.cpp
+++ b/xLights/controllers/Falcon.cpp
@@ -957,7 +957,17 @@ void Falcon::V4_MakeStringsValid(Controller* controller, UDController& cud, std:
}
}
-bool Falcon::V4_PopulateStrings(std::vector& uploadStrings, const std::vector& falconStrings, UDController& cud, ControllerCaps* caps, int defaultBrightness, std::string& error, bool oneBased, uint32_t controllerFirstChannel)
+int Falcon::V4_GetBrightness(int port, int sr, int defaultBrightness, const std::vector& falconStrings)
+{
+ for (const auto& it : falconStrings)
+ {
+ if (it.port == port && it.smartRemote == sr)
+ return it.brightness;
+ }
+ return defaultBrightness;
+}
+
+bool Falcon::V4_PopulateStrings(std::vector& uploadStrings, const std::vector& falconStrings, UDController& cud, ControllerCaps* caps, int defaultBrightness, std::string& error, bool oneBased, uint32_t controllerFirstChannel, bool fullcontrol)
{
bool success = true;
@@ -1091,12 +1101,12 @@ bool Falcon::V4_PopulateStrings(std::vector& uploadStrings, co
str.name = SafeDescription(it->_description);
str.blank = false;
str.gamma = V4_ValidGamma(it->_gammaSet ? it->_gamma * 10 : gamma);
- str.brightness = V4_ValidBrightness(it->_brightnessSet ? it->_brightness : defaultBrightness);
+ str.brightness = V4_ValidBrightness(it->_brightnessSet ? it->_brightness : (fullcontrol ? defaultBrightness : V4_GetBrightness(p, sr, defaultBrightness, falconStrings)));
str.zigcount = 0;
str.endNulls = it->_endNullPixelsSet ? it->_endNullPixels : 0;
str.startNulls = it->_startNullPixelsSet ? it->_startNullPixels : 0;
str.colourOrder = it->_colourOrderSet ? V4_EncodeColourOrder(it->_colourOrder) : colourOrder;
- str.direction = it->_reverseSet ? (it->_reverse == "F" ? 0 : 1) : direction;
+ str.direction = it->_reverseSet ? (it->_reverse == "Forward" ? 0 : 1) : direction;
str.group = it->_groupCountSet ? it->_groupCount : group;
str.zigcount = it->_zigZagSet ? it->_zigZag : 0; // dont carry between props
str.pixels = INTROUNDUPDIV(it->Channels(), GetChannelsPerPixel(it->_protocol)) * str.group;
@@ -1123,7 +1133,7 @@ bool Falcon::V4_PopulateStrings(std::vector& uploadStrings, co
str.name = wxString::Format("Port %d", p + 1);
str.blank = false;
str.gamma = 10;
- str.brightness = defaultBrightness;
+ str.brightness = fullcontrol ? defaultBrightness : V4_GetBrightness(p, 0, defaultBrightness, falconStrings);
str.zigcount = 0;
str.endNulls = 0;
str.startNulls = 0;
@@ -1156,7 +1166,7 @@ bool Falcon::V4_PopulateStrings(std::vector& uploadStrings, co
str.name = wxString::Format("Port %d", p + 1);
str.blank = false;
str.gamma = 10;
- str.brightness = defaultBrightness;
+ str.brightness = fullcontrol ? defaultBrightness : V4_GetBrightness(p, sr, defaultBrightness, falconStrings);
str.zigcount = 0;
str.endNulls = 0;
str.startNulls = 0;
@@ -1180,7 +1190,7 @@ bool Falcon::V4_PopulateStrings(std::vector& uploadStrings, co
str.name = wxString::Format("Port %d", p + 1);
str.blank = false;
str.gamma = 10;
- str.brightness = defaultBrightness;
+ str.brightness = fullcontrol ? defaultBrightness : V4_GetBrightness(p, sr, defaultBrightness, falconStrings);
str.zigcount = 0;
str.endNulls = 0;
str.startNulls = 0;
@@ -1340,7 +1350,7 @@ bool Falcon::V4_SetOutputs(ModelManager* allmodels, OutputManager* outputManager
std::vector uploadStrings;
std::string error;
- if (!V4_PopulateStrings(uploadStrings, falconStrings, cud, caps, defaultBrightness, error, oneBased, controller->GetStartChannel())) {
+ if (!V4_PopulateStrings(uploadStrings, falconStrings, cud, caps, defaultBrightness, error, oneBased, controller->GetStartChannel(), fullcontrol)) {
DisplayError("Falcon Outputs Upload: Problem constructing strings for upload:\n" + error, parent);
if (doProgress) progress->Update(100, "Aborting.");
return false;
diff --git a/xLights/controllers/Falcon.h b/xLights/controllers/Falcon.h
index be10a39910..f9bede2eb2 100644
--- a/xLights/controllers/Falcon.h
+++ b/xLights/controllers/Falcon.h
@@ -101,7 +101,8 @@ class Falcon : public BaseController
bool V4_ValidateWAV(const std::string& media);
#ifndef DISCOVERYONLY
- bool V4_PopulateStrings(std::vector& uploadStrings, const std::vector& falconStrings, UDController& cud, ControllerCaps* caps, int defaultBrightness, std::string& error, bool oneBased, uint32_t firstControllerChannel);
+ int V4_GetBrightness(int port, int sr, int defaultBrightness, const std::vector& falconStrings);
+ bool V4_PopulateStrings(std::vector& uploadStrings, const std::vector& falconStrings, UDController& cud, ControllerCaps* caps, int defaultBrightness, std::string& error, bool oneBased, uint32_t firstControllerChannel, bool fullcontrol);
void V4_MakeStringsValid(Controller* controlle, UDController& cud, std::vector& falconStrings, int addressingMode);
#endif
diff --git a/xLights/controllers/HinksPix.cpp b/xLights/controllers/HinksPix.cpp
index dd738f35e2..c601eb0112 100644
--- a/xLights/controllers/HinksPix.cpp
+++ b/xLights/controllers/HinksPix.cpp
@@ -26,6 +26,13 @@
#include
+static size_t writeFunction(void* ptr, size_t size, size_t nmemb, std::string* data) {
+
+ if (data == nullptr) return 0;
+ data->append((char*)ptr, size * nmemb);
+ return size * nmemb;
+}
+
#pragma region HinksPixOutput
void HinksPixOutput::Dump() const {
static log4cpp::Category& logger_base = log4cpp::Category::getInstance(std::string("log_base"));
diff --git a/xLights/controllers/HinksPixExportDialog.cpp b/xLights/controllers/HinksPixExportDialog.cpp
index f776af1ff8..a4460a9422 100644
--- a/xLights/controllers/HinksPixExportDialog.cpp
+++ b/xLights/controllers/HinksPixExportDialog.cpp
@@ -196,6 +196,8 @@ HinksPixExportDialog::HinksPixExportDialog(wxWindow* parent, OutputManager* outp
HinkControllerList->SetMinSize(wxDLG_UNIT(SplitterWindow1,wxSize(-1,150)));
HinkControllerSizer = new wxFlexGridSizer(0, 8, 0, 0);
HinkControllerList->SetSizer(HinkControllerSizer);
+ HinkControllerSizer->Fit(HinkControllerList);
+ HinkControllerSizer->SetSizeHints(HinkControllerList);
Panel1 = new wxPanel(SplitterWindow1, ID_PANEL1, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL, _T("ID_PANEL1"));
FlexGridSizer2 = new wxFlexGridSizer(2, 2, 0, 0);
FlexGridSizer2->AddGrowableCol(1);
@@ -218,10 +220,12 @@ HinksPixExportDialog::HinksPixExportDialog(wxWindow* parent, OutputManager* outp
BoxSizer2->Add(BitmapButtonMoveUp, 1, wxALL|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxFIXED_MINSIZE, 5);
BitmapButtonMoveDown = new wxBitmapButton(Panel1, ID_BITMAPBUTTON_MOVE_DOWN, wxArtProvider::GetBitmapBundle("wxART_GO_DOWN", wxART_BUTTON), wxDefaultPosition, wxDefaultSize, wxBU_AUTODRAW, wxDefaultValidator, _T("ID_BITMAPBUTTON_MOVE_DOWN"));
BoxSizer2->Add(BitmapButtonMoveDown, 1, wxALL|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxFIXED_MINSIZE, 5);
- FlexGridSizer2->Add(BoxSizer2, 1, wxALL|wxALIGN_CENTER_VERTICAL, 0);
+ FlexGridSizer2->Add(BoxSizer2, 1, wxALL|wxEXPAND, 0);
CheckListBox_Sequences = new wxListView(Panel1, ID_LISTVIEW_Sequences, wxDefaultPosition, wxDefaultSize, wxLC_REPORT, wxDefaultValidator, _T("ID_LISTVIEW_Sequences"));
FlexGridSizer2->Add(CheckListBox_Sequences, 1, wxEXPAND, 0);
Panel1->SetSizer(FlexGridSizer2);
+ FlexGridSizer2->Fit(Panel1);
+ FlexGridSizer2->SetSizeHints(Panel1);
SplitterWindow1->SplitHorizontally(HinkControllerList, Panel1);
FlexGridSizer1->Add(SplitterWindow1, 1, wxALL|wxEXPAND, 5);
BoxSizer1 = new wxBoxSizer(wxHORIZONTAL);
@@ -231,22 +235,18 @@ HinksPixExportDialog::HinksPixExportDialog(wxWindow* parent, OutputManager* outp
BoxSizer1->Add(StaticText5, 0, wxALL|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5);
SpinCtrlStartHour = new wxSpinCtrl(this, ID_SPINCTRL_START_HOUR, _T("0"), wxDefaultPosition, wxDefaultSize, 0, 0, 23, 0, _T("ID_SPINCTRL_START_HOUR"));
SpinCtrlStartHour->SetValue(_T("0"));
- SpinCtrlStartHour->SetMinSize(wxSize(50,-1));
- BoxSizer1->Add(SpinCtrlStartHour, 0, wxALL|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5);
- SpinCtrlStartMin = new wxSpinCtrl(this, ID_SPINCTRL_START_MIN, _T("0"), wxDefaultPosition, wxDefaultSize, 0, 0, 59, 0, _T("ID_SPINCTRL_START_MIN"));
+ BoxSizer1->Add(SpinCtrlStartHour, 0, wxALL|wxEXPAND, 5);
+ SpinCtrlStartMin = new wxSpinCtrl(this, ID_SPINCTRL_START_MIN, _T("0"), wxDefaultPosition, wxSize(66,37), 0, 0, 59, 0, _T("ID_SPINCTRL_START_MIN"));
SpinCtrlStartMin->SetValue(_T("0"));
- SpinCtrlStartMin->SetMinSize(wxSize(50,-1));
- BoxSizer1->Add(SpinCtrlStartMin, 0, wxALL|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5);
+ BoxSizer1->Add(SpinCtrlStartMin, 0, wxALL|wxEXPAND, 5);
StaticText6 = new wxStaticText(this, ID_STATICTEXT6, _("End Time:"), wxDefaultPosition, wxDefaultSize, 0, _T("ID_STATICTEXT6"));
BoxSizer1->Add(StaticText6, 0, wxALL|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5);
SpinCtrlEndHour = new wxSpinCtrl(this, ID_SPINCTRL_END_HOUR, _T("23"), wxDefaultPosition, wxDefaultSize, 0, 0, 23, 23, _T("ID_SPINCTRL_END_HOUR"));
SpinCtrlEndHour->SetValue(_T("23"));
- SpinCtrlEndHour->SetMinSize(wxSize(50,-1));
- BoxSizer1->Add(SpinCtrlEndHour, 0, wxALL|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5);
+ BoxSizer1->Add(SpinCtrlEndHour, 0, wxALL|wxEXPAND, 5);
SpinCtrlEndMin = new wxSpinCtrl(this, ID_SPINCTRL_END_MIN, _T("59"), wxDefaultPosition, wxDefaultSize, 0, 0, 59, 59, _T("ID_SPINCTRL_END_MIN"));
SpinCtrlEndMin->SetValue(_T("59"));
- SpinCtrlEndMin->SetMinSize(wxSize(50,-1));
- BoxSizer1->Add(SpinCtrlEndMin, 0, wxALL|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5);
+ BoxSizer1->Add(SpinCtrlEndMin, 0, wxALL|wxEXPAND, 5);
BoxSizer1->Add(-1,-1,0, wxALL|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5);
Button_Export = new wxButton(this, ID_BUTTON_EXPORT, _("Export to SD Card"), wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator, _T("ID_BUTTON_EXPORT"));
BoxSizer1->Add(Button_Export, 0, wxALL|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5);
@@ -254,10 +254,9 @@ HinksPixExportDialog::HinksPixExportDialog(wxWindow* parent, OutputManager* outp
BoxSizer1->Add(cancelButton, 0, wxALL|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5);
FlexGridSizer1->Add(BoxSizer1, 1, wxALL|wxEXPAND, 5);
SetSizer(FlexGridSizer1);
+ FlexGridSizer1->Fit(this);
FlexGridSizer1->SetSizeHints(this);
- Connect(ID_CHOICE_FILTER,wxEVT_COMMAND_CHOICE_SELECTED,(wxObjectEventFunction)&HinksPixExportDialog::OnChoiceFilterSelect);
- Connect(ID_CHOICE_FOLDER,wxEVT_COMMAND_CHOICE_SELECTED,(wxObjectEventFunction)&HinksPixExportDialog::OnChoiceFolderSelect);
Connect(ID_BITMAPBUTTON_MOVE_UP,wxEVT_COMMAND_BUTTON_CLICKED,(wxObjectEventFunction)&HinksPixExportDialog::OnBitmapButtonMoveUpClick);
Connect(ID_BITMAPBUTTON_MOVE_DOWN,wxEVT_COMMAND_BUTTON_CLICKED,(wxObjectEventFunction)&HinksPixExportDialog::OnBitmapButtonMoveDownClick);
Connect(ID_LISTVIEW_Sequences,wxEVT_COMMAND_LIST_ITEM_RIGHT_CLICK,(wxObjectEventFunction)&HinksPixExportDialog::SequenceListPopup);
@@ -734,14 +733,6 @@ void HinksPixExportDialog::SequenceListPopup(wxListEvent& /*event*/) {
PopupMenu(&mnu);
}
-void HinksPixExportDialog::OnChoiceFolderSelect(wxCommandEvent& /*event*/) {
- LoadSequences();
-}
-
-void HinksPixExportDialog::OnChoiceFilterSelect(wxCommandEvent& /*event*/) {
- LoadSequences();
-}
-
void HinksPixExportDialog::GetFolderList(const wxString& folder) {
ChoiceFolder->Append("--Show Folder--");
wxArrayString subfolders;
@@ -833,7 +824,7 @@ void HinksPixExportDialog::OnButton_ExportClick(wxCommandEvent& /*event*/) {
}
wxString drive = GetChoiceValue(DISK_COL + rowStr);
-
+
//try to fix path
wxFileName dirname( drive, "" );
drive = dirname.GetPath();
@@ -939,6 +930,7 @@ void HinksPixExportDialog::OnBitmapButtonMoveUpClick(wxCommandEvent& /*event*/)
}
void HinksPixExportDialog::OnChoiceSelected(wxCommandEvent& event) {
+
wxString const text = event.GetString();
if (text.IsEmpty()) {
return;
@@ -948,6 +940,11 @@ void HinksPixExportDialog::OnChoiceSelected(wxCommandEvent& event) {
if (item) {
wxChoice* cb = dynamic_cast(item);
if (cb) {
+ if (cb == ChoiceFilter || cb == ChoiceFolder) {
+ LoadSequences();
+ return;
+ }
+
auto name = cb->GetName();
if (name.Contains(SLAVE1_COL) || name.Contains(SLAVE2_COL)) {
int row = 0;
@@ -1013,7 +1010,7 @@ void HinksPixExportDialog::createPlayList(std::vectorIsOk()) {
+ } else if (!discovery->IsOk()) {
logger_base.error("Error initialising PixLite/PixCon datagram ... is network connected? OK : FALSE");
delete discovery;
- }
- else if (discovery->Error()) {
+ } else if (discovery->Error()) {
logger_base.error("Error creating PixLite/PixCon socket => %d : %s.", discovery->LastError(), (const char*)DecodeIPError(discovery->LastError()).c_str());
delete discovery;
- }
- else {
+ } else {
discovery->SetTimeout(1);
discovery->Notify(false);
wxIPV4address remoteAddr;
- remoteAddr.Hostname(_ip);
+ remoteAddr.Hostname(ip);
remoteAddr.Service(PIXLITE_PORT);
uint8_t discoveryData[12];
Pixlite16::CreateDiscovery(discoveryData);
- logger_base.debug("Sending discovery to pixlite: %s:%d.", (const char*)_ip.c_str(), PIXLITE_PORT);
+ logger_base.debug("Sending discovery to pixlite: %s:%d.", (const char*)ip.c_str(), PIXLITE_PORT);
discovery->SendTo(remoteAddr, discoveryData, sizeof(discoveryData));
if (discovery->Error()) {
- logger_base.error("PixLite/PixCon error sending to %s => %d : %s.", (const char*)_ip.c_str(), discovery->LastError(), (const char*)DecodeIPError(discovery->LastError()).c_str());
- }
- else {
+ logger_base.error("PixLite/PixCon error sending to %s => %d : %s.", (const char*)ip.c_str(), discovery->LastError(), (const char*)DecodeIPError(discovery->LastError()).c_str());
+ } else {
uint32_t count = 0;
- #define SLP_TIME 100
+#define SLP_TIME 100
while (count < 5000 && !discovery->IsData()) {
wxMilliSleep(SLP_TIME);
count += SLP_TIME;
}
-
+
if (!discovery->IsData()) {
logger_base.warn("No discovery responses.");
}
@@ -1007,57 +1008,65 @@ bool Pixlite16::GetConfig()
discovery->RecvFrom(pixliteAddr, data, sizeof(data));
if (!discovery->Error() && data[10] == 0x02) {
- logger_base.debug(" Discover response from %s.", (const char *)pixliteAddr.IPAddress().c_str());
- bool connected = false;
- _config._protocolVersion = data[11];
- logger_base.debug(" Protocol version %d.", _config._protocolVersion);
- switch (_config._protocolVersion) {
- case 4:
- connected = ParseV4Config(data, _config);
- if (!connected) {
- logger_base.error(" Failed to parse v4 config packet.");
+ logger_base.debug(" Discover response from %s.", (const char*)pixliteAddr.IPAddress().c_str());
+
+ if (desiredip == pixliteAddr.IPAddress()) {
+ logger_base.debug(" This is the one we wanted to see.");
+
+ bool connected = false;
+ _config._protocolVersion = data[11];
+ logger_base.debug(" Protocol version %d.", _config._protocolVersion);
+ switch (_config._protocolVersion) {
+ case 4:
+ connected = ParseV4Config(data, _config);
+ if (!connected) {
+ logger_base.error(" Failed to parse v4 config packet.");
+ }
+ break;
+ case 5:
+ connected = ParseV5Config(data, _config);
+ if (!connected) {
+ logger_base.error(" Failed to parse v5 config packet.");
+ }
+ break;
+ case 6:
+ connected = ParseV6Config(data, _config);
+ if (!connected) {
+ logger_base.error(" Failed to parse v6 config packet.");
+ }
+ break;
+ case 8:
+ connected = ParseV8Config(data, _config);
+ if (!connected) {
+ logger_base.error(" Failed to parse v8 config packet.");
+ }
+ break;
+ default:
+ logger_base.error("Unsupported Pixlite protocol version: %d.", _config._protocolVersion);
+ wxASSERT(false);
+ break;
}
- break;
- case 5:
- connected = ParseV5Config(data, _config);
- if (!connected) {
- logger_base.error(" Failed to parse v5 config packet.");
- }
- break;
- case 6:
- connected = ParseV6Config(data, _config);
- if (!connected) {
- logger_base.error(" Failed to parse v6 config packet.");
- }
- break;
- case 8:
- connected = ParseV8Config(data, _config);
- if (!connected) {
- logger_base.error(" Failed to parse v8 config packet.");
- }
- break;
- default:
- logger_base.error("Unsupported Pixlite protocol version: %d.", _config._protocolVersion);
- wxASSERT(false);
- break;
- }
-
- if (connected) {
- wxString rcvIP = wxString::Format("%i.%i.%i.%i", _config._currentIP[0], _config._currentIP[1], _config._currentIP[2], _config._currentIP[3]);
- logger_base.debug("Found PixLite/PixCon controller on %s.", (const char*)rcvIP.c_str());
- logger_base.debug(" Model %s %.1f.", (const char*)_config._modelName.c_str(), (float)_config._hwRevision / 10.0);
- logger_base.debug(" Firmware %s.", (const char*)_config._firmwareVersion.c_str());
- logger_base.debug(" Nickname %s.", (const char*)_config._nickname.c_str());
- logger_base.debug(" Brand %d.", _config._brand);
- res = true;
- break;
+ if (connected) {
+ wxString rcvIP = wxString::Format("%i.%i.%i.%i", _config._currentIP[0], _config._currentIP[1], _config._currentIP[2], _config._currentIP[3]);
+
+ logger_base.debug("Found PixLite/PixCon controller on %s.", (const char*)rcvIP.c_str());
+ logger_base.debug(" Model %s %.1f.", (const char*)_config._modelName.c_str(), (float)_config._hwRevision / 10.0);
+ logger_base.debug(" Firmware %s.", (const char*)_config._firmwareVersion.c_str());
+ logger_base.debug(" Nickname %s.", (const char*)_config._nickname.c_str());
+ logger_base.debug(" Brand %d.", _config._brand);
+ res = true;
+ break;
+ } else {
+ logger_base.error("Unable to download PixLite/PixCon controller configuration from %s.", (const char*)ip.c_str());
+ }
}
- else {
- logger_base.error("Unable to download PixLite/PixCon controller configuration from %s.", (const char*)_ip.c_str());
+ if (!discovery->Error() && data[10] == 0x01) {
+ // ignore this ... this is the discovery we sent
+ } else {
+ logger_base.debug(" Not the controller we wanted to see.");
}
- }
- else if (discovery->Error()) {
+ } else if (discovery->Error()) {
logger_base.error("Error reading PixLite/PixCon response => %d : %s.", discovery->LastError(), (const char*)DecodeIPError(discovery->LastError()).c_str());
}
}
@@ -1069,6 +1078,36 @@ bool Pixlite16::GetConfig()
return res;
}
+bool Pixlite16::GetConfig()
+{
+ static log4cpp::Category& logger_base = log4cpp::Category::getInstance(std::string("log_base"));
+
+ bool res = false;
+ wxIPV4address localAddr;
+
+ if (_ip != "") {
+ localAddr.AnyAddress();
+ res = GetConfig(localAddr, _ip, _ip);
+ }
+
+ // if we had no luck broadcast to all adapters and see if we can find it
+ if (!res)
+ {
+ logger_base.warn("Trying broadcast to each adapter to see if we can find");
+
+ for (const auto& lip : GetLocalIPs())
+ {
+ if (!res) {
+ logger_base.warn(" Trying %s.", (const char*)lip.c_str());
+ localAddr.Hostname(lip);
+ res = GetConfig(localAddr, "255.255.255.255", _ip);
+ }
+ }
+ }
+
+ return res;
+}
+
// we populate what we can in the Config structure so we can be consistent with earlier versions
bool Pixlite16::GetMK3Config()
{
diff --git a/xLights/controllers/Pixlite16.h b/xLights/controllers/Pixlite16.h
index d468336c50..17954b1061 100644
--- a/xLights/controllers/Pixlite16.h
+++ b/xLights/controllers/Pixlite16.h
@@ -123,6 +123,7 @@ class Pixlite16 : public BaseController
static void CreateDiscovery(uint8_t* buffer);
bool GetConfig();
+ bool GetConfig(wxIPV4address localAddr, std::string ip, const std::string& desiredip);
bool SendConfig(bool logresult = false) const;
bool SendMk3Config(bool logresult = false) const;
diff --git a/xLights/controllers/SanDevices.h b/xLights/controllers/SanDevices.h
index 2349eec584..9318001b81 100644
--- a/xLights/controllers/SanDevices.h
+++ b/xLights/controllers/SanDevices.h
@@ -190,5 +190,6 @@ class SanDevices : public BaseController
virtual bool SetOutputs(ModelManager* allmodels, OutputManager* outputManager, Controller* controller, wxWindow* parent) override;
#endif
virtual bool UsesHTTP() const override { return true; }
+ virtual bool needsHTTP_0_9() const override { return true; }
#pragma endregion
};
diff --git a/xLights/controllers/WLED.cpp b/xLights/controllers/WLED.cpp
index 103ebcb767..139ebbac8e 100644
--- a/xLights/controllers/WLED.cpp
+++ b/xLights/controllers/WLED.cpp
@@ -228,6 +228,13 @@ void WLED::UpdatePixelOutputs(bool& worked, int totalPixelCount, wxJSONValue& js
jsonVal["hw"]["led"]["ins"] = newLEDS;
}
+static size_t writeFunction(void* ptr, size_t size, size_t nmemb, std::string* data) {
+
+ if (data == nullptr) return 0;
+ data->append((char*)ptr, size * nmemb);
+ return size * nmemb;
+}
+
bool WLED::PostJSON(wxJSONValue const& jsonVal) {
wxString str;
wxJSONWriter writer(wxJSONWRITER_NONE, 0, 3);
diff --git a/xLights/effects/AdjustEffect.cpp b/xLights/effects/AdjustEffect.cpp
new file mode 100644
index 0000000000..e6a6fd48e9
--- /dev/null
+++ b/xLights/effects/AdjustEffect.cpp
@@ -0,0 +1,176 @@
+/***************************************************************
+ * This source files comes from the xLights project
+ * https://www.xlights.org
+ * https://github.com/smeighan/xLights
+ * See the github commit history for a record of contributing
+ * developers.
+ * Copyright claimed based on commit dates recorded in Github
+ * License: https://github.com/smeighan/xLights/blob/master/License.txt
+ **************************************************************/
+
+#include "AdjustEffect.h"
+#include "AdjustPanel.h"
+#include "../sequencer/Effect.h"
+#include "../RenderBuffer.h"
+#include "../UtilClasses.h"
+#include "../models/Model.h"
+#include "../models/ModelGroup.h"
+#include "../xLightsApp.h"
+#include "../xLightsMain.h"
+#include "../TimingPanel.h"
+
+#include "../../include/adjust16.xpm"
+#include "../../include/adjust24.xpm"
+#include "../../include/adjust32.xpm"
+#include "../../include/adjust48.xpm"
+#include "../../include/adjust64.xpm"
+#include "UtilFunctions.h"
+
+AdjustEffect::AdjustEffect(int id) :
+ RenderableEffect(id, "Adjust", adjust16_xpm, adjust24_xpm, adjust32_xpm, adjust48_xpm, adjust64_xpm)
+{
+ //ctor
+}
+
+AdjustEffect::~AdjustEffect()
+{
+ //dtor
+}
+
+xlEffectPanel *AdjustEffect::CreatePanel(wxWindow *parent) {
+ return new AdjustPanel(parent);
+}
+
+std::list AdjustEffect::CheckEffectSettings(const SettingsMap& settings, AudioManager* media, Model* model, Effect* eff, bool renderCache)
+{
+ std::list res;
+
+ if (settings.Get("T_CHECKBOX_Canvas", "0") == "0") {
+ res.push_back(wxString::Format(" WARN: Canvas mode not enabled on a Adjust effect. Without canvas mode Adjust is unlikely to do anything useful. Effect: Adjust, Model: %s, Start %s", model->GetFullName(), FORMATTIME(eff->GetStartTimeMS())).ToStdString());
+ }
+
+ return res;
+}
+
+void AdjustEffect::SetDefaultParameters() {
+ AdjustPanel *ap = (AdjustPanel*)panel;
+ if (ap == nullptr) {
+ return;
+ }
+
+ SetChoiceValue(ap->Choice_Action, "None");
+ SetSpinValue(ap->SpinCtrl_Value1, 0);
+ SetSpinValue(ap->SpinCtrl_Value2, 0);
+ SetSpinValue(ap->SpinCtrl_NthChannel, 1);
+ SetSpinValue(ap->SpinCtrl_StartingAt, 1);
+
+ // Turn on canvas mode as this really only makes sense in canvas mode
+ xLightsFrame* frame = xLightsApp::GetFrame();
+ TimingPanel* layerBlendingPanel = frame->GetLayerBlendingPanel();
+ layerBlendingPanel->CheckBox_Canvas->SetValue(true);
+}
+
+void AdjustEffect::AdjustChannels(bool singleColour, int numChannels, RenderBuffer& buffer, const std::string& action, int value1, int value2, int nth, int starting, int count)
+{
+ int channels = std::min(numChannels, buffer.BufferWi * buffer.BufferHt * (singleColour ? 1 : 3));
+ int done = 0;
+
+ for (int i = starting - 1; (count == 0 || done < count) && i < channels; i = i + nth) {
+ ++done;
+ int value = 0;
+ xlColor c = xlBLACK;
+
+ // get the channel value
+ if (singleColour) {
+ c = buffer.GetPixel(i % buffer.BufferWi, i / buffer.BufferWi);
+ value = c.red;
+ } else {
+ c = buffer.GetPixel((i / 3) % buffer.BufferWi, (i / 3) / buffer.BufferWi);
+ if (i % 3 == 0)
+ value = c.red;
+ else if (i % 3 == 1)
+ value = c.green;
+ else
+ value = c.blue;
+ }
+ c.alpha = 255;
+
+ // adjust it
+ if (action == "None") {
+ // do nothing
+ } else if (action == "Adjust By Value") {
+ value += value1;
+ } else if (action == "Adjust By Percentage") {
+ value += (value * value1) / 100;
+ } else if (action == "Set Minimum") {
+ value = std::max(value1, value);
+ } else if (action == "Set Maximum") {
+ value = std::min(value1, value);
+ } else if (action == "Set Range") {
+ if (value1 > value2)
+ std::swap(value1, value2);
+ value = std::min(value2, std::max(value1, value));
+ } else if (action == "Shift With Wrap By Value") {
+ value += value1;
+ if (value < 0)
+ value += 256;
+ else if (value > 255)
+ value -= 256;
+ } else if (action == "Shift With Wrap By Percentage") {
+ } else if (action == "Prevent Range") {
+ if (value1 > value2)
+ std::swap(value1, value2);
+ if (value >= value1 && value <= value2) {
+ // within restricted range ... move it to the closest value
+ if (value - value1 < value2 - value)
+ value = value1;
+ else
+ value = value2;
+ }
+ } else if (action == "Reverse") {
+ value = 255 - value;
+ }
+
+ // force it in range
+ value = std::min(255, std::max(0, value));
+
+ // write the channel value
+ if (singleColour) {
+ c.red = value;
+ c.green = value;
+ c.blue = value;
+ buffer.SetPixel(i % buffer.BufferWi, i / buffer.BufferWi, c, false, true, true);
+ } else {
+ if (i % 3 == 0)
+ c.red = value;
+ else if (i % 3 == 1)
+ c.green = value;
+ else
+ c.blue = value;
+ buffer.SetPixel((i / 3) % buffer.BufferWi, (i / 3) / buffer.BufferWi, c);
+ }
+ }
+}
+
+void AdjustEffect::Render(Effect* effect, const SettingsMap& SettingsMap, RenderBuffer& buffer)
+{
+ int num_channels = 0;
+ std::string string_type = "";
+
+ Model* model_info = buffer.GetModel();
+ if (model_info == nullptr)
+ num_channels = buffer.BufferWi * buffer.BufferHt * 3;
+ else {
+ num_channels = model_info->GetNumChannels();
+ string_type = model_info->GetStringType();
+ }
+
+ auto action = SettingsMap.Get("CHOICE_Action", "None");
+ auto value1 = SettingsMap.GetInt("SPINCTRL_Value1", 0);
+ auto value2 = SettingsMap.GetInt("SPINCTRL_Value2", 0);
+ auto nth = SettingsMap.GetInt("SPINCTRL_NthChannel", 1);
+ auto starting = SettingsMap.GetInt("SPINCTRL_StartingAt", 1);
+ auto count = SettingsMap.GetInt("SPINCTRL_Count", 0);
+
+ AdjustChannels(StartsWith(string_type, "Single Color"), num_channels, buffer, action, value1, value2, nth, starting, count);
+}
diff --git a/xLights/effects/AdjustEffect.h b/xLights/effects/AdjustEffect.h
new file mode 100644
index 0000000000..221da613ad
--- /dev/null
+++ b/xLights/effects/AdjustEffect.h
@@ -0,0 +1,37 @@
+#pragma once
+
+/***************************************************************
+ * This source files comes from the xLights project
+ * https://www.xlights.org
+ * https://github.com/smeighan/xLights
+ * See the github commit history for a record of contributing
+ * developers.
+ * Copyright claimed based on commit dates recorded in Github
+ * License: https://github.com/smeighan/xLights/blob/master/License.txt
+ **************************************************************/
+
+#include "RenderableEffect.h"
+#include "../UtilFunctions.h"
+
+class AdjustEffect : public RenderableEffect
+{
+public:
+ AdjustEffect(int id);
+ virtual ~AdjustEffect();
+ virtual bool CanBeRandom() override
+ {
+ return false;
+ }
+ virtual void Render(Effect* effect, const SettingsMap& settings, RenderBuffer& buffer) override;
+ virtual void SetDefaultParameters() override;
+ virtual bool CanRenderPartialTimeInterval() const override
+ {
+ return true;
+ }
+ virtual std::list CheckEffectSettings(const SettingsMap& settings, AudioManager* media, Model* model, Effect* eff, bool renderCache) override;
+
+protected:
+ virtual xlEffectPanel* CreatePanel(wxWindow* parent) override;
+ void AdjustChannels(bool singleColour, int numChannels, RenderBuffer& buffer, const std::string& action, int value1, int value2, int nth, int starting, int count);
+};
+
diff --git a/xLights/effects/AdjustPanel.cpp b/xLights/effects/AdjustPanel.cpp
new file mode 100644
index 0000000000..87bcad1f1c
--- /dev/null
+++ b/xLights/effects/AdjustPanel.cpp
@@ -0,0 +1,176 @@
+/***************************************************************
+ * This source files comes from the xLights project
+ * https://www.xlights.org
+ * https://github.com/smeighan/xLights
+ * See the github commit history for a record of contributing
+ * developers.
+ * Copyright claimed based on commit dates recorded in Github
+ * License: https://github.com/smeighan/xLights/blob/master/License.txt
+ **************************************************************/
+
+ //(*InternalHeaders(AdjustPanel)
+ #include
+ #include
+ #include
+ #include
+ #include
+ #include
+ //*)
+
+#include
+
+#include "AdjustPanel.h"
+#include "AdjustEffect.h"
+#include "EffectPanelUtils.h"
+#include "../UtilFunctions.h"
+
+//(*IdInit(AdjustPanel)
+const long AdjustPanel::ID_STATICTEXT1 = wxNewId();
+const long AdjustPanel::ID_CHOICE_Action = wxNewId();
+const long AdjustPanel::ID_STATICTEXT2 = wxNewId();
+const long AdjustPanel::ID_SPINCTRL_Value1 = wxNewId();
+const long AdjustPanel::ID_STATICTEXT3 = wxNewId();
+const long AdjustPanel::ID_SPINCTRL_Value2 = wxNewId();
+const long AdjustPanel::ID_STATICTEXT4 = wxNewId();
+const long AdjustPanel::ID_SPINCTRL_NthChannel = wxNewId();
+const long AdjustPanel::ID_STATICTEXT5 = wxNewId();
+const long AdjustPanel::ID_SPINCTRL_StartingAt = wxNewId();
+const long AdjustPanel::ID_STATICTEXT6 = wxNewId();
+const long AdjustPanel::ID_SPINCTRL_Count = wxNewId();
+//*)
+
+BEGIN_EVENT_TABLE(AdjustPanel,wxPanel)
+ //(*EventTable(AdjustPanel)
+ //*)
+END_EVENT_TABLE()
+
+AdjustPanel::AdjustPanel(wxWindow* parent) : xlEffectPanel(parent)
+{
+ //(*Initialize(AdjustPanel)
+ Create(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL, _T("wxID_ANY"));
+ FlexGridSizer_Main = new wxFlexGridSizer(0, 2, 0, 0);
+ FlexGridSizer_Main->AddGrowableCol(1);
+ StaticText1 = new wxStaticText(this, ID_STATICTEXT1, _("Adjustment:"), wxDefaultPosition, wxDefaultSize, 0, _T("ID_STATICTEXT1"));
+ FlexGridSizer_Main->Add(StaticText1, 1, wxALL|wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL, 5);
+ Choice_Action = new BulkEditChoice(this, ID_CHOICE_Action, wxDefaultPosition, wxDefaultSize, 0, 0, 0, wxDefaultValidator, _T("ID_CHOICE_Action"));
+ FlexGridSizer_Main->Add(Choice_Action, 1, wxALL|wxEXPAND, 5);
+ StaticText_Value1 = new wxStaticText(this, ID_STATICTEXT2, _("Value 1:"), wxDefaultPosition, wxDefaultSize, 0, _T("ID_STATICTEXT2"));
+ FlexGridSizer_Main->Add(StaticText_Value1, 1, wxALL|wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL, 5);
+ SpinCtrl_Value1 = new BulkEditSpinCtrl(this, ID_SPINCTRL_Value1, _T("0"), wxDefaultPosition, wxDefaultSize, 0, -255, 255, 0, _T("ID_SPINCTRL_Value1"));
+ SpinCtrl_Value1->SetValue(_T("0"));
+ FlexGridSizer_Main->Add(SpinCtrl_Value1, 1, wxALL|wxEXPAND, 5);
+ StaticText_Value2 = new wxStaticText(this, ID_STATICTEXT3, _("Value 2:"), wxDefaultPosition, wxDefaultSize, 0, _T("ID_STATICTEXT3"));
+ FlexGridSizer_Main->Add(StaticText_Value2, 1, wxALL|wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL, 5);
+ SpinCtrl_Value2 = new BulkEditSpinCtrl(this, ID_SPINCTRL_Value2, _T("0"), wxDefaultPosition, wxDefaultSize, 0, -255, 255, 0, _T("ID_SPINCTRL_Value2"));
+ SpinCtrl_Value2->SetValue(_T("0"));
+ FlexGridSizer_Main->Add(SpinCtrl_Value2, 1, wxALL|wxEXPAND, 5);
+ StaticText2 = new wxStaticText(this, ID_STATICTEXT4, _("Nth Channel:"), wxDefaultPosition, wxDefaultSize, 0, _T("ID_STATICTEXT4"));
+ FlexGridSizer_Main->Add(StaticText2, 1, wxALL|wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL, 5);
+ SpinCtrl_NthChannel = new BulkEditSpinCtrl(this, ID_SPINCTRL_NthChannel, _T("1"), wxDefaultPosition, wxDefaultSize, 0, 1, 32, 1, _T("ID_SPINCTRL_NthChannel"));
+ SpinCtrl_NthChannel->SetValue(_T("1"));
+ FlexGridSizer_Main->Add(SpinCtrl_NthChannel, 1, wxALL|wxEXPAND, 5);
+ StaticText3 = new wxStaticText(this, ID_STATICTEXT5, _("Starting At:"), wxDefaultPosition, wxDefaultSize, 0, _T("ID_STATICTEXT5"));
+ FlexGridSizer_Main->Add(StaticText3, 1, wxALL|wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL, 5);
+ SpinCtrl_StartingAt = new BulkEditSpinCtrl(this, ID_SPINCTRL_StartingAt, _T("1"), wxDefaultPosition, wxDefaultSize, 0, 1, 100, 1, _T("ID_SPINCTRL_StartingAt"));
+ SpinCtrl_StartingAt->SetValue(_T("1"));
+ FlexGridSizer_Main->Add(SpinCtrl_StartingAt, 1, wxALL|wxEXPAND, 5);
+ StaticText4 = new wxStaticText(this, ID_STATICTEXT6, _("Count:"), wxDefaultPosition, wxDefaultSize, 0, _T("ID_STATICTEXT6"));
+ FlexGridSizer_Main->Add(StaticText4, 1, wxALL|wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL, 5);
+ SpinCtrl_Count = new BulkEditSpinCtrl(this, ID_SPINCTRL_Count, _T("0"), wxDefaultPosition, wxDefaultSize, 0, 0, 10000, 0, _T("ID_SPINCTRL_Count"));
+ SpinCtrl_Count->SetValue(_T("0"));
+ FlexGridSizer_Main->Add(SpinCtrl_Count, 1, wxALL|wxEXPAND, 5);
+ SetSizer(FlexGridSizer_Main);
+ FlexGridSizer_Main->Fit(this);
+ FlexGridSizer_Main->SetSizeHints(this);
+
+ Connect(ID_CHOICE_Action,wxEVT_COMMAND_CHOICE_SELECTED,(wxObjectEventFunction)&AdjustPanel::OnChoice_ActionSelect);
+ //*)
+ SetName("ID_PANEL_ADJUST");
+
+ Connect(wxID_ANY, EVT_VC_CHANGED, (wxObjectEventFunction)&AdjustPanel::OnVCChanged, 0, this);
+ Connect(wxID_ANY, EVT_VALIDATEWINDOW, (wxObjectEventFunction)&AdjustPanel::OnValidateWindow, 0, this);
+
+ Choice_Action->Append("None");
+ Choice_Action->Append("Adjust By Value");
+ Choice_Action->Append("Adjust By Percentage");
+ Choice_Action->Append("Set Minimum");
+ Choice_Action->Append("Set Maximum");
+ Choice_Action->Append("Set Range");
+ Choice_Action->Append("Shift With Wrap By Value");
+ Choice_Action->Append("Prevent Range");
+ Choice_Action->Append("Reverse");
+
+ ValidateWindow();
+}
+
+AdjustPanel::~AdjustPanel()
+{
+ //(*Destroy(AdjustPanel)
+ //*)
+}
+
+void AdjustPanel::ValidateWindow()
+{
+ auto action = Choice_Action->GetStringSelection();
+
+ if (action == "None") {
+ StaticText_Value1->SetLabel("Unused:");
+ SpinCtrl_Value1->Disable();
+ StaticText_Value2->SetLabel("Unused:");
+ SpinCtrl_Value2->Disable();
+ } else if (action == "Adjust By Value") {
+ StaticText_Value1->SetLabel("Adjust by:");
+ SpinCtrl_Value1->SetRange(-255, 255);
+ SpinCtrl_Value1->Enable();
+ StaticText_Value2->SetLabel("Unused:");
+ SpinCtrl_Value2->Disable();
+ } else if (action == "Adjust By Percentage") {
+ StaticText_Value1->SetLabel("Adjust by:");
+ SpinCtrl_Value1->SetRange(-100, 25500);
+ SpinCtrl_Value1->Enable();
+ StaticText_Value2->SetLabel("Unused:");
+ SpinCtrl_Value2->Disable();
+ } else if (action == "Set Minimum") {
+ StaticText_Value1->SetLabel("Minimum:");
+ SpinCtrl_Value1->SetRange(0, 255);
+ SpinCtrl_Value1->Enable();
+ StaticText_Value2->SetLabel("Unused:");
+ SpinCtrl_Value2->Disable();
+ } else if (action == "Set Maximum") {
+ StaticText_Value1->SetLabel("Maximum:");
+ SpinCtrl_Value1->SetRange(0, 255);
+ SpinCtrl_Value1->Enable();
+ StaticText_Value2->SetLabel("Unused:");
+ SpinCtrl_Value2->Disable();
+ } else if (action == "Set Range") {
+ StaticText_Value1->SetLabel("Minimum:");
+ SpinCtrl_Value1->SetRange(0, 255);
+ SpinCtrl_Value1->Enable();
+ StaticText_Value2->SetLabel("Maximum:");
+ SpinCtrl_Value2->SetRange(0, 255);
+ SpinCtrl_Value2->Enable();
+ } else if (action == "Shift With Wrap By Value") {
+ StaticText_Value1->SetLabel("Shift by:");
+ SpinCtrl_Value1->SetRange(-255, 255);
+ SpinCtrl_Value1->Enable();
+ StaticText_Value2->SetLabel("Unused:");
+ SpinCtrl_Value2->Disable();
+ } else if (action == "Prevent Range") {
+ StaticText_Value1->SetLabel("Minimum:");
+ SpinCtrl_Value1->SetRange(0, 255);
+ SpinCtrl_Value1->Enable();
+ StaticText_Value2->SetLabel("Maximum:");
+ SpinCtrl_Value2->SetRange(0, 255);
+ SpinCtrl_Value2->Enable();
+ } else if (action == "Reverse") {
+ StaticText_Value1->SetLabel("Unused:");
+ SpinCtrl_Value1->Disable();
+ StaticText_Value2->SetLabel("Unused:");
+ SpinCtrl_Value2->Disable();
+ }
+}
+
+void AdjustPanel::OnChoice_ActionSelect(wxCommandEvent& event)
+{
+ ValidateWindow();
+}
diff --git a/xLights/effects/AdjustPanel.h b/xLights/effects/AdjustPanel.h
new file mode 100644
index 0000000000..1d3aa1f4f8
--- /dev/null
+++ b/xLights/effects/AdjustPanel.h
@@ -0,0 +1,83 @@
+#pragma once
+
+/***************************************************************
+ * This source files comes from the xLights project
+ * https://www.xlights.org
+ * https://github.com/smeighan/xLights
+ * See the github commit history for a record of contributing
+ * developers.
+ * Copyright claimed based on commit dates recorded in Github
+ * License: https://github.com/smeighan/xLights/blob/master/License.txt
+ **************************************************************/
+
+ //(*Headers(AdjustPanel)
+ #include
+ class wxChoice;
+ class wxFlexGridSizer;
+ class wxSpinCtrl;
+ class wxSpinEvent;
+ class wxStaticText;
+ //*)
+
+#include "../BulkEditControls.h"
+#include "EffectPanelUtils.h"
+
+class Model;
+
+class AdjustPanel: public xlEffectPanel
+{
+ std::list GetActiveModels();
+
+ public:
+
+ AdjustPanel(wxWindow* parent);
+ virtual ~AdjustPanel();
+ virtual void ValidateWindow() override;
+
+ //(*Declarations(AdjustPanel)
+ BulkEditChoice* Choice_Action;
+ BulkEditSpinCtrl* SpinCtrl_Count;
+ BulkEditSpinCtrl* SpinCtrl_NthChannel;
+ BulkEditSpinCtrl* SpinCtrl_StartingAt;
+ BulkEditSpinCtrl* SpinCtrl_Value1;
+ BulkEditSpinCtrl* SpinCtrl_Value2;
+ wxFlexGridSizer* FlexGridSizer_Main;
+ wxStaticText* StaticText1;
+ wxStaticText* StaticText2;
+ wxStaticText* StaticText3;
+ wxStaticText* StaticText4;
+ wxStaticText* StaticText_Value1;
+ wxStaticText* StaticText_Value2;
+ //*)
+
+ protected:
+
+ //(*Identifiers(AdjustPanel)
+ static const long ID_STATICTEXT1;
+ static const long ID_CHOICE_Action;
+ static const long ID_STATICTEXT2;
+ static const long ID_SPINCTRL_Value1;
+ static const long ID_STATICTEXT3;
+ static const long ID_SPINCTRL_Value2;
+ static const long ID_STATICTEXT4;
+ static const long ID_SPINCTRL_NthChannel;
+ static const long ID_STATICTEXT5;
+ static const long ID_SPINCTRL_StartingAt;
+ static const long ID_STATICTEXT6;
+ static const long ID_SPINCTRL_Count;
+ //*)
+
+ public:
+
+ //(*Handlers(AdjustPanel)
+ void OnButtonRemapClick(wxCommandEvent& event);
+ void OnButton_SaveAsStateClick(wxCommandEvent& event);
+ void OnButton_Load_StateClick(wxCommandEvent& event);
+ void OnChoice_ActionSelect(wxCommandEvent& event);
+ //*)
+
+ void OnButtonRemapRClick(wxCommandEvent& event);
+ void OnChoicePopup(wxCommandEvent& event);
+
+ DECLARE_EVENT_TABLE()
+};
diff --git a/xLights/effects/EffectManager.cpp b/xLights/effects/EffectManager.cpp
index fba8c6fd23..25506414d7 100644
--- a/xLights/effects/EffectManager.cpp
+++ b/xLights/effects/EffectManager.cpp
@@ -11,6 +11,7 @@
#include "EffectManager.h"
#include "OffEffect.h"
#include "OnEffect.h"
+#include "AdjustEffect.h"
#include "BarsEffect.h"
#include "ButterflyEffect.h"
#include "CandleEffect.h"
@@ -81,6 +82,7 @@ EffectManager::EffectManager()
{
add(createEffect(eff_OFF));
add(createEffect(eff_ON));
+ add(createEffect(eff_ADJUST));
add(createEffect(eff_BARS));
add(createEffect(eff_BUTTERFLY));
add(createEffect(eff_CANDLE));
@@ -153,7 +155,10 @@ RenderableEffect *EffectManager::createEffect(RGB_EFFECTS_e eff) {
switch (eff) {
case eff_OFF: return new OffEffect(eff_OFF);
case eff_ON: return new OnEffect(eff_ON);
- case eff_BARS: return new BarsEffect(eff_BARS);
+ case eff_ADJUST:
+ return new AdjustEffect(eff_ADJUST);
+ case eff_BARS:
+ return new BarsEffect(eff_BARS);
case eff_BUTTERFLY: return new ButterflyEffect(eff_BUTTERFLY);
case eff_CANDLE: return new CandleEffect(eff_CANDLE);
case eff_CIRCLES: return new CirclesEffect(eff_CIRCLES);
diff --git a/xLights/effects/EffectManager.h b/xLights/effects/EffectManager.h
index 60c449f8d5..d1c76d4823 100644
--- a/xLights/effects/EffectManager.h
+++ b/xLights/effects/EffectManager.h
@@ -23,6 +23,7 @@ class EffectManager
enum RGB_EFFECTS_e {
eff_OFF,
eff_ON,
+ eff_ADJUST,
eff_BARS,
eff_BUTTERFLY,
eff_CANDLE,
diff --git a/xLights/effects/FacesEffect.cpp b/xLights/effects/FacesEffect.cpp
index 866b162f1c..da7d1da0bc 100644
--- a/xLights/effects/FacesEffect.cpp
+++ b/xLights/effects/FacesEffect.cpp
@@ -38,8 +38,8 @@ class FacesRenderCache : public EffectRenderCache {
int nextBlinkTime;
std::map nodeNameCache;
- FacesRenderCache() :
- blinkEndTime(0), nextBlinkTime(intRand(0, 5000)) {
+ FacesRenderCache(int nextBlinkTime) :
+ blinkEndTime(0), nextBlinkTime(nextBlinkTime) {
}
virtual ~FacesRenderCache() {
for (auto it : _imageCache) {
@@ -112,6 +112,10 @@ wxString FacesEffect::GetEffectString() {
ret << p->Choice_Faces_Eyes->GetStringSelection().ToStdString();
ret << ",";
+ ret << "E_CHOICE_Faces_EyeBlinkFrequency=";
+ ret << p->Choice_Faces_EyeBlinkFrequency->GetStringSelection().ToStdString();
+ ret << ",";
+
ret << "E_CHOICE_Faces_FaceDefinition=";
ret << p->Face_FaceDefinitonChoice->GetStringSelection().ToStdString();
ret << ",";
@@ -235,6 +239,20 @@ std::list FacesEffect::CheckEffectSettings(const SettingsMap& setti
return res;
}
+int FacesEffect::GetMaxEyeDelay( std::string& eyeBlinkFreqString ) const {
+ int maxEyeDelay = 5500; //Normal
+ if( eyeBlinkFreqString == "Slowest")
+ maxEyeDelay = 9000;
+ else if( eyeBlinkFreqString == "Slow")
+ maxEyeDelay = 7250;
+ else if( eyeBlinkFreqString == "Fast")
+ maxEyeDelay = 3750;
+ else if( eyeBlinkFreqString == "Fastest")
+ maxEyeDelay = 2000;
+
+ return maxEyeDelay;
+}
+
void FacesEffect::SetPanelStatus(Model* cls) {
FacesPanel* fp = (FacesPanel*)panel;
if (fp == nullptr)
@@ -353,6 +371,7 @@ void FacesEffect::SetDefaultParameters() {
SetRadioValue(fp->RadioButton1);
SetChoiceValue(fp->Choice_Faces_Phoneme, "AI");
SetChoiceValue(fp->Choice_Faces_Eyes, "Auto");
+ SetChoiceValue(fp->Choice_Faces_EyeBlinkFrequency, "Normal");
SetChoiceValue(fp->Choice1, "");
if (fp->Face_FaceDefinitonChoice->GetCount() > 0) {
@@ -453,6 +472,7 @@ void FacesEffect::Render(Effect* effect, const SettingsMap& SettingsMap, RenderB
RenderCoroFacesFromPGO(buffer,
SettingsMap["CHOICE_Faces_Phoneme"],
SettingsMap.Get("CHOICE_Faces_Eyes", "Auto"),
+ SettingsMap.Get("CHOICE_Faces_EyeBlinkFrequency", "Normal"),
SettingsMap.GetBool("CHECKBOX_Faces_Outline"),
alpha, SettingsMap.GetBool("CHECKBOX_Faces_SuppressShimmer", false));
} else {
@@ -462,6 +482,7 @@ void FacesEffect::Render(Effect* effect, const SettingsMap& SettingsMap, RenderB
SettingsMap["CHOICE_Faces_Phoneme"],
SettingsMap["CHOICE_Faces_TimingTrack"],
SettingsMap["CHOICE_Faces_Eyes"],
+ SettingsMap["CHOICE_Faces_EyeBlinkFrequency"],
SettingsMap.GetBool("CHECKBOX_Faces_Outline"),
SettingsMap.GetBool("CHECKBOX_Faces_TransparentBlack", false),
SettingsMap.GetInt("TEXTCTRL_Faces_TransparentBlack", 0),
@@ -475,7 +496,7 @@ void FacesEffect::Render(Effect* effect, const SettingsMap& SettingsMap, RenderB
//}
}
-void FacesEffect::RenderFaces(RenderBuffer& buffer, const std::string& Phoneme, const std::string& eyes, bool outline, uint8_t alpha, bool suppressShimmer) {
+void FacesEffect::RenderFaces(RenderBuffer& buffer, const std::string& Phoneme, const std::string& eyes, const std::string& eyeBlinkFreq, bool outline, uint8_t alpha, bool suppressShimmer) {
if (alpha == 0)
return; // 0 alpha means there is nothing to do
@@ -507,7 +528,7 @@ void FacesEffect::RenderFaces(RenderBuffer& buffer, const std::string& Phoneme,
int Wt = buffer.BufferWi;
// this draws eyes as well
- drawoutline(buffer, PhonemeInt, outline, eyes, buffer.BufferHt, buffer.BufferWi);
+ drawoutline(buffer, PhonemeInt, outline, eyes, eyeBlinkFreq, buffer.BufferHt, buffer.BufferWi);
mouth(buffer, PhonemeInt, Ht, Wt, shimmer); // draw a mouth syllable
}
@@ -672,12 +693,14 @@ void FacesEffect::facesCircle(RenderBuffer& buffer, int Phoneme, int xc, int yc,
}
}
-void FacesEffect::drawoutline(RenderBuffer& buffer, int Phoneme, bool outline, const std::string& eyes, int BufferHt, int BufferWi) {
+void FacesEffect::drawoutline(RenderBuffer& buffer, int Phoneme, bool outline, const std::string& eyes, const std::string& eyeBlinkFreqIn, int BufferHt, int BufferWi) {
std::string eye = eyes;
+ std::string eyeBlinkFreq = eyeBlinkFreqIn;
FacesRenderCache* cache = (FacesRenderCache*)buffer.infoCache[id];
if (cache == nullptr) {
- cache = new FacesRenderCache();
+ int maxEyeDelay = GetMaxEyeDelay(eyeBlinkFreq);
+ cache = new FacesRenderCache(intRand(0, maxEyeDelay));
buffer.infoCache[id] = cache;
}
@@ -695,8 +718,9 @@ void FacesEffect::drawoutline(RenderBuffer& buffer, int Phoneme, bool outline, c
if (eye == "Auto") {
if (Phoneme == 9 || Phoneme == 10) {
if ((buffer.curPeriod * buffer.frameTimeInMs) >= cache->nextBlinkTime) {
- //roughly every 5 seconds we'll blink
- cache->nextBlinkTime += intRand(4500, 5500);
+ //calculate the blink time taking into account user selection
+ int maxEyeDelay = GetMaxEyeDelay(eyeBlinkFreq);
+ cache->nextBlinkTime += intRand(maxEyeDelay-1000, maxEyeDelay);
cache->blinkEndTime = buffer.curPeriod * buffer.frameTimeInMs + 101; //100ms blink
eye = "Closed";
} else if ((buffer.curPeriod * buffer.frameTimeInMs) < cache->blinkEndTime) {
@@ -811,7 +835,7 @@ static bool parse_model(const wxString& want_model)
// Outline_x_y = list of persistent/sticky elements (stays on after frame ends)
// Eyes_x_y = list of random elements (intended for eye blinks, etc)
-void FacesEffect::RenderCoroFacesFromPGO(RenderBuffer& buffer, const std::string& Phoneme, const std::string& eyes, bool face_outline, uint8_t alpha, bool suppressShimmer)
+void FacesEffect::RenderCoroFacesFromPGO(RenderBuffer& buffer, const std::string& Phoneme, const std::string& eyes, const std::string& eyeBlinkFreq, bool face_outline, uint8_t alpha, bool suppressShimmer)
{
if (alpha == 0) return;
@@ -827,7 +851,7 @@ void FacesEffect::RenderCoroFacesFromPGO(RenderBuffer& buffer, const std::string
if (auto_phonemes.find((const char*)Phoneme.c_str()) != auto_phonemes.end())
{
- RenderFaces(buffer, auto_phonemes[(const char*)Phoneme.c_str()], eyes, face_outline, alpha, suppressShimmer);
+ RenderFaces(buffer, auto_phonemes[(const char*)Phoneme.c_str()], eyes, eyeBlinkFreq, face_outline, alpha, suppressShimmer);
return;
}
@@ -890,14 +914,19 @@ std::string FacesEffect::MakeKey(int bufferWi, int bufferHt, std::string dirstr,
void FacesEffect::RenderFaces(RenderBuffer& buffer,
SequenceElements* elements, const std::string& faceDef,
const std::string& Phoneme, const std::string& trackName,
- const std::string& eyesIn, bool face_outline, bool transparentBlack, int transparentBlackLevel, uint8_t alpha, const std::string& outlineState, bool suppressShimmer)
+ const std::string& eyesIn, const std::string& eyeBlinkFreqIn, bool face_outline, bool transparentBlack, int transparentBlackLevel, uint8_t alpha, const std::string& outlineState, bool suppressShimmer)
{
if (alpha == 0)
return; // if alpha is zero dont bother.
+ std::string eyes = eyesIn;
+ std::string eyeBlinkFreq = eyeBlinkFreqIn;
+
FacesRenderCache* cache = (FacesRenderCache*)buffer.infoCache[id];
if (cache == nullptr) {
- cache = new FacesRenderCache();
+ int maxEyeDelay = GetMaxEyeDelay(eyeBlinkFreq);
+ cache = new FacesRenderCache(intRand(0, maxEyeDelay));
+
buffer.infoCache[id] = cache;
}
@@ -906,7 +935,6 @@ void FacesEffect::RenderFaces(RenderBuffer& buffer,
elements->AddRenderDependency(trackName, buffer.cur_model);
cache->Clear();
}
- std::string eyes = eyesIn;
if (buffer.cur_model == "") {
return;
@@ -1007,8 +1035,9 @@ void FacesEffect::RenderFaces(RenderBuffer& buffer,
phoneme = "rest";
if ("Auto" == eyes) {
if ((buffer.curPeriod * buffer.frameTimeInMs) >= cache->nextBlinkTime) {
- // roughly every 5 seconds we'll blink
- cache->nextBlinkTime += intRand(4500, 5500);
+ //calculate the blink time taking into account user selection
+ int maxEyeDelay = GetMaxEyeDelay( eyeBlinkFreq );
+ cache->nextBlinkTime += intRand(maxEyeDelay-1000, maxEyeDelay);
cache->blinkEndTime = buffer.curPeriod * buffer.frameTimeInMs + 101; // 100ms blink
eyes = "Closed";
} else if ((buffer.curPeriod * buffer.frameTimeInMs) < cache->blinkEndTime) {
@@ -1058,7 +1087,7 @@ void FacesEffect::RenderFaces(RenderBuffer& buffer,
if ((buffer.curPeriod * buffer.frameTimeInMs) >= cache->nextBlinkTime) {
if ((startms + 150) >= (buffer.curPeriod * buffer.frameTimeInMs)) {
- // don't want to blink RIGHT at the start of the rest, delay a little bie
+ // don't want to blink RIGHT at the start of the rest, delay a little bit
int tmp = (buffer.curPeriod * buffer.frameTimeInMs) + intRand(150, 549);
// also don't want it right at the end
@@ -1068,8 +1097,9 @@ void FacesEffect::RenderFaces(RenderBuffer& buffer,
cache->nextBlinkTime = tmp;
}
} else {
- // roughly every 5 seconds we'll blink
- cache->nextBlinkTime += intRand(4500, 5500);
+ //calculate the blink time taking into account user selection
+ int maxEyeDelay = GetMaxEyeDelay(eyeBlinkFreq);
+ cache->nextBlinkTime += intRand(maxEyeDelay-1000, maxEyeDelay);
cache->blinkEndTime = buffer.curPeriod * buffer.frameTimeInMs + 101; // 100ms blink
eyes = "Closed";
}
@@ -1084,8 +1114,9 @@ void FacesEffect::RenderFaces(RenderBuffer& buffer,
} else if (phoneme == "rest" || phoneme == "(off)") {
if ("Auto" == eyes) {
if ((buffer.curPeriod * buffer.frameTimeInMs) >= cache->nextBlinkTime) {
- // roughly every 5 seconds we'll blink
- cache->nextBlinkTime += intRand(4500, 5500);
+ //calculate the blink time, taking into account user selection
+ int maxEyeDelay = GetMaxEyeDelay(eyeBlinkFreq);
+ cache->nextBlinkTime += intRand(maxEyeDelay-1000, maxEyeDelay);
cache->blinkEndTime = buffer.curPeriod * buffer.frameTimeInMs + 101; // 100ms blink
eyes = "Closed";
} else if ((buffer.curPeriod * buffer.frameTimeInMs) < cache->blinkEndTime) {
@@ -1273,7 +1304,7 @@ void FacesEffect::RenderFaces(RenderBuffer& buffer,
}
if (type == 2) {
- RenderFaces(buffer, phoneme, eyes, face_outline, alpha, suppressShimmer);
+ RenderFaces(buffer, phoneme, eyes, eyeBlinkFreq, face_outline, alpha, suppressShimmer);
return;
}
if (type == 3) {
@@ -1371,6 +1402,9 @@ void FacesEffect::RenderFaces(RenderBuffer& buffer,
auto c = sts[wxString::Format("s%d-Color", (int)i)];
if (r != "") {
xlColor colour = xlColor(c);
+ if (c.empty()) {
+ colour = xlWHITE;
+ }
colour.alpha = ((int)alpha * colour.alpha) / 255;
// use the nodes as it is faster
diff --git a/xLights/effects/FacesEffect.h b/xLights/effects/FacesEffect.h
index be90ec6835..ee8761ba60 100644
--- a/xLights/effects/FacesEffect.h
+++ b/xLights/effects/FacesEffect.h
@@ -41,17 +41,19 @@ class FacesEffect : public RenderableEffect
virtual xlEffectPanel* CreatePanel(wxWindow* parent) override;
private:
+ const std::map eyeBlinkMap;
void mouth(RenderBuffer& buffer, int Phoneme, int BufferHt, int BufferWt, bool shimmer);
void drawline1(RenderBuffer& buffer, int Phoneme, int x1, int x2, int y1, int y2, int colorIdx);
- void drawoutline(RenderBuffer& buffer, int Phoneme, bool outline, const std::string& eyes, int BufferHt, int BufferWi);
+ void drawoutline(RenderBuffer& buffer, int Phoneme, bool outline, const std::string& eyes, const std::string& eyeBlinkFreq, int BufferHt, int BufferWi);
void facesCircle(RenderBuffer& buffer, int Phoneme, int xc, int yc, double radius, int start_degrees, int end_degrees, int colorIdx);
void drawline3(RenderBuffer& buffer, int Phoneme, int x1, int x2, int y6, int y7, int colorIdx);
- void RenderFaces(RenderBuffer& buffer, const std::string& Phoneme, const std::string& eyes, bool face_outline, uint8_t alpha, bool suppressShimmer);
- void RenderCoroFacesFromPGO(RenderBuffer& buffer, const std::string& Phoneme, const std::string& eyes, bool face_outline, uint8_t alpha, bool suppressShimmer);
+ void RenderFaces(RenderBuffer& buffer, const std::string& Phoneme, const std::string& eyes, const std::string& eyeBlinkFreq, bool face_outline, uint8_t alpha, bool suppressShimmer);
+ void RenderCoroFacesFromPGO(RenderBuffer& buffer, const std::string& Phoneme, const std::string& eyes, const std::string& eyeBlinkFreq, bool face_outline, uint8_t alpha, bool suppressShimmer);
void RenderFaces(RenderBuffer& buffer, SequenceElements* elements, const std::string& faceDefintion,
- const std::string& Phoneme, const std::string& track, const std::string& eyes, bool face_outline, bool transparentBlack, int transparentBlackLevel, uint8_t alpha, const std::string& outlineState, bool suppressShimmer);
+ const std::string& Phoneme, const std::string& track, const std::string& eyes, const std::string& eyeBlinkFreq, bool face_outline, bool transparentBlack, int transparentBlackLevel, uint8_t alpha, const std::string& outlineState, bool suppressShimmer);
std::string MakeKey(int bufferWi, int bufferHt, std::string dirstr, std::string picture, std::string stf);
uint8_t CalculateAlpha(SequenceElements* elements, int leadFrames, bool fade, const std::string& timingTrack, RenderBuffer& buffer);
bool ShimmerState(RenderBuffer& buffer) const;
+ int GetMaxEyeDelay( std::string& eyeBlinkFreq ) const;
};
diff --git a/xLights/effects/FacesPanel.cpp b/xLights/effects/FacesPanel.cpp
index 13a145f9d1..3eff5c9ee2 100644
--- a/xLights/effects/FacesPanel.cpp
+++ b/xLights/effects/FacesPanel.cpp
@@ -33,6 +33,8 @@ const long FacesPanel::ID_STATICTEXT15 = wxNewId();
const long FacesPanel::ID_CHOICE_Faces_FaceDefinition = wxNewId();
const long FacesPanel::ID_STATICTEXT_Faces_Eyes = wxNewId();
const long FacesPanel::ID_CHOICE_Faces_Eyes = wxNewId();
+const long FacesPanel::ID_STATICTEXT_EYEBLINKFREQUENCY = wxNewId();
+const long FacesPanel::ID_CHOICE_Faces_EyeBlinkFrequency = wxNewId();
const long FacesPanel::ID_CHECKBOX_Faces_Outline = wxNewId();
const long FacesPanel::ID_CHECKBOX_Faces_SuppressShimmer = wxNewId();
const long FacesPanel::ID_STATICTEXT1 = wxNewId();
@@ -70,7 +72,7 @@ FacesPanel::FacesPanel(wxWindow* parent) : xlEffectPanel(parent)
RadioButton1 = new wxRadioButton(this, IDD_RADIOBUTTON_Faces_Phoneme, _("Phoneme"), wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator, _T("IDD_RADIOBUTTON_Faces_Phoneme"));
RadioButton1->SetValue(true);
FlexGridSizer97->Add(RadioButton1, 1, wxALL|wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL, 5);
- Choice_Faces_Phoneme = new wxChoice(this, ID_CHOICE_Faces_Phoneme, wxDefaultPosition, wxDefaultSize, 0, 0, 0, wxDefaultValidator, _T("ID_CHOICE_Faces_Phoneme"));
+ Choice_Faces_Phoneme = new BulkEditChoice(this, ID_CHOICE_Faces_Phoneme, wxDefaultPosition, wxDefaultSize, 0, 0, 0, wxDefaultValidator, _T("ID_CHOICE_Faces_Phoneme"));
Choice_Faces_Phoneme->SetSelection( Choice_Faces_Phoneme->Append(_("AI")) );
Choice_Faces_Phoneme->Append(_("E"));
Choice_Faces_Phoneme->Append(_("FV"));
@@ -104,7 +106,16 @@ FacesPanel::FacesPanel(wxWindow* parent) : xlEffectPanel(parent)
Choice_Faces_Eyes->SetSelection( Choice_Faces_Eyes->Append(_("Auto")) );
Choice_Faces_Eyes->Append(_("(off)"));
FlexGridSizer98->Add(Choice_Faces_Eyes, 1, wxALL|wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL, 5);
- FlexGridSizer98->Add(-1,-1,1, wxALL|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5);
+ StaticText_Faces_EyeBlinkFrequency = new wxStaticText(this, ID_STATICTEXT_EYEBLINKFREQUENCY, _("Eye Blink Frequency"), wxDefaultPosition, wxDefaultSize, 0, _T("ID_STATICTEXT_EYEBLINKFREQUENCY"));
+ FlexGridSizer98->Add(StaticText_Faces_EyeBlinkFrequency, 1, wxALL|wxALIGN_RIGHT|wxALIGN_CENTER_VERTICAL, 5);
+ Choice_Faces_EyeBlinkFrequency = new BulkEditChoice(this, ID_CHOICE_Faces_EyeBlinkFrequency, wxDefaultPosition, wxDefaultSize, 0, 0, 0, wxDefaultValidator, _T("ID_CHOICE_Faces_EyeBlinkFrequency"));
+ Choice_Faces_EyeBlinkFrequency->Append(_("Slowest"));
+ Choice_Faces_EyeBlinkFrequency->Append(_("Slow"));
+ Choice_Faces_EyeBlinkFrequency->SetSelection( Choice_Faces_EyeBlinkFrequency->Append(_("Normal")) );
+ Choice_Faces_EyeBlinkFrequency->Append(_("Fast"));
+ Choice_Faces_EyeBlinkFrequency->Append(_("Fastest"));
+ FlexGridSizer98->Add(Choice_Faces_EyeBlinkFrequency, 1, wxALL|wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL, 5);
+ FlexGridSizer98->Add(-1,-1,1, wxALL|wxALIGN_CENTER_VERTICAL, 5);
CheckBox_Faces_Outline = new BulkEditCheckBox(this, ID_CHECKBOX_Faces_Outline, _("Show outline"), wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator, _T("ID_CHECKBOX_Faces_Outline"));
CheckBox_Faces_Outline->SetValue(false);
FlexGridSizer98->Add(CheckBox_Faces_Outline, 1, wxALL|wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL, 5);
@@ -125,10 +136,10 @@ FacesPanel::FacesPanel(wxWindow* parent) : xlEffectPanel(parent)
FlexGridSizer1->Add(-1,-1,1, wxALL|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5);
StaticText1 = new wxStaticText(this, ID_STATICTEXT_Faces_Lead_Frames, _("Lead In/Out Frames"), wxDefaultPosition, wxDefaultSize, 0, _T("ID_STATICTEXT_Faces_Lead_Frames"));
FlexGridSizer1->Add(StaticText1, 1, wxALL|wxEXPAND, 5);
- SpinCtrl_LeadFrames = new wxSpinCtrl(this, ID_SPINCTRL_Faces_LeadFrames, _T("0"), wxDefaultPosition, wxSize(100,-1), 0, 0, 1000, 0, _T("ID_SPINCTRL_Faces_LeadFrames"));
+ SpinCtrl_LeadFrames = new BulkEditSpinCtrl(this, ID_SPINCTRL_Faces_LeadFrames, _T("0"), wxDefaultPosition, wxSize(100,-1), 0, 0, 1000, 0, _T("ID_SPINCTRL_Faces_LeadFrames"));
SpinCtrl_LeadFrames->SetValue(_T("0"));
FlexGridSizer1->Add(SpinCtrl_LeadFrames, 1, wxALL|wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL, 5);
- CheckBox_Fade = new wxCheckBox(this, ID_CHECKBOX_Faces_Fade, _("Fade during lead in/out"), wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator, _T("ID_CHECKBOX_Faces_Fade"));
+ CheckBox_Fade = new BulkEditCheckBox(this, ID_CHECKBOX_Faces_Fade, _("Fade during lead in/out"), wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator, _T("ID_CHECKBOX_Faces_Fade"));
CheckBox_Fade->SetValue(false);
FlexGridSizer1->Add(CheckBox_Fade, 1, wxALL|wxEXPAND, 5);
FlexGridSizer98->Add(FlexGridSizer1, 1, wxALL|wxEXPAND, 5);
@@ -149,6 +160,8 @@ FacesPanel::FacesPanel(wxWindow* parent) : xlEffectPanel(parent)
Connect(IDD_RADIOBUTTON_Faces_Phoneme,wxEVT_COMMAND_RADIOBUTTON_SELECTED,(wxObjectEventFunction)&FacesPanel::OnMouthMovementTypeSelected);
Connect(IDD_RADIOBUTTON_Faces_TimingTrack,wxEVT_COMMAND_RADIOBUTTON_SELECTED,(wxObjectEventFunction)&FacesPanel::OnMouthMovementTypeSelected);
+ Connect(ID_CHOICE_Faces_Eyes,wxEVT_COMMAND_CHOICE_SELECTED,(wxObjectEventFunction)&FacesPanel::OnChoice_Faces_EyesSelect);
+ Connect(ID_CHOICE_Faces_EyeBlinkFrequency,wxEVT_COMMAND_CHOICE_SELECTED,(wxObjectEventFunction)&FacesPanel::OnChoice_Faces_EyeBlinkFrequencySelect);
Connect(ID_CHECKBOX_Faces_Outline,wxEVT_COMMAND_CHECKBOX_CLICKED,(wxObjectEventFunction)&FacesPanel::OnCheckBox_Faces_OutlineClick);
Connect(ID_CHECKBOX_Faces_SuppressWhenNotSinging,wxEVT_COMMAND_CHECKBOX_CLICKED,(wxObjectEventFunction)&FacesPanel::OnCheckBox_SuppressWhenNotSingingClick);
Connect(ID_CHECKBOX_Faces_Fade,wxEVT_COMMAND_CHECKBOX_CLICKED,(wxObjectEventFunction)&FacesPanel::OnCheckBox_FadeClick);
@@ -202,6 +215,16 @@ void FacesPanel::ValidateWindow()
else {
Choice1->Enable(false);
}
+
+ wxString type = Choice_Faces_Eyes->GetStringSelection();
+ if (type == "Auto")
+ {
+ Choice_Faces_EyeBlinkFrequency->Enable();
+ }
+ else
+ {
+ Choice_Faces_EyeBlinkFrequency->Disable();
+ }
}
void FacesPanel::OnMouthMovementTypeSelected(wxCommandEvent& event)
@@ -222,3 +245,13 @@ void FacesPanel::OnCheckBox_Faces_OutlineClick(wxCommandEvent& event)
{
ValidateWindow();
}
+
+void FacesPanel::OnChoice_Faces_EyeBlinkFrequencySelect(wxCommandEvent& event)
+{
+ ValidateWindow();
+}
+
+void FacesPanel::OnChoice_Faces_EyesSelect(wxCommandEvent& event)
+{
+ ValidateWindow();
+}
diff --git a/xLights/effects/FacesPanel.h b/xLights/effects/FacesPanel.h
index f89e8279bd..9e6bd47ab3 100644
--- a/xLights/effects/FacesPanel.h
+++ b/xLights/effects/FacesPanel.h
@@ -37,24 +37,26 @@ class FacesPanel: public xlEffectPanel
//(*Declarations(FacesPanel)
BulkEditCheckBox* CheckBox_Faces_Outline;
+ BulkEditCheckBox* CheckBox_Fade;
BulkEditCheckBox* CheckBox_SuppressShimmer;
BulkEditCheckBox* CheckBox_SuppressWhenNotSinging;
BulkEditCheckBox* CheckBox_TransparentBlack;
+ BulkEditChoice* Choice_Faces_EyeBlinkFrequency;
BulkEditChoice* Choice_Faces_Eyes;
+ BulkEditChoice* Choice_Faces_Phoneme;
BulkEditChoice* Choice_Faces_TimingTrack;
BulkEditFaceChoice* Face_FaceDefinitonChoice;
BulkEditSlider* Slider_Faces_TransparentBlack;
+ BulkEditSpinCtrl* SpinCtrl_LeadFrames;
BulkEditStateChoice* Choice1;
BulkEditTextCtrl* TextCtrl_Faces_TransparentBlack;
- wxCheckBox* CheckBox_Fade;
- wxChoice* Choice_Faces_Phoneme;
wxRadioButton* RadioButton1;
wxRadioButton* RadioButton2;
- wxSpinCtrl* SpinCtrl_LeadFrames;
wxStaticText* StaticText14;
wxStaticText* StaticText1;
wxStaticText* StaticText2;
wxStaticText* StaticText71;
+ wxStaticText* StaticText_Faces_EyeBlinkFrequency;
//*)
protected:
@@ -68,6 +70,8 @@ class FacesPanel: public xlEffectPanel
static const long ID_CHOICE_Faces_FaceDefinition;
static const long ID_STATICTEXT_Faces_Eyes;
static const long ID_CHOICE_Faces_Eyes;
+ static const long ID_STATICTEXT_EYEBLINKFREQUENCY;
+ static const long ID_CHOICE_Faces_EyeBlinkFrequency;
static const long ID_CHECKBOX_Faces_Outline;
static const long ID_CHECKBOX_Faces_SuppressShimmer;
static const long ID_STATICTEXT1;
@@ -88,6 +92,8 @@ class FacesPanel: public xlEffectPanel
void OnCheckBox_SuppressWhenNotSingingClick(wxCommandEvent& event);
void OnCheckBox_FadeClick(wxCommandEvent& event);
void OnCheckBox_Faces_OutlineClick(wxCommandEvent& event);
+ void OnChoice_Faces_EyeBlinkFrequencySelect(wxCommandEvent& event);
+ void OnChoice_Faces_EyesSelect(wxCommandEvent& event);
//*)
DECLARE_EVENT_TABLE()
diff --git a/xLights/effects/LiquidEffect.cpp b/xLights/effects/LiquidEffect.cpp
index 7bc1b4827f..f9ae9fd572 100644
--- a/xLights/effects/LiquidEffect.cpp
+++ b/xLights/effects/LiquidEffect.cpp
@@ -29,6 +29,7 @@
#include
//#define LE_INTERPOLATE
+#define MAX_PARTICLES 100000
LiquidEffect::LiquidEffect(int id) : RenderableEffect(id, "Liquid", liquid_16, liquid_24, liquid_32, liquid_48, liquid_64)
{
@@ -47,14 +48,34 @@ std::list LiquidEffect::CheckEffectSettings(const SettingsMap& sett
std::list res;
if (media == nullptr && (settings.GetBool("E_CHECKBOX_FlowMusic1", false) ||
- settings.GetBool("E_CHECKBOX_FlowMusic2", false) ||
- settings.GetBool("E_CHECKBOX_FlowMusic3", false) ||
- settings.GetBool("E_CHECKBOX_FlowMusic4", false))
- )
- {
+ settings.GetBool("E_CHECKBOX_FlowMusic2", false) ||
+ settings.GetBool("E_CHECKBOX_FlowMusic3", false) ||
+ settings.GetBool("E_CHECKBOX_FlowMusic4", false))) {
res.push_back(wxString::Format(" WARN: Liquid effect cant change flow to music if there is no music. Model '%s', Start %s", model->GetName(), FORMATTIME(eff->GetStartTimeMS())).ToStdString());
}
+ int frameInterval = 50;
+ if (media != nullptr)
+ frameInterval = media->GetFrameInterval();
+ int lifetimeFrames = (1.1 * GetValueCurveIntMax("LifeTime", 1000, settings, LIQUID_LIFETIME_MIN, LIQUID_LIFETIME_MAX)) / 100 * frameInterval / 1000; // this is the lifetime in frames
+ if (lifetimeFrames == 0) {
+ lifetimeFrames = (eff->GetEndTimeMS() - eff->GetStartTimeMS()) / frameInterval;
+ }
+ lifetimeFrames = std::min(lifetimeFrames, (eff->GetEndTimeMS() - eff->GetStartTimeMS()) / frameInterval);
+ int flow1 = GetValueCurveIntMax("Flow1", 100, settings, LIQUID_FLOW_MIN, LIQUID_FLOW_MAX);
+ int flow2 = settings.GetBool("E_CHECKBOX_Enabled2", false) ? GetValueCurveIntMax("Flow2", 100, settings, LIQUID_FLOW_MIN, LIQUID_FLOW_MAX) : 0;
+ int flow3 = settings.GetBool("E_CHECKBOX_Enabled3", false) ? GetValueCurveIntMax("Flow3", 100, settings, LIQUID_FLOW_MIN, LIQUID_FLOW_MAX) : 0;
+ int flow4 = settings.GetBool("E_CHECKBOX_Enabled4", false) ? GetValueCurveIntMax("Flow4", 100, settings, LIQUID_FLOW_MIN, LIQUID_FLOW_MAX) : 0;
+ int count = lifetimeFrames * (flow1 + flow2 + flow3 + flow4);
+
+ if (count > MAX_PARTICLES) {
+ res.push_back(wxString::Format(" WARN: Liquid effect lifetime * (flow 1 + flow 2 + flow 3 + flow 4) = %d exceeds %d. Particle count will be limited. Model '%s', Start %s", count, MAX_PARTICLES, model->GetName(), FORMATTIME(eff->GetStartTimeMS())).ToStdString());
+ }
+
+ if (settings.GetInt("E_TEXTCTRL_Size", 500) > 1000) {
+ res.push_back(wxString::Format(" WARN: Liquid effect particle size > 1000 can slow render times significantly. Model '%s', Start %s", model->GetName(), FORMATTIME(eff->GetStartTimeMS())).ToStdString());
+ }
+
return res;
}
@@ -224,7 +245,49 @@ void LiquidEffect::CreateBarrier(b2World* world, float x, float y, float width,
groundBody->CreateFixture((b2Shape*)&groundBox, 0.0f);
}
-void LiquidEffect::Draw(RenderBuffer& buffer, b2ParticleSystem* ps, const xlColor& color, bool mixColors, int despeckle)
+bool LiquidEffect::LostForever(int x, int y, int w, int h, float gravityX, float gravityY)
+{
+ // essentially vertical ... at this point it wont come back
+ if (gravityX < 0.0001f && gravityX > -0.0001f) {
+ if (x < -1 || x > w + 1)
+ return true;
+ }
+
+ // gravity left and off the screen so it wont come back
+ if (gravityX < 0.0001f)
+ {
+ if (x < -1)
+ return true;
+ }
+
+ // gravity right and off the screen so it wont come back
+ if (gravityX > -0.0001f) {
+ if (x > w + 1)
+ return true;
+ }
+
+ // essentially horizontal
+ if (gravityY < 0.0001f && gravityY > -0.0001f) {
+ if (y < -1 || y > h + 1)
+ return true;
+ }
+
+ // gravity down and off the bottom of the screen
+ if (gravityY > -0.0001f) {
+ if (y < -1)
+ return true;
+ }
+
+ // gravity up and off the top of the screen
+ if (gravityY < 0.0001f) {
+ if (y > h + 1)
+ return true;
+ }
+
+ return false;
+}
+
+void LiquidEffect::Draw(RenderBuffer& buffer, b2ParticleSystem* ps, const xlColor& color, bool mixColors, int despeckle, float gravityX, float gravityY)
{
#ifdef LE_INTERPOLATE
size_t bufsiz = sizeof(size_t) * buffer.BufferWi * buffer.BufferHt;
@@ -309,7 +372,7 @@ void LiquidEffect::Draw(RenderBuffer& buffer, b2ParticleSystem* ps, const xlColo
int x = positionBuffer[i].x;
int y = positionBuffer[i].y;
- if (y < -1 || x < -1 || x > buffer.BufferWi + 1)
+ if (LostForever(x, y, buffer.BufferWi, buffer.BufferHt, gravityX, gravityY))
{
ps->DestroyParticle(i);
}
@@ -331,7 +394,7 @@ void LiquidEffect::Draw(RenderBuffer& buffer, b2ParticleSystem* ps, const xlColo
if (despeckle > 0)
{
- for (size_t y = 0; y < buffer.BufferHt; y++) {
+ for (size_t y = 0; y < buffer.BufferHt; ++y) {
for (size_t x = 0; x < buffer.BufferWi; ++x) {
if (buffer.GetPixel(x, y) == xlBLACK) {
buffer.SetPixel(x, y, GetDespeckleColor(buffer, x, y, despeckle));
@@ -373,14 +436,14 @@ xlColor LiquidEffect::GetDespeckleColor(RenderBuffer& buffer, size_t x, size_t y
// if any surrounding pixel is also black then we return black ... we only despeckly totally surrounded pixels
if (c == xlBLACK)
{
- blacks++;
+ ++blacks;
if (blacks >= despeckle) return xlBLACK;
}
red += c.red;
green += c.green;
blue += c.blue;
- count++;
+ ++count;
}
}
}
@@ -406,6 +469,7 @@ void LiquidEffect::CreateParticles(b2ParticleSystem* ps, int x, int y, int direc
velx -= velx * velVariation;
vely -= vely * velVariation;
+ // if lifetime is 1000 ... then we live for 10 seconds
float lt = lifetime / 100.0;
int count = flow;
@@ -414,7 +478,15 @@ void LiquidEffect::CreateParticles(b2ParticleSystem* ps, int x, int y, int direc
count *= audioLevel;
}
- for (int i = 0; i < count; i++)
+ // if we are going to exceed the maximum particles in 2 steps then we need to start flagging the older particles for deletion
+ // DestroyOldestParticle does not delete them immediately
+ if (ps->GetParticleCount() > MAX_PARTICLES - (2 * count)) {
+ for (int i = 0; i < ps->GetParticleCount() - (MAX_PARTICLES - 2 * count); ++i) {
+ ps->DestroyOldestParticle(i, true);
+ }
+ }
+
+ for (int i = 0; i < count && ps->GetParticleCount() < MAX_PARTICLES; ++i)
{
b2ParticleDef pd;
if (particleType == "Elastic")
@@ -506,7 +578,7 @@ void LiquidEffect::CreateParticleSystem(b2World* world, int lifetime, int size)
b2ParticleSystemDef particleSystemDef;
auto particleSystem = world->CreateParticleSystem(&particleSystemDef);
particleSystem->SetRadius((float)size / 1000.0f);
- particleSystem->SetMaxParticleCount(100000);
+ particleSystem->SetMaxParticleCount(MAX_PARTICLES);
if (lifetime > 0)
{
particleSystem->SetDestructionByAge(true);
@@ -521,6 +593,7 @@ void LiquidEffect::Step(b2World* world, RenderBuffer &buffer, bool enabled[], in
)
{
// move all existing items
+ // If frame time is 50ms then time advances by 0.05s
float32 timeStep = (float)buffer.frameTimeInMs / 1000.0;
int32 velocityIterations = 6;
int32 positionIterations = 2;
@@ -542,7 +615,7 @@ void LiquidEffect::Step(b2World* world, RenderBuffer &buffer, bool enabled[], in
}
int j = 0;
- for (int i = 0; i < 4; i++)
+ for (int i = 0; i < 4; ++i)
{
if (enabled[i])
{
@@ -564,7 +637,7 @@ void LiquidEffect::Step(b2World* world, RenderBuffer &buffer, bool enabled[], in
CreateParticles(ps, x4, y4, direction4, velocity4, flow4, flowMusic4, lifetime, buffer.BufferWi, buffer.BufferHt, color, particleType, mixcolors, audioLevel, sourceSize4);
break;
}
- j++;
+ ++j;
}
}
}
@@ -672,7 +745,7 @@ void LiquidEffect::Render(RenderBuffer &buffer,
{
xlColor color;
buffer.palette.GetColor(0, color);
- Draw(buffer, ps, color, holdcolor || mixcolors, despeckle);
+ Draw(buffer, ps, color, holdcolor || mixcolors, despeckle, gravityX, gravityY);
}
// because of memory usage delete our world when rendered the last frame
diff --git a/xLights/effects/LiquidEffect.h b/xLights/effects/LiquidEffect.h
index fafcfab55b..03cc322dca 100644
--- a/xLights/effects/LiquidEffect.h
+++ b/xLights/effects/LiquidEffect.h
@@ -130,7 +130,8 @@ class LiquidEffect : public RenderableEffect
bool enabled4, int direction4, int x4, int y4, int velocity4, int flow4, int sourceSize4, bool flowMusic4,
const std::string& particleType, int despeckle, float gravity, int gravityAngle);
void CreateBarrier(b2World* world, float x, float y, float width, float height);
- void Draw(RenderBuffer& buffer, b2ParticleSystem* ps, const xlColor& color, bool mixColors, int despeckle);
+ void Draw(RenderBuffer& buffer, b2ParticleSystem* ps, const xlColor& color, bool mixColors, int despeckle, float gravityX, float gravityY);
+ bool LostForever(int x, int y, int w, int h, float gravityX, float gravityY);
void CreateParticles(b2ParticleSystem* ps, int x, int y, int direction, int velocity, int flow, bool flowMusic, int lifetime, int width, int height, const xlColor& c, const std::string& particleType, bool mixcolors, float audioLevel, int sourceSize);
void CreateParticleSystem(b2World* world, int lifetime, int size);
void Step(b2World* world, RenderBuffer& buffer, bool enabled[], int lifetime, const std::string& particleType, bool mixcolors,
diff --git a/xLights/effects/PianoEffect.cpp b/xLights/effects/PianoEffect.cpp
index 46ef588085..49aba49c5c 100644
--- a/xLights/effects/PianoEffect.cpp
+++ b/xLights/effects/PianoEffect.cpp
@@ -723,6 +723,7 @@ std::map>> PianoEffect::LoadTimingTrack(c
}
}
+ // std::eraseif
std::remove_if(tracker.begin(), tracker.end(), [](std::tuple t) { return std::get<0>(t) == -1; });
}
diff --git a/xLights/effects/PicturesEffect.cpp b/xLights/effects/PicturesEffect.cpp
index d2dfc37d12..1157981a21 100644
--- a/xLights/effects/PicturesEffect.cpp
+++ b/xLights/effects/PicturesEffect.cpp
@@ -463,6 +463,7 @@ bool PicturesEffect::IsPictureFile(std::string filename)
ext == "jpg" ||
ext == "jpeg" ||
ext == "png" ||
+ ext == "webp" ||
ext == "bmp"
)
{
diff --git a/xLights/effects/RenderableEffect.cpp b/xLights/effects/RenderableEffect.cpp
index 869d8b79c4..11333da606 100644
--- a/xLights/effects/RenderableEffect.cpp
+++ b/xLights/effects/RenderableEffect.cpp
@@ -936,6 +936,35 @@ double RenderableEffect::GetValueCurveDouble(const std::string &name, double def
return res;
}
+int RenderableEffect::GetValueCurveIntMax(const std::string& name, int def, const SettingsMap& SettingsMap, int min, int max, int divisor)
+{
+ int res = def;
+
+ const std::string vn = "E_VALUECURVE_" + name;
+ if (SettingsMap.Contains(vn)) {
+ const std::string& vc = SettingsMap.Get(vn, xlEMPTY_STRING);
+
+ ValueCurve valc;
+ valc.SetDivisor(divisor);
+ valc.SetLimits(min, max);
+ valc.Deserialise(vc);
+ if (valc.IsActive()) {
+ return valc.GetMaxValueDivided();
+ }
+ }
+
+ const std::string sn = "E_SLIDER_" + name;
+ const std::string tn = "E_TEXTCTRL_" + name;
+ // bool slider = false;
+ if (SettingsMap.Contains(sn)) {
+ res = SettingsMap.GetInt(sn, def);
+ // slider = true;
+ } else if (SettingsMap.Contains(tn)) {
+ res = SettingsMap.GetInt(tn, def);
+ }
+ return res;
+}
+
int RenderableEffect::GetValueCurveInt(const std::string &name, int def, const SettingsMap &SettingsMap, float offset, int min, int max, long startMS, long endMS, int divisor)
{
int res = def;
diff --git a/xLights/effects/RenderableEffect.h b/xLights/effects/RenderableEffect.h
index 0c8b1c849c..f56d6f1de6 100644
--- a/xLights/effects/RenderableEffect.h
+++ b/xLights/effects/RenderableEffect.h
@@ -170,6 +170,7 @@ class RenderableEffect
double GetValueCurveDouble(const std::string& name, double def, const SettingsMap& SettingsMap, float offset, double min, double max, long startMS, long endMS, int divisor = 1);
int GetValueCurveInt(const std::string& name, int def, const SettingsMap& SettingsMap, float offset, int min, int max, long startMS, long endMS, int divisor = 1);
+ int GetValueCurveIntMax(const std::string& name, int def, const SettingsMap& SettingsMap, int min, int max, int divisor = 1);
EffectLayer* GetTiming(const std::string& timingtrack) const;
Effect* GetCurrentTiming(const RenderBuffer& buffer, const std::string& timingtrack) const;
std::string GetTimingTracks(const int maxLayers = 0, const int absoluteLayers = 0) const;
diff --git a/xLights/effects/ServoEffect.cpp b/xLights/effects/ServoEffect.cpp
index bf4758a0e2..ff651d8deb 100644
--- a/xLights/effects/ServoEffect.cpp
+++ b/xLights/effects/ServoEffect.cpp
@@ -81,6 +81,10 @@ void ServoEffect::SetDefaultParameters() {
dp->ValueCurve_Servo->SetActive(false);
SetSliderValue(dp->Slider_Servo, 0);
+ dp->Choice_Channel->SetSelection(-1);
+ SetCheckBoxValue(dp->CheckBox_Timing_Track, false);
+ SetCheckBoxValue(dp->CheckBox_16bit, false);
+ dp->Choice_Servo_TimingTrack->SetSelection(-1);
}
void ServoEffect::Render(Effect *effect, const SettingsMap &SettingsMap, RenderBuffer &buffer) {
diff --git a/xLights/effects/ShockwaveEffect.cpp b/xLights/effects/ShockwaveEffect.cpp
index a76347fd68..0a42b2b453 100644
--- a/xLights/effects/ShockwaveEffect.cpp
+++ b/xLights/effects/ShockwaveEffect.cpp
@@ -39,7 +39,14 @@ xlEffectPanel *ShockwaveEffect::CreatePanel(wxWindow *parent) {
int ShockwaveEffect::DrawEffectBackground(const Effect *e, int x1, int y1, int x2, int y2,
xlVertexColorAccumulator &backgrounds, xlColor* colorMask, bool ramps) {
- backgrounds.AddHBlendedRectangleAsTriangles(x1, y1, x2, y2, colorMask, 0, e->GetPalette());
+ int cycles = e->GetSettings().GetInt("E_SLIDER_Shockwave_Cycles", 1);
+ int totalsize = x2 - x1;
+ double x_size = totalsize / (double)cycles;
+ x_size = std::max(x_size, 0.01);
+ for (int i = 0; i < cycles; ++i) {
+ backgrounds.AddHBlendedRectangleAsTriangles(x1 + (i * x_size), y1, x1 + (i * x_size) + x_size, y2, colorMask, 0, e->GetPalette());
+ }
+
return 2;
}
@@ -63,14 +70,17 @@ void ShockwaveEffect::SetDefaultParameters() {
SetSliderValue(sp->Slider_Shockwave_End_Width, 10);
SetSliderValue(sp->Slider_Shockwave_Start_Radius, 1);
SetSliderValue(sp->Slider_Shockwave_Start_Width, 5);
+ SetSliderValue(sp->Slider_Shockwave_Cycles, 1);
SetCheckBoxValue(sp->CheckBox_Shockwave_Blend_Edges, true);
+ SetCheckBoxValue(sp->CheckBox_Shockwave_Scale, false);
}
#define ToRadians(x) ((double)x * PI / (double)180.0)
void ShockwaveEffect::Render(Effect *effect, const SettingsMap &SettingsMap, RenderBuffer &buffer) {
- double eff_pos = buffer.GetEffectTimeIntervalPosition();
+ int cycles = SettingsMap.GetInt("SLIDER_Shockwave_Cycles", 1);
+ double eff_pos = buffer.GetEffectTimeIntervalPosition(cycles);
int center_x = GetValueCurveInt("Shockwave_CenterX", 50, SettingsMap, eff_pos, SHOCKWAVE_X_MIN, SHOCKWAVE_X_MAX, buffer.GetStartTimeMS(), buffer.GetEndTimeMS());
int center_y = GetValueCurveInt("Shockwave_CenterY", 50, SettingsMap, eff_pos, SHOCKWAVE_Y_MIN, SHOCKWAVE_Y_MAX, buffer.GetStartTimeMS(), buffer.GetEndTimeMS());
int start_radius = GetValueCurveInt("Shockwave_Start_Radius", 0, SettingsMap, eff_pos, SHOCKWAVE_STARTRADIUS_MIN, SHOCKWAVE_STARTRADIUS_MAX, buffer.GetStartTimeMS(), buffer.GetEndTimeMS());
@@ -79,10 +89,12 @@ void ShockwaveEffect::Render(Effect *effect, const SettingsMap &SettingsMap, Ren
int end_width = GetValueCurveInt("Shockwave_End_Width", 0, SettingsMap, eff_pos, SHOCKWAVE_ENDWIDTH_MIN, SHOCKWAVE_ENDWIDTH_MAX, buffer.GetStartTimeMS(), buffer.GetEndTimeMS());
int acceleration = SettingsMap.GetInt("SLIDER_Shockwave_Accel", 0);
bool blend_edges = SettingsMap.GetBool("CHECKBOX_Shockwave_Blend_Edges");
+ bool scale = SettingsMap.GetBool("CHECKBOX_Shockwave_Scale", false);
int num_colors = buffer.palette.Size();
if( num_colors == 0 )
num_colors = 1;
+
double eff_pos_adj = buffer.calcAccel(eff_pos, acceleration);
HSVValue hsv, hsv1;
@@ -103,6 +115,13 @@ void ShockwaveEffect::Render(Effect *effect, const SettingsMap &SettingsMap, Ren
double radius1 = start_radius;
double radius2 = end_radius;
+ if (scale) {//convert to percentage of buffer, i.e 100 is 100% of buffer size
+ double bufferMax = std::max(buffer.BufferHt, buffer.BufferWi);
+ radius1 = radius1 * (bufferMax / 200.0); //200 bc radius is half of the width
+ radius2 = radius2 * (bufferMax / 200.0);
+ start_width = start_width * (bufferMax / 100.0);
+ end_width = end_width * (bufferMax / 100.0);
+ }
double radius_center = radius1 + (radius2 - radius1) * eff_pos_adj;
double half_width = (start_width + (end_width - start_width) * eff_pos_adj) / 2.0;
if (half_width < 0.25) {
diff --git a/xLights/effects/ShockwavePanel.cpp b/xLights/effects/ShockwavePanel.cpp
index cbc954ebc1..094efab5b3 100644
--- a/xLights/effects/ShockwavePanel.cpp
+++ b/xLights/effects/ShockwavePanel.cpp
@@ -63,7 +63,12 @@ const long ShockwavePanel::ID_STATICTEXT_Shockwave_Accel = wxNewId();
const long ShockwavePanel::ID_SLIDER_Shockwave_Accel = wxNewId();
const long ShockwavePanel::IDD_TEXTCTRL_Shockwave_Accel = wxNewId();
const long ShockwavePanel::ID_BITMAPBUTTON_SLIDER_Shockwave_Accel = wxNewId();
+const long ShockwavePanel::ID_STATICTEXT1 = wxNewId();
+const long ShockwavePanel::ID_SLIDER_Shockwave_Cycles = wxNewId();
+const long ShockwavePanel::IDD_TEXTCTRL_Shockwave_Cycles = wxNewId();
+const long ShockwavePanel::ID_BITMAPBUTTON_SLIDER_Shockwave_Cycles = wxNewId();
const long ShockwavePanel::ID_CHECKBOX_Shockwave_Blend_Edges = wxNewId();
+const long ShockwavePanel::ID_CHECKBOX_Shockwave_Scale = wxNewId();
const long ShockwavePanel::ID_PANEL44 = wxNewId();
const long ShockwavePanel::ID_NOTEBOOK_Shockwave = wxNewId();
//*)
@@ -133,7 +138,7 @@ ShockwavePanel::ShockwavePanel(wxWindow* parent) : xlEffectPanel(parent)
BitmapButton_Shockwave_StartRadius->SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_BTNHIGHLIGHT));
FlexGridSizer100->Add(BitmapButton_Shockwave_StartRadius, 1, wxALL|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5);
StaticText158 = new wxStaticText(Panel10, ID_STATICTEXT_Shockwave_End_Radius, _("Radius2:"), wxDefaultPosition, wxDefaultSize, 0, _T("ID_STATICTEXT_Shockwave_End_Radius"));
- FlexGridSizer100->Add(StaticText158, 1, wxALL|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5);
+ FlexGridSizer100->Add(StaticText158, 1, wxALL|wxALIGN_RIGHT|wxALIGN_CENTER_VERTICAL, 5);
Slider_Shockwave_End_Radius = new BulkEditSlider(Panel10, ID_SLIDER_Shockwave_End_Radius, 10, 0, 750, wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator, _T("ID_SLIDER_Shockwave_End_Radius"));
FlexGridSizer100->Add(Slider_Shockwave_End_Radius, 1, wxALL|wxEXPAND, 5);
BitmapButton_Shockwave_End_Radius = new BulkEditValueCurveButton(Panel10, ID_VALUECURVE_Shockwave_End_Radius, GetValueCurveNotSelectedBitmap(), wxDefaultPosition, wxDefaultSize, wxBU_AUTODRAW|wxBORDER_NONE, wxDefaultValidator, _T("ID_VALUECURVE_Shockwave_End_Radius"));
@@ -157,7 +162,7 @@ ShockwavePanel::ShockwavePanel(wxWindow* parent) : xlEffectPanel(parent)
BitmapButton_Shockwave_StartWidth->SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_BTNHIGHLIGHT));
FlexGridSizer100->Add(BitmapButton_Shockwave_StartWidth, 1, wxALL|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5);
StaticText159 = new wxStaticText(Panel10, ID_STATICTEXT_Shockwave_End_Width, _("Width2:"), wxDefaultPosition, wxDefaultSize, 0, _T("ID_STATICTEXT_Shockwave_End_Width"));
- FlexGridSizer100->Add(StaticText159, 1, wxALL|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5);
+ FlexGridSizer100->Add(StaticText159, 1, wxALL|wxALIGN_RIGHT|wxALIGN_CENTER_VERTICAL, 5);
Slider_Shockwave_End_Width = new BulkEditSlider(Panel10, ID_SLIDER_Shockwave_End_Width, 10, 0, 255, wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator, _T("ID_SLIDER_Shockwave_End_Width"));
FlexGridSizer100->Add(Slider_Shockwave_End_Width, 1, wxALL|wxEXPAND, 5);
BitmapButton_Shockwave_End_Width = new BulkEditValueCurveButton(Panel10, ID_VALUECURVE_Shockwave_End_Width, GetValueCurveNotSelectedBitmap(), wxDefaultPosition, wxDefaultSize, wxBU_AUTODRAW|wxBORDER_NONE, wxDefaultValidator, _T("ID_VALUECURVE_Shockwave_End_Width"));
@@ -169,6 +174,8 @@ ShockwavePanel::ShockwavePanel(wxWindow* parent) : xlEffectPanel(parent)
BitmapButton_Shockwave_EndWidth->SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_BTNHIGHLIGHT));
FlexGridSizer100->Add(BitmapButton_Shockwave_EndWidth, 1, wxALL|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5);
Panel10->SetSizer(FlexGridSizer100);
+ FlexGridSizer100->Fit(Panel10);
+ FlexGridSizer100->SetSizeHints(Panel10);
Panel14 = new wxPanel(Notebook3, ID_PANEL44, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL, _T("ID_PANEL44"));
FlexGridSizer101 = new wxFlexGridSizer(0, 4, 0, 0);
FlexGridSizer101->AddGrowableCol(1);
@@ -182,14 +189,35 @@ ShockwavePanel::ShockwavePanel(wxWindow* parent) : xlEffectPanel(parent)
BitmapButton_Shockwave_Accel = new xlLockButton(Panel14, ID_BITMAPBUTTON_SLIDER_Shockwave_Accel, wxNullBitmap, wxDefaultPosition, wxSize(14,14), wxBU_AUTODRAW|wxBORDER_NONE, wxDefaultValidator, _T("ID_BITMAPBUTTON_SLIDER_Shockwave_Accel"));
BitmapButton_Shockwave_Accel->SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_BTNHIGHLIGHT));
FlexGridSizer101->Add(BitmapButton_Shockwave_Accel, 1, wxALL|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5);
+ StaticText1 = new wxStaticText(Panel14, ID_STATICTEXT1, _("Cycles:"), wxDefaultPosition, wxDefaultSize, 0, _T("ID_STATICTEXT1"));
+ FlexGridSizer101->Add(StaticText1, 1, wxALL|wxALIGN_RIGHT|wxALIGN_CENTER_VERTICAL, 5);
+ Slider_Shockwave_Cycles = new BulkEditSlider(Panel14, ID_SLIDER_Shockwave_Cycles, 1, 1, 100, wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator, _T("ID_SLIDER_Shockwave_Cycles"));
+ FlexGridSizer101->Add(Slider_Shockwave_Cycles, 1, wxALL|wxEXPAND, 5);
+ TextCtrl_Shockwave_Cycles = new BulkEditTextCtrl(Panel14, IDD_TEXTCTRL_Shockwave_Cycles, _("1"), wxDefaultPosition, wxDLG_UNIT(Panel14,wxSize(20,-1)), 0, wxDefaultValidator, _T("IDD_TEXTCTRL_Shockwave_Cycles"));
+ TextCtrl_Shockwave_Cycles->SetMaxLength(3);
+ FlexGridSizer101->Add(TextCtrl_Shockwave_Cycles, 1, wxALL|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5);
+ BitmapButton_Shockwave_Cycles = new xlLockButton(Panel14, ID_BITMAPBUTTON_SLIDER_Shockwave_Cycles, wxNullBitmap, wxDefaultPosition, wxSize(14,14), wxBU_AUTODRAW|wxBORDER_NONE, wxDefaultValidator, _T("ID_BITMAPBUTTON_SLIDER_Shockwave_Cycles"));
+ BitmapButton_Shockwave_Cycles->SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_BTNHIGHLIGHT));
+ FlexGridSizer101->Add(BitmapButton_Shockwave_Cycles, 1, wxALL|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5);
+ FlexGridSizer101->Add(-1,-1,1, wxALL|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5);
CheckBox_Shockwave_Blend_Edges = new BulkEditCheckBox(Panel14, ID_CHECKBOX_Shockwave_Blend_Edges, _("Blend Edges:"), wxDefaultPosition, wxDefaultSize, wxALIGN_RIGHT, wxDefaultValidator, _T("ID_CHECKBOX_Shockwave_Blend_Edges"));
CheckBox_Shockwave_Blend_Edges->SetValue(true);
FlexGridSizer101->Add(CheckBox_Shockwave_Blend_Edges, 1, wxALL|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5);
+ FlexGridSizer101->Add(-1,-1,1, wxALL|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5);
+ FlexGridSizer101->Add(-1,-1,1, wxALL|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5);
+ FlexGridSizer101->Add(-1,-1,1, wxALL|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5);
+ CheckBox_Shockwave_Scale = new BulkEditCheckBox(Panel14, ID_CHECKBOX_Shockwave_Scale, _("Scale to Buffer:"), wxDefaultPosition, wxDefaultSize, wxALIGN_RIGHT, wxDefaultValidator, _T("ID_CHECKBOX_Shockwave_Scale"));
+ CheckBox_Shockwave_Scale->SetValue(false);
+ FlexGridSizer101->Add(CheckBox_Shockwave_Scale, 1, wxALL|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5);
Panel14->SetSizer(FlexGridSizer101);
+ FlexGridSizer101->Fit(Panel14);
+ FlexGridSizer101->SetSizeHints(Panel14);
Notebook3->AddPage(Panel10, _("Position"), false);
Notebook3->AddPage(Panel14, _("Options"), false);
FlexGridSizer110->Add(Notebook3, 1, wxALL|wxEXPAND, 5);
SetSizer(FlexGridSizer110);
+ FlexGridSizer110->Fit(this);
+ FlexGridSizer110->SetSizeHints(this);
Connect(ID_VALUECURVE_Shockwave_CenterX,wxEVT_COMMAND_BUTTON_CLICKED,(wxObjectEventFunction)&ShockwavePanel::OnVCButtonClick);
Connect(ID_BITMAPBUTTON_SLIDER_Shockwave_CenterX,wxEVT_COMMAND_BUTTON_CLICKED,(wxObjectEventFunction)&ShockwavePanel::OnLockButtonClick);
@@ -204,6 +232,7 @@ ShockwavePanel::ShockwavePanel(wxWindow* parent) : xlEffectPanel(parent)
Connect(ID_VALUECURVE_Shockwave_End_Width,wxEVT_COMMAND_BUTTON_CLICKED,(wxObjectEventFunction)&ShockwavePanel::OnVCButtonClick);
Connect(ID_BITMAPBUTTON_SLIDER_Shockwave_End_Width,wxEVT_COMMAND_BUTTON_CLICKED,(wxObjectEventFunction)&ShockwavePanel::OnLockButtonClick);
Connect(ID_BITMAPBUTTON_SLIDER_Shockwave_Accel,wxEVT_COMMAND_BUTTON_CLICKED,(wxObjectEventFunction)&ShockwavePanel::OnLockButtonClick);
+ Connect(ID_BITMAPBUTTON_SLIDER_Shockwave_Cycles,wxEVT_COMMAND_BUTTON_CLICKED,(wxObjectEventFunction)&ShockwavePanel::OnLockButtonClick);
//*)
Connect(wxID_ANY, EVT_VC_CHANGED, (wxObjectEventFunction)&ShockwavePanel::OnVCChanged, 0, this);
diff --git a/xLights/effects/ShockwavePanel.h b/xLights/effects/ShockwavePanel.h
index e559a94f28..ce6866aeef 100644
--- a/xLights/effects/ShockwavePanel.h
+++ b/xLights/effects/ShockwavePanel.h
@@ -35,13 +35,16 @@ class ShockwavePanel: public xlEffectPanel
//(*Declarations(ShockwavePanel)
BulkEditCheckBox* CheckBox_Shockwave_Blend_Edges;
+ BulkEditCheckBox* CheckBox_Shockwave_Scale;
BulkEditSlider* Slider_Shockwave_Accel;
BulkEditSlider* Slider_Shockwave_CenterX;
BulkEditSlider* Slider_Shockwave_CenterY;
+ BulkEditSlider* Slider_Shockwave_Cycles;
BulkEditSlider* Slider_Shockwave_End_Radius;
BulkEditSlider* Slider_Shockwave_End_Width;
BulkEditSlider* Slider_Shockwave_Start_Radius;
BulkEditSlider* Slider_Shockwave_Start_Width;
+ BulkEditTextCtrl* TextCtrl_Shockwave_Cycles;
BulkEditValueCurveButton* BitmapButton_Shockwave_CenterX;
BulkEditValueCurveButton* BitmapButton_Shockwave_CenterY;
BulkEditValueCurveButton* BitmapButton_Shockwave_End_Radius;
@@ -56,9 +59,11 @@ class ShockwavePanel: public xlEffectPanel
wxStaticText* StaticText158;
wxStaticText* StaticText159;
wxStaticText* StaticText166;
+ wxStaticText* StaticText1;
xlLockButton* BitmapButton_Shockwave_Accel;
xlLockButton* BitmapButton_Shockwave_Center_X;
xlLockButton* BitmapButton_Shockwave_Center_Y;
+ xlLockButton* BitmapButton_Shockwave_Cycles;
xlLockButton* BitmapButton_Shockwave_EndRadius;
xlLockButton* BitmapButton_Shockwave_EndWidth;
xlLockButton* BitmapButton_Shockwave_StartRadius;
@@ -103,7 +108,12 @@ class ShockwavePanel: public xlEffectPanel
static const long ID_SLIDER_Shockwave_Accel;
static const long IDD_TEXTCTRL_Shockwave_Accel;
static const long ID_BITMAPBUTTON_SLIDER_Shockwave_Accel;
+ static const long ID_STATICTEXT1;
+ static const long ID_SLIDER_Shockwave_Cycles;
+ static const long IDD_TEXTCTRL_Shockwave_Cycles;
+ static const long ID_BITMAPBUTTON_SLIDER_Shockwave_Cycles;
static const long ID_CHECKBOX_Shockwave_Blend_Edges;
+ static const long ID_CHECKBOX_Shockwave_Scale;
static const long ID_PANEL44;
static const long ID_NOTEBOOK_Shockwave;
//*)
diff --git a/xLights/effects/SketchEffect.cpp b/xLights/effects/SketchEffect.cpp
index 8cf3c4f829..95dc62eb2b 100644
--- a/xLights/effects/SketchEffect.cpp
+++ b/xLights/effects/SketchEffect.cpp
@@ -180,15 +180,18 @@ std::list SketchEffect::CheckEffectSettings(const SettingsMap& sett
wxLogNull logNo; // suppress popups from png images. See http://trac.wxwidgets.org/ticket/15331
std::list res;
- wxString filename = settings.Get("E_FILEPICKER_SketchBackground", "");
-
- if (filename == "" || !FileExists(filename)) {
- res.push_back(wxString::Format(" ERR: Sketch effect cant find image file '%s'. Model '%s', Start %s", filename, model->GetName(), FORMATTIME(eff->GetStartTimeMS())).ToStdString());
- } else {
- if (!IsFileInShowDir(xLightsFrame::CurrentDir, filename.ToStdString())) {
- res.push_back(wxString::Format(" WARN: Sketch effect image file '%s' not under show directory. Model '%s', Start %s", filename, model->GetName(), FORMATTIME(eff->GetStartTimeMS())).ToStdString());
+ if (!xLightsFrame::IsCheckSequenceOptionDisabled("SketchImage")) {
+ wxString filename = settings.Get("E_FILEPICKER_SketchBackground", "");
+ if (filename == "" || !FileExists(filename)) {
+ // this is only a warning as it does not affect rendering
+ res.push_back(wxString::Format(" WARN: Sketch effect cant find image file '%s'. Model '%s', Start %s", filename, model->GetName(), FORMATTIME(eff->GetStartTimeMS())).ToStdString());
+ } else {
+ if (!IsFileInShowDir(xLightsFrame::CurrentDir, filename.ToStdString())) {
+ res.push_back(wxString::Format(" WARN: Sketch effect image file '%s' not under show directory. Model '%s', Start %s", filename, model->GetName(), FORMATTIME(eff->GetStartTimeMS())).ToStdString());
+ }
}
}
+
return res;
}
diff --git a/xLights/effects/SketchPanel.cpp b/xLights/effects/SketchPanel.cpp
index 568737449a..fe4d69826c 100644
--- a/xLights/effects/SketchPanel.cpp
+++ b/xLights/effects/SketchPanel.cpp
@@ -25,7 +25,7 @@ namespace
const char demoVideoURL2[] = "https://vimeo.com/698053599";
const wxString imgSelect("Select an image file");
- const wxString imgFilters("*.jpg;*.gif;*.png;*.bmp;*.jpeg");
+ const wxString imgFilters("*.jpg;*.gif;*.png;*.bmp;*.jpeg;*.webp");
SketchAssistPanel* getSketchAssistPanel(wxWindow* win)
{
diff --git a/xLights/effects/assist/SketchAssistPanel.cpp b/xLights/effects/assist/SketchAssistPanel.cpp
index 05e8e3d0a4..8c90e33e7a 100644
--- a/xLights/effects/assist/SketchAssistPanel.cpp
+++ b/xLights/effects/assist/SketchAssistPanel.cpp
@@ -43,7 +43,7 @@
namespace
{
const wxString imgSelect("Select an image file");
- const wxString imgFilters("*.jpg;*.gif;*.png;*.bmp;*.jpeg");
+ const wxString imgFilters("*.jpg;*.gif;*.png;*.bmp;*.jpeg;*.webp");
const char HotkeysText[] =
"Shift\tToggle segment type (line, one-point curve, two-point curve)\n"
diff --git a/xLights/effects/assist/xlGridCanvasPictures.cpp b/xLights/effects/assist/xlGridCanvasPictures.cpp
index a194247bc2..71181cf8ed 100644
--- a/xLights/effects/assist/xlGridCanvasPictures.cpp
+++ b/xLights/effects/assist/xlGridCanvasPictures.cpp
@@ -18,7 +18,9 @@
#include "../../ExternalHooks.h"
#include "UtilFunctions.h"
-static const wxString strSupportedImageTypes = "Image files|*.png;*.bmp;*.jpg;*.gif;*.jpeg|All files (*.*)|*.*";
+static const wxString strSupportedImageTypes = "Image files|*.png;*.bmp;*.jpg;*.gif;*.jpeg"
+ ";*.webp"
+ "|All files (*.*)|*.*";
BEGIN_EVENT_TABLE(xlGridCanvasPictures, xlGridCanvas)
EVT_PAINT(xlGridCanvasPictures::render)
diff --git a/xLights/effects/metal/MetalComputeUtilities.mm b/xLights/effects/metal/MetalComputeUtilities.mm
index 09618f6081..4b79ccef0f 100644
--- a/xLights/effects/metal/MetalComputeUtilities.mm
+++ b/xLights/effects/metal/MetalComputeUtilities.mm
@@ -336,8 +336,11 @@
}
bool MetalRenderBufferComputeData::callRotoZoomFunction(id &function, RotoZoomData &data) {
- id bufferResult = getPixelBuffer();
id commandBuffer = getCommandBuffer();
+ if (commandBuffer == nil) {
+ return false;
+ }
+ id bufferResult = getPixelBuffer();
id bufferCopy = getPixelBufferCopy();
id blitCommandEncoder = [commandBuffer blitCommandEncoder];
[blitCommandEncoder setLabel:@"CopyDataToCopyBuffer"];
diff --git a/xLights/graphics/opengl/xlOGL3GraphicsContext.cpp b/xLights/graphics/opengl/xlOGL3GraphicsContext.cpp
index a98a0354b3..9202ecf63a 100644
--- a/xLights/graphics/opengl/xlOGL3GraphicsContext.cpp
+++ b/xLights/graphics/opengl/xlOGL3GraphicsContext.cpp
@@ -1790,26 +1790,41 @@ class xlGLMesh : public xlMesh {
LOG_GL_ERRORV(glGenBuffers(1, &indexBuffer));
- LOG_GL_ERRORV(glBindBuffer(GL_ARRAY_BUFFER, vbuffer));
- LOG_GL_ERRORV(glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(float), &vertices[0], GL_STATIC_DRAW));
+ if (vertices.size() > 0)
+ {
+ LOG_GL_ERRORV(glBindBuffer(GL_ARRAY_BUFFER, vbuffer));
+ LOG_GL_ERRORV(glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(float), &vertices[0], GL_STATIC_DRAW));
- LOG_GL_ERRORV(glBindBuffer(GL_ARRAY_BUFFER, tbuffer));
- LOG_GL_ERRORV(glBufferData(GL_ARRAY_BUFFER, tvertices.size() * sizeof(float), &tvertices[0], GL_STATIC_DRAW));
+ LOG_GL_ERRORV(glBindBuffer(GL_ARRAY_BUFFER, tbuffer));
+ LOG_GL_ERRORV(glBufferData(GL_ARRAY_BUFFER, tvertices.size() * sizeof(float), &tvertices[0], GL_STATIC_DRAW));
+ }
- LOG_GL_ERRORV(glBindBuffer(GL_ARRAY_BUFFER, nbuffer));
- LOG_GL_ERRORV(glBufferData(GL_ARRAY_BUFFER, normals.size() * sizeof(float), &normals[0], GL_STATIC_DRAW));
+ if (normals.size() > 0)
+ {
+ LOG_GL_ERRORV(glBindBuffer(GL_ARRAY_BUFFER, nbuffer));
+ LOG_GL_ERRORV(glBufferData(GL_ARRAY_BUFFER, normals.size() * sizeof(float), &normals[0], GL_STATIC_DRAW));
+ }
- LOG_GL_ERRORV(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, wfIndexes));
- LOG_GL_ERRORV(glBufferData(GL_ELEMENT_ARRAY_BUFFER, wireFrame.size() * sizeof(uint32_t), &wireFrame[0], GL_STATIC_DRAW));
- LOG_GL_ERRORV(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0));
+ if (wireFrame.size() > 0)
+ {
+ LOG_GL_ERRORV(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, wfIndexes));
+ LOG_GL_ERRORV(glBufferData(GL_ELEMENT_ARRAY_BUFFER, wireFrame.size() * sizeof(uint32_t), &wireFrame[0], GL_STATIC_DRAW));
+ LOG_GL_ERRORV(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0));
+ }
- LOG_GL_ERRORV(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, lineIndexes));
- LOG_GL_ERRORV(glBufferData(GL_ELEMENT_ARRAY_BUFFER, lines.size() * sizeof(uint32_t), &lines[0], GL_STATIC_DRAW));
- LOG_GL_ERRORV(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0));
+ if (lines.size() > 0)
+ {
+ LOG_GL_ERRORV(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, lineIndexes));
+ LOG_GL_ERRORV(glBufferData(GL_ELEMENT_ARRAY_BUFFER, lines.size() * sizeof(uint32_t), &lines[0], GL_STATIC_DRAW));
+ LOG_GL_ERRORV(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0));
+ }
- LOG_GL_ERRORV(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer));
- LOG_GL_ERRORV(glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexes.size() * sizeof(uint32_t), &indexes[0], GL_STATIC_DRAW));
- LOG_GL_ERRORV(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0));
+ if (indexes.size() > 0)
+ {
+ LOG_GL_ERRORV(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer));
+ LOG_GL_ERRORV(glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexes.size() * sizeof(uint32_t), &indexes[0], GL_STATIC_DRAW));
+ LOG_GL_ERRORV(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0));
+ }
}
GLuint vbuffer = 0;
diff --git a/xLights/models/ArchesModel.cpp b/xLights/models/ArchesModel.cpp
index e5a55faa17..166647c4c2 100644
--- a/xLights/models/ArchesModel.cpp
+++ b/xLights/models/ArchesModel.cpp
@@ -122,9 +122,7 @@ int ArchesModel::OnPropertyGridChange(wxPropertyGridInterface* grid, wxPropertyG
AddASAPWork(OutputModelManager::WORK_CALCULATE_START_CHANNELS, "ArchesModel::OnPropertyGridChange::ArchesCount");
AddASAPWork(OutputModelManager::WORK_MODELS_REWORK_STARTCHANNELS, "ArchesModel::OnPropertyGridChange::ArchesCount");
AddASAPWork(OutputModelManager::WORK_UPDATE_PROPERTYGRID, "ArchesModel::OnPropertyGridChange::ArchesCount");
- if (ModelXml->GetAttribute("Advanced", "0") == "1") {
- AddASAPWork(OutputModelManager::WORK_RELOAD_PROPERTYGRID, "ArchesModel::OnPropertyGridChange::ArchesCount");
- }
+ AddASAPWork(OutputModelManager::WORK_RELOAD_PROPERTYGRID, "ArchesModel::OnPropertyGridChange::ArchesCount");
return 0;
} else if ("ArchesNodes" == event.GetPropertyName()) {
ModelXml->DeleteAttribute("parm2");
diff --git a/xLights/models/BaseObject.cpp b/xLights/models/BaseObject.cpp
index 36295718d9..1539630a32 100644
--- a/xLights/models/BaseObject.cpp
+++ b/xLights/models/BaseObject.cpp
@@ -248,6 +248,15 @@ void BaseObject::FlipVertical(bool ignoreLock) {
IncrementChangeCount();
}
+static inline bool checkNameAttributes(wxXmlNode* nn, wxXmlNode* cc) {
+ if (nn->HasAttribute("name")) {
+ return (cc->GetAttribute("name") == nn->GetAttribute("name"));
+ } else if (nn->HasAttribute("Name")) {
+ return (cc->GetAttribute("Name") == nn->GetAttribute("Name"));
+ }
+ return true;
+}
+
bool BaseObject::IsXmlChanged(wxXmlNode* n) const
{
for (wxXmlAttribute* a = n->GetAttributes(); a != nullptr; a = a->GetNext()) {
@@ -263,7 +272,7 @@ bool BaseObject::IsXmlChanged(wxXmlNode* n) const
for (wxXmlNode* nn = n->GetChildren(); nn != nullptr; nn = nn->GetNext()) {
bool found = false;
for (wxXmlNode* cc = ModelXml->GetChildren(); cc != nullptr; cc = cc->GetNext()) {
- if (cc->GetName() == nn->GetName() && (!nn->HasAttribute("name") || (cc->GetAttribute("name") == nn->GetAttribute("name")))) {
+ if (cc->GetName() == nn->GetName() && checkNameAttributes(nn, cc)) {
found = true;
for (wxXmlAttribute* a = cc->GetAttributes(); a != nullptr; a = a->GetNext()) {
if (!cc->HasAttribute(a->GetName()) || nn->GetAttribute(a->GetName()) != a->GetValue()) {
diff --git a/xLights/models/CandyCaneModel.cpp b/xLights/models/CandyCaneModel.cpp
index 3219ec42d5..0ba18ab25c 100644
--- a/xLights/models/CandyCaneModel.cpp
+++ b/xLights/models/CandyCaneModel.cpp
@@ -112,10 +112,8 @@ int CandyCaneModel::OnPropertyGridChange(wxPropertyGridInterface *grid, wxProper
AddASAPWork(OutputModelManager::WORK_REDRAW_LAYOUTPREVIEW, "CandyCaneModel::OnPropertyGridChange::CandyCaneCount");
AddASAPWork(OutputModelManager::WORK_CALCULATE_START_CHANNELS, "CandyCaneModel::OnPropertyGridChange::CandyCaneCount");
AddASAPWork(OutputModelManager::WORK_MODELS_REWORK_STARTCHANNELS, "CandyCaneModel::OnPropertyGridChange::CandyCaneCount");
- AddASAPWork(OutputModelManager::WORK_UPDATE_PROPERTYGRID, "ArchesModel::OnPropertyGridChange::CandyCaneCount");
- if (ModelXml->GetAttribute("Advanced", "0") == "1") {
- AddASAPWork(OutputModelManager::WORK_RELOAD_PROPERTYGRID, "CandyCaneModel::OnPropertyGridChange::CandyCaneCount");
- }
+ AddASAPWork(OutputModelManager::WORK_UPDATE_PROPERTYGRID, "CandyCaneModel::OnPropertyGridChange::CandyCaneCount");
+ AddASAPWork(OutputModelManager::WORK_RELOAD_PROPERTYGRID, "CandyCaneModel::OnPropertyGridChange::CandyCaneCount");
return 0;
} else if ("CandyCaneNodes" == event.GetPropertyName()) {
ModelXml->DeleteAttribute("parm2");
@@ -211,21 +209,22 @@ bool CandyCaneModel::IsNodeFirst(int n) const
return (GetIsLtoR() && n == 0) || (!GetIsLtoR() && n == Nodes.size() - parm2);
}
+// Canes are 3 high per width of each indivudual cane, then multiply by 2 because standard ThreePointLocation applies a / 2 for heights
std::string CandyCaneModel::GetDimension() const
{
if (parm1 != 0) {
- return GetModelScreenLocation().GetDimension(3.0 / parm1);
+ return GetModelScreenLocation().GetDimension(6.0 / parm1);
}
- return GetModelScreenLocation().GetDimension(1.0);
+ return GetModelScreenLocation().GetDimension(6.0);
}
void CandyCaneModel::AddDimensionProperties(wxPropertyGridInterface* grid)
{
if (parm1 != 0) {
- GetModelScreenLocation().AddDimensionProperties(grid, 3.0 / parm1);
+ GetModelScreenLocation().AddDimensionProperties(grid, 6.0 / parm1);
}
else {
- GetModelScreenLocation().AddDimensionProperties(grid, 1.0);
+ GetModelScreenLocation().AddDimensionProperties(grid, 6.0);
}
}
diff --git a/xLights/models/CustomModel.cpp b/xLights/models/CustomModel.cpp
index 4723a6c79f..a009af4d4d 100644
--- a/xLights/models/CustomModel.cpp
+++ b/xLights/models/CustomModel.cpp
@@ -106,6 +106,8 @@ class CustomModelProperty : public wxStringProperty
void CustomModel::AddTypeProperties(wxPropertyGridInterface* grid, OutputManager* outputManager)
{
+ static log4cpp::Category& logger_base = log4cpp::Category::getInstance(std::string("log_base"));
+
wxPGProperty* p = grid->Append(new CustomModelProperty(this, outputManager, "Model Data", "CustomData", CLICK_TO_EDIT));
grid->LimitPropertyEditing(p);
@@ -132,7 +134,7 @@ void CustomModel::AddTypeProperties(wxPropertyGridInterface* grid, OutputManager
if (hasIndiv) {
int c = _strings;
- for (int x = 0; x < c; x++) {
+ for (int x = 0; x < c; ++x) {
nm = StartNodeAttrName(x);
std::string val = ModelXml->GetAttribute(nm, "").ToStdString();
if (val.empty()) {
@@ -156,10 +158,17 @@ void CustomModel::AddTypeProperties(wxPropertyGridInterface* grid, OutputManager
}
}
+ wxStopWatch sw;
p = grid->Append(new wxImageFileProperty("Background Image",
"CustomBkgImage",
custom_background));
- p->SetAttribute(wxPG_FILE_WILDCARD, "Image files|*.png;*.bmp;*.jpg;*.gif;*.jpeg|All files (*.*)|*.*");
+
+ if (sw.Time() > 500)
+ logger_base.debug(" Adding background image property (%s) to model %s really slow: %lums", (const char*)custom_background.c_str(), (const char*)name.c_str(), sw.Time());
+
+ p->SetAttribute(wxPG_FILE_WILDCARD, "Image files|*.png;*.bmp;*.jpg;*.gif;*.jpeg"
+ ";*.webp"
+ "|All files (*.*)|*.*");
}
int CustomModel::OnPropertyGridChange(wxPropertyGridInterface* grid, wxPropertyGridEvent& event)
@@ -282,6 +291,9 @@ void CustomModel::UpdateModel(int width, int height, int depth, const std::strin
void CustomModel::InitModel()
{
+ static log4cpp::Category& logger_base = log4cpp::Category::getInstance(std::string("log_base"));
+ wxStopWatch sw;
+
std::string customModel = ModelXml->GetAttribute("CustomModel").ToStdString();
InitCustomMatrix(customModel);
//CopyBufCoord2ScreenCoord();
@@ -294,6 +306,11 @@ void CustomModel::InitModel()
if (_depth > 1) {
screenLocation.SetPerspective2D(0.1f); // if i dont do this you cant see the back nodes in 2D
}
+
+ if (sw.Time() > 5)
+ {
+ logger_base.debug("Custom model %s took %lums to initialise.", (const char*)name.c_str(), sw.Time());
+ }
}
void CustomModel::SetCustomWidth(long w)
diff --git a/xLights/models/DMX/DmxImage.cpp b/xLights/models/DMX/DmxImage.cpp
index 5f2f5e5a16..eaad4344db 100644
--- a/xLights/models/DMX/DmxImage.cpp
+++ b/xLights/models/DMX/DmxImage.cpp
@@ -123,7 +123,9 @@ void DmxImage::AddTypeProperties(wxPropertyGridInterface *grid) {
grid->Append(new wxPropertyCategory(base_name, base_name + "Properties"));
wxPGProperty* prop = grid->Append(new wxImageFileProperty("Image", base_name + "Image", _imageFile));
- prop->SetAttribute(wxPG_FILE_WILDCARD, "Image files|*.png;*.bmp;*.jpg;*.gif;*.jpeg|All files (*.*)|*.*");
+ prop->SetAttribute(wxPG_FILE_WILDCARD, "Image files|*.png;*.bmp;*.jpg;*.gif;*.jpeg"
+ ";*.webp"
+ "|All files (*.*)|*.*");
prop = grid->Append(new wxFloatProperty("Offset X", base_name + "OffsetX", offset_x * OFFSET_SCALE));
prop->SetAttribute("Precision", 1);
diff --git a/xLights/models/ImageModel.cpp b/xLights/models/ImageModel.cpp
index 3f82a198db..50550acb8a 100644
--- a/xLights/models/ImageModel.cpp
+++ b/xLights/models/ImageModel.cpp
@@ -86,7 +86,9 @@ void ImageModel::AddTypeProperties(wxPropertyGridInterface* grid, OutputManager*
wxPGProperty *p = grid->Append(new wxImageFileProperty("Image",
"Image",
_imageFile));
- p->SetAttribute(wxPG_FILE_WILDCARD, "Image files|*.png;*.bmp;*.jpg;*.gif;*.jpeg|All files (*.*)|*.*");
+ p->SetAttribute(wxPG_FILE_WILDCARD, "Image files|*.png;*.bmp;*.jpg;*.gif;*.jpeg"
+ ";*.webp"
+ "|All files (*.*)|*.*");
p = grid->Append(new wxUIntProperty("Off Brightness", "OffBrightness", _offBrightness));
p->SetAttribute("Min", 0);
diff --git a/xLights/models/ImageObject.cpp b/xLights/models/ImageObject.cpp
index 277d8f36a3..79a09483aa 100644
--- a/xLights/models/ImageObject.cpp
+++ b/xLights/models/ImageObject.cpp
@@ -57,7 +57,9 @@ void ImageObject::AddTypeProperties(wxPropertyGridInterface* grid, OutputManager
wxPGProperty *p = grid->Append(new wxImageFileProperty("Image",
"Image",
_imageFile));
- p->SetAttribute(wxPG_FILE_WILDCARD, "Image files|*.png;*.bmp;*.jpg;*.gif;*.jpeg|All files (*.*)|*.*");
+ p->SetAttribute(wxPG_FILE_WILDCARD, "Image files|*.png;*.bmp;*.jpg;*.gif;*.jpeg"
+ ";*.webp"
+ "|All files (*.*)|*.*");
p = grid->Append(new wxUIntProperty("Transparency", "Transparency", transparency));
p->SetAttribute("Min", 0);
diff --git a/xLights/models/MatrixModel.cpp b/xLights/models/MatrixModel.cpp
index 915e304518..0a077bf720 100644
--- a/xLights/models/MatrixModel.cpp
+++ b/xLights/models/MatrixModel.cpp
@@ -97,13 +97,19 @@ void MatrixModel::AddStyleProperties(wxPropertyGridInterface *grid) {
grid->Append(new wxEnumProperty("Direction", "MatrixStyle", MATRIX_STYLES, vMatrix ? 1 : 0));
wxPGProperty *p = grid->Append(new wxBoolProperty("Alternate Nodes", "AlternateNodes", _alternateNodes));
p->SetEditor("CheckBox");
+ p->Enable(_noZig == false);
+
+ p = grid->Append(new wxBoolProperty("Don't Zig Zag", "NoZig", _noZig));
+ p->SetEditor("CheckBox");
+ p->Enable(_alternateNodes == false);
}
-int MatrixModel::OnPropertyGridChange(wxPropertyGridInterface *grid, wxPropertyGridEvent& event) {
+int MatrixModel::OnPropertyGridChange(wxPropertyGridInterface* grid, wxPropertyGridEvent& event)
+{
if ("MatrixStyle" == event.GetPropertyName()) {
ModelXml->DeleteAttribute("DisplayAs");
ModelXml->AddAttribute("DisplayAs", event.GetPropertyValue().GetLong() ? "Vert Matrix" : "Horiz Matrix");
- //AdjustStringProperties(grid, parm1);
+ // AdjustStringProperties(grid, parm1);
IncrementChangeCount();
AddASAPWork(OutputModelManager::WORK_RGBEFFECTS_CHANGE, "MatrixModel::OnPropertyGridChange::MatrixStyle");
AddASAPWork(OutputModelManager::WORK_MODELS_CHANGE_REQUIRING_RERENDER, "MatrixModel::OnPropertyGridChange::MatrixStyle");
@@ -113,7 +119,7 @@ int MatrixModel::OnPropertyGridChange(wxPropertyGridInterface *grid, wxPropertyG
} else if ("MatrixStringCount" == event.GetPropertyName()) {
ModelXml->DeleteAttribute("parm1");
ModelXml->AddAttribute("parm1", wxString::Format("%d", (int)event.GetPropertyValue().GetLong()));
- //AdjustStringProperties(grid, parm1);
+ // AdjustStringProperties(grid, parm1);
IncrementChangeCount();
AddASAPWork(OutputModelManager::WORK_RGBEFFECTS_CHANGE, "MatrixModel::OnPropertyGridChange::MatrixStringCount");
AddASAPWork(OutputModelManager::WORK_MODELS_CHANGE_REQUIRING_RERENDER, "MatrixModel::OnPropertyGridChange::MatrixStringCount");
@@ -163,6 +169,17 @@ int MatrixModel::OnPropertyGridChange(wxPropertyGridInterface *grid, wxPropertyG
AddASAPWork(OutputModelManager::WORK_MODELS_CHANGE_REQUIRING_RERENDER, "TreeModel::OnPropertyGridChange::AlternateNodes");
AddASAPWork(OutputModelManager::WORK_RELOAD_MODEL_FROM_XML, "TreeModel::OnPropertyGridChange::AlternateNodes");
AddASAPWork(OutputModelManager::WORK_REDRAW_LAYOUTPREVIEW, "TreeModel::OnPropertyGridChange::AlternateNodes");
+ grid->GetPropertyByName("NoZig")->Enable(event.GetPropertyValue().GetBool() == false);
+ return 0;
+ } else if (event.GetPropertyName() == "NoZig") {
+ ModelXml->DeleteAttribute("NoZig");
+ ModelXml->AddAttribute("NoZig", event.GetPropertyValue().GetBool() ? "true" : "false");
+ IncrementChangeCount();
+ AddASAPWork(OutputModelManager::WORK_RGBEFFECTS_CHANGE, "TreeModel::OnPropertyGridChange::NoZig");
+ AddASAPWork(OutputModelManager::WORK_MODELS_CHANGE_REQUIRING_RERENDER, "TreeModel::OnPropertyGridChange::NoZig");
+ AddASAPWork(OutputModelManager::WORK_RELOAD_MODEL_FROM_XML, "TreeModel::OnPropertyGridChange::NoZig");
+ AddASAPWork(OutputModelManager::WORK_REDRAW_LAYOUTPREVIEW, "TreeModel::OnPropertyGridChange::NoZig");
+ grid->GetPropertyByName("AlternateNodes")->Enable(event.GetPropertyValue().GetBool() == false);
return 0;
}
@@ -264,6 +281,7 @@ void MatrixModel::InitSingleChannelModel()
void MatrixModel::InitModel() {
_alternateNodes = (ModelXml->GetAttribute("AlternateNodes", "false") == "true");
+ _noZig = (ModelXml->GetAttribute("NoZig", "false") == "true");
if (DisplayAs == "Vert Matrix") {
InitVMatrix();
} else if (DisplayAs == "Horiz Matrix") {
@@ -371,7 +389,12 @@ void MatrixModel::InitVMatrix(int firstExportStrand)
}
}
} else {
- Nodes[idx]->Coords[0].bufY = isBotToTop == (segmentnum % 2 == 0) ? y : PixelsPerStrand - y - 1;
+ if (_noZig)
+ {
+ Nodes[idx]->Coords[0].bufY = isBotToTop == true ? y : PixelsPerStrand - y - 1;
+ } else {
+ Nodes[idx]->Coords[0].bufY = isBotToTop == (segmentnum % 2 == 0) ? y : PixelsPerStrand - y - 1;
+ }
}
}
}
@@ -405,7 +428,12 @@ void MatrixModel::InitVMatrix(int firstExportStrand)
}
}
} else {
- Nodes[idx]->Coords[0].bufY = isBotToTop == (segmentnum % 2 == 0) ? y : PixelsPerStrand - y - 1;
+ if (_noZig)
+ {
+ Nodes[idx]->Coords[0].bufY = isBotToTop == true ? y : PixelsPerStrand - y - 1;
+ } else {
+ Nodes[idx]->Coords[0].bufY = isBotToTop == (segmentnum % 2 == 0) ? y : PixelsPerStrand - y - 1;
+ }
}
// before we adjust the buffer capture the screen coordinates
for (size_t c = 0; c < GetCoordCount(idx); c++) {
@@ -504,7 +532,12 @@ void MatrixModel::InitHMatrix() {
}
}
} else {
- Nodes[idx]->Coords[0].bufX = IsLtoR != (segmentnum % 2 == 0) ? PixelsPerStrand - x - 1 : x;
+ if (_noZig)
+ {
+ Nodes[idx]->Coords[0].bufX = IsLtoR != true ? PixelsPerStrand - x - 1 : x;
+ } else {
+ Nodes[idx]->Coords[0].bufX = IsLtoR != (segmentnum % 2 == 0) ? PixelsPerStrand - x - 1 : x;
+ }
}
}
}
@@ -540,7 +573,12 @@ void MatrixModel::InitHMatrix() {
}
}
} else {
- Nodes[idx]->Coords[0].bufX = IsLtoR != (segmentnum % 2 == 0) ? PixelsPerStrand - x - 1 : x;
+ if (_noZig)
+ {
+ Nodes[idx]->Coords[0].bufX = IsLtoR != true ? PixelsPerStrand - x - 1 : x;
+ } else {
+ Nodes[idx]->Coords[0].bufX = IsLtoR != (segmentnum % 2 == 0) ? PixelsPerStrand - x - 1 : x;
+ }
}
// before we adjust the buffer capture the screen coordinates
for (size_t c = 0; c < GetCoordCount(idx); c++) {
@@ -579,6 +617,7 @@ void MatrixModel::ExportXlightsModel()
wxString nn = ModelXml->GetAttribute("NodeNames");
wxString da = ModelXml->GetAttribute("DisplayAs");
wxString an = ModelXml->GetAttribute("AlternateNodes", "false");
+ wxString nz = ModelXml->GetAttribute("NoZig", "false");
wxString ld = ModelXml->GetAttribute("LowDefinition", "100");
wxString v = xlights_version_string;
f.Write("\nGetAttribute("PixelType");
wxString psp = root->GetAttribute("PixelSpacing");
wxString an = root->GetAttribute("AlternateNodes");
+ wxString nz = root->GetAttribute("NoZig");
wxString ld = root->GetAttribute("LowDefinition", "100");
// generally xmodels dont have these ... but there are some cases where we do where it would point to a shadow model ... in those cases we want to bring it in
@@ -677,6 +718,7 @@ void MatrixModel::ImportXlightsModel(wxXmlNode* root, xLightsFrame* xlights, flo
SetProperty("PixelType", pt);
SetProperty("PixelSpacing", psp);
SetProperty("AlternateNodes", an);
+ SetProperty("NoZig", nz);
SetProperty("LowDefinition", ld);
if (smf != "") {
SetProperty("ShadowModelFor", smf);
diff --git a/xLights/models/MatrixModel.h b/xLights/models/MatrixModel.h
index a626bdbb88..53ca2a0a69 100644
--- a/xLights/models/MatrixModel.h
+++ b/xLights/models/MatrixModel.h
@@ -50,5 +50,6 @@ class MatrixModel : public ModelWithScreenLocation
bool vMatrix = false;
bool _alternateNodes = false;
+ bool _noZig = false;
private:
};
diff --git a/xLights/models/Model.cpp b/xLights/models/Model.cpp
index 9de68035a4..f7e0ac39a9 100644
--- a/xLights/models/Model.cpp
+++ b/xLights/models/Model.cpp
@@ -8,53 +8,54 @@
* License: https://github.com/smeighan/xLights/blob/master/License.txt
**************************************************************/
-#include
-#include
-#include
-#include
#include
+#include
#include
+#include
+#include
#include
+#include
+#include
#include
-#include
+#include "CachedFileDownloader.h"
+#include "CustomModel.h"
#include "Model.h"
-#include "ModelManager.h"
#include "ModelGroup.h"
-#include "../xLightsApp.h"
-#include "../xLightsMain.h" //for Preview and Other model collections
-#include "../xLightsXmlFile.h"
+#include "ModelManager.h"
+#include "ModelScreenLocation.h"
+#include "RulerObject.h"
+#include "SubModel.h"
#include "../Color.h"
#include "../DimmingCurve.h"
-#include "../StrandNodeNamesDialog.h"
-#include "../ModelFaceDialog.h"
-#include "../ModelStateDialog.h"
+#include "../EditAliasesDialog.h"
+#include "../ExternalHooks.h"
#include "../ModelChainDialog.h"
#include "../ModelDimmingCurveDialog.h"
+#include "../ModelFaceDialog.h"
+#include "../ModelPreview.h"
+#include "../ModelStateDialog.h"
+#include "../Pixels.h"
#include "../StartChannelDialog.h"
+#include "../StrandNodeNamesDialog.h"
#include "../SubModelsDialog.h"
-#include "../outputs/Output.h"
-#include "../outputs/OutputManager.h"
-#include "../outputs/IPOutput.h"
-#include "../outputs/Controller.h"
-#include "../outputs/ControllerSerial.h"
-#include "../VendorModelDialog.h"
-#include "../ModelPreview.h"
-#include "ModelScreenLocation.h"
-#include "SubModel.h"
#include "../UtilFunctions.h"
-#include "../xLightsVersion.h"
+#include "../VendorModelDialog.h"
#include "../controllers/ControllerCaps.h"
-#include "../Pixels.h"
-#include "../ExternalHooks.h"
-#include "CustomModel.h"
-#include "RulerObject.h"
+#include "../outputs/Controller.h"
+#include "../outputs/ControllerSerial.h"
+#include "../outputs/IPOutput.h"
+#include "../outputs/Output.h"
+#include "../outputs/OutputManager.h"
#include "../utils/ip_utils.h"
+#include "../xLightsApp.h"
+#include "../xLightsMain.h" //for Preview and Other model collections
+#include "../xLightsVersion.h"
+#include "../xLightsXmlFile.h"
#include
#include "../xSchedule/wxJSON/jsonreader.h"
-#include "CachedFileDownloader.h"
#include
@@ -72,24 +73,25 @@ static const char* NODE_TYPE_VLUES[] = {
"4 Channel RGBW", "4 Channel WRGB", "Strobes", "Single Color",
"Single Color Intensity", "Superstring", "WRGB Nodes", "WRBG Nodes",
"WGBR Nodes", "WGRB Nodes", "WBRG Nodes", "WBGR Nodes", "RGBW Nodes",
- "RBGW Nodes", "GBRW Nodes", "GRBW Nodes", "BRGW Nodes", "BGRW Nodes"
+ "RBGW Nodes", "GBRW Nodes", "GRBW Nodes", "BRGW Nodes", "BGRW Nodes"
};
static wxArrayString NODE_TYPES(26, NODE_TYPE_VLUES);
-static const char *RGBW_HANDLING_VALUES[] = {"R=G=B -> W", "RGB Only", "White Only", "Advanced", "White On All"};
+static const char* RGBW_HANDLING_VALUES[] = { "R=G=B -> W", "RGB Only", "White Only", "Advanced", "White On All" };
static wxArrayString RGBW_HANDLING(5, RGBW_HANDLING_VALUES);
-static const char *PIXEL_STYLES_VALUES[] = {"Square", "Smooth", "Solid Circle", "Blended Circle"};
+static const char* PIXEL_STYLES_VALUES[] = { "Square", "Smooth", "Solid Circle", "Blended Circle" };
static wxArrayString PIXEL_STYLES(4, PIXEL_STYLES_VALUES);
-static const char *CONTROLLER_DIRECTION_VALUES[] = {"Forward", "Reverse"};
+static const char* CONTROLLER_DIRECTION_VALUES[] = { "Forward", "Reverse" };
static wxArrayString CONTROLLER_DIRECTION(2, CONTROLLER_DIRECTION_VALUES);
-static const char *CONTROLLER_COLORORDER_VALUES[] = {
+static const char* CONTROLLER_COLORORDER_VALUES[] = {
"RGB", "RBG", "GBR", "GRB", "BRG", "BGR",
"RGBW", "RBGW", "GBRW", "GRBW", "BRGW", "BGRW",
- "WRGB", "WRBG", "WGBR", "WGRB", "WBRG", "WBGR"};
-static wxArrayString CONTROLLER_COLORORDER(18, CONTROLLER_COLORORDER_VALUES);
+ "WRGB", "WRBG", "WGBR", "WGRB", "WBRG", "WBGR"
+};
+wxArrayString Model::CONTROLLER_COLORORDER(18, CONTROLLER_COLORORDER_VALUES);
static wxArrayString LAYOUT_GROUPS;
static wxArrayString CONTROLLERS;
@@ -104,7 +106,7 @@ static const std::string HORIZ_PER_STRAND("Horizontal Per Strand");
static const std::string PER_PREVIEW_NO_OFFSET("Per Preview No Offset");
-const std::vector Model::DEFAULT_BUFFER_STYLES {DEFAULT, PER_PREVIEW, SINGLE_LINE, AS_PIXEL};
+const std::vector Model::DEFAULT_BUFFER_STYLES{ DEFAULT, PER_PREVIEW, SINGLE_LINE, AS_PIXEL };
static void clearUnusedProtocolProperties(wxXmlNode* node)
{
@@ -134,8 +136,8 @@ static const std::string MODEL_PREVIEW_CACHE_3D("ModelPreviewCache2D");
static const std::string LAYOUT_PREVIEW_CACHE_2D("LayoutPreviewCache3D");
static const std::string LAYOUT_PREVIEW_CACHE_3D("LayoutPreviewCache2D");
-
-Model::Model(const ModelManager& manager) : modelManager(manager)
+Model::Model(const ModelManager& manager) :
+ modelManager(manager)
{
}
@@ -155,17 +157,17 @@ static const std::string CLICK_TO_EDIT("--Click To Edit--");
class StrandNodeNamesDialogAdapter : public wxPGEditorDialogAdapter
{
public:
- StrandNodeNamesDialogAdapter(const Model* model)
- : wxPGEditorDialogAdapter(), m_model(model)
+ StrandNodeNamesDialogAdapter(const Model* model) :
+ wxPGEditorDialogAdapter(), m_model(model)
{
}
virtual bool DoShowDialog(wxPropertyGrid* propGrid,
- wxPGProperty* WXUNUSED(property)) override
+ wxPGProperty* WXUNUSED(property)) override
{
StrandNodeNamesDialog dlg(propGrid);
dlg.Setup(m_model,
- m_model->GetModelXml()->GetAttribute("NodeNames").ToStdString(),
- m_model->GetModelXml()->GetAttribute("StrandNames").ToStdString());
+ m_model->GetModelXml()->GetAttribute("NodeNames").ToStdString(),
+ m_model->GetModelXml()->GetAttribute("StrandNames").ToStdString());
if (dlg.ShowModal() == wxID_OK) {
m_model->GetModelXml()->DeleteAttribute("NodeNames");
m_model->GetModelXml()->DeleteAttribute("StrandNames");
@@ -177,6 +179,7 @@ class StrandNodeNamesDialogAdapter : public wxPGEditorDialogAdapter
}
return false;
}
+
protected:
const Model* m_model;
};
@@ -189,7 +192,7 @@ class FacesDialogAdapter : public wxPGEditorDialogAdapter
{
}
virtual bool DoShowDialog(wxPropertyGrid* propGrid,
- wxPGProperty* WXUNUSED(property)) override
+ wxPGProperty* WXUNUSED(property)) override
{
ModelFaceDialog dlg(propGrid, _outputManager);
dlg.SetFaceInfo(m_model, m_model->faceInfo);
@@ -202,20 +205,44 @@ class FacesDialogAdapter : public wxPGEditorDialogAdapter
}
return false;
}
+
protected:
Model* m_model = nullptr;
OutputManager* _outputManager = nullptr;
};
+class AliasDialogAdapter : public wxPGEditorDialogAdapter
+{
+public:
+ AliasDialogAdapter(Model* model) :
+ wxPGEditorDialogAdapter(), m_model(model)
+ {
+ }
+ virtual bool DoShowDialog(wxPropertyGrid* propGrid,
+ wxPGProperty* WXUNUSED(property)) override
+ {
+ EditAliasesDialog dlg(propGrid, m_model);
+ if (dlg.ShowModal() == wxID_OK) {
+ wxVariant v(CLICK_TO_EDIT);
+ SetValue(v);
+ return true;
+ }
+ return false;
+ }
+
+protected:
+ Model* m_model = nullptr;
+};
+
class StatesDialogAdapter : public wxPGEditorDialogAdapter
{
public:
- StatesDialogAdapter(Model* model, OutputManager* om)
- : wxPGEditorDialogAdapter(), m_model(model), _outputManager(om)
+ StatesDialogAdapter(Model* model, OutputManager* om) :
+ wxPGEditorDialogAdapter(), m_model(model), _outputManager(om)
{
}
virtual bool DoShowDialog(wxPropertyGrid* propGrid,
- wxPGProperty* WXUNUSED(property)) override
+ wxPGProperty* WXUNUSED(property)) override
{
ModelStateDialog dlg(propGrid, _outputManager);
dlg.SetStateInfo(m_model, m_model->stateInfo);
@@ -228,14 +255,15 @@ class StatesDialogAdapter : public wxPGEditorDialogAdapter
}
return false;
}
+
protected:
Model* m_model = nullptr;
OutputManager* _outputManager = nullptr;
};
-std::map > Model::GetDimmingInfo() const
+std::map> Model::GetDimmingInfo() const
{
- std::map > dimmingInfo;
+ std::map> dimmingInfo;
wxXmlNode* f = GetModelXml()->GetChildren();
while (f != nullptr) {
if ("dimmingCurve" == f->GetName()) {
@@ -259,15 +287,17 @@ std::map > Model::GetDimmingInfo
class DimmingCurveDialogAdapter : public wxPGEditorDialogAdapter
{
public:
- DimmingCurveDialogAdapter(const Model *model)
- : wxPGEditorDialogAdapter(), m_model(model) {
+ DimmingCurveDialogAdapter(const Model* model) :
+ wxPGEditorDialogAdapter(), m_model(model)
+ {
}
virtual bool DoShowDialog(wxPropertyGrid* propGrid,
- wxPGProperty* WXUNUSED(property) ) override {
+ wxPGProperty* WXUNUSED(property)) override
+ {
ModelDimmingCurveDialog dlg(propGrid);
auto dimmingInfo = m_model->GetDimmingInfo();
- if(dimmingInfo.empty()) {
- wxString b = m_model->GetModelXml()->GetAttribute("ModelBrightness","0");
+ if (dimmingInfo.empty()) {
+ wxString b = m_model->GetModelXml()->GetAttribute("ModelBrightness", "0");
if (b.empty()) {
b = "0";
}
@@ -278,7 +308,7 @@ class DimmingCurveDialogAdapter : public wxPGEditorDialogAdapter
if (dlg.ShowModal() == wxID_OK) {
dimmingInfo.clear();
dlg.Update(dimmingInfo);
- wxXmlNode *f1 = m_model->GetModelXml()->GetChildren();
+ wxXmlNode* f1 = m_model->GetModelXml()->GetChildren();
while (f1 != nullptr) {
if ("dimmingCurve" == f1->GetName()) {
m_model->GetModelXml()->RemoveChild(f1);
@@ -288,10 +318,10 @@ class DimmingCurveDialogAdapter : public wxPGEditorDialogAdapter
f1 = f1->GetNext();
}
}
- f1 = new wxXmlNode(wxXML_ELEMENT_NODE , "dimmingCurve");
+ f1 = new wxXmlNode(wxXML_ELEMENT_NODE, "dimmingCurve");
m_model->GetModelXml()->AddChild(f1);
for (const auto& it : dimmingInfo) {
- wxXmlNode *dc = new wxXmlNode(wxXML_ELEMENT_NODE , it.first);
+ wxXmlNode* dc = new wxXmlNode(wxXML_ELEMENT_NODE, it.first);
f1->AddChild(dc);
for (const auto& it2 : it.second) {
dc->AddAttribute(it2.first, it2.second);
@@ -303,18 +333,20 @@ class DimmingCurveDialogAdapter : public wxPGEditorDialogAdapter
}
return false;
}
+
protected:
- const Model *m_model;
+ const Model* m_model;
};
class SubModelsDialogAdapter : public wxPGEditorDialogAdapter
{
public:
- SubModelsDialogAdapter(Model *model, OutputManager* om) :
+ SubModelsDialogAdapter(Model* model, OutputManager* om) :
wxPGEditorDialogAdapter(), m_model(model), _outputManager(om)
{
}
virtual bool DoShowDialog(wxPropertyGrid* propGrid,
- wxPGProperty* WXUNUSED(property) ) override {
+ wxPGProperty* WXUNUSED(property)) override
+ {
SubModelsDialog dlg(propGrid, _outputManager);
dlg.Setup(m_model);
if (dlg.ShowModal() == wxID_OK) {
@@ -323,7 +355,7 @@ class SubModelsDialogAdapter : public wxPGEditorDialogAdapter
SetValue(v);
return true;
}
- if (dlg.ReloadLayout) {//force grid to reload
+ if (dlg.ReloadLayout) { // force grid to reload
wxCommandEvent eventForceRefresh(EVT_FORCE_SEQUENCER_REFRESH);
wxPostEvent(m_model->GetModelManager().GetXLightsFrame(), eventForceRefresh);
m_model->AddASAPWork(OutputModelManager::WORK_RELOAD_ALLMODELS, "Model::SubModelsDialog::SubModels");
@@ -332,19 +364,22 @@ class SubModelsDialogAdapter : public wxPGEditorDialogAdapter
}
return false;
}
+
protected:
- Model *m_model = nullptr;
+ Model* m_model = nullptr;
OutputManager* _outputManager = nullptr;
};
class ModelChainDialogAdapter : public wxPGEditorDialogAdapter
{
public:
- ModelChainDialogAdapter(Model *model)
- : wxPGEditorDialogAdapter(), m_model(model) {
+ ModelChainDialogAdapter(Model* model) :
+ wxPGEditorDialogAdapter(), m_model(model)
+ {
}
virtual bool DoShowDialog(wxPropertyGrid* propGrid,
- wxPGProperty* property) override {
+ wxPGProperty* property) override
+ {
ModelChainDialog dlg(propGrid);
dlg.Set(m_model, m_model->GetModelManager());
if (dlg.ShowModal() == wxID_OK) {
@@ -354,14 +389,15 @@ class ModelChainDialogAdapter : public wxPGEditorDialogAdapter
}
return false;
}
+
protected:
- Model *m_model;
+ Model* m_model = nullptr;
};
class PopupDialogProperty : public wxStringProperty
{
public:
- PopupDialogProperty(Model *m,
+ PopupDialogProperty(Model* m,
OutputManager* om,
const wxString& label,
const wxString& name,
@@ -371,11 +407,13 @@ class PopupDialogProperty : public wxStringProperty
{
}
// Set editor to have button
- virtual const wxPGEditor* DoGetEditorClass() const override {
+ virtual const wxPGEditor* DoGetEditorClass() const override
+ {
return wxPGEditor_TextCtrlAndButton;
}
// Set what happens on button click
- virtual wxPGEditorDialogAdapter* GetEditorDialog() const override {
+ virtual wxPGEditorDialogAdapter* GetEditorDialog() const override
+ {
switch (m_tp) {
case 1:
return new StrandNodeNamesDialogAdapter(m_model);
@@ -387,25 +425,30 @@ class PopupDialogProperty : public wxStringProperty
return new StatesDialogAdapter(m_model, _outputManager);
case 5:
return new SubModelsDialogAdapter(m_model, _outputManager);
+ case 6:
+ return new AliasDialogAdapter(m_model);
default:
break;
}
return nullptr;
}
+
protected:
- Model *m_model = nullptr;
+ Model* m_model = nullptr;
OutputManager* _outputManager = nullptr;
- int m_tp;
+ int m_tp = 0;
};
class StartChannelDialogAdapter : public wxPGEditorDialogAdapter
{
public:
- StartChannelDialogAdapter(Model *model, std::string preview)
- : wxPGEditorDialogAdapter(), m_model(model), _preview(preview) {
+ StartChannelDialogAdapter(Model* model, std::string preview) :
+ wxPGEditorDialogAdapter(), m_model(model), _preview(preview)
+ {
}
virtual bool DoShowDialog(wxPropertyGrid* propGrid,
- wxPGProperty* property) override {
+ wxPGProperty* property) override
+ {
StartChannelDialog dlg(propGrid);
dlg.Set(property->GetValue().GetString(), m_model->GetModelManager(), _preview, m_model);
if (dlg.ShowModal() == wxID_OK) {
@@ -415,8 +458,9 @@ class StartChannelDialogAdapter : public wxPGEditorDialogAdapter
}
return false;
}
+
protected:
- Model *m_model = nullptr;
+ Model* m_model = nullptr;
std::string _preview;
};
@@ -454,22 +498,26 @@ class StartChannelProperty : public wxStringProperty
class ModelChainProperty : public wxStringProperty
{
public:
- ModelChainProperty(Model *m,
- const wxString& label,
- const wxString& name,
- const wxString& value)
- : wxStringProperty(label, name, value), m_model(m) {
+ ModelChainProperty(Model* m,
+ const wxString& label,
+ const wxString& name,
+ const wxString& value) :
+ wxStringProperty(label, name, value), m_model(m)
+ {
}
// Set editor to have button
- virtual const wxPGEditor* DoGetEditorClass() const override {
+ virtual const wxPGEditor* DoGetEditorClass() const override
+ {
return wxPGEditor_TextCtrlAndButton;
}
// Set what happens on button click
- virtual wxPGEditorDialogAdapter* GetEditorDialog() const override {
+ virtual wxPGEditorDialogAdapter* GetEditorDialog() const override
+ {
return new ModelChainDialogAdapter(m_model);
}
+
protected:
- Model *m_model = nullptr;
+ Model* m_model = nullptr;
};
wxArrayString Model::GetLayoutGroups(const ModelManager& mm)
@@ -480,13 +528,10 @@ wxArrayString Model::GetLayoutGroups(const ModelManager& mm)
lg.push_back("Unassigned");
wxXmlNode* layouts_node = mm.GetLayoutsNode();
- for (wxXmlNode* e = layouts_node->GetChildren(); e != nullptr; e = e->GetNext())
- {
- if (e->GetName() == "layoutGroup")
- {
+ for (wxXmlNode* e = layouts_node->GetChildren(); e != nullptr; e = e->GetNext()) {
+ if (e->GetName() == "layoutGroup") {
wxString grp_name = e->GetAttribute("name");
- if (!grp_name.IsEmpty())
- {
+ if (!grp_name.IsEmpty()) {
lg.push_back(grp_name.ToStdString());
}
}
@@ -495,16 +540,27 @@ wxArrayString Model::GetLayoutGroups(const ModelManager& mm)
return lg;
}
-void Model::Rename(std::string const& newName) {
+void Model::Rename(std::string const& newName)
+{
+ auto oldname = ModelXml->GetAttribute("name", "");
+
name = Trim(newName);
ModelXml->DeleteAttribute("name");
ModelXml->AddAttribute("name", name);
+
+ if (oldname != "") {
+ if (wxMessageBox("Would you like to save the old name as an alias for this prop. This could be useful if you have sequences already sequenced against this prop using the old name.", "Save old name as alias", wxYES_NO | wxICON_QUESTION, GetModelManager().GetXLightsFrame()) == wxYES) {
+ AddAlias("oldname:" + oldname);
+ }
+ }
}
-void Model::SetStartChannel(std::string const& startChannel) {
- //wxASSERT(!StartsWith(startChannel, "!:"));
+void Model::SetStartChannel(std::string const& startChannel)
+{
+ // wxASSERT(!StartsWith(startChannel, "!:"));
- if (startChannel == ModelXml->GetAttribute("StartChannel", "xyzzy_kw")) return;
+ if (startChannel == ModelXml->GetAttribute("StartChannel", "xyzzy_kw"))
+ return;
ModelXml->DeleteAttribute("StartChannel");
ModelXml->AddAttribute("StartChannel", startChannel);
@@ -517,25 +573,22 @@ void Model::SetStartChannel(std::string const& startChannel) {
IncrementChangeCount();
}
-void Model::SetProperty(wxString const& property, wxString const& value, bool apply) {
- if (ModelXml->HasAttribute(property))
- {
+void Model::SetProperty(wxString const& property, wxString const& value, bool apply)
+{
+ if (ModelXml->HasAttribute(property)) {
ModelXml->DeleteAttribute(property);
ModelXml->AddAttribute(property, value);
- }
- else
- {
+ } else {
ModelXml->AddAttribute(property, value);
}
- if (apply)
- {
+ if (apply) {
modelManager.GetXLightsFrame()->AbortRender();
SetFromXml(ModelXml);
}
}
-void Model::UpdateProperties(wxPropertyGridInterface* grid, OutputManager* outputManager) {
-
+void Model::UpdateProperties(wxPropertyGridInterface* grid, OutputManager* outputManager)
+{
UpdateTypeProperties(grid);
if (grid->GetPropertyByName("Controller") != nullptr) {
@@ -544,14 +597,12 @@ void Model::UpdateProperties(wxPropertyGridInterface* grid, OutputManager* outpu
if (HasOneString(DisplayAs) && grid->GetPropertyByName("ModelStartChannel") != nullptr) {
grid->GetPropertyByName("ModelStartChannel")->Enable(GetControllerName() == "" || _controller == 0);
- }
- else {
+ } else {
if (grid->GetPropertyByName("ModelIndividualStartChannels") != nullptr) {
grid->GetPropertyByName("ModelIndividualStartChannels")->Enable(parm1 > 1 && (GetControllerName() == "" || _controller == 0));
if (parm1 > 1 && (GetControllerName() != "" && _controller != 0)) {
grid->GetPropertyByName("ModelIndividualStartChannels")->SetHelpString("Individual start channels cannot be set if you have assigned a model to a controller rather than using start channels.");
- }
- else {
+ } else {
grid->GetPropertyByName("ModelIndividualStartChannels")->SetHelpString("");
}
}
@@ -574,12 +625,10 @@ void Model::UpdateProperties(wxPropertyGridInterface* grid, OutputManager* outpu
}
if (NODE_TYPES[i] == "Node Single Color") {
grid->GetPropertyByName("ModelStringType")->SetHelpString("This represents a string of single color LEDS which are individually controlled. These are very uncommon.");
- }
- else {
+ } else {
grid->GetPropertyByName("ModelStringType")->SetHelpString("");
}
- }
- else {
+ } else {
if (grid->GetPropertyByName("ModelStringColor") != nullptr) {
grid->GetPropertyByName("ModelStringColor")->Enable(false);
}
@@ -593,7 +642,8 @@ void Model::UpdateProperties(wxPropertyGridInterface* grid, OutputManager* outpu
void Model::ColourClashingChains(wxPGProperty* p)
{
- if (p == nullptr) return;
+ if (p == nullptr)
+ return;
std::string tip;
if (GetControllerName() != "" && _controller != 0 && GetControllerProtocol() != "" && GetControllerPort() != 0 && p->IsEnabled()) {
@@ -614,7 +664,7 @@ uint32_t Model::ApplyLowDefinition(uint32_t val) const
{
if (_lowDefFactor == 100 || !SupportsLowDefinitionRender() || !GetModelManager().GetXLightsFrame()->IsLowDefinitionRender())
return val;
- return (val * _lowDefFactor) / 100;
+ return (val * _lowDefFactor) / 100;
}
std::string Model::GetPixelStyleDescription(PIXEL_STYLE pixelStyle)
@@ -630,10 +680,10 @@ int Model::GetNumPhysicalStrings() const
int ts = GetSmartTs();
if (ts <= 1) {
return parm1;
- }
- else {
+ } else {
int strings = parm1 / ts;
- if (strings == 0) strings = 1;
+ if (strings == 0)
+ strings = 1;
return strings;
}
}
@@ -641,7 +691,8 @@ int Model::GetNumPhysicalStrings() const
ControllerCaps* Model::GetControllerCaps() const
{
auto c = GetController();
- if (c == nullptr) return nullptr;
+ if (c == nullptr)
+ return nullptr;
return c->GetControllerCaps();
}
@@ -655,18 +706,109 @@ Controller* Model::GetController() const
}
if (controller == "") {
int32_t start;
- Controller *cp = modelManager.GetXLightsFrame()->GetOutputManager()->GetController(GetFirstChannel() + 1, start);
+ Controller* cp = modelManager.GetXLightsFrame()->GetOutputManager()->GetController(GetFirstChannel() + 1, start);
if (cp != nullptr) {
return cp;
}
}
- if (controller == "") return nullptr;
+ if (controller == "")
+ return nullptr;
return modelManager.GetXLightsFrame()->GetOutputManager()->GetController(controller);
}
+bool Model::IsAlias(const std::string& alias, bool oldnameOnly) const
+{
+ for (auto x = ModelXml->GetChildren(); x != nullptr; x = x->GetNext()) {
+ if (x->GetName() == "Aliases") {
+ for (auto xx = x->GetChildren(); xx != nullptr; xx = xx->GetNext()) {
+ if ((!oldnameOnly && Lower(alias) == xx->GetAttribute("name").Lower()) || Lower("oldname:" + alias) == xx->GetAttribute("name").Lower()) {
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+}
+
+void Model::AddAlias(const std::string& alias)
+{
+ if (IsAlias(alias))
+ return;
+ for (auto x = ModelXml->GetChildren(); x != nullptr; x = x->GetNext()) {
+ if (x->GetName() == "Aliases") {
+ auto n = new wxXmlNode(wxXmlNodeType::wxXML_ELEMENT_NODE, "alias");
+ n->AddAttribute("name", Lower(alias));
+ x->AddChild(n);
+ IncrementChangeCount();
+ return;
+ }
+ }
+ auto aliases = new wxXmlNode(wxXmlNodeType::wxXML_ELEMENT_NODE, "Aliases");
+ auto n = new wxXmlNode(wxXmlNodeType::wxXML_ELEMENT_NODE, "alias");
+ n->AddAttribute("name", Lower(alias));
+ aliases->AddChild(n);
+ IncrementChangeCount();
+
+ ModelXml->AddChild(aliases);
+}
+
+void Model::DeleteAlias(const std::string& alias)
+{
+ while (IsAlias(alias)) { // while should not be required ... but just in case it ever ends up there more than once
+ for (auto x = ModelXml->GetChildren(); x != nullptr; x = x->GetNext()) {
+ if (x->GetName() == "Aliases") {
+ for (auto xx = x->GetChildren(); xx != nullptr; xx = xx->GetNext()) {
+ if (Lower(alias) == xx->GetContent().Lower()) {
+ x->RemoveChild(xx);
+ IncrementChangeCount();
+ return;
+ }
+ }
+ }
+ }
+ }
+}
+
+std::list Model::GetAliases() const
+{
+ std::list aliases;
+
+ for (auto x = ModelXml->GetChildren(); x != nullptr; x = x->GetNext()) {
+ if (x->GetName() == "Aliases") {
+ for (auto xx = x->GetChildren(); xx != nullptr; xx = xx->GetNext()) {
+ aliases.push_back(xx->GetAttribute("name"));
+ }
+ }
+ }
+
+ return aliases;
+}
+
+void Model::SetAliases(std::list& aliases)
+{
+ AddAlias("dummy"); // this ensures the owning element exists
+
+ for (auto x = ModelXml->GetChildren(); x != nullptr; x = x->GetNext()) {
+ if (x->GetName() == "Aliases") {
+ while (x->GetChildren() != nullptr)
+ x->RemoveChild(x->GetChildren());
+
+ for (const auto& it : aliases) {
+ auto n = new wxXmlNode(wxXmlNodeType::wxXML_ELEMENT_NODE, "alias");
+ n->AddAttribute("name", Lower(it));
+ x->AddChild(n);
+ }
+ }
+ }
+ IncrementChangeCount();
+}
+
void Model::AddProperties(wxPropertyGridInterface* grid, OutputManager* outputManager)
{
+ static log4cpp::Category& logger_base = log4cpp::Category::getInstance(std::string("log_base"));
+ wxStopWatch sw;
+
LAYOUT_GROUPS = Model::GetLayoutGroups(modelManager);
wxPGProperty* sp;
@@ -707,16 +849,14 @@ void Model::AddProperties(wxPropertyGridInterface* grid, OutputManager* outputMa
if (HasOneString(DisplayAs)) {
p = grid->Append(new StartChannelProperty(this, 0, "Start Channel", "ModelStartChannel", ModelXml->GetAttribute("StartChannel", "1"), modelManager.GetXLightsFrame()->GetSelectedLayoutPanelPreview()));
p->Enable(GetControllerName() == "" || _controller == 0);
- }
- else {
+ } else {
bool hasIndiv = ModelXml->GetAttribute("Advanced", "0") == "1";
p = grid->Append(new wxBoolProperty("Indiv Start Chans", "ModelIndividualStartChannels", hasIndiv));
p->SetAttribute("UseCheckbox", true);
p->Enable(parm1 > 1 && (GetControllerName() == "" || _controller == 0));
if (parm1 > 1 && (GetControllerName() != "" && _controller != 0)) {
p->SetHelpString("Individual start channels cannot be set if you have assigned a model to a controller rather than using start channels.");
- }
- else {
+ } else {
p->SetHelpString("");
}
sp = grid->AppendIn(p, new StartChannelProperty(this, 0, "Start Channel", "ModelStartChannel", ModelXml->GetAttribute("StartChannel", "1"), modelManager.GetXLightsFrame()->GetSelectedLayoutPanelPreview()));
@@ -734,13 +874,11 @@ void Model::AddProperties(wxPropertyGridInterface* grid, OutputManager* outputMa
if (x == 0) {
sp->SetLabel(nm);
sp->SetValue(val);
- }
- else {
+ } else {
sp = grid->AppendIn(p, new StartChannelProperty(this, x, nm, nm, val, modelManager.GetXLightsFrame()->GetSelectedLayoutPanelPreview()));
}
}
- }
- else {
+ } else {
// remove per strand start channels if individual isnt selected
for (uint32_t x = 0; x < MOST_STRINGS_WE_EXPECT; ++x) {
wxString nm = StartChanAttrName(x);
@@ -772,7 +910,7 @@ void Model::AddProperties(wxPropertyGridInterface* grid, OutputManager* outputMa
grid->Append(new wxEnumProperty("Shadow Model For", "ShadowModelFor", OTHERMODELLIST, wxArrayInt(), shadowModelFor));
int layout_group_number = 0;
- for (int grp = 0; grp < LAYOUT_GROUPS.Count(); grp++) {
+ for (int grp = 0; grp < LAYOUT_GROUPS.Count(); ++grp) {
if (LAYOUT_GROUPS[grp] == layout_group) {
layout_group_number = grp;
break;
@@ -792,6 +930,9 @@ void Model::AddProperties(wxPropertyGridInterface* grid, OutputManager* outputMa
grid->LimitPropertyEditing(p);
p = grid->Append(new PopupDialogProperty(this, outputManager, "SubModels", "SubModels", CLICK_TO_EDIT, 5));
grid->LimitPropertyEditing(p);
+ p = grid->Append(new PopupDialogProperty(this, outputManager, "Aliases", "Aliases", CLICK_TO_EDIT, 6));
+ grid->LimitPropertyEditing(p);
+ p->SetHelpString("Aliases are used in mapping to provide alternate names for this model which might match a model in a sequence you are importing from. To use it use the Auto Map button.");
auto modelGroups = modelManager.GetGroupsContainingModel(this);
if (modelGroups.size() > 0) {
@@ -820,32 +961,26 @@ void Model::AddProperties(wxPropertyGridInterface* grid, OutputManager* outputMa
}
grid->AppendIn(p, new wxEnumProperty("String Type", "ModelStringType", NODE_TYPES, wxArrayInt(), i));
if (NODE_TYPES[i] == "Single Color" || NODE_TYPES[i] == "Single Color Intensity" || NODE_TYPES[i] == "Node Single Color") {
- //get the color
+ // get the color
wxColor v;
if (StringType == "Single Color Red") {
v = *wxRED;
- }
- else if (StringType == "Single Color Green" || StringType == "G") {
+ } else if (StringType == "Single Color Green" || StringType == "G") {
v = *wxGREEN;
- }
- else if (StringType == "Single Color Blue" || StringType == "B") {
+ } else if (StringType == "Single Color Blue" || StringType == "B") {
v = *wxBLUE;
- }
- else if (StringType == "Single Color White" || StringType == "W") {
+ } else if (StringType == "Single Color White" || StringType == "W") {
v = *wxWHITE;
- }
- else if (StringType == "Single Color Custom" || StringType == "Single Color Intensity" || StringType == "Node Single Color") {
+ } else if (StringType == "Single Color Custom" || StringType == "Single Color Intensity" || StringType == "Node Single Color") {
v = customColor.asWxColor();
- }
- else if (StringType[0] == '#') {
+ } else if (StringType[0] == '#') {
v = xlColor(StringType).asWxColor();
}
grid->AppendIn(p, new wxColourProperty("Color", "ModelStringColor", v));
if (NODE_TYPES[i] == "Node Single Color") {
grid->GetPropertyByName("ModelStringType")->SetHelpString("This represents a string of single color LEDS which are individually controlled. These are very uncommon.");
}
- }
- else if (NODE_TYPES[i] == "Superstring") {
+ } else if (NODE_TYPES[i] == "Superstring") {
if (superStringColours.size() == 0) {
superStringColours.push_back(xlRED);
SaveSuperStringColours();
@@ -857,8 +992,7 @@ void Model::AddProperties(wxPropertyGridInterface* grid, OutputManager* outputMa
for (int i = 0; i < superStringColours.size(); ++i) {
grid->AppendIn(p, new wxColourProperty(wxString::Format("Colour %d", i + 1), wxString::Format("SuperStringColour%d", i), superStringColours[i].asWxColor()));
}
- }
- else {
+ } else {
sp = grid->AppendIn(p, new wxColourProperty("Color", "ModelStringColor", *wxRED));
sp->Enable(false);
}
@@ -887,12 +1021,16 @@ void Model::AddProperties(wxPropertyGridInterface* grid, OutputManager* outputMa
grid->AppendIn(p, new wxColourProperty("Tag Color", "ModelTagColour", modelTagColour));
UpdateControllerProperties(grid);
DisableUnusedProperties(grid);
+
+ if (sw.Time() > 500)
+ logger_base.debug(" Model::AddProperties took %lums", sw.Time());
}
void Model::ClearIndividualStartChannels()
{
// dont clear custom models
- if (IsCustom()) return;
+ if (IsCustom())
+ return;
ModelXml->DeleteAttribute("Advanced");
// remove per strand start channels if individual isnt selected
@@ -905,10 +1043,10 @@ void Model::ClearIndividualStartChannels()
void Model::GetControllerProtocols(wxArrayString& cp, int& idx)
{
auto caps = GetControllerCaps();
- Controller *c = GetController();
+ Controller* c = GetController();
wxString protocol = GetControllerProtocol();
if (c) {
- ControllerSerial *cs = dynamic_cast(c);
+ ControllerSerial* cs = dynamic_cast(c);
if (cs) {
wxString cprotocol = cs->GetProtocol();
if (cprotocol != protocol) {
@@ -947,7 +1085,8 @@ void Model::GetControllerProtocols(wxArrayString& cp, int& idx)
np = ChooseBestControllerSerial(controllerProtocols, protocol);
}
}
- if (protocol != np) SetControllerProtocol(np);
+ if (protocol != np)
+ SetControllerProtocol(np);
}
// now work out the index
@@ -964,8 +1103,8 @@ void Model::GetControllerProtocols(wxArrayString& cp, int& idx)
wxArrayString Model::GetSmartRemoteValues(int smartRemoteCount)
{
wxArrayString res;
- //res.push_back("None");
- for (int i = 0; i < smartRemoteCount; i++) {
+ // res.push_back("None");
+ for (int i = 0; i < smartRemoteCount; ++i) {
res.push_back(wxString((char)(65 + i)));
}
return res;
@@ -973,7 +1112,6 @@ wxArrayString Model::GetSmartRemoteValues(int smartRemoteCount)
void Model::AddControllerProperties(wxPropertyGridInterface* grid)
{
-
auto caps = GetControllerCaps();
wxString protocol = GetControllerProtocol();
@@ -1008,8 +1146,7 @@ void Model::AddControllerProperties(wxPropertyGridInterface* grid)
if (caps != nullptr) {
smartRemoteCount = caps->GetSmartRemoteCount();
}
- if (smartRemoteCount != 0)
- {
+ if (smartRemoteCount != 0) {
int sr = GetSmartRemote();
sp = grid->AppendIn(p, new wxBoolProperty("Use Smart Remote", "UseSmartRemote", sr));
@@ -1025,8 +1162,7 @@ void Model::AddControllerProperties(wxPropertyGridInterface* grid)
srlist.Add(typ);
}
grid->AppendIn(p, new wxEnumProperty("Smart Remote Type", "SmartRemoteType", srlist, wxArrayInt(), GetSmartRemoteTypeIndex(GetSmartRemoteType())));
- }
- else {
+ } else {
std::string type = GetSmartRemoteType();
auto smt = grid->AppendIn(p, new wxStringProperty("Smart Remote Type", "SmartRemoteType", type));
smt->ChangeFlag(wxPG_PROP_READONLY, true);
@@ -1035,10 +1171,9 @@ void Model::AddControllerProperties(wxPropertyGridInterface* grid)
}
wxArrayString srv = GetSmartRemoteValues(smartRemoteCount);
- grid->AppendIn(p, new wxEnumProperty("Smart Remote", "SmartRemote", srv, wxArrayInt(), sr-1));
+ grid->AppendIn(p, new wxEnumProperty("Smart Remote", "SmartRemote", srv, wxArrayInt(), sr - 1));
- if (GetNumPhysicalStrings() > 1 )
- {
+ if (GetNumPhysicalStrings() > 1) {
sp = grid->AppendIn(p, new wxUIntProperty("Max Cascade Remotes", "MaxCascadeRemotes", GetSRMaxCascade()));
sp->SetAttribute("Min", 1);
sp->SetAttribute("Max", smartRemoteCount);
@@ -1067,18 +1202,16 @@ void Model::AddControllerProperties(wxPropertyGridInterface* grid)
sp->SetAttribute("Min", 1);
if (caps == nullptr) {
sp->SetAttribute("Max", 512);
- }
- else {
+ } else {
sp->SetAttribute("Max", caps->GetMaxSerialPortChannels());
}
sp->SetEditor("SpinCtrl");
} else if (IsPixelProtocol()) {
-
if (caps == nullptr || caps->SupportsPixelPortNullPixels()) {
sp = grid->AppendIn(p, new wxBoolProperty("Set Start Null Pixels", "ModelControllerConnectionPixelSetNullNodes", node->HasAttribute("nullNodes")));
sp->SetAttribute("UseCheckbox", true);
auto sp2 = grid->AppendIn(sp, new wxUIntProperty("Start Null Pixels", "ModelControllerConnectionPixelNullNodes",
- wxAtoi(GetControllerConnection()->GetAttribute("nullNodes", "0"))));
+ wxAtoi(GetControllerConnection()->GetAttribute("nullNodes", "0"))));
sp2->SetAttribute("Min", 0);
sp2->SetAttribute("Max", 100);
sp2->SetEditor("SpinCtrl");
@@ -1092,7 +1225,7 @@ void Model::AddControllerProperties(wxPropertyGridInterface* grid)
sp = grid->AppendIn(p, new wxBoolProperty("Set End Null Pixels", "ModelControllerConnectionPixelSetEndNullNodes", node->HasAttribute("endNullNodes")));
sp->SetAttribute("UseCheckbox", true);
auto sp2 = grid->AppendIn(sp, new wxUIntProperty("End Null Pixels", "ModelControllerConnectionPixelEndNullNodes",
- wxAtoi(GetControllerConnection()->GetAttribute("endNullNodes", "0"))));
+ wxAtoi(GetControllerConnection()->GetAttribute("endNullNodes", "0"))));
sp2->SetAttribute("Min", 0);
sp2->SetAttribute("Max", 100);
sp2->SetEditor("SpinCtrl");
@@ -1106,7 +1239,7 @@ void Model::AddControllerProperties(wxPropertyGridInterface* grid)
sp = grid->AppendIn(p, new wxBoolProperty("Set Brightness", "ModelControllerConnectionPixelSetBrightness", node->HasAttribute("brightness")));
sp->SetAttribute("UseCheckbox", true);
auto sp2 = grid->AppendIn(sp, new wxUIntProperty("Brightness", "ModelControllerConnectionPixelBrightness",
- wxAtoi(GetControllerConnection()->GetAttribute("brightness", "100"))));
+ wxAtoi(GetControllerConnection()->GetAttribute("brightness", "100"))));
sp2->SetAttribute("Min", 0);
sp2->SetAttribute("Max", 100);
sp2->SetEditor("SpinCtrl");
@@ -1147,7 +1280,7 @@ void Model::AddControllerProperties(wxPropertyGridInterface* grid)
sp = grid->AppendIn(p, new wxBoolProperty("Set Pixel Direction", "ModelControllerConnectionPixelSetDirection", node->HasAttribute("reverse")));
sp->SetAttribute("UseCheckbox", true);
auto sp2 = grid->AppendIn(sp, new wxEnumProperty("Direction", "ModelControllerConnectionPixelDirection", CONTROLLER_DIRECTION, wxArrayInt(),
- wxAtoi(GetControllerConnection()->GetAttribute("reverse", "0"))));
+ wxAtoi(GetControllerConnection()->GetAttribute("reverse", "0"))));
if (!node->HasAttribute("reverse")) {
grid->DisableProperty(sp2);
grid->Collapse(sp);
@@ -1186,7 +1319,7 @@ void Model::AddControllerProperties(wxPropertyGridInterface* grid)
sp = grid->AppendIn(p, new wxBoolProperty("Set Smart Ts", "ModelControllerConnectionPixelSetTs", node->HasAttribute("ts")));
sp->SetAttribute("UseCheckbox", true);
auto sp2 = grid->AppendIn(sp, new wxUIntProperty("Smart Ts", "ModelControllerConnectionPixelTs",
- GetSmartTs()));
+ GetSmartTs()));
sp2->SetAttribute("Min", 0);
sp2->SetAttribute("Max", 20);
sp2->SetEditor("SpinCtrl");
@@ -1198,8 +1331,8 @@ void Model::AddControllerProperties(wxPropertyGridInterface* grid)
}
}
-void Model::UpdateControllerProperties(wxPropertyGridInterface* grid) {
-
+void Model::UpdateControllerProperties(wxPropertyGridInterface* grid)
+{
auto caps = GetControllerCaps();
auto p = grid->GetPropertyByName("ModelControllerConnectionPort");
@@ -1222,8 +1355,7 @@ void Model::UpdateControllerProperties(wxPropertyGridInterface* grid) {
if (GetControllerName() != "" && _controller != 0 && (GetControllerPort() == 0 || GetControllerProtocol() == "")) {
p->SetHelpString("When using controller name instead of start channels then protocol must be specified.");
p->SetBackgroundColour(*wxRED);
- }
- else {
+ } else {
p->SetHelpString("");
grid->GetPropertyByName("ModelControllerConnectionProtocol")->SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_LISTBOX));
}
@@ -1235,13 +1367,11 @@ void Model::UpdateControllerProperties(wxPropertyGridInterface* grid) {
wxXmlNode* node = GetControllerConnection();
if (IsPixelProtocol()) {
- if (grid->GetPropertyByName("ModelControllerConnectionPixelSetNullNodes") != nullptr)
- {
+ if (grid->GetPropertyByName("ModelControllerConnectionPixelSetNullNodes") != nullptr) {
if (!node->HasAttribute("nullNodes")) {
grid->GetPropertyByName("ModelControllerConnectionPixelSetNullNodes")->SetExpanded(false);
grid->GetPropertyByName("ModelControllerConnectionPixelSetNullNodes.ModelControllerConnectionPixelNullNodes")->Enable(false);
- }
- else {
+ } else {
grid->GetPropertyByName("ModelControllerConnectionPixelSetNullNodes")->SetExpanded(true);
grid->GetPropertyByName("ModelControllerConnectionPixelSetNullNodes.ModelControllerConnectionPixelNullNodes")->Enable();
}
@@ -1251,68 +1381,57 @@ void Model::UpdateControllerProperties(wxPropertyGridInterface* grid) {
if (!node->HasAttribute("endNullNodes")) {
grid->GetPropertyByName("ModelControllerConnectionPixelSetEndNullNodes")->SetExpanded(false);
grid->GetPropertyByName("ModelControllerConnectionPixelSetEndNullNodes.ModelControllerConnectionPixelEndNullNodes")->Enable(false);
- }
- else {
+ } else {
grid->GetPropertyByName("ModelControllerConnectionPixelSetEndNullNodes")->SetExpanded(true);
grid->GetPropertyByName("ModelControllerConnectionPixelSetEndNullNodes.ModelControllerConnectionPixelEndNullNodes")->Enable();
}
}
- if (grid->GetPropertyByName("ModelControllerConnectionPixelSetBrightness") != nullptr)
- {
+ if (grid->GetPropertyByName("ModelControllerConnectionPixelSetBrightness") != nullptr) {
if (!node->HasAttribute("brightness")) {
grid->GetPropertyByName("ModelControllerConnectionPixelSetBrightness")->SetExpanded(false);
grid->GetPropertyByName("ModelControllerConnectionPixelSetBrightness.ModelControllerConnectionPixelBrightness")->Enable(false);
- }
- else {
+ } else {
grid->GetPropertyByName("ModelControllerConnectionPixelSetBrightness")->SetExpanded(true);
grid->GetPropertyByName("ModelControllerConnectionPixelSetBrightness.ModelControllerConnectionPixelBrightness")->Enable();
}
}
- if (grid->GetPropertyByName("ModelControllerConnectionPixelSetGamma") != nullptr)
- {
+ if (grid->GetPropertyByName("ModelControllerConnectionPixelSetGamma") != nullptr) {
if (!node->HasAttribute("gamma")) {
grid->GetPropertyByName("ModelControllerConnectionPixelSetGamma")->SetExpanded(false);
grid->GetPropertyByName("ModelControllerConnectionPixelSetGamma.ModelControllerConnectionPixelGamma")->Enable(false);
- }
- else {
+ } else {
grid->GetPropertyByName("ModelControllerConnectionPixelSetGamma")->SetExpanded(true);
grid->GetPropertyByName("ModelControllerConnectionPixelSetGamma.ModelControllerConnectionPixelGamma")->Enable();
}
}
- if (grid->GetPropertyByName("ModelControllerConnectionPixelSetColorOrder") != nullptr)
- {
+ if (grid->GetPropertyByName("ModelControllerConnectionPixelSetColorOrder") != nullptr) {
if (!node->HasAttribute("colorOrder")) {
grid->GetPropertyByName("ModelControllerConnectionPixelSetColorOrder")->SetExpanded(false);
grid->GetPropertyByName("ModelControllerConnectionPixelSetColorOrder.ModelControllerConnectionPixelColorOrder")->Enable(false);
- }
- else {
+ } else {
grid->GetPropertyByName("ModelControllerConnectionPixelSetColorOrder")->SetExpanded(true);
grid->GetPropertyByName("ModelControllerConnectionPixelSetColorOrder.ModelControllerConnectionPixelColorOrder")->Enable();
}
}
- if (grid->GetPropertyByName("ModelControllerConnectionPixelSetDirection") != nullptr)
- {
+ if (grid->GetPropertyByName("ModelControllerConnectionPixelSetDirection") != nullptr) {
if (!node->HasAttribute("reverse")) {
grid->GetPropertyByName("ModelControllerConnectionPixelSetDirection")->SetExpanded(false);
grid->GetPropertyByName("ModelControllerConnectionPixelSetDirection.ModelControllerConnectionPixelDirection")->Enable(false);
- }
- else {
+ } else {
grid->GetPropertyByName("ModelControllerConnectionPixelSetDirection")->SetExpanded(true);
grid->GetPropertyByName("ModelControllerConnectionPixelSetDirection.ModelControllerConnectionPixelDirection")->Enable();
}
}
- if (grid->GetPropertyByName("ModelControllerConnectionPixelSetGroupCount") != nullptr)
- {
+ if (grid->GetPropertyByName("ModelControllerConnectionPixelSetGroupCount") != nullptr) {
if (!node->HasAttribute("groupCount")) {
grid->GetPropertyByName("ModelControllerConnectionPixelSetGroupCount")->SetExpanded(false);
grid->GetPropertyByName("ModelControllerConnectionPixelSetGroupCount.ModelControllerConnectionPixelGroupCount")->Enable(false);
- }
- else {
+ } else {
grid->GetPropertyByName("ModelControllerConnectionPixelSetGroupCount")->SetExpanded(true);
grid->GetPropertyByName("ModelControllerConnectionPixelSetGroupCount.ModelControllerConnectionPixelGroupCount")->Enable();
}
@@ -1332,8 +1451,7 @@ void Model::UpdateControllerProperties(wxPropertyGridInterface* grid) {
if (!node->HasAttribute("ts")) {
grid->GetPropertyByName("ModelControllerConnectionPixelSetTs")->SetExpanded(false);
grid->GetPropertyByName("ModelControllerConnectionPixelSetTs.ModelControllerConnectionPixelTs")->Enable(false);
- }
- else {
+ } else {
grid->GetPropertyByName("ModelControllerConnectionPixelSetTs")->SetExpanded(true);
grid->GetPropertyByName("ModelControllerConnectionPixelSetTs.ModelControllerConnectionPixelTs")->Enable();
}
@@ -1342,24 +1460,21 @@ void Model::UpdateControllerProperties(wxPropertyGridInterface* grid) {
grid->RefreshGrid();
}
-static wxString GetColorString(wxPGProperty *p, xlColor &xc) {
+static wxString GetColorString(wxPGProperty* p, xlColor& xc)
+{
wxString tp = "Single Color Custom";
if (p != nullptr) {
wxColour c;
c << p->GetValue();
if (c == *wxRED) {
tp = "Single Color Red";
- }
- else if (c == *wxBLUE) {
+ } else if (c == *wxBLUE) {
tp = "Single Color Blue";
- }
- else if (c == *wxGREEN) {
+ } else if (c == *wxGREEN) {
tp = "Single Color Green";
- }
- else if (c == *wxWHITE) {
+ } else if (c == *wxWHITE) {
tp = "Single Color White";
- }
- else {
+ } else {
xc = c;
}
}
@@ -1376,15 +1491,15 @@ wxString Model::GetIndividualStartChannel(size_t s) const
return ModelXml->GetAttribute(StartChanAttrName(s), "");
}
-int Model::OnPropertyGridChange(wxPropertyGridInterface *grid, wxPropertyGridEvent& event) {
+int Model::OnPropertyGridChange(wxPropertyGridInterface* grid, wxPropertyGridEvent& event)
+{
static log4cpp::Category& logger_base = log4cpp::Category::getInstance(std::string("log_base"));
auto caps = GetControllerCaps();
modelManager.GetXLightsFrame()->AddTraceMessage("Model::OnPropertyGridChange : " + event.GetPropertyName() + " : " + (event.GetValue().GetType() == "string" ? event.GetValue().GetString() : "N/A") + " : " + (event.GetValue().GetType() == "long" ? wxString::Format("%ld", event.GetValue().GetLong()) : "N/A"));
- if (HandleLayerSizePropertyChange(grid, event))
- {
+ if (HandleLayerSizePropertyChange(grid, event)) {
return 0;
}
@@ -1448,36 +1563,26 @@ int Model::OnPropertyGridChange(wxPropertyGridInterface *grid, wxPropertyGridEve
return 0;
} else if (event.GetPropertyName() == "ModelChain") {
std::string modelChain = event.GetValue().GetString();
- if (modelChain == "Beginning")
- {
+ if (modelChain == "Beginning") {
modelChain = "";
}
SetModelChain(modelChain);
- if (modelChain != "")
- {
+ if (modelChain != "") {
ModelXml->DeleteAttribute("Advanced");
AdjustStringProperties(grid, parm1);
- if (grid->GetPropertyByName("ModelStartChannel") != nullptr)
- {
+ if (grid->GetPropertyByName("ModelStartChannel") != nullptr) {
grid->GetPropertyByName("ModelStartChannel")->SetValue(ModelXml->GetAttribute("StartChannel", "1"));
grid->GetPropertyByName("ModelStartChannel")->Enable(false);
- }
- else if (grid->GetPropertyByName("ModelIndividualStartChannels.ModelStartChannel") != nullptr)
- {
+ } else if (grid->GetPropertyByName("ModelIndividualStartChannels.ModelStartChannel") != nullptr) {
grid->GetPropertyByName("ModelIndividualStartChannels.ModelStartChannel")->Enable(false);
grid->GetPropertyByName("ModelIndividualStartChannels")->Enable(false);
grid->GetPropertyByName("ModelIndividualStartChannels.ModelStartChannel")->SetValue(ModelXml->GetAttribute("StartChannel", "1"));
}
- }
- else
- {
+ } else {
SetStartChannel("1");
- if (grid->GetPropertyByName("ModelStartChannel") != nullptr)
- {
+ if (grid->GetPropertyByName("ModelStartChannel") != nullptr) {
grid->GetPropertyByName("ModelStartChannel")->Enable();
- }
- else if (grid->GetPropertyByName("ModelIndividualStartChannels.ModelStartChannel") != nullptr)
- {
+ } else if (grid->GetPropertyByName("ModelIndividualStartChannels.ModelStartChannel") != nullptr) {
grid->GetPropertyByName("ModelIndividualStartChannels.ModelStartChannel")->Enable();
grid->GetPropertyByName("ModelIndividualStartChannels")->Enable();
}
@@ -1490,15 +1595,13 @@ int Model::OnPropertyGridChange(wxPropertyGridInterface *grid, wxPropertyGridEve
AddASAPWork(OutputModelManager::WORK_MODELS_CHANGE_REQUIRING_RERENDER, "Model::OnPropertyGridChange::ModelChain");
AddASAPWork(OutputModelManager::WORK_RESEND_CONTROLLER_CONFIG, "Model::OnPropertyGridChange::ModelChain");
return 0;
- }
- else if (event.GetPropertyName() == "ShadowModelFor") {
+ } else if (event.GetPropertyName() == "ShadowModelFor") {
if (GetShadowModelFor() != OTHERMODELLIST[event.GetValue().GetInteger()]) {
SetShadowModelFor(OTHERMODELLIST[event.GetValue().GetInteger()]);
}
AddASAPWork(OutputModelManager::WORK_RGBEFFECTS_CHANGE, "Model::OnPropertyGridChange::Controller");
return 0;
- }
- else if (event.GetPropertyName() == "Controller") {
+ } else if (event.GetPropertyName() == "Controller") {
if (GetControllerName() != CONTROLLERS[event.GetValue().GetInteger()]) {
SetControllerName(CONTROLLERS[event.GetValue().GetInteger()]);
if (GetControllerPort() != 0 && IsPixelProtocol()) {
@@ -1539,7 +1642,6 @@ int Model::OnPropertyGridChange(wxPropertyGridInterface *grid, wxPropertyGridEve
AddASAPWork(OutputModelManager::WORK_RESEND_CONTROLLER_CONFIG, "Model::OnPropertyGridChange::Controller");
return 0;
} else if (event.GetPropertyName() == "ModelControllerConnectionPort") {
-
bool protocolChanged = false;
if (GetControllerPort() != event.GetValue().GetLong()) {
SetControllerPort(event.GetValue().GetLong());
@@ -1596,8 +1698,7 @@ int Model::OnPropertyGridChange(wxPropertyGridInterface *grid, wxPropertyGridEve
SetActive(event.GetValue().GetBool());
IncrementChangeCount();
return 0;
- }
- else if (event.GetPropertyName() == "UseSmartRemote") {
+ } else if (event.GetPropertyName() == "UseSmartRemote") {
auto usr = event.GetValue().GetBool();
if (!usr) {
SetSmartRemote(0);
@@ -1637,8 +1738,7 @@ int Model::OnPropertyGridChange(wxPropertyGridInterface *grid, wxPropertyGridEve
if (GetControllerName() != "" && _controller != 0) {
if (grid->GetPropertyByName("ModelStartChannel") != nullptr) {
grid->GetPropertyByName("ModelStartChannel")->SetValue(ModelXml->GetAttribute("StartChannel", "1"));
- }
- else if (grid->GetPropertyByName("ModelIndividualStartChannels.ModelStartChannel") != nullptr) {
+ } else if (grid->GetPropertyByName("ModelIndividualStartChannels.ModelStartChannel") != nullptr) {
grid->GetPropertyByName("ModelIndividualStartChannels.ModelStartChannel")->SetValue(ModelXml->GetAttribute("StartChannel", "1"));
}
}
@@ -1655,7 +1755,7 @@ int Model::OnPropertyGridChange(wxPropertyGridInterface *grid, wxPropertyGridEve
}
}
if (
- //FIXME-Matrix
+ // FIXME-Matrix
(::IsSerialProtocol(newProtocol) && ::IsPixelProtocol(oldProtocol)) ||
(::IsSerialProtocol(oldProtocol) && ::IsPixelProtocol(newProtocol)) ||
(oldProtocol == "" && newProtocol != "") ||
@@ -1673,7 +1773,7 @@ int Model::OnPropertyGridChange(wxPropertyGridInterface *grid, wxPropertyGridEve
return 0;
} else if (event.GetPropertyName() == "ModelControllerConnectionPixelSetBrightness") {
GetControllerConnection()->DeleteAttribute("brightness");
- wxPGProperty *prop = grid->GetFirstChild(event.GetProperty());
+ wxPGProperty* prop = grid->GetFirstChild(event.GetProperty());
grid->EnableProperty(prop, event.GetValue().GetBool());
if (event.GetValue().GetBool()) {
GetControllerConnection()->AddAttribute("brightness", "100");
@@ -1699,7 +1799,7 @@ int Model::OnPropertyGridChange(wxPropertyGridInterface *grid, wxPropertyGridEve
return 0;
} else if (event.GetPropertyName() == "ModelControllerConnectionPixelSetGamma") {
GetControllerConnection()->DeleteAttribute("gamma");
- wxPGProperty *prop = grid->GetFirstChild(event.GetProperty());
+ wxPGProperty* prop = grid->GetFirstChild(event.GetProperty());
grid->EnableProperty(prop, event.GetValue().GetBool());
if (event.GetValue().GetBool()) {
GetControllerConnection()->AddAttribute("gamma", "1.0");
@@ -1723,7 +1823,7 @@ int Model::OnPropertyGridChange(wxPropertyGridInterface *grid, wxPropertyGridEve
return 0;
} else if (event.GetPropertyName() == "ModelControllerConnectionPixelSetDirection") {
GetControllerConnection()->DeleteAttribute("reverse");
- wxPGProperty *prop = grid->GetFirstChild(event.GetProperty());
+ wxPGProperty* prop = grid->GetFirstChild(event.GetProperty());
grid->EnableProperty(prop, event.GetValue().GetBool());
if (event.GetValue().GetBool()) {
GetControllerConnection()->AddAttribute("reverse", "0");
@@ -1749,7 +1849,7 @@ int Model::OnPropertyGridChange(wxPropertyGridInterface *grid, wxPropertyGridEve
return 0;
} else if (event.GetPropertyName() == "ModelControllerConnectionPixelSetColorOrder") {
GetControllerConnection()->DeleteAttribute("colorOrder");
- wxPGProperty *prop = grid->GetFirstChild(event.GetProperty());
+ wxPGProperty* prop = grid->GetFirstChild(event.GetProperty());
grid->EnableProperty(prop, event.GetValue().GetBool());
if (event.GetValue().GetBool()) {
GetControllerConnection()->AddAttribute("colorOrder", "RGB");
@@ -1773,7 +1873,7 @@ int Model::OnPropertyGridChange(wxPropertyGridInterface *grid, wxPropertyGridEve
return 0;
} else if (event.GetPropertyName() == "ModelControllerConnectionPixelSetNullNodes") {
GetControllerConnection()->DeleteAttribute("nullNodes");
- wxPGProperty *prop = grid->GetFirstChild(event.GetProperty());
+ wxPGProperty* prop = grid->GetFirstChild(event.GetProperty());
grid->EnableProperty(prop, event.GetValue().GetBool());
if (event.GetValue().GetBool()) {
GetControllerConnection()->AddAttribute("nullNodes", "0");
@@ -1795,8 +1895,7 @@ int Model::OnPropertyGridChange(wxPropertyGridInterface *grid, wxPropertyGridEve
GetControllerConnection()->AddAttribute("endNullNodes", "0");
prop->SetValueFromInt(0);
grid->Expand(event.GetProperty());
- }
- else {
+ } else {
grid->Collapse(event.GetProperty());
}
AddASAPWork(OutputModelManager::WORK_RGBEFFECTS_CHANGE, "Model::OnPropertyGridChange::ModelControllerConnectionPixelSetEndNullNodes");
@@ -1814,7 +1913,7 @@ int Model::OnPropertyGridChange(wxPropertyGridInterface *grid, wxPropertyGridEve
AddASAPWork(OutputModelManager::WORK_MODELS_CHANGE_REQUIRING_RERENDER, "Model::OnPropertyGridChange::ModelControllerConnectionPixelNullNodes");
AddASAPWork(OutputModelManager::WORK_RESEND_CONTROLLER_CONFIG, "Model::OnPropertyGridChange::ModelControllerConnectionPixelNullNodes");
return 0;
- } else if (event.GetPropertyName() == "ModelControllerConnectionPixelSetEndNullNodes.ModelControllerConnectionPixelEndNullNodes") {
+ } else if (event.GetPropertyName() == "ModelControllerConnectionPixelSetEndNullNodes.ModelControllerConnectionPixelEndNullNodes") {
GetControllerConnection()->DeleteAttribute("endNullNodes");
if (event.GetValue().GetLong() >= 0) {
GetControllerConnection()->AddAttribute("endNullNodes", wxString::Format("%i", (int)event.GetValue().GetLong()));
@@ -1826,7 +1925,7 @@ int Model::OnPropertyGridChange(wxPropertyGridInterface *grid, wxPropertyGridEve
return 0;
} else if (event.GetPropertyName() == "ModelControllerConnectionPixelSetGroupCount") {
GetControllerConnection()->DeleteAttribute("groupCount");
- wxPGProperty *prop = grid->GetFirstChild(event.GetProperty());
+ wxPGProperty* prop = grid->GetFirstChild(event.GetProperty());
grid->EnableProperty(prop, event.GetValue().GetBool());
if (event.GetValue().GetBool()) {
GetControllerConnection()->AddAttribute("groupCount", "0");
@@ -1884,8 +1983,7 @@ int Model::OnPropertyGridChange(wxPropertyGridInterface *grid, wxPropertyGridEve
GetControllerConnection()->AddAttribute("ts", "0");
prop->SetValueFromInt(0);
grid->Expand(event.GetProperty());
- }
- else {
+ } else {
grid->Collapse(event.GetProperty());
}
AddASAPWork(OutputModelManager::WORK_RGBEFFECTS_CHANGE, "Model::OnPropertyGridChange::ModelControllerConnectionPixelSetTs");
@@ -1893,8 +1991,7 @@ int Model::OnPropertyGridChange(wxPropertyGridInterface *grid, wxPropertyGridEve
AddASAPWork(OutputModelManager::WORK_MODELS_CHANGE_REQUIRING_RERENDER, "Model::OnPropertyGridChange::ModelControllerConnectionPixelSetTs");
AddASAPWork(OutputModelManager::WORK_RESEND_CONTROLLER_CONFIG, "Model::OnPropertyGridChange::ModelControllerConnectionPixelSetTs");
return 0;
- }
- else if (event.GetPropertyName() == "ModelControllerConnectionPixelSetTs.ModelControllerConnectionPixelTs") {
+ } else if (event.GetPropertyName() == "ModelControllerConnectionPixelSetTs.ModelControllerConnectionPixelTs") {
GetControllerConnection()->DeleteAttribute("ts");
if (event.GetValue().GetLong() >= 0) {
GetControllerConnection()->AddAttribute("ts", wxString::Format("%i", (int)event.GetValue().GetLong()));
@@ -1904,8 +2001,7 @@ int Model::OnPropertyGridChange(wxPropertyGridInterface *grid, wxPropertyGridEve
AddASAPWork(OutputModelManager::WORK_MODELS_CHANGE_REQUIRING_RERENDER, "Model::OnPropertyGridChange::ModelControllerConnectionPixelTs");
AddASAPWork(OutputModelManager::WORK_RESEND_CONTROLLER_CONFIG, "Model::OnPropertyGridChange::ModelControllerConnectionPixelTs");
return 0;
- }
- else if (event.GetPropertyName() == "SubModels") {
+ } else if (event.GetPropertyName() == "SubModels") {
// We cant know which subModels changed so increment all their change counts to ensure anything using them knows they may have changed
for (auto& it : GetSubModels()) {
it->IncrementChangeCount();
@@ -1921,22 +2017,20 @@ int Model::OnPropertyGridChange(wxPropertyGridInterface *grid, wxPropertyGridEve
} else if (event.GetPropertyName() == "Description") {
description = event.GetValue().GetString();
ModelXml->DeleteAttribute("Description");
- if (description != "")
- {
+ if (description != "") {
ModelXml->AddAttribute("Description", XmlSafe(description));
}
IncrementChangeCount();
AddASAPWork(OutputModelManager::WORK_RGBEFFECTS_CHANGE, "Model::OnPropertyGridChange::Description");
return 0;
} else if (event.GetPropertyName() == "ModelFaces") {
- wxXmlNode *f = ModelXml->GetChildren();
+ wxXmlNode* f = ModelXml->GetChildren();
while (f != nullptr) {
if ("faceInfo" == f->GetName()) {
ModelXml->RemoveChild(f);
delete f;
f = ModelXml->GetChildren();
- }
- else {
+ } else {
f = f->GetNext();
}
}
@@ -1944,16 +2038,14 @@ int Model::OnPropertyGridChange(wxPropertyGridInterface *grid, wxPropertyGridEve
IncrementChangeCount();
AddASAPWork(OutputModelManager::WORK_RGBEFFECTS_CHANGE, "Model::OnPropertyGridChange::ModelFaces");
return 0;
- }
- else if (event.GetPropertyName() == "ModelStates") {
+ } else if (event.GetPropertyName() == "ModelStates") {
wxXmlNode* f = ModelXml->GetChildren();
while (f != nullptr) {
if ("stateInfo" == f->GetName()) {
ModelXml->RemoveChild(f);
delete f;
f = ModelXml->GetChildren();
- }
- else {
+ } else {
f = f->GetNext();
}
}
@@ -1961,6 +2053,9 @@ int Model::OnPropertyGridChange(wxPropertyGridInterface *grid, wxPropertyGridEve
IncrementChangeCount();
AddASAPWork(OutputModelManager::WORK_RGBEFFECTS_CHANGE, "Model::OnPropertyGridChange::ModelStates");
return 0;
+ } else if (event.GetPropertyName() == "Aliases") {
+ AddASAPWork(OutputModelManager::WORK_RGBEFFECTS_CHANGE, "Model::OnPropertyGridChange::Aliases");
+ return 0;
} else if (event.GetPropertyName().StartsWith("SuperStringColours")) {
IncrementChangeCount();
SetSuperStringColours(event.GetValue().GetLong());
@@ -1972,27 +2067,25 @@ int Model::OnPropertyGridChange(wxPropertyGridInterface *grid, wxPropertyGridEve
IncrementChangeCount();
SetSuperStringColour(index, c);
return 0;
- } else if (event.GetPropertyName() == "ModelStringColor"
- || event.GetPropertyName() == "ModelStringType"
- || event.GetPropertyName() == "ModelRGBWHandling") {
- wxPGProperty *p2 = grid->GetPropertyByName("ModelStringType");
+ } else if (event.GetPropertyName() == "ModelStringColor" || event.GetPropertyName() == "ModelStringType" || event.GetPropertyName() == "ModelRGBWHandling") {
+ wxPGProperty* p2 = grid->GetPropertyByName("ModelStringType");
int i = p2->GetValue().GetLong();
ModelXml->DeleteAttribute("StringType");
ModelXml->DeleteAttribute("RGBWHandling");
- if (NODE_TYPES[i] == "Single Color"|| NODE_TYPES[i] == "Single Color Intensity" || NODE_TYPES[i] == "Node Single Color") {
- wxPGProperty *p = grid->GetPropertyByName("ModelStringColor");
+ if (NODE_TYPES[i] == "Single Color" || NODE_TYPES[i] == "Single Color Intensity" || NODE_TYPES[i] == "Node Single Color") {
+ wxPGProperty* p = grid->GetPropertyByName("ModelStringColor");
xlColor c;
wxString tp = GetColorString(p, c);
if (NODE_TYPES[i] == "Single Color Intensity") {
tp = "Single Color Intensity";
- }
- else if (NODE_TYPES[i] == "Node Single Color" && p != nullptr) {
+ } else if (NODE_TYPES[i] == "Node Single Color" && p != nullptr) {
tp = "Node Single Color";
wxColor cc;
cc << p->GetValue();
c = cc;
}
- if (p != nullptr) p->Enable();
+ if (p != nullptr)
+ p->Enable();
if (tp == "Single Color Custom" || tp == "Single Color Intensity" || tp == "Node Single Color") {
ModelXml->DeleteAttribute("CustomColor");
xlColor xc = c;
@@ -2010,8 +2103,7 @@ int Model::OnPropertyGridChange(wxPropertyGridInterface *grid, wxPropertyGridEve
IncrementChangeCount();
AddASAPWork(OutputModelManager::WORK_RELOAD_MODEL_FROM_XML, "Model::OnPropertyGridChange::ModelStringType");
AddASAPWork(OutputModelManager::WORK_RGBEFFECTS_CHANGE, "Model::OnPropertyGridChange::ModelStringType");
- if (event.GetPropertyName() == "ModelStringType")
- {
+ if (event.GetPropertyName() == "ModelStringType") {
AddASAPWork(OutputModelManager::WORK_RELOAD_PROPERTYGRID, "Model::OnPropertyGridChange::ModelStringType");
AddASAPWork(OutputModelManager::WORK_RELOAD_MODELLIST, "Model::OnPropertyGridChange::ModelStringType");
AddASAPWork(OutputModelManager::WORK_CALCULATE_START_CHANNELS, "Model::OnPropertyGridChange::ModelStringType");
@@ -2020,11 +2112,9 @@ int Model::OnPropertyGridChange(wxPropertyGridInterface *grid, wxPropertyGridEve
}
return 0;
} else if (event.GetPropertyName() == "ModelStartChannel" || event.GetPropertyName() == "ModelIndividualStartChannels.ModelStartChannel") {
-
wxString val = event.GetValue().GetString();
- if ((val.StartsWith("@") || val.StartsWith("#") || val.StartsWith(">") || val.StartsWith("!")) && !val.Contains(":"))
- {
+ if ((val.StartsWith("@") || val.StartsWith("#") || val.StartsWith(">") || val.StartsWith("!")) && !val.Contains(":")) {
val = val + ":1";
event.GetProperty()->SetValue(val);
}
@@ -2038,7 +2128,7 @@ int Model::OnPropertyGridChange(wxPropertyGridInterface *grid, wxPropertyGridEve
}
IncrementChangeCount();
AddASAPWork(OutputModelManager::WORK_RGBEFFECTS_CHANGE, "Model::OnPropertyGridChange::ModelStartChannel");
- //AddASAPWork(OutputModelManager::WORK_RELOAD_PROPERTYGRID, "Model::OnPropertyGridChange::ModelStartChannel");
+ // AddASAPWork(OutputModelManager::WORK_RELOAD_PROPERTYGRID, "Model::OnPropertyGridChange::ModelStartChannel");
AddASAPWork(OutputModelManager::WORK_RELOAD_MODELLIST, "Model::OnPropertyGridChange::ModelStartChannel");
AddASAPWork(OutputModelManager::WORK_CALCULATE_START_CHANNELS, "Model::OnPropertyGridChange::ModelStartChannel");
AddASAPWork(OutputModelManager::WORK_MODELS_CHANGE_REQUIRING_RERENDER, "Model::OnPropertyGridChange::ModelStartChannel");
@@ -2048,23 +2138,23 @@ int Model::OnPropertyGridChange(wxPropertyGridInterface *grid, wxPropertyGridEve
int c = Model::HasOneString(DisplayAs) ? 1 : parm1;
if (event.GetValue().GetBool()) {
ModelXml->AddAttribute("Advanced", "1");
- for (int x = 0; x < c; x++) {
+ for (int x = 0; x < c; ++x) {
if (ModelXml->GetAttribute(StartChanAttrName(x)) == "") {
ModelXml->DeleteAttribute(StartChanAttrName(x));
ModelXml->AddAttribute(StartChanAttrName(x),
- ComputeStringStartChannel(x));
+ ComputeStringStartChannel(x));
}
}
} else {
// overkill but just delete any that are there
- for (int x = 0; x < MOST_STRINGS_WE_EXPECT; x++) {
+ for (int x = 0; x < MOST_STRINGS_WE_EXPECT; ++x) {
ModelXml->DeleteAttribute(StartChanAttrName(x));
}
wxASSERT(!ModelXml->HasAttribute(StartChanAttrName(MOST_STRINGS_WE_EXPECT)));
}
// Not sure if i can just remove these
- //RecalcStartChannels();
- //AdjustStringProperties(grid, parm1);
+ // RecalcStartChannels();
+ // AdjustStringProperties(grid, parm1);
IncrementChangeCount();
AddASAPWork(OutputModelManager::WORK_RGBEFFECTS_CHANGE, "Model::OnPropertyGridChange::ModelIndividualStartChannels");
AddASAPWork(OutputModelManager::WORK_RELOAD_PROPERTYGRID, "Model::OnPropertyGridChange::ModelIndividualStartChannels");
@@ -2077,8 +2167,7 @@ int Model::OnPropertyGridChange(wxPropertyGridInterface *grid, wxPropertyGridEve
str = str.SubString(str.Find(".") + 1, str.length());
wxString val = event.GetValue().GetString();
- if ((val.StartsWith("@") || val.StartsWith("#") || val.StartsWith(">") || val.StartsWith("!")) && !val.Contains(":"))
- {
+ if ((val.StartsWith("@") || val.StartsWith("#") || val.StartsWith(">") || val.StartsWith("!")) && !val.Contains(":")) {
val = val + ":1";
event.GetProperty()->SetValue(val);
}
@@ -2087,7 +2176,7 @@ int Model::OnPropertyGridChange(wxPropertyGridInterface *grid, wxPropertyGridEve
ModelXml->AddAttribute(str, val);
IncrementChangeCount();
AddASAPWork(OutputModelManager::WORK_RGBEFFECTS_CHANGE, "Model::OnPropertyGridChange::ModelIndividualStartChannels2");
- //AddASAPWork(OutputModelManager::WORK_RELOAD_PROPERTYGRID, "Model::OnPropertyGridChange::ModelIndividualStartChannels2");
+ // AddASAPWork(OutputModelManager::WORK_RELOAD_PROPERTYGRID, "Model::OnPropertyGridChange::ModelIndividualStartChannels2");
AddASAPWork(OutputModelManager::WORK_RELOAD_MODELLIST, "Model::OnPropertyGridChange::ModelIndividualStartChannels2");
AddASAPWork(OutputModelManager::WORK_CALCULATE_START_CHANNELS, "Model::OnPropertyGridChange::ModelIndividualStartChannels2");
AddASAPWork(OutputModelManager::WORK_MODELS_CHANGE_REQUIRING_RERENDER, "Model::OnPropertyGridChange::ModelIndividualStartChannels2");
@@ -2101,7 +2190,7 @@ int Model::OnPropertyGridChange(wxPropertyGridInterface *grid, wxPropertyGridEve
AddASAPWork(OutputModelManager::WORK_RELOAD_ALLMODELS, "Model::OnPropertyGridChange::ModelLayoutGroup");
AddASAPWork(OutputModelManager::WORK_RELOAD_MODELLIST, "Model::OnPropertyGridChange::ModelLayoutGroup");
AddASAPWork(OutputModelManager::WORK_REDRAW_LAYOUTPREVIEW, "Model::OnPropertyGridChange::ModelLayoutGroup");
- //AddASAPWork(OutputModelManager::WORK_RELOAD_MODEL_FROM_XML, "Model::OnPropertyGridChange::ModelLayoutGroup");
+ // AddASAPWork(OutputModelManager::WORK_RELOAD_MODEL_FROM_XML, "Model::OnPropertyGridChange::ModelLayoutGroup");
return 0;
}
@@ -2113,9 +2202,10 @@ int Model::OnPropertyGridChange(wxPropertyGridInterface *grid, wxPropertyGridEve
return i;
}
-void Model::AdjustStringProperties(wxPropertyGridInterface *grid, int newNum) {
- wxPropertyGrid *pg = static_cast(grid);
- wxPGProperty *p = grid->GetPropertyByName("ModelIndividualStartChannels");
+void Model::AdjustStringProperties(wxPropertyGridInterface* grid, int newNum)
+{
+ wxPropertyGrid* pg = static_cast(grid);
+ wxPGProperty* p = grid->GetPropertyByName("ModelIndividualStartChannels");
if (p != nullptr) {
pg->Freeze();
p->Enable(GetControllerName() == "" || _controller == 0);
@@ -2125,7 +2215,7 @@ void Model::AdjustStringProperties(wxPropertyGridInterface *grid, int newNum) {
while (count > newNum) {
count--;
wxString nm = StartChanAttrName(count);
- wxPGProperty *sp = grid->GetPropertyByName("ModelIndividualStartChannels." + nm);
+ wxPGProperty* sp = grid->GetPropertyByName("ModelIndividualStartChannels." + nm);
if (sp != nullptr) {
grid->DeleteProperty(sp);
}
@@ -2145,21 +2235,19 @@ void Model::AdjustStringProperties(wxPropertyGridInterface *grid, int newNum) {
}
} else if (p->GetChildCount() > 1) {
int count = p->GetChildCount();
- for (int x = 1; x < count; x++) {
+ for (int x = 1; x < count; ++x) {
wxString nm = StartChanAttrName(x);
- wxPGProperty *sp = grid->GetPropertyByName("ModelIndividualStartChannels." + nm);
+ wxPGProperty* sp = grid->GetPropertyByName("ModelIndividualStartChannels." + nm);
if (sp != nullptr) {
grid->DeleteProperty(sp);
}
}
}
- wxPGProperty *sp = grid->GetPropertyByName("ModelIndividualStartChannels.ModelStartChannel");
- if (sp != nullptr)
- {
+ wxPGProperty* sp = grid->GetPropertyByName("ModelIndividualStartChannels.ModelStartChannel");
+ if (sp != nullptr) {
if (adv) {
sp->SetLabel(StartChanAttrName(0));
- }
- else {
+ } else {
sp->SetLabel("Start Channel");
}
}
@@ -2169,7 +2257,8 @@ void Model::AdjustStringProperties(wxPropertyGridInterface *grid, int newNum) {
}
}
-void Model::ParseFaceInfo(wxXmlNode *f, std::map > &faceInfo) {
+void Model::ParseFaceInfo(wxXmlNode* f, std::map>& faceInfo)
+{
std::string name = f->GetAttribute("Name", "SingleNode").ToStdString();
std::string type = f->GetAttribute("Type", "SingleNode").ToStdString();
if (name == "") {
@@ -2186,23 +2275,18 @@ void Model::ParseFaceInfo(wxXmlNode *f, std::mapDeleteAttribute("Type");
f->AddAttribute("Type", type);
}
- wxXmlAttribute *att = f->GetAttributes();
+ wxXmlAttribute* att = f->GetAttributes();
while (att != nullptr) {
- if (att->GetName() != "Name")
- {
- if (att->GetName().Left(5) == "Mouth" || att->GetName().Left(4) == "Eyes")
- {
- if (type == "Matrix")
- {
+ if (att->GetName() != "Name") {
+ if (att->GetName().Left(5) == "Mouth" || att->GetName().Left(4) == "Eyes") {
+ if (type == "Matrix") {
faceInfo[name][att->GetName().ToStdString()] = FixFile("", att->GetValue());
- if (att->GetValue() != faceInfo[name][att->GetName().ToStdString()]) att->SetValue(faceInfo[name][att->GetName().ToStdString()]);
- }
- else {
+ if (att->GetValue() != faceInfo[name][att->GetName().ToStdString()])
+ att->SetValue(faceInfo[name][att->GetName().ToStdString()]);
+ } else {
faceInfo[name][att->GetName().ToStdString()] = att->GetValue();
}
- }
- else
- {
+ } else {
faceInfo[name][att->GetName().ToStdString()] = att->GetValue();
}
}
@@ -2210,10 +2294,11 @@ void Model::ParseFaceInfo(wxXmlNode *f, std::map > &faceInfo) {
+void Model::WriteFaceInfo(wxXmlNode* rootXml, const std::map>& faceInfo)
+{
if (!faceInfo.empty()) {
for (const auto& it : faceInfo) {
- wxXmlNode *f = new wxXmlNode(rootXml, wxXML_ELEMENT_NODE , "faceInfo");
+ wxXmlNode* f = new wxXmlNode(rootXml, wxXML_ELEMENT_NODE, "faceInfo");
std::string name = it.first;
f->AddAttribute("Name", name);
for (const auto& it2 : it.second) {
@@ -2345,7 +2430,7 @@ void Model::UpdateFaceInfoNodes()
}
start--;
end--;
- for (int n = start; n <= end; n++) {
+ for (int n = start; n <= end; ++n) {
nodes.push_back(n);
}
}
@@ -2384,7 +2469,7 @@ void Model::UpdateStateInfoNodes()
}
start--;
end--;
- for (int n = start; n <= end; n++) {
+ for (int n = start; n <= end; ++n) {
nodes.push_back(n);
}
}
@@ -2411,7 +2496,7 @@ void Model::ParseStateInfo(wxXmlNode* f, std::mapDeleteAttribute("Type");
f->AddAttribute("Type", type);
}
- wxXmlAttribute *att = f->GetAttributes();
+ wxXmlAttribute* att = f->GetAttributes();
while (att != nullptr) {
if (att->GetName() != "Name") {
if (att->GetValue() != "") { // we only save non default values to keep xml file small
@@ -2422,16 +2507,15 @@ void Model::ParseStateInfo(wxXmlNode* f, std::map > &stateInfo, bool forceCustom) {
+void Model::WriteStateInfo(wxXmlNode* rootXml, const std::map>& stateInfo, bool forceCustom)
+{
if (!stateInfo.empty()) {
for (const auto& it : stateInfo) {
std::string name = it.first;
- if (wxString(name).Trim(true).Trim(false) != "")
- {
- wxXmlNode *f = new wxXmlNode(rootXml, wxXML_ELEMENT_NODE, "stateInfo");
+ if (wxString(name).Trim(true).Trim(false) != "") {
+ wxXmlNode* f = new wxXmlNode(rootXml, wxXML_ELEMENT_NODE, "stateInfo");
f->AddAttribute("Name", name);
- if (forceCustom)
- {
+ if (forceCustom) {
f->AddAttribute("CustomColors", "1");
}
for (const auto& it2 : it.second) {
@@ -2451,8 +2535,7 @@ wxString Model::SerialiseState() const
for (const auto& it : stateInfo) {
res += " GetAttribute("models");
- if (grpModels.length() == 0) return;
+ if (grpModels.length() == 0)
+ return;
modelManager.GetXLightsFrame()->AllModels.AddModelGroups(n, w, h, name, merge, ask);
}
-std::string Model::ComputeStringStartChannel(int i) {
+std::string Model::ComputeStringStartChannel(int i)
+{
if (i == 0) {
return ModelXml->GetAttribute("StartChannel", "1").ToStdString();
}
wxString existingStartChannelAsString = ModelXml->GetAttribute(StartChanAttrName(i));
- if (existingStartChannelAsString != "")
- {
+ if (existingStartChannelAsString != "") {
return existingStartChannelAsString;
}
@@ -2504,7 +2588,7 @@ std::string Model::ComputeStringStartChannel(int i) {
wxString priorStringStartChannelAsString = ModelXml->GetAttribute(StartChanAttrName(i - 1));
int priorLength = CalcCannelsPerString();
// This will be required once custom model supports multiple strings ... working on that
- //if (DisplayAs == "Custom")
+ // if (DisplayAs == "Custom")
//{
// priorLength = GetStrandLength(i - 1) * GetChanCountPerNode();
//}
@@ -2512,38 +2596,25 @@ std::string Model::ComputeStringStartChannel(int i) {
int32_t startChannel = priorStringStartChannel + priorLength;
if (stch.Contains(":")) {
auto comps = wxSplit(priorStringStartChannelAsString, ':');
- if (comps[0].StartsWith("#"))
- {
+ if (comps[0].StartsWith("#")) {
int32_t ststch;
Output* o = modelManager.GetOutputManager()->GetOutput(startChannel, ststch);
- if (comps.size() == 2)
- {
- if (o != nullptr)
- {
+ if (comps.size() == 2) {
+ if (o != nullptr) {
return wxString::Format("#%i:%d", o->GetUniverse(), ststch).ToStdString();
- }
- else
- {
+ } else {
return wxString::Format("%d", startChannel);
}
- }
- else
- {
- if (o != nullptr)
- {
+ } else {
+ if (o != nullptr) {
return wxString::Format("%s:%i:%d", comps[0], o->GetUniverse(), ststch).ToStdString();
- }
- else
- {
+ } else {
return wxString::Format("%d", startChannel);
}
}
- }
- else if (comps[0].StartsWith(">") || comps[0].StartsWith("@") || comps[0].StartsWith("!") )
- {
+ } else if (comps[0].StartsWith(">") || comps[0].StartsWith("@") || comps[0].StartsWith("!")) {
return wxString::Format("%s:%d", comps[0], wxAtol(comps[1]) + priorLength);
- }
- else {
+ } else {
// This used to be on:sc but this is no longer supported
return wxString::Format("%d", startChannel);
}
@@ -2551,15 +2622,17 @@ std::string Model::ComputeStringStartChannel(int i) {
return wxString::Format("%d", startChannel);
}
-int Model::GetNumStrands() const {
+int Model::GetNumStrands() const
+{
return 1;
}
-bool Model::ModelRenamed(const std::string &oldName, const std::string &newName) {
+bool Model::ModelRenamed(const std::string& oldName, const std::string& newName)
+{
bool changed = false;
- std::string sc = ModelXml->GetAttribute("StartChannel","1").ToStdString();
+ std::string sc = ModelXml->GetAttribute("StartChannel", "1").ToStdString();
if ((sc[0] == '@' || sc[0] == '<' || sc[0] == '>') && sc.size() > 1) {
- std::string mn = sc.substr(1, sc.find(':')-1);
+ std::string mn = sc.substr(1, sc.find(':') - 1);
if (mn == oldName) {
sc = sc[0] + newName + sc.substr(sc.find(':'), sc.size());
ModelXml->DeleteAttribute("StartChannel");
@@ -2574,19 +2647,18 @@ bool Model::ModelRenamed(const std::string &oldName, const std::string &newName)
}
std::string mc = ModelXml->GetAttribute("ModelChain", "").ToStdString();
- if (mc == ">" + oldName)
- {
+ if (mc == ">" + oldName) {
ModelXml->DeleteAttribute("ModelChain");
ModelXml->AddAttribute("ModelChain", ">" + newName);
changed = true;
}
- for (size_t i=0; iHasAttribute(tempstr)) {
sc = ModelXml->GetAttribute(tempstr, "1").ToStdString();
if ((sc[0] == '@' || sc[0] == '<' || sc[0] == '>') && sc.size() > 1) {
- std::string mn = sc.substr(1, sc.find(':')-1);
+ std::string mn = sc.substr(1, sc.find(':') - 1);
if (mn == oldName) {
sc = sc[0] + newName + sc.substr(sc.find(':'), sc.size());
ModelXml->DeleteAttribute(tempstr);
@@ -2612,52 +2684,43 @@ bool Model::IsValidStartChannelString() const
sc = ModelXml->GetAttribute("StartChannel").Trim(true).Trim(false);
}
- if (sc.IsNumber() && wxAtol(sc) > 0 && ! sc.Contains('.')) return true; // absolule
+ if (sc.IsNumber() && wxAtol(sc) > 0 && !sc.Contains('.'))
+ return true; // absolule
- if (!sc.Contains(':')) return false; // all other formats need a colon
+ if (!sc.Contains(':'))
+ return false; // all other formats need a colon
wxArrayString parts = wxSplit(sc, ':');
- if (parts.size() > 3) return false;
+ if (parts.size() > 3)
+ return false;
- if (parts[0][0] == '#')
- {
- if (parts.size() == 2)
- {
+ if (parts[0][0] == '#') {
+ if (parts.size() == 2) {
Output* o = modelManager.GetOutputManager()->GetOutput(wxAtoi(parts[0].substr(1)), "");
if (o != nullptr &&
- (parts[1].Trim(true).Trim(false).IsNumber() && wxAtol(parts[1]) > 0 && !parts[1].Contains('.')))
- {
+ (parts[1].Trim(true).Trim(false).IsNumber() && wxAtol(parts[1]) > 0 && !parts[1].Contains('.'))) {
return true;
}
- }
- else if (parts.size() == 3)
- {
+ } else if (parts.size() == 3) {
wxString ip = parts[0].substr(1);
Output* o = modelManager.GetOutputManager()->GetOutput(wxAtoi(parts[1]), ip.ToStdString());
if (ip_utils::IsIPValidOrHostname(ip.ToStdString()) && o != nullptr &&
- (parts[2].Trim(true).Trim(false).IsNumber() && wxAtol(parts[2]) > 0 && !parts[2].Contains('.')))
- {
+ (parts[2].Trim(true).Trim(false).IsNumber() && wxAtol(parts[2]) > 0 && !parts[2].Contains('.'))) {
return true;
}
}
- }
- else if (parts[0][0] == '>' || parts[0][0] == '@')
- {
+ } else if (parts[0][0] == '>' || parts[0][0] == '@') {
if ((parts.size() == 2) &&
(parts[0].Trim(true).Trim(false).substr(1) != GetName()) && // self referencing
- (parts[1].Trim(true).Trim(false).IsNumber() && wxAtol(parts[1]) > 0 && !parts[1].Contains('.')))
- {
+ (parts[1].Trim(true).Trim(false).IsNumber() && wxAtol(parts[1]) > 0 && !parts[1].Contains('.'))) {
// dont bother checking the model name ... other processes will check for that
return true;
}
- }
- else if (parts[0][0] == '!')
- {
+ } else if (parts[0][0] == '!') {
if ((parts.size() == 2) &&
(modelManager.GetOutputManager()->GetController(Trim(parts[0].substr(1))) != nullptr) &&
- (parts[1].Trim(true).Trim(false).IsNumber() && wxAtol(parts[1]) > 0 && !parts[1].Contains('.')))
- {
+ (parts[1].Trim(true).Trim(false).IsNumber() && wxAtol(parts[1]) > 0 && !parts[1].Contains('.'))) {
return true;
}
}
@@ -2674,21 +2737,17 @@ bool Model::UpdateStartChannelFromChannelString(std::map& m
std::string dependsonmodel;
ModelStartChannel = ModelXml->GetAttribute("StartChannel");
int32_t StartChannel = GetNumberFromChannelString(ModelStartChannel, valid, dependsonmodel);
- while (!valid && dependsonmodel != "" && std::find(used.begin(), used.end(), dependsonmodel) == used.end())
- {
+ while (!valid && dependsonmodel != "" && std::find(used.begin(), used.end(), dependsonmodel) == used.end()) {
Model* m = models[dependsonmodel];
- if (m != nullptr)
- {
+ if (m != nullptr) {
valid = m->UpdateStartChannelFromChannelString(models, used);
}
- if (valid)
- {
+ if (valid) {
StartChannel = GetNumberFromChannelString(ModelStartChannel, valid, dependsonmodel);
}
}
- if (valid)
- {
+ if (valid) {
size_t NumberOfStrings = HasOneString(DisplayAs) ? 1 : parm1;
int ChannelsPerString = CalcCannelsPerString();
SetStringStartChannels(zeroBased, NumberOfStrings, StartChannel, ChannelsPerString);
@@ -2698,13 +2757,15 @@ bool Model::UpdateStartChannelFromChannelString(std::map& m
return valid;
}
-int Model::GetNumberFromChannelString(const std::string &sc) const {
+int Model::GetNumberFromChannelString(const std::string& sc) const
+{
bool v = false;
std::string dependsonmodel;
return GetNumberFromChannelString(sc, v, dependsonmodel);
}
-int Model::GetNumberFromChannelString(const std::string &str, bool &valid, std::string& dependsonmodel) const {
+int Model::GetNumberFromChannelString(const std::string& str, bool& valid, std::string& dependsonmodel) const
+{
std::string sc(Trim(str));
valid = true;
if (sc.find(":") != std::string::npos) {
@@ -2715,8 +2776,7 @@ int Model::GetNumberFromChannelString(const std::string &str, bool &valid, std::
bool chain = start[0] == '>';
bool fromStart = start[0] == '@';
start = Trim(start.substr(1, start.size()));
- if (start == GetName() && !CouldComputeStartChannel)
- {
+ if (start == GetName() && !CouldComputeStartChannel) {
valid = false;
} else {
if (start != GetName()) {
@@ -2730,80 +2790,67 @@ int Model::GetNumberFromChannelString(const std::string &str, bool &valid, std::
i = stringStartChan[0];
}
int res = i + returnChannel;
- if (res < 1)
- {
+ if (res < 1) {
valid = false;
res = 1;
}
return res;
- }
- else {
+ } else {
int res = m->GetLastChannel() + returnChannel + 1;
- if (res < 1)
- {
+ if (res < 1) {
valid = false;
res = 1;
}
return res;
}
- }
- else {
+ } else {
valid = false;
}
}
- } else if (start[0] == '!') {
+ } else if (start[0] == '!') {
if (sc.find_first_of(':') == std::string::npos) {
std::string cs = Trim(start.substr(1));
Controller* c = modelManager.GetOutputManager()->GetController(cs);
- if (c != nullptr) {
+ if (c != nullptr && c->GetProtocol() != OUTPUT_PLAYER_ONLY) {
return c->GetStartChannel() - 1 + wxAtoi(sc);
}
}
valid = false;
return 1;
- }
- else if (start[0] == '#') {
+ } else if (start[0] == '#') {
wxString ss = wxString(str);
wxArrayString cs = wxSplit(ss.SubString(1, ss.Length()), ':');
- if (cs.Count() == 3)
- {
+ if (cs.Count() == 3) {
// #ip:universe:channel
int returnUniverse = wxAtoi(cs[1]);
int returnChannel = wxAtoi(cs[2]);
int res = modelManager.GetOutputManager()->GetAbsoluteChannel(cs[0].Trim(false).Trim(true).ToStdString(), returnUniverse - 1, returnChannel - 1);
- if (res < 1)
- {
+ if (res < 1) {
res = 1;
valid = false;
}
return res;
- }
- else if (cs.Count() == 2)
- {
+ } else if (cs.Count() == 2) {
// #universe:channel
int returnChannel = wxAtoi(sc);
int returnUniverse = wxAtoi(ss.SubString(1, ss.Find(":") - 1));
// find output based on universe number ...
int res = modelManager.GetOutputManager()->GetAbsoluteChannel("", returnUniverse - 1, returnChannel - 1);
- if (res < 1)
- {
+ if (res < 1) {
res = 1;
valid = false;
}
return res;
- }
- else
- {
+ } else {
valid = false;
return 1;
}
}
}
int returnChannel = wxAtoi(sc);
- if (returnChannel < 1)
- {
+ if (returnChannel < 1) {
valid = false;
returnChannel = 1;
}
@@ -2816,8 +2863,7 @@ std::list Model::ParseFaceNodes(std::string channels)
std::list res;
wxStringTokenizer wtkz(channels, ",");
- while (wtkz.HasMoreTokens())
- {
+ while (wtkz.HasMoreTokens()) {
wxString valstr = wtkz.GetNextToken();
int start, end;
@@ -2825,9 +2871,9 @@ std::list Model::ParseFaceNodes(std::string channels)
int idx = valstr.Index('-');
start = wxAtoi(valstr.Left(idx));
end = wxAtoi(valstr.Right(valstr.size() - idx - 1));
- if (end < start) std::swap(start, end);
- }
- else {
+ if (end < start)
+ std::swap(start, end);
+ } else {
start = end = wxAtoi(valstr);
}
if (start > end) {
@@ -2835,7 +2881,7 @@ std::list Model::ParseFaceNodes(std::string channels)
}
start--;
end--;
- for (int n = start; n <= end; n++) {
+ for (int n = start; n <= end; ++n) {
res.push_back(n);
}
}
@@ -2845,7 +2891,6 @@ std::list Model::ParseFaceNodes(std::string channels)
void Model::SetFromXml(wxXmlNode* ModelNode, bool zb)
{
-
if (modelDimmingCurve != nullptr) {
delete modelDimmingCurve;
modelDimmingCurve = nullptr;
@@ -2886,7 +2931,7 @@ void Model::SetFromXml(wxXmlNode* ModelNode, bool zb)
SetShadowModelFor("");
}
- //this needs to be done before GetNodeChannelCount call
+ // this needs to be done before GetNodeChannelCount call
bool found = true;
int index = 0;
while (found) {
@@ -2894,8 +2939,7 @@ void Model::SetFromXml(wxXmlNode* ModelNode, bool zb)
auto v = ModelXml->GetAttribute(an, "");
if (v == "") {
found = false;
- }
- else {
+ } else {
superStringColours.push_back(wxColour(v));
}
index++;
@@ -2906,21 +2950,22 @@ void Model::SetFromXml(wxXmlNode* ModelNode, bool zb)
SingleChannel = (ncc == 1) && StringType != "Node Single Color";
if (SingleNode) {
rgbOrder = "RGB";
- }
- else if (ncc == 4 && StringType[0] == 'W') {
+ } else if (ncc == 4 && StringType[0] == 'W') {
rgbOrder = StringType.substr(1, 4);
- }
- else {
+ } else {
rgbOrder = StringType.substr(0, 3);
}
if (ncc == 4) {
std::string s = ModelNode->GetAttribute("RGBWHandling").ToStdString();
- for (int x = 0; x < RGBW_HANDLING.size(); x++) {
+ for (int x = 0; x < RGBW_HANDLING.size(); ++x) {
if (RGBW_HANDLING[x] == s) {
rgbwHandlingType = x;
}
}
+ } else {
+ rgbwHandlingType = 1; // RGB
}
+
description = UnXmlSafe(ModelNode->GetAttribute("Description"));
wxString tempstr = ModelNode->GetAttribute("parm1");
@@ -2936,12 +2981,10 @@ void Model::SetFromXml(wxXmlNode* ModelNode, bool zb)
if (tempstr[0] == ',') {
t2 = "";
tempstr = tempstr(1, tempstr.length());
- }
- else if (tempstr.Contains(",")) {
+ } else if (tempstr.Contains(",")) {
t2 = tempstr.SubString(0, tempstr.Find(",") - 1);
tempstr = tempstr.SubString(tempstr.Find(",") + 1, tempstr.length());
- }
- else {
+ } else {
tempstr = "";
}
strandNames.push_back(t2);
@@ -2953,27 +2996,24 @@ void Model::SetFromXml(wxXmlNode* ModelNode, bool zb)
if (tempstr[0] == ',') {
t2 = "";
tempstr = tempstr(1, tempstr.length());
- }
- else if (tempstr.Contains(",")) {
+ } else if (tempstr.Contains(",")) {
t2 = tempstr.SubString(0, tempstr.Find(",") - 1);
tempstr = tempstr.SubString(tempstr.Find(",") + 1, tempstr.length());
- }
- else {
+ } else {
tempstr = "";
}
nodeNames.push_back(t2);
}
CouldComputeStartChannel = false;
- std::string dependsonmodel;
+ std::string dependsonmodel;
int32_t StartChannel = GetNumberFromChannelString(ModelNode->GetAttribute("StartChannel", "1").ToStdString(), CouldComputeStartChannel, dependsonmodel);
tempstr = ModelNode->GetAttribute("Dir");
IsLtoR = tempstr != "R";
if (ModelNode->HasAttribute("StartSide")) {
tempstr = ModelNode->GetAttribute("StartSide");
isBotToTop = (tempstr == "B");
- }
- else {
+ } else {
isBotToTop = true;
}
customColor = xlColor(ModelNode->GetAttribute("CustomColor", "#000000").ToStdString());
@@ -3004,7 +3044,7 @@ void Model::SetFromXml(wxXmlNode* ModelNode, bool zb)
InitModel();
size_t NodeCount = GetNodeCount();
- for (size_t i = 0; i < NodeCount; i++) {
+ for (size_t i = 0; i < NodeCount; ++i) {
Nodes[i]->sparkle = rand() % 10000;
}
@@ -3016,18 +3056,14 @@ void Model::SetFromXml(wxXmlNode* ModelNode, bool zb)
while (f != nullptr) {
if ("faceInfo" == f->GetName()) {
ParseFaceInfo(f, faceInfo);
- }
- else if ("stateInfo" == f->GetName()) {
+ } else if ("stateInfo" == f->GetName()) {
ParseStateInfo(f, stateInfo);
- }
- else if ("dimmingCurve" == f->GetName()) {
+ } else if ("dimmingCurve" == f->GetName()) {
dimmingCurveNode = f;
modelDimmingCurve = DimmingCurve::createFromXML(f);
- }
- else if ("subModel" == f->GetName()) {
+ } else if ("subModel" == f->GetName()) {
ParseSubModel(f);
- }
- else if ("ControllerConnection" == f->GetName()) {
+ } else if ("ControllerConnection" == f->GetName()) {
controllerConnectionNode = f;
}
f = f->GetNext();
@@ -3074,7 +3110,8 @@ void Model::SetFromXml(wxXmlNode* ModelNode, bool zb)
std::string Model::GetControllerConnectionString() const
{
- if (GetControllerProtocol() == "") return "";
+ if (GetControllerProtocol() == "")
+ return "";
std::string ret = wxString::Format("%s:%d", GetControllerProtocol(), GetControllerPort(1)).ToStdString();
ret += GetControllerConnectionAttributeString();
@@ -3083,7 +3120,8 @@ std::string Model::GetControllerConnectionString() const
std::string Model::GetControllerConnectionRangeString() const
{
- if (GetControllerProtocol() == "") return "";
+ if (GetControllerProtocol() == "")
+ return "";
std::string ret = wxString::Format("%s:%d", GetControllerProtocol(), GetControllerPort(1)).ToStdString();
if (GetControllerPort(1) == 0) {
ret = wxString::Format("%s", GetControllerProtocol()).ToStdString();
@@ -3106,7 +3144,8 @@ std::string Model::GetControllerConnectionPortRangeString() const
return ret;
}
-bool compare_pairstring(const std::pair& a, const std::pair& b) {
+bool compare_pairstring(const std::pair& a, const std::pair& b)
+{
return a.first > b.first;
}
@@ -3117,9 +3156,9 @@ std::string Model::GetControllerConnectionAttributeString() const
wxXmlAttribute* att = GetControllerConnection()->GetAttributes();
while (att != nullptr) {
if (att->GetName() == "SmartRemote") {
- props.push_back({"SmartRemote", DecodeSmartRemote(wxAtoi(att->GetValue()))});
+ props.push_back({ "SmartRemote", DecodeSmartRemote(wxAtoi(att->GetValue())) });
} else if (att->GetName() != "Port" && att->GetName() != "Protocol" && att->GetName() != "SRMaxCascade" && att->GetName() != "SRCascadeOnPort" && att->GetName() != "SmartRemoteType") {
- props.push_back({att->GetName(), att->GetValue()});
+ props.push_back({ att->GetName(), att->GetValue() });
}
att = att->GetNext();
}
@@ -3136,8 +3175,7 @@ std::string Model::GetControllerConnectionAttributeString() const
void Model::ReplaceIPInStartChannels(const std::string& oldIP, const std::string& newIP)
{
bool changed = false;
- if (Contains(ModelStartChannel, oldIP))
- {
+ if (Contains(ModelStartChannel, oldIP)) {
wxString sc(ModelStartChannel);
sc.Replace(oldIP, newIP);
SetStartChannel(sc);
@@ -3145,12 +3183,11 @@ void Model::ReplaceIPInStartChannels(const std::string& oldIP, const std::string
}
size_t NumberOfStrings = HasOneString(DisplayAs) ? 1 : parm1;
- for (int i = 0; i < NumberOfStrings; i++) {
+ for (int i = 0; i < NumberOfStrings; ++i) {
auto tempstr = StartChanAttrName(i);
if (ModelXml->HasAttribute(tempstr)) {
wxString sc = ModelXml->GetAttribute(tempstr, "");
- if (Contains(sc, oldIP))
- {
+ if (Contains(sc, oldIP)) {
sc.Replace(oldIP, newIP);
ModelXml->DeleteAttribute(tempstr);
ModelXml->AddAttribute(tempstr, sc);
@@ -3158,8 +3195,7 @@ void Model::ReplaceIPInStartChannels(const std::string& oldIP, const std::string
}
}
}
- if (changed)
- {
+ if (changed) {
AddASAPWork(OutputModelManager::WORK_MODELS_CHANGE_REQUIRING_RERENDER, "Model::ReplaceIPInStartChannels");
AddASAPWork(OutputModelManager::WORK_RGBEFFECTS_CHANGE, "Model::ReplaceIPInStartChannels");
AddASAPWork(OutputModelManager::WORK_MODELS_REWORK_STARTCHANNELS, "Model::ReplaceIPInStartChannels");
@@ -3170,28 +3206,32 @@ void Model::ReplaceIPInStartChannels(const std::string& oldIP, const std::string
std::string Model::DecodeSmartRemote(int sr)
{
- if(sr == 0) return "None";
- return std::string (1, ('A' + sr - 1));
+ if (sr == 0)
+ return "None";
+ return std::string(1, ('A' + sr - 1));
}
-wxXmlNode *Model::GetControllerConnection() const {
- if (GetModelXml() == nullptr) return nullptr;
+wxXmlNode* Model::GetControllerConnection() const
+{
+ if (GetModelXml() == nullptr)
+ return nullptr;
- wxXmlNode *n = GetModelXml()->GetChildren();
+ wxXmlNode* n = GetModelXml()->GetChildren();
while (n != nullptr) {
if (n->GetName() == "ControllerConnection") {
return n;
}
n = n->GetNext();
}
- n = new wxXmlNode(wxXML_ELEMENT_NODE , "ControllerConnection");
+ n = new wxXmlNode(wxXML_ELEMENT_NODE, "ControllerConnection");
GetModelXml()->AddChild(n);
return n;
}
-void Model::RemoveSubModel(const std::string &name) {
+void Model::RemoveSubModel(const std::string& name)
+{
for (auto a = subModels.begin(); a != subModels.end(); ++a) {
- Model *m = *a;
+ Model* m = *a;
if (m->GetName() == name) {
delete m;
subModels.erase(a);
@@ -3199,7 +3239,8 @@ void Model::RemoveSubModel(const std::string &name) {
}
}
-Model *Model::GetSubModel(const std::string &name) const {
+Model* Model::GetSubModel(const std::string& name) const
+{
for (auto a = subModels.begin(); a != subModels.end(); ++a) {
if ((*a)->GetName() == name) {
return *a;
@@ -3210,54 +3251,56 @@ Model *Model::GetSubModel(const std::string &name) const {
std::string Model::GenerateUniqueSubmodelName(const std::string suggested) const
{
- if (GetSubModel(suggested) == nullptr) return suggested;
+ if (GetSubModel(suggested) == nullptr)
+ return suggested;
int i = 2;
- for (;;)
- {
+ for (;;) {
auto name = wxString::Format("%s_%d", suggested, i++);
- if (GetSubModel(name) == nullptr) return name;
+ if (GetSubModel(name) == nullptr)
+ return name;
}
}
-void Model::ParseSubModel(wxXmlNode *node) {
+void Model::ParseSubModel(wxXmlNode* node)
+{
subModels.push_back(new SubModel(this, node));
}
-int Model::CalcCannelsPerString() {
- int ChannelsPerString = parm2*GetNodeChannelCount(StringType);
+int Model::CalcCannelsPerString()
+{
+ int ChannelsPerString = parm2 * GetNodeChannelCount(StringType);
if (SingleChannel)
- ChannelsPerString=1;
+ ChannelsPerString = 1;
else if (SingleNode)
- ChannelsPerString=GetNodeChannelCount(StringType);
+ ChannelsPerString = GetNodeChannelCount(StringType);
return ChannelsPerString;
}
-void Model::SetStringStartChannels(bool zeroBased, int NumberOfStrings, int StartChannel, int ChannelsPerString) {
+void Model::SetStringStartChannels(bool zeroBased, int NumberOfStrings, int StartChannel, int ChannelsPerString)
+{
std::string tempstr = ModelXml->GetAttribute("Advanced", "0").ToStdString();
- bool HasIndividualStartChans=tempstr == "1";
+ bool HasIndividualStartChans = tempstr == "1";
stringStartChan.clear();
stringStartChan.resize(NumberOfStrings);
- for (int i=0; iHasAttribute(tempstr)) {
bool b = false;
std::string dependsonmodel;
- stringStartChan[i] = GetNumberFromChannelString(ModelXml->GetAttribute(tempstr, "1").ToStdString(), b, dependsonmodel)-1;
+ stringStartChan[i] = GetNumberFromChannelString(ModelXml->GetAttribute(tempstr, "1").ToStdString(), b, dependsonmodel) - 1;
CouldComputeStartChannel &= b;
} else {
- stringStartChan[i] = (zeroBased? 0 : StartChannel-1) + i*ChannelsPerString;
+ stringStartChan[i] = (zeroBased ? 0 : StartChannel - 1) + i * ChannelsPerString;
}
}
}
int Model::FindNodeAtXY(int bufx, int bufy)
{
- for (int i = 0; i < Nodes.size(); ++i)
- {
- if ((bufx == -1 || Nodes[i]->Coords[0].bufX == bufx) && (bufy == -1 || Nodes[i]->Coords[0].bufY == bufy))
- {
+ for (int i = 0; i < Nodes.size(); ++i) {
+ if ((bufx == -1 || Nodes[i]->Coords[0].bufX == bufx) && (bufy == -1 || Nodes[i]->Coords[0].bufY == bufy)) {
return i;
}
}
@@ -3265,55 +3308,60 @@ int Model::FindNodeAtXY(int bufx, int bufy)
return -1;
}
-void Model::InitModel() {
+void Model::InitModel()
+{
}
-void Model::GetNodeChannelValues(size_t nodenum, unsigned char *buf) {
+void Model::GetNodeChannelValues(size_t nodenum, unsigned char* buf)
+{
wxASSERT(nodenum < Nodes.size()); // trying to catch an error i can see in crash reports
if (nodenum < Nodes.size()) {
Nodes[nodenum]->GetForChannels(buf);
}
}
-void Model::SetNodeChannelValues(size_t nodenum, const unsigned char *buf) {
+void Model::SetNodeChannelValues(size_t nodenum, const unsigned char* buf)
+{
wxASSERT(nodenum < Nodes.size()); // trying to catch an error i can see in crash reports
if (nodenum < Nodes.size()) {
Nodes[nodenum]->SetFromChannels(buf);
}
}
-xlColor Model::GetNodeColor(size_t nodenum) const {
+xlColor Model::GetNodeColor(size_t nodenum) const
+{
wxASSERT(nodenum < Nodes.size()); // trying to catch an error i can see in crash reports
xlColor color;
if (nodenum < Nodes.size()) {
Nodes[nodenum]->GetColor(color);
- }
- else
- {
+ } else {
color = xlWHITE;
}
return color;
}
-xlColor Model::GetNodeMaskColor(size_t nodenum) const {
- if (nodenum >= Nodes.size()) return xlWHITE; // this shouldnt happen but it does if you have a custom model with no nodes in it
+xlColor Model::GetNodeMaskColor(size_t nodenum) const
+{
+ if (nodenum >= Nodes.size())
+ return xlWHITE; // this shouldnt happen but it does if you have a custom model with no nodes in it
xlColor color;
Nodes[nodenum]->GetMaskColor(color);
return color;
}
-void Model::SetNodeColor(size_t nodenum, const xlColor &c) {
+void Model::SetNodeColor(size_t nodenum, const xlColor& c)
+{
wxASSERT(nodenum < Nodes.size()); // trying to catch an error i can see in crash reports
if (nodenum < Nodes.size()) {
Nodes[nodenum]->SetColor(c);
}
}
-bool Model::IsNodeInBufferRange(size_t nodeNum, int x1, int y1, int x2, int y2) {
+bool Model::IsNodeInBufferRange(size_t nodeNum, int x1, int y1, int x2, int y2)
+{
if (nodeNum < Nodes.size()) {
for (auto a = Nodes[nodeNum]->Coords.begin(); a != Nodes[nodeNum]->Coords.end(); ++a) {
- if (a->bufX >= x1 && a->bufX <= x2
- && a->bufY >= y1 && a->bufY <= y2) {
+ if (a->bufX >= x1 && a->bufX <= x2 && a->bufY >= y1 && a->bufY <= y2) {
return true;
}
}
@@ -3322,26 +3370,23 @@ bool Model::IsNodeInBufferRange(size_t nodeNum, int x1, int y1, int x2, int y2)
}
// only valid for rgb nodes and dumb strings (not traditional strings)
-wxChar Model::GetChannelColorLetter(wxByte chidx) {
+wxChar Model::GetChannelColorLetter(wxByte chidx)
+{
return rgbOrder[chidx];
}
char Model::EncodeColour(const xlColor& c)
{
- if (c.red > 0 && c.green == 0 && c.blue == 0)
- {
+ if (c.red > 0 && c.green == 0 && c.blue == 0) {
return 'R';
}
- if (c.red == 0 && c.green > 0 && c.blue == 0)
- {
+ if (c.red == 0 && c.green > 0 && c.blue == 0) {
return 'G';
}
- if (c.red == 0 && c.green == 0 && c.blue > 0)
- {
+ if (c.red == 0 && c.green == 0 && c.blue > 0) {
return 'B';
}
- if (c.red > 0 && c.red == c.green && c.red == c.blue)
- {
+ if (c.red > 0 && c.red == c.green && c.red == c.blue) {
return 'W';
}
@@ -3353,7 +3398,8 @@ char Model::EncodeColour(const xlColor& c)
char Model::GetAbsoluteChannelColorLetter(int32_t absoluteChannel)
{
int32_t fc = GetFirstChannel();
- if (absoluteChannel < fc + 1 || absoluteChannel > (int32_t)GetLastChannel() + 1) return ' ';
+ if (absoluteChannel < fc + 1 || absoluteChannel > (int32_t)GetLastChannel() + 1)
+ return ' ';
if (SingleChannel) {
return EncodeColour(GetNodeMaskColor(0));
@@ -3377,28 +3423,17 @@ std::string Model::GetControllerPortSortString() const
std::string Model::GetStartChannelInDisplayFormat(OutputManager* outputManager)
{
auto s = Trim(ModelStartChannel);
- if (!IsValidStartChannelString())
- {
+ if (!IsValidStartChannelString()) {
return "(1)";
- }
- else if (s[0] == '>')
- {
+ } else if (s[0] == '>') {
return s + wxString::Format(" (%u)", GetFirstChannel() + 1);
- }
- else if (s[0] == '@')
- {
+ } else if (s[0] == '@') {
return s + wxString::Format(" (%u)", GetFirstChannel() + 1);
- }
- else if (s[0] == '!')
- {
+ } else if (s[0] == '!') {
return s + wxString::Format(" (%u)", GetFirstChannel() + 1);
- }
- else if (s[0] == '#')
- {
+ } else if (s[0] == '#') {
return GetFirstChannelInStartChannelFormat(outputManager);
- }
- else
- {
+ } else {
return wxString::Format("%u", GetFirstChannel() + 1);
}
}
@@ -3408,7 +3443,8 @@ std::string Model::GetLastChannelInStartChannelFormat(OutputManager* outputManag
return GetChannelInStartChannelFormat(outputManager, GetLastChannel() + 1);
}
-std::string Model::GetChannelInStartChannelFormat(OutputManager* outputManager, uint32_t channel) {
+std::string Model::GetChannelInStartChannelFormat(OutputManager* outputManager, uint32_t channel)
+{
std::list visitedModels;
visitedModels.push_back(GetName());
@@ -3416,46 +3452,33 @@ std::string Model::GetChannelInStartChannelFormat(OutputManager* outputManager,
char firstChar = modelFormat[0];
bool done = false;
- while (!done && (firstChar == '@' || firstChar == '>') && CountChar(modelFormat, ':') == 1)
- {
+ while (!done && (firstChar == '@' || firstChar == '>') && CountChar(modelFormat, ':') == 1) {
std::string referencedModel = Trim(modelFormat.substr(1, modelFormat.find(':') - 1));
Model* m = modelManager[referencedModel];
- if (m != nullptr && std::find(visitedModels.begin(), visitedModels.end(), referencedModel) == visitedModels.end())
- {
+ if (m != nullptr && std::find(visitedModels.begin(), visitedModels.end(), referencedModel) == visitedModels.end()) {
modelFormat = Trim(m->ModelStartChannel);
firstChar = modelFormat[0];
- }
- else
- {
+ } else {
done = true;
}
visitedModels.push_back(referencedModel);
}
- if (!modelFormat.empty())
- {
- if (modelFormat[0] == '#')
- {
+ if (!modelFormat.empty()) {
+ if (modelFormat[0] == '#') {
firstChar = '#';
- }
- else if (modelFormat[0] == '!')
- {
+ } else if (modelFormat[0] == '!') {
firstChar = '!';
- }
- else if (CountChar(modelFormat, ':') == 1)
- {
+ } else if (CountChar(modelFormat, ':') == 1) {
firstChar = '0';
}
- }
- else
- {
+ } else {
firstChar = '0';
modelFormat = "0";
}
- if (firstChar == '#')
- {
+ if (firstChar == '#') {
// universe:channel
int32_t startChannel;
Output* output = outputManager->GetOutput(channel, startChannel);
@@ -3465,45 +3488,34 @@ std::string Model::GetChannelInStartChannelFormat(OutputManager* outputManager,
}
// This should not be the case any more
- //if (output->IsOutputCollection())
+ // if (output->IsOutputCollection())
//{
// output = output->GetActualOutput(channel);
// startChannel = channel - output->GetStartChannel() + 1;
//}
- if (CountChar(modelFormat, ':') == 1)
- {
+ if (CountChar(modelFormat, ':') == 1) {
return wxString::Format("#%d:%d (%u)", output->GetUniverse(), startChannel, channel).ToStdString();
- }
- else
- {
+ } else {
std::string ip = "";
- if (output->IsIpOutput())
- {
+ if (output->IsIpOutput()) {
ip = ((IPOutput*)output)->GetIP();
}
return wxString::Format("#%s:%d:%d (%u)", ip, output->GetUniverse(), startChannel, channel).ToStdString();
}
- }
- else if (firstChar == '!')
- {
+ } else if (firstChar == '!') {
auto comps = wxSplit(modelFormat, ':');
auto c = outputManager->GetController(Trim(comps[0].substr(1)));
int32_t start = 1;
- if (c != nullptr)
- {
+ if (c != nullptr) {
start = c->GetStartChannel();
}
unsigned int lastChannel = GetLastChannel() + 1;
return wxString(modelFormat).BeforeFirst(':').Trim(true).Trim(false) + ":" + wxString::Format("%d (%u)", lastChannel - start + 1, lastChannel);
- }
- else if (firstChar == '@' || firstChar == '>' || CountChar(modelFormat, ':') == 0)
- {
+ } else if (firstChar == '@' || firstChar == '>' || CountChar(modelFormat, ':') == 0) {
// absolute
return std::to_string(channel);
- }
- else
- {
+ } else {
// This used to be output:sc ... but that is no longer valid
return std::to_string(channel);
}
@@ -3514,28 +3526,28 @@ std::string Model::GetFirstChannelInStartChannelFormat(OutputManager* outputMana
return GetChannelInStartChannelFormat(outputManager, GetFirstChannel() + 1);
}
-uint32_t Model::GetLastChannel() const {
+uint32_t Model::GetLastChannel() const
+{
uint32_t LastChan = 0;
size_t NodeCount = GetNodeCount();
- for (size_t idx = 0; idx < NodeCount; idx++) {
- if (Nodes[idx]->ActChan == (unsigned int)-1)
- {
+ for (size_t idx = 0; idx < NodeCount; ++idx) {
+ if (Nodes[idx]->ActChan == (unsigned int)-1) {
return (unsigned int)NodeCount * Nodes[idx]->GetChanCount() - 1;
}
unsigned int lc = std::max(LastChan, Nodes[idx]->ActChan + Nodes[idx]->GetChanCount() - 1);
- if (lc > LastChan)
- {
+ if (lc > LastChan) {
LastChan = lc;
}
}
return LastChan;
}
-//zero based channel number, i.e. 0 is the first channel
-uint32_t Model::GetFirstChannel() const {
+// zero based channel number, i.e. 0 is the first channel
+uint32_t Model::GetFirstChannel() const
+{
uint32_t FirstChan = 0xFFFFFFFF;
size_t NodeCount = GetNodeCount();
- for (size_t idx = 0; idx < NodeCount; idx++) {
+ for (size_t idx = 0; idx < NodeCount; ++idx) {
uint32_t fc = std::min(FirstChan, Nodes[idx]->ActChan);
if (fc < FirstChan) {
FirstChan = fc;
@@ -3544,12 +3556,13 @@ uint32_t Model::GetFirstChannel() const {
return FirstChan;
}
-unsigned int Model::GetNumChannels() {
+unsigned int Model::GetNumChannels()
+{
return GetLastChannel() - GetFirstChannel() + 1;
}
-void Model::SetPosition(double posx, double posy) {
-
+void Model::SetPosition(double posx, double posy)
+{
if (GetModelScreenLocation().IsLocked() || IsFromBase())
return;
@@ -3560,61 +3573,66 @@ void Model::SetPosition(double posx, double posy) {
// initialize screen coordinates
// parm1=Number of Strings/Arches/Canes
// parm2=Pixels Per String/Arch/Cane
-void Model::SetLineCoord() {
- float x,y;
- float idx=0;
- size_t NodeCount=GetNodeCount();
- int numlights=parm1*parm2;
- float half=numlights/2;
- GetModelScreenLocation().SetRenderSize(numlights, numlights*2);
-
- for(size_t n=0; nCoords[c].screenX=x;
- Nodes[n]->Coords[c].screenY=y + numlights;
+void Model::SetLineCoord()
+{
+ float x, y;
+ float idx = 0;
+ size_t NodeCount = GetNodeCount();
+ int numlights = parm1 * parm2;
+ float half = numlights / 2;
+ GetModelScreenLocation().SetRenderSize(numlights, numlights * 2);
+
+ for (size_t n = 0; n < NodeCount; ++n) {
+ size_t CoordCount = GetCoordCount(n);
+ for (size_t c = 0; c < CoordCount; ++c) {
+ x = idx;
+ x = IsLtoR ? x - half : half - x;
+ y = 0;
+ Nodes[n]->Coords[c].screenX = x;
+ Nodes[n]->Coords[c].screenY = y + numlights;
idx++;
}
}
}
-void Model::SetBufferSize(int NewHt, int NewWi) {
+void Model::SetBufferSize(int NewHt, int NewWi)
+{
BufferHt = NewHt;
BufferWi = NewWi;
IncrementChangeCount();
}
// not valid for Frame or Custom
-int Model::NodesPerString() const {
+int Model::NodesPerString() const
+{
if (SingleNode) {
return 1;
- }
- else {
+ } else {
int ts = GetSmartTs();
if (ts <= 1) {
return parm2;
- }
- else {
+ } else {
return parm2 * ts;
}
}
}
-int32_t Model::NodeStartChannel(size_t nodenum) const {
- return Nodes.size() && nodenum < Nodes.size() ? Nodes[nodenum]->ActChan: 0; //avoid memory access error if no nods -DJ
+int32_t Model::NodeStartChannel(size_t nodenum) const
+{
+ return Nodes.size() && nodenum < Nodes.size() ? Nodes[nodenum]->ActChan : 0; // avoid memory access error if no nods -DJ
}
-int32_t Model::NodeEndChannel(size_t nodenum) const {
- return Nodes.size() && nodenum < Nodes.size() ? Nodes[nodenum]->ActChan + Nodes[nodenum]->GetChanCount() - 1: 0; //avoid memory access error if no nods -DJ
+int32_t Model::NodeEndChannel(size_t nodenum) const
+{
+ return Nodes.size() && nodenum < Nodes.size() ? Nodes[nodenum]->ActChan + Nodes[nodenum]->GetChanCount() - 1 : 0; // avoid memory access error if no nods -DJ
}
-const std::string &Model::NodeType(size_t nodenum) const {
- return Nodes.size() && nodenum < Nodes.size() ? Nodes[nodenum]->GetNodeType(): NodeBaseClass::RGB; //avoid memory access error if no nods -DJ
+const std::string& Model::NodeType(size_t nodenum) const
+{
+ return Nodes.size() && nodenum < Nodes.size() ? Nodes[nodenum]->GetNodeType() : NodeBaseClass::RGB; // avoid memory access error if no nods -DJ
}
-void Model::GetBufferSize(const std::string &type, const std::string &camera, const std::string &transform, int &bufferWi, int &bufferHi, int stagger) const {
+void Model::GetBufferSize(const std::string& type, const std::string& camera, const std::string& transform, int& bufferWi, int& bufferHi, int stagger) const
+{
if (type == DEFAULT) {
bufferHi = this->BufferHt;
bufferWi = this->BufferWi;
@@ -3627,26 +3645,27 @@ void Model::GetBufferSize(const std::string &type, const std::string &camera, co
} else if (type == VERT_PER_STRAND) {
bufferHi = GetNumStrands();
bufferWi = 1;
- for (int x = 0; x < bufferHi; x++) {
+ for (int x = 0; x < bufferHi; ++x) {
bufferWi = std::max(bufferWi, GetStrandLength(x));
}
} else if (type == HORIZ_PER_STRAND) {
bufferWi = GetNumStrands();
bufferHi = 1;
- for (int x = 0; x < bufferWi; x++) {
+ for (int x = 0; x < bufferWi; ++x) {
bufferHi = std::max(bufferHi, GetStrandLength(x));
}
} else {
- //if (type == PER_PREVIEW) {
- //default is to go ahead and build the full node buffer
+ // if (type == PER_PREVIEW) {
+ // default is to go ahead and build the full node buffer
std::vector newNodes;
InitRenderBufferNodes(type, camera, "None", newNodes, bufferWi, bufferHi, stagger);
}
AdjustForTransform(transform, bufferWi, bufferHi);
}
-void Model::AdjustForTransform(const std::string &transform,
- int &bufferWi, int &bufferHi) const {
+void Model::AdjustForTransform(const std::string& transform,
+ int& bufferWi, int& bufferHi) const
+{
if (transform == "Rotate CC 90" || transform == "Rotate CW 90") {
int x = bufferHi;
bufferHi = bufferWi;
@@ -3654,12 +3673,14 @@ void Model::AdjustForTransform(const std::string &transform,
}
}
-static inline void SetCoords(NodeBaseClass::CoordStruct &it2, int x, int y) {
+static inline void SetCoords(NodeBaseClass::CoordStruct& it2, int x, int y)
+{
it2.bufX = x;
it2.bufY = y;
}
-static inline void SetCoords(NodeBaseClass::CoordStruct &it2, int x, int y, int maxX, int maxY, int scale) {
+static inline void SetCoords(NodeBaseClass::CoordStruct& it2, int x, int y, int maxX, int maxY, int scale)
+{
if (maxX != -1) {
x = x * maxX;
x = x / scale;
@@ -3673,14 +3694,11 @@ static inline void SetCoords(NodeBaseClass::CoordStruct &it2, int x, int y, int
}
// this is really slow
-char GetPixelDump(int x, int y, std::vector &newNodes)
+char GetPixelDump(int x, int y, std::vector& newNodes)
{
- for (auto n = newNodes.begin(); n != newNodes.end(); ++n)
- {
- for (auto c = (*n)->Coords.begin(); c != (*n)->Coords.end(); ++c)
- {
- if (c->bufX == x && c->bufY == y)
- {
+ for (auto n = newNodes.begin(); n != newNodes.end(); ++n) {
+ for (auto c = (*n)->Coords.begin(); c != (*n)->Coords.end(); ++c) {
+ if (c->bufX == x && c->bufY == y) {
return '*';
}
}
@@ -3689,17 +3707,15 @@ char GetPixelDump(int x, int y, std::vector &newNodes)
return '-';
}
-void Model::DumpBuffer(std::vector &newNodes,
- int bufferWi, int bufferHt) const
+void Model::DumpBuffer(std::vector& newNodes,
+ int bufferWi, int bufferHt) const
{
- static log4cpp::Category &logger_base = log4cpp::Category::getInstance(std::string("log_base"));
+ static log4cpp::Category& logger_base = log4cpp::Category::getInstance(std::string("log_base"));
logger_base.debug("Dumping render buffer for '%s':", (const char*)GetFullName().c_str());
- for (int y = bufferHt - 1; y >= 0; y--)
- {
+ for (int y = bufferHt - 1; y >= 0; y--) {
std::string line = "";
- for (int x = 0; x < bufferWi; x++)
- {
+ for (int x = 0; x < bufferWi; ++x) {
line += GetPixelDump(x, y, newNodes);
}
logger_base.debug("> %s", (const char*)line.c_str());
@@ -3714,59 +3730,59 @@ void Model::ApplyTransform(const std::string& type,
if (type == "None") {
return;
} else if (type == "Rotate 180") {
- for (size_t x = 0; x < newNodes.size(); x++) {
+ for (size_t x = 0; x < newNodes.size(); ++x) {
for (auto& it2 : newNodes[x]->Coords) {
SetCoords(it2, bufferWi - it2.bufX - 1, bufferHi - it2.bufY - 1);
}
}
} else if (type == "Flip Vertical") {
- for (size_t x = 0; x < newNodes.size(); x++) {
+ for (size_t x = 0; x < newNodes.size(); ++x) {
for (auto& it2 : newNodes[x]->Coords) {
SetCoords(it2, it2.bufX, bufferHi - it2.bufY - 1);
}
}
} else if (type == "Flip Horizontal") {
- for (size_t x = 0; x < newNodes.size(); x++) {
+ for (size_t x = 0; x < newNodes.size(); ++x) {
for (auto& it2 : newNodes[x]->Coords) {
SetCoords(it2, bufferWi - it2.bufX - 1, it2.bufY);
}
}
} else if (type == "Rotate CW 90") {
- for (size_t x = 0; x < newNodes.size(); x++) {
+ for (size_t x = 0; x < newNodes.size(); ++x) {
for (auto& it2 : newNodes[x]->Coords) {
SetCoords(it2, bufferHi - it2.bufY - 1, it2.bufX);
}
}
std::swap(bufferWi, bufferHi);
} else if (type == "Rotate CC 90") {
- for (int x = 0; x < newNodes.size(); x++) {
+ for (int x = 0; x < newNodes.size(); ++x) {
for (auto& it2 : newNodes[x]->Coords) {
SetCoords(it2, it2.bufY, bufferWi - it2.bufX - 1);
}
}
std::swap(bufferWi, bufferHi);
} else if (type == "Rotate CC 90 Flip Horizontal") {
- for (int x = 0; x < newNodes.size(); x++) {
+ for (int x = 0; x < newNodes.size(); ++x) {
for (auto& it2 : newNodes[x]->Coords) {
SetCoords(it2, it2.bufY, bufferWi - it2.bufX - 1);
}
}
std::swap(bufferWi, bufferHi);
- for (size_t x = 0; x < newNodes.size(); x++) {
+ for (size_t x = 0; x < newNodes.size(); ++x) {
for (auto& it2 : newNodes[x]->Coords) {
SetCoords(it2, it2.bufX, bufferHi - it2.bufY - 1);
}
}
} else if (type == "Rotate CW 90 Flip Horizontal") {
- for (size_t x = 0; x < newNodes.size(); x++) {
+ for (size_t x = 0; x < newNodes.size(); ++x) {
for (auto& it2 : newNodes[x]->Coords) {
SetCoords(it2, bufferHi - it2.bufY - 1, it2.bufX);
}
}
std::swap(bufferWi, bufferHi);
- for (size_t x = 0; x < newNodes.size(); x++) {
+ for (size_t x = 0; x < newNodes.size(); ++x) {
for (auto& it2 : newNodes[x]->Coords) {
SetCoords(it2, it2.bufX, bufferHi - it2.bufY - 1);
}
@@ -3774,25 +3790,23 @@ void Model::ApplyTransform(const std::string& type,
}
}
-void Model::InitRenderBufferNodes(const std::string &type, const std::string &camera,
- const std::string &transform,
- std::vector& newNodes, int& bufferWi, int& bufferHt, int stagger, bool deep) const
+void Model::InitRenderBufferNodes(const std::string& type, const std::string& camera,
+ const std::string& transform,
+ std::vector& newNodes, int& bufferWi, int& bufferHt, int stagger, bool deep) const
{
-
- static log4cpp::Category &logger_base = log4cpp::Category::getInstance(std::string("log_base"));
+ static log4cpp::Category& logger_base = log4cpp::Category::getInstance(std::string("log_base"));
int firstNode = newNodes.size();
// want to see if i can catch something that causes this to crash
- if (firstNode + Nodes.size() <= 0)
- {
+ if (firstNode + Nodes.size() <= 0) {
// This seems to happen when an effect is dropped on a strand with zero pixels
// Like a polyline segment with no nodes
- logger_base.warn("Model::InitRenderBufferNodes firstNode + Nodes.size() = %d. %s::'%s'. This commonly happens on a polyline segment with zero pixels or a custom model with no nodes but with effects dropped on it.", (int32_t)firstNode + Nodes.size(), (const char *)GetDisplayAs().c_str(), (const char *)GetFullName().c_str());
+ logger_base.warn("Model::InitRenderBufferNodes firstNode + Nodes.size() = %d. %s::'%s'. This commonly happens on a polyline segment with zero pixels or a custom model with no nodes but with effects dropped on it.", (int32_t)firstNode + Nodes.size(), (const char*)GetDisplayAs().c_str(), (const char*)GetFullName().c_str());
}
// Don't add model group nodes if its a 3D preview render buffer
- if ( !((camera != "2D") && GetDisplayAs() == "ModelGroup" && (type == PER_PREVIEW || type == PER_PREVIEW_NO_OFFSET)) ) {
+ if (!((camera != "2D") && GetDisplayAs() == "ModelGroup" && (type == PER_PREVIEW || type == PER_PREVIEW_NO_OFFSET))) {
newNodes.reserve(firstNode + Nodes.size());
for (auto& it : Nodes) {
newNodes.push_back(NodeBaseClassPtr(it.get()->clone()));
@@ -3802,14 +3816,12 @@ void Model::InitRenderBufferNodes(const std::string &type, const std::string &ca
if (type == DEFAULT) {
bufferHt = this->BufferHt;
bufferWi = this->BufferWi;
- }
- else if (type == SINGLE_LINE) {
+ } else if (type == SINGLE_LINE) {
bufferHt = 1;
bufferWi = newNodes.size();
int cnt = 0;
- for (int x = firstNode; x < newNodes.size(); x++) {
- if (newNodes[x] == nullptr)
- {
+ for (int x = firstNode; x < newNodes.size(); ++x) {
+ if (newNodes[x] == nullptr) {
logger_base.crit("XXX Model::InitRenderBufferNodes newNodes[x] is null ... this is going to crash.");
wxASSERT(false);
}
@@ -3818,13 +3830,11 @@ void Model::InitRenderBufferNodes(const std::string &type, const std::string &ca
}
cnt++;
}
- }
- else if (type == AS_PIXEL) {
+ } else if (type == AS_PIXEL) {
bufferHt = 1;
bufferWi = 1;
- for (int x = firstNode; x < newNodes.size(); x++) {
- if (newNodes[x] == nullptr)
- {
+ for (int x = firstNode; x < newNodes.size(); ++x) {
+ if (newNodes[x] == nullptr) {
logger_base.crit("XXX Model::InitRenderBufferNodes newNodes[x] is null ... this is going to crash.");
wxASSERT(false);
}
@@ -3832,11 +3842,10 @@ void Model::InitRenderBufferNodes(const std::string &type, const std::string &ca
SetCoords(it2, 0, 0);
}
}
- }
- else if (type == HORIZ_PER_STRAND) {
+ } else if (type == HORIZ_PER_STRAND) {
bufferWi = GetNumStrands();
bufferHt = 1;
- for (int x = 0; x < bufferWi; x++) {
+ for (int x = 0; x < bufferWi; ++x) {
bufferHt = std::max(bufferHt, GetStrandLength(x));
}
int cnt = 0;
@@ -3847,17 +3856,14 @@ void Model::InitRenderBufferNodes(const std::string &type, const std::string &ca
strand++;
if (strand < GetNumStrands()) {
strandLen = GetStrandLength(GetMappedStrand(strand));
- }
- else {
+ } else {
// not sure what to do here ... we have more nodes than strands ... so lets just start again
strandLen = GetStrandLength(GetMappedStrand(0));
strand = 0;
}
cnt = 0;
- }
- else {
- if (newNodes[x] == nullptr)
- {
+ } else {
+ if (newNodes[x] == nullptr) {
logger_base.crit("AAA Model::InitRenderBufferNodes newNodes[x] is null ... this is going to crash.");
wxASSERT(false);
}
@@ -3868,11 +3874,10 @@ void Model::InitRenderBufferNodes(const std::string &type, const std::string &ca
x++;
}
}
- }
- else if (type == VERT_PER_STRAND) {
+ } else if (type == VERT_PER_STRAND) {
bufferHt = GetNumStrands();
bufferWi = 1;
- for (int x = 0; x < bufferHt; x++) {
+ for (int x = 0; x < bufferHt; ++x) {
bufferWi = std::max(bufferWi, GetStrandLength(x));
}
int cnt = 0;
@@ -3883,17 +3888,14 @@ void Model::InitRenderBufferNodes(const std::string &type, const std::string &ca
strand++;
if (strand < GetNumStrands()) {
strandLen = GetStrandLength(GetMappedStrand(strand));
- }
- else {
+ } else {
// not sure what to do here ... we have more nodes than strands ... so lets just start again
strandLen = GetStrandLength(GetMappedStrand(0));
strand = 0;
}
cnt = 0;
- }
- else {
- if (newNodes[x] == nullptr)
- {
+ } else {
+ if (newNodes[x] == nullptr) {
logger_base.crit("BBB Model::InitRenderBufferNodes newNodes[x] is null ... this is going to crash.");
wxASSERT(false);
}
@@ -3904,8 +3906,7 @@ void Model::InitRenderBufferNodes(const std::string &type, const std::string &ca
x++;
}
}
- }
- else if (type == PER_PREVIEW || type == PER_PREVIEW_NO_OFFSET) {
+ } else if (type == PER_PREVIEW || type == PER_PREVIEW_NO_OFFSET) {
float maxX = -1000000.0;
float minX = 1000000.0;
float maxY = -1000000.0;
@@ -3913,8 +3914,7 @@ void Model::InitRenderBufferNodes(const std::string &type, const std::string &ca
ModelPreview* modelPreview = nullptr;
PreviewCamera* pcamera = nullptr;
- if (xLightsApp::GetFrame() != nullptr)
- {
+ if (xLightsApp::GetFrame() != nullptr) {
modelPreview = xLightsApp::GetFrame()->GetHousePreview();
pcamera = xLightsApp::GetFrame()->viewpoint_mgr.GetNamedCamera3D(camera);
}
@@ -3927,11 +3927,11 @@ void Model::InitRenderBufferNodes(const std::string &type, const std::string &ca
// For 3D render view buffers recursively process each individual model...should be able to handle nested model groups
if (GetDisplayAs() == "ModelGroup" && camera != "2D") {
- std::vector models;
+ std::vector models;
auto mn = Split(ModelXml->GetAttribute("models").ToStdString(), ',', true);
int nc = 0;
- for (int x = 0; x < mn.size(); x++) {
- Model *c = modelManager.GetModel(mn[x]);
+ for (int x = 0; x < mn.size(); ++x) {
+ Model* c = modelManager.GetModel(mn[x]);
if (c != nullptr) {
models.push_back(c);
nc += c->GetNodeCount();
@@ -3943,7 +3943,7 @@ void Model::InitRenderBufferNodes(const std::string &type, const std::string &ca
if (nc) {
newNodes.reserve(nc);
}
- for (Model *c : models) {
+ for (Model* c : models) {
int bw, bh;
c->InitRenderBufferNodes("Per Preview No Offset", camera, transform, newNodes, bw, bh, stagger);
}
@@ -3952,9 +3952,9 @@ void Model::InitRenderBufferNodes(const std::string &type, const std::string &ca
// We save the transformed coordinates here so we dont have to calculate them all twice
std::vector outx;
std::vector outy;
- outx.reserve(newNodes.size() - firstNode); //common case is one coord per node so size for that
+ outx.reserve(newNodes.size() - firstNode); // common case is one coord per node so size for that
outy.reserve(newNodes.size() - firstNode);
- for (int x = firstNode; x < newNodes.size(); x++) {
+ for (int x = firstNode; x < newNodes.size(); ++x) {
if (newNodes[x] == nullptr) {
logger_base.crit("CCC Model::InitRenderBufferNodes newNodes[x] is null ... this is going to crash.");
wxASSERT(false);
@@ -3969,7 +3969,7 @@ void Model::InitRenderBufferNodes(const std::string &type, const std::string &ca
} else {
if (pcamera == nullptr || camera == "2D") {
// Handle all of the 2D classic transformations
- //float sz = 0;
+ // float sz = 0;
// reintroducing the z coordinate as otherwise with some rotations we end up with a zero width buffer
float sz = it2.screenZ;
GetModelScreenLocation().TranslatePoint(sx, sy, sz);
@@ -4024,17 +4024,17 @@ void Model::InitRenderBufferNodes(const std::string &type, const std::string &ca
}
}
}
- if ((type != PER_PREVIEW_NO_OFFSET) && (((maxX - minX) > 2048) || ((maxY - minY) > 2048))){
+ if ((type != PER_PREVIEW_NO_OFFSET) && (((maxX - minX) > 2048) || ((maxY - minY) > 2048))) {
// this will result in a GIANT render buffer, lets reduce to something we can reasonably render
float fx = ((float)(maxX - minX)) / 2048.0f;
float fy = ((float)(maxY - minY)) / 2048.0f;
factor = fx > fy ? fx : fy;
}
- // if we have a dense model with lots of pixels but (int)(maxx - minx) and (int)(maxy - miny) are really small then it generates a render buffer that is quite small with lots of nodes in each cell
- // We need a factor that scales up the screen locations to separate the pixels
- // The empty space factor is the number of empty cells expected per filled cell in the average model ... of course in models where there are dense and sparse areas this wont necessarily be true
- #define MODEL_EMPTY_SPACE_FACTOR 4.0f
+// if we have a dense model with lots of pixels but (int)(maxx - minx) and (int)(maxy - miny) are really small then it generates a render buffer that is quite small with lots of nodes in each cell
+// We need a factor that scales up the screen locations to separate the pixels
+// The empty space factor is the number of empty cells expected per filled cell in the average model ... of course in models where there are dense and sparse areas this wont necessarily be true
+#define MODEL_EMPTY_SPACE_FACTOR 4.0f
if (type == PER_PREVIEW && GetDisplayAs() != "ModelGroup" && factor == 1.0 && (newNodes.size() * (MODEL_EMPTY_SPACE_FACTOR + 1.0) > (maxX - minX) * (maxY - minY))) {
float deltaX = maxX - minX;
float deltaY = maxY - minY;
@@ -4054,7 +4054,7 @@ void Model::InitRenderBufferNodes(const std::string &type, const std::string &ca
float x = std::sqrt(mx);
factor = deltaX / x;
if (std::max(deltaX / factor, deltaY / factor) > 400) { // if this results in an overly large scaling ... ie a buffer > 400 in any dimension
- factor = std::max(deltaX, deltaY) / 400; // work out a scaling that gives a 400x400 buffer
+ factor = std::max(deltaX, deltaY) / 400; // work out a scaling that gives a 400x400 buffer
}
}
@@ -4062,7 +4062,7 @@ void Model::InitRenderBufferNodes(const std::string &type, const std::string &ca
maxX /= factor;
minY /= factor;
maxY /= factor;
- //logger_base.debug("Factor '%f':", factor);
+ // logger_base.debug("Factor '%f':", factor);
float offx = minX;
float offy = minY;
@@ -4078,13 +4078,12 @@ void Model::InitRenderBufferNodes(const std::string &type, const std::string &ca
if (!(pcamera != nullptr && camera != "2D" && GetDisplayAs() != "ModelGroup" && noOff)) {
auto itx = outx.begin();
auto ity = outy.begin();
- for (int x = firstNode; x < newNodes.size(); x++) {
+ for (int x = firstNode; x < newNodes.size(); ++x) {
if (newNodes[x] == nullptr) {
logger_base.crit("DDD Model::InitRenderBufferNodes newNodes[x] is null ... this is going to crash.");
wxASSERT(false);
}
for (auto& it2 : newNodes[x]->Coords) {
-
// grab the previously transformed coordinate
float sx = *itx / factor;
float sy = *ity / factor;
@@ -4111,27 +4110,23 @@ void Model::InitRenderBufferNodes(const std::string &type, const std::string &ca
if (!noOff) {
bufferHt++;
bufferWi++;
- }
- else {
+ } else {
bufferHt = std::round(maxY - minY + 1.0f);
bufferWi = std::round(maxX - minX + 1.0f);
}
- //DumpBuffer(newNodes, bufferWi, bufferHt);
- }
- else {
+ // DumpBuffer(newNodes, bufferWi, bufferHt);
+ } else {
bufferHt = this->BufferHt;
bufferWi = this->BufferWi;
}
// Zero buffer sizes are bad
// This can happen when a strand is zero length ... maybe also a custom model with no nodes
- if (bufferHt == 0)
- {
+ if (bufferHt == 0) {
logger_base.warn("Model::InitRenderBufferNodes BufferHt was 0 ... overridden to be 1.");
bufferHt = 1;
}
- if (bufferWi == 0)
- {
+ if (bufferWi == 0) {
logger_base.warn("Model::InitRenderBufferNodes BufferWi was 0 ... overridden to be 1.");
bufferWi = 1;
}
@@ -4139,7 +4134,8 @@ void Model::InitRenderBufferNodes(const std::string &type, const std::string &ca
ApplyTransform(transform, newNodes, bufferWi, bufferHt);
}
-std::string Model::GetNextName() {
+std::string Model::GetNextName()
+{
if (nodeNames.size() > Nodes.size()) {
return nodeNames[Nodes.size()];
}
@@ -4151,84 +4147,84 @@ bool Model::FourChannelNodes() const
// true if string contains WRGB or any variant thereof
// I do the W search first to try to abort quickly for strings unlikely to be 4 channel
return (Contains(StringType, "W") &&
- (Contains(StringType, "RGBW") ||
- Contains(StringType, "WRGB") ||
- Contains(StringType, "WRBG") ||
- Contains(StringType, "RBGW") ||
- Contains(StringType, "WGRB") ||
- Contains(StringType, "GRBW") ||
- Contains(StringType, "WGBR") ||
- Contains(StringType, "GBRW") ||
- Contains(StringType, "WBRG") ||
- Contains(StringType, "BRGW") ||
- Contains(StringType, "WBGR") ||
- Contains(StringType, "BGRW")));
-}
-
-std::list Model::GetShadowedBy() const {
+ (Contains(StringType, "RGBW") ||
+ Contains(StringType, "WRGB") ||
+ Contains(StringType, "WRBG") ||
+ Contains(StringType, "RBGW") ||
+ Contains(StringType, "WGRB") ||
+ Contains(StringType, "GRBW") ||
+ Contains(StringType, "WGBR") ||
+ Contains(StringType, "GBRW") ||
+ Contains(StringType, "WBRG") ||
+ Contains(StringType, "BRGW") ||
+ Contains(StringType, "WBGR") ||
+ Contains(StringType, "BGRW")));
+}
+
+std::list Model::GetShadowedBy() const
+{
return GetModelManager().GetModelsShadowing(this);
}
// set size of Nodes vector and each Node's Coords vector
-void Model::SetNodeCount(size_t NumStrings, size_t NodesPerString, const std::string &rgbOrder) {
+void Model::SetNodeCount(size_t NumStrings, size_t NodesPerString, const std::string& rgbOrder)
+{
size_t n;
if (SingleNode) {
- if (StringType=="Single Color Red") {
- for(n = 0; n < NumStrings; n++) {
+ if (StringType == "Single Color Red") {
+ for (n = 0; n < NumStrings; ++n) {
Nodes.push_back(NodeBaseClassPtr(new NodeClassRed(n, NodesPerString, GetNextName())));
Nodes.back()->model = this;
}
- } else if (StringType=="Single Color Green") {
- for(n = 0; n < NumStrings; n++) {
+ } else if (StringType == "Single Color Green") {
+ for (n = 0; n < NumStrings; ++n) {
Nodes.push_back(NodeBaseClassPtr(new NodeClassGreen(n, NodesPerString, GetNextName())));
Nodes.back()->model = this;
}
- }
- else if (StringType == "Superstring") {
- for (n = 0; n < NumStrings; n++) {
+ } else if (StringType == "Superstring") {
+ for (n = 0; n < NumStrings; ++n) {
Nodes.push_back(NodeBaseClassPtr(new NodeClassSuperString(n, NodesPerString, superStringColours, rgbwHandlingType, GetNextName())));
Nodes.back()->model = this;
}
- } else if (StringType=="Single Color Blue") {
- for(n = 0; n < NumStrings; n++) {
+ } else if (StringType == "Single Color Blue") {
+ for (n = 0; n < NumStrings; ++n) {
Nodes.push_back(NodeBaseClassPtr(new NodeClassBlue(n, NodesPerString, GetNextName())));
Nodes.back()->model = this;
}
- } else if (StringType=="Single Color White") {
- for(n = 0; n < NumStrings; n++) {
+ } else if (StringType == "Single Color White") {
+ for (n = 0; n < NumStrings; ++n) {
Nodes.push_back(NodeBaseClassPtr(new NodeClassWhite(n, NodesPerString, GetNextName())));
Nodes.back()->model = this;
}
- } else if (StringType=="Strobes White 3fps" || StringType=="Strobes") {
- StrobeRate=7; // 1 out of every 7 frames
- for(n = 0; n < NumStrings; n++) {
+ } else if (StringType == "Strobes White 3fps" || StringType == "Strobes") {
+ StrobeRate = 7; // 1 out of every 7 frames
+ for (n = 0; n < NumStrings; ++n) {
Nodes.push_back(NodeBaseClassPtr(new NodeClassWhite(n, NodesPerString, GetNextName())));
Nodes.back()->model = this;
}
- } else if (StringType=="Single Color Custom") {
- for(n = 0; n < NumStrings; n++) {
+ } else if (StringType == "Single Color Custom") {
+ for (n = 0; n < NumStrings; ++n) {
Nodes.push_back(NodeBaseClassPtr(new NodeClassCustom(n, NodesPerString, customColor, GetNextName())));
Nodes.back()->model = this;
}
- } else if (StringType=="Single Color Intensity") {
- for(n = 0; n < NumStrings; n++) {
+ } else if (StringType == "Single Color Intensity") {
+ for (n = 0; n < NumStrings; ++n) {
Nodes.push_back(NodeBaseClassPtr(new NodeClassIntensity(n, NodesPerString, customColor, GetNextName())));
Nodes.back()->model = this;
}
- } else if (StringType=="4 Channel RGBW") {
- for(n = 0; n < NumStrings; n++) {
+ } else if (StringType == "4 Channel RGBW") {
+ for (n = 0; n < NumStrings; ++n) {
Nodes.push_back(NodeBaseClassPtr(new NodeClassRGBW(n, NodesPerString, "RGB", true, rgbwHandlingType, GetNextName())));
Nodes.back()->model = this;
}
- }
- else if (StringType == "4 Channel WRGB") {
- for (n = 0; n < NumStrings; n++) {
+ } else if (StringType == "4 Channel WRGB") {
+ for (n = 0; n < NumStrings; ++n) {
Nodes.push_back(NodeBaseClassPtr(new NodeClassRGBW(n, NodesPerString, "RGB", false, rgbwHandlingType, GetNextName())));
Nodes.back()->model = this;
}
} else {
// 3 Channel RGB
- for(n = 0; n < NumStrings; n++) {
+ for (n = 0; n < NumStrings; ++n) {
Nodes.push_back(NodeBaseClassPtr(new NodeBaseClass(n, NodesPerString, "RGB", GetNextName())));
Nodes.back()->model = this;
}
@@ -4236,39 +4232,37 @@ void Model::SetNodeCount(size_t NumStrings, size_t NodesPerString, const std::st
} else if (NodesPerString == 0) {
if (StringType == "Node Single Color") {
Nodes.push_back(NodeBaseClassPtr(new NodeClassCustom(0, 0, customColor, GetNextName())));
- }
- else if (FourChannelNodes()) {
+ } else if (FourChannelNodes()) {
bool wLast = StringType[3] == 'W';
Nodes.push_back(NodeBaseClassPtr(new NodeClassRGBW(0, 0, rgbOrder, wLast, rgbwHandlingType, GetNextName())));
- }
- else {
+ } else {
Nodes.push_back(NodeBaseClassPtr(new NodeBaseClass(0, 0, rgbOrder, GetNextName())));
}
Nodes.back()->model = this;
} else if (StringType[3] == ' ') {
size_t numnodes = NumStrings * NodesPerString;
- for(n = 0; n < numnodes; n++) {
- Nodes.push_back(NodeBaseClassPtr(new NodeBaseClass(n/NodesPerString, 1, rgbOrder, GetNextName())));
+ for (n = 0; n < numnodes; ++n) {
+ Nodes.push_back(NodeBaseClassPtr(new NodeBaseClass(n / NodesPerString, 1, rgbOrder, GetNextName())));
Nodes.back()->model = this;
}
- }
- else if (StringType == "Node Single Color") {
+ } else if (StringType == "Node Single Color") {
size_t numnodes = NumStrings * NodesPerString;
- for (n = 0; n < numnodes; n++) {
- Nodes.push_back(NodeBaseClassPtr(new NodeClassCustom(n/NodesPerString, 1, customColor, GetNextName())));
+ for (n = 0; n < numnodes; ++n) {
+ Nodes.push_back(NodeBaseClassPtr(new NodeClassCustom(n / NodesPerString, 1, customColor, GetNextName())));
Nodes.back()->model = this;
}
} else {
bool wLast = StringType[3] == 'W';
size_t numnodes = NumStrings * NodesPerString;
- for (n = 0; n < numnodes; n++) {
+ for (n = 0; n < numnodes; ++n) {
Nodes.push_back(NodeBaseClassPtr(new NodeClassRGBW(n / NodesPerString, 1, rgbOrder, wLast, rgbwHandlingType, GetNextName())));
Nodes.back()->model = this;
}
}
}
-size_t Model::GetNodeChannelCount(const std::string & nodeType) const {
+size_t Model::GetNodeChannelCount(const std::string& nodeType) const
+{
if (nodeType.compare(0, 12, "Single Color") == 0) {
return 1;
} else if (nodeType == "Strobes White 3fps") {
@@ -4279,14 +4273,12 @@ size_t Model::GetNodeChannelCount(const std::string & nodeType) const {
return 4;
} else if (nodeType == "4 Channel WRGB") {
return 4;
- } else if (nodeType[0] == 'W' || nodeType [3] == 'W') {
- //various WRGB and RGBW types
+ } else if (nodeType[0] == 'W' || nodeType[3] == 'W') {
+ // various WRGB and RGBW types
return 4;
- }
- else if (nodeType == "Superstring") {
+ } else if (nodeType == "Superstring") {
return std::max(1, (int)superStringColours.size());
- }
- else if (nodeType == "Node Single Color") {
+ } else if (nodeType == "Node Single Color") {
return 1;
}
return 3;
@@ -4300,12 +4292,13 @@ void Model::AddLayerSizeProperty(wxPropertyGridInterface* grid)
psn->SetEditor("SpinCtrl");
if (GetLayerSizeCount() > 1) {
- for (int i = 0; i < GetLayerSizeCount(); i++)
- {
+ for (int i = 0; i < GetLayerSizeCount(); ++i) {
wxString id = wxString::Format("Layer%d", i);
wxString nm = wxString::Format("Layer %d", i + 1);
- if (i == 0) nm = "Inside";
- else if (i == GetLayerSizeCount() - 1) nm = "Outside";
+ if (i == 0)
+ nm = "Inside";
+ else if (i == GetLayerSizeCount() - 1)
+ nm = "Outside";
wxPGProperty* pls = grid->AppendIn(psn, new wxUIntProperty(nm, id, GetLayerSize(i)));
pls->SetAttribute("Min", 1);
@@ -4332,8 +4325,7 @@ bool Model::HandleLayerSizePropertyChange(wxPropertyGridInterface* grid, wxPrope
OnLayerSizesChange(true);
return true;
- }
- else if (event.GetPropertyName().StartsWith("Layers.Layer")) {
+ } else if (event.GetPropertyName().StartsWith("Layers.Layer")) {
int layer = wxAtoi(event.GetPropertyName().AfterLast('r'));
SetLayerSize(layer, event.GetValue().GetLong());
AddASAPWork(OutputModelManager::WORK_RGBEFFECTS_CHANGE, "Model::HandleLayerSizePropertyChange::Layer");
@@ -4365,7 +4357,7 @@ bool Model::ContainsChannel(uint32_t startChannel, uint32_t endChannel) const
bool Model::ContainsChannel(int strand, uint32_t startChannel, uint32_t endChannel) const
{
uint32_t sc = GetChannelForNode(strand, 0);
- uint32_t ec = GetChannelForNode(strand, GetStrandLength(strand)-1) + GetChanCountPerNode() - 1;
+ uint32_t ec = GetChannelForNode(strand, GetStrandLength(strand) - 1) + GetChanCountPerNode() - 1;
return !(endChannel < sc || startChannel > ec);
}
@@ -4458,7 +4450,8 @@ uint32_t Model::GetNodeNumber(int bufY, int bufX) const
return -1;
}
-uint32_t Model::GetNodeCount() const {
+uint32_t Model::GetNodeCount() const
+{
return Nodes.size();
}
@@ -4470,22 +4463,23 @@ uint32_t Model::GetActChanCount() const
}
uint32_t count = 0;
- for (uint32_t x = 0; x < NodeCnt; x++) {
+ for (uint32_t x = 0; x < NodeCnt; ++x) {
count += Nodes[x]->GetChanCount();
}
return count;
}
-uint32_t Model::GetChanCount() const {
- size_t NodeCnt=GetNodeCount();
+uint32_t Model::GetChanCount() const
+{
+ size_t NodeCnt = GetNodeCount();
if (NodeCnt == 0) {
return 0;
}
int min = 999999999;
int max = 0;
- for (int x = 0; x < NodeCnt; x++) {
+ for (int x = 0; x < NodeCnt; ++x) {
int i = Nodes[x]->ActChan;
if (i < min) {
min = i;
@@ -4506,7 +4500,8 @@ NodeBaseClass* Model::GetNode(uint32_t node) const
return nullptr;
}
-int Model::GetChanCountPerNode() const {
+int Model::GetChanCountPerNode() const
+{
size_t NodeCnt = GetNodeCount();
if (NodeCnt == 0) {
return 0;
@@ -4514,38 +4509,45 @@ int Model::GetChanCountPerNode() const {
return Nodes[0]->GetChanCount();
}
-uint32_t Model::GetCoordCount(size_t nodenum) const {
+uint32_t Model::GetCoordCount(size_t nodenum) const
+{
return nodenum < Nodes.size() ? Nodes[nodenum]->Coords.size() : 0;
}
-int Model::GetNodeStringNumber(size_t nodenum) const {
+int Model::GetNodeStringNumber(size_t nodenum) const
+{
return nodenum < Nodes.size() ? Nodes[nodenum]->StringNum : 0;
}
-void Model::GetNode3DScreenCoords(int nodeidx, std::vector>& pts) {
- for (int x = 0; x < Nodes[nodeidx]->Coords.size(); x++) {
+void Model::GetNode3DScreenCoords(int nodeidx, std::vector>& pts)
+{
+ for (int x = 0; x < Nodes[nodeidx]->Coords.size(); ++x) {
pts.push_back(std::make_tuple(Nodes[nodeidx]->Coords[x].screenX, Nodes[nodeidx]->Coords[x].screenY, Nodes[nodeidx]->Coords[x].screenZ));
}
}
-void Model::GetNodeCoords(int nodeidx, std::vector &pts) {
- if (nodeidx >= Nodes.size()) return;
- for (int x = 0; x < Nodes[nodeidx]->Coords.size(); x++) {
+void Model::GetNodeCoords(int nodeidx, std::vector& pts)
+{
+ if (nodeidx >= Nodes.size())
+ return;
+ for (int x = 0; x < Nodes[nodeidx]->Coords.size(); ++x) {
pts.push_back(wxPoint(Nodes[nodeidx]->Coords[x].bufX, Nodes[nodeidx]->Coords[x].bufY));
}
}
-bool Model::IsCustom(void) {
+bool Model::IsCustom(void)
+{
return (DisplayAs == "Custom");
}
-//convert # to AA format so it matches Custom Model grid display:
-//this makes it *so* much easier to visually compare with Custom Model grid display
-//A - Z == 1 - 26
-//AA - AZ == 27 - 52
-//BA - BZ == 53 - 78
-//etc
-static wxString AA(int x) {
+// convert # to AA format so it matches Custom Model grid display:
+// this makes it *so* much easier to visually compare with Custom Model grid display
+// A - Z == 1 - 26
+// AA - AZ == 27 - 52
+// BA - BZ == 53 - 78
+// etc
+static wxString AA(int x)
+{
wxString retval;
--x;
// if (x >= 26 * 26) { retval += 'A' + x / (26 * 26); x %= 26 * 26; }
@@ -4557,9 +4559,10 @@ static wxString AA(int x) {
return retval;
}
-//add just the node#s to a choice list:
-//NO add parsed info to choice list or check list box:
-size_t Model::GetChannelCoords(wxArrayString& choices) { //wxChoice* choices1, wxCheckListBox* choices2, wxListBox* choices3)
+// add just the node#s to a choice list:
+// NO add parsed info to choice list or check list box:
+size_t Model::GetChannelCoords(wxArrayString& choices)
+{ // wxChoice* choices1, wxCheckListBox* choices2, wxListBox* choices3)
// if (choices1) choices1->Clear();
// if (choices2) choices2->Clear();
// if (choices3) choices3->Clear();
@@ -4567,10 +4570,11 @@ size_t Model::GetChannelCoords(wxArrayString& choices) { //wxChoice* choices1, w
// if (choices2) choices2->Append(wxT("0: (none)"));
// if (choices3) choices3->Append(wxT("0: (none)"));
size_t NodeCount = GetNodeCount();
- for (size_t n = 0; n < NodeCount; n++) {
+ for (size_t n = 0; n < NodeCount; ++n) {
wxString newstr;
// debug(10, "model::node[%d/%d]: #coords %d, ach# %d, str %d", n, NodeCount, Nodes[n]->Coords.size(), Nodes[n]->StringNum, Nodes[n]->ActChan);
- if (Nodes[n]->Coords.empty()) continue;
+ if (Nodes[n]->Coords.empty())
+ continue;
// newstr = wxString::Format(wxT("%i"), GetNodeNumber(n));
// choices.Add(newstr);
choices.Add(GetNodeXY(n));
@@ -4582,56 +4586,67 @@ size_t Model::GetChannelCoords(wxArrayString& choices) { //wxChoice* choices1, w
// choices3->InsertItems(strary, choices3->GetCount() + 0);
// }
}
- return choices.GetCount(); //choices1? choices1->GetCount(): 0) + (choices2? choices2->GetCount(): 0);
+ return choices.GetCount(); // choices1? choices1->GetCount(): 0) + (choices2? choices2->GetCount(): 0);
}
-//get parsed node info:
-std::string Model::GetNodeXY(const std::string& nodenumstr) {
+// get parsed node info:
+std::string Model::GetNodeXY(const std::string& nodenumstr)
+{
size_t NodeCount = GetNodeCount();
try {
int32_t nodenum = std::stod(nodenumstr);
- for (size_t inx = 0; inx < NodeCount; inx++) {
- if (Nodes[inx]->Coords.empty()) continue;
- if (GetNodeNumber(inx) == nodenum) return GetNodeXY(inx);
+ for (size_t inx = 0; inx < NodeCount; ++inx) {
+ if (Nodes[inx]->Coords.empty())
+ continue;
+ if (GetNodeNumber(inx) == nodenum)
+ return GetNodeXY(inx);
}
- } catch ( ... ) {
-
+ } catch (...) {
}
- return nodenumstr; //not found?
+ return nodenumstr; // not found?
}
-std::string Model::GetNodeXY(int nodeinx) {
- if ((nodeinx < 0) || (nodeinx >= (int)GetNodeCount())) return "";
- if (Nodes[nodeinx]->Coords.empty()) return "";
- if (GetCoordCount(nodeinx) > 1) //show count and first + last coordinates
+std::string Model::GetNodeXY(int nodeinx)
+{
+ if ((nodeinx < 0) || (nodeinx >= (int)GetNodeCount()))
+ return "";
+ if (Nodes[nodeinx]->Coords.empty())
+ return "";
+ if (GetCoordCount(nodeinx) > 1) // show count and first + last coordinates
+ if (IsCustom())
+ return wxString::Format(wxT("%d: %d# @%s%d-%s%d"), GetNodeNumber(nodeinx), GetCoordCount(nodeinx), AA(Nodes[nodeinx]->Coords.front().bufX + 1), BufferHt - Nodes[nodeinx]->Coords.front().bufY, AA(Nodes[nodeinx]->Coords.back().bufX + 1), BufferHt - Nodes[nodeinx]->Coords.back().bufY).ToStdString(); // NOTE: only need first (X,Y) for each channel, but show last and count as well; Y is in reverse order
+ else
+ return wxString::Format(wxT("%d: %d# @(%d,%d)-(%d,%d"), GetNodeNumber(nodeinx), GetCoordCount(nodeinx), Nodes[nodeinx]->Coords.front().bufX + 1, BufferHt - Nodes[nodeinx]->Coords.front().bufY, Nodes[nodeinx]->Coords.back().bufX + 1, BufferHt - Nodes[nodeinx]->Coords.back().bufY).ToStdString(); // NOTE: only need first (X,Y) for each channel, but show last and count as well; Y is in reverse order
+ else // just show singleton
if (IsCustom())
- return wxString::Format(wxT("%d: %d# @%s%d-%s%d"), GetNodeNumber(nodeinx), GetCoordCount(nodeinx), AA(Nodes[nodeinx]->Coords.front().bufX + 1), BufferHt - Nodes[nodeinx]->Coords.front().bufY, AA(Nodes[nodeinx]->Coords.back().bufX + 1), BufferHt - Nodes[nodeinx]->Coords.back().bufY).ToStdString(); //NOTE: only need first (X,Y) for each channel, but show last and count as well; Y is in reverse order
+ return wxString::Format(wxT("%d: @%s%d"), GetNodeNumber(nodeinx), AA(Nodes[nodeinx]->Coords.front().bufX + 1), BufferHt - Nodes[nodeinx]->Coords.front().bufY).ToStdString();
else
- return wxString::Format(wxT("%d: %d# @(%d,%d)-(%d,%d"), GetNodeNumber(nodeinx), GetCoordCount(nodeinx), Nodes[nodeinx]->Coords.front().bufX + 1, BufferHt - Nodes[nodeinx]->Coords.front().bufY, Nodes[nodeinx]->Coords.back().bufX + 1, BufferHt - Nodes[nodeinx]->Coords.back().bufY).ToStdString(); //NOTE: only need first (X,Y) for each channel, but show last and count as well; Y is in reverse order
- else //just show singleton
- if (IsCustom())
- return wxString::Format(wxT("%d: @%s%d"), GetNodeNumber(nodeinx), AA(Nodes[nodeinx]->Coords.front().bufX + 1), BufferHt - Nodes[nodeinx]->Coords.front().bufY).ToStdString();
- else
- return wxString::Format(wxT("%d: @(%d,%d)"), GetNodeNumber(nodeinx), Nodes[nodeinx]->Coords.front().bufX + 1, BufferHt - Nodes[nodeinx]->Coords.front().bufY).ToStdString();
+ return wxString::Format(wxT("%d: @(%d,%d)"), GetNodeNumber(nodeinx), Nodes[nodeinx]->Coords.front().bufX + 1, BufferHt - Nodes[nodeinx]->Coords.front().bufY).ToStdString();
}
-//extract first (X,Y) from string formatted above:
-bool Model::ParseFaceElement(const std::string& multi_str, std::vector& first_xy) {
+// extract first (X,Y) from string formatted above:
+bool Model::ParseFaceElement(const std::string& multi_str, std::vector& first_xy)
+{
// first_xy->x = first_xy->y = 0;
// first_xy.clear();
wxStringTokenizer wtkz(multi_str, "+");
while (wtkz.HasMoreTokens()) {
wxString str = wtkz.GetNextToken();
- if (str.empty()) continue;
- if (str.Find('@') == wxNOT_FOUND) continue; //return false;
+ if (str.empty())
+ continue;
+ if (str.Find('@') == wxNOT_FOUND)
+ continue; // return false;
wxString xystr = str.AfterFirst('@');
- if (xystr.empty()) continue; //return false;
+ if (xystr.empty())
+ continue; // return false;
long xval = 0, yval = 0;
if (xystr[0] == '(') {
xystr.Remove(0, 1);
- if (!xystr.BeforeFirst(',').ToLong(&xval)) continue; //return false;
- if (!xystr.AfterFirst(',').BeforeFirst(')').ToLong(&yval)) continue; //return false;
+ if (!xystr.BeforeFirst(',').ToLong(&xval))
+ continue; // return false;
+ if (!xystr.AfterFirst(',').BeforeFirst(')').ToLong(&yval))
+ continue; // return false;
} else {
int parts = 0;
while (!xystr.empty() && (xystr[0] >= 'A') && (xystr[0] <= 'Z')) {
@@ -4646,35 +4661,42 @@ bool Model::ParseFaceElement(const std::string& multi_str, std::vector&
xystr.Remove(0, 1);
parts |= 2;
}
- if (parts != 3) continue; //return false;
- if (!xystr.empty() && (xystr[0] != '-')) continue; //return false;
+ if (parts != 3)
+ continue; // return false;
+ if (!xystr.empty() && (xystr[0] != '-'))
+ continue; // return false;
}
wxPoint newxy(xval, yval);
first_xy.push_back(newxy);
}
- return !first_xy.empty(); //true;
+ return !first_xy.empty(); // true;
}
-//extract first (X,Y) from string formatted above:
-bool Model::ParseStateElement(const std::string& multi_str, std::vector& first_xy) {
+// extract first (X,Y) from string formatted above:
+bool Model::ParseStateElement(const std::string& multi_str, std::vector& first_xy)
+{
// first_xy->x = first_xy->y = 0;
// first_xy.clear();
wxStringTokenizer wtkz(multi_str, "+");
while (wtkz.HasMoreTokens()) {
wxString str = wtkz.GetNextToken();
- if (str.empty()) continue;
- if (str.Find('@') == wxNOT_FOUND) continue; //return false;
+ if (str.empty())
+ continue;
+ if (str.Find('@') == wxNOT_FOUND)
+ continue; // return false;
wxString xystr = str.AfterFirst('@');
- if (xystr.empty()) continue; //return false;
+ if (xystr.empty())
+ continue; // return false;
long xval = 0, yval = 0;
if (xystr[0] == '(') {
xystr.Remove(0, 1);
- if (!xystr.BeforeFirst(',').ToLong(&xval)) continue; //return false;
- if (!xystr.AfterFirst(',').BeforeFirst(')').ToLong(&yval)) continue; //return false;
- }
- else {
+ if (!xystr.BeforeFirst(',').ToLong(&xval))
+ continue; // return false;
+ if (!xystr.AfterFirst(',').BeforeFirst(')').ToLong(&yval))
+ continue; // return false;
+ } else {
int parts = 0;
while (!xystr.empty() && (xystr[0] >= 'A') && (xystr[0] <= 'Z')) {
xval *= 26;
@@ -4688,14 +4710,16 @@ bool Model::ParseStateElement(const std::string& multi_str, std::vector
xystr.Remove(0, 1);
parts |= 2;
}
- if (parts != 3) continue; //return false;
- if (!xystr.empty() && (xystr[0] != '-')) continue; //return false;
+ if (parts != 3)
+ continue; // return false;
+ if (!xystr.empty() && (xystr[0] != '-'))
+ continue; // return false;
}
wxPoint newxy(xval, yval);
first_xy.push_back(newxy);
}
- return !first_xy.empty(); //true;
+ return !first_xy.empty(); // true;
}
void Model::ExportAsCustomXModel() const
@@ -4720,7 +4744,7 @@ void Model::ExportAsCustomXModel() const
float maxsy = -1;
size_t nodeCount = GetNodeCount();
- for (size_t i = 0; i < nodeCount; i++) {
+ for (size_t i = 0; i < nodeCount; ++i) {
float Sbufx = Nodes[i]->Coords[0].screenX;
float Sbufy = Nodes[i]->Coords[0].screenY;
if (Sbufx < minsx)
@@ -4861,8 +4885,7 @@ void Model::ImportSuperStringColours(wxXmlNode* root)
auto an = wxString::Format("SuperStringColour%d", index);
if (root->HasAttribute(an)) {
superStringColours.push_back(xlColor(root->GetAttribute(an)));
- }
- else {
+ } else {
found = false;
}
@@ -4878,8 +4901,8 @@ bool Model::FindCustomModelScale(int scale) const
if (nodeCount <= 1) {
return true;
}
- for (int i = 0; i < nodeCount; i++) {
- for (int j = i + 1; j < nodeCount; j++) {
+ for (int i = 0; i < nodeCount; ++i) {
+ for (int j = i + 1; j < nodeCount; ++j) {
int x1 = (Nodes[i]->Coords[0].screenX * scale);
int y1 = (Nodes[i]->Coords[0].screenY * scale);
int x2 = (Nodes[j]->Coords[0].screenX * scale);
@@ -4895,16 +4918,15 @@ bool Model::FindCustomModelScale(int scale) const
std::string Model::GetStartLocation() const
{
if (!IsLtoR) {
- if (!isBotToTop)
- return "Top Right";
- else
- return "Bottom Right";
- }
- else {
- if (!isBotToTop)
- return "Top Left";
- else
- return "Bottom Left";
+ if (!isBotToTop)
+ return "Top Right";
+ else
+ return "Bottom Right";
+ } else {
+ if (!isBotToTop)
+ return "Top Left";
+ else
+ return "Bottom Left";
}
}
@@ -4945,7 +4967,7 @@ std::string Model::ChannelLayoutHtml(OutputManager* outputManager)
if (BufferHt == 1) {
// single line or arch or cane
html += "";
- for (size_t i = 1; i <= NodeCount; i++) {
+ for (size_t i = 1; i <= NodeCount; ++i) {
int n = IsLtoR ? i : NodeCount - i + 1;
int s = Nodes[n - 1]->StringNum + 1;
wxString bgcolor = s % 2 == 1 ? "#ADD8E6" : "#90EE90";
@@ -4957,7 +4979,7 @@ std::string Model::ChannelLayoutHtml(OutputManager* outputManager)
html += "
";
} else if (BufferHt > 1) {
// horizontal or vertical matrix or frame
- for (size_t i = 0; i < NodeCount; i++) {
+ for (size_t i = 0; i < NodeCount; ++i) {
size_t idx = Nodes[i]->Coords[0].bufY * BufferWi + Nodes[i]->Coords[0].bufX;
if (idx < chmap.size()) {
chmap[idx] = i + 1;
@@ -4965,7 +4987,7 @@ std::string Model::ChannelLayoutHtml(OutputManager* outputManager)
}
for (int y = BufferHt - 1; y >= 0; y--) {
html += "";
- for (int x = 0; x < BufferWi; x++) {
+ for (int x = 0; x < BufferWi; ++x) {
int n = chmap[y * BufferWi + x];
if (n == 0) {
html += " | ";
@@ -4993,9 +5015,9 @@ void Model::CopyBufCoord2ScreenCoord()
size_t NodeCount = GetNodeCount();
int xoffset = BufferWi / 2;
int yoffset = BufferHt / 2;
- for (size_t n = 0; n < NodeCount; n++) {
+ for (size_t n = 0; n < NodeCount; ++n) {
size_t CoordCount = GetCoordCount(n);
- for (size_t c = 0; c < CoordCount; c++) {
+ for (size_t c = 0; c < CoordCount; ++c) {
Nodes[n]->Coords[c].screenX = Nodes[n]->Coords[c].bufX - xoffset;
Nodes[n]->Coords[c].screenY = Nodes[n]->Coords[c].bufY - yoffset;
}
@@ -5019,7 +5041,8 @@ bool Model::HitTest(ModelPreview* preview, glm::vec3& ray_origin, glm::vec3& ray
return GetModelScreenLocation().HitTest(ray_origin, ray_direction);
}
-wxCursor Model::InitializeLocation(int &handle, wxCoord x, wxCoord y, ModelPreview* preview) {
+wxCursor Model::InitializeLocation(int& handle, wxCoord x, wxCoord y, ModelPreview* preview)
+{
return GetModelScreenLocation().InitializeLocation(handle, x, y, Nodes, preview);
}
@@ -5033,7 +5056,7 @@ void Model::ApplyTransparency(xlColor& color, int transparency, int blackTranspa
int i = std::floor(t);
colorAlpha = i > 255 ? 255 : (i < 0 ? 0 : i);
}
- } else if (transparency || blackTransparency){
+ } else if (transparency || blackTransparency) {
int maxCol = std::max(color.red, std::max(color.green, color.blue));
if (transparency) {
float t = 100.0f - transparency;
@@ -5041,7 +5064,7 @@ void Model::ApplyTransparency(xlColor& color, int transparency, int blackTranspa
colorAlpha = std::floor(t);
}
if (maxCol < 64 && blackTransparency) {
- //if we're getting close to black, we'll start migrating toward the black's transparency setting
+ // if we're getting close to black, we'll start migrating toward the black's transparency setting
float t = 100.0f - blackTransparency;
t *= 2.55f;
int blackAlpha = std::floor(t);
@@ -5053,11 +5076,13 @@ void Model::ApplyTransparency(xlColor& color, int transparency, int blackTranspa
color.alpha = colorAlpha;
}
-void Model::DisplayModelOnWindow(ModelPreview* preview, xlGraphicsContext *ctx, xlGraphicsProgram *solidProgram, xlGraphicsProgram *transparentProgram, bool is_3d,
- const xlColor* c, bool allowSelected, bool wiring, bool highlightFirst, int highlightpixel,
- float *boundingBox) {
-
- if (!IsActive() && preview->IsNoCurrentModel()) { return; }
+void Model::DisplayModelOnWindow(ModelPreview* preview, xlGraphicsContext* ctx, xlGraphicsProgram* solidProgram, xlGraphicsProgram* transparentProgram, bool is_3d,
+ const xlColor* c, bool allowSelected, bool wiring, bool highlightFirst, int highlightpixel,
+ float* boundingBox)
+{
+ if (!IsActive() && preview->IsNoCurrentModel()) {
+ return;
+ }
size_t NodeCount = Nodes.size();
xlColor color;
xlColor saveColor;
@@ -5072,9 +5097,9 @@ void Model::DisplayModelOnWindow(ModelPreview* preview, xlGraphicsContext *ctx,
ModelScreenLocation& screenLocation = GetModelScreenLocation();
screenLocation.PrepareToDraw(is_3d, allowSelected);
- const std::string &cacheKey = allowSelected
- ? (is_3d ? LAYOUT_PREVIEW_CACHE_3D : LAYOUT_PREVIEW_CACHE_2D)
- : (is_3d ? MODEL_PREVIEW_CACHE_3D : MODEL_PREVIEW_CACHE_2D);
+ const std::string& cacheKey = allowSelected
+ ? (is_3d ? LAYOUT_PREVIEW_CACHE_3D : LAYOUT_PREVIEW_CACHE_2D)
+ : (is_3d ? MODEL_PREVIEW_CACHE_3D : MODEL_PREVIEW_CACHE_2D);
if (uiObjectsInvalid) {
deleteUIObjects();
}
@@ -5092,7 +5117,7 @@ void Model::DisplayModelOnWindow(ModelPreview* preview, xlGraphicsContext *ctx,
for (const auto& it : Nodes) {
vcount += it.get()->Coords.size();
}
- if (_pixelStyle == PIXEL_STYLE::PIXEL_STYLE_SOLID_CIRCLE || _pixelStyle == PIXEL_STYLE::PIXEL_STYLE_BLENDED_CIRCLE) {
+ if (_pixelStyle == PIXEL_STYLE::PIXEL_STYLE_SOLID_CIRCLE || _pixelStyle == PIXEL_STYLE::PIXEL_STYLE_BLENDED_CIRCLE) {
int f = pixelSize;
if (pixelSize < 16) {
f = 16;
@@ -5114,8 +5139,8 @@ void Model::DisplayModelOnWindow(ModelPreview* preview, xlGraphicsContext *ctx,
cache->vica->SetColorCount(_pixelStyle == PIXEL_STYLE::PIXEL_STYLE_BLENDED_CIRCLE ? NodeCount * 2 : NodeCount);
float modelPixelSize = pixelSize;
- //pixelSize is in world coordinate sizes, not model size. Thus, we need to reverse the matrices to
- //get the size to use for the pixelStyle 3/4 that use triangles
+ // pixelSize is in world coordinate sizes, not model size. Thus, we need to reverse the matrices to
+ // get the size to use for the pixelStyle 3/4 that use triangles
if (_pixelStyle == PIXEL_STYLE::PIXEL_STYLE_SOLID_CIRCLE || _pixelStyle == PIXEL_STYLE::PIXEL_STYLE_BLENDED_CIRCLE) {
modelPixelSize = preview->calcPixelSize(pixelSize);
@@ -5125,17 +5150,16 @@ void Model::DisplayModelOnWindow(ModelPreview* preview, xlGraphicsContext *ctx,
GetModelScreenLocation().TranslatePoint(x2, y2, z2);
glm::vec3 a = glm::vec3(x2, y2, z2) - glm::vec3(x1, y1, z1);
- float length =std::max(std::max(std::abs(a.x), std::abs(a.y)), std::abs(a.z));
+ float length = std::max(std::max(std::abs(a.x), std::abs(a.y)), std::abs(a.z));
modelPixelSize /= std::abs(length);
}
-
int first = 0;
int last = NodeCount;
int buffFirst = -1;
int buffLast = -1;
bool left = true;
- //int lastChan = -999;
+ // int lastChan = -999;
while (first < last) {
int n;
if (left) {
@@ -5160,8 +5184,8 @@ void Model::DisplayModelOnWindow(ModelPreview* preview, xlGraphicsContext *ctx,
}
}
- size_t CoordCount=GetCoordCount(n);
- for(size_t c2=0; c2 < CoordCount; ++c2) {
+ size_t CoordCount = GetCoordCount(n);
+ for (size_t c2 = 0; c2 < CoordCount; ++c2) {
// draw node on screen
float sx = Nodes[n]->Coords[c2].screenX;
float sy = Nodes[n]->Coords[c2].screenY;
@@ -5193,20 +5217,20 @@ void Model::DisplayModelOnWindow(ModelPreview* preview, xlGraphicsContext *ctx,
}
}
- //lastChan = Nodes[n]->ActChan;
+ // lastChan = Nodes[n]->ActChan;
}
- cache->program->addStep([=](xlGraphicsContext *ctx) {
+ cache->program->addStep([=](xlGraphicsContext* ctx) {
if (_pixelStyle == PIXEL_STYLE::PIXEL_STYLE_SOLID_CIRCLE || _pixelStyle == PIXEL_STYLE::PIXEL_STYLE_BLENDED_CIRCLE) {
ctx->drawTriangles(cache->vica, 0, cache->vica->getCount());
} else {
- ModelPreview *preview = (ModelPreview *)ctx->getWindow();
+ ModelPreview* preview = (ModelPreview*)ctx->getWindow();
float pointSize = preview->calcPixelSize(pixelSize);
ctx->drawPoints(cache->vica, pointSize, _pixelStyle == PIXEL_STYLE::PIXEL_STYLE_SMOOTH, 0, cache->vica->getCount());
}
});
}
for (int n = 0; n < NodeCount; ++n) {
- if (n+1 == highlightpixel) {
+ if (n + 1 == highlightpixel) {
color = xlMAGENTA;
} else if (highlightFirst && Nodes.size() > 1) {
if (IsNodeFirst(n)) {
@@ -5247,12 +5271,12 @@ void Model::DisplayModelOnWindow(ModelPreview* preview, xlGraphicsContext *ctx,
boundingBox[4] = cache->boundingBox[4];
boundingBox[5] = cache->boundingBox[5];
}
- xlGraphicsProgram *p = cache->isTransparent ? transparentProgram : solidProgram;
+ xlGraphicsProgram* p = cache->isTransparent ? transparentProgram : solidProgram;
if (wiring && NodeCount > 1 && cache->va == nullptr) {
cache->va = ctx->createVertexAccumulator();
cache->va->SetName(GetName() + (is_3d ? " - 3DPWiring" : " - 2DWiring"));
cache->va->PreAlloc(NodeCount);
- for (int x = 0; x < NodeCount; x++) {
+ for (int x = 0; x < NodeCount; ++x) {
float sx = Nodes[x]->Coords[0].screenX;
float sy = Nodes[x]->Coords[0].screenY;
float sz = Nodes[x]->Coords[0].screenZ;
@@ -5261,12 +5285,12 @@ void Model::DisplayModelOnWindow(ModelPreview* preview, xlGraphicsContext *ctx,
cache->va->Finalize(false);
}
- p->addStep([=](xlGraphicsContext *ctx) {
+ p->addStep([=](xlGraphicsContext* ctx) {
// cache has the model in model coordinates
// we need to scale/translate/etc.... to world
ctx->PushMatrix();
if (!is_3d) {
- //not 3d, flatten to the 0 plane
+ // not 3d, flatten to the 0 plane
ctx->Scale(1.0, 1.0, 0.0);
}
GetModelScreenLocation().ApplyModelViewMatrices(ctx);
@@ -5277,7 +5301,6 @@ void Model::DisplayModelOnWindow(ModelPreview* preview, xlGraphicsContext *ctx,
ctx->PopMatrix();
});
-
if ((Selected || (Highlighted && is_3d)) && c != nullptr && allowSelected) {
if (is_3d) {
GetModelScreenLocation().DrawHandles(transparentProgram, preview->GetCameraZoomForHandles(), preview->GetHandleScale(), Highlighted, IsFromBase());
@@ -5405,8 +5428,7 @@ std::vector Model::GetNodesInBoundingBox(ModelPreview* preview, wxPoint sta
GetScreenLocation(sx, sy, it2, w, h, scale);
if (sx >= startpx && sx <= endpx &&
- sy >= startpy && sy <= endpy)
- {
+ sy >= startpy && sy <= endpy) {
nodes.push_back(i);
}
}
@@ -5417,8 +5439,9 @@ std::vector Model::GetNodesInBoundingBox(ModelPreview* preview, wxPoint sta
bool Model::IsMultiCoordsPerNode() const
{
- for (const auto& it : Nodes) {
- if (it.get()->Coords.size() > 1) return true;
+ for (const auto& it : Nodes) {
+ if (it.get()->Coords.size() > 1)
+ return true;
}
return false;
}
@@ -5463,8 +5486,7 @@ void Model::DisplayEffectOnWindow(ModelPreview* preview, double pointSize)
// size indepentent and thus can be re-used unless the models rendeWi/Hi
// changes (which should trigger the uiObjectsInvalid and clear
// the cache anyway)
- if (cache == nullptr || cache->renderWi != renderWi || cache->renderHi != renderHi
- || cache->modelChangeCount != this->changeCount) {
+ if (cache == nullptr || cache->renderWi != renderWi || cache->renderHi != renderHi || cache->modelChangeCount != this->changeCount) {
if (cache != nullptr) {
delete cache;
}
@@ -5550,8 +5572,7 @@ void Model::DisplayEffectOnWindow(ModelPreview* preview, double pointSize)
if (cache->vica->getCount() && (lastPixelStyle == PIXEL_STYLE::PIXEL_STYLE_SQUARE ||
lastPixelStyle == PIXEL_STYLE::PIXEL_STYLE_SMOOTH ||
Nodes[n]->model->_pixelStyle == PIXEL_STYLE::PIXEL_STYLE_SQUARE ||
- Nodes[n]->model->_pixelStyle == PIXEL_STYLE::PIXEL_STYLE_SMOOTH))
- {
+ Nodes[n]->model->_pixelStyle == PIXEL_STYLE::PIXEL_STYLE_SMOOTH)) {
int count = cache->vica->getCount();
cache->program->addStep([=](xlGraphicsContext* ctx) {
if (lastPixelStyle == PIXEL_STYLE::PIXEL_STYLE_SOLID_CIRCLE || lastPixelStyle == PIXEL_STYLE::PIXEL_STYLE_BLENDED_CIRCLE) {
@@ -5643,8 +5664,8 @@ void Model::DisplayEffectOnWindow(ModelPreview* preview, double pointSize)
}
}
-glm::vec3 Model::MoveHandle(ModelPreview* preview, int handle, bool ShiftKeyPressed, int mouseX,int mouseY) {
-
+glm::vec3 Model::MoveHandle(ModelPreview* preview, int handle, bool ShiftKeyPressed, int mouseX, int mouseY)
+{
if (GetModelScreenLocation().IsLocked() || IsFromBase())
return GetModelScreenLocation().GetHandlePosition(handle);
@@ -5658,60 +5679,69 @@ glm::vec3 Model::MoveHandle(ModelPreview* preview, int handle, bool ShiftKeyPres
return GetModelScreenLocation().GetHandlePosition(handle);
}
-int Model::GetSelectedHandle() {
+int Model::GetSelectedHandle()
+{
return GetModelScreenLocation().GetSelectedHandle();
}
-int Model::GetNumHandles() {
+int Model::GetNumHandles()
+{
return GetModelScreenLocation().GetNumHandles();
}
-int Model::GetSelectedSegment() {
+int Model::GetSelectedSegment()
+{
return GetModelScreenLocation().GetSelectedSegment();
}
-bool Model::SupportsCurves() {
+bool Model::SupportsCurves()
+{
return GetModelScreenLocation().SupportsCurves();
}
-bool Model::HasCurve(int segment) {
+bool Model::HasCurve(int segment)
+{
return GetModelScreenLocation().HasCurve(segment);
}
-void Model::SetCurve(int segment, bool create) {
+void Model::SetCurve(int segment, bool create)
+{
return GetModelScreenLocation().SetCurve(segment, create);
}
-void Model::AddHandle(ModelPreview* preview, int mouseX, int mouseY) {
+void Model::AddHandle(ModelPreview* preview, int mouseX, int mouseY)
+{
GetModelScreenLocation().AddHandle(preview, mouseX, mouseY);
}
-void Model::InsertHandle(int after_handle, float zoom, int scale) {
-
+void Model::InsertHandle(int after_handle, float zoom, int scale)
+{
if (GetModelScreenLocation().IsLocked() || IsFromBase())
return;
GetModelScreenLocation().InsertHandle(after_handle, zoom, scale);
}
-void Model::DeleteHandle(int handle) {
-
+void Model::DeleteHandle(int handle)
+{
if (GetModelScreenLocation().IsLocked() || IsFromBase())
return;
GetModelScreenLocation().DeleteHandle(handle);
}
-int Model::GetStrandLength(int strand) const {
- int numStrands = std::max( 1, GetNumStrands() );
+int Model::GetStrandLength(int strand) const
+{
+ int numStrands = std::max(1, GetNumStrands());
return GetNodeCount() / numStrands;
}
-int Model::MapToNodeIndex(int strand, int node) const {
+int Model::MapToNodeIndex(int strand, int node) const
+{
static log4cpp::Category& logger_base = log4cpp::Category::getInstance(std::string("log_base"));
- //if ((DisplayAs == wxT("Vert Matrix") || DisplayAs == wxT("Horiz Matrix") || DisplayAs == wxT("Matrix")) && SingleChannel) {
- // return node;
- //}
+ // if ((DisplayAs == wxT("Vert Matrix") || DisplayAs == wxT("Horiz Matrix") || DisplayAs == wxT("Matrix")) && SingleChannel) {
+ // return node;
+ // }
if (GetNumStrands() == 1) {
return node;
}
@@ -5747,7 +5777,7 @@ bool Model::RenameController(const std::string& oldName, const std::string& newN
changed = true;
}
if (ModelXml->GetAttribute("Advanced") == "1") {
- for (int i = 0; i < parm1; i++) {
+ for (int i = 0; i < parm1; ++i) {
auto str = StartChanAttrName(i);
if (ModelXml->HasAttribute(str)) {
auto sc = ModelXml->GetAttribute(str);
@@ -5781,15 +5811,12 @@ void Model::ImportModelChildren(wxXmlNode* root, xLightsFrame* xlights, wxString
{
bool merge = false;
bool showPopup = true;
- for (wxXmlNode* n = root->GetChildren(); n != nullptr; n = n->GetNext())
- {
+ for (wxXmlNode* n = root->GetChildren(); n != nullptr; n = n->GetNext()) {
if (n->GetName() == "stateInfo") {
AddState(n);
- }
- else if (n->GetName() == "subModel") {
+ } else if (n->GetName() == "subModel") {
AddSubmodel(n);
- }
- else if (n->GetName() == "faceInfo") {
+ } else if (n->GetName() == "faceInfo") {
AddFace(n);
} else if (n->GetName() == "ControllerConnection") {
if (n->HasAttribute("zigZag")) {
@@ -5801,11 +5828,10 @@ void Model::ImportModelChildren(wxXmlNode* root, xLightsFrame* xlights, wxString
}
} else if (n->GetName() == "modelGroup") {
AddModelGroups(n, xlights->GetLayoutPreview()->GetVirtualCanvasWidth(),
- xlights->GetLayoutPreview()->GetVirtualCanvasHeight(), newname, merge, showPopup);
+ xlights->GetLayoutPreview()->GetVirtualCanvasHeight(), newname, merge, showPopup);
} else if (n->GetName() == "shadowmodels") {
ImportShadowModels(n, xlights);
} else if (n->GetName() == "dimensions") {
-
if (RulerObject::GetRuler() != nullptr) {
std::string units = n->GetAttribute("units", "mm");
float width = wxAtof(n->GetAttribute("width", "1000"));
@@ -5848,7 +5874,8 @@ Model* Model::GetXlightsModel(Model* model, std::string& last_model, xLightsFram
return model;
}
} else {
- if (prog != nullptr) prog->Hide();
+ if (prog != nullptr)
+ prog->Hide();
xlights->SetCursor(wxCURSOR_DEFAULT);
xlights->SuspendAutoSave(false);
cancelled = true;
@@ -5909,8 +5936,7 @@ Model* Model::GetXlightsModel(Model* model, std::string& last_model, xLightsFram
}
}
}
- }
- else if (name == modelName) {
+ } else if (name == modelName) {
matches = true;
newModelName = v["model"].AsString();
if (v.HasMember("block")) {
@@ -5931,26 +5957,20 @@ Model* Model::GetXlightsModel(Model* model, std::string& last_model, xLightsFram
}
if (dlg->FindModelFile(vendor, newModelName)) {
if (localBlock) {
- wxString msg = "'" + vendor + "' provides a certified model for '" + newModelName + "' in the xLights downloads. The "
- + "vendor has requested that the model they provide be the model that is used."
- + "Use the Vendor provided model instead?";
+ wxString msg = "'" + vendor + "' provides a certified model for '" + newModelName + "' in the xLights downloads. The " + "vendor has requested that the model they provide be the model that is used." + "Use the Vendor provided model instead?";
if (wxMessageBox(msg, "Use Vendor Certified Model?", wxYES_NO | wxICON_QUESTION, xlights) == wxYES) {
last_model = dlg->GetModelFile();
- }
- else {
+ } else {
last_model = "";
}
docLoaded = false;
break;
- }
- else if (!xlights->GetIgnoreVendorModelRecommendations()) {
+ } else if (!xlights->GetIgnoreVendorModelRecommendations()) {
// I do not believe we should be saying xLights recommends this as fom what I have seen this claim on quality is historically dubious and I do not believe we have
// ever actually assessed the quality of their models. My own experience has been the quality of some models is poor or worse. Others are fine. No vendor in
// my experience is noticably better or worse than any other ... they all have had their poor models.
// If you want to change the message back then have an OSX specific phrasing.
- wxString msg = "xLights found a '" + vendor + "' provided and certified model for '" + newModelName + "' in the xLights downloads. The "
- + "Vendor provided models are strongly recommended by the vendor due to their claimed quality and ease of use.\n\nWould you prefer to "
- + "use the Vendor provided model instead?";
+ wxString msg = "xLights found a '" + vendor + "' provided and certified model for '" + newModelName + "' in the xLights downloads. The " + "Vendor provided models are strongly recommended by the vendor due to their claimed quality and ease of use.\n\nWould you prefer to " + "use the Vendor provided model instead?";
if (wxMessageBox(msg, "Use Vendor Certified Model?", wxYES_NO | wxICON_QUESTION, xlights) == wxYES) {
last_model = dlg->GetModelFile();
docLoaded = false;
@@ -5973,9 +5993,9 @@ Model* Model::GetXlightsModel(Model* model, std::string& last_model, xLightsFram
}
}
}
- #ifdef __WXMSW__
+#ifdef __WXMSW__
}
- #endif
+#endif
}
}
}
@@ -6006,7 +6026,8 @@ Model* Model::GetXlightsModel(Model* model, std::string& last_model, xLightsFram
}
}
- if (modes.size() == 0) break;
+ if (modes.size() == 0)
+ break;
std::string mode = modes.begin()->first;
if (modes.size() > 1) {
@@ -6027,7 +6048,6 @@ Model* Model::GetXlightsModel(Model* model, std::string& last_model, xLightsFram
class DMXChannel
{
public:
-
class DMXValue
{
int ParseValue(wxString s, int channels)
@@ -6039,8 +6059,7 @@ Model* Model::GetXlightsModel(Model* model, std::string& last_model, xLightsFram
return wxAtoi(ss[0]) << 8;
}
return wxAtoi(ss[0]);
- }
- else if (ss[1] == "2") {
+ } else if (ss[1] == "2") {
wxASSERT(channels == 2);
return wxAtoi(ss[0]);
}
@@ -6061,8 +6080,7 @@ Model* Model::GetXlightsModel(Model* model, std::string& last_model, xLightsFram
_low = ParseValue(n->GetAttribute("DMXFrom"), channels);
if (nn == nullptr) {
_high = _low;
- }
- else {
+ } else {
_high = ParseValue(nn->GetAttribute("DMXFrom"), channels) - 1;
}
}
@@ -6138,8 +6156,7 @@ Model* Model::GetXlightsModel(Model* model, std::string& last_model, xLightsFram
}
if (isMovingHead) {
model = xlights->AllModels.CreateDefaultModel("DmxMovingHead3D", startChannel);
- }
- else {
+ } else {
model = xlights->AllModels.CreateDefaultModel("DmxMovingHead", startChannel);
model->GetModelXml()->DeleteAttribute("DmxStyle");
model->GetModelXml()->AddAttribute("DmxStyle", "Moving Head Bars");
@@ -6162,34 +6179,28 @@ Model* Model::GetXlightsModel(Model* model, std::string& last_model, xLightsFram
}
std::vector nodeNames = std::vector