gtk实现spice剪切板

news2024/12/21 23:27:45

 

重要的函数:
void spice_main_channel_clipboard_selection_grab(SpiceMainChannel *channel, guint selection,
guint32 *types, int ntypes);  抓取剪切板
void spice_main_channel_clipboard_selection_release(SpiceMainChannel *channel, guint selection);  剪切板释放
void spice_main_channel_clipboard_selection_notify(SpiceMainChannel *channel, guint selection,guint32 type, const guchar *data, size_t size);   通知剪切板内容
void spice_main_channel_clipboard_selection_request(SpiceMainChannel *channel, guint selection,guint32 type); 请求剪切板
1、host--> guest复制粘贴
1.1、宿主机复制:监听剪切板变化,获取剪切板内容
1.1.1、连接剪切板内容变化的回调函数
void spice_gtk_session_copy_to_guest(SpiceGtkSession *self)
{
    g_return_if_fail(SPICE_IS_GTK_SESSION(self));
    g_return_if_fail(read_only(self) == FALSE);
    SpiceGtkSessionPrivate *s = self->priv;
    int selection = VD_AGENT_CLIPBOARD_SELECTION_CLIPBOARD;
    if (s->clip_hasdata[selection] && !s->clip_grabbed[selection]) {
        gtk_clipboard_request_targets(s->clipboard, clipboard_get_targets,get_weak_ref(self)); //剪贴板收到支持类型的内容之后时,callback将被调用。
    }
}
1.1.2、剪切板内容处理
static void clipboard_get_targets(GtkClipboard *clipboard,GdkAtom *atoms,gint n_atoms, gpointer user_data)
{
    SpiceGtkSession *self = free_weak_ref(user_data);
    SPICE_DEBUG("%s:", __FUNCTION__);
    if (self == NULL)
        return;

    g_return_if_fail(SPICE_IS_GTK_SESSION(self));
    if (atoms == NULL) {
        SPICE_DEBUG("Retrieving the clipboard data has failed");
        return;
    }

    SpiceGtkSessionPrivate *s = self->priv;
    guint32 types[SPICE_N_ELEMENTS(atom2agent)] = { 0 };
    gint num_types;
    int a;
    int selection;
    if (s->main == NULL)
        return;

    selection = get_selection_from_clipboard(s, clipboard);
    g_return_if_fail(selection != -1);

    /* GTK+ does seem to cache atoms, but not for Wayland */
    g_free(s->atoms[selection]);
    s->atoms[selection] = g_memdup(atoms, n_atoms * sizeof(GdkAtom));
    s->n_atoms[selection] = n_atoms;
    if (s->clip_grabbed[selection]) {
        SPICE_DEBUG("Clipboard is already grabbed, re-grab: %d atoms", n_atoms);
    }

    /* Set all Atoms that matches our current protocol implementation */
    num_types = 0;
    for (a = 0; a < n_atoms; a++) {
        guint m;
        gchar *name = gdk_atom_name(atoms[a]);
        SPICE_DEBUG(" \"%s\"", name);
        for (m = 0; m < SPICE_N_ELEMENTS(atom2agent); m++) {
            guint t;
            if (strcasecmp(name, atom2agent[m].xatom) != 0) {
                continue;
            }

            if (atom2agent[m].vdagent == VD_AGENT_CLIPBOARD_FILE_LIST) {
#ifdef HAVE_PHODAV_VIRTUAL
                if (!clipboard_get_open_webdav(s->session)) {
                    SPICE_DEBUG("Received %s target, but the clipboard webdav channel isn't available, skipping", atom2agent[m].xatom);
                    break;
                }
#else
                break;
#endif
            }

            /* check if type is already in list */
            for (t = 0; t < num_types; t++) {
                if (types[t] == atom2agent[m].vdagent) {
                    break;
                }
            }
            if (t == num_types) {
                /* add type to empty slot */
                types[t] = atom2agent[m].vdagent;
                num_types++;
            }
        }
        g_free(name);
    }

    if (num_types == 0) {
        SPICE_DEBUG("No GdkAtoms will be sent from %d", n_atoms);
        return;
    }

    s->clip_grabbed[selection] = TRUE;
    if (spice_main_channel_agent_test_capability(s->main, VD_AGENT_CAP_CLIPBOARD_BY_DEMAND))
        spice_main_channel_clipboard_selection_grab(s->main, selection, types, num_types);//
    /* Sending a grab causes the agent to do an implicit release */
    s->nclip_targets[selection] = 0;
}
1.2、guest粘贴:发送request请求获取内容
1.2.1、连接回调函数
static void channel_new(SpiceSession *session, SpiceChannel *channel,
                        gpointer user_data)
{
    g_return_if_fail(SPICE_IS_GTK_SESSION(user_data));

    SpiceGtkSession *self = user_data;
    SpiceGtkSessionPrivate *s = self->priv;

    if (SPICE_IS_MAIN_CHANNEL(channel)) {
        SPICE_DEBUG("Changing main channel from %p to %p", s->main, channel);
        s->main = SPICE_MAIN_CHANNEL(channel);
        g_signal_connect(channel, "main-clipboard-selection-grab",
                         G_CALLBACK(clipboard_grab), self);
        g_signal_connect(channel, "main-clipboard-selection-request",
                         G_CALLBACK(clipboard_request), self);
        g_signal_connect(channel, "main-clipboard-selection-release",
                         G_CALLBACK(clipboard_release_delay), self);
    }
    if (SPICE_IS_INPUTS_CHANNEL(channel)) {
        spice_g_signal_connect_object(channel, "inputs-modifiers",G_CALLBACK(guest_modifiers_changed), self, 0);
        spice_gtk_session_sync_keyboard_modifiers_for_channel(self, SPICE_INPUTS_CHANNEL(channel), TRUE);
    }
}
1.2.2、收到服务器的request请求剪切板内容
static gboolean clipboard_request(SpiceMainChannel *main, guint selection,
                                  guint type, gpointer user_data)
{
    g_return_val_if_fail(SPICE_IS_GTK_SESSION(user_data), FALSE);
    SpiceGtkSession *self = user_data;
    SpiceGtkSessionPrivate *s = self->priv;
    GdkAtom atom;
    GtkClipboard* cb;
    int m;
    cb = get_clipboard_from_selection(s, selection);
    g_return_val_if_fail(cb != NULL, FALSE);
    g_return_val_if_fail(s->clipboard_by_guest[selection] == FALSE, FALSE);
    g_return_val_if_fail(s->clip_grabbed[selection], FALSE);

    if (read_only(self))
        return FALSE;

    if (type == VD_AGENT_CLIPBOARD_UTF8_TEXT) {
        gtk_clipboard_request_text(cb, clipboard_received_text_cb,get_weak_ref(self)); //发送文本
    } else if (type == VD_AGENT_CLIPBOARD_FILE_LIST) {
#ifdef HAVE_PHODAV_VIRTUAL
        atom = clipboard_select_uris_atom(s, selection);
        if (atom == GDK_NONE) {
            return FALSE;
        }
        gtk_clipboard_request_contents(cb, atom, clipboard_received_uri_contents_cb, get_weak_ref(self));//发视其他内容
#else
        return FALSE;
#endif
    } else {
        for (m = 0; m < SPICE_N_ELEMENTS(atom2agent); m++) {
            if (atom2agent[m].vdagent == type)
                break;
        }
        g_return_val_if_fail(m < SPICE_N_ELEMENTS(atom2agent), FALSE);
        atom = gdk_atom_intern_static_string(atom2agent[m].xatom);
        gtk_clipboard_request_contents(cb, atom, clipboard_received_cb,get_weak_ref(self));
    }
    return TRUE;
}
static gboolean clipboard_grab(SpiceMainChannel *main, guint selection,
                               guint32* types, guint32 ntypes,
                               gpointer user_data)
{
    g_return_val_if_fail(SPICE_IS_GTK_SESSION(user_data), FALSE);
    SpiceGtkSession *self = user_data;
    SpiceGtkSessionPrivate *s = self->priv;
    GtkTargetEntry targets[SPICE_N_ELEMENTS(atom2agent)];
    gboolean target_selected[SPICE_N_ELEMENTS(atom2agent)] = { FALSE, };
    gboolean found;
    GtkClipboard* cb;
    int m, n;
    int num_targets = 0;
    clipboard_release_delay_remove(self, selection, false);

    cb = get_clipboard_from_selection(s, selection);
    g_return_val_if_fail(cb != NULL, FALSE);

    for (n = 0; n < ntypes; ++n) {
        found = FALSE;
        for (m = 0; m < SPICE_N_ELEMENTS(atom2agent); m++) {
            if (atom2agent[m].vdagent == types[n] && !target_selected[m]) {
                found = TRUE;
                g_return_val_if_fail(num_targets < SPICE_N_ELEMENTS(atom2agent), FALSE);
                targets[num_targets].target = (gchar*)atom2agent[m].xatom;
                targets[num_targets].info = m;
                target_selected[m] = TRUE;
                num_targets++;
            }
        }
        if (!found) {
            g_warning("clipboard: couldn't find a matching type for: %u",
                      types[n]);
        }
    }

    g_free(s->clip_targets[selection]);
    s->nclip_targets[selection] = num_targets;
    s->clip_targets[selection] = g_memdup(targets, sizeof(GtkTargetEntry) * num_targets);
    /* Receiving a grab implies we've released our own grab */
    s->clip_grabbed[selection] = FALSE;
    if (read_only(self) ||
        !s->auto_clipboard_enable ||
        s->nclip_targets[selection] == 0) {
        return TRUE;
    }
    if (!gtk_clipboard_set_with_owner(cb,
                                      targets,
                                      num_targets,
                                      clipboard_get,
                                      clipboard_clear,
                                      G_OBJECT(self))) {
        g_warning("clipboard grab failed");
        return FALSE;
    }
    s->clipboard_by_guest[selection] = TRUE;
    s->clip_hasdata[selection] = FALSE;

    return TRUE;
}
2、guest --> host复制粘贴
2.1、剪切板粘贴时调用函数 clipboard_get
void spice_gtk_session_paste_from_guest(SpiceGtkSession *self)
{
    g_return_if_fail(SPICE_IS_GTK_SESSION(self));
    g_return_if_fail(read_only(self) == FALSE);
    SpiceGtkSessionPrivate *s = self->priv;
    int selection = VD_AGENT_CLIPBOARD_SELECTION_CLIPBOARD;

    if (s->nclip_targets[selection] == 0) {
        g_warning("Guest clipboard is not available.");
        return;
    }

    if (!gtk_clipboard_set_with_owner(s->clipboard, s->clip_targets[selection], s->nclip_targets[selection], clipboard_get, clipboard_clear, G_OBJECT(self))) {
        g_warning("Clipboard grab failed");
        return;
    }
    s->clipboard_by_guest[selection] = TRUE;
    s->clip_hasdata[selection] = FALSE;
}
2.2、连接主通道信号 main-clipboard-selection处理粘贴内容
static void clipboard_get(GtkClipboard *clipboard, GtkSelectionData *selection_data, guint info, gpointer user_data)
{
    g_return_if_fail(SPICE_IS_GTK_SESSION(user_data));
    RunInfo ri = { NULL, };
    SpiceGtkSession *self = user_data;
    SpiceGtkSessionPrivate *s = self->priv;
    gboolean agent_connected = FALSE;
    gulong clipboard_handler;
    gulong agent_handler;
    int selection;
    SPICE_DEBUG("clipboard get");

    selection = get_selection_from_clipboard(s, clipboard);
    g_return_if_fail(selection != -1);
    g_return_if_fail(info < SPICE_N_ELEMENTS(atom2agent));
    g_return_if_fail(s->main != NULL);

    if (s->clipboard_release_delay[selection]) {
        SPICE_DEBUG("not requesting data from guest during delayed release");
        return;
    }

    ri.selection_data = selection_data;
    ri.info = info;
    ri.loop = g_main_loop_new(NULL, FALSE);
    ri.selection = selection;
    ri.self = self;
    clipboard_handler = g_signal_connect(s->main, "main-clipboard-selection",G_CALLBACK(clipboard_got_from_guest),&ri);
    agent_handler = g_signal_connect_swapped(s->main, "notify::agent-connected",G_CALLBACK(clipboard_agent_connected),&ri);
    spice_main_channel_clipboard_selection_request(s->main, selection,atom2agent[info].vdagent);

    g_object_get(s->main, "agent-connected", &agent_connected, NULL);
    if (!agent_connected) {
        SPICE_DEBUG("canceled clipboard_get, before running loop");
        goto cleanup;
    }

    /* This is modeled on the implementation of gtk_dialog_run() even though
     * these thread functions are deprecated and appears to be needed to avoid
     * dead-lock from gtk_dialog_run().
     */
    G_GNUC_BEGIN_IGNORE_DEPRECATIONS
    gdk_threads_leave();
    g_main_loop_run(ri.loop);
    gdk_threads_enter();
    G_GNUC_END_IGNORE_DEPRECATIONS

cleanup:
    g_clear_pointer(&ri.loop, g_main_loop_unref);
    g_signal_handler_disconnect(s->main, clipboard_handler);
    g_signal_handler_disconnect(s->main, agent_handler);
}
2.3、将内容设置到剪切板上面
static void clipboard_got_from_guest(SpiceMainChannel *main, guint selection,
                                     guint type, const guchar *data, guint size,
                                     gpointer user_data)
{
    RunInfo *ri = user_data;
    SpiceGtkSessionPrivate *s = ri->self->priv;
    gchar *conv = NULL;

    g_return_if_fail(selection == ri->selection);
    SPICE_DEBUG("clipboard got data");

    if (atom2agent[ri->info].vdagent == VD_AGENT_CLIPBOARD_UTF8_TEXT) {
        /* on windows, gtk+ would already convert to LF endings, but
           not on unix */
        if (spice_main_channel_agent_test_capability(s->main, VD_AGENT_CAP_GUEST_LINEEND_CRLF)) {
            conv = spice_dos2unix((gchar*)data, size);
            size = strlen(conv);
        }
        gtk_selection_data_set_text(ri->selection_data, conv ?: (gchar*)data, size);
    } else {
        gtk_selection_data_set(ri->selection_data,
            gdk_atom_intern_static_string(atom2agent[ri->info].xatom),
            8, data, size);
    }

    if (g_main_loop_is_running (ri->loop))
        g_main_loop_quit (ri->loop);

    g_free(conv);
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/645065.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

软件项目管理需要具备哪些能力?

作为一名软件项目管理者&#xff0c;在处理许多事情时需要不断提高个人在数据分析处理、项目业务流程管理等各个领域的能力。当然作为过来人&#xff0c;我也很清楚很多软件项目管理新人也较为疑惑如何提高自己的能力和专业水平&#xff0c;以便提高工作效率。那我也想与大家唠…

【论文阅读笔记】Local Model Poisoning Attacks to Byzantine-Robust Federated Learning

个人阅读笔记&#xff0c;如有错误欢迎指出&#xff01; 会议&#xff1a; Usenix 2020 [1911.11815] Local Model Poisoning Attacks to Byzantine-Robust Federated Learning (arxiv.org) 问题&#xff1a; 模型攻击对拜占庭鲁棒性联邦学习的攻击效果尚未清楚 创新点&…

SpringBatch从入门到实战(六):ItemReader

一&#xff1a;ListItemReader 用于简单的开发测试。 Bean public ItemReader<String> listItemReader() {return new ListItemReader<>(Arrays.asList("a", "b", "c")); }二&#xff1a;FlatFileItemReader 1.1 完全映射 当文件…

万物云原生下的服务进化 | 京东云技术团队

导读&#xff1a; 在万物云原生下的环境下&#xff0c;Java的市场份额也因耗资源、启动慢等缺点&#xff0c;导致在云原生环境里被放大而降低&#xff0c;通过这篇文章&#xff0c;读者可以更好地了解如何在云原生环境下通过升级相关版本和使用GraalVM打出原生镜像到方式&…

Linux之特殊权限

目录 Linux之特殊权限 SUID 定义 案例 原因 查找真个系统的SUID/SGID文件 SGID 定义&#xff1a; Sticky Bit 案例 设置文件和目录的特殊权限 方法一 使用 chmod命令 方法二 使用数字形式的权限模式 设置新建文件或目录的默认权限 设置修改文件的扩展性 设置文件…

MySQL连接查询——外连接

内连接查询顺序 首先看student和exam表的内容&#xff1a; 然后执行如下内连接查询&#xff1a; explain select a.*,b.* from student a inner join exam b on a.uidb.uid;查询计划如下 由于a表记录数量少为小表做全表扫描&#xff08;rows为6&#xff09;&#xff0c;然后到…

微信小程序标签知识点总结

View 标签 <scroll-view class"scroll_list" scroll-x"true"> 标签 设置 scroll-x/y是可以设置 滚动模式到底是x方向还是Y方向 &#xff08; 需要调整样式&#xff0c;请参考如下 .scroll_list{ border: 1px solid red; width: 240px; white-sp…

算法学习day21

文章目录 530.二叉搜索树的最小绝对差递归 501.二叉搜索树中的众数递归 236.二叉树的最近公共祖先递归 总结 530.二叉搜索树的最小绝对差 给你一个二叉搜索树的根节点 root &#xff0c;返回 树中任意两不同节点值之间的最小差值 。 差值是一个正数&#xff0c;其数值等于两值…

管理类联考——逻辑——知识篇——题型说明

管理类联考基础逻辑—逻辑规划 一、联考中逻辑部分的重要性 管理类综合能力测试的数学、逻辑、写作三个部分中&#xff0c;逻辑是毫无疑问最重要的一部分&#xff0c;体现在以下三个方面&#xff1a; 1、时间分配&#xff1a;逻辑部分的阅读量相当大&#xff0c;30道题的阅读…

2023史上最全java面试题题库大全800题含答案

如果你不停地加班。却很少冒险&#xff0c;也很少学习&#xff0c;那你极大可能会陷入到内卷中。 为什么这么说呢&#xff1f;我们先来捋清楚「内卷」的概念&#xff1a; 「内卷化」简而言之就是&#xff1a;日复一日&#xff0c;越混越掉坑里。 所谓内卷化&#xff0c;指一种社…

HTTP 网络通讯过程

1.知识点&#xff1a; 在计算机网络中&#xff0c;通信协议用于规范数据传输的格式和传送方式。下面是常见的网络通信协议&#xff1a; HTTP协议&#xff1a;超文本传输协议&#xff0c;用于在Web浏览器和Web服务器之间传输HTML文件和其他资源。 HTTPS协议&#xff1a;HTTP安…

《水经注地图服务》如何快速发布经纬度DAT缓存

概述 《水经注地图服务》的快速发布功能是一个能够帮助用户实现快速发布地图服务的功能&#xff0c;并且提供常规情况下大多数用户所需的默认配置&#xff0c;让用户在发布地图时更加便捷。 这里为大家演示如何快速发布经纬度DAT缓存以及如何在水经微图中加载。 准备工作 离…

2023-6-14-第五式原型模式

&#x1f37f;*★,*:.☆(&#xffe3;▽&#xffe3;)/$:*.★* &#x1f37f; &#x1f4a5;&#x1f4a5;&#x1f4a5;欢迎来到&#x1f91e;汤姆&#x1f91e;的csdn博文&#x1f4a5;&#x1f4a5;&#x1f4a5; &#x1f49f;&#x1f49f;喜欢的朋友可以关注一下&#xf…

IDEA中类模板注释和方法注释模板

类注释 /* *ClassName ${NAME} *Author --你的别名 *Description //TODO *Date ${DATE} ${TIME} *Version 1.0 **/ 把上面的代码粘贴到settings-editor-file and code templates下的class的“public class”和#parese的中间 2.方法注释 /* *Author --你的别名 *Description …

ZK+麦克风:反AI音频认证

1. 引言 当前&#xff0c;已越来越难以区分AI生成的音频与人类的声音。可能带来欺诈、身份盗用以及其它滥用问题。 在AI生成的音频可以完美模仿人声的当前环境中&#xff0c;需要一个可靠的信任链——从最初的音频捕获到最终的播放。这种信任链可以使用加密技术建立&#xff…

Java实训日志04

文章目录 八、项目开发实现步骤&#xff08;六&#xff09;创建数据访问接口1、创建学校数据访问接口2、创建状态数据访问接口3、创建学生数据访问接口4、创建用户数据访问接口 八、项目开发实现步骤 &#xff08;六&#xff09;创建数据访问接口 DAO: Data Acess Object - 数…

antd-vue - - - - - upload组件的使用

upload组件的使用 参数说明&#xff1a; file:list : 上传列表数据 name: 上传时的key data: 上传时额外的参数 header: 上传列表数据 actions: 上传接口地址 before-upload: 上传之前的回掉 change: 传文件改变时 // 变量定义updateData: { billId: "", fileType: &…

Flutter 自定义Grade组件

/*** images 图片数组* titles title数组* length_w 一行的数量* length_h 行数*/static Widget getMenuGrade(List<String> images, List<String> titles, int length_w, int length_h) {int startIndex 0;List<Widget> rowList [];List<List<Widge…

docker-compose服务名称和容器名称区别

需求及结论 在docker-compose文件一般书写如下&#xff1a; version: 3.8 x-logging: &default-loggingoptions:max-size: "200m"max-file: "5"driver: json-file x-environment: &default-environmentTZ: Asia/ShanghaiLANG: C.UTF-8 services:…

前端什么最难学?

前言 个人认为是JS&#xff0c;无论是在平时的项目或者找工作时候JS都是大头&#xff0c;相比起其他的部分&#xff0c;它相对而言是难一点&#xff0c;同时也是十分重要的一部分&#xff0c;学好原生JS&#xff0c;后续的学习才能基于此循序渐进&#xff0c;下面是我总结的关…