-
-
Save bathtime/39502e6ae6524a4fc37cb55f4d5459fa to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// This program moniters the clipboard / primary (mouse selection) buffers | |
// and prints text or runs a command on change. | |
// | |
// The goals of this project: | |
// | |
// 1. < 100 lines code | |
// 2. Simple & elegant coding | |
// 3. Fast & efficient execution. | |
// | |
// "Do one thing, | |
// and do it well." | |
// | |
// —Linux Credo | |
// | |
// Credits: | |
// https://stackoverflow.com/questions/27378318/c-get-string-from-clipboard-on-linux | |
// https://cboard.cprogramming.com/c-programming/63166-kbhit-linux.html | |
// https://www.unix.com/ | |
// | |
// Compile with: | |
// $ g++ -O -Wall clcp.cpp -o clcp -lX11 -std=c++17 | |
// | |
// Print clipboard buffer text to screen and wait 1000 milliseconds | |
// to check again: | |
// $ clcp -c 1000 | |
// | |
// Run output of clipboard buffer as a command then exit immediately | |
// (a '0' tells the program to exit and not wait and grab more input): | |
// $ clcp -c -e 'midori https://duckduckgo.com/?q=%clip%' 0 | |
// | |
// Where the command ran would be (if the word 'debian' were to have | |
// been selected by the mouse): | |
// $ midori https://duckduckgo.com/?q=debian | |
// | |
// Print to screen (default) what is mouse selected and check buffers | |
// every 500ms (default) for a change: | |
// $ clcp | |
// | |
// Use with UTF-8 instead of regular string. This is helpful when using | |
// characters with macrons and other characters of this kind: | |
// $ clcp -u | |
// | |
// Press any key to exit program. | |
// | |
// github: https://gist.github.com/bathtime/39502e6ae6524a4fc37cb55f4d5459fa | |
// | |
// Feel free to fork or make contributions, but if you contribute, and I | |
// very much welcome it :), please remove a line of code for every | |
// line you add by making the code more efficient; the program must not | |
// exceed 200 code lines (or the world explodes). | |
// | |
#include<experimental/string_view> | |
#include<algorithm> | |
#include<string.h> | |
#include<iostream> | |
#include<limits.h> | |
#include<X11/Xlib.h> | |
#include<chrono> | |
#include<thread> | |
#include<termios.h> | |
#include<unistd.h> | |
#include<sys/time.h> | |
#include<sys/types.h> | |
#include<stdio.h> | |
#include<termios.h> | |
#include<unistd.h> | |
#include<fcntl.h> | |
// Function to capture clipboard buffers | |
std::string PrintSelection(Display *display, Window & window, const char *bufname, const char *fmtname, std::string text) | |
{ | |
char *result; | |
unsigned long ressize, restail; | |
int resbits; | |
Atom bufid = XInternAtom(display, bufname, False), | |
fmtid = XInternAtom(display, fmtname, False), | |
propid = XInternAtom(display, "XSEL_DATA", False), | |
incrid = XInternAtom(display, "INCR", False); | |
XEvent event; | |
XConvertSelection(display, bufid, fmtid, propid, window, CurrentTime); | |
do | |
XNextEvent(display, &event); | |
while (event.type != SelectionNotify || event.xselection.selection != bufid); | |
if (event.xselection.property) | |
{ | |
XGetWindowProperty(display, window, propid, 0, LONG_MAX/4, False, AnyPropertyType, &fmtid, &resbits, &ressize, &restail, (unsigned char**)&result); | |
if (fmtid == incrid) | |
printf("Buffer is too large and INCR reading is not implemented yet.\n"); | |
else { | |
std::string clpWrd = "%clip%"; | |
// Replace clpWrd with buffer contents | |
std::size_t pos = text.find(clpWrd); | |
if (pos != std::string::npos) | |
text = text.substr(0, pos) + result + text.substr(pos + clpWrd.length(), text.length()); | |
else | |
text += result; | |
} | |
XFree(result); | |
return text; | |
} else{ // request failed, e.g. owner can't convert to the target format | |
std::cout << "No buffered content. Please fill buffer." << std::endl; | |
return ""; | |
} | |
} | |
// Capture keyboard input | |
void changemode(int); | |
int kbhit(void); | |
void changemode(int dir) | |
{ | |
static struct termios oldt, newt; | |
if ( dir == 1 ) | |
{ | |
tcgetattr( STDIN_FILENO, &oldt); | |
newt = oldt; | |
newt.c_lflag &= ~( ICANON | ECHO ); | |
tcsetattr( STDIN_FILENO, TCSANOW, &newt); | |
} else | |
tcsetattr( STDIN_FILENO, TCSANOW, &oldt); | |
} | |
// Keyboard input | |
int kbhit (void) | |
{ | |
struct timeval tv; | |
fd_set rdfs; | |
tv.tv_sec = 0; | |
tv.tv_usec = 0; | |
FD_ZERO(&rdfs); | |
FD_SET (STDIN_FILENO, &rdfs); | |
select(STDIN_FILENO+1, &rdfs, NULL, NULL, &tv); | |
return FD_ISSET(STDIN_FILENO, &rdfs); | |
} | |
using namespace std; | |
int main(int argc, char* argv[]) { | |
// Set defaults if they are not entered as parameters | |
std::string line = ""; // Text to be printed/manipulated | |
std::string tmpStr = ""; | |
const char * strType = "STRING"; // STRING or UTF8_STRING | |
const char * clpType = "PRIMARY"; // PRIMARY or CLIPBOARD | |
bool isExec = 0; // Default to print text, not execute | |
long milSec = 500; // Miliseconds for thread sleep | |
// Sort out parameters and set variables | |
for(int pNum = 1; pNum < argc; pNum++) { | |
tmpStr = argv[pNum]; | |
// First check if it is a number. Use new efficient string_view func :) | |
if (!std::experimental::string_view(argv[pNum]).empty() && std::all_of(tmpStr.begin(), tmpStr.end(), ::isdigit)) | |
milSec = stoi(argv[pNum]); | |
else if (std::experimental::string_view(argv[pNum]) == "-c") | |
clpType = "CLIPBOARD"; | |
else if (std::experimental::string_view(argv[pNum]) == "-u") | |
strType = "UTF8_STRING"; | |
else if (std::experimental::string_view(argv[pNum]) == "-e") | |
isExec = 1; | |
else // Just append other stuff to text (quoted or unquated) | |
line += argv[pNum]; | |
} | |
// Buffer capture variables | |
Display *display = XOpenDisplay(NULL); | |
unsigned long color = BlackPixel(display, DefaultScreen(display)); | |
Window window = XCreateSimpleWindow(display, DefaultRootWindow(display), 0,0, 1,1, 0, color, color); | |
changemode(1); // keyboard input | |
std::string lastClip; | |
std::chrono::milliseconds timespan(milSec); | |
// Let's roll whilst no key is pressed! | |
while (!kbhit()){ | |
// Gather all the output info | |
std::string result = PrintSelection(display, window, clpType, strType, line); | |
if (result != lastClip) | |
{ | |
lastClip = result; | |
if (isExec) // Execute or print to screen? | |
system(result.c_str()); | |
else | |
std::cout << result << std::endl; | |
if (milSec == 0) return 0; // Just run once and exit. | |
} | |
// Sleep in an efficient manner | |
std::this_thread::sleep_for(timespan); | |
} | |
// Buffer capture shutting down commands | |
XDestroyWindow(display, window); | |
XCloseDisplay(display); | |
changemode(1); // Keyboard input. Change back to regular mode. | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment