Skip to content

Commit

Permalink
player/command: add clipboard property
Browse files Browse the repository at this point in the history
  • Loading branch information
rcombs committed Apr 8, 2024
1 parent 6629045 commit 535c1c8
Show file tree
Hide file tree
Showing 3 changed files with 173 additions and 1 deletion.
16 changes: 16 additions & 0 deletions DOCS/man/input.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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).
Expand Down
148 changes: 147 additions & 1 deletion player/command.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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)}
Expand Down Expand Up @@ -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"),
Expand Down Expand Up @@ -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);
}
Expand Down
10 changes: 10 additions & 0 deletions player/playloop.c
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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)
{
Expand Down Expand Up @@ -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);
Expand Down

0 comments on commit 535c1c8

Please sign in to comment.