Add keyboard layout support
This adds a keyboard layout switch callback and a query for the human-readable name of the current layout. Fixes #1201.
This commit is contained in:
parent
d7ae90a790
commit
761d7e7c4e
@ -119,6 +119,10 @@ information on what to include when reporting a bug.
|
||||
|
||||
## Changelog
|
||||
|
||||
- Added `glfwGetKeyboardLayoutName` for querying the name of the current
|
||||
keyboard layout (#1201)
|
||||
- Added `glfwSetKeyboardLayoutCallback` and `GLFWkeyboardlayoutfun` for
|
||||
receiving keyboard layout events (#1201)
|
||||
- Added `GLFW_RESIZE_NWSE_CURSOR`, `GLFW_RESIZE_NESW_CURSOR`,
|
||||
`GLFW_RESIZE_ALL_CURSOR` and `GLFW_NOT_ALLOWED_CURSOR` cursor shapes (#427)
|
||||
- Added `GLFW_RESIZE_EW_CURSOR` alias for `GLFW_HRESIZE_CURSOR` (#427)
|
||||
|
@ -230,6 +230,32 @@ specified key is `GLFW_KEY_UNKNOWN` then the scancode is used, otherwise it is
|
||||
ignored. This matches the behavior of the key callback, meaning the callback
|
||||
arguments can always be passed unmodified to this function.
|
||||
|
||||
The name of a key will not change unless the keyboard layout changes.
|
||||
|
||||
|
||||
@subsection keyboard_layout Keyboard layout
|
||||
|
||||
The human-readable name of the current keyboard layout is returned by @ref
|
||||
glfwGetKeyboardLayoutName.
|
||||
|
||||
If you wish to be notified when the keyboard layout changes, set a keyboard
|
||||
layout callback.
|
||||
|
||||
@code
|
||||
glfwSetKeyboardLayoutCallback(keyboard_layout_callback);
|
||||
@endcode
|
||||
|
||||
The callback is called when the new layout takes effect for the application,
|
||||
which on some platforms may not happen until one of its windows gets input
|
||||
focus.
|
||||
|
||||
@code
|
||||
void keyboard_layout_callback(void)
|
||||
{
|
||||
update_keyboard_layout_name(glfwGetKeyboardLayoutName());
|
||||
}
|
||||
@endcode
|
||||
|
||||
|
||||
@section input_mouse Mouse input
|
||||
|
||||
|
@ -9,6 +9,15 @@
|
||||
|
||||
@subsection features_34 New features in version 3.4
|
||||
|
||||
@subsubsection keyboard_layout_34 Keyboard layouts
|
||||
|
||||
GLFW can now notify when the keyboard layout has changed with @ref
|
||||
glfwSetKeyboardLayoutCallback and provides the human-readable name of the
|
||||
current layout with @ref glfwGetKeyboardLayoutName.
|
||||
|
||||
For more information, see @ref keyboard_layout.
|
||||
|
||||
|
||||
@subsubsection standard_cursors_34 More standard cursors
|
||||
|
||||
GLFW now provides the standard cursor shapes @ref GLFW_RESIZE_NWSE_CURSOR and
|
||||
|
@ -1270,6 +1270,23 @@ typedef struct GLFWcursor GLFWcursor;
|
||||
*/
|
||||
typedef void (* GLFWerrorfun)(int,const char*);
|
||||
|
||||
/*! @brief The function pointer type for keyboard layout callbacks.
|
||||
*
|
||||
* This is the function pointer type for keyboard layout callbacks. A keyboard
|
||||
* layout callback function has the following signature:
|
||||
* @code
|
||||
* void callback_name(void);
|
||||
* @endcode
|
||||
*
|
||||
* @sa @ref keyboard_layout
|
||||
* @sa @ref glfwSetKeyboardLayoutCallback
|
||||
*
|
||||
* @since Added in version 3.4.
|
||||
*
|
||||
* @ingroup input
|
||||
*/
|
||||
typedef void (* GLFWkeyboardlayoutfun)(void);
|
||||
|
||||
/*! @brief The function pointer type for window position callbacks.
|
||||
*
|
||||
* This is the function pointer type for window position callbacks. A window
|
||||
@ -4265,6 +4282,11 @@ GLFWAPI int glfwRawMouseMotionSupported(void);
|
||||
* non-printable keys are the same across layouts but depend on the application
|
||||
* language and should be localized along with other user interface text.
|
||||
*
|
||||
* The contents of the returned string may change when a keyboard
|
||||
* layout change event is received. Set a
|
||||
* [keyboard layout](@ref keyboard_layout) callback to be notified when the
|
||||
* layout changes.
|
||||
*
|
||||
* @param[in] key The key to query, or `GLFW_KEY_UNKNOWN`.
|
||||
* @param[in] scancode The scancode of the key to query.
|
||||
* @return The UTF-8 encoded, layout-specific name of the key, or `NULL`.
|
||||
@ -4272,9 +4294,6 @@ GLFWAPI int glfwRawMouseMotionSupported(void);
|
||||
* @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
|
||||
* GLFW_PLATFORM_ERROR.
|
||||
*
|
||||
* @remark The contents of the returned string may change when a keyboard
|
||||
* layout change event is received.
|
||||
*
|
||||
* @pointer_lifetime The returned string is allocated and freed by GLFW. You
|
||||
* should not free it yourself. It is valid until the library is terminated.
|
||||
*
|
||||
@ -4312,6 +4331,73 @@ GLFWAPI const char* glfwGetKeyName(int key, int scancode);
|
||||
*/
|
||||
GLFWAPI int glfwGetKeyScancode(int key);
|
||||
|
||||
/*! @brief Returns the human-readable name of the current keyboard layout.
|
||||
*
|
||||
* This function returns the human-readable name, encoded as UTF-8, of the
|
||||
* current keyboard layout. On some platforms this may not be updated until
|
||||
* one of the application's windows gets input focus.
|
||||
*
|
||||
* The keyboard layout name is intended to be shown to the user during text
|
||||
* input, especially in full screen applications.
|
||||
*
|
||||
* The name may be localized into the current operating system UI language. It
|
||||
* is provided by the operating system and may not be identical for a given
|
||||
* layout across platforms.
|
||||
*
|
||||
* @return The UTF-8 encoded name of the current keyboard layout, or `NULL` if
|
||||
* an [error](@ref error_handling) occurred.
|
||||
*
|
||||
* @errors Possible errors include @ref GLFW_PLATFORM_ERROR and @ref
|
||||
* GLFW_NOT_INITIALIZED.
|
||||
*
|
||||
* @pointer_lifetime The returned string is allocated and freed by GLFW. You
|
||||
* should not free it yourself. It is valid until the next call to this
|
||||
* function or the library is terminated.
|
||||
*
|
||||
* @thread_safety This function must only be called from the main thread.
|
||||
*
|
||||
* @sa @ref keyboard_layout
|
||||
* @sa @ref glfwSetKeyboardLayoutCallback
|
||||
*
|
||||
* @since Added in version 3.4.
|
||||
*
|
||||
* @ingroup input
|
||||
*/
|
||||
GLFWAPI const char* glfwGetKeyboardLayoutName(void);
|
||||
|
||||
/*! @brief Sets the keyboard layout callback.
|
||||
*
|
||||
* This function sets the keyboard layout callback, which is called when the
|
||||
* keyboard layout is changed. The name of the current layout is returned by
|
||||
* @ref glfwGetKeyboardLayoutName.
|
||||
*
|
||||
* On some platforms the keyboard layout event may not arrive until one of the
|
||||
* application's windows get input focus. Layout changes may not be reported
|
||||
* while other applications have input focus.
|
||||
*
|
||||
* @param[in] callback The new callback, or `NULL` to remove the currently set
|
||||
* callback.
|
||||
* @return The previously set callback, or `NULL` if no callback was set or the
|
||||
* library had not been [initialized](@ref intro_init).
|
||||
*
|
||||
* @callback_signature
|
||||
* @code
|
||||
* void function_name(void)
|
||||
* @endcode
|
||||
*
|
||||
* @errors Possible errors include @ref GLFW_NOT_INITIALIZED.
|
||||
*
|
||||
* @thread_safety This function must only be called from the main thread.
|
||||
*
|
||||
* @sa @ref keyboard_layout
|
||||
* @sa @ref glfwGetKeyboardLayoutName
|
||||
*
|
||||
* @since Added in version 3.4.
|
||||
*
|
||||
* @ingroup input
|
||||
*/
|
||||
GLFWAPI GLFWkeyboardlayoutfun glfwSetKeyboardLayoutCallback(GLFWkeyboardlayoutfun callback);
|
||||
|
||||
/*! @brief Returns the last reported state of a keyboard key for the specified
|
||||
* window.
|
||||
*
|
||||
|
@ -305,38 +305,6 @@ static void createKeyTables(void)
|
||||
}
|
||||
}
|
||||
|
||||
// Retrieve Unicode data for the current keyboard layout
|
||||
//
|
||||
static GLFWbool updateUnicodeDataNS(void)
|
||||
{
|
||||
if (_glfw.ns.inputSource)
|
||||
{
|
||||
CFRelease(_glfw.ns.inputSource);
|
||||
_glfw.ns.inputSource = NULL;
|
||||
_glfw.ns.unicodeData = nil;
|
||||
}
|
||||
|
||||
_glfw.ns.inputSource = TISCopyCurrentKeyboardLayoutInputSource();
|
||||
if (!_glfw.ns.inputSource)
|
||||
{
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR,
|
||||
"Cocoa: Failed to retrieve keyboard layout input source");
|
||||
return GLFW_FALSE;
|
||||
}
|
||||
|
||||
_glfw.ns.unicodeData =
|
||||
TISGetInputSourceProperty(_glfw.ns.inputSource,
|
||||
kTISPropertyUnicodeKeyLayoutData);
|
||||
if (!_glfw.ns.unicodeData)
|
||||
{
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR,
|
||||
"Cocoa: Failed to retrieve keyboard layout Unicode data");
|
||||
return GLFW_FALSE;
|
||||
}
|
||||
|
||||
return GLFW_TRUE;
|
||||
}
|
||||
|
||||
// Load HIToolbox.framework and the TIS symbols we need from it
|
||||
//
|
||||
static GLFWbool initializeTIS(void)
|
||||
@ -354,6 +322,15 @@ static GLFWbool initializeTIS(void)
|
||||
CFStringRef* kPropertyUnicodeKeyLayoutData =
|
||||
CFBundleGetDataPointerForName(_glfw.ns.tis.bundle,
|
||||
CFSTR("kTISPropertyUnicodeKeyLayoutData"));
|
||||
CFStringRef* kPropertyInputSourceID =
|
||||
CFBundleGetDataPointerForName(_glfw.ns.tis.bundle,
|
||||
CFSTR("kTISPropertyInputSourceID"));
|
||||
CFStringRef* kPropertyLocalizedName =
|
||||
CFBundleGetDataPointerForName(_glfw.ns.tis.bundle,
|
||||
CFSTR("kTISPropertyLocalizedName"));
|
||||
_glfw.ns.tis.CopyCurrentKeyboardInputSource =
|
||||
CFBundleGetFunctionPointerForName(_glfw.ns.tis.bundle,
|
||||
CFSTR("TISCopyCurrentKeyboardInputSource"));
|
||||
_glfw.ns.tis.CopyCurrentKeyboardLayoutInputSource =
|
||||
CFBundleGetFunctionPointerForName(_glfw.ns.tis.bundle,
|
||||
CFSTR("TISCopyCurrentKeyboardLayoutInputSource"));
|
||||
@ -365,6 +342,9 @@ static GLFWbool initializeTIS(void)
|
||||
CFSTR("LMGetKbdType"));
|
||||
|
||||
if (!kPropertyUnicodeKeyLayoutData ||
|
||||
!kPropertyInputSourceID ||
|
||||
!kPropertyLocalizedName ||
|
||||
!TISCopyCurrentKeyboardInputSource ||
|
||||
!TISCopyCurrentKeyboardLayoutInputSource ||
|
||||
!TISGetInputSourceProperty ||
|
||||
!LMGetKbdType)
|
||||
@ -376,8 +356,18 @@ static GLFWbool initializeTIS(void)
|
||||
|
||||
_glfw.ns.tis.kPropertyUnicodeKeyLayoutData =
|
||||
*kPropertyUnicodeKeyLayoutData;
|
||||
_glfw.ns.tis.kPropertyInputSourceID =
|
||||
*kPropertyInputSourceID;
|
||||
_glfw.ns.tis.kPropertyLocalizedName =
|
||||
*kPropertyLocalizedName;
|
||||
|
||||
return updateUnicodeDataNS();
|
||||
_glfw.ns.inputSource = TISCopyCurrentKeyboardInputSource();
|
||||
_glfw.ns.keyboardLayout = TISCopyCurrentKeyboardLayoutInputSource();
|
||||
_glfw.ns.unicodeData =
|
||||
TISGetInputSourceProperty(_glfw.ns.keyboardLayout,
|
||||
kTISPropertyUnicodeKeyLayoutData);
|
||||
|
||||
return GLFW_TRUE;
|
||||
}
|
||||
|
||||
@interface GLFWHelper : NSObject
|
||||
@ -387,7 +377,28 @@ static GLFWbool initializeTIS(void)
|
||||
|
||||
- (void)selectedKeyboardInputSourceChanged:(NSObject* )object
|
||||
{
|
||||
updateUnicodeDataNS();
|
||||
// The keyboard layout is needed for Unicode data which is the source of
|
||||
// GLFW key names on Cocoa (the generic input source may not have this)
|
||||
CFRelease(_glfw.ns.keyboardLayout);
|
||||
_glfw.ns.keyboardLayout = TISCopyCurrentKeyboardLayoutInputSource();
|
||||
_glfw.ns.unicodeData =
|
||||
TISGetInputSourceProperty(_glfw.ns.keyboardLayout,
|
||||
kTISPropertyUnicodeKeyLayoutData);
|
||||
|
||||
// The generic input source may be something higher level than a keyboard
|
||||
// layout and if so will provide a better layout name than the layout source
|
||||
const TISInputSourceRef source = TISCopyCurrentKeyboardInputSource();
|
||||
const CFStringRef newID =
|
||||
TISGetInputSourceProperty(source, kTISPropertyInputSourceID);
|
||||
const CFStringRef oldID =
|
||||
TISGetInputSourceProperty(_glfw.ns.inputSource, kTISPropertyInputSourceID);
|
||||
const CFComparisonResult result = CFStringCompare(oldID, newID, 0);
|
||||
CFRelease(_glfw.ns.inputSource);
|
||||
_glfw.ns.inputSource = source;
|
||||
|
||||
// Filter events as we may receive more than one per input source switch
|
||||
if (result != kCFCompareEqualTo)
|
||||
_glfwInputKeyboardLayout();
|
||||
}
|
||||
|
||||
- (void)doNothing:(id)object
|
||||
@ -567,11 +578,17 @@ void _glfwPlatformTerminate(void)
|
||||
{
|
||||
@autoreleasepool {
|
||||
|
||||
if (_glfw.ns.keyboardLayout)
|
||||
{
|
||||
CFRelease(_glfw.ns.keyboardLayout);
|
||||
_glfw.ns.keyboardLayout = NULL;
|
||||
_glfw.ns.unicodeData = NULL;
|
||||
}
|
||||
|
||||
if (_glfw.ns.inputSource)
|
||||
{
|
||||
CFRelease(_glfw.ns.inputSource);
|
||||
_glfw.ns.inputSource = NULL;
|
||||
_glfw.ns.unicodeData = nil;
|
||||
}
|
||||
|
||||
if (_glfw.ns.eventSource)
|
||||
@ -603,6 +620,7 @@ void _glfwPlatformTerminate(void)
|
||||
[NSEvent removeMonitor:_glfw.ns.keyUpMonitor];
|
||||
|
||||
free(_glfw.ns.clipboardString);
|
||||
free(_glfw.ns.keyboardLayoutName);
|
||||
|
||||
_glfwTerminateNSGL();
|
||||
_glfwTerminateJoysticksNS();
|
||||
|
@ -103,6 +103,10 @@ typedef VkResult (APIENTRY *PFN_vkCreateMetalSurfaceEXT)(VkInstance,const VkMeta
|
||||
|
||||
// HIToolbox.framework pointer typedefs
|
||||
#define kTISPropertyUnicodeKeyLayoutData _glfw.ns.tis.kPropertyUnicodeKeyLayoutData
|
||||
#define kTISPropertyInputSourceID _glfw.ns.tis.kPropertyInputSourceID
|
||||
#define kTISPropertyLocalizedName _glfw.ns.tis.kPropertyLocalizedName
|
||||
typedef TISInputSourceRef (*PFN_TISCopyCurrentKeyboardInputSource)(void);
|
||||
#define TISCopyCurrentKeyboardInputSource _glfw.ns.tis.CopyCurrentKeyboardInputSource
|
||||
typedef TISInputSourceRef (*PFN_TISCopyCurrentKeyboardLayoutInputSource)(void);
|
||||
#define TISCopyCurrentKeyboardLayoutInputSource _glfw.ns.tis.CopyCurrentKeyboardLayoutInputSource
|
||||
typedef void* (*PFN_TISGetInputSourceProperty)(TISInputSourceRef,CFStringRef);
|
||||
@ -144,8 +148,9 @@ typedef struct _GLFWlibraryNS
|
||||
id delegate;
|
||||
GLFWbool cursorHidden;
|
||||
TISInputSourceRef inputSource;
|
||||
TISInputSourceRef keyboardLayout;
|
||||
IOHIDManagerRef hidManager;
|
||||
id unicodeData;
|
||||
void* unicodeData;
|
||||
id helper;
|
||||
id keyUpMonitor;
|
||||
id nibObjects;
|
||||
@ -154,6 +159,7 @@ typedef struct _GLFWlibraryNS
|
||||
short int keycodes[256];
|
||||
short int scancodes[GLFW_KEY_LAST + 1];
|
||||
char* clipboardString;
|
||||
char* keyboardLayoutName;
|
||||
CGPoint cascadePoint;
|
||||
// Where to place the cursor when re-enabled
|
||||
double restoreCursorPosX, restoreCursorPosY;
|
||||
@ -162,10 +168,13 @@ typedef struct _GLFWlibraryNS
|
||||
|
||||
struct {
|
||||
CFBundleRef bundle;
|
||||
PFN_TISCopyCurrentKeyboardInputSource CopyCurrentKeyboardInputSource;
|
||||
PFN_TISCopyCurrentKeyboardLayoutInputSource CopyCurrentKeyboardLayoutInputSource;
|
||||
PFN_TISGetInputSourceProperty GetInputSourceProperty;
|
||||
PFN_LMGetKbdType GetKbdType;
|
||||
CFStringRef kPropertyUnicodeKeyLayoutData;
|
||||
CFStringRef kPropertyInputSourceID;
|
||||
CFStringRef kPropertyLocalizedName;
|
||||
} tis;
|
||||
|
||||
} _GLFWlibraryNS;
|
||||
|
@ -1516,6 +1516,12 @@ const char* _glfwPlatformGetScancodeName(int scancode)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!_glfw.ns.unicodeData)
|
||||
{
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR, "Cocoa: Keyboard Unicode data missing");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const int key = _glfw.ns.keycodes[scancode];
|
||||
|
||||
UInt32 deadKeyState = 0;
|
||||
@ -1559,6 +1565,26 @@ int _glfwPlatformGetKeyScancode(int key)
|
||||
return _glfw.ns.scancodes[key];
|
||||
}
|
||||
|
||||
const char* _glfwPlatformGetKeyboardLayoutName(void)
|
||||
{
|
||||
TISInputSourceRef source = TISCopyCurrentKeyboardInputSource();
|
||||
NSString* name = (__bridge NSString*)
|
||||
TISGetInputSourceProperty(source, kTISPropertyLocalizedName);
|
||||
if (!name)
|
||||
{
|
||||
CFRelease(source);
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR,
|
||||
"Cocoa: Failed to retrieve keyboard layout name");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
free(_glfw.ns.keyboardLayoutName);
|
||||
_glfw.ns.keyboardLayoutName = _glfw_strdup([name UTF8String]);
|
||||
|
||||
CFRelease(source);
|
||||
return _glfw.ns.keyboardLayoutName;
|
||||
}
|
||||
|
||||
int _glfwPlatformCreateCursor(_GLFWcursor* cursor,
|
||||
const GLFWimage* image,
|
||||
int xhot, int yhot)
|
||||
|
21
src/input.c
21
src/input.c
@ -256,6 +256,14 @@ static GLFWbool parseMapping(_GLFWmapping* mapping, const char* string)
|
||||
////// GLFW event API //////
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Notifies shared code of a keyboard layout change event
|
||||
//
|
||||
void _glfwInputKeyboardLayout(void)
|
||||
{
|
||||
if (_glfw.callbacks.layout)
|
||||
_glfw.callbacks.layout();
|
||||
}
|
||||
|
||||
// Notifies shared code of a physical key event
|
||||
//
|
||||
void _glfwInputKey(_GLFWwindow* window, int key, int scancode, int action, int mods)
|
||||
@ -626,6 +634,19 @@ GLFWAPI int glfwGetKeyScancode(int key)
|
||||
return _glfwPlatformGetKeyScancode(key);
|
||||
}
|
||||
|
||||
GLFWAPI const char* glfwGetKeyboardLayoutName(void)
|
||||
{
|
||||
_GLFW_REQUIRE_INIT_OR_RETURN(NULL);
|
||||
return _glfwPlatformGetKeyboardLayoutName();
|
||||
}
|
||||
|
||||
GLFWAPI GLFWkeyboardlayoutfun glfwSetKeyboardLayoutCallback(GLFWkeyboardlayoutfun cbfun)
|
||||
{
|
||||
_GLFW_REQUIRE_INIT_OR_RETURN(NULL);
|
||||
_GLFW_SWAP_POINTERS(_glfw.callbacks.layout, cbfun);
|
||||
return cbfun;
|
||||
}
|
||||
|
||||
GLFWAPI int glfwGetKey(GLFWwindow* handle, int key)
|
||||
{
|
||||
_GLFWwindow* window = (_GLFWwindow*) handle;
|
||||
|
@ -572,6 +572,7 @@ struct _GLFWlibrary
|
||||
struct {
|
||||
GLFWmonitorfun monitor;
|
||||
GLFWjoystickfun joystick;
|
||||
GLFWkeyboardlayoutfun layout;
|
||||
} callbacks;
|
||||
|
||||
// This is defined in the window API's platform.h
|
||||
@ -612,6 +613,7 @@ void _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor);
|
||||
|
||||
const char* _glfwPlatformGetScancodeName(int scancode);
|
||||
int _glfwPlatformGetKeyScancode(int key);
|
||||
const char* _glfwPlatformGetKeyboardLayoutName(void);
|
||||
|
||||
void _glfwPlatformFreeMonitor(_GLFWmonitor* monitor);
|
||||
void _glfwPlatformGetMonitorPos(_GLFWmonitor* monitor, int* xpos, int* ypos);
|
||||
@ -717,6 +719,7 @@ void _glfwInputWindowDamage(_GLFWwindow* window);
|
||||
void _glfwInputWindowCloseRequest(_GLFWwindow* window);
|
||||
void _glfwInputWindowMonitor(_GLFWwindow* window, _GLFWmonitor* monitor);
|
||||
|
||||
void _glfwInputKeyboardLayout(void);
|
||||
void _glfwInputKey(_GLFWwindow* window,
|
||||
int key, int scancode, int action, int mods);
|
||||
void _glfwInputChar(_GLFWwindow* window,
|
||||
|
@ -310,6 +310,11 @@ int _glfwPlatformGetKeyScancode(int key)
|
||||
return -1;
|
||||
}
|
||||
|
||||
const char* _glfwPlatformGetKeyboardLayoutName(void)
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
void _glfwPlatformGetRequiredInstanceExtensions(char** extensions)
|
||||
{
|
||||
}
|
||||
|
@ -602,6 +602,7 @@ void _glfwPlatformTerminate(void)
|
||||
SPIF_SENDCHANGE);
|
||||
|
||||
free(_glfw.win32.clipboardString);
|
||||
free(_glfw.win32.keyboardLayoutName);
|
||||
free(_glfw.win32.rawInput);
|
||||
|
||||
_glfwTerminateWGL();
|
||||
|
@ -331,6 +331,7 @@ typedef struct _GLFWlibraryWin32
|
||||
DWORD foregroundLockTimeout;
|
||||
int acquiredMonitorCount;
|
||||
char* clipboardString;
|
||||
char* keyboardLayoutName;
|
||||
short int keycodes[512];
|
||||
short int scancodes[GLFW_KEY_LAST + 1];
|
||||
char keynames[GLFW_KEY_LAST + 1][5];
|
||||
|
@ -645,6 +645,7 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg,
|
||||
case WM_INPUTLANGCHANGE:
|
||||
{
|
||||
_glfwUpdateKeyNamesWin32();
|
||||
_glfwInputKeyboardLayout();
|
||||
break;
|
||||
}
|
||||
|
||||
@ -2042,6 +2043,48 @@ int _glfwPlatformGetKeyScancode(int key)
|
||||
return _glfw.win32.scancodes[key];
|
||||
}
|
||||
|
||||
const char* _glfwPlatformGetKeyboardLayoutName(void)
|
||||
{
|
||||
WCHAR klid[KL_NAMELENGTH];
|
||||
int size;
|
||||
LCID lcid;
|
||||
WCHAR* language;
|
||||
|
||||
if (!GetKeyboardLayoutNameW(klid))
|
||||
{
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR,
|
||||
"Win32: Failed to retrieve keyboard layout name");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// NOTE: We only care about the language part of the keyboard layout ID
|
||||
lcid = MAKELCID(LANGIDFROMLCID(wcstoul(klid, NULL, 16)), 0);
|
||||
|
||||
size = GetLocaleInfoW(lcid, LOCALE_SLANGUAGE, NULL, 0);
|
||||
if (!size)
|
||||
{
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR,
|
||||
"Win32: Failed to retrieve keyboard layout name length");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
language = calloc(size, sizeof(WCHAR));
|
||||
|
||||
if (!GetLocaleInfoW(lcid, LOCALE_SLANGUAGE, language, size))
|
||||
{
|
||||
free(language);
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR,
|
||||
"Win32: Failed to translate keyboard layout name");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
free(_glfw.win32.keyboardLayoutName);
|
||||
_glfw.win32.keyboardLayoutName = _glfwCreateUTF8FromWideStringWin32(language);
|
||||
free(language);
|
||||
|
||||
return _glfw.win32.keyboardLayoutName;
|
||||
}
|
||||
|
||||
int _glfwPlatformCreateCursor(_GLFWcursor* cursor,
|
||||
const GLFWimage* image,
|
||||
int xhot, int yhot)
|
||||
|
@ -640,6 +640,12 @@ static void keyboardHandleModifiers(void* data,
|
||||
if (mask & _glfw.wl.xkb.numLockMask)
|
||||
modifiers |= GLFW_MOD_NUM_LOCK;
|
||||
_glfw.wl.xkb.modifiers = modifiers;
|
||||
|
||||
if (_glfw.wl.xkb.group != group)
|
||||
{
|
||||
_glfw.wl.xkb.group = group;
|
||||
_glfwInputKeyboardLayout();
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef WL_KEYBOARD_REPEAT_INFO_SINCE_VERSION
|
||||
@ -1101,6 +1107,8 @@ int _glfwPlatformInit(void)
|
||||
_glfw_dlsym(_glfw.wl.xkb.handle, "xkb_state_update_mask");
|
||||
_glfw.wl.xkb.state_serialize_mods = (PFN_xkb_state_serialize_mods)
|
||||
_glfw_dlsym(_glfw.wl.xkb.handle, "xkb_state_serialize_mods");
|
||||
_glfw.wl.xkb.keymap_layout_get_name = (PFN_xkb_keymap_layout_get_name)
|
||||
_glfw_dlsym(_glfw.wl.xkb.handle, "xkb_keymap_layout_get_name");
|
||||
|
||||
#ifdef HAVE_XKBCOMMON_COMPOSE_H
|
||||
_glfw.wl.xkb.compose_table_new_from_locale = (PFN_xkb_compose_table_new_from_locale)
|
||||
@ -1300,6 +1308,8 @@ void _glfwPlatformTerminate(void)
|
||||
free(_glfw.wl.clipboardString);
|
||||
if (_glfw.wl.clipboardSendString)
|
||||
free(_glfw.wl.clipboardSendString);
|
||||
if (_glfw.wl.keyboardLayoutName)
|
||||
free(_glfw.wl.keyboardLayoutName);
|
||||
}
|
||||
|
||||
const char* _glfwPlatformGetVersionString(void)
|
||||
|
@ -117,6 +117,7 @@ typedef void (* PFN_xkb_state_unref)(struct xkb_state*);
|
||||
typedef int (* PFN_xkb_state_key_get_syms)(struct xkb_state*, xkb_keycode_t, const xkb_keysym_t**);
|
||||
typedef enum xkb_state_component (* PFN_xkb_state_update_mask)(struct xkb_state*, xkb_mod_mask_t, xkb_mod_mask_t, xkb_mod_mask_t, xkb_layout_index_t, xkb_layout_index_t, xkb_layout_index_t);
|
||||
typedef xkb_mod_mask_t (* PFN_xkb_state_serialize_mods)(struct xkb_state*, enum xkb_state_component);
|
||||
typedef const char * (* PFN_xkb_keymap_layout_get_name)(struct xkb_keymap*,xkb_layout_index_t);
|
||||
#define xkb_context_new _glfw.wl.xkb.context_new
|
||||
#define xkb_context_unref _glfw.wl.xkb.context_unref
|
||||
#define xkb_keymap_new_from_string _glfw.wl.xkb.keymap_new_from_string
|
||||
@ -128,6 +129,7 @@ typedef xkb_mod_mask_t (* PFN_xkb_state_serialize_mods)(struct xkb_state*, enum
|
||||
#define xkb_state_key_get_syms _glfw.wl.xkb.state_key_get_syms
|
||||
#define xkb_state_update_mask _glfw.wl.xkb.state_update_mask
|
||||
#define xkb_state_serialize_mods _glfw.wl.xkb.state_serialize_mods
|
||||
#define xkb_keymap_layout_get_name _glfw.wl.xkb.keymap_layout_get_name
|
||||
|
||||
#ifdef HAVE_XKBCOMMON_COMPOSE_H
|
||||
typedef struct xkb_compose_table* (* PFN_xkb_compose_table_new_from_locale)(struct xkb_context*, const char*, enum xkb_compose_compile_flags);
|
||||
@ -259,6 +261,7 @@ typedef struct _GLFWlibraryWayland
|
||||
size_t clipboardSize;
|
||||
char* clipboardSendString;
|
||||
size_t clipboardSendSize;
|
||||
char* keyboardLayoutName;
|
||||
int timerfd;
|
||||
short int keycodes[256];
|
||||
short int scancodes[GLFW_KEY_LAST + 1];
|
||||
@ -280,6 +283,7 @@ typedef struct _GLFWlibraryWayland
|
||||
xkb_mod_mask_t capsLockMask;
|
||||
xkb_mod_mask_t numLockMask;
|
||||
unsigned int modifiers;
|
||||
xkb_layout_index_t group;
|
||||
|
||||
PFN_xkb_context_new context_new;
|
||||
PFN_xkb_context_unref context_unref;
|
||||
@ -292,6 +296,7 @@ typedef struct _GLFWlibraryWayland
|
||||
PFN_xkb_state_key_get_syms state_key_get_syms;
|
||||
PFN_xkb_state_update_mask state_update_mask;
|
||||
PFN_xkb_state_serialize_mods state_serialize_mods;
|
||||
PFN_xkb_keymap_layout_get_name keymap_layout_get_name;
|
||||
|
||||
#ifdef HAVE_XKBCOMMON_COMPOSE_H
|
||||
PFN_xkb_compose_table_new_from_locale compose_table_new_from_locale;
|
||||
|
@ -1194,6 +1194,29 @@ int _glfwPlatformGetKeyScancode(int key)
|
||||
return _glfw.wl.scancodes[key];
|
||||
}
|
||||
|
||||
const char* _glfwPlatformGetKeyboardLayoutName(void)
|
||||
{
|
||||
if (!_glfw.wl.xkb.keymap)
|
||||
{
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR,
|
||||
"Wayland: Keymap missing");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const char* name = xkb_keymap_layout_get_name(_glfw.wl.xkb.keymap,
|
||||
_glfw.wl.xkb.group);
|
||||
if (!name)
|
||||
{
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR,
|
||||
"Wayland: Failed to query keyboard layout name");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
free(_glfw.wl.keyboardLayoutName);
|
||||
_glfw.wl.keyboardLayoutName = _glfw_strdup(name);
|
||||
return _glfw.wl.keyboardLayoutName;
|
||||
}
|
||||
|
||||
int _glfwPlatformCreateCursor(_GLFWcursor* cursor,
|
||||
const GLFWimage* image,
|
||||
int xhot, int yhot)
|
||||
|
@ -1153,6 +1153,8 @@ int _glfwPlatformInit(void)
|
||||
_glfw_dlsym(_glfw.x11.xlib.handle, "XFreeCursor");
|
||||
_glfw.x11.xlib.FreeEventData = (PFN_XFreeEventData)
|
||||
_glfw_dlsym(_glfw.x11.xlib.handle, "XFreeEventData");
|
||||
_glfw.x11.xlib.GetAtomName = (PFN_XGetAtomName)
|
||||
_glfw_dlsym(_glfw.x11.xlib.handle, "XGetAtomName");
|
||||
_glfw.x11.xlib.GetErrorText = (PFN_XGetErrorText)
|
||||
_glfw_dlsym(_glfw.x11.xlib.handle, "XGetErrorText");
|
||||
_glfw.x11.xlib.GetEventData = (PFN_XGetEventData)
|
||||
@ -1263,6 +1265,8 @@ int _glfwPlatformInit(void)
|
||||
_glfw_dlsym(_glfw.x11.xlib.handle, "XVisualIDFromVisual");
|
||||
_glfw.x11.xlib.WarpPointer = (PFN_XWarpPointer)
|
||||
_glfw_dlsym(_glfw.x11.xlib.handle, "XWarpPointer");
|
||||
_glfw.x11.xkb.AllocKeyboard = (PFN_XkbAllocKeyboard)
|
||||
_glfw_dlsym(_glfw.x11.xlib.handle, "XkbAllocKeyboard");
|
||||
_glfw.x11.xkb.FreeKeyboard = (PFN_XkbFreeKeyboard)
|
||||
_glfw_dlsym(_glfw.x11.xlib.handle, "XkbFreeKeyboard");
|
||||
_glfw.x11.xkb.FreeNames = (PFN_XkbFreeNames)
|
||||
@ -1376,6 +1380,9 @@ void _glfwPlatformTerminate(void)
|
||||
free(_glfw.x11.primarySelectionString);
|
||||
free(_glfw.x11.clipboardString);
|
||||
|
||||
if (_glfw.x11.keyboardLayoutName)
|
||||
XFree(_glfw.x11.keyboardLayoutName);
|
||||
|
||||
XUnregisterIMInstantiateCallback(_glfw.x11.display,
|
||||
NULL, NULL, NULL,
|
||||
inputMethodInstantiateCallback,
|
||||
|
@ -76,6 +76,7 @@ typedef int (* PFN_XFree)(void*);
|
||||
typedef int (* PFN_XFreeColormap)(Display*,Colormap);
|
||||
typedef int (* PFN_XFreeCursor)(Display*,Cursor);
|
||||
typedef void (* PFN_XFreeEventData)(Display*,XGenericEventCookie*);
|
||||
typedef char* (* PFN_XGetAtomName)(Display*,Atom);
|
||||
typedef int (* PFN_XGetErrorText)(Display*,int,char*,int);
|
||||
typedef Bool (* PFN_XGetEventData)(Display*,XGenericEventCookie*);
|
||||
typedef char* (* PFN_XGetICValues)(XIC,...);
|
||||
@ -133,6 +134,7 @@ typedef VisualID (* PFN_XVisualIDFromVisual)(Visual*);
|
||||
typedef int (* PFN_XWarpPointer)(Display*,Window,Window,int,int,unsigned int,unsigned int,int,int);
|
||||
typedef void (* PFN_XkbFreeKeyboard)(XkbDescPtr,unsigned int,Bool);
|
||||
typedef void (* PFN_XkbFreeNames)(XkbDescPtr,unsigned int,Bool);
|
||||
typedef XkbDescPtr (* PFN_XkbAllocKeyboard)(void);
|
||||
typedef XkbDescPtr (* PFN_XkbGetMap)(Display*,unsigned int,unsigned int);
|
||||
typedef Status (* PFN_XkbGetNames)(Display*,unsigned int,XkbDescPtr);
|
||||
typedef Status (* PFN_XkbGetState)(Display*,unsigned int,XkbStatePtr);
|
||||
@ -176,6 +178,7 @@ typedef void (* PFN_Xutf8SetWMProperties)(Display*,Window,const char*,const char
|
||||
#define XFreeColormap _glfw.x11.xlib.FreeColormap
|
||||
#define XFreeCursor _glfw.x11.xlib.FreeCursor
|
||||
#define XFreeEventData _glfw.x11.xlib.FreeEventData
|
||||
#define XGetAtomName _glfw.x11.xlib.GetAtomName
|
||||
#define XGetErrorText _glfw.x11.xlib.GetErrorText
|
||||
#define XGetEventData _glfw.x11.xlib.GetEventData
|
||||
#define XGetICValues _glfw.x11.xlib.GetICValues
|
||||
@ -231,6 +234,7 @@ typedef void (* PFN_Xutf8SetWMProperties)(Display*,Window,const char*,const char
|
||||
#define XUnsetICFocus _glfw.x11.xlib.UnsetICFocus
|
||||
#define XVisualIDFromVisual _glfw.x11.xlib.VisualIDFromVisual
|
||||
#define XWarpPointer _glfw.x11.xlib.WarpPointer
|
||||
#define XkbAllocKeyboard _glfw.x11.xkb.AllocKeyboard
|
||||
#define XkbFreeKeyboard _glfw.x11.xkb.FreeKeyboard
|
||||
#define XkbFreeNames _glfw.x11.xkb.FreeNames
|
||||
#define XkbGetMap _glfw.x11.xkb.GetMap
|
||||
@ -442,6 +446,7 @@ typedef struct _GLFWlibraryX11
|
||||
short int keycodes[256];
|
||||
// GLFW key to X11 keycode LUT
|
||||
short int scancodes[GLFW_KEY_LAST + 1];
|
||||
char* keyboardLayoutName;
|
||||
// Where to place the cursor when re-enabled
|
||||
double restoreCursorPosX, restoreCursorPosY;
|
||||
// The window whose disabled cursor mode is active
|
||||
@ -533,6 +538,7 @@ typedef struct _GLFWlibraryX11
|
||||
PFN_XFreeColormap FreeColormap;
|
||||
PFN_XFreeCursor FreeCursor;
|
||||
PFN_XFreeEventData FreeEventData;
|
||||
PFN_XGetAtomName GetAtomName;
|
||||
PFN_XGetErrorText GetErrorText;
|
||||
PFN_XGetEventData GetEventData;
|
||||
PFN_XGetICValues GetICValues;
|
||||
@ -638,6 +644,7 @@ typedef struct _GLFWlibraryX11
|
||||
int major;
|
||||
int minor;
|
||||
unsigned int group;
|
||||
PFN_XkbAllocKeyboard AllocKeyboard;
|
||||
PFN_XkbFreeKeyboard FreeKeyboard;
|
||||
PFN_XkbFreeNames FreeNames;
|
||||
PFN_XkbGetMap GetMap;
|
||||
|
@ -1184,6 +1184,7 @@ static void processEvent(XEvent *event)
|
||||
(((XkbEvent*) event)->state.changed & XkbGroupStateMask))
|
||||
{
|
||||
_glfw.x11.xkb.group = ((XkbEvent*) event)->state.group;
|
||||
_glfwInputKeyboardLayout();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2920,6 +2921,42 @@ int _glfwPlatformGetKeyScancode(int key)
|
||||
return _glfw.x11.scancodes[key];
|
||||
}
|
||||
|
||||
const char* _glfwPlatformGetKeyboardLayoutName(void)
|
||||
{
|
||||
if (!_glfw.x11.xkb.available)
|
||||
{
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR,
|
||||
"X11: XKB extension required for keyboard layout names");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
XkbStateRec state = {0};
|
||||
XkbGetState(_glfw.x11.display, XkbUseCoreKbd, &state);
|
||||
|
||||
XkbDescPtr desc = XkbAllocKeyboard();
|
||||
if (XkbGetNames(_glfw.x11.display, XkbGroupNamesMask, desc) != Success)
|
||||
{
|
||||
XkbFreeKeyboard(desc, 0, True);
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR,
|
||||
"X11: Failed to retrieve keyboard layout names");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const Atom atom = desc->names->groups[state.group];
|
||||
XkbFreeKeyboard(desc, 0, True);
|
||||
|
||||
if (atom == None)
|
||||
{
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR,
|
||||
"X11: Name missing for current keyboard layout");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
free(_glfw.x11.keyboardLayoutName);
|
||||
_glfw.x11.keyboardLayoutName = XGetAtomName(_glfw.x11.display, atom);
|
||||
return _glfw.x11.keyboardLayoutName;
|
||||
}
|
||||
|
||||
int _glfwPlatformCreateCursor(_GLFWcursor* cursor,
|
||||
const GLFWimage* image,
|
||||
int xhot, int yhot)
|
||||
|
@ -465,6 +465,12 @@ static void drop_callback(GLFWwindow* window, int count, const char* paths[])
|
||||
printf(" %i: \"%s\"\n", i, paths[i]);
|
||||
}
|
||||
|
||||
static void keyboard_layout_callback(void)
|
||||
{
|
||||
printf("%08x at %0.3f: Keyboard layout changed to \'%s\'\n",
|
||||
counter++, glfwGetTime(), glfwGetKeyboardLayoutName());
|
||||
}
|
||||
|
||||
static void monitor_callback(GLFWmonitor* monitor, int event)
|
||||
{
|
||||
if (event == GLFW_CONNECTED)
|
||||
@ -546,6 +552,7 @@ int main(int argc, char** argv)
|
||||
|
||||
glfwSetMonitorCallback(monitor_callback);
|
||||
glfwSetJoystickCallback(joystick_callback);
|
||||
glfwSetKeyboardLayoutCallback(keyboard_layout_callback);
|
||||
|
||||
while ((ch = getopt(argc, argv, "hfn:")) != -1)
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user