backup
commit
28db16e977
|
@ -0,0 +1,3 @@
|
||||||
|
*.o
|
||||||
|
*.swp
|
||||||
|
xmousepasteblock
|
|
@ -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
|
|
@ -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`
|
|
@ -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;
|
||||||
|
}
|
Reference in New Issue