Setting up notifications for physical key press combinations - bash

I've got a Royal Kludge RK61 60% keyboard that has different modes depending on a few different combinations of FN + <ENTER, CTRL, WINKEY>. Unfortunately, there is no actual indication as to which mode the keyboard has selected in any way. I'm looking to change that with my extremely limited bash knowledge by implementing a notification upon key press.
I've done a bit of searching over the past couple days trying to find out the input data for the keys so that it can be incorporated into code.
I found some code that seems to do what I want it, but I think my incorporation of the data needed is flawed, or even wrong.
#!/bin/bash
while true; do
read $key input
if [ "$input" = "keysym 0x1008ff2b" + "keysym 0xff0d" ]; then
notify-send -u normal -i $HOME/Downloads/keyboard_icon.jpeg "Keyboard is now in Function mode:" " Arrow Keys on"
fi
done
Is this correct? How do I ensure that it would be executed without needing the console open?

Related

tput sequence that results in no apparent change?

I frequently have to ssh to a remote system that, if faced with N minutes of inactivity (where N is rather small), will terminate my connection. I'm getting tired of being kicked off and losing my work/context just because, e.g., I went to look something up on the web and it took a little long.
I've been trying to come up with a simple script that I can run in the background that merely sends a small amount of output to the terminal now and then, so I will not be logged off.
Of course, ideally, I'd like the output not to interfere with whatever I'm doing, regardless of what I'm doing -- plain text, curses, whatever.
So I'm looking for some tput capability(s) that will reset the I/O timer, but result in a noop regarding screen appearance. I've found a lot of things that come close, but so far, all of them could conceivably result in a glitch if the timing is wrong.
What I'm currently doing is
echo -e "$(tput sc)$(tput rc)\c"
(save cursor, restore cursor). This has served me well so far, but I'm sure it's just a matter of time before it happens between some curses program doing it's own save/restore and getting messed up.
cub and/or cuf (cursor back, cursor forward) look like they could almost work. If either of them moved the cursor not-at-all when given a 0 parameter, that would be exactly what I'm looking for. But, unfortunately for me, 0 behaves exactly like 1 (cursor moves one position in whatever direction). At least, for the terminal I'm using, it does.
echo -e "$(tput cub1)$(tput cuf1)"
works great, except if it happens when the cursor is at the left edge of the screen. The cub1, in that case, does nothing, but then the cuf1 moves the cursor.
If I reversed them:
echo -e "$(tput cuf1)$(tput cub1)"
then I'd only have trouble if it happened while the cursor was sitting at the right edge of the screen, and I suppose that probably happens less often. Still...
If I could figure out how to get the cursor's current position, I'd get it, and then use it to cup to where it already is. But there doesn't seem to be a way to get the cursor's current position. (Or did I just miss it?)
mrcup with 0's for parameters might work if my terminal supported mrcup, but it doesn't.
Things like
echo -e '\c'
and
cat /dev/null
result in nothing at all going to the terminal, and so don't reset the inactivity timer.
Seems like there should be some simple way.
Any ideas?
I think I have it:
echo -e '\000\c'

Bash shell turns to symbols when using VIM Ack Plugin

Every now and then when using the ack-vim plugin the font in my window will change to all symbols. I've yet to see any pattern to when this happens. Any suggestions on the cause and possible remedy would be appreciated.
I've seen that happen when binary content got printed to the terminal. Do your Ack queries potentially include binary files?
A fix might be
:!echo -e '\ec\e(K\e[J'
These ANSI Escape sequences attempt to reset the terminal:
# "ESC c" - sends reset to the terminal.
# "ESC ( K" - reloads the screen output mapping table.
# "ESC [ J" - erases display.
This looks like the typical character set translation enabled by the Shift Out control character; you usually just need to send the Shift In control character to counteract it.
Basically, something is outputting a C-n character (Control-N, U+000E, named Shift Out) which tells your terminal to switch to a different display character set. You should be able to get your terminal to switch back to the normal display character set by sending a C-o (Control-O, U+000F, named Shift In) to it.
If you are in Vim, then you can probably send the C-o with a command like this:
:!printf \\017
You will have to type (or paste) this command “blindly” since (due to the alternate character set) you will probably not be able to read what you are typing. If you are typing it (not pasting), then you can also type C-v C-o (to insert a single, literal C-o) instead of the backslashed octal, if that is easier to remember.
If you find that this problem occurs only sporadically when you use the vim-ack plugin, then perhaps some bit of the text results contains the problematic Shift Out character. You might try searching for the file with a command like this:
grep -FRl $(printf \\016) .
Once you know the names of the files, then you should be able to use Vim to search for the character (start a search and type C-v C-n to insert a literal C-n). Maybe it is just some garbage that you can clean out, or maybe you can configure your ack-based searches to exclude the problematic files.
You also tagged the question with tmux. I can not tell for sure, but it looks like the top line might be a tmux status line. Since this line is also corrupted it indicates that it your external terminal emulator that has switched character sets, not just one of your tmux panes.
If you send Shift Out or Shift In directly to a tmux pane it will only affect that pane (each pane is emulated independently), so your status like could not have been munged just by a stray Shift Out hitting a single pane.
If you are running inside tmux, then the easiest way to reset the outside terminal is to suspend and resume your tmux client (or detach from and reattach to your session). tmux pretty much resets the outside terminal when it gives up control.
Depending on the situation, you may also have to reset the character set of the tmux pane by sending it a C-o, too (i.e. printf \\017 at a shell, or a :! prompt in Vim).
It is easy to see how a stray Shift Out could reconfigure a single tmux pane, but it is harder to see how it could have “leaked” out to reconfigure the external terminal (tmux is pretty good at isolating things like this). However, there is a control sequence that tmux recognizes that instructs it to pass data directly to the external terminal (thus “leaking out”), but it is much less likely that you would randomly encounter this sequence since it is much longer:
printf '\ePtmux;%s\e\\' 'stuff bound for the external terminal'
You could use it to send the restorative Shift In like this:
printf '\ePtmux;%s\e\\' $(printf \\017)
You will also want to tell tmux to redraw itself after this (by default, the refresh-client command is bound to C-b r).
It is probably easier to just suspend and resume (or detach and reattach), but this sequence is useful if that is not possible. It also provides a means toward understanding what kind of sequence might “leak” out of tmux to switch the character set of the external terminal.

Elegant and efficient way to start GUI programs from terminal without spamming it (Bash or any Posix shell)

Every once in a while I have to fire up a GUI program from my terminal session to do something. It usually is Chrome to display some HTML file are some task alike.
These programs however throw warnings all over the place and it can actually become ridiculous to write anything so I always wanted to redirect stderr/stdout to /dev/null.
While ($PROGRAM &) &>/dev/null seems okay I decided to create a simple Bash function for it so I don't have to repeat myself everytime.
So for now my solution is something like this:
#
# silly little function
#
gui ()
{
if [ $# -gt 0 ] ; then
($# &) &>/dev/null
else
echo "missing argument"
fi
}
#
# silly little example
#
alias google-chrome='gui google-chrome'
So what I'm wondering about is:
Is there a way without an endless list of aliases that's still snappy?
Are there different strategies to accomplish this?
Do other shells offer different solutions?
In asking these questions I want to point out that your strategies and solutions might deviate substantially form mine. Redirecting output to /dev/null and aliasing it was the only way I know but there might be entirely different ways that are more efficient.
Hence this question :-)
As others have pointed in the comments, I think the real problem is a way to distinguish between gui vs. non-gui apps on the commandline. As such, the cleanest way I could think of is to put this part of your script:
#!/bin/bash
if [ $# -gt 0 ] ; then
($# &) &>/dev/null
else
echo "missing argument"
fi
into a file called gui, then chmod +x it and put it in your ~/bin/ (make sure ~/bin is in your $PATH). Now you can launch gui apps with:
`gui google-chrome`
on the prompt.
Alternatively, you can do the above, then make use of bind:
bind 'RETURN: "\e[1~gui \e[4~\n"'
This will allow you to just do:
google-chrome
on the prompt and it would automatically append gui before google-chrome
Or, you can bind the above action to F12 instead of RETURN with
bind '"\e[24~": "\e[1~gui \e[4~\n"'
To separate what you want launched with gui vs. non-gui.
More discussion on binding here and here.
These alternatives offer you a way out of endless aliases; a mix of:
Putting gui in your ~/bin/, and
Binding use of gui to F12 as shown above
seems the most ideal (albeit hacky) solution.
Update - #Enno Weichert's Resultant Solution:
Rounding this solution out ...
This would take care of aliases (in a somewhat whacky way though) and different escape encodings (in a more pragmatic rather than exhaustive way).
Put this in $(HOME)/bin/quiet
#!/bin/bash -i
if [ $# -gt 0 ] ; then
# Expand if $1 is an alias
if [ $(alias -p | awk -F "[ =]" '{print $2}' | grep -x $1) > 0 ] ; then
set -- $(alias $1 | awk -F "['']" '{print $2}') "${#:2}"
fi
($# &) &>/dev/null
else
echo "missing argument"
fi
And this in $(HOME)/.inputrc
#
# Bind prepend `quiet ` to [ALT][RETURN]
#
# The condition is of limited use actually but serves to seperate
# TTY instances from Gnome Terminal instances for me.
# There might very well be other VT emulators that ID as `xterm`
# but use totally different escape codes!
#
$if $term=xterm
"\e\C-j": "\eOHquiet \eOF\n"
$else
"\e\C-m": "\e[1~quiet \e[4~\n"
$endif
Although it is an ugly hack, I sometimes use nohup for a similar needs. It has the side effect of redirecting the command's output, and it makes the program independent of the terminal session.
For the case of running GUI programs in a desktop envinroment it has only little risk for resource leaks as the program will anyway end with window manager session. Nevertheless it should be taken into account.
There is also the option of opening another terminal session. In Gnome for instance, you can use gnome-terminal --comand 'yourapp'.
But this will result in opening many useless terminal windows.
First, it's a great idea to launch GUI applications from the terminal, as this reduces mouse usage. It's faster, and more convenient in terms of options and arguments. For example, take the browser. Say you have the URL in the clipboard, ready to paste. Just type, say, ice (for Iceweasel) and hit Shift-Insert (to paste) and Enter. Compare this to clicking an icon (possibly in a menu), wait for the window to load (even worse if there is a start up page), then click the URL bar (or hit Ctrl-L), then Ctrl-V... So I understand you desire for this to work.
But, I don't see how this would require an "infinite" list of aliases and functions. Are you really using that many GUI applications? And even so, aliases are one line - functions, which may be more practical for handling arguments, are perhaps 1-5 lines of (sparse) code. And, don't feel you need to set them up once and for all - set them up one by one as you go along, when the need arises. Before long, you'll have them all.
Also, if you have a tabbed terminal, like urxvt (there is a Perl extension), you'd benefit from moving from "GUI:s" to "CLI:s": for downloading, there's rtorrent; for IRC, irssi; instead of XEmacs (or emacs), emacs -nw; there is a CLI interface to vlc for streaming music; for mail, Emacs' rmail; etc. etc.! Go hunt :)
(The only sad exception I've run across that I think is a lost cause is the browser. Lynx, W3M, etc., may be great from a technical perspective, but that won't always even matter, as modern web pages are simply not designed with those, text-only browsers in mind. In all honesty, a lot of those pages look a lot less clear in those browsers.)
Hint: To get the most out of a tabbed terminal, you'd like the "change tab" shortcuts "close" (e.g., Alt-J for previous tab and Alt-K for next tab, not the arrow keys that'll make you reach).
Last, one solution that'll circumvent this problem, and that is to launch the "GUI:s" as background processes (with &) in your ~/.xinitrc (including your terminal emulator). Not very flexible, but great for the stuff you always use, every time you use your computer.
Ok, so I kept thinking that the one function should be enough. No aliases, no bashisms, no nonsense. But it seemed to me that the only the way to do that without possibly affecting regular use, such as expansions and completions, was to put the function at the end of the command. This is not as easy as one might first assume.
First I considered a function tacked onto the end in which I would call, say, printf "%b" "\u" in order to cut the current line, plug in another printf, paste it back in, and a quote or two at the beginning, then do what little is needed at the end. I don't know how to make this work though, I'm sorry to say. And even if I did, I couldn't hope for any real reliability/portability with this method due to the varying ways shells interpret escape sequences, not to mention the terminal emulators they run in. Perhaps stty could offer a way forward along these lines, but if so, you won't find it here... now, anyway.
I eventually instead resorted to actually copying the current command's /proc/{PID}/cmdline to a variable, then (shamefully) killing it entirely, and finally wrapping it as I pleased. On the plus side, this is very easily done, very quickly done (though I can imagine arguing its 'efficiency' either way), and seems mostly to work, regardless of the original input, whether that be an alias, a variable, a function, etc. I believe it is also POSIX portable (though I can't remember if I need to specify the kill SIGNALS by name for POSIX or not), and is definitely no nonsense.
On the other hand, its elegance certainly leaves much to be desired, and, though it's probably not worth worrying about, it does waste entirely a single PID. And it doesn't stop completely the shell spam; that is, in my shell I have enabled background jobs reporting with set and so, when first run, the shell kindly informs me that I've just opened and wasted a PID in two lines. Also, because I copy the ../cmdline instead of interfacing directly with the 0 file descriptor, I expect pipes and ; and etc to be problematic. This I can, and likely will, fix myself very soon.
I will fix that, that is, if I cannot find a way to instead make use of, as I suspect can be done,SIGTSTP + SIGCONT by first suspending the process outside a subshell then within one continuing it after redirecting the subshell's output. It seems that this works unreliably for reasons I haven't yet discovered, but I think it's promising. Perhaps nohup and a trap (to effectively rehup it, as it were) is what is needed, but I'm not really sure how to put those together either...
Amyway, without further ado, my semi-aborted, backwards gui launcher:
% _G() { (
_gui_cmd="$(tr '\0' ' ' </proc/"$\!"/cmdline )" ;
kill -9 "$\!" ;
exec eval "${_gui_cmd} &" )
&>/dev/null 2&>1
}
% google-chrome-beta --disk-cache-dir="/tmp/cache" --disk-cache-size=100000000 &_G
[1] 2674
[1] + 2674 killed google-chrome-beta --disk-cache-dir="/tmp/cache" --disk-cache-
%
So another problem one might encounter if attempting to do similar is the order of expansion the shell assumes. Without some trick such as mine you're sure to have some serious difficulty expanding your function before the prior command snags it as an argument. I believe I have guarded against this without unnecessary redundancy by simply tacking my _G function call onto the original command's & background intstruction. Simply add &_Gto the tail-end of the command you wish to run and, well, good luck.
-Mike
P.S. Ok, so writing that last sentence makes me think of what might be done with tee.

How do I read any one key in Bash?

I can get read -n 1 KEY to get most keys, except for keys which are represented by multiple characters. For example, if I press the up arrow key:
$ read -n 1; echo
^[[A
$ [A
As you can see, read only takes the Esc and the [A is left over.
What I want to be able to do in a script is:
Go through a list with the arrow keys and press Enter to do something with it
For other actions, press different keys.
You are better off using dialog as jm666 mentioned, but there are other ways to skin that cat.
read -n 1 x; while read -n 1 -t .1 y; do x="$x$y"; done
Basically wait until you read a character and then spin consuming input until .1 seconds has passed w/o input.
Warning, fast typists could get annoyed. You might need to tweak that timeout.
Not a direct answer to your question - but the way of solution:
You probably should check the "dialog" utility for creating "ncurses" (screen oriented) dialog boxes from the shell. see: http://hightek.org/dialog/
Google form some examples, or check: http://unstableme.blogspot.sk/2009/12/linux-dialog-utility-short-tutorial.html

BASIC - activate microphone recording on keypress?

I am charged with an old BASIC program that needs to be altered to activate microphone recording on a specific keypress. I'm having trouble finding out how.
Anyone here able to shed any light?
Thanks for any help.
Edit: I'm pretty sure it was originally written for GW-BASIC.
Since it sounds like you don't have any of the audio code written already, my advice is that you don't try to record from GW-BASIC. There are no built-in functions for accessing the sound card (SOUND and BEEP don't count, as they work with the PC speaker), and sending SoundBlaster control codes is unreliable at best in Windows. Use a secondary, Windows-native program to record.
As for the BASIC code, you're going to have to poll the keyboard. Example:
100 PRINT "Press any key to continue"
110 A$ = INKEY$
120 IF A$ = "" THEN GOTO 110
130 IF A$ = CHR$(1) THEN GOSUB 1000
140 PRINT "Rest of code goes here..."
1000 ' Ctrl+A triggered the microphone
1010 PRINT "Starting microphone recording."
1020 SHELL "otherprg --startrecording"
1030 RETURN
Substitute your preferred key code. If you use INPUT, there's a way--the KEY statement?--to make a function key insert text of your choice. Use KEY to insert, say, CHR$(2)+CHR$(13) (^B plus Enter) when the function key is pressed, then in every INPUT call scan the results for CHR$(2) using INSTR, and branch to the microphone code as desired.
This still won't work if you're using INPUT to read numbers, though. Seriously, unless the microphone recording case is extremely constrained, you're setting yourself up for hideous code that only mostly works.
EDIT: And all this is skating around the biggest problem: GW-BASIC is single-tasking. When you're recording from the mic, you're not able to do real work elsewhere in the program, and vice versa.

Resources