Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added

- Support for ftdi chip based LED-devices with ws2812, sk6812 apa102 LED types (Many thanks to @nurikk) (#1746)
- Support for Skydimo devices (being an Adalight variant)
- Support for Skydimo devices
- Support gaps on Matrix Layout (#1696)
- Windows: Added a new grabber that uses the DXGI DDA (Desktop Duplication API). This has much better performance than the DX grabber as it does more of its work on the GPU.

Expand All @@ -41,6 +41,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Fixed: Philip Hue APIv2 support without Entertainment group defined (#1742)
- Refactored: Database access layer
- Refactored: Hyperion's configuration database is validated before start-up (and migrated, if required)
- Refactored: Python to enable parallel effect processing under Python 3.12
- Fixed: Python 3.12 crashes (#1747)
- osX Grabber: Use ScreenCaptureKit under macOS 15 and above

**JSON-API**
- Refactored JSON-API to ensure consistent authorization behaviour across sessions and single requests with token authorization.
Expand Down
10 changes: 0 additions & 10 deletions CMakePresets.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,6 @@
"name": "hyperion-bare-minimum",
"hidden": true,
"cacheVariables": {
// Disable Grabbers
"ENABLE_AMLOGIC": "OFF",
"ENABLE_DDA": "OFF",
"ENABLE_DISPMANX": "OFF",
Expand All @@ -64,32 +63,23 @@
"ENABLE_X11": "OFF",
"ENABLE_XCB": "OFF",
"ENABLE_AUDIO": "OFF",

// LED-Devices
"ENABLE_DEV_FTDI": "OFF",
"ENABLE_DEV_NETWORK": "OFF",
"ENABLE_DEV_SERIAL": "ON",
"ENABLE_DEV_SPI": "OFF",
"ENABLE_DEV_TINKERFORGE": "OFF",
"ENABLE_DEV_USB_HID": "OFF",
"ENABLE_DEV_WS281XPWM": "OFF",

// Disable Input Servers
"ENABLE_BOBLIGHT_SERVER": "OFF",
"ENABLE_CEC": "OFF",
"ENABLE_FLATBUF_SERVER": "OFF",
"ENABLE_PROTOBUF_SERVER": "OFF",

// Disable Output Connectors
"ENABLE_FORWARDER": "OFF",
"ENABLE_FLATBUF_CONNECT": "OFF",

// Disable Services
"ENABLE_EXPERIMENTAL": "OFF",
"ENABLE_MDNS": "OFF",
"ENABLE_REMOTE_CTL": "OFF",
"ENABLE_EFFECTENGINE": "OFF",

"ENABLE_JSONCHECKS": "ON",
"ENABLE_DEPLOY_DEPENDENCIES": "ON"
}
Expand Down
60 changes: 37 additions & 23 deletions cmake/Dependencies.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,15 @@ macro(DeployMacOS TARGET)
OUTPUT_STRIP_TRAILING_WHITESPACE
)

install(CODE "set(TARGET_FILE \"${TARGET_FILE}\")" COMPONENT "Hyperion")
install(CODE "set(TARGET_BUNDLE_NAME \"${TARGET}.app\")" COMPONENT "Hyperion")
install(CODE "set(PLUGIN_DIR \"${QT_PLUGIN_DIR}\")" COMPONENT "Hyperion")
install(CODE "set(BUILD_DIR \"${CMAKE_BINARY_DIR}\")" COMPONENT "Hyperion")
install(CODE "set(TARGET_FILE \"${TARGET_FILE}\")" COMPONENT "Hyperion")
install(CODE "set(TARGET_BUNDLE_NAME \"${TARGET}.app\")" COMPONENT "Hyperion")
install(CODE "set(PLUGIN_DIR \"${QT_PLUGIN_DIR}\")" COMPONENT "Hyperion")
install(CODE "set(ENABLE_EFFECTENGINE \"${ENABLE_EFFECTENGINE}\")" COMPONENT "Hyperion")

install(CODE [[

set(BUNDLE_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/${TARGET_BUNDLE_NAME}")

file(GET_RUNTIME_DEPENDENCIES
EXECUTABLES ${TARGET_FILE}
RESOLVED_DEPENDENCIES_VAR resolved_deps
Expand All @@ -28,13 +29,13 @@ macro(DeployMacOS TARGET)
if (${_index} GREATER -1)
file(INSTALL
FILES "${dependency}"
DESTINATION "${CMAKE_INSTALL_PREFIX}/${TARGET_BUNDLE_NAME}/Contents/Frameworks"
DESTINATION "${BUNDLE_INSTALL_DIR}/Contents/Frameworks"
TYPE SHARED_LIBRARY
)
else()
file(INSTALL
FILES "${dependency}"
DESTINATION "${CMAKE_INSTALL_PREFIX}/${TARGET_BUNDLE_NAME}/Contents/lib"
DESTINATION "${BUNDLE_INSTALL_DIR}/Contents/lib"
TYPE SHARED_LIBRARY
FOLLOW_SYMLINK_CHAIN
)
Expand All @@ -58,18 +59,18 @@ macro(DeployMacOS TARGET)

foreach(DEPENDENCY ${PLUGINS})
file(INSTALL
DESTINATION "${CMAKE_INSTALL_PREFIX}/${TARGET_BUNDLE_NAME}/Contents/lib"
DESTINATION "${BUNDLE_INSTALL_DIR}/Contents/lib"
TYPE SHARED_LIBRARY
FILES ${DEPENDENCY}
FOLLOW_SYMLINK_CHAIN
)
endforeach()

get_filename_component(singleQtLib ${file} NAME)
list(APPEND QT_PLUGINS "${CMAKE_INSTALL_PREFIX}/${TARGET_BUNDLE_NAME}/Contents/plugins/${PLUGIN}/${singleQtLib}")
list(APPEND QT_PLUGINS "${BUNDLE_INSTALL_DIR}/Contents/plugins/${PLUGIN}/${singleQtLib}")
file(INSTALL
FILES ${file}
DESTINATION "${CMAKE_INSTALL_PREFIX}/${TARGET_BUNDLE_NAME}/Contents/plugins/${PLUGIN}"
DESTINATION "${BUNDLE_INSTALL_DIR}/Contents/plugins/${PLUGIN}"
TYPE SHARED_LIBRARY
)

Expand All @@ -78,10 +79,10 @@ macro(DeployMacOS TARGET)
endforeach()

include(BundleUtilities)
fixup_bundle("${CMAKE_INSTALL_PREFIX}/${TARGET_BUNDLE_NAME}" "${QT_PLUGINS}" "${CMAKE_INSTALL_PREFIX}/${TARGET_BUNDLE_NAME}/Contents/lib" IGNORE_ITEM "python;python3;Python;Python3;.Python;.Python3")
fixup_bundle("${BUNDLE_INSTALL_DIR}" "${QT_PLUGINS}" "${BUNDLE_INSTALL_DIR}/Contents/lib" IGNORE_ITEM "python;python3;Python;Python3;.Python;.Python3")
file(REMOVE_RECURSE "${BUNDLE_INSTALL_DIR}/Contents/lib")

if(ENABLE_EFFECTENGINE)

# Detect the Python version and modules directory
if(NOT CMAKE_VERSION VERSION_LESS "3.12")
find_package(Python3 COMPONENTS Interpreter Development REQUIRED)
Expand All @@ -98,24 +99,37 @@ macro(DeployMacOS TARGET)

# Copy Python modules to '/../Frameworks/Python.framework/Versions/Current/lib/PythonMAJOR.MINOR' and ignore the unnecessary stuff listed below
if (PYTHON_MODULES_DIR)
set(PYTHON_FRAMEWORK "${BUNDLE_INSTALL_DIR}/Contents/Frameworks/Python.framework")
file(
COPY ${PYTHON_MODULES_DIR}/
DESTINATION "${CMAKE_INSTALL_PREFIX}/${TARGET_BUNDLE_NAME}/Contents/Frameworks/Python.framework/Versions/Current/lib/python${PYTHON_VERSION_MAJOR_MINOR}"
PATTERN "*.pyc" EXCLUDE # compiled bytecodes
PATTERN "__pycache__" EXCLUDE # any cache
PATTERN "config-${PYTHON_VERSION_MAJOR_MINOR}*" EXCLUDE # static libs
PATTERN "lib2to3" EXCLUDE # automated Python 2 to 3 code translation
PATTERN "tkinter" EXCLUDE # Tk interface
PATTERN "turtledemo" EXCLUDE # Tk demo folder
PATTERN "turtle.py" EXCLUDE # Tk demo file
PATTERN "test" EXCLUDE # unittest module
PATTERN "sitecustomize.py" EXCLUDE # site-specific configs
DESTINATION "${PYTHON_FRAMEWORK}/Versions/Current/lib/python${PYTHON_VERSION_MAJOR_MINOR}"
PATTERN "*.pyc" EXCLUDE # compiled bytecodes
PATTERN "__pycache__" EXCLUDE # any cache
PATTERN "config-${PYTHON_VERSION_MAJOR}.${PYTHON_VERSION_MINOR}*" EXCLUDE # static libs
PATTERN "lib2to3" EXCLUDE # automated Python 2 to 3 code translation
PATTERN "tkinter" EXCLUDE # Tk interface
PATTERN "lib-dynload/_tkinter.*" EXCLUDE
PATTERN "idlelib" EXCLUDE
PATTERN "turtle.py" EXCLUDE # Tk demo
PATTERN "test" EXCLUDE # unittest module
PATTERN "sitecustomize.py" EXCLUDE # site-specific configs
)
endif(PYTHON_MODULES_DIR)
endif(ENABLE_EFFECTENGINE)

file(REMOVE_RECURSE "${CMAKE_INSTALL_PREFIX}/${TARGET_BUNDLE_NAME}/Contents/lib")
file(REMOVE_RECURSE "${CMAKE_INSTALL_PREFIX}/share")
file(GLOB_RECURSE LIBS FOLLOW_SYMLINKS "${BUNDLE_INSTALL_DIR}/*.dylib")
file(GLOB FRAMEWORKS FOLLOW_SYMLINKS LIST_DIRECTORIES ON "${BUNDLE_INSTALL_DIR}/Contents/Frameworks/*")
foreach(item ${LIBS} ${FRAMEWORKS} ${PYTHON_FRAMEWORK} ${BUNDLE_INSTALL_DIR})
set(cmd codesign --deep --force --sign - "${item}")
execute_process(
COMMAND ${cmd}
RESULT_VARIABLE codesign_result
)

if(NOT codesign_result EQUAL 0)
message(WARNING "macOS signing failed; ${cmd} returned ${codesign_result}")
endif()
endforeach()

]] COMPONENT "Hyperion")

Expand Down
5 changes: 0 additions & 5 deletions cmake/osxbundle/AppleScript.scpt
Original file line number Diff line number Diff line change
Expand Up @@ -49,11 +49,6 @@ on run argv
delay 1
close

-- one last open and close so you can see everything looks correct
open
delay 5
close

end tell

delay 1
Expand Down
2 changes: 2 additions & 0 deletions cmake/osxbundle/Info.plist.in
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@
<string>APPL</string>
<key>LSUIElement</key>
<string>1</string>
<key>NSCameraUsageDescription</key>
<string>Hyperion uses this access to record screencasts</string>
<key>NSHumanReadableCopyright</key>
<string>${MACOSX_BUNDLE_COPYRIGHT}</string>
<key>Source Code</key>
Expand Down
106 changes: 63 additions & 43 deletions effects/collision.py
Original file line number Diff line number Diff line change
@@ -1,60 +1,80 @@
# Two projectiles are sent from random positions and collide with each other
# Template from https://github.com/nickpesce/lit/blob/master/lit/effects/collision.py
import hyperion, time, colorsys, random

# Get parameters
sleepTime = max(0.02, float(hyperion.args.get('speed', 100))/1000.0)
trailLength = max(3, int(hyperion.args.get('trailLength', 5)))
explodeRadius = int(hyperion.args.get('explodeRadius', 8))

# Ensure that the range for pixel indices stays within bounds
maxPixelIndex = hyperion.ledCount - 1
if trailLength > maxPixelIndex or explodeRadius > maxPixelIndex:
exit(f"Error: Color length ({trailLength}) and detonation range ({explodeRadius}) must be less than number of LEDs configured ({hyperion.ledCount})")

# Create additional variables
increment = None
projectiles = []

# Initialize the led data
ledData = bytearray()
for i in range(hyperion.ledCount):
ledData += bytearray((0,0,0))
ledData += bytearray((0,0,0))

# Start the write data loop
while not hyperion.abort():
if (len(projectiles) != 2):
projectiles = [ [0, 1, random.uniform(0.0, 1.0)], [hyperion.ledCount-1, -1, random.uniform(0.0, 1.0)] ]
increment = -random.randint(0, hyperion.ledCount-1) if random.choice([True, False]) else random.randint(0, hyperion.ledCount-1)

ledDataBuf = ledData[:]
for i, v in enumerate(projectiles):
projectiles[i][0] = projectiles[i][0]+projectiles[i][1]
for t in range(0, trailLength):
pixel = v[0] - v[1]*t
if pixel + 2 < 0:
pixel += hyperion.ledCount
if pixel + 2 > hyperion.ledCount-1:
pixel -= hyperion.ledCount-1
rgb = colorsys.hsv_to_rgb(v[2], 1, (trailLength - 1.0*t)/trailLength)
ledDataBuf[3*pixel ] = int(255*rgb[0])
ledDataBuf[3*pixel + 1] = int(255*rgb[1])
ledDataBuf[3*pixel + 2] = int(255*rgb[2])

hyperion.setColor(ledDataBuf[-increment:] + ledDataBuf[:-increment])

for i1, p1 in enumerate(projectiles):
for i2, p2 in enumerate(projectiles):
if (p1 is not p2):
prev1 = p1[0] - p1[1]
prev2 = p2[0] - p2[1]
if (prev1 - prev2 < 0) != (p1[0] - p2[0] < 0):
for d in range(0, explodeRadius):
for pixel in range(p1[0] - d, p1[0] + d):
rgb = colorsys.hsv_to_rgb(random.choice([p1[2], p2[2]]), 1, (1.0 * explodeRadius - d) / explodeRadius)
ledDataBuf[3*pixel ] = int(255*rgb[0])
ledDataBuf[3*pixel + 1] = int(255*rgb[1])
ledDataBuf[3*pixel + 2] = int(255*rgb[2])

hyperion.setColor(ledDataBuf[-increment:] + ledDataBuf[:-increment])
time.sleep(sleepTime)

projectiles.remove(p1)
projectiles.remove(p2)

time.sleep(sleepTime)
if len(projectiles) != 2:
projectiles = [
[0, 1, random.uniform(0.0, 1.0)], # Start positions of projectiles
[hyperion.ledCount-1, -1, random.uniform(0.0, 1.0)]
]
increment = -random.randint(0, hyperion.ledCount-1) if random.choice([True, False]) else random.randint(0, hyperion.ledCount-1)

# Backup the LED data
ledDataBuf = ledData[:]
for i, v in enumerate(projectiles):
# Update projectile positions
projectiles[i][0] = projectiles[i][0] + projectiles[i][1]

for t in range(0, trailLength):
# Calculate pixel index for the trail
pixel = v[0] - v[1] * t
if pixel < 0:
pixel += hyperion.ledCount
if pixel >= hyperion.ledCount:
pixel -= hyperion.ledCount

# Make sure pixel is within bounds
if pixel < 0 or pixel >= hyperion.ledCount:
continue

rgb = colorsys.hsv_to_rgb(v[2], 1, (trailLength - 1.0 * t) / trailLength)
ledDataBuf[3*pixel] = int(255 * rgb[0])
ledDataBuf[3*pixel + 1] = int(255 * rgb[1])
ledDataBuf[3*pixel + 2] = int(255 * rgb[2])

hyperion.setColor(ledDataBuf[-increment:] + ledDataBuf[:-increment])

# Check for collision and handle explosion
for i1, p1 in enumerate(projectiles):
for i2, p2 in enumerate(projectiles):
if p1 is not p2:
prev1 = p1[0] - p1[1]
prev2 = p2[0] - p2[1]
if (prev1 - prev2 < 0) != (p1[0] - p2[0] < 0):
for d in range(0, explodeRadius):
for pixel in range(p1[0] - d, p1[0] + d):
# Check if pixel is out of bounds
if pixel < 0 or pixel >= hyperion.ledCount:
continue

rgb = colorsys.hsv_to_rgb(random.choice([p1[2], p2[2]]), 1, (1.0 * explodeRadius - d) / explodeRadius)
ledDataBuf[3 * pixel] = int(255 * rgb[0])
ledDataBuf[3 * pixel + 1] = int(255 * rgb[1])
ledDataBuf[3 * pixel + 2] = int(255 * rgb[2])

hyperion.setColor(ledDataBuf[-increment:] + ledDataBuf[:-increment])
time.sleep(sleepTime)

projectiles.remove(p1)
projectiles.remove(p2)

time.sleep(sleepTime)
30 changes: 15 additions & 15 deletions include/effectengine/Effect.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,13 @@ class Effect : public QThread

friend class EffectModule;

Effect(Hyperion *hyperion
, int priority
, int timeout
, const QString &script
, const QString &name
, const QJsonObject &args = QJsonObject()
, const QString &imageData = ""
Effect(Hyperion* hyperion
, int priority
, int timeout
, const QString& script
, const QString& name
, const QJsonObject& args = QJsonObject()
, const QString& imageData = ""
);
~Effect() override;

Expand Down Expand Up @@ -64,20 +64,20 @@ class Effect : public QThread
QString getScript() const { return _script; }
QString getName() const { return _name; }

int getTimeout() const {return _timeout; }
int getTimeout() const { return _timeout; }
bool isEndless() const { return _isEndless; }

QJsonObject getArgs() const { return _args; }

signals:
void setInput(int priority, const std::vector<ColorRgb> &ledColors, int timeout_ms, bool clearEffect);
void setInputImage(int priority, const Image<ColorRgb> &image, int timeout_ms, bool clearEffect);
void setInput(int priority, const std::vector<ColorRgb>& ledColors, int timeout_ms, bool clearEffect);
void setInputImage(int priority, const Image<ColorRgb>& image, int timeout_ms, bool clearEffect);

private:
void setModuleParameters();
bool setModuleParameters();
void addImage();

Hyperion *_hyperion;
Hyperion* _hyperion;

const int _priority;

Expand All @@ -95,12 +95,12 @@ class Effect : public QThread
/// Buffer for colorData
QVector<ColorRgb> _colors;

Logger *_log;
Logger* _log;
// Reflects whenever this effects should interrupt (timeout or external request)
std::atomic<bool> _interupt {};
std::atomic<bool> _interupt{};

QSize _imageSize;
QImage _image;
QPainter *_painter;
QPainter* _painter;
QVector<QImage> _imageStack;
};
Loading