/* mp_key.c — 远程控制域:按键注入(写 /dev/input/event* EV_KEY,物理键红线内)。 */ #include "mp.h" #define PWR_KEYDEV "/dev/input/event0" #define ADC_KEYDEV "/dev/input/event2" int keycode_to_device(int code, const char **dev) { switch (code) { case KEY_POWER: *dev = PWR_KEYDEV; return KEY_POWER; case KEY_PREVIOUSSONG: *dev = PWR_KEYDEV; return KEY_PREVIOUSSONG; case KEY_VOLUMEUP: *dev = ADC_KEYDEV; return KEY_VOLUMEUP; case KEY_VOLUMEDOWN: *dev = ADC_KEYDEV; return KEY_VOLUMEDOWN; case KEY_NEXTSONG: *dev = ADC_KEYDEV; return KEY_NEXTSONG; case KEY_PLAYPAUSE: *dev = ADC_KEYDEV; return KEY_PLAYPAUSE; default: return -1; } } int inject_key_event(const char *dev, int code, int down) { int fd = open(dev, O_WRONLY | O_NONBLOCK); if (fd < 0) return -1; struct input_event ev; memset(&ev, 0, sizeof ev); ev.type = EV_KEY; ev.code = code; ev.value = down ? 1 : 0; int ok = (write(fd, &ev, sizeof ev) == (ssize_t)sizeof ev); if (ok) { memset(&ev, 0, sizeof ev); ev.type = EV_SYN; ev.code = SYN_REPORT; ev.value = 0; ok = (write(fd, &ev, sizeof ev) == (ssize_t)sizeof ev); } close(fd); return ok ? 0 : -1; } int inject_key_release(int code, int press_ms) { const char *dev = NULL; int kc = keycode_to_device(code, &dev); if (kc < 0) return -1; int release_ms = press_ms > 0 ? press_ms : 0; if (inject_key_event(dev, code, 1) < 0) return -1; if (release_ms > 0) usleep((useconds_t)release_ms * 1000); else usleep(80 * 1000); if (inject_key_event(dev, code, 0) < 0) return -1; return 0; } int api_key_single(int sock, int code, const req_t *r) { int press_ms = 0; char pressbuf[16] = "tap"; const char *press = pressbuf; if (r->body && r->body[0]) { jv_t *root = jv_parse(r->body, (int)r->body_len); if (root) { jv_t *p = jv_obj_get(root, "press"); if (p && p->type == J_STR) snprintf(pressbuf, sizeof pressbuf, "%s", p->str); jv_t *m = jv_obj_get(root, "ms"); if (m && m->type == J_NUM) press_ms = (int)m->inum; jv_free(root); } } if (!strcmp(press, "hold")) return send_err(sock, 501, "NOT_IMPLEMENTED", "hold 持续注入需常驻通道(阶段 D),后端未实现"); if (strcmp(press, "tap") && strcmp(press, "long")) return send_err(sock, 400, "BAD_REQUEST", "press 非法"); if (press_ms <= 0) { if (!strcmp(press, "long")) press_ms = 3000; } /* 物理键红线:禁止映射 返回(158)/主页(172)/确认/exit(APPS.md)。 */ if (code == 158 || code == 172 || code == 32 || code == 28 || code == 96) return send_err(sock, 403, "FORBIDDEN", "该键码(返回/主页/确认)按物理键红线禁止映射"); const char *tmpdev = NULL; int dispatchable = (keycode_to_device(code, &tmpdev) >= 0); if (!dispatchable) return send_err(sock, 422, "UNSUPPORTED", "不支持的键码(仅音量/电源/播放/上下曲)"); if (inject_key_release(code, press_ms) < 0) return send_err(sock, 500, "INTERNAL", "按键注入失败(无法打开输入设备)"); sbuf_t s = {0}; sA(&s, "{\"code\":%d,\"press\":\"%s\",\"dispatched\":true}", code, press); int ret = send_json(sock, 200, s.b ? s.b : ""); sbuf_free(&s); return ret; } int api_key_batch(int sock, const req_t *r) { if (!r->body || !r->body[0]) return send_err(sock, 400, "BAD_REQUEST", "缺少 body"); jv_t *root = jv_parse(r->body, (int)r->body_len); if (!root) return send_err(sock, 400, "BAD_REQUEST", "body 非合法 JSON"); jv_t *keys = jv_obj_get(root, "keys"); if (!keys || keys->type != J_ARR) { jv_free(root); return send_err(sock, 400, "BAD_REQUEST", "缺 keys 数组"); } sbuf_t s = {0}; int first = 1; for (const jv_t *k = keys->child; k; k = k->next) { if (k->type != J_OBJ) continue; jv_t *c = jv_obj_get(k, "code"); jv_t *p = jv_obj_get(k, "press"); jv_t *m = jv_obj_get(k, "ms"); if (!c || c->type != J_NUM) continue; int code = (int)c->inum; if (code == 158 || code == 172) continue; /* 红线跳过 */ int press_ms = (m && m->type == J_NUM) ? (int)m->inum : 0; if (p && p->type == J_STR && !strcmp(p->str, "long") && press_ms <= 0) press_ms = 3000; const char *tmpdev = NULL; if (keycode_to_device(code, &tmpdev) < 0) continue; if (inject_key_release(code, press_ms) < 0) continue; if (!first) sA(&s, ","); first = 0; sA(&s, "%d", code); } jv_free(root); sbuf_t o = {0}; if (first) sA(&o, "{\"dispatched\":[]}"); else sA(&o, "{\"dispatched\":[%s]}", s.b); int ret = send_json(sock, 200, o.b ? o.b : ""); sbuf_free(&s); sbuf_free(&o); return ret; }