This repository has been archived on 2023-08-29. You can view files and clone it, but cannot push or open issues/pull-requests.
1
0
Fork 0
master
宇天 2020-01-15 20:24:05 +08:00
commit 28db16e977
4 changed files with 201 additions and 0 deletions

3
.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
*.o
*.swp
xmousepasteblock

42
Makefile Normal file
View File

@ -0,0 +1,42 @@
TARGET = xmousepasteblock
INSTALL = install
PREFIX = /usr
BINDIR = $(PREFIX)/bin
CC = gcc
CFLAGS += -std=gnu99
CFLAGS += -Wall -Wundef -Wshadow -Wformat-security
LD = $(CC)
LDFLAGS += $(shell pkg-config --libs x11 xi)
LDFLAGS += -lev
.NOTPARALLEL:
.PHONY: all
all: clean $(TARGET)
.PHONY: debug
debug: clean
debug: CFLAGS += -g -DDEBUG
debug: $(TARGET)
.PHONY: $(TARGET)
$(TARGET): $(TARGET).o
$(LD) "$<" $(LDFLAGS) -o "$(TARGET)"
$(TARGET).o: $(TARGET).c
$(CC) $(CFLAGS) -o "$@" -c "$<"
.PHONY: install
install: $(TARGET)
$(INSTALL) -Dm 0755 "$(TARGET)" "$(DESTDIR)$(BINDIR)/$(TARGET)"
.PHONY: uninstall
uninstall:
$(RM) "$(DESTDIR)$(BINDIR)/$(TARGET)"
.PHONY: clean
clean:
$(RM) $(TARGET) $(TARGET).o

27
README.md Normal file
View File

@ -0,0 +1,27 @@
# XMousePasteBlock
Listens for middle mouse click events and clears the primary X selection/clipboard on detection to avoid accidentially pasting stuff all over the place.
## About
No need to disable your precious middle mouse button bindings, no clearing of visual selections nor performance losses because of emptying the primary X clipboard periodically.
With the utilization of XInput and Xlibs this has _no_ measurable impact on performance whatsoever.
No elevated privileges required. Just run within your regular users' X session.
## Building
````
make
sudo make install
````
## Running
Just add `xmousepasteblock` to your startup script/config.
## Known issues
In case of devices which are configured with middle mouse button hold-to-scroll (e.g. Trackpoints), it may happen that the primary selection clear action gets fired too late on older and slower machines.
You can observe the behavior by building with the DEBUG flag set (`make debug`), running `xmousepasteblock` in a shell and watching the debug output as you long press and hold the mouse buttons.
This is due to the fact that the XI_RawButtonPress event only gets fired _after_ releasing the middle mouse button (in case the user wanted to execute a scroll action).
The only option to work around this is to disable the middle mouse button hold-to-scroll functionality on Trackpoint devices (which is often not desirable).
To do so (using _libinput_):
`xinput set-prop <device id> 'libinput Button Scrolling Button' 0`

129
xmousepasteblock.c Normal file
View File

@ -0,0 +1,129 @@
/*
* Copyright (c) 2018 Micha LaQua <micha.laqua@gmail.com>
*
* Special thanks to Ingo Buerk (Airblader) for his work on the
* awesome unclutter-xfixes project, upon which the XInput eventcode
* is based on.
*
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 2.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <ev.h>
#include <X11/Xlib.h>
#include <X11/Xatom.h>
#include <X11/extensions/XInput2.h>
static Display *display;
static struct ev_io *x_watcher;
static struct ev_check *x_check;
static int xi_opcode = -1;
void errormsg(char *msg) {
printf("ERROR: %s\n", msg);
exit(1);
}
void init_xinput(void) {
int event, error;
if (!XQueryExtension(display, "XInputExtension", &xi_opcode, &event, &error)) {
errormsg("XInput extension not available");
}
int major_op = 2, minor_op = 2;
int result = XIQueryVersion(display, &major_op, &minor_op);
if (result == BadRequest) {
errormsg("XI2 is not supported in a sufficient version (>=2.2 required).");
} else if (result != Success) {
errormsg("Failed to query XI2");
}
}
void init_eventmask(void) {
XIEventMask masks[1];
unsigned char mask[(XI_LASTEVENT + 7)/8];
memset(mask, 0, sizeof(mask));
masks[0].deviceid = XIAllMasterDevices;
masks[0].mask_len = sizeof(mask);
masks[0].mask = mask;
XISetMask(mask, XI_RawButtonPress);
XISelectEvents(display, DefaultRootWindow(display), masks, 1);
XFlush(display);
}
void clear_primary(void) {
XSetSelectionOwner(display, XA_PRIMARY, None, CurrentTime);
XSync(display, False);
#ifdef DEBUG
printf("primary selection cleared\n");
#endif
}
void stub_cb(EV_P_ ev_io *w, int revents) {
/* STUB */
}
void check_cb(EV_P_ ev_check *w, int revents) {
XEvent ev;
while (XPending(display) > 0) {
XNextEvent(display, &ev);
XGenericEventCookie *cookie = &ev.xcookie;
if (cookie->type != GenericEvent || cookie->extension != xi_opcode || !XGetEventData(display, cookie)) {
continue;
}
const XIRawEvent *data = (const XIRawEvent *) cookie->data;
#ifdef DEBUG
printf("button %i pressed\n", data->detail);
#endif
if (data->detail == 2) {
clear_primary();
}
XFreeEventData(display, cookie);
}
}
int main(int argc, const char* argv[]) {
struct ev_loop *evloop;
display = XOpenDisplay(NULL);
if (display == NULL) {
errormsg("Failed to connect to the X server");
return 1;
}
init_xinput();
init_eventmask();
evloop = EV_DEFAULT;
x_watcher = calloc(sizeof(struct ev_io), 1);
ev_io_init(x_watcher, stub_cb, XConnectionNumber(display), EV_READ);
ev_io_start(evloop, x_watcher);
x_check = calloc(sizeof(struct ev_check), 1);
ev_check_init(x_check, check_cb);
ev_check_start(evloop, x_check);
ev_run(evloop, 0);
return 0;
}