Sponsored Content
Top Forums Programming Simple capturing of keyboard input without interruption Post 303014065 by bedtime on Sunday 4th of March 2018 11:52:19 AM
Old 03-04-2018
Simple capturing of keyboard input without interruption

I would like to make a function or command that checks for keyboard input without interrupting the program and exits the program when a key is pressed (perhaps the 'q' key).

The program below monitors and prints/executes commands upon a change in primary (mouse selection) or clipboard buffer. If someone wanted to look up any mouse selected word in a dictionary, they could do this by running:
Code:
clcp -e 'midori http://www.webster-dictionary.org/definition/%clip%'

Where '%clip%' is the word to be replaced by the buffer text and the '-e' parameter tells the program to execute and not just print to screen.

This part of the program is working. The issue is with the keyboard input. Upon pressing a key, it will exit and then run the key pressed as a command. If I pressed <ENTER> to exit it would drop down a line in the console; there would be an extra '$' in the console. If I pressed 'q', a 'q' would be echoed in the next console line. I do not want this. Also, I haven't figured out how to exit when only a certain key is pressed.

I've tried using ncurses, and I can show the code if asked. Curses seems to want to do its own thing and not cooperate with console program outputs; it will shift the next line to the right of where it left off on the line above causing a horizontal splash of sorts and distorting command outputs such as 'ls' and 'cat'. The program I have made uses the 'system' command to run commands, and so I don't want ncurses getting in the way.

Here is the program:
Code:
// 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;
}

I've tried to tidy up the code and make as simple and readible as possible; most buffer functions/commands and key input functions/commands have been marked to aid this.

For the love of Jupiter, if there is any way to simply exit a program, I'd gladly welcome it. I've tried with two different custom versions of key input, both three times from scratch and ncurses three times from scratch, all with less than satisfying results.

Thank you. Smilie
 

10 More Discussions You Might Find Interesting

1. Shell Programming and Scripting

suppressing keyboard input

Setup Info: This User Id and Password mention below are being used with the ISQL command to connect to a sybase database so they are likely to not be the same as those that were signed on from the session. Situation: Using a korn shell, the shell prompts for a User Id and Password. During the... (1 Reply)
Discussion started by: anthreedhr
1 Replies

2. Programming

Detecting Keyboard Input without return

Hi, I need a way to detect the up and down arrow key inputs for my program. I do not want to wait for the return key to be entered(so that rules out getch() and family). Also I need to process several of these inputs in parallel, by servicing each request with a thread. Is that possible? ... (4 Replies)
Discussion started by: ravneetd
4 Replies

3. Programming

Keyboard Input

Does anyone know how do you determine the user idle time of STDIN in order to log the user out for being idle too long. I would like to write a c program to do this but I it is not clear upon how to determine idle time from keyboard input. I have found that the "who.c" source file uses the last... (4 Replies)
Discussion started by: cpaquette
4 Replies

4. UNIX for Dummies Questions & Answers

Capturing Input Parameters on Shell Script

i have this basic line of code that doesn't work. i simply want to get the input parameter strings but when the script is run it appears that the first parameter is assigning the value to the second parameter. #!/bin/sh pdir=`pwd` p1=$1 p2=$2 echo "directory: $pdir\n" echo "parameter... (2 Replies)
Discussion started by: wtolentino
2 Replies

5. Shell Programming and Scripting

Keyboard input question

How would I change up a script that currently has something like: bash script echo what's 1 2 3 4? then using read 1 2 3 4 I type 1 2 3 4. so in the script i can do stuff like echo $1 $2 $3 $4 and such... i was just doing echo "1 2 3 4"|bash script But was wondering how could I... (5 Replies)
Discussion started by: biopulse
5 Replies

6. Shell Programming and Scripting

Capturing script output and input to log

Hi Is there a way that I can capture a shell script (both output and input) to a log file where I can analyze it? Thanks (6 Replies)
Discussion started by: nimo
6 Replies

7. Shell Programming and Scripting

sed execution with input from keyboard

> sed '' Hello hi Hello output How hi output ^D How > sed should take each line as input, process and output the result. In the above scenario the input is passed from keyboard and the output of 'Hello' as you can see is displayed on the screen after 'hi' is passed as input but not as... (1 Reply)
Discussion started by: loggedin.ksh
1 Replies

8. Shell Programming and Scripting

Capturing multiple values from user input

Hello, I need to capture multiple values from user input and do not know how to do it. I'm writing a script to delete old files, but want to give the option to keep some by asking the user. This is what my output looks like... Old files to be deleted... 1 file1 2 file2 Then this bit of... (3 Replies)
Discussion started by: jrymer
3 Replies

9. Shell Programming and Scripting

How to get input from keyboard with watch?

Hello, i`m trying to create an network monitoring script and i dont know how to make affect that script by pressing an key from keyboard and that script runs not in while or for or any other loop, but with bash command watch for example: i have created an file (for example check) with content... (0 Replies)
Discussion started by: bacarrdy
0 Replies

10. Shell Programming and Scripting

Read input from Keyboard, do not proceed if no input

Hi, I am working on a script, which requests users to enter input. Ex: read -p "Please enter your email id:" email I don't want users skipping this entry, this has to be mandatory.I dont want to proceed without input. I can do a check if variable $email is empty and proceed if not.But, i... (7 Replies)
Discussion started by: aravindadla
7 Replies
All times are GMT -4. The time now is 07:02 PM.
Unix & Linux Forums Content Copyright 1993-2022. All Rights Reserved.
Privacy Policy