--- /dev/null
+/*
+ * Print contents of primary X selection. Inspired by the `xclip' utility by Kim Saunders.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <X11/Xlib.h>
+#include <X11/Xatom.h>
+
+static Display *dpy;
+static Window win;
+
+static void die(char *msg)
+{
+ fprintf(stderr, "xclipcat: %s\n", msg);
+ exit(1);
+}
+
+int main(void)
+{
+ /* Create display and window */
+ if (!(dpy = XOpenDisplay(NULL)))
+ die("Cannot open display");
+ win = XCreateSimpleWindow(dpy, DefaultRootWindow(dpy), 0, 0, 1, 1, 0, 0, 0);
+
+ /* Select which events we want to listen to */
+ XSelectInput(dpy, win, PropertyChangeMask);
+
+ /* Atoms we will need */
+ Atom pty = XInternAtom(dpy, "XCLIPCAT_CONTENTS", False);
+ Atom incr = XInternAtom(dpy, "INCR", False);
+
+ /* Request the selection */
+ XConvertSelection(dpy, XA_PRIMARY, XA_STRING, pty, win, CurrentTime);
+
+ /* Wait for the right event */
+ XEvent ev;
+ do
+ XNextEvent(dpy, &ev);
+ while (ev.type != SelectionNotify);
+
+ /* Read type and length of our property */
+ Atom pty_type;
+ int pty_format;
+ unsigned long pty_items, pty_size;
+ unsigned char *buf;
+ XGetWindowProperty(dpy, win, pty, 0, 0, False, AnyPropertyType, &pty_type, &pty_format, &pty_items, &pty_size, &buf);
+ XFree(buf);
+
+ /* Check type and format */
+ if (pty_type == incr)
+ die("Incremental transfer not supported yet");
+ if (pty_format != 8)
+ die("Unrecognized property format");
+
+ /* Read the contents of the property */
+ XGetWindowProperty(dpy, win, pty, 0, pty_size, False, AnyPropertyType, &pty_type, &pty_format, &pty_items, &pty_size, &buf);
+
+ /* Print the contents */
+ for (unsigned int i=0; i<pty_items; i++)
+ putchar(buf[i]);
+ putchar('\n');
+
+ /* Free the buffer and delete the property (just for completeness) */
+ XFree(buf);
+ XDeleteProperty(dpy, win, pty);
+
+ return 0;
+}