Private
Public Access
197 lines
8.2 KiB
C
197 lines
8.2 KiB
C
/* Luna API handlers. Included after the shared runtime context. */
|
|
#include "service_calibration.h"
|
|
static bool start_failure(void *h, void *m, const char *message) {
|
|
retry_update(&retries, boot_enabled() && geteuid() == 0, false, milliseconds());
|
|
char json[512];
|
|
snprintf(
|
|
json, sizeof(json),
|
|
"{\"returnValue\":false,\"errorText\":\"%s\",\"retryScheduled\":%s,\"retryBlocked\":%s}",
|
|
message, retries.due ? "true" : "false", retries.blocked ? "true" : "false");
|
|
return respond(h, m, json);
|
|
}
|
|
static bool start(void *h, void *m, void *ctx) {
|
|
(void)ctx;
|
|
const char *p = m ? payload(m) : NULL;
|
|
if (p && strcmp(p, "{}") != 0)
|
|
return respond(h, m,
|
|
"{\"returnValue\":false,\"errorText\":\"Start expects an empty object; use "
|
|
"settings to configure the receiver\"}");
|
|
if (m)
|
|
retry_reset(&retries);
|
|
if (piccap_owned)
|
|
return respond(h, m, "{\"returnValue\":true,\"running\":true,\"activeSource\":\"PicCap\"}");
|
|
if (capture_ctx.handle)
|
|
return respond(h, m, "{\"returnValue\":true,\"running\":true}");
|
|
if (!playback_json.lib) {
|
|
set_error("JSON-C is unavailable; cannot start receiver output");
|
|
return start_failure(h, m, "JSON-C is unavailable");
|
|
}
|
|
capture_ctx.frames = 0;
|
|
capture_ctx.revoked = 0;
|
|
last_error[0] = 0;
|
|
capture_ctx.diagnostic[0] = 0;
|
|
if (receiver_runtime.starting)
|
|
return respond(h, m, "{\"returnValue\":true,\"connecting\":true}");
|
|
receiver_runtime.clear_pending = false;
|
|
if (!output_begin(&receiver_runtime.output, &target, milliseconds())) {
|
|
set_error(receiver_runtime.output.error);
|
|
return start_failure(h, m, receiver_runtime.output.error);
|
|
}
|
|
receiver_runtime.starting = true;
|
|
return respond(h, m, "{\"returnValue\":true,\"connecting\":true}");
|
|
}
|
|
static bool initialize_capture(void *h, void *m) {
|
|
if (!enter_developer_mode()) {
|
|
stop_capture();
|
|
set_error("Developer initialization needs root. Elevate this own service through Homebrew "
|
|
"Channel.");
|
|
return start_failure(h, m,
|
|
"Root access is required for temporary LG developer initialization");
|
|
}
|
|
capture_ctx.handle = vt_init(policy);
|
|
if (!leave_developer_mode()) {
|
|
stop_capture();
|
|
retries.blocked = true;
|
|
set_error("Developer marker cleanup failed; manual intervention required");
|
|
return start_failure(h, m, "Developer marker cleanup failed");
|
|
}
|
|
if (!capture_ctx.handle) {
|
|
stop_capture();
|
|
set_error("LG refused histogram initialization");
|
|
return start_failure(
|
|
h, m,
|
|
"LG refused histogram initialization. Another capture source may hold VT_CAPTURE.");
|
|
}
|
|
if (vt_preset(capture_ctx.handle, 0) != 0) {
|
|
stop_capture();
|
|
set_error("LG histogram preset initialization failed");
|
|
return start_failure(h, m, "Histogram preset failed");
|
|
}
|
|
capture_ctx.preset = capture_ctx.letterbox_mode = capture_ctx.settling = 0;
|
|
capture_ctx.pending_letterbox = -1;
|
|
capture_ctx.next_border_probe = 0;
|
|
retries.due = 0;
|
|
retries.attempts = 0;
|
|
retries.quick_retries = 0;
|
|
if (capture_ctx.recovery_started) {
|
|
capture_ctx.last_recovery_ms = milliseconds() - capture_ctx.recovery_started;
|
|
capture_ctx.recovery_started = 0;
|
|
}
|
|
return respond(h, m, "{\"returnValue\":true,\"running\":true}");
|
|
}
|
|
static bool stop(void *h, void *m, void *ctx) {
|
|
(void)ctx;
|
|
calibration_ctx.until = 0;
|
|
retry_pause(&retries);
|
|
bool bridging = bridge_phase != BRIDGE_IDLE;
|
|
stream_close(milliseconds());
|
|
bridge_phase = BRIDGE_IDLE;
|
|
memset(&capture_ctx.continuity, 0, sizeof(capture_ctx.continuity));
|
|
stop_capture();
|
|
if (bridging && receiver_runtime.output.stage != OUTPUT_READY) {
|
|
output_begin(&receiver_runtime.output, &target, milliseconds());
|
|
receiver_runtime.clear_pending = true;
|
|
}
|
|
return respond(h, m, "{\"returnValue\":true,\"running\":false}");
|
|
}
|
|
static bool autostart(void *h, void *m, void *ctx) {
|
|
(void)ctx;
|
|
const char *p = m ? payload(m) : NULL;
|
|
bool enabled;
|
|
if (p && strcmp(p, "{\"enabled\":true}") == 0)
|
|
enabled = true;
|
|
else if (p && strcmp(p, "{\"enabled\":false}") == 0)
|
|
enabled = false;
|
|
else
|
|
return respond(h, m, "{\"returnValue\":false,\"errorText\":\"Expected enabled boolean\"}");
|
|
if (geteuid() != 0)
|
|
return respond(
|
|
h, m,
|
|
"{\"returnValue\":false,\"rootRequired\":true,\"errorText\":\"Root access is required "
|
|
"to change autostart. Select Enable Root Access in Overview.\"}");
|
|
if (!set_boot(enabled))
|
|
return respond(
|
|
h, m,
|
|
"{\"returnValue\":false,\"errorText\":\"Cannot update the HyperDRM boot hook. Check "
|
|
"the installed boot script and whether another file occupies the hook path.\"}");
|
|
if (enabled)
|
|
retry_reset(&retries);
|
|
retry_update(&retries, enabled, capture_ctx.handle != NULL, milliseconds());
|
|
return respond(h, m,
|
|
enabled ? "{\"returnValue\":true,\"autostart\":true}"
|
|
: "{\"returnValue\":true,\"autostart\":false}");
|
|
}
|
|
static bool restart_service(void *h, void *m, void *ctx) {
|
|
(void)ctx;
|
|
if (strcmp(payload(m), "{}"))
|
|
return respond(h, m,
|
|
"{\"returnValue\":false,\"errorText\":\"Restart expects an empty object\"}");
|
|
bool ok = respond(h, m, "{\"returnValue\":true,\"restarting\":true}");
|
|
quitting = 1;
|
|
return ok;
|
|
}
|
|
static bool handover(void *h, void *m, void *ctx) {
|
|
(void)ctx;
|
|
const char *p = payload(m);
|
|
bool enabled;
|
|
if (strcmp(p, "{\"enabled\":true}") == 0)
|
|
enabled = true;
|
|
else if (strcmp(p, "{\"enabled\":false}") == 0)
|
|
enabled = false;
|
|
else
|
|
return respond(h, m, "{\"returnValue\":false,\"errorText\":\"Expected enabled boolean\"}");
|
|
if (!playback_json.lib || geteuid() != 0)
|
|
return respond(
|
|
h, m, "{\"returnValue\":false,\"errorText\":\"Root service and JSON-C are required\"}");
|
|
if (enabled && !piccap_installed())
|
|
return respond(h, m, "{\"returnValue\":false,\"errorText\":\"PicCap is not installed\"}");
|
|
if (!handover_save(enabled))
|
|
return respond(
|
|
h, m,
|
|
"{\"returnValue\":false,\"errorText\":\"Could not save PicCap handover preference\"}");
|
|
handover_enabled = enabled;
|
|
protection = PLAYBACK_UNKNOWN;
|
|
clear_samples = 0;
|
|
clear_since = unknown_since = 0;
|
|
next_media_probe = 0;
|
|
return respond(h, m, "{\"returnValue\":true}");
|
|
}
|
|
static bool settings(void *h, void *m, void *ctx) {
|
|
(void)ctx;
|
|
TargetConfig next = target;
|
|
if (!playback_json.lib || !target_parse(&playback_json, payload(m), &next))
|
|
return respond(h, m,
|
|
"{\"returnValue\":false,\"errorText\":\"Enter a valid IPv4/IPv6 address, "
|
|
"integer port (1-65535), receiver and optional API token\"}");
|
|
if (!target_save(TARGET_PREF, &next))
|
|
return respond(
|
|
h, m, "{\"returnValue\":false,\"errorText\":\"Could not save receiver settings\"}");
|
|
bool active = capture_ctx.handle || receiver_runtime.starting || piccap_owned || retries.due;
|
|
stop_capture();
|
|
stream_close(milliseconds());
|
|
bridge_phase = BRIDGE_IDLE;
|
|
memset(&capture_ctx.continuity, 0, sizeof(capture_ctx.continuity));
|
|
test_close();
|
|
connection_test.stage = TEST_IDLE;
|
|
target = next;
|
|
last_error[0] = 0;
|
|
protection = PLAYBACK_UNKNOWN;
|
|
clear_samples = 0;
|
|
clear_since = unknown_since = 0;
|
|
if (active && !retries.paused && !retries.blocked)
|
|
retries.due = milliseconds();
|
|
return respond(h, m, "{\"returnValue\":true}");
|
|
}
|
|
static bool test_connection(void *h, void *m, void *ctx) {
|
|
(void)ctx;
|
|
if (strcmp(payload(m), "{}") || !playback_json.lib)
|
|
return respond(
|
|
h, m,
|
|
"{\"returnValue\":false,\"errorText\":\"Test requires an empty object and JSON-C\"}");
|
|
if (!test_begin(&target, milliseconds()))
|
|
return respond(
|
|
h, m, "{\"returnValue\":false,\"errorText\":\"A connection test is already running\"}");
|
|
return respond(h, m, "{\"returnValue\":true,\"testing\":true}");
|
|
}
|
|
#include "service_status.h"
|