AE ANDROEQ+文档

ANDROEQ+ 插件 API 参考(SDK v6)

本文是 sdk/PluginApi.h 的完整说明。插件是独立 DLL,不链接进androeq_win.exe;删除 DLL 即可停用。

  • 头文件:sdk/PluginApi.h
  • ABI 版本:AEQ_PLUGIN_API_VERSION == 6
  • 入口:androeq_plugin_entry()
  • C ABI,可用于 MSVC / Clang / MinGW 的 64 位 DLL。
  • 在线版:https://api.androeq.cc/plugin-api.html

1. 最小插件#

cpp
#include "PluginApi.h"

namespace {
const AeqHostApi* g_api = nullptr;
void* g_host = nullptr;
AeqWindowId g_window = 0;

void* AEQ_CALL init_plugin(const AeqHostApi* api, void* host) {
    g_api = api;
    g_host = host;

    AeqWindowDesc wd{};
    wd.size = sizeof(wd);
    wd.title = L"Hello";
    wd.x = wd.y = -1;      // 居中
    wd.width = 520;
    wd.height = 300;
    wd.standalone = 0;     // 0 内置窗口;1 外置悬浮窗口
    wd.theme_mode = AEQ_THEME_FOLLOW;
    wd.user_data = nullptr;

    g_window = api->create_window(host, &wd);
    if (!g_window) return nullptr;

    AeqControlDesc d{};
    d.size = sizeof(d);
    d.type = AEQ_CTL_LABEL;
    d.x = 24; d.y = 24; d.w = 460; d.h = 40;
    d.id = L"hello";
    d.text = L"Hello, ANDROEQ+";
    d.flags = AEQ_CF_BOLD;
    d.font_size = 18.0f;
    api->add_control(host, g_window, &d);
    return reinterpret_cast<void*>(1);
}

void AEQ_CALL shutdown_plugin(void*) {}
void AEQ_CALL activate_plugin(void*) {
    g_api->show_window(g_host, g_window, 1);
}
}

extern "C" __declspec(dllexport) const AeqPluginDescriptor* AEQ_CALL
androeq_plugin_entry() {
    static const AeqPluginDescriptor desc = {
        sizeof(AeqPluginDescriptor), AEQ_PLUGIN_API_VERSION,
        L"hello", L"Hello", L"1.0.0", L"you", L"示例",
        &init_plugin, &shutdown_plugin, &activate_plugin, nullptr};
    return &desc;
}

构建(MSVC):

bat
cmake -S . -B build -G "Visual Studio 18 2026" -A x64
cmake --build build --config Release

把 DLL 放入 <exe目录>\plugins 或 %APPDATA%\ANDROEQ+\plugins,在「插件」页刷新、加载、运行。


2. 描述符与生命周期#

c
typedef struct AeqPluginDescriptor {
    uint32_t size;
    uint32_t api_version;
    const wchar_t* id;
    const wchar_t* name;
    const wchar_t* version;
    const wchar_t* author;
    const wchar_t* description;
    void* (AEQ_CALL* init)(const AeqHostApi* api, void* host_user);
    void  (AEQ_CALL* shutdown)(void* plugin_ctx);
    void  (AEQ_CALL* activate)(void* plugin_ctx);
    void  (AEQ_CALL* deactivate)(void* plugin_ctx);
} AeqPluginDescriptor;
字段说明
sizesizeof(AeqPluginDescriptor)
api_version必须等于 AEQ_PLUGIN_API_VERSION(当前 6)
id唯一 ASCII id,建议短横线
init加载时调用;创建窗口/控件、启动线程
shutdown卸载/退出时调用;必须 join 自己的线程
activate点「运行」时调用;显示窗口/恢复任务
deactivate切走/关闭时调用,可空

宿主按以下顺序扫描 *.dll:

text
<exe目录>\plugins
%APPDATA%\ANDROEQ+\plugins
<exe目录>\old_plugins

3. 窗口#

c
typedef struct AeqWindowDesc {
    uint32_t size;
    const wchar_t* title;
    int32_t x;
    int32_t y;
    int32_t width;
    int32_t height;
    uint32_t flags;
    int32_t standalone;
    int32_t theme_mode;
    uint32_t color_window;
    uint32_t color_header;
    uint32_t color_border;
    uint32_t color_text;
    uint32_t color_muted;
    uint32_t color_accent;
    void* user_data;
} AeqWindowDesc;
  • 坐标单位是 96 DPI 逻辑像素;宿主负责 DPI 缩放。
  • x/y = -1:内置窗口在宿主客户区居中;独立窗口在显示器工作区居中。
  • standalone = 0:内置窗口,宿主内自绘的模态层,打开时主界面变暗并拦截点击。
  • standalone = 1:外置悬浮窗口,独立的顶层 HWND,可拖出主界面并置顶;
    不阻塞主界面。标题栏可拖动,右上角关闭。
  • flags:
    • AEQ_WF_NO_CLOSE:隐藏关闭按钮;
    • AEQ_WF_NO_TITLE:隐藏标题栏;
    • AEQ_WF_TRANSPARENT:外置窗口无面板/边框,仅绘制文字/控件,背景逐像素透明;
    • AEQ_WF_DRAG_ANYWHERE:整个客户区可作为拖动区域(适合无标题栏歌词窗)。
    • 窗口尺寸由 width/height 决定;外置窗口按显示器工作区裁剪到合理范围。

主题#

c
enum AeqThemeMode {
    AEQ_THEME_FOLLOW = 0,
    AEQ_THEME_CUSTOM = 1
};
  • AEQ_THEME_FOLLOW:自动跟随主窗口当前主题(含主色、文字、面板、边框);
    所有 color_* 字段忽略。
  • AEQ_THEME_CUSTOM:使用代码指定颜色;某字段为 0 时回退到当前宿主主题。

颜色格式为 0xAARRGGBB,可用宏:

c
AEQ_RGBA(r, g, b, a)

例如:

c
wd.theme_mode = AEQ_THEME_CUSTOM;
wd.color_window = AEQ_RGBA(28, 34, 52, 255);
wd.color_header = AEQ_RGBA(36, 44, 68, 255);
wd.color_border = AEQ_RGBA(86, 104, 156, 255);
wd.color_text   = AEQ_RGBA(238, 242, 255, 255);
wd.color_muted  = AEQ_RGBA(158, 172, 205, 255);
wd.color_accent = AEQ_RGBA(104, 137, 255, 255);

创建参数#

user_data 是插件自己的任意指针。事件回调里用:

c
void* ctx = api->get_window_user_data(host, window);

取回。宿主不释放该指针;生命周期由插件保证。


4. 控件#

c
typedef struct AeqControlDesc {
    uint32_t size;
    int32_t type;
    int32_t x, y, w, h;
    const wchar_t* id;
    const wchar_t* text;
    const wchar_t* value;
    const wchar_t* items;
    int32_t checked;
    uint32_t flags;
    float font_size;
} AeqControlDesc;
type说明
AEQ_CTL_LABEL文本
AEQ_CTL_BUTTON按钮;点击触发 AEQ_EV_CLICK
AEQ_CTL_EDIT单行输入;回车触发 AEQ_EV_SUBMIT
AEQ_CTL_CHECKBOX勾选;变化触发 AEQ_EV_CHANGE
AEQ_CTL_LIST换行分隔的列表;选择触发 AEQ_EV_CHANGE,双击 AEQ_EV_DOUBLE_CLICK,滚轮滚动
AEQ_CTL_PROGRESS进度条,value 0.0–1.0
AEQ_CTL_IMAGE显示 set_control_image 指定的图片
AEQ_CTL_SEPARATOR分隔线

控件 flags:

flag说明
AEQ_CF_READONLY输入只读
AEQ_CF_PASSWORD输入显示为圆点
AEQ_CF_MULTILINE多行(保留)
AEQ_CF_ALIGN_RIGHT文本右对齐
AEQ_CF_BOLD粗体
AEQ_CF_WRAP自动换行
AEQ_CF_HIDDEN初始隐藏
AEQ_CF_SECONDARY按钮次要样式:header 底 + 边框
AEQ_CF_GHOST按钮幽灵样式:透明底 + 描边

所有控件都有稳定字符串 id 和数字句柄;字符串 id 在窗口内唯一。


5. 事件#

c
typedef void (AEQ_CALL* AeqEventCallback)(
    void* plugin_ctx, AeqWindowId window,
    AeqControlId control, int32_t event);
event触发
AEQ_EV_CLICK按钮
AEQ_EV_CHANGE输入/勾选/列表选择
AEQ_EV_SUBMIT编辑框回车
AEQ_EV_DOUBLE_CLICK列表双击
AEQ_EV_CLOSE窗口关闭
AEQ_EV_RIGHT_CLICK窗口/控件右键(SDK v5+)

回调在 UI 线程执行;回调内可以继续调用宿主 API,但不要做长时间阻塞。


6. 宿主 API#

AeqHostApi 的函数表按顺序追加;不要假设结构体大小固定,使用api->size / api->version。

窗口 / 控件#

函数说明
create_window(desc)创建窗口,返回 AeqWindowId,失败返回 0
destroy_window(window)销毁窗口
show_window(window, show)显示/隐藏(外置窗口会显示为置顶悬浮窗)
set_window_title(window, title)修改标题
invalidate_window(window)请求重绘
add_control(window, desc)添加控件
remove_control(window, control)删除控件
get_control_id(window, id)按字符串 id 查句柄

控件状态(线程安全)#

c
set_control_text(control, text)
get_control_text(control, buffer, capacity)
set_control_value(control, value)
get_control_value(control)
set_control_checked(control, checked)
get_control_checked(control)
set_control_enabled(control, enabled)
get_control_enabled(control)
set_control_visible(control, visible)
get_control_visible(control)
set_control_items(control, newline_items)
get_control_items(control, buffer, capacity)
set_control_image(control, file_path)
set_control_focus(control)
set_control_font_size(control, logical_px)
set_control_rect(control, x, y, w, h)
set_control_opacity(control, 0.0..1.0)

set_control_rect / set_control_opacity 用于歌词推挤、渐隐等逐帧动画。

后台线程可以安全调用这些状态函数;宿主会合并重绘。

宿主服务#

函数说明
set_status(text)主窗口底部状态栏
add_media_file(path,title,artist)把已存在本地音频加入曲库
set_today_recommendations(paths,count)推送到曲库页「今天听什么」
get_data_dir()%APPDATA%\ANDROEQ+
get_plugin_dir()<exe目录>\plugins
open_url(url)系统浏览器打开
set_clipboard_text / get_clipboard_text剪贴板
now_ms()GetTickCount64
get_window_user_data(window)取回窗口创建参数
set_window_opacity(window, 0..1)外置窗口整体透明度(样式切换动画)
show_context_menu(window, items, &selected)鼠标位置弹原生右键菜单;items 用 \n 分隔,- 为分隔线

7. 播放 / 歌词 API(v4 新增)#

c
int32_t get_playback_state(void* user, AeqPlaybackState* state);
int32_t get_current_track(void* user,
                          wchar_t* title, int32_t title_capacity,
                          wchar_t* artist, int32_t artist_capacity);
int32_t get_current_lyric(void* user,
                          wchar_t* current, int32_t current_capacity,
                          wchar_t* next, int32_t next_capacity);

AeqPlaybackState:

c
typedef struct AeqPlaybackState {
    uint32_t size;     // 调用方填 sizeof(AeqPlaybackState)
    int32_t playing;
    int32_t paused;
    double position;   // 秒
    double duration;   // 秒;未打开为 0
} AeqPlaybackState;
  • 这些函数可在插件后台线程每 100–200 ms 轮询;
  • 歌词由宿主按当前曲目自动加载(本地 LRC / 已缓存的 AI 歌词);
  • 无歌词时 get_current_lyric 返回 0,并把缓冲区置空;
  • 返回的字符串在调用返回后复制到调用方缓冲区,插件不要保存宿主指针。

外置歌词窗示例#

cpp
AeqPlaybackState st{};
st.size = sizeof(st);
api->get_playback_state(host, &st);

wchar_t current[2048]{}, next[2048]{};
api->get_current_lyric(host, current, 2048, next, 2048);
api->set_control_text(host, lyricControl, current);

完整示例:plugins/lyrics_float/LyricsFloat.cpp(外置悬浮窗,跟随主题)。


8. 线程与生命周期规则#

  1. 控件状态函数是线程安全的;窗口创建/销毁、事件回调建议在 UI 线程。
  2. shutdown 必须 request_stop() + join() 自己的线程,之后宿主才
    FreeLibrary。
  3. 不要保存宿主返回的 wchar_t* 指针;立即复制。
  4. 不要在事件回调里做网络/磁盘长任务;用后台线程 + 控件状态更新。
  5. 插件与宿主同进程,只有信任的 DLL 才应加载。
  6. 外置窗口在插件卸载/宿主退出时由宿主销毁;插件不要自行 DestroyWindow。

9. 兼容与版本#

  • v2:新增 set_today_recommendations;
  • v3:新增窗口 standalone、theme_mode、color_*、user_data 和
    get_window_user_data;
  • v4:新增播放状态、当前曲目、当前歌词 API;
  • v5:新增 AEQ_WF_TRANSPARENT / AEQ_WF_DRAG_ANYWHERE、AEQ_EV_RIGHT_CLICK、
    set_window_opacity、show_context_menu;
  • v6:新增 set_control_rect / set_control_opacity(歌词推挤/渐隐动画)。

宿主当前要求 api_version == 6;旧插件请用 sdk/PluginApi.h 重新编译。

更偏使用说明的文档见 PLUGINS.md。