Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

User32 extensions to support Keyboard related functionality. #1063

Closed
wants to merge 13 commits into from
Closed

User32 extensions to support Keyboard related functionality. #1063

wants to merge 13 commits into from

Conversation

kevemueller
Copy link

Extend User32 signatures. Introduce utility function for convenient use
of LoadString and enum for all defined Win32 VK.

Keve Müller added 4 commits February 4, 2019 09:57
Extend User32 signatures. Introduce utility function for convenient use
of LoadString and enum for all defined Win32 VK.
Copy link
Member

@matthiasblaesing matthiasblaesing left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I left some comments inline. Please also add an entry to CHANGES.md.

*/
public final int code;
/** This VK may be used to map to a unicode codepoint in a Keyboard Layout. */
public final boolean mappable;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where does this info come from?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is based on own investigation. I understand that this might be borderline to include here, but it is such a useful bit of information. I can refactor the code to leave it out if you consider it out-of-policy.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I gave this a second thought and went for a feasible alternative. I have taken out mappable member and introduced User32Util.WIN32VK_MAPPABLE, a set of the mappable virtual keys. This makes the enum clean (based purely on header definitions) and keeps the information in a concise way as a useful utility.

* @return the VK enum instance.
*/
public static Win32VK fromValue(final int code) {
if (code < 0 || code > 0xFF) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any reason to special case this? The fall through to line 300 should catch it.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok.

* Virtual Keys, Standard Set
*/
VK_LBUTTON(0x01, false), VK_RBUTTON(0x02, false), VK_CANCEL(0x03, true), VK_MBUTTON(0x04, false), /*
* NOT
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lines 15-19 look strange (indent?)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok

/*
* Virtual Keys, Standard Set
*/
VK_LBUTTON(0x01, false), VK_RBUTTON(0x02, false), VK_CANCEL(0x03, true), VK_MBUTTON(0x04, false), /*
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please define one enum per line - its difficult to see where one entry ends and the next starts.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok.

/**
* Handle to a input locale identifier (formerly called keyboard layout handle).
*/
public static class HKL extends HANDLE {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The definition of HKL should be in the file matching its header - that would be WTypes.java. I'm not sure if HANDLE is the correct super class. The C definition is typedef void *HKL. There is also the question: where does the information about the internal structure (embedded language identifier and device handle) come from?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

HKL is defined as DECLARE_HANDLE(HKL); in minwindef.h
So it should be defined same way as e.g. HINSTANCE in the same file.
I have moved it to WinDef.java.

The internal structure is described in the Javadoc of GetKeyboardLayout, which is taken from official docs.

* If the function finds no key that translates to the passed character
* code, both the low-order and high-order bytes contain -1.
*/
short VkKeyScanEx(char ch, HKL dwhkl);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is problematic - there are to definitions for this function:
https://docs.microsoft.com/en-us/windows/desktop/api/winuser/nf-winuser-vkkeyscanexw
https://docs.microsoft.com/en-us/windows/desktop/api/winuser/nf-winuser-vkkeyscanexa
At this point I would consider to explicitly map

short VkKeyScanExW(char ch, HKL dwhkl);
short VkKeyScanExA(byte ch, HKL dwhkl);

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok.

* character value, depending on the value of uCode and uMapType. If
* there is no translation, the return value is zero.
*/
UINT MapVirtualKeyEx(UINT uCode, UINT uMapType, HKL dwhkl);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

UINT is defined to be 32 bit wide (https://docs.microsoft.com/en-us/windows/desktop/winprog/windows-data-types) it might be substituted with an int.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

UINT is extensively used in User32. I have it there for consistency with the remainder of the code.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

User32 is old, the current calling parameters will not change to keep the API stable. If there is a good reason to use an UINT I'm good with it. One thing UINT can represent, that int can't is the unsignedness. So if the values need to be ordered a UINT might be better.

* extended error information, call GetLastError.
*
*/
int LoadString(HINSTANCE hInstance, UINT uID, LPSTR lpBuffer, int cchBufferMax);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

'lpBufferisLPTSTRand thus changes depending whether or not the unicode version is called. I would use aPointer` and add a convinience function to decode it. Have a look at this:
com.sun.jna.platform.win32.Kernel32Util.expandEnvironmentStrings(String)
there memory is allocated based on characters and read based on the unicode settings.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pointer - OK.
There is already a convenience function using the more common way to call this function.

if (0 == x) {
throw new Win32Exception(Kernel32.INSTANCE.GetLastError());
}
return new String(lpBuffer.getValue().getCharArray(0, x));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks suspicious. As written above, you could get a non unicde value, than you are interested in the byte array. And the Native#toString functions.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pointer.getString() and Pointer.getWideString() cannot be used as the returned buffer might not be null terminated.
I am now distinguishing between Unicode and non-unicode.

Copy link
Member

@matthiasblaesing matthiasblaesing left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for your work. In general this looks good. I left a few comments inline. One think I'd like to see before merging are unittests for the new functions. I learned the hard way, that they help.

@@ -2001,4 +2003,83 @@ public String toString() {
* or 22 kHz PCM.
*/
public int CF_WAVE = 12;

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please correct the indention. There is not "one right" indention style for JNA, but in most cases 4 spaces per indention level is correct. Tabs are normally not used.

/**
* Bitmask for the SHIFT key modifier.
*/
int MODIFIER_SHIFT_MASK = 1;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where do the "MODIFIER_*" constants from? I could not find in my headers.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They are in the documentation of the function. I have extracted them as constants.

/**
* Handle to a input locale identifier (formerly called keyboard layout handle).
*/
public static class HKL extends HANDLE {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please reindent (4 spaces per ident level)

/**
* Set of {@link Win32VK} members that can be mapped to a UniCode code point via a keyboard layout.
*/
public static final EnumSet<Win32VK> WIN32VK_MAPPABLE = EnumSet.of(Win32VK.VK_BACK, Win32VK.VK_TAB, Win32VK.VK_CLEAR, Win32VK.VK_RETURN,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I'm not mistaken the WIN32VK_MAPPABLE property is derived, as is the introducedVersion in the Win32VK enum. You pulled one out. I have no strong feelings either way, if you want to reintegrate the mappable property from the first revision, I would be ok with it.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like the mappable property like this. Very convenvient to use and clearly distinguished header values vs. "other known" values.
introducedVersion is part of the header (IFDEF), so it should be in the enum.

* character value, depending on the value of uCode and uMapType. If
* there is no translation, the return value is zero.
*/
UINT MapVirtualKeyEx(UINT uCode, UINT uMapType, HKL dwhkl);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

User32 is old, the current calling parameters will not change to keep the API stable. If there is a good reason to use an UINT I'm good with it. One thing UINT can represent, that int can't is the unsignedness. So if the values need to be ordered a UINT might be better.

* the high word contains a device handle to the physical layout of the
* keyboard.
*/
HKL GetKeyboardLayout(DWORD idThread);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See my comment below for my reasoning (comment on UINT)

@kevemueller
Copy link
Author

kevemueller commented Feb 20, 2019

Hi @matthiasblaesing,

I have added the tests and also applied some minor cosmetics.

I have not found a source formatting definition. Please create one and then the source base can be reformatted in a tidy matter. I got rid of the tabs.

Hope this can be closed now.
Cheers,

Keve

@matthiasblaesing
Copy link
Member

I added some more changes - see here: https://github.com/matthiasblaesing/jna/commits/pr-1063
Please see if that works for you and if you agree. If so, I would squash all commits, leave you as the author and merge to master.

@kevemueller
Copy link
Author

Looked at your changes. All fine with me. Indeed the non-English locale was a good one...
Fully agree, esp. to the squashing.

Cheers,
Keve

@matthiasblaesing
Copy link
Member

Merged as 07adbff
Thank you for your work.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants