keyChar & virtualKey in remoteControlMBS.macPressKey

I am struggling with the parameters keyChar and virtualKey in RemoteControlMBS.MacPressKey.

I am capturing key presses using keyDown and keyUp events in textField and textArea controls. I want to be able to play back those keys into the control - but it is not working for me.

I understand that keyChar is supposed to be the an integer representing the ascii value of the key - eg key.asc from keyDown event. But what is the virtual key?

The virtual key is the value of the “physical key”.

An example:
On a “QWERTY…” keyboard, the 6th key after the tab key is “Y”; on a “QWERTZ…” keyboard, it’s obviously a “Z”. In both cases, this key is at the same location: that’s the virtual key (being 16 for this Z/Y key, as you can see using Keyboard.AsyncKeyDown).

So, when you simulate a key press, you have to tell the OS two things: the physical key (=the virtual key you’re wondering) and the resulting character. This makes sense, as a single character may appear several times on a keyboard (and the keyboard event mechanism uses this physical key anyway, so you have to provide the same information).

The MBS plugin has methods to convert to/from characters from/to virtual key codes, I think.

That’s a perfect nonsense! :slight_smile:

I was hoping so but not seeming to find the right solution.

e.g. taking “key” from a keyUp event and applying to:

Dim keyChar As Integer = key.Asc
Dim b As Boolean = remoteControlMBS.MacPressKey( keyChar, keyChar)

Seems to result in nonsense characters being applied to the selected textField.

Maybe this table here helps you?

I’m not the one who invented the terms :man_shrugging:t2:
My explanation is still true. For instance, try this (e.g. in a Timer):
call RemoteControlMBS.MacPressKey(106,38,True)
call RemoteControlMBS.MacPressKey(106,38,False)

106: the ascii for j
38: the physical keycode for “j” (on most keyboard: that one doesn’t change)

You should get a “j” typed. I just tried and it’s what I get.

As long as you consider my previous answer as nonsense and don’t try these physical characters, it won’t work :wink:

I’ve checked this table: it’s an english-layout one (only).
On my Swiss keyboard (and I expect your German one will do the same), some of these characters won’t work as expected.

For instance, try this:
call RemoteControlMBS.MacPressKey(122,6)

122: ascii for “z”
6: the key physically left to the “x” key. According to the table (and to an english layout), “z” is the keycode 6. On my keyboard’s official layout, “y” is at the left of “x”.

122 and 6 are “contradictory” on my layout (as well as a German one), as the keycode 6 doesn’t produce “z” but “y”. The code above discards the 122 (z) and produces a “y”. Only the keycode is taken in this case.

Thanks Arnaud, my joke was about “physical = virtual” - a new concept - perhaps part of the Avatar movie, certainly not about your answer.

So which of the integers, eg 106 or 38, is Mac using to generate the MacPressKey? Surely it can’t use both as that would just generate conflict if the programmer didn’t get it right all the time.

And how do I generate those codes from a KeyDown or KeyUp event - surely not a lookup table or dictionary as implied by Christian’s answer - that feels far too error prone and far too many possible combinations to get cover.

I’m still confused.

Ok, no problem; thanks for the clarification.

According to my testings, only the physical (a.k.a virtual :wink:) key is used. A simple test using either of those calls tells this:
call RemoteControlMBS.MacPressKey(74,38) 'Should be a capital “J” (so, possible to do with the key 38) but the output is still a small “j”.
call RemoteControlMBS.MacPressKey(169,8) '© is option-c on my keyboard. This call just outputs a “c”.
I’m not sure when the other parameter (the ascii value) is used; apparently, never. Perhaps when the keyboard layout doesn’t have the wanted character? :thinking:

You’re right, that’s not the most easier thing in the world. You have to get the physical key code in the KeyDown/KeyUp event. This information exists in the events chain, but Xojo doesn’t provide it in the available parameters (a shame, IMO).

There are at least two ways of doing this, one in pure Xojo code (less reliable) and the other with plugins/declares.
The Xojo one involves the only available method to check for pressed keys: the Keyboard.AsyncKeyDown call. Since it’s async and lacks a sync counterpart, you have to check in a loop:

for i as integer=0 to 127 'Covers all physical keys
if Keyboard.AsyncKeyDown(i) then 'The key code “i” is pressed
//Strip out modifiers keys that may be present. E.g. Command key is always 55, 57 for Control key, etc.
//Store “i” (may be an array if several keys are pressed at the same time)
end if
next
Theoretically, you may miss the key on a slow/busy computer if the user releases the key too fast, before the loop detects it (e.g. if the CPU gets really busy while your loop executes or if the event gets delayed before you get the KeyDown event); that’s the nature of async calls. If you choose that method, just be prepared to discard the event if you get apparently no key pressed. I’ve never encountered this theory in reality, though.
As I said, this is a rather “unreliable” method, but “doable” in pure Xojo code.

The other method is to ask the events manager for the current event’s pressed keys. I’ve not done that yet, being on another project, but the MBS plugin has some available ways for that (check, in order of reliability, the CurrentEvent property of the NSApplicationMBS class, the NSEventMonitorMBS class and the CarbonMonitorEventMBS class (regardless of its name, it’s still supported) for how to get the data of the pressed keys).
If you need more assistance, I’ll dig into that; just let me know.

Thanks Arnaud, I will give that a go.

I suppose the other way for my particular application would be to capture the changes to the textField or textArea contents after each keypress. That might work more reliably but then I would miss the ability to track keypresses in the recorded screen (which I think I want for the recorded videos).

I have followed a slightly different approach to try and avoid the problems you cite.

Firstly create a dictionary in a calculated property comprising all the character codes:

Public Shared Property diAscii as xojo.core.Dictionary
Get
  If mdiAscii = Nil Then
    
    //Note, standard dictionaries are not case sensitive, so need to generate case sensitive version
    
    mdiAscii = New Xojo.Core.Dictionary
    AddHandler mdiAscii.compareKeys, AddressOf diCaseSensitivity
    
    Dim modKeys() As Integer = Array(0, 2, 8, 10) //none, shift, option, shift+option
    
    For Each modValue As Integer In modKeys
      
      Dim modCode As Integer = modValue * 128
      
      For i As Integer = 0 To 127
        //apply the keyCode for each of the base ascii characters in the keyboard
        
        //only option and shift keys modify the recieved ascii
        Dim key As String = RemoteControlMBS.MacTextForKeyCode(i, 3, modValue)
        mdiAscii.value(key.ToText) = modCode +  i 
        
      Next
      
    Next
    
  End
  
  Return mdiAscii
End Get

Set
  
End Set

End Property

Note: this has to be a xojo.core.dictionary made case sensitive using the compare keys event. The modKeys applied are to capture shift and option keys and have to do all the conversion to Text from String etc.

Next in the KeyDown and/or KeyUp methods add this code:

Private Shared Sub System_KeyDown(v as variant)
  
  Dim key As String = v
  
  Dim keyChar As Integer = key.Asc
  
  if targetwin32 then
    Break
  Elseif targetmachO Then
    
    Dim di As xojo.core.dictionary = diAscii
    
    //get the modified code from the dictionary  - ie ascii code plus 128 * modifiers
    Dim ModifiedCode As Integer = di.Lookup(key.toText, 63)  //leave 63 = ? as default
    
    //Get the modifier values
    Dim modCode As Integer = ModifiedCode/128 //ie the integer value - 0, 2, 8, 10
    
    //set the modifier keys
    SetModKeys(modCode, true)
    
    Dim virtualKey As Integer = ModifiedCode - modCode*128  //remove the modifiers
    
    Dim b As Boolean = RemoteControlMBS.MacPressKey(keyChar, virtualKey, True)
    
    
  Elseif TargetLinux Then 
    Break
  Else
    Break
  End If
  
  th_clicks.Resume
End Sub

This grabs the virtual key and ascii key from the dictionary and applies them to the MBS.macPressKey.

The setModKeys method simply applies the decoded mod keys on or off. In my methods I turn them on before KeyDown and off after KeyUp.

Private Shared Sub SetModKeys(modCode as integer, down as boolean)
  
  Select Case modCode
  Case 0
    //ignore
  Case 2
    Call RemoteControlMBS.MacPressShiftKey( down ) 
  Case 8
    Call RemoteControlMBS.MacPressOptionKey( down ) 
  Case 10 
    Call RemoteControlMBS.MacPressShiftKey( down ) 
    Call RemoteControlMBS.MacPressOptionKey( down ) 
  Else
    Break
  End Select
End Sub

Hopefully this method will be independent of keyboard attached to the device - though I am sure there are code improvements to be made (especially with the auto, text, string conversions).

It seems to work OK with basic characters but has problems with the delete key and multi-key diacritics like é.

1 Like

An interesting approach.
How are you handling the case where the user switches his keyboard layout while your app runs?

Simple answer: I’m not, it wouldn’t handle that situation.

This topic was automatically closed 182 days after the last reply. New replies are no longer allowed.