From 535c1c89e5403903bdc4cbc13427f5d118535eed Mon Sep 17 00:00:00 2001 From: rcombs Date: Mon, 8 Apr 2024 00:55:59 -0700 Subject: [PATCH] player/command: add clipboard property --- DOCS/man/input.rst | 16 +++++ player/command.c | 148 ++++++++++++++++++++++++++++++++++++++++++++- player/playloop.c | 10 +++ 3 files changed, 173 insertions(+), 1 deletion(-) diff --git a/DOCS/man/input.rst b/DOCS/man/input.rst index 1decc70f6b2b6..64bc195aeba0b 100644 --- a/DOCS/man/input.rst +++ b/DOCS/man/input.rst @@ -3447,6 +3447,22 @@ Property list ``current-ao`` Current audio output driver (name as used with ``--ao``). +``clipboard`` (RW) + Access to the system clipboard as a key/value map of types and data. + + Sub-paths can be accessed directly; e.g. ``clipboard/text`` can be read, written, + or observed. + + The top-level object itself can be written directly as a shortcut; the intended + type will be guessed. + + Converting this property to a string will give a JSON representation of all types. + Unset types will be null. + + Currently, the ``text``, ``url``, and ``path`` types are available. + + Writing to one type may cause other types to also be updated. + ``user-data`` (RW) This is a recursive key/value map of arbitrary nodes shared between clients for general use (i.e. scripts, IPC clients, host applications, etc). diff --git a/player/command.c b/player/command.c index 528b87d844c50..117412e2524dc 100644 --- a/player/command.c +++ b/player/command.c @@ -132,6 +132,10 @@ static const struct m_option mdata_type = { .type = CONF_TYPE_NODE }; +static const struct m_option clipboard_type = { + .type = CONF_TYPE_NODE +}; + struct overlay { struct mp_image *source; int x, y; @@ -3933,6 +3937,146 @@ static int mp_property_udata(void *ctx, struct m_property *prop, return ret; } +static int do_get_clipboard(struct MPContext *mpctx, mpv_node *out, enum m_clipboard_type type) +{ + struct m_clipboard_item item = { + .type = type, + }; + + switch (type) { + case CLIPBOARD_UNKNOWN: + return M_PROPERTY_UNKNOWN; + case CLIPBOARD_TEXT: + case CLIPBOARD_URL: + case CLIPBOARD_PATH: + break; + default: + return M_PROPERTY_NOT_IMPLEMENTED; + } + + int ret = m_clipboard_get(mpctx, &item); + if (ret < 0) + return M_PROPERTY_ERROR; + else if (ret == CLIPBOARD_NONE) + return M_PROPERTY_UNAVAILABLE; + + m_option_free(&clipboard_type, out); + + switch (type) { + case CLIPBOARD_TEXT: + case CLIPBOARD_URL: + case CLIPBOARD_PATH: + out->format = MPV_FORMAT_STRING; + out->u.string = item.string; + return M_PROPERTY_OK; + default: + m_clipboard_item_free(&item); + return M_PROPERTY_NOT_IMPLEMENTED; + } +} + +static int do_set_clipboard(struct MPContext *mpctx, mpv_node *node, enum m_clipboard_type type) +{ + struct m_clipboard_item item = {}; + + switch (type) { + case CLIPBOARD_UNKNOWN: + break; + case CLIPBOARD_TEXT: + case CLIPBOARD_URL: + case CLIPBOARD_PATH: + if (node->format != MPV_FORMAT_STRING) + return M_PROPERTY_INVALID_FORMAT; + break; + default: + return M_PROPERTY_NOT_IMPLEMENTED; + } + + switch (node->format) { + case MPV_FORMAT_STRING: + item.type = (type == CLIPBOARD_UNKNOWN) ? CLIPBOARD_TEXT : type; + item.string = node->u.string; + break; + default: + return M_PROPERTY_NOT_IMPLEMENTED; + } + + return (m_clipboard_set(mpctx, &item) == CLIPBOARD_OK) ? M_PROPERTY_OK : M_PROPERTY_ERROR; +} + +static void do_clipboard_node(struct MPContext *mpctx, mpv_node *parent, + enum m_clipboard_type type, const char *key) +{ + struct mpv_node *node = node_map_add(parent, key, MPV_FORMAT_NONE); + do_get_clipboard(mpctx, node, type); +} + +static int mp_property_clipboard(void *ctx, struct m_property *prop, + int action, void *arg) +{ + struct MPContext *mpctx = ctx; + + switch (action) { + case M_PROPERTY_GET_TYPE: + *(struct m_option *)arg = clipboard_type; + return M_PROPERTY_OK; + case M_PROPERTY_GET: + case M_PROPERTY_GET_NODE: { + mpv_node *node = arg; + m_option_free(&clipboard_type, node); + node_init(node, MPV_FORMAT_NODE_MAP, NULL); + do_clipboard_node(mpctx, node, CLIPBOARD_TEXT, "text"); + do_clipboard_node(mpctx, node, CLIPBOARD_URL, "url"); + do_clipboard_node(mpctx, node, CLIPBOARD_PATH, "path"); + return M_PROPERTY_OK; + } + case M_PROPERTY_SET: + case M_PROPERTY_SET_NODE: + return do_set_clipboard(mpctx, arg, CLIPBOARD_UNKNOWN); + case M_PROPERTY_KEY_ACTION: { + struct m_property_action_arg *act = arg; + const char *key = act->key; + + enum m_clipboard_type type = CLIPBOARD_UNKNOWN; + if (!strcmp(key, "text")) { + type = CLIPBOARD_TEXT; + } else if (!strcmp(key, "url")) { + type = CLIPBOARD_URL; + } else if (!strcmp(key, "path")) { + type = CLIPBOARD_PATH; + } + + if (type == CLIPBOARD_UNKNOWN) + return M_PROPERTY_UNKNOWN; + + switch (act->action) { + case M_PROPERTY_GET_TYPE: + switch (type) { + case CLIPBOARD_TEXT: + case CLIPBOARD_URL: + case CLIPBOARD_PATH: + *(struct m_option *)act->arg = (struct m_option){ + .type = CONF_TYPE_STRING, + }; + return M_PROPERTY_OK; + default: + return M_PROPERTY_UNKNOWN; + } + case M_PROPERTY_GET: + case M_PROPERTY_GET_NODE: + return do_get_clipboard(mpctx, act->arg, type); + case M_PROPERTY_SET: + case M_PROPERTY_SET_NODE: + return do_set_clipboard(mpctx, act->arg, type); + default: + return M_PROPERTY_NOT_IMPLEMENTED; + } + } + default: + return M_PROPERTY_NOT_IMPLEMENTED; + } +} + // Redirect a property name to another #define M_PROPERTY_ALIAS(name, real_property) \ {(name), mp_property_alias, .priv = (real_property)} @@ -4149,6 +4293,8 @@ static const struct m_property mp_properties_base[] = { {"user-data", mp_property_udata}, {"term-size", mp_property_term_size}, + {"clipboard", mp_property_clipboard}, + M_PROPERTY_ALIAS("video", "vid"), M_PROPERTY_ALIAS("audio", "aid"), M_PROPERTY_ALIAS("sub", "sid"), @@ -6609,7 +6755,7 @@ static void cmd_context_menu(void *p) struct mp_cmd_ctx *cmd = p; struct MPContext *mpctx = cmd->mpctx; struct vo *vo = mpctx->video_out; - + if (vo) vo_control(vo, VOCTRL_SHOW_MENU, NULL); } diff --git a/player/playloop.c b/player/playloop.c index bf903e54f75c7..9407d5cb74efb 100644 --- a/player/playloop.c +++ b/player/playloop.c @@ -41,6 +41,7 @@ #include "options/m_config_frontend.h" #include "options/m_property.h" #include "options/options.h" +#include "osdep/clipboard.h" #include "osdep/terminal.h" #include "osdep/timer.h" #include "stream/stream.h" @@ -1066,6 +1067,13 @@ int handle_force_window(struct MPContext *mpctx, bool force) return -1; } +static void handle_clipboard_changes(struct MPContext *mpctx) +{ + bool changed = m_clipboard_poll(mpctx); + if (changed) + mp_notify_property(mpctx, "clipboard"); +} + // Potentially needed by some Lua scripts, which assume TICK always comes. static void handle_dummy_ticks(struct MPContext *mpctx) { @@ -1232,6 +1240,8 @@ void run_playloop(struct MPContext *mpctx) handle_dummy_ticks(mpctx); + handle_clipboard_changes(mpctx); + update_osd_msg(mpctx); if (mpctx->video_status == STATUS_EOF) update_subtitles(mpctx, mpctx->playback_pts);