C ++从Linux上的Clipboard获取字符串

时间:2014-12-09 11:52:11

标签: c++ linux codeblocks clipboard x11

您好我正在编写一个c ++程序,我需要将剪贴板上的内容变为字符串变量。我找到了很多解决方案,但所有解决方案都是针对Windows编写的。有没有使用QT库的方法?我找到了与X11相关的东西,但不是很清楚。

非常感谢

2 个答案:

答案 0 :(得分:10)

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

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

理论

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

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

主要选择不需要按键,因此在彼此相邻的窗口之间复制小片段非常有用。这个功能大多是特定于unix的,但我看过putty,trillian和一些gtk应用程序在Windows操作系统上模拟它。此外,当中间单击页面的空非交互空间时,firefox具有“粘贴和放置”功能。

优化那些应用程序端缓冲区:不是每次更改时都将整个剪贴板/选择推送到服务器,应用程序只是告诉服务器“我拥有它”。要获取缓冲区,请让所有者向您提供其内容。这样,即使大型缓冲区在实际请求之前也不会占用任何资源。

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

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

所以要获得缓冲区:

  • 选择缓冲区名称(CLIPBOARDPRIMARY),格式 (UTF8_STRINGSTRING)和将结果存储到
  • 的窗口属性
  • 致电XConvertSelection()以请求缓冲区
  • 等待SelectionNotify活动
  • 从窗口属性中读取缓冲区内容

天真的实施

// 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千兆字节的文本日志。而X11允许这样!但是数据必须以递增方式传递,分成几个块。

如果请求的缓冲区太大,而不是将其存储到window属性中,则owner会设置格式为INCR的属性。如果删除它,则所有者假定您已阅读它,并将下一个块放在同一属性中。这一直持续到读取和删除最后一个块为止。最后,owner设置大小为0的属性以标记数据的结尾。

所以要读取大缓冲区,删除INCR属性并等待属性再次出现(PropertyNotify event,state == PropertyNewValue),读取并删除它,等待它再次出现,依此类推,直到它出现零大小。

// 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;
}

例如xsel工具使用INCR传输大于4000的缓冲区。根据ICCCM,应用程序可以选择合理的大小限制。

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

参考

答案 1 :(得分:2)

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

  

以下程序专门用于数据传输   机制:

     

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

     

xclipboardglipper(Gnome),parcellite(LXDE)和klipper(KDE)是   剪贴板管理员,也许wmcliphist以及xcb显示的内容   剪切缓冲区并允许用户操作它们xselection,

     

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

     

synergy是一个跨平台工具,可让您共享剪贴板   运行多个操作系统的多台计算机

     

xfce4-clipman-plugin是Xfce4的&#34;剪贴板历史插件   面板&#34;还有一个剪贴板管理器xtranslate查找中的单词   多语言词典中的Xselection autocutsel同步切割缓冲区   和选择缓冲区

很简单,从理论上讲,X11有2&#34;剪贴板&#34;:实际上是一个键盘和选择 - 你可以通过按下鼠标中键来实时选择你所选择的文字。键盘&#34;用于主/缺省剪贴板目的,由不同类型的对象交换。

P.S。根据我的经验,我不再使用x11了。享受:)