首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >C++在Linux上从剪贴板获取字符串

C++在Linux上从剪贴板获取字符串
EN

Stack Overflow用户
提问于 2014-12-09 11:52:11
回答 2查看 10.2K关注 0票数 10

你好,我正在写一个c++程序,我需要把剪贴板上的东西变成一个字符串变量。我找到了很多解决方案,但它们都是为windows编写的。有没有不使用QT库的方法?我发现了一些与X11相关的东西,但也不是很明确。

非常感谢

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2014-12-09 12:23:37

你有没有试着先找到一个程序,而不是一个代码?我为你做了这件事,发现了很多使用直接X11调用的实现。我认为最有价值的是this,但你也可以阅读this。只要找到任何程序并查找源代码即可。尝试在维基百科上查找哪些应用程序使用x11剪贴板/选择系统。

以下程序专门在数据传输机制上运行:

xcutsel将数据从选择传输到cut缓冲区,反之亦然

xclipboardglipper (Gnome)、parcellite (LXDE)和klipper (KDE)都是剪贴板管理器,也许wmcliphist也可以使用xcb显示剪切缓冲区的内容,并允许用户对其进行操作。

xclipxselxcopy是将数据复制到X选择或从X选择复制数据的命令行程序。xcopy有一个详细选项,可以帮助调试X选择问题。parcellite还具有从命令行读取和写入特定X选择的能力。

synergy是一个跨平台工具,允许您在运行多个操作系统的多台计算机之间共享剪贴板

xfce4-clipman-plugin是一个“Xfce4面板的剪贴板历史插件”,同时也是一个剪贴板管理器xtranslate在多语言词典中查找X选择中的单词自动剪切同步剪切缓冲区和选择缓冲区

简而言之,在理论上,X11有两个“剪贴板”:实际上是一个键盘,用于选择--你立即选择的文本可以通过按下鼠标中键粘贴到你想要的任何地方,而实际的“键盘”是为了主/默认剪贴板的目的而制作的,作为不同类型的对象交换。

另外,在我的经历之后,我不会再和x11一起工作了。享受:)

票数 2
EN

Stack Overflow用户

发布于 2017-07-09 04:20:39

X11使用灵活的多缓冲区、多格式、异步应用程序端剪贴板协议。

大多数工具包都实现了它(GTK的gtk_clipboard_get()、Qt的QApplication::clipboard()、Tk的clipboard_get)。但您可以使用X11 API手动完成此操作,例如,如果您不使用工具包,或者如果您必须通过剪贴板缓冲区传递大量数据,而不同时将其全部保存在内存中。

理论

可能有很多缓冲区,但您只需要知道两个:

  • CLIPBOARD是通常的显式缓冲区:您可以使用编辑/复制菜单复制内容,然后使用编辑/粘贴将其粘贴。menu.
  • PRIMARY选择是一种隐式鼠标选择功能:当使用鼠标光标选择文本时,文本将进入其中,在文本输入字段中单击鼠标中键时,文本将从中粘贴。

主选择不需要按键,因此它对于在相邻的窗口之间复制小片段很有用。这个特性主要是unix特有的,但我见过putty、trillian和一些gtk应用程序在Windows操作系统上模拟它。此外,当用鼠标中键点击页面的非交互空白区域时,firefox还具有“粘贴并转到”功能。

为了优化这些东西,这些都是application-side缓冲区:应用程序只需告诉服务器“我拥有它”,而不是在每次更改时将整个剪贴板/选择推送到服务器。要获取buffer,您需要所有者将其内容提供给您。这样,即使是一个大的缓冲区也不会占用任何资源,直到它真正被请求。

在请求缓冲区时,您可以向所有者询问所需的特定格式。例如,从seamonkey浏览器复制的图像(右键单击图像并按“复制图像”)可以用不同的格式表示。如果您将其粘贴到终端中,它将显示为图像URL。如果您将其粘贴到libreoffice writer中,它将成为从该URL加载的图片。如果粘贴到gimp中,它将是图像本身。这是因为seamonkey很聪明,它为每个应用程序提供了它所要求的格式:终端使用文本字符串,libreoffice使用html,gimp使用图像数据。要请求文本格式,您需要使用回退到STRINGUTF8_STRING格式。

当您请求另一个应用程序准备缓冲区时,这可能需要一些时间,请求是asynchronous:所有者准备缓冲区,将其保存在指定位置(窗口属性用作临时存储),并在完成时通过SelectionNotify事件通知您。

因此,要获取缓冲区:

  • 选择缓冲区名称(CLIPBOARDPRIMARY)、格式(UTF8_STRINGSTRING)和窗口属性以将结果存储到
  • 调用XConvertSelection()以请求缓冲区<代码>H227<代码>H128等待<代码>D29事件<代码>H230<代码>H131从窗口属性读取缓冲区内容<代码>H232<代码>F233

朴素的实现

代码语言:javascript
运行
AI代码解释
复制
// gcc -o xclipget xclipget.c -lX11
#include <stdio.h>
#include <limits.h>
#include <X11/Xlib.h>

Bool PrintSelection(Display *display, Window window, const char *bufname, const char *fmtname)
{
  char *result;
  unsigned long ressize, restail;
  int resbits;
  Atom bufid = XInternAtom(display, bufname, False),
       fmtid = XInternAtom(display, fmtname, False),
       propid = XInternAtom(display, "XSEL_DATA", False),
       incrid = XInternAtom(display, "INCR", False);
  XEvent event;

  XConvertSelection(display, bufid, fmtid, propid, window, CurrentTime);
  do {
    XNextEvent(display, &event);
  } while (event.type != SelectionNotify || event.xselection.selection != bufid);

  if (event.xselection.property)
  {
    XGetWindowProperty(display, window, propid, 0, LONG_MAX/4, False, AnyPropertyType,
      &fmtid, &resbits, &ressize, &restail, (unsigned char**)&result);

    if (fmtid == incrid)
      printf("Buffer is too large and INCR reading is not implemented yet.\n");
    else
      printf("%.*s", (int)ressize, result);

    XFree(result);
    return True;
  }
  else // request failed, e.g. owner can't convert to the target format
    return False;
}

int main()
{
  Display *display = XOpenDisplay(NULL);
  unsigned long color = BlackPixel(display, DefaultScreen(display));
  Window window = XCreateSimpleWindow(display, DefaultRootWindow(display), 0,0, 1,1, 0, color, color);
  Bool result = PrintSelection(display, window, "CLIPBOARD", "UTF8_STRING") ||
                PrintSelection(display, window, "CLIPBOARD", "STRING");
  XDestroyWindow(display, window);
  XCloseDisplay(display);
  return !result;
}

这将适用于许多简单的情况。这里缺少的一点是对大缓冲区的增量读取的支持。让我们添加它!

大缓冲区

一些应用程序可能想要复制/粘贴100 to的文本日志。而X11允许这样做!但数据必须以增量方式传递,并拆分成块。

如果请求的缓冲区太大,则owner会设置格式为INCR的属性,而不是将其存储到窗口属性中。如果你删除了它,owner会假设你已经读过了,并将下一个块放在同一个属性中。这一过程一直持续到最后一个块被读取和删除。最后,owner设置大小为0的属性来标记数据的结尾。

因此,要读取大缓冲区,请删除INCR属性并等待该属性再次出现(PropertyNotify事件,状态为== PropertyNewValue),读取并删除它,等待它再次出现,依此类推,直到它以零大小出现。

代码语言:javascript
运行
AI代码解释
复制
// gcc -o xclipget xclipget.c -lX11
#include <stdio.h>
#include <limits.h>
#include <X11/Xlib.h>

Bool PrintSelection(Display *display, Window window, const char *bufname, const char *fmtname)
{
  char *result;
  unsigned long ressize, restail;
  int resbits;
  Atom bufid = XInternAtom(display, bufname, False),
       fmtid = XInternAtom(display, fmtname, False),
       propid = XInternAtom(display, "XSEL_DATA", False),
       incrid = XInternAtom(display, "INCR", False);
  XEvent event;

  XSelectInput (display, window, PropertyChangeMask);
  XConvertSelection(display, bufid, fmtid, propid, window, CurrentTime);
  do {
    XNextEvent(display, &event);
  } while (event.type != SelectionNotify || event.xselection.selection != bufid);

  if (event.xselection.property)
  {
    XGetWindowProperty(display, window, propid, 0, LONG_MAX/4, True, AnyPropertyType,
      &fmtid, &resbits, &ressize, &restail, (unsigned char**)&result);
    if (fmtid != incrid)
      printf("%.*s", (int)ressize, result);
    XFree(result);

    if (fmtid == incrid)
      do {
        do {
          XNextEvent(display, &event);
        } while (event.type != PropertyNotify || event.xproperty.atom != propid || event.xproperty.state != PropertyNewValue);

        XGetWindowProperty(display, window, propid, 0, LONG_MAX/4, True, AnyPropertyType,
          &fmtid, &resbits, &ressize, &restail, (unsigned char**)&result);
        printf("%.*s", (int)ressize, result);
        XFree(result);
      } while (ressize > 0);

    return True;
  }
  else // request failed, e.g. owner can't convert to the target format
    return False;
}

int main()
{
  Display *display = XOpenDisplay(NULL);
  unsigned long color = BlackPixel(display, DefaultScreen(display));
  Window window = XCreateSimpleWindow(display, DefaultRootWindow(display), 0,0, 1,1, 0, color, color);
  Bool result = PrintSelection(display, window, "CLIPBOARD", "UTF8_STRING") ||
                PrintSelection(display, window, "CLIPBOARD", "STRING");
  XDestroyWindow(display, window);
  XCloseDisplay(display);
  return !result;
}

例如,对于大于4000的缓冲区,xsel工具使用INCR传输。根据ICCCM的说法,这取决于应用程序选择一个合理的大小限制。

同样的代码也适用于PRIMARY选择。将"CLIPBOARD“替换为"PRIMARY”以打印PRIMARY选择内容。

参考文献

-XPaste() Lindsey

票数 34
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/27378318

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档