Prompted by these open API tracker issues:
- Tool#onKeyDown and onKeyUp flags broken #541 - 2 OCT 2020 @eneroth3
- onKeyDown event firing #926 - 3- OCT 2023
… and this topic:
… in which, after I had tested from SU2021 onward, and the results showed that the flags
passed to Tool#onKeyDown
are always 0
for SketchUp 23, 24 and 25, … Steve took it another step, thus:
He mentions that the flags
require some special handling to extract data:
This [code] post is my response for Windows platform tools with Example
Note that on MS Windows, the window messages sent to an application can have a combination of flags. SketchUp did not define these flag constants when they defined the virtual key constants in the API.
Some of these may not apply to MacOS as they do not use window messaging like the Windows system does.
So you may need to define these in your class or modules as local constants.
# REF:
# https://learn.microsoft.com/en-us/windows/win32/inputdev/about-keyboard-input
#
KF_EXTENDED = 0x0100 # 256
KF_MENUMODE = 0x1000 # 4096
KF_ALTDOWN = 0x2000 # 8192
KF_REPEAT = 0x4000 # 16384
KF_UP = 0x8000 # 32768
My testing shows that now since SU2023 began the Qt migration, the only modifier key that returns flags for onKeyDown
is ALT.
When pressed by itself first, we get key VK_ALT
and flags 8192
which is KF_ALTDOWN
.
When we hold ALT down and strike another key, we get the keycode for the 2nd key and flags that are equal to (or contain) KF_ALTDOWN
.
alt_depressed = ( flags & KF_ALTDOWN ) == KF_ALTDOWN
Sometimes, using ALT (on Windows) activates the application menu bar and the flags may contain MF_MENUMODE
. When this happens we (sometimes) do not get a follow up onKeyUp
firing for the ALT key when it is released. (This was reported recently.)
The thing about Windows keystroke messages is that they do not contain information about the CTRL, SHIFT or toggle keys.
A C/C++
developer must call a system function in the message handler callback in order to get whether either of these modifiers are held down.
See Key Status in the above reference article, which mentions calling GetKeyState()
, which we can do on Windows. (This likely means you will have separate Tool
definition files for Mac and Windows.)
We can call Windows API C functions from Ruby using the Fiddle
library.
(Credit Anton whom I believe suggested this workaround in past conversations.)
This is a preliminary version of my KeyTestTool:
- Testing_KeyTestTool.rb (3.7 KB)
For those not wanting to download it, click to expand code viewer ...
# encoding: UTF-8
module Testing
# RUN ONCE PER SESSION
unless defined?(@loaded)
submenu = UI.menu("Plugins").add_submenu("TESTING")
submenu.add_item("Ruby Tool Keys") {
Sketchup.active_model.select_tool(KeyTestTool.new)
}
@loaded = true
end
module KeyInfo
require 'fiddle' unless defined?(Fiddle)
require 'fiddle/import' unless defined?(Fiddle::Importer)
extend Fiddle::Importer
dlload 'user32.dll'
# https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getkeystate
extern 'int GetKeyState(int)'
# Toggle Keys:
VK_CAPITAL = 0x14
VK_NUMLOCK = 0x90
VK_SCROLL = 0x91
# KEY FLAGS - Reference:
# https://learn.microsoft.com/en-us/windows/win32/inputdev/about-keyboard-input
#
KF_EXTENDED = 0x0100 # 256
KF_DLGMODE = 0x0800 # 2048
KF_MENUMODE = 0x1000 # 4096
KF_ALTDOWN = 0x2000 # 8192
KF_REPEAT = 0x4000 # 16384
KF_UP = 0x8000 # 32768
def self.alt_down?(flags)
# Test whether flags contain KF_ALTDOWN:
( flags & KF_ALTDOWN ) == KF_ALTDOWN
end ###
def self.caps_toggled?
# Test whether keystate's low order bit is set (1):
(self.GetKeyState(VK_CAPITAL) & 0x0001) != 0
end ###
def self.control_down?
# Test whether keystate's high order bit 15 is set (1):
(self.GetKeyState(VK_CONTROL) & 0x8000) != 0
end ###
def self.dialog_mode?(flags)
# Test whether flags contain KF_DLGMODE:
( flags & KF_DLGMODE ) == KF_DLGMODE
end ###
def self.extended_key?(flags)
# Test whether flags contain KF_EXTENDED:
( flags & KF_EXTENDED ) == KF_EXTENDED
end ###
def self.key_up?(flags)
# Test whether flags contain KF_UP:
( flags & KF_UP ) == KF_UP
end ###
def self.menu_mode?(flags)
# Test whether flags contain KF_MENUMODE:
( flags & KF_MENUMODE ) == KF_MENUMODE
end ###
def self.numlock_toggled?
# Test whether keystate's low order bit is set (1):
(self.GetKeyState(VK_NUMLOCK) & 0x0001) != 0
end ###
def self.repeat?(flags)
# Test whether flags contain KF_REPEAT:
( flags & KF_REPEAT ) == KF_REPEAT
end ###
def self.scroll_toggled?
# Test whether keystate's low order bit is set (1):
(self.GetKeyState(VK_SCROLL) & 0x0001) != 0
end ###
def self.shift_down?
# Test whether keystate's high order bit 15 is set (1):
(self.GetKeyState(VK_SHIFT) & 0x8000) != 0
end ###
end # module
class KeyTestTool
def initialize
@cursor_id = 633 # Select cursor
end ###
def onKeyDown(key, repeat, flags, view)
puts "onKeyDown - key: #{key} flags: #{flags}"
puts " ALT : #{KeyInfo.alt_down?(flags)}"
puts " CTRL : #{KeyInfo.control_down?}"
puts " SHIFT : #{KeyInfo.shift_down?}"
puts
puts " CAPS : #{KeyInfo.caps_toggled?}"
puts " NUMLOCK : #{KeyInfo.numlock_toggled?}"
puts " SCROLL : #{KeyInfo.scroll_toggled?}"
return true
end ###
def onKeyUp(key, repeat, flags, view)
puts "onKeyUp - key: #{key} flags: #{flags}"
puts " ALT : #{KeyInfo.alt_down?(flags)}"
puts " CTRL : #{KeyInfo.control_down?}"
puts " SHIFT : #{KeyInfo.shift_down?}"
puts
puts " CAPS : #{KeyInfo.caps_toggled?}"
puts " NUMLOCK : #{KeyInfo.numlock_toggled?}"
puts " SCROLL : #{KeyInfo.scroll_toggled?}"
return true
end ###
def onSetCursor
UI.set_cursor(@cursor_id)
end ###
end # KeyTestTool class
end # module Testing