commit 28db16e977b096c3d4968fb9ad9de61f133a1d46 Author: 宇天 Date: Wed Jan 15 20:24:05 2020 +0800 backup diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e939412 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +*.o +*.swp +xmousepasteblock diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..9b0ac7d --- /dev/null +++ b/Makefile @@ -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 diff --git a/README.md b/README.md new file mode 100644 index 0000000..e86e998 --- /dev/null +++ b/README.md @@ -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 'libinput Button Scrolling Button' 0` diff --git a/xmousepasteblock.c b/xmousepasteblock.c new file mode 100644 index 0000000..3019e66 --- /dev/null +++ b/xmousepasteblock.c @@ -0,0 +1,129 @@ +/* + * Copyright (c) 2018 Micha LaQua + * + * 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 . + * + */ + +#include +#include +#include + +#include +#include +#include + +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; +}