Prepare webOSbrew publishing audit and safe symlink autostart

This commit is contained in:
matze19999 committed 2026-10-06 13:43:00 +02:00
1 parent aaa8aa82e6
commit b81cb9a8a0
16 files changed
+185 -31

No files matched your search

+1
View File
@@ -10,3 +10,4 @@
*.ipk binary *.ipk binary
*.mp4 binary *.mp4 binary
service/histogram-service binary service/histogram-service binary
*.sh text eol=lf
+2
View File
@@ -21,3 +21,5 @@ test-interpolation
test-layout test-layout
service/led-mapping.txt service/led-mapping.txt
service/led-interpolation.txt service/led-interpolation.txt
test-autostart
+15
View File
@@ -0,0 +1,15 @@
{
"id": "org.webosbrew.histogramhyperion",
"version": "0.6.1",
"type": "web",
"title": "HyperDRM",
"appDescription": "Ambient Light für webOS und Hyperion",
"iconUri": "https://gitea.adnau.de/matze19999/HyperDRM/raw/branch/main/app/icon.png",
"sourceUrl": "https://gitea.adnau.de/matze19999/HyperDRM",
"rootRequired": true,
"ipkUrl": "https://gitea.adnau.de/matze19999/HyperDRM/raw/branch/main/HyperDRM_0.6.1_arm.ipk",
"ipkHash": {
"sha256": "164f8e18139fbd931de5999d744f0633cca8c979570727db81777ba430d41f29"
},
"ipkSize": 1163172
}
Binary file not shown.
+8 -3
View File
@@ -25,7 +25,7 @@ Only the own service is modified. No Hue, LG library, driver or firmware binarie
## Installation ## Installation
The current package is [HyperDRM_0.6.0_arm.ipk](HyperDRM_0.6.0_arm.ipk). Install it using the TV's webOS/Homebrew package installation workflow. The current package is [HyperDRM_0.6.1_arm.ipk](HyperDRM_0.6.1_arm.ipk). Install it using the TV's webOS/Homebrew package installation workflow.
Elevate the installed own service through Homebrew Channel: Elevate the installed own service through Homebrew Channel:
@@ -41,7 +41,7 @@ The internal app/service IDs remain `org.webosbrew.histogramhyperion` and `org.w
The app's **Autostart: Ein/Aus** switch controls the own hook `/var/lib/webosbrew/init.d/60-histogramhyperion`. Homebrew Channel runs it during boot after service elevation. It retries start while the TV and Hyperion initialize, with up to 120 attempts separated by five seconds. The Homebrew failsafe can suppress boot hooks. The app's **Autostart: Ein/Aus** switch controls the own hook `/var/lib/webosbrew/init.d/60-histogramhyperion`. Homebrew Channel runs it during boot after service elevation. It retries start while the TV and Hyperion initialize, with up to 120 attempts separated by five seconds. The Homebrew failsafe can suppress boot hooks.
**Stoppen** ends the current capture session and leaves the saved autostart preference unchanged. Turning autostart off removes the own hook and cancels waiting boot attempts. A foreign file or symlink at that path will not be overwritten. **Stoppen** ends the current capture session and leaves the saved autostart preference unchanged. The hook is a symlink to `boot.sh` inside the service container. Removal leaves a harmless dangling link; the script also exits when its service target is absent. Turning autostart off removes the own hook and cancels waiting boot attempts. A foreign file or symlink at that path will not be overwritten.
Boot log: `/var/lib/webosbrew/histogramhyperion-boot.log`. Boot log: `/var/lib/webosbrew/histogramhyperion-boot.log`.
@@ -52,6 +52,7 @@ Use an ARM EABI cross-compiler and Python 3:
```sh ```sh
arm-linux-gnueabi-gcc -Wall -Wextra -Werror -O2 -rdynamic native/service.c -ldl -o service/histogram-service arm-linux-gnueabi-gcc -Wall -Wextra -Werror -O2 -rdynamic native/service.c -ldl -o service/histogram-service
python3 build_package.py python3 build_package.py
python3 build_manifest.py
``` ```
The package builder bundles only the own app, native executable, metadata and logo assets. A built executable and current IPK are included for the tested platform. The package builder bundles only the own app, native executable, metadata and logo assets. A built executable and current IPK are included for the tested platform.
@@ -67,7 +68,7 @@ arm-linux-gnueabi-gcc -Wall -Wextra -Werror -O2 native/test_layout.c -ldl -o tes
arm-linux-gnueabi-gcc -Wall -Wextra -Werror -O2 native/test_interpolation.c -o test-interpolation arm-linux-gnueabi-gcc -Wall -Wextra -Werror -O2 native/test_interpolation.c -o test-interpolation
``` ```
The tests passed on the G4. Boot-hook invocation started a stopped service; an actual cold boot has not yet been tested. Autostart activation was tested; deactivation is implemented but has not yet been practically tested on the TV. The tests passed on the G4. Boot-hook invocation started a stopped service; an actual cold boot has not yet been tested. Isolated ARM tests also cover autostart deactivation, legacy-hook migration, foreign-hook protection and removal of the target container.
`calibration-gradient.mp4` is the own red/green coordinate ramp used for calibration. `spatial-calibration.json` and `spatial-interpolation.json` record the calibration and example layout. The offline mapping scripts require locally obtained `calibration-samples.jsonl` and `serverinfo.json`; these diagnostic snapshots are not distributed. Runtime mapping is implemented in `native/layout.h` and reads the actual Hyperion layout. `calibration-gradient.mp4` is the own red/green coordinate ramp used for calibration. `spatial-calibration.json` and `spatial-interpolation.json` record the calibration and example layout. The offline mapping scripts require locally obtained `calibration-samples.jsonl` and `serverinfo.json`; these diagnostic snapshots are not distributed. Runtime mapping is implemented in `native/layout.h` and reads the actual Hyperion layout.
@@ -76,3 +77,7 @@ To regenerate the test video, install `imageio-ffmpeg` in your Python environmen
## Logo ## Logo
The logo master is `app/logo.png`, with 80 px and 130 px installation icons. The logo master is `app/logo.png`, with 80 px and 130 px installation icons.
## Publishing status
See [the publishing review](docs/publishing-review.md) for the requirements audit and unresolved submission conditions. This repository is not a claim that webOSbrew has approved the app.
+1 -1
View File
@@ -1 +1 @@
{"id": "org.webosbrew.histogramhyperion", "version": "0.6.0", "vendor": "Local development", "type": "web", "main": "index.html", "title": "HyperDRM", "icon": "icon.png", "resolution": "1920x1080", "disableBackHistoryAPI": true, "largeIcon": "large-icon.png", "appDescription": "Ambient Light für webOS und Hyperion"} {"id": "org.webosbrew.histogramhyperion", "version": "0.6.1", "vendor": "Local development", "type": "web", "main": "index.html", "title": "HyperDRM", "icon": "icon.png", "resolution": "1920x1080", "disableBackHistoryAPI": true, "largeIcon": "large-icon.png", "appDescription": "Ambient Light für webOS und Hyperion", "sourceUrl": "https://gitea.adnau.de/matze19999/HyperDRM"}
+14
View File
@@ -0,0 +1,14 @@
"""Describe the exact built IPK for Homebrew Channel."""
import hashlib, json
from pathlib import Path
ROOT=Path(__file__).resolve().parent
BASE='https://gitea.adnau.de/matze19999/HyperDRM'
RAW=BASE+'/raw/branch/main/'
app=json.loads((ROOT/'app/appinfo.json').read_text(encoding='utf-8'))
ipk=ROOT/('HyperDRM_'+app['version']+'_arm.ipk')
data=ipk.read_bytes()
manifest={key:app[key] for key in ('id','version','type','title','appDescription')}
manifest.update(iconUri=RAW+'app/icon.png',sourceUrl=BASE,rootRequired=True,
ipkUrl=RAW+ipk.name,ipkHash={'sha256':hashlib.sha256(data).hexdigest()},ipkSize=len(data))
(ROOT/'HyperDRM.manifest.json').write_text(json.dumps(manifest,indent=2,ensure_ascii=False)+'\n',encoding='utf-8')
print('Manifest:',ipk.name,len(data),'bytes',manifest['ipkHash']['sha256'])
+3 -3
View File
@@ -1,9 +1,9 @@
"""Package own app and executable; no Signify or LG binaries are included.""" """Package own app and executable; no Signify or LG binaries are included."""
import io, json, struct, tarfile, zlib import io, json, tarfile
from pathlib import Path from pathlib import Path
ROOT=Path(__file__).resolve().parent ROOT=Path(__file__).resolve().parent
ID='org.webosbrew.histogramhyperion' ID='org.webosbrew.histogramhyperion'
VERSION=json.loads((ROOT/'app/appinfo.json').read_text())['version'] VERSION=json.loads((ROOT/'app/appinfo.json').read_text(encoding='utf-8'))['version']
def tar_bytes(entries): def tar_bytes(entries):
stream=io.BytesIO() stream=io.BytesIO()
with tarfile.open(fileobj=stream,mode='w:gz') as tar: with tarfile.open(fileobj=stream,mode='w:gz') as tar:
@@ -14,7 +14,7 @@ def tar_bytes(entries):
entries=[] entries=[]
for directory,target in [('app','applications/'+ID),('service','services/'+ID+'.service'),('package','packages/'+ID)]: for directory,target in [('app','applications/'+ID),('service','services/'+ID+'.service'),('package','packages/'+ID)]:
for path in sorted((ROOT/directory).iterdir()): for path in sorted((ROOT/directory).iterdir()):
if path.is_file() and path.name not in {"led-mapping.txt", "led-interpolation.txt"}:entries.append(('usr/palm/'+target+'/'+path.name,path.read_bytes(),0o755 if path.name=='histogram-service' else 0o644)) if path.is_file() and path.name not in {"led-mapping.txt", "led-interpolation.txt"}:entries.append(('usr/palm/'+target+'/'+path.name,path.read_bytes(),0o755 if path.name in {'histogram-service','boot.sh'} else 0o644))
control=f'Package: {ID}\nVersion: {VERSION}\nArchitecture: arm\nMaintainer: Local development\nDescription: HyperDRM ambient light for webOS and Hyperion\nSection: misc\nPriority: optional\n\n'.encode() control=f'Package: {ID}\nVersion: {VERSION}\nArchitecture: arm\nMaintainer: Local development\nDescription: HyperDRM ambient light for webOS and Hyperion\nSection: misc\nPriority: optional\n\n'.encode()
members=[('debian-binary',b'2.0\n'),('control.tar.gz',tar_bytes([('control',control,0o644)])),('data.tar.gz',tar_bytes(entries))] members=[('debian-binary',b'2.0\n'),('control.tar.gz',tar_bytes([('control',control,0o644)])),('data.tar.gz',tar_bytes(entries))]
out=bytearray(b'!<arch>\n') out=bytearray(b'!<arch>\n')
+30
View File
@@ -0,0 +1,30 @@
# webOSbrew publishing review
Requirements: https://www.webosbrew.org/develop/guides/publishing/rules/
Reviewed: 2026-10-06. No submission has been opened.
## Human review and disclosure
The implementation and original icon were produced with substantial AI assistance. The owner directed development and tested visual behavior on a real LG G4 with webOS 24. These user tests do not establish that a human has reviewed the entire source. Before submission, a maintainer must meaningfully review the implementation and be able to explain its design; the submission's AI disclosure must reflect the actual history. Removing creation notes does not change that history. The generated icon should be reviewed or replaced by the owner, according to the recommendation that icons and descriptions be the author's own work.
Design review points: histogram bin width and RGB averages; LG developer-marker lifetime; bounded Hyperion layout parsing; interpolation/calibration limits; black-bar preset transitions; root-only methods; boot-hook migration; source-transition retries and cancellation.
## DRM and content
No media, DRM keys, ROMs, account bypasses or third-party capture binaries are distributed. The calibration video is a locally generated coordinate ramp. The app sends RGB color values rather than video frames.
The capture initializer temporarily creates `/var/luna/preferences/vt_devmode` to pass LG's developer gate. Although no decryption or DRM patch is implemented, it has not been established whether the resulting access to color data during protected playback is acceptable under webOSbrew's prohibition of DRM circumvention. This must be reviewed explicitly before submission. Do not certify DRM compliance solely because only colors are output.
## Hardware and removal
No writes to signed system partitions. Writes outside the service container are limited to the own Homebrew autostart symlink, bounded boot log and transient developer marker. A guardian removes the own marker after initialization or process termination; a previously existing marker is preserved. Concurrent writes/power loss remain review considerations.
The boot hook is a symlink to `service/boot.sh` in the installed container. The script checks for its executable and service metadata before acting, checks them again on every retry, and exits when the hook is disabled or the target is removed. Only an exact known legacy own hook is migrated; foreign files/symlinks are rejected. Logs are truncated at each invocation and per-attempt output is bounded.
## Source, licence and release
Public repository page and raw README access were verified without credentials (HTTP 200); the API restricts anonymous requests. An identifiable project licence must be chosen by the owner. No vendored proprietary libraries are shipped. A release manifest must record the exact IPK hash/size and root requirement. The apps-repo package must use pool `main`, a public sourceUrl and public HTTPS manifest/IPK/icon URLs. No listing is ready until those URLs resolve without credentials.
## Evidence and remaining checks
G4/webOS 24: original capture, spatial output, color boost, dynamic layout tests (50/500), black-bar classification and boot-hook invocation tested. Other models are unsupported until tested. Autostart symlink enable/disable, exact legacy migration, rejection of foreign files/symlinks, temporary-file collision and missing-target removal passed in an isolated ARM test on the TV. Version 0.6.1 was installed and started on the G4; the existing copied production hook migrated to the correct container symlink. Boot script syntax passed on the TV. Actual cold boot and clean-state installation/removal have not yet been demonstrated. Public listing eligibility remains pending human and DRM review, release URL validation and licence choice. The runtime reports Rockhopper 10.2.2-57 and device SoC o24; these measured values are used for the package requirements rather than an unverified minimum release.
+30 -22
View File
@@ -1,28 +1,36 @@
#ifndef HISTOGRAM_AUTOSTART_H #ifndef HISTOGRAM_AUTOSTART_H
#define HISTOGRAM_AUTOSTART_H #define HISTOGRAM_AUTOSTART_H
#define BOOT_HOOK "/var/lib/webosbrew/init.d/60-histogramhyperion" #ifndef BOOT_DIRECTORY
static const char boot_script[] = "#!/bin/sh\n# Histogram Hyperion own boot hook v1\n(\n attempt=0\n while [ -x /var/lib/webosbrew/init.d/60-histogramhyperion ] && [ \"$attempt\" -lt 120 ]; do\n result=$(timeout 12 luna-send -n 1 luna://org.webosbrew.histogramhyperion.service/start '{}' 2>&1)\n case \"$result\" in\n *'\"running\":true'*) exit 0 ;;\n esac\n attempt=$((attempt + 1))\n sleep 5\n done\n echo 'Histogram Hyperion boot start cancelled or timed out'\n) >/var/lib/webosbrew/histogramhyperion-boot.log 2>&1 </dev/null &\nexit 0\n"; #define BOOT_DIRECTORY "/var/lib/webosbrew/init.d"
static bool boot_enabled(void){return access(BOOT_HOOK,X_OK)==0;} #endif
static bool set_boot(bool enabled){ #ifndef BOOT_SCRIPT
struct stat st; #define BOOT_SCRIPT "/media/developer/apps/usr/palm/services/org.webosbrew.histogramhyperion.service/boot.sh"
if(lstat(BOOT_HOOK,&st)==0){ #endif
if(!S_ISREG(st.st_mode))return false; #define BOOT_HOOK BOOT_DIRECTORY "/60-histogramhyperion"
char existing[sizeof(boot_script)];int fd=open(BOOT_HOOK,O_RDONLY|O_NOFOLLOW|O_CLOEXEC);if(fd<0)return false; /* Exact pre-0.6.1 hook; only this known own file may be migrated. */
ssize_t size=read(fd,existing,sizeof(existing));close(fd); static const char legacy_boot_script[] = "#!/bin/sh\n# Histogram Hyperion own boot hook v1\n(\n attempt=0\n while [ -x /var/lib/webosbrew/init.d/60-histogramhyperion ] && [ \"$attempt\" -lt 120 ]; do\n result=$(timeout 12 luna-send -n 1 luna://org.webosbrew.histogramhyperion.service/start '{}' 2>&1)\n case \"$result\" in\n *'\"running\":true'*) exit 0 ;;\n esac\n attempt=$((attempt + 1))\n sleep 5\n done\n echo 'Histogram Hyperion boot start cancelled or timed out'\n) >/var/lib/webosbrew/histogramhyperion-boot.log 2>&1 </dev/null &\nexit 0\n";
if(size!=(ssize_t)(sizeof(boot_script)-1) || memcmp(existing,boot_script,(size_t)size)!=0)return false; /* 0: absent, 1: own legacy copy, 2: own symlink, -1: foreign/error. */
if(!enabled)return unlink(BOOT_HOOK)==0; static int boot_hook_kind(void){
return chmod(BOOT_HOOK,0755)==0; struct stat st;if(lstat(BOOT_HOOK,&st)!=0)return errno==ENOENT?0:-1;
if(S_ISLNK(st.st_mode)){
char target[sizeof(BOOT_SCRIPT)+1];ssize_t n=readlink(BOOT_HOOK,target,sizeof(target));
return n==(ssize_t)(sizeof(BOOT_SCRIPT)-1) && memcmp(target,BOOT_SCRIPT,(size_t)n)==0?2:-1;
} }
if(errno!=ENOENT)return false; if(!S_ISREG(st.st_mode))return -1;
if(!enabled)return true; char data[sizeof(legacy_boot_script)];int fd=open(BOOT_HOOK,O_RDONLY|O_NOFOLLOW|O_CLOEXEC);if(fd<0)return -1;
if(mkdir("/var/lib/webosbrew",0755)!=0 && errno!=EEXIST)return false; ssize_t n=read(fd,data,sizeof(data));close(fd);
if(mkdir("/var/lib/webosbrew/init.d",0755)!=0 && errno!=EEXIST)return false; return n==(ssize_t)(sizeof(legacy_boot_script)-1) && memcmp(data,legacy_boot_script,(size_t)n)==0?1:-1;
int fd=open(BOOT_HOOK ".tmp",O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC|O_NOFOLLOW,0700);if(fd<0)return false; }
size_t used=0;bool ok=true; static bool boot_enabled(void){return boot_hook_kind()>0 && access(BOOT_HOOK,X_OK)==0;}
while(used<sizeof(boot_script)-1){ssize_t n=write(fd,boot_script+used,sizeof(boot_script)-1-used);if(n<=0){ok=false;break;}used+=(size_t)n;} static bool set_boot(bool enabled){
if(ok)ok=fchmod(fd,0755)==0 && fsync(fd)==0; int kind=boot_hook_kind();if(kind<0)return false;
if(close(fd)!=0)ok=false; if(!enabled)return kind==0 || unlink(BOOT_HOOK)==0;
if(ok)ok=rename(BOOT_HOOK ".tmp",BOOT_HOOK)==0; if(access(BOOT_SCRIPT,X_OK)!=0)return false;
if(kind==2)return true;
if(mkdir(BOOT_DIRECTORY,0755)!=0 && errno!=EEXIST)return false;
/* Never overwrite another process's temporary file or a foreign hook. */
if(symlink(BOOT_SCRIPT,BOOT_HOOK ".tmp")!=0)return false;
bool ok=rename(BOOT_HOOK ".tmp",BOOT_HOOK)==0;
if(!ok)unlink(BOOT_HOOK ".tmp"); if(!ok)unlink(BOOT_HOOK ".tmp");
return ok; return ok;
} }
+2 -1
View File
@@ -150,7 +150,7 @@ static bool autostart(void *h,void *m,void *ctx){
static bool status(void *h,void *m,void *ctx){ static bool status(void *h,void *m,void *ctx){
(void)ctx;char json[4096],safe[512];size_t n=0; (void)ctx;char json[4096],safe[512];size_t n=0;
for(size_t i=0;last_error[i] && n+2<sizeof(safe);i++){char c=last_error[i];if(c=='"'||c=='\\')safe[n++]='\\';if((unsigned char)c>=32)safe[n++]=c;}safe[n]=0; for(size_t i=0;last_error[i] && n+2<sizeof(safe);i++){char c=last_error[i];if(c=='"'||c=='\\')safe[n++]='\\';if((unsigned char)c>=32)safe[n++]=c;}safe[n]=0;
int used=snprintf(json,sizeof(json),"{\"returnValue\":true,\"version\":\"0.6.0\",\"running\":%s,\"colorValid\":%s,\"frames\":%lu,\"endpoint\":\"127.0.0.1:19444\",\"priority\":%d,\"uid\":%u,\"drmVerified\":false,\"mappingVerified\":false,\"outputColor\":[%u,%u,%u],\"error\":\"%s\",\"regions\":[",capture?"true":"false",color_valid?"true":"false",frames,PRIORITY,(unsigned)geteuid(),sent_rgb[0],sent_rgb[1],sent_rgb[2],safe); int used=snprintf(json,sizeof(json),"{\"returnValue\":true,\"version\":\"0.6.1\",\"running\":%s,\"colorValid\":%s,\"frames\":%lu,\"endpoint\":\"127.0.0.1:19444\",\"priority\":%d,\"uid\":%u,\"drmVerified\":false,\"mappingVerified\":false,\"outputColor\":[%u,%u,%u],\"error\":\"%s\",\"regions\":[",capture?"true":"false",color_valid?"true":"false",frames,PRIORITY,(unsigned)geteuid(),sent_rgb[0],sent_rgb[1],sent_rgb[2],safe);
for(int i=0;i<16;i++)used+=snprintf(json+used,sizeof(json)-used,"%s[%u,%u,%u]",i?",":"",rgb[i][0],rgb[i][1],rgb[i][2]); for(int i=0;i<16;i++)used+=snprintf(json+used,sizeof(json)-used,"%s[%u,%u,%u]",i?",":"",rgb[i][0],rgb[i][1],rgb[i][2]);
used+=snprintf(json+used,sizeof(json)-used,"],\"ledCount\":%u,\"spatialOutput\":%s,\"blackPercent\":[",led_count,led_count?"true":"false"); used+=snprintf(json+used,sizeof(json)-used,"],\"ledCount\":%u,\"spatialOutput\":%s,\"blackPercent\":[",led_count,led_count?"true":"false");
for(int i=0;i<16;i++)used+=snprintf(json+used,sizeof(json)-used,"%s%u",i?",":"",black_percent[i]); for(int i=0;i<16;i++)used+=snprintf(json+used,sizeof(json)-used,"%s%u",i?",":"",black_percent[i]);
@@ -218,6 +218,7 @@ static int tick(void *unused){
static void *symbol(void *lib,const char *name){void *s=dlsym(lib,name);if(!s){fprintf(stderr,"Missing symbol %s\n",name);exit(2);}return s;} static void *symbol(void *lib,const char *name){void *s=dlsym(lib,name);if(!s){fprintf(stderr,"Missing symbol %s\n",name);exit(2);}return s;}
int main(void){ int main(void){
signal(SIGTERM,quit_signal);signal(SIGINT,quit_signal); signal(SIGTERM,quit_signal);signal(SIGINT,quit_signal);
if(boot_enabled() && !set_boot(true))fprintf(stderr,"Own legacy boot hook migration failed\n");
void *vt=dlopen("libvtcapture.so.1",RTLD_NOW),*ls=dlopen("libluna-service2.so.3",RTLD_NOW),*glib=dlopen("libglib-2.0.so.0",RTLD_NOW); void *vt=dlopen("libvtcapture.so.1",RTLD_NOW),*ls=dlopen("libluna-service2.so.3",RTLD_NOW),*glib=dlopen("libglib-2.0.so.0",RTLD_NOW);
if(!vt||!ls||!glib){fprintf(stderr,"Missing LG library: %s\n",dlerror());return 2;} if(!vt||!ls||!glib){fprintf(stderr,"Missing LG library: %s\n",dlerror());return 2;}
vt_init=symbol(vt,"vtCapture_initHistogram");vt_preset=symbol(vt,"vtCapture_setPresetHistogram");vt_get=symbol(vt,"vtCapture_getHistogram");vt_finish=symbol(vt,"vtCapture_finalizeHistogram"); vt_init=symbol(vt,"vtCapture_initHistogram");vt_preset=symbol(vt,"vtCapture_setPresetHistogram");vt_get=symbol(vt,"vtCapture_getHistogram");vt_finish=symbol(vt,"vtCapture_finalizeHistogram");
+36
View File
@@ -0,0 +1,36 @@
#define _DEFAULT_SOURCE
#include <assert.h>
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
#define TEST_ROOT "/tmp/hyperdrm-autostart-test"
#define BOOT_DIRECTORY TEST_ROOT "/init.d"
#define BOOT_SCRIPT TEST_ROOT "/boot.sh"
#include "autostart.h"
static void put_file(const char *path,const char *data){
int fd=open(path,O_WRONLY|O_CREAT|O_EXCL,0700);assert(fd>=0);
size_t size=strlen(data);assert(write(fd,data,size)==(ssize_t)size);assert(close(fd)==0);
}
int main(void){
/* Fail rather than touching another run's files. */
assert(mkdir(TEST_ROOT,0700)==0);put_file(BOOT_SCRIPT,"#!/bin/sh\nexit 0\n");
assert(!boot_enabled() && set_boot(false));
assert(set_boot(true));assert(boot_hook_kind()==2 && boot_enabled());
assert(set_boot(true));assert(set_boot(false) && boot_hook_kind()==0);
put_file(BOOT_HOOK,legacy_boot_script);assert(boot_hook_kind()==1);
assert(set_boot(true) && boot_hook_kind()==2);assert(set_boot(false));
put_file(BOOT_HOOK,"foreign startup\n");assert(!set_boot(true) && !set_boot(false));
assert(unlink(BOOT_HOOK)==0);
assert(symlink("/foreign/script",BOOT_HOOK)==0);assert(!set_boot(true) && !set_boot(false));
assert(unlink(BOOT_HOOK)==0);
put_file(BOOT_HOOK ".tmp","foreign temporary\n");assert(!set_boot(true));assert(boot_hook_kind()==0);
assert(unlink(BOOT_HOOK ".tmp")==0);
assert(set_boot(true));assert(unlink(BOOT_SCRIPT)==0);assert(!boot_enabled());
assert(!set_boot(true));assert(set_boot(false));
assert(rmdir(BOOT_DIRECTORY)==0 && rmdir(TEST_ROOT)==0);
puts("PASS: symlink enable/disable, legacy migration, foreign files preserved, missing target harmless");
}
+1 -1
View File
@@ -1 +1 @@
{"id": "org.webosbrew.histogramhyperion", "version": "0.6.0", "vendor": "Local development", "app": "org.webosbrew.histogramhyperion", "services": ["org.webosbrew.histogramhyperion.service"]} {"id": "org.webosbrew.histogramhyperion", "version": "0.6.1", "vendor": "Local development", "app": "org.webosbrew.histogramhyperion", "services": ["org.webosbrew.histogramhyperion.service"], "sourceUrl": "https://gitea.adnau.de/matze19999/HyperDRM"}
+19
View File
@@ -0,0 +1,19 @@
title: HyperDRM
iconUri: https://gitea.adnau.de/matze19999/HyperDRM/raw/branch/main/app/icon.png
manifestUrl: https://gitea.adnau.de/matze19999/HyperDRM/raw/branch/main/HyperDRM.manifest.json
category: amblight
pool: main
shortDescription: TV color histograms for local Hyperion ambient lighting
requirements:
webosRelease: "10.2.2"
deviceSoC:
- o24
description: |
HyperDRM sends spatial LED colors from LG color histograms to Hyperion
running on the TV. It supports dynamic LED layouts, interpolated colors,
top/bottom black-bar detection and optional boot autostart.
Tested on a rooted LG G4, Rockhopper release 10.2.2-57, device SoC o24.
Homebrew Channel service elevation and local Hyperion are required.
Other models and releases are not validated. This is color output;
no video preview stream is provided.
+23
View File
@@ -0,0 +1,23 @@
#!/bin/sh
# HyperDRM: container-owned startup script, linked by the app.
SERVICE_DIR=/media/developer/apps/usr/palm/services/org.webosbrew.histogramhyperion.service
TARGET="$SERVICE_DIR/histogram-service"
HOOK=/var/lib/webosbrew/init.d/60-histogramhyperion
[ -x "$TARGET" ] && [ -f "$SERVICE_DIR/services.json" ] || exit 0
command -v luna-send >/dev/null 2>&1 || exit 0
command -v timeout >/dev/null 2>&1 || exit 0
(
echo 'HyperDRM boot start requested'
attempt=0
while [ -x "$TARGET" ] && [ -f "$SERVICE_DIR/services.json" ] && [ -L "$HOOK" ] && [ "$(readlink "$HOOK")" = "$SERVICE_DIR/boot.sh" ] && [ "$attempt" -lt 120 ]; do
result=$(timeout 12 luna-send -n 1 luna://org.webosbrew.histogramhyperion.service/start '{}' 2>&1)
case "$result" in
*'"running":true'*) echo 'HyperDRM service started'; exit 0 ;;
esac
attempt=$((attempt + 1))
printf 'Attempt %s failed: %.512s\n' "$attempt" "$result"
sleep 5
done
echo 'HyperDRM boot start cancelled, target removed or retry limit reached'
) >/var/lib/webosbrew/histogramhyperion-boot.log 2>&1 </dev/null &
exit 0
Binary file not shown.