Skip to content

Commit

Permalink
windows: add GetKeyboardLayout & ToUnicodeEx
Browse files Browse the repository at this point in the history
These are used along with GetForegroundWindow and GetWindowThreadProcessId to determine the current user layout and translate the base key the user has pressed.

Change-Id: Ib833ba7ab54213d83e889ff74c5bc0ace5edbe95
GitHub-Last-Rev: 2afe997
GitHub-Pull-Request: #188
Reviewed-on: https://go-review.googlesource.com/c/sys/+/574755
LUCI-TryBot-Result: Go LUCI <[email protected]>
Reviewed-by: Cherry Mui <[email protected]>
Reviewed-by: Than McIntosh <[email protected]>
Reviewed-by: Alex Brainman <[email protected]>
Reviewed-by: Ayman Bagabas <[email protected]>
  • Loading branch information
aymanbagabas authored and alexbrainman committed Jul 17, 2024
1 parent 0eac9b5 commit bce4cf7
Show file tree
Hide file tree
Showing 4 changed files with 101 additions and 2 deletions.
12 changes: 10 additions & 2 deletions windows/syscall_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,10 @@ import (
"unsafe"
)

type Handle uintptr
type HWND uintptr
type (
Handle uintptr
HWND uintptr
)

const (
InvalidHandle = ^Handle(0)
Expand Down Expand Up @@ -211,6 +213,10 @@ func NewCallbackCDecl(fn interface{}) uintptr {
//sys OpenProcess(desiredAccess uint32, inheritHandle bool, processId uint32) (handle Handle, err error)
//sys ShellExecute(hwnd Handle, verb *uint16, file *uint16, args *uint16, cwd *uint16, showCmd int32) (err error) [failretval<=32] = shell32.ShellExecuteW
//sys GetWindowThreadProcessId(hwnd HWND, pid *uint32) (tid uint32, err error) = user32.GetWindowThreadProcessId
//sys LoadKeyboardLayout(name *uint16, flags uint32) (hkl Handle, err error) [failretval==0] = user32.LoadKeyboardLayoutW
//sys UnloadKeyboardLayout(hkl Handle) (err error) = user32.UnloadKeyboardLayout
//sys GetKeyboardLayout(tid uint32) (hkl Handle) = user32.GetKeyboardLayout
//sys ToUnicodeEx(vkey uint32, scancode uint32, keystate *byte, pwszBuff *uint16, cchBuff int32, flags uint32, hkl Handle) (ret int32) = user32.ToUnicodeEx
//sys GetShellWindow() (shellWindow HWND) = user32.GetShellWindow
//sys MessageBox(hwnd HWND, text *uint16, caption *uint16, boxtype uint32) (ret int32, err error) [failretval==0] = user32.MessageBoxW
//sys ExitWindowsEx(flags uint32, reason uint32) (err error) = user32.ExitWindowsEx
Expand Down Expand Up @@ -1368,9 +1374,11 @@ func SetsockoptLinger(fd Handle, level, opt int, l *Linger) (err error) {
func SetsockoptInet4Addr(fd Handle, level, opt int, value [4]byte) (err error) {
return Setsockopt(fd, int32(level), int32(opt), (*byte)(unsafe.Pointer(&value[0])), 4)
}

func SetsockoptIPMreq(fd Handle, level, opt int, mreq *IPMreq) (err error) {
return Setsockopt(fd, int32(level), int32(opt), (*byte)(unsafe.Pointer(mreq)), int32(unsafe.Sizeof(*mreq)))
}

func SetsockoptIPv6Mreq(fd Handle, level, opt int, mreq *IPv6Mreq) (err error) {
return syscall.EWINDOWS
}
Expand Down
47 changes: 47 additions & 0 deletions windows/syscall_windows_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1437,3 +1437,50 @@ uintptr_t beep(void) {
t.Fatal("LoadLibraryEx unexpectedly found beep.dll")
}
}

func TestGetKeyboardLayout(t *testing.T) {
fg := windows.GetForegroundWindow()
tid, err := windows.GetWindowThreadProcessId(fg, nil)
if err != nil {
t.Fatalf("GetWindowThreadProcessId failed: %v", err)
}

// We don't care about the result, just that it doesn't crash.
_ = windows.GetKeyboardLayout(tid)
}

func TestToUnicodeEx(t *testing.T) {
var utf16Buf [16]uint16

// Arabic (101) Keyboard Identifier
// See https://learn.microsoft.com/en-us/windows-hardware/manufacture/desktop/windows-language-pack-default-values
ara, err := windows.UTF16PtrFromString("00000401")
if err != nil {
t.Fatalf("UTF16PtrFromString failed: %v", err)
}
araLayout, err := windows.LoadKeyboardLayout(ara, windows.KLF_ACTIVATE)
if err != nil {
t.Fatalf("LoadKeyboardLayout failed: %v", err)
}

var keyState [256]byte
ret := windows.ToUnicodeEx(
0x41, // 'A' vkCode
0x1e, // 'A' scanCode
&keyState[0],
&utf16Buf[0],
int32(len(utf16Buf)),
0x4, // don't change keyboard state
araLayout,
)

if ret != 1 {
t.Errorf("ToUnicodeEx failed, wanted 1, got %d", ret)
}
if utf16Buf[0] != 'ش' {
t.Errorf("ToUnicodeEx failed, wanted 'ش', got %q", utf16Buf[0])
}
if err := windows.UnloadKeyboardLayout(araLayout); err != nil {
t.Errorf("UnloadKeyboardLayout failed: %v", err)
}
}
11 changes: 11 additions & 0 deletions windows/types_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -3418,3 +3418,14 @@ type DCB struct {
EvtChar byte
wReserved1 uint16
}

// Keyboard Layout Flags.
// See https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-loadkeyboardlayoutw
const (
KLF_ACTIVATE = 0x00000001
KLF_SUBSTITUTE_OK = 0x00000002
KLF_REORDER = 0x00000008
KLF_REPLACELANG = 0x00000010
KLF_NOTELLSHELL = 0x00000080
KLF_SETFORPROCESS = 0x00000100
)
33 changes: 33 additions & 0 deletions windows/zsyscall_windows.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit bce4cf7

Please sign in to comment.