直接清除特定的X缓冲区,无需经过xsel或xclip

直接清除特定的X缓冲区,无需经过xsel或xclip

我正在尝试完全禁用中键单击以从缓冲区粘贴,使用拉瓦里格的解决方案

将其放入~/.xbindkeysrc

"echo -n | xsel -n -i; pkill xbindkeys; xdotool click 2; xbindkeys"
b:2 + Release

然而,该解决方案依赖于xsel(或者等效地xclip)快速完成其工作。

最近,我注意到尝试清除主缓冲区时xsel有几秒钟的延迟。xclip

xsel是否有比任何或正在做的事情更不“礼貌”的方式xclip来强制 X 清空特定缓冲区?

xsel有问题的 Linux 发行版是 Manjaro ...这可能是 Manjaro 或 Arch 特定的错误,但关于如何在没有或或其他类似工具的情况下与 X11 服务器交互的面向最终用户的信息xclip似乎有些缺乏。

~  > xclip -selection primary -verbose -in </dev/null
Connected to X server.
Using selection: XA_PRIMARY
Using UTF8_STRING.
Waiting for selection requests, Control-C to quit
  Waiting for selection request number 1
  Waiting for selection request number 2
Time: 13s

~  > xclip -selection primary -verbose -in </dev/null
...
Time: 11s

~  > xclip -selection primary -verbose -in </dev/null
...
Time: 23s

我附加gdb到了其中一个hang xclip,它似乎被卡住了,等待来自X 服务器的响应。

(gdb) where
#0  0x00007f905e1f1b78 in poll () from /usr/lib/libc.so.6
#1  0x00007f905dc68630 in ?? () from /usr/lib/libxcb.so.1
#2  0x00007f905dc6a2db in xcb_wait_for_event () from /usr/lib/libxcb.so.1
#3  0x00007f905e306009 in _XReadEvents () from /usr/lib/libX11.so.6
#4  0x00007f905e2f4ee1 in XNextEvent () from /usr/lib/libX11.so.6
#5  0x0000563eb8eaea70 in ?? ()
#6  0x00007f905e125223 in __libc_start_main () from /usr/lib/libc.so.6
#7  0x0000563eb8eaf53e in ?? ()

我尝试根据部分xsel源代码直接使用 X API 编写一个精简的程序,特别是:https://github.com/kfish/xsel/blob/master/xsel.c#L1003-L1018

为了清除缓冲区,xsel 似乎依赖于以下属性XSetSelectionOwner

如果新所有者(无论是客户端还是 None )与所选内容的当前所有者不同,并且当前所有者不是 None ,则向当前所有者发送 SelectionClear 事件。如果作为选择的所有者的客户端稍后被终止(即,其连接被关闭),或者如果它在请求中指定的所有者窗口随后被销毁,则选择的所有者会自动恢复为 None ,但最后一个- 更改时间不受影响。 X 服务器不解释选择原子。 XGetSelectionOwner() 返回所有者窗口,该窗口在 SelectionRequest 和 SelectionClear 事件中报告。选择对于 X 服务器来说是全局的。

这是我尝试精简xsel为我需要的功能。

我假设XA_PRIMARY缓冲区的所有者通常不是None。我将它设置到None我的 C 程序体内,然后希望它能起作用。

// clear.c
#include <stdio.h>
#include <X11/Xlib.h>
#include <X11/Xatom.h>
#include <assert.h>

// always debug
#undef NDEBUG

static Display * display = NULL;

static char * display_name = NULL;

static void clear_selection(void)
{
  printf("%d\n", 300);
  display = XOpenDisplay(display_name);
  assert(display != NULL);
  printf("%d\n", 200);
  XSetSelectionOwner(display, XA_PRIMARY, None, CurrentTime);
  printf("%d\n, 500);
  XSync(display, False);
  return;
}

int main(void)
{
  printf("%d\n", 100);
  clear_selection();
  printf("%d\n", 200);
  return 0;
}

该程序运行并打印

100
300
400
500
200

正如预期的那样。

但是,它未能清除主缓冲区。

xclip -selection -primary out前后显示相同的内容。

答案1

  XSetSelectionOwner(display, XA_PRIMARY, None, CurrentTime);

这是行不通的。正如第一行DESCRIPTION所说XSetSelectionOwner(3)

XSetSelectionOwner 函数更改指定选择的所有者和最后更改时间,并具有如果指定时间早于指定选择的当前上次更改时间,则无效或者晚于当前 X 服务器时间。

您必须向其传递一个真实的时间戳,您可以从XEvent服务器接收到的时间戳中获取该时间戳。这就是我在自己的实现中所做的xsel

Time getctime(void){
        XSelectInput(dpy, w, PropertyChangeMask);
        XStoreName(dpy, w, "xsel");
        for(;;){
                XEvent e;
                XNextEvent(dpy, &e);
                if(e.type == PropertyNotify && e.xproperty.window == w)
                        return e.xproperty.time;
        }
}

我在窗口上设置一个属性,等待事件PropertyNotify,然后从结构中获取时间戳XPropertyEvent。窗户可以是InputOnly一个。 xlib 编程手册或某些 X11 联机帮助页中也对此进行了描述。

不幸的是,这也意味着您的小程序也不会足够快,因为它必须等待该事件;-)

我不认为链接的答案问题都令人满意。您最好尝试使用一些LD_PRELOAD技巧,或者修改给您带来麻烦的程序。

相关内容