Showing posts with label keyboard-shortcuts. Show all posts
Showing posts with label keyboard-shortcuts. Show all posts

Sunday, April 1, 2018

Javascript - get key description for any keyboard layout

1 comment

For a rich web application, I need keyboard shortcuts. Because there are many different keyboard layouts, they have to be configurable. Unfortunately, I can't figure out a way to map keyboard events to human-readable shortcut names such as Ctrl + Alt + Y or Alt + \.

The keypress event is useless since it doesn't fire for all keys. Here are some properties of keydown events:

  • charCode: Works only for printable characters. Deprecated, according to MDN
  • code: Works, but ignores the keyboard layout. When I press Z, I get code: "KeyY" on my German keyboard.
  • key: Works, but gives different results depending on modifiers. E. g. Shift+3 yields key: "§" on my keyboard and key: "#" on most US keyboards.
  • keyCode: The value is not unique. Ä, Ö, Ü or ^ yields keyCode: 0. Deprecated, according to MDN
  • which: Just like keyCode, the value is not unique. Deprecated, according to MDN
  • altKey, ctrlKey, metaKey, shiftKey: Useful for detecting modifier keys

How should I do this? Is it even possible without knowing the user's keyboard layout?

3 Answers

Answers 1

The thing is, at this point in time you can never know the keyboard layout from within the browser, you can only guess and speculate. There are various ways to try some kind of detection, but they are always complex and not out-of-the-box solutions.

However, there are some things which you can do to determine somehow what key has been pressed.

So Keydown will give you (say the event object is e):

e.code: Holds a string that identifies the physical key being pressed. The value is not affected by the current keyboard layout or modifier state, so a particular key will always return the same value. (from mozilla.org)

e.key: The key value of the key represented by the event. If the value has a printed representation, this attribute's value is the same as the char attribute.

e.which and e.keyCode: Although deprecated, will give you the position code of the key on the keyboard and if the e.charCode is 0, then this is either a position code (if < 256) or unicode (that means the keyboard is switched in another language)

Now the tricky part here is that e.key actually represents the unicode character if it is printable and is not from the US keyboard and is not a special key. You can convert this character into decimal number and check the value. If it is a unicode character, it's decimal value will be equal to the which and keyCode values. If it is and ordinary ASCII, it's lowercase value will be equal to the which and keyCode values. Also, before doing this (the conversion), you can check if e.key is a string (like Control, Alt, CapsLock, etc.) or a char (like a, b, c, etc.) So if it is a char, then you convert it.

All this will give you the idea which key has been pressed and what is the native printable character for this key. I don't have a German, Italian or some other keyboard to test, but you get the point.

Oh, and if you worry if Shift+3 yields "§" or "#", you should always represent that combination as Shift+3 no matter the character it yields, not to speak that the same will be produced with the combination of Ctrl+Shift+3. You have to worry only about the alphabetical characters, but I believe I gave you the means to check that.

Also, I believe that you may not need even the e.which and e.keyCode, because the e.key should give you everything.

Answers 2

I think that your problem is not a problem but only an incorrect analyze of the problem and that you have already the answer !

You say that you want to define human readable shortcuts and you refuse key property because when user press Shift+3, key value is § and I think that you are waiting that key value is 3.

The question is "What is human readable ?".

Pressing § and obtaining § or the developper that say it is not normal that when he press § in pressing Shift+3, he obtains § and not Shift+3.

But if user presses § or Shift+3 (on your german keyboard), it is normal that key value is equal to § and you can use keydown event to intercept when user press only 3 or §.

On my french keyboard, I have a key é that is on same key as digit 2. If I press key é (or 2) I obtain 'é' character. If I press key Shift+2 (or Shift+é) I obtain a 2 character.

I can use keydown event to intercept Ctrl+é and Ctrl+2 (=Ctrlt+Shift+2) without problem.

I think that you can use keydown event to intercept §. Intercepting Shift+3 is impossible because shift+3 is equivalent to §.

If your goal is to intercept SHIFT+DIGIT key independently of keyboard layout, you can add some Javascript code to create some exceptions (or filter) for digit keys (but only digit keys), I think that it is not necessary for other keys.

In this case, you can consider keyCode properties to intercept pressing digit keys.

I all situations, trying to know the keyboard's layout is not necessary.

Currently, I work with french/french, french/belgian, french/swiss and american/american keyboard (or Tastatur) and I have encountered same problem in writing a C++ ActiveControl using MFC 20 year ago.

If my answer is not enough good for you, can you post some examples so I can help you for this specific new cases.

Answers 3

You can detect the keyboard input, by combining some of the methods you mentioned. More specifically, you can:

  • Use ctrlKey, altKey, metaKey and shiftKey to detect those specific keys.
  • Combine code and key to detect the physical key that is pressed and the actual value that the key produces (if printable).
  • Utilize code to print special keys such as Enter.

The below example, while far from perfect, shows you where you could start utilizing these methods.

document.addEventListener('keydown', function(e) {    let keyboard = [];    if (e.ctrlKey) keyboard.push('Ctrl');    if (e.altKey) keyboard.push('Alt');    if (e.metaKey) keyboard.push('Meta');    if (e.shiftKey) keyboard.push('Shift');    if (!e.key.includes('Shift') && !e.key.includes('Alt') && !e.key.includes('Meta') && !e.key.includes('Ctrl'))      if (e.code.toLowerCase() === e.key.toLowerCase() || e.code.toLowerCase() === ('Key' + e.key).toLowerCase())        keyboard.push(e.key.toUpperCase())    else keyboard.push(e.code.replace('Key', '').replace('Digit', '').toUpperCase());    let prettyKeyboard = '<p>' + keyboard.map(v => `<kbd>${v}</kbd>`).join(' + ') + '</p>';    document.body.innerHTML += prettyKeyboard;  });

Notes & Considerations

  • The stylization of the keys (capitalized) is up to you, feel free to change it to your liking.
  • Certain keys produce imperfect values, so they have to be mapped using an array in order to be human-readable. Prime examples of this are the backslash and backquote characters.
  • The above example works only for combinations of up to two keys, so you might want to utilize some sort of timeout system along with an array to hold the values of the pressed keys.
  • If you want to make sure you know the user's keyboard layout, show them an on-screen keyboard and ask them to type a specific letter so that you can associate it with a certain layout and go from there. This is what Windows 10 does during installation and it's a pretty good and fool-proof method of keyboard layout detection.
  • Most keyboards have English characters printed on them alongside the keyboard language, so you should consider printing the English key value instead of whatever language the user is using. After all most applications shortcuts are usually shown as English key combinations.
Read More

Monday, June 19, 2017

macOS App: handling key combinations bound to global keyboard shortcuts

Leave a Comment

In some apps, it makes sense for the app to directly handle keyboard shortcuts which are otherwise bound to system wide combinations. For example, ⌘-Space (normally Spotlight) or ⌘-Tab (normally app switcher). This works in various Mac apps, such as VMWare Fusion, Apple's own Screen Sharing and Remote Desktop clients (forwarding the events to the VM or server, respectively, instead of handling them locally), and also some similar third-party apps in the App Store.

We would like to implement such a mode in the app we're working on, but are having a hard time working out how to do it. I should point out that the app in question is a regular foreground app, is sandboxed, and any solution must comply with App Store rules. The fact that other apps on the store can do it implies that this must be possible.

To be clear, we want to:

  • Detect and handle all key presses, including those bound to global shortcuts.
  • Prevent global shortcuts from triggering their globally bound effect.

Apple's Event Architecture document suggests that the foreground application should already be receiving these events. (It only talks about earlier levels handling things such as the power and eject buttons, which is fine.) It goes on to suggest, and the key events document also implies that NSApplication's sendEvent: method is what detects potential shortcuts based on modifier flags, dispatching them to the windows and if that fails, on to the menu bar. It's not explicitly stated what happens to globally-bound shortcuts.

I tried subclassing NSApplication and overriding sendEvent:. No matter if I pass through all events to the superclass implementation, or if I say, filter modifier key events, when I press ⌘-Space, I receive the events for pressing and releasing the command (⌘) key, but not the spacebar. The Spotlight UI always pops up.

I haven't found much information on subclassing NSApplication and its early event handling, from Apple or otherwise. I can't seem to find out at what level global shortcuts are detected and handled.

Can someone please point me in the right direction?

Possible solutions which don't work:

Suggestions I've seen in other Stack Overflow posts but which don't apply to the other apps I've seen which do this (and which would break App Store rules):

  • Accessibilty APIs (needs special permission)
  • Event taps/hooks (needs to run as root)

Both of these would be overkill anyway, as they let you intercept all events at all times, not just while your app is the foreground app.

NSevent's addGlobalMonitorForEventsMatchingMask:handler: meanwhile doesn't prevent the global shortcut handler from firing for those events, so I didn't even bother trying it.

1 Answers

Answers 1

The normal way to achieve this is by installing a Quartz event tap. However to receive events targeting other applications, you need (as you say) to be either root, or have accessibility access enabled for your app.

It seems not possible to use an event tap with the current sandboxing rules. This is confirmed in the developer forum. The link is login only, but to quote from the thread:

Is there are any chance to handle events that comming from media keys by prevents launch iTunes. Before sandbox it was possible by create CGEventTap but now sandbox deny using hid-controll.

No, this is not currently possible within App Sandbox.

I'm not sure of another way to do this; and I'd be interested to know what apps in the App Store can?

VMWare Fusion is clearly not sandboxed, and Apple's own apps are exempt from the rules. Remember that sandboxing is only enforced on new apps added after it was introduced, in 2012. Apps added before that date do not have sandboxing enforced. See this answer.

Read More

Wednesday, May 3, 2017

Swipe Back support in Android Studio

Leave a Comment

I am looking for swipe back support in Android studio like Xcode. Does anyone know if any plugin or hack available in android studio for that?

I tried to apply with the the back functionality on mouse gesture but that doesn't work well. and its cumbersome to work with that. Its not that easy as it is in Xcode.

0 Answers

Read More

Thursday, April 28, 2016

How to intercept music control keyboard shortcuts in Java?

Leave a Comment

If your keyboard has buttons for play/pause/etc (music control shortcuts), and you press them, iTunes will open (at least on Mac).

If you recently opened another music player, like Spotify, it will actually intercept the shortcut keys, and iTunes won't do anything.

Well, I want to make a music player with Java, and I want to have the same behavior. I want my application to intercept such shortcuts, and other programs shouldn't be able to interfere.

I am using JavaFX, although I don't think that really matters.

How can I achieve this?

I am already able to detect they keys the user presses using JNativeHook, but I do not know how to intercept the keys so that other applications won't do things with them.

2 Answers

Answers 1

Once you detect the keys, you could send the pause key so that the song that is being played by itunes is paused, you could use a boolean variable to detect between the shortcuts being typed on the keyboard or being send by the program(in case if you need)

or

You could use some c code(start the c program along with your java program) take a look at @Dave Delongs answer over here Modify NSEvent to send a different key than the one that was pressed You could have a different keyboard shortcut and modify the c program to send your shortcut keys while the Itunes Shortcut keys are pressed, if you need the key codes Where can I find a list of Mac virtual key codes?

for example if your music program uses p to play songs and r to listen to the next song, and itunes uses spacebar to play songs and right arrow key to go to the next one, you could do modify @Dave Delongs answer here are the changes :-

#import <Cocoa/Cocoa.h>  CGEventRef myCGEventCallback(CGEventTapProxy proxy, CGEventType type, CGEventRef event, void *refcon) {  //0x31 is the virtual keycode for "Spacebar" //0x23 is the virtual keycode for "p"   if (CGEventGetIntegerValueField(event, kCGKeyboardEventKeycode) == 0x31) {     CGEventSetIntegerValueField(event, kCGKeyboardEventKeycode, 0x23);   }  //0x7C is the virtual keycode for "Right arrow" //0x0F is the virtual keycode for "R"   if (CGEventGetIntegerValueField(event, kCGKeyboardEventKeycode) == 0x7C) {     CGEventSetIntegerValueField(event, kCGKeyboardEventKeycode, 0x0F);   }    return event; }  int main(int argc, char *argv[]) {   NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];   CFRunLoopSourceRef runLoopSource;    CFMachPortRef eventTap = CGEventTapCreate(kCGHIDEventTap, kCGHeadInsertEventTap, kCGEventTapOptionDefault, kCGEventMaskForAllEvents, myCGEventCallback, NULL);    if (!eventTap) {     NSLog(@"Couldn't create event tap!");     exit(1);   }    runLoopSource = CFMachPortCreateRunLoopSource(kCFAllocatorDefault, eventTap, 0);    CFRunLoopAddSource(CFRunLoopGetCurrent(), runLoopSource, kCFRunLoopCommonModes);    CGEventTapEnable(eventTap, true);    CFRunLoopRun();    CFRelease(eventTap);   CFRelease(runLoopSource);   [pool release];    exit(0); } 

Answers 2

You may be able to use some of the code from iTunesPatch to accomplish what you're looking for, but it appears that a system daemon may need to be modified upon installation, and you will likely have to use Objective-C/Swift.

There are further details about iTunesPatch in a blog post here.

Read More