7
$\begingroup$

We can use:

SetOptions[EvaluationNotebook[], NotebookEventActions -> {{"KeyDown","s"} :> Print["triggered"]}]

to make the current notebook respond to s button press, also we can do the same to Modifier Keys like Ctrl, Shift, Alt or so.

Can we do the same with Backspace, Delete or F1~F12?

Additionally, can we detect the mouse-wheel's scrolling state?

$\endgroup$
  • $\begingroup$ If you are Windows you probably can rig something with .NET I can't find the code but I have done this once before with some obscure keys otherwise you can't. Mac probably has no way and linux/Ubuntu probably has a way but it is quite complex. $\endgroup$ – William Mar 10 '17 at 2:18
  • $\begingroup$ @Liam On the topic of .NET I was going back through a scrape for this answer and I believe I saw some internal stuff where they were adding .NET event handlers there, so it should be possible at that level. Wjx, I don't really know why WRI hasn't put in a "BackspaceKeyDown" option for EventHandler. Maybe try to contact tech support there. They'd be the final authority. $\endgroup$ – b3m2a1 Mar 10 '17 at 6:06
  • $\begingroup$ @Liam I'm using windows, but unfortunately I know nothing about .NET, could you show some way? Thanks! $\endgroup$ – Wjx Mar 10 '17 at 10:48
  • $\begingroup$ What exactly do you mean by "scrolling state"? If you look here you'll notice we have "MouseMoved" and "MouseDragged". If you mean a mouse-wheel scroll on Windows I'm not sure how to get that. $\endgroup$ – b3m2a1 Mar 10 '17 at 16:12
  • $\begingroup$ I mean the mouse-wheel scroll, sorry for the confusion...... $\endgroup$ – Wjx Mar 10 '17 at 16:17
12
$\begingroup$

Following detects backspace on Mac:

SetOptions[EvaluationNotebook[],NotebookEventActions->
     {{"KeyDown","\.08"}:>Print["triggered"]}]

This code can be helpful for finding out different codes for non-standard keys.

SetOptions[EvaluationNotebook[],NotebookEventActions->
     {"KeyDown":>Print[FullForm@CurrentValue["EventKey"]]}]
$\endgroup$
  • 1
    $\begingroup$ (+1) {"KeyDown","\.08"} works also on Windows. But {"KeyDown":>Print[FullForm@CurrentValue["EventKey"]]} doesn't catch Backspace. $\endgroup$ – Alexey Popkov Mar 10 '17 at 19:08
  • $\begingroup$ @AlexeyPopkov are you saying that {"KeyDown","\.08"} , but {"KeyDown":>Print[FullForm@CurrentValue["EventKey"]]} doesn't print "\.008"??!! Does it catch ESC? $\endgroup$ – BlacKow Mar 10 '17 at 19:39
  • $\begingroup$ Oh fascinating! I guess I should have found this from: here. It prints something invisible and I assumed it was a blank cell. I will have to back and figure out what all of these do. $\endgroup$ – b3m2a1 Mar 11 '17 at 1:09
  • 1
    $\begingroup$ @BlacKow Yes, {"KeyDown":>Print[FullForm@CurrentValue["EventKey"]]} prints None when I press Backspace or ESC. I'm on Windows 7 x64, checked with versions 8.0.4, 10.4.1 and 11.0.1. $\endgroup$ – Alexey Popkov Mar 11 '17 at 4:33
5
$\begingroup$

So after BlacKow's insight I found more of these by playing with this:

EventHandler[
 Panel["", ImageSize -> 100],
 "KeyDown" :>
  Print[Row@{FullForm@CurrentValue@"EventKey", " pressed"}]
 ]

A few more points of interest (this is potentially Mac specific as I am a Mac user):

"\[RawEscape]" is the escape key

"\.1c" is "LeftArrowKeyDown"

"\.1e" is "UpArrowKeyDown"

"\.1d" is "RightArrowKeyDown"

"\.1f" is "DownArrowKeyDown"

"\r" is "ReturnKeyDown"

"\[GreaterEqual]" is control-. (and various other special characters can be found as a control-key combo, but these can be easily figured out I think)

"\.10" is function-(any function key not bound)

Hopefully someone can extend this further and to Windows / Linux? Useful collection of keys to have out there.

$\endgroup$
  • $\begingroup$ I don't have ColoredPanel in version 11.0.1. What is this? $\endgroup$ – Alexey Popkov Mar 11 '17 at 4:36
  • $\begingroup$ Oops. That's something I wrote to generate a panel with that sort of rounded background like in the docked cells of the documentation notebooks (I actually pulled the appearance from there then tweaked). I'll change that to just Panel in a bit. $\endgroup$ – b3m2a1 Mar 11 '17 at 5:57
3
$\begingroup$

@BlacKow's answer works well on Mac, but Mathematica cannot handle special keys (s.a. F2 or PAUSE) on Windows. Thus,

Here is a Windows workaround:

The idea is to create a dynamic link library that runs in a separate thread, as explained here. In this separate thread, the keyboard status is continuously tested. Once the specified key is pressed, an event is raised to the MMA kernel. The following code requires a working C-compiler on a Windows PC:

Needs["CCompilerDriver`GenericCCompiler`"];

keyListen[keycode_, action_] := With[{
    libMain = First@FileNames["async-examples-libmain.c", {$InstallationDirectory}, Infinity],
    src = "
      #include <stdlib.h>
      #include \"WolframLibrary.h\"
      #include \"WolframCompileLibrary.h\"
      #include \"WolframStreamsLibrary.h\"
      #include \"windows.h\"                    // for GetAsyncKeyState()

      /**********************************************************/
      typedef struct keyTrackerArgs_st
      {
          WolframIOLibrary_Functions ioLibrary;
          mint key;
      }* keyTrackerArgs;

      static void key_tracker(mint asyncObjID, void* vtarg)
      {
          keyTrackerArgs targ = (keyTrackerArgs)vtarg;
          WolframIOLibrary_Functions ioLibrary = targ->ioLibrary;
          const mint key = targ->key;
          free(targ);

          while(ioLibrary->asynchronousTaskAliveQ(asyncObjID))
          {
              if(GetAsyncKeyState(key) & 0x0001) {
                  ioLibrary->raiseAsyncEvent(asyncObjID, \"KeyPress\", NULL);
              }
          }
      }

      DLLEXPORT int start_key_tracker(WolframLibraryData libData,
          mint Argc, MArgument *Args, MArgument Res) 
      {
          mint asyncObjID;
          WolframIOLibrary_Functions ioLibrary = libData->ioLibraryFunctions;
          keyTrackerArgs threadArg = (keyTrackerArgs)malloc(sizeof(struct keyTrackerArgs_st));

          if(Argc != 1)
              return LIBRARY_FUNCTION_ERROR;

          threadArg->ioLibrary = ioLibrary;
          threadArg->key = MArgument_getInteger(Args[0]);
          asyncObjID = ioLibrary->createAsynchronousTaskWithThread(key_tracker, threadArg);
          MArgument_setInteger(Res, asyncObjID);

          return LIBRARY_NO_ERROR;
      }
      "
  },
  Module[{srcFile},
    srcFile = CreateFile["src.c"];
    WriteString[srcFile, src];
    Close[srcFile];
    lib = CreateLibrary[{srcFile, libMain}, "keyLib", "IncludeDirectories" -> {DirectoryName[libMain]}];
    DeleteFile[srcFile];
    keyListener = LibraryFunctionLoad[lib, "start_key_tracker", {Integer}, Integer];
    Internal`CreateAsynchronousTask[keyListener, {keycode}, action];
  ]
]

Let's say we want to make a sound (Beep[]) every time the "PAUSE" key is hit. Then we evaluate

keyListen[19, Beep[] &]

where 19 is the code for "VK_PAUSE". Here are all the Windows key codes, in case you need them:

virtualKeyList = {"VK_LBUTTON" -> 1, "VK_RBUTTON" -> 2, 
   "VK_CANCEL" -> 3, "VK_MBUTTON" -> 4, "VK_XBUTTON1" -> 5, 
   "VK_XBUTTON2" -> 6, "VK_BACK" -> 8, "VK_TAB" -> 9, 
   "VK_CLEAR" -> 12, "VK_RETURN" -> 13, "VK_SHIFT" -> 16, 
   "VK_CONTROL" -> 17, "VK_MENU" -> 18, "VK_PAUSE" -> 19, 
   "VK_CAPITAL" -> 20, "VK_KANA" -> 21, "VK_HANGUEL" -> 21, 
   "VK_HANGUL" -> 21, "VK_JUNJA" -> 23, "VK_FINAL" -> 24, 
   "VK_HANJA" -> 25, "VK_KANJI" -> 25, "VK_ESCAPE" -> 27, 
   "VK_CONVERT" -> 28, "VK_NONCONVERT" -> 29, "VK_ACCEPT" -> 30, 
   "VK_MODECHANGE" -> 31, "VK_SPACE" -> 32, "VK_PRIOR" -> 33, 
   "VK_NEXT" -> 34, "VK_END" -> 35, "VK_HOME" -> 36, "VK_LEFT" -> 37, 
   "VK_UP" -> 38, "VK_RIGHT" -> 39, "VK_DOWN" -> 40, 
   "VK_SELECT" -> 41, "VK_PRINT" -> 42, "VK_EXECUTE" -> 43, 
   "VK_SNAPSHOT" -> 44, "VK_INSERT" -> 45, "VK_DELETE" -> 46, 
   "VK_HELP" -> 47, "VK_LWIN" -> 91, "VK_RWIN" -> 92, "VK_APPS" -> 93,
    "VK_SLEEP" -> 95, "VK_NUMPAD0" -> 96, "VK_NUMPAD1" -> 97, 
   "VK_NUMPAD2" -> 98, "VK_NUMPAD3" -> 99, "VK_NUMPAD4" -> 100, 
   "VK_NUMPAD5" -> 101, "VK_NUMPAD6" -> 102, "VK_NUMPAD7" -> 103, 
   "VK_NUMPAD8" -> 104, "VK_NUMPAD9" -> 105, "VK_MULTIPLY" -> 106, 
   "VK_ADD" -> 107, "VK_SEPARATOR" -> 108, "VK_SUBTRACT" -> 109, 
   "VK_DECIMAL" -> 110, "VK_DIVIDE" -> 111, "VK_F1" -> 112, 
   "VK_F2" -> 113, "VK_F3" -> 114, "VK_F4" -> 115, "VK_F5" -> 116, 
   "VK_F6" -> 117, "VK_F7" -> 118, "VK_F8" -> 119, "VK_F9" -> 120, 
   "VK_F10" -> 121, "VK_F11" -> 122, "VK_F12" -> 123, "VK_F13" -> 124,
    "VK_F14" -> 125, "VK_F15" -> 126, "VK_F16" -> 127, 
   "VK_F17" -> 128, "VK_F18" -> 129, "VK_F19" -> 130, "VK_F20" -> 131,
    "VK_F21" -> 132, "VK_F22" -> 133, "VK_F23" -> 134, 
   "VK_F24" -> 135, "VK_NUMLOCK" -> 144, "VK_SCROLL" -> 145, 
   "VK_OEM_PLUS" -> 187, "VK_OEM_COMMA" -> 188, "VK_OEM_MINUS" -> 189,
    "VK_OEM_PERIOD" -> 190, "VK_OEM_2" -> 191, "VK_OEM_5" -> 220, 
   "VK_OEM_8" -> 223, "VK_PACKET" -> 231};
$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.