Restrict the brightness sink to one app, picked by name not typed

Confirmed the brightness command applies globally, not per-LED
(serverinfo showed exactly one adjustment object, "id": "default",
covering the whole string), so no LED-count configuration is needed
for this at all -- that question resolved itself once the mechanism
was actually inspected instead of assumed.

For "only react while Spotify is running": only one app can be in the
foreground on webOS at a time, so a "capture the current app" button
in this app's own UI can never work -- pressing it means this app is
foreground, not Spotify. The only workable UI is picking a target from
every *installed* app by name, regardless of what's currently running.

That needed a new native capability this service never had: calling
OUT to another Luna service, not just being called. Two additions:

  foreground_app.c   subscribes once, at startup, to
                      com.webos.applicationManager/getForegroundAppInfo
                      and keeps a thread-safe cache the audio thread can
                      read without a blocking Luna call
  service.c           new listApps method, bridging to
                      com.webos.applicationManager/listApps so the
                      frontend never has to call another service
                      directly -- same rule as everywhere else here

Until the subscription has delivered at least one reply, a restricted
sink treats the target app as inactive, not active -- reacting to
audio when the user explicitly restricted it to one app would be the
wrong failure mode. Verified end to end on the host: engine_smoke.c
opens the sink with a restriction set, confirms it reports itself
correctly inactive against the stub Luna bus (which always "fails" to
call out, exactly like a real host with no bus).

Needed real, linkable stub bodies for LSCall/LSCallOneReply/
LSCallCancel/LSMessageGetPayload/LSErrorInit/LSErrorFree
(test/stubs/luna-service2/lunaservice_stub.c) since foreground_app.c
is the first source file here that's actually linked into a host test
binary rather than only syntax-checked -- service.c/main.c's existing
stub declarations were never called, only compiled against. Confirmed
those really are the correct symbol names by cross-compiling clean
against the real webOS SDK's actual libluna-service2, not just the
stub.

Bumped to 1.0.5.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Rene Kievits
2026-08-26 15:46:09 +02:00
co-authored by Claude Opus 5
parent e9f6c87d27
commit d3e4cb6410
20 changed files with 411 additions and 13 deletions
+13
View File
@@ -20,6 +20,8 @@ typedef struct {
} LSError;
typedef bool (*LSMethodFunction)(LSHandle* sh, LSMessage* msg, void* category_context);
typedef bool (*LSFilterFunc)(LSHandle* sh, LSMessage* reply, void* ctx);
typedef unsigned long LSMessageToken;
typedef enum {
LUNA_METHOD_FLAGS_NONE = 0,
@@ -58,6 +60,17 @@ bool LSGmainAttach(LSHandle* handle, GMainLoop* loop, LSError* error);
const char* LSMessageGetPayload(LSMessage* message);
bool LSMessageIsSubscription(LSMessage* message);
bool LSMessageReply(LSHandle* sh, LSMessage* message, const char* reply, LSError* error);
void LSMessageRef(LSMessage* message);
void LSMessageUnref(LSMessage* message);
bool LSSubscriptionAdd(LSHandle* sh, const char* key, LSMessage* message, LSError* error);
bool LSSubscriptionReply(LSHandle* sh, const char* key, const char* payload, LSError* error);
// Client-call API: this service acting as a caller of another service, not
// just a callee. LSCall keeps calling `callback` for every reply (used for
// subscribe:true); LSCallOneReply auto-cancels after the first one.
bool LSCall(LSHandle* sh, const char* uri, const char* payload, LSFilterFunc callback,
void* ctx, LSMessageToken* ret_token, LSError* error);
bool LSCallOneReply(LSHandle* sh, const char* uri, const char* payload, LSFilterFunc callback,
void* ctx, LSMessageToken* ret_token, LSError* error);
bool LSCallCancel(LSHandle* sh, LSMessageToken token, LSError* error);
@@ -0,0 +1,58 @@
// Linkable bodies for the handful of luna-service2 client-call functions
// foreground_app.c calls. service.c/main.c only ever get -fsyntax-only'd, so
// declarations alone are enough for them; foreground_app.c is linked into
// real host test binaries (engine_smoke, rtp_send) via SOURCES[] in
// run-tests.sh, so those symbols need bodies too, or the link fails.
//
// A host has no Luna bus, so "the call failed" is exactly the right
// simulated behaviour -- every caller here already treats that as
// "foreground app tracking unavailable" and degrades accordingly, which is
// also genuinely exercised by the test suite (see the fail-closed check in
// engine_smoke.c).
#include "luna-service2/lunaservice.h"
#include <string.h>
void LSErrorInit(LSError* error)
{
memset(error, 0, sizeof(*error));
}
void LSErrorFree(LSError* error)
{
(void)error;
}
bool LSCall(LSHandle* sh, const char* uri, const char* payload, LSFilterFunc callback,
void* ctx, LSMessageToken* ret_token, LSError* error)
{
(void)sh;
(void)uri;
(void)payload;
(void)callback;
(void)ctx;
(void)ret_token;
if (error)
error->message = (char*)"no Luna bus on this host";
return false;
}
bool LSCallOneReply(LSHandle* sh, const char* uri, const char* payload, LSFilterFunc callback,
void* ctx, LSMessageToken* ret_token, LSError* error)
{
return LSCall(sh, uri, payload, callback, ctx, ret_token, error);
}
bool LSCallCancel(LSHandle* sh, LSMessageToken token, LSError* error)
{
(void)sh;
(void)token;
(void)error;
return true;
}
const char* LSMessageGetPayload(LSMessage* message)
{
(void)message;
return NULL;
}