├── LICENSE ├── Makefile ├── README.md └── clipnotify.c /LICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or distribute this 4 | software, either in source code form or as a compiled binary, for any purpose, 5 | commercial or non-commercial, and by any means. 6 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | PREFIX ?= /usr/local 2 | 3 | x11_bsd_flags = -I/usr/X11R6/include -L/usr/X11R6/lib 4 | 5 | all: 6 | ${CC} ${CFLAGS} ${LDFLAGS} clipnotify.c -o clipnotify $(x11_bsd_flags) -lX11 -lXfixes 7 | 8 | install: all 9 | mkdir -p ${DESTDIR}${PREFIX}/bin 10 | cp -f clipnotify ${DESTDIR}${PREFIX}/bin 11 | chmod 755 ${DESTDIR}${PREFIX}/bin/clipnotify 12 | 13 | uninstall: 14 | rm -f ${DESTDIR}${PREFIX}/bin/clipnotify 15 | 16 | clean: 17 | rm -f *.o *~ clipnotify 18 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | clipnotify is a simple program that, using the 2 | [XFIXES](https://cgit.freedesktop.org/xorg/proto/fixesproto/plain/fixesproto.txt) 3 | extension to X11, waits until a new selection is available and then exits. 4 | 5 | It was primarily designed for [clipmenu](https://github.com/cdown/clipmenu), to 6 | avoid polling for new selections. 7 | 8 | Here's how it's intended to be used: 9 | 10 | while read; do 11 | [an event happened, do something with the selection] 12 | done < <(clipnotify -l) 13 | 14 | Or: 15 | 16 | while clipnotify; do 17 | [an event happened, do something with the selection] 18 | done 19 | 20 | clipnotify doesn't try to print anything about the contents of the selection, 21 | it just exits when it changes. This is intentional -- X11's selection API is 22 | verging on the insane, and there are plenty of others who have already lost 23 | their sanity to bring us xclip/xsel/etc. Use one of those tools to complement 24 | clipnotify. 25 | 26 | You can choose a particular selection with `-s`, and loop instead of exiting 27 | with `-l`. See `clipmenu -h` for more information. 28 | -------------------------------------------------------------------------------- /clipnotify.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | static enum selections { 10 | NONE = 0, 11 | SELECTION_CLIPBOARD = (1 << 0), 12 | SELECTION_PRIMARY = (1 << 1), 13 | SELECTION_SECONDARY = (1 << 2) 14 | } selections = NONE; 15 | 16 | static int loop; 17 | 18 | int main(int argc, char *argv[]) { 19 | static const char *usage = 20 | "%s: Notify by exiting on clipboard events.\n\n" 21 | " -l Instead of exiting, print a newline when a new selection is available.\n" 22 | " -s The selection to use. Available selections:\n" 23 | " clipboard, primary, secondary\n" 24 | " The default is to monitor clipboard and primary.\n"; 25 | Display *disp; 26 | Window root; 27 | Atom clip; 28 | XEvent evt; 29 | int opt; 30 | 31 | while ((opt = getopt(argc, argv, "hs:l")) != -1) { 32 | switch (opt) { 33 | case 'h': 34 | printf(usage, argv[0]); 35 | return EXIT_SUCCESS; 36 | case 'l': 37 | loop = 1; 38 | break; 39 | case 's': { 40 | char *token = strtok(optarg, ","); 41 | while (token != NULL) { 42 | if (strcmp(token, "clipboard") == 0) { 43 | selections |= SELECTION_CLIPBOARD; 44 | } else if (strcmp(token, "primary") == 0) { 45 | selections |= SELECTION_PRIMARY; 46 | } else if (strcmp(token, "secondary") == 0) { 47 | selections |= SELECTION_SECONDARY; 48 | } else { 49 | fprintf(stderr, "Unknown selection '%s'\n", token); 50 | return EXIT_FAILURE; 51 | } 52 | token = strtok(NULL, ","); 53 | } 54 | break; 55 | } 56 | default: 57 | fprintf(stderr, usage, argv[0]); 58 | return EXIT_FAILURE; 59 | } 60 | } 61 | 62 | disp = XOpenDisplay(NULL); 63 | if (!disp) { 64 | fprintf(stderr, "Can't open X display\n"); 65 | return EXIT_FAILURE; 66 | } 67 | 68 | root = DefaultRootWindow(disp); 69 | 70 | clip = XInternAtom(disp, "CLIPBOARD", False); 71 | 72 | /* <= 1.0.2 backwards compatibility */ 73 | if (!selections) 74 | selections = SELECTION_CLIPBOARD | SELECTION_PRIMARY; 75 | 76 | if (selections & SELECTION_CLIPBOARD) 77 | XFixesSelectSelectionInput(disp, root, clip, 78 | XFixesSetSelectionOwnerNotifyMask); 79 | if (selections & SELECTION_PRIMARY) 80 | XFixesSelectSelectionInput(disp, root, XA_PRIMARY, 81 | XFixesSetSelectionOwnerNotifyMask); 82 | if (selections & SELECTION_SECONDARY) 83 | XFixesSelectSelectionInput(disp, root, XA_SECONDARY, 84 | XFixesSetSelectionOwnerNotifyMask); 85 | 86 | if (loop) { 87 | (void)setvbuf(stdout, NULL, _IONBF, 0); 88 | do { 89 | XNextEvent(disp, &evt); 90 | printf("\n"); 91 | } while (1); 92 | } else { 93 | XNextEvent(disp, &evt); 94 | } 95 | XCloseDisplay(disp); 96 | } 97 | --------------------------------------------------------------------------------