Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
194 commits
Select commit Hold shift + click to select a range
c76f899
Attempt a conversion to N-API
savetheclocktower Oct 28, 2024
e777c8f
Attempt to modernize CI
savetheclocktower Oct 28, 2024
9ae6e2d
Fix some Linux issues
savetheclocktower Oct 28, 2024
19c46df
Fix some Windows issues
savetheclocktower Oct 28, 2024
cd868ac
More Linux fixes
savetheclocktower Oct 28, 2024
cc21053
Further Linux fixes
savetheclocktower Oct 28, 2024
fda6f68
Windows again
savetheclocktower Oct 28, 2024
ce8e85e
Typo
savetheclocktower Oct 28, 2024
74b9dd4
Windows again (again)
savetheclocktower Oct 28, 2024
4c7db63
Missing function
savetheclocktower Oct 28, 2024
f23da3c
Test in Node 16
savetheclocktower Oct 28, 2024
37ecffd
Use `node-addon-api` v7 (for Node 16 support)
savetheclocktower Oct 28, 2024
d74f393
Let's try even earlier
savetheclocktower Oct 28, 2024
bf201eb
Install `setuptools` in CI
savetheclocktower Oct 28, 2024
2aa649a
Rename module (no hyphens!)
savetheclocktower Oct 28, 2024
98b6547
Don't install `setuptools` on macOS
savetheclocktower Oct 28, 2024
96ee7ae
Use Homebrew for `setuptools` on macOS
savetheclocktower Oct 28, 2024
5e3a076
How about this
savetheclocktower Oct 28, 2024
1a5913e
Embrace terseness
savetheclocktower Feb 5, 2025
0813f83
Experimentation for wayland
savetheclocktower May 9, 2025
29f0fd1
Install CI dependencies
savetheclocktower May 9, 2025
b6400fe
pleeeeeeease
savetheclocktower May 9, 2025
b7f67e8
Another dependency
savetheclocktower May 9, 2025
1093027
More
savetheclocktower May 9, 2025
18b609c
Tweaked approach
savetheclocktower May 9, 2025
ff08dec
Forgot
savetheclocktower May 9, 2025
86c8e72
Typo
savetheclocktower May 9, 2025
12e61a6
Sanity check the Linux locale reporting
savetheclocktower May 9, 2025
e53a4bc
Force the Wayland path
savetheclocktower May 9, 2025
64b6ea9
Troubleshooting
savetheclocktower May 9, 2025
7448374
More dependencies
savetheclocktower May 9, 2025
59e44ea
Update `binding.gyp`
savetheclocktower May 9, 2025
77f3c93
Don't make strings out of null
savetheclocktower May 9, 2025
dc0d59f
Try again
savetheclocktower May 9, 2025
f61e13e
Add logging
savetheclocktower May 9, 2025
c8aa4e4
Include
savetheclocktower May 9, 2025
506dd6c
List all layouts
savetheclocktower May 9, 2025
78306d5
oops
savetheclocktower May 9, 2025
ecaeaef
Nah
savetheclocktower May 9, 2025
9b28736
Just use layout name for now
savetheclocktower May 9, 2025
bbba85f
More logging
savetheclocktower May 9, 2025
75744b5
Even more logging
savetheclocktower May 9, 2025
b3e4c8c
Forget about `getCurrentKeymap` for now
savetheclocktower May 9, 2025
81aadcf
Probably broken
savetheclocktower May 9, 2025
c6e24d3
oops
savetheclocktower May 9, 2025
442f45c
Missing parameters
savetheclocktower May 9, 2025
7cb01ae
Add missing fn
savetheclocktower May 9, 2025
013d28e
Leftover code
savetheclocktower May 9, 2025
f5e403e
Fixes
savetheclocktower May 9, 2025
34bdac4
Base state
savetheclocktower May 9, 2025
92c0590
Missing param
savetheclocktower May 9, 2025
1d39c62
Re-enable keymap test
savetheclocktower May 9, 2025
cc2cdc1
Log failures
savetheclocktower May 9, 2025
64c7e27
Disable layout spec
savetheclocktower May 9, 2025
58601da
Log keymap acquisition
savetheclocktower May 9, 2025
2116ff4
Troubleshooting
savetheclocktower May 9, 2025
cb64d8d
Big swing!
savetheclocktower May 10, 2025
7b5da0c
Fixes
savetheclocktower May 10, 2025
c9472ce
More fixes
savetheclocktower May 10, 2025
c9aeb20
Again
savetheclocktower May 10, 2025
faab89f
Add setup logging
savetheclocktower May 10, 2025
2866912
oof
savetheclocktower May 10, 2025
cea0537
Logging inside `registry_global`
savetheclocktower May 10, 2025
3a9fa19
more
savetheclocktower May 10, 2025
3b4295f
x
savetheclocktower May 10, 2025
dbdcd69
Need the context!
savetheclocktower May 10, 2025
d67766b
And in this other place!
savetheclocktower May 10, 2025
ad93377
Fix key character retrieval
savetheclocktower May 10, 2025
864f219
oops
savetheclocktower May 10, 2025
b152d1a
Don't think I should be adding 8 here
savetheclocktower May 10, 2025
1d52b97
Attempt to implement `getCurrentKeyboardLayout`
savetheclocktower May 10, 2025
aa171bf
oops
savetheclocktower May 10, 2025
b603dee
more oops
savetheclocktower May 10, 2025
5fc4478
oops again
savetheclocktower May 10, 2025
53c8e8b
try this
savetheclocktower May 10, 2025
fd75e1a
y
savetheclocktower May 10, 2025
f6f1b61
Is this it?
savetheclocktower May 10, 2025
732483f
Identify layout by most-used layouts for various test keys
savetheclocktower May 10, 2025
814c332
Fixes
savetheclocktower May 10, 2025
8e10707
Add `$`
savetheclocktower May 10, 2025
5b8a66b
Make it `#`
savetheclocktower May 10, 2025
8bc2595
Logging
savetheclocktower May 10, 2025
c0867e9
Don't use current keyboard state
savetheclocktower May 10, 2025
d8d5c78
typo
savetheclocktower May 10, 2025
1959c13
ugh
savetheclocktower May 10, 2025
6139caf
unused
savetheclocktower May 10, 2025
4899582
Add index
savetheclocktower May 10, 2025
c30141b
This is nuts
savetheclocktower May 10, 2025
41fbbea
fix
savetheclocktower May 10, 2025
89fcae9
Stop with the plus-eight stuff
savetheclocktower May 10, 2025
71d1a3f
orig
savetheclocktower May 10, 2025
2125336
sigh
savetheclocktower May 10, 2025
0a16abd
Try this
savetheclocktower May 10, 2025
769474c
Debug the keymap string
savetheclocktower May 10, 2025
8485ed9
Resurrect old layout approach
savetheclocktower May 10, 2025
f94d7b2
Paranoia
savetheclocktower May 10, 2025
a3876e0
Simplify
savetheclocktower May 10, 2025
fe468f3
Attempt to wire up notifications
savetheclocktower May 10, 2025
86d89c3
Typo
savetheclocktower May 10, 2025
dbbab5b
Simplify cleanup
savetheclocktower May 10, 2025
add4229
Use the `env` as the data
savetheclocktower May 10, 2025
beeb718
->
savetheclocktower May 10, 2025
d701f4b
again
savetheclocktower May 10, 2025
32cbecd
Unused
savetheclocktower May 10, 2025
645e7b5
stuff
savetheclocktower May 10, 2025
54dcfca
oops
savetheclocktower May 10, 2025
df48c89
Include `libuv`
savetheclocktower May 10, 2025
16aa59a
how about this
savetheclocktower May 10, 2025
0be18cc
Move it around
savetheclocktower May 10, 2025
fdfca68
How about this
savetheclocktower May 10, 2025
0a0e2de
This
savetheclocktower May 10, 2025
9bfa42c
fixes
savetheclocktower May 10, 2025
e7004c9
pardon me
savetheclocktower May 10, 2025
a73cde3
How about this
savetheclocktower May 10, 2025
760acae
Rework callback
savetheclocktower May 10, 2025
77eb553
duh
savetheclocktower May 10, 2025
de53629
Add test script
savetheclocktower May 10, 2025
e83320c
Wat?
savetheclocktower May 10, 2025
97d904f
Sanity check
savetheclocktower May 10, 2025
266b0cd
Remove dumb function
savetheclocktower May 10, 2025
178fd5c
Reintroduce polling
savetheclocktower May 10, 2025
37c0ed2
I am smart
savetheclocktower May 10, 2025
7c34dcb
ugh
savetheclocktower May 10, 2025
4cfd766
Another test
savetheclocktower May 10, 2025
06b64a1
More logging
savetheclocktower May 11, 2025
5e09020
Prioritize `AltGr` over `Alt`
savetheclocktower May 11, 2025
a397527
How about this
savetheclocktower May 11, 2025
201d085
Add modifier logging
savetheclocktower May 11, 2025
e520bad
Log the keymap string again
savetheclocktower May 11, 2025
5c9bb99
Remove hallucination
savetheclocktower May 11, 2025
79c6e6e
oops
savetheclocktower May 11, 2025
df65b39
Try another strategy
savetheclocktower May 11, 2025
aa3df7e
Fixes
savetheclocktower May 11, 2025
3c31cad
Test script
savetheclocktower May 11, 2025
41d4649
Logging
savetheclocktower May 11, 2025
50ea72e
Feels like this will be no better?
savetheclocktower May 11, 2025
6c7423c
oops
savetheclocktower May 11, 2025
da80702
Try this
savetheclocktower May 11, 2025
e345213
Getting the layout correctly?
savetheclocktower May 11, 2025
8058700
Try weird thing
savetheclocktower May 11, 2025
9462147
How about this
savetheclocktower May 11, 2025
a94d95b
Huh?
savetheclocktower May 11, 2025
9335be1
No, YOU’RE crazy
savetheclocktower May 11, 2025
6e6b1cb
OK
savetheclocktower May 11, 2025
7ebd37e
Makes no sense
savetheclocktower May 11, 2025
2a66e3b
cmon
savetheclocktower May 11, 2025
fd3659f
sigh
savetheclocktower May 11, 2025
5eeec8d
Wits’ end
savetheclocktower May 11, 2025
9901eda
sanity
savetheclocktower May 11, 2025
d570876
hard-code
savetheclocktower May 11, 2025
bea6daf
try this instead
savetheclocktower May 11, 2025
1434140
how about this
savetheclocktower May 11, 2025
6519081
k
savetheclocktower May 11, 2025
eae41b2
desperate
savetheclocktower May 11, 2025
f434ef0
hope
savetheclocktower May 11, 2025
71ddc84
sad
savetheclocktower May 11, 2025
437348c
Not use instance data
savetheclocktower May 11, 2025
a8b4e5b
fix
savetheclocktower May 11, 2025
ad709ef
?
savetheclocktower May 11, 2025
7746537
How about this hack
savetheclocktower May 11, 2025
631c677
Does this work?
savetheclocktower May 11, 2025
e56807b
Here we go
savetheclocktower May 11, 2025
ca46b3c
wat?
savetheclocktower May 11, 2025
b5006e5
k
savetheclocktower May 11, 2025
e1dbe77
declare
savetheclocktower May 11, 2025
c8c2c2c
Use C++ strings
savetheclocktower May 11, 2025
18acdaa
OK
savetheclocktower May 11, 2025
16fb24e
oops
savetheclocktower May 11, 2025
322b0ad
Grab the current value in the JS callback
savetheclocktower May 11, 2025
de2616c
Simplify
savetheclocktower May 11, 2025
bc7ce40
Streamlining 1
savetheclocktower May 11, 2025
1d37e21
Conditional compilation
savetheclocktower May 11, 2025
02f9830
Seems unnecessary
savetheclocktower May 11, 2025
10a1318
Fix Mac CI
savetheclocktower May 11, 2025
3ff6f3f
Fix CI on Windows
savetheclocktower May 11, 2025
0fa0575
Use C++17
savetheclocktower May 11, 2025
b4543b3
oops
savetheclocktower May 11, 2025
e3dd9aa
Fix Linux
savetheclocktower May 11, 2025
5cc6aa1
Remove `FailOnWaylandSetup`
savetheclocktower May 11, 2025
f50572c
Update README
savetheclocktower May 11, 2025
db06f00
Update README again
savetheclocktower May 11, 2025
70f5fd8
Rename function for consistency
savetheclocktower May 11, 2025
a0136ec
Cleanup
savetheclocktower May 13, 2025
ce99853
Remove debug function
savetheclocktower May 13, 2025
e8b14ab
Ignore `.atom` folder
savetheclocktower May 13, 2025
120985c
Install wayland prerequisites in CI
savetheclocktower May 13, 2025
beaa51b
(oops)
savetheclocktower May 13, 2025
3c8ecb7
Bold CI experiment
savetheclocktower May 13, 2025
4356118
Fix package names
savetheclocktower May 13, 2025
eefa7cf
Temporary logging
savetheclocktower May 13, 2025
8c02fd1
Revert changes (I'll tilt at the CI windmill later)
savetheclocktower May 13, 2025
5b6ae05
Merge branch 'master' of github.com:pulsar-edit/keyboard-layout into …
savetheclocktower Sep 1, 2025
6035575
Fix syntax in publish workflow
savetheclocktower Sep 1, 2025
a39d92f
Fix publish workflow dependency
savetheclocktower Sep 1, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 40 additions & 4 deletions .github/workflows/ci.yml
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Seems okay to my eyes. 👍

Original file line number Diff line number Diff line change
Expand Up @@ -17,18 +17,54 @@ jobs:
- windows-latest
node_version:
- 16
- 20
steps:
- uses: actions/checkout@v4

- name: Install dependencies
if: ${{ runner.os == 'Linux' }}
run: sudo apt-get update && sudo apt-get install libx11-dev libxkbfile-dev libwayland-dev libxkbcommon-dev libxkbcommon-x11-dev libxkbcommon0 xkb-data

- name: Set up locale
if: ${{ runner.os == 'Linux' }}
run: |
sudo apt-get update
sudo apt-get install -y locales
sudo locale-gen fr_FR.UTF-8 # Example: French locale
sudo update-locale LANG=fr_FR.UTF-8 LC_ALL=fr_FR.UTF-8
echo "LANG=fr_FR.UTF-8" | sudo tee -a /etc/environment
echo "LC_ALL=fr_FR.UTF-8" | sudo tee -a /etc/environment
# Apply and verify the change
source /etc/environment
locale

- name: Set keyboard layout
if: ${{ runner.os == 'Linux' }}
run: |
sudo apt-get update
sudo apt-get install -y console-setup keyboard-configuration
# Set keyboard layout non-interactively
sudo sed -i 's/XKBLAYOUT=.*/XKBLAYOUT="fr"/' /etc/default/keyboard
sudo dpkg-reconfigure -f noninteractive keyboard-configuration
# Apply the changes
sudo setupcon
echo "Current locale:"
locale
echo "Current keyboard layout:"
cat /etc/default/keyboard | grep XKBLAYOUT

- name: Report session type
if: ${{ runner.os == 'Linux' }}
run: |
echo "X or Wayland? $XKB_SESSION_TYPE"
echo "WAYLAND_DISPLAY? $WAYLAND_DISPLAY"
echo "DISPLAY?: $DISPLAY"

- name: Install Node ${{ matrix.node }}
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node_version }}

- name: Install dependencies (Linux)
run: sudo apt install libx11-dev libxkbfile-dev
if: "contains(matrix.os, 'ubuntu')"

- name: Install Python setuptools
# This is needed for Python 3.12+, since many versions of node-gyp
# are incompatible with Python 3.12+, which no-longer ships 'distutils'
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/publish.yml
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

These fixes seem intuitively correct, again to my human eyeballs unaided by language-aware tooling at least. 👍

Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,16 @@ jobs:
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
uses: GabrielBB/xvfb-action@v1
with:
node-version: ${{ env.NODE_VERSION }}
- uses: GabrielBB/xvfb-action@v1
- run: sudo apt install libx11-dev libxkbfile-dev
- run: python3 -m pip install setuptools
- run: npm ci
- run: npm test

publish:
needs: build
needs: test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
node_modules/
.atom
.DS_Store
npm-debug.log
*.swp
Expand Down
42 changes: 29 additions & 13 deletions README.md
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I like that there is documentation, including helpful hints and caveats for users of this stuff. It makes me feel a bit out of depth not knowing the implications and context of some of these things I'm reading, but the comments here still seem relevant and to the point (as far as I can tell). 👍

Original file line number Diff line number Diff line change
@@ -1,33 +1,23 @@
# keyboard-layout
[![CI](https://github.com/atom/keyboard-layout/actions/workflows/ci.yml/badge.svg)](https://github.com/atom/keyboard-layout/actions/workflows/ci.yml)

Read and observe the current keyboard layout.

To get the current keyboard layout, call `getCurrentKeyboardLayout`. It returns
the string identifier of the current layout based on the value returned by the
operating system.
To get the current keyboard layout, call `getCurrentKeyboardLayout`. It returns the string identifier of the current layout based on the value returned by the operating system.

```js
const KeyboardLayout = require('keyboard-layout')
KeyboardLayout.getCurrentKeyboardLayout() // => "com.apple.keylayout.Dvorak"
```

If you want to watch for layout changes, use `onDidChangeCurrentKeyboardLayout`
or `observeCurrentKeyboardLayout`. They work the same, except
`observeCurrentKeyboardLayout` invokes the given callback immediately with the
current layout value and then again next time it changes, whereas
`onDidChangeCurrentKeyboardLayout` only invokes the callback on the next
change.
If you want to watch for layout changes, use `onDidChangeCurrentKeyboardLayout` or `observeCurrentKeyboardLayout`. They work the same, except `observeCurrentKeyboardLayout` invokes the given callback immediately with the current layout value and then again next time it changes, whereas `onDidChangeCurrentKeyboardLayout` only invokes the callback on the next change.

```js
const KeyboardLayout = require('keyboard-layout')
subscription = KeyboardLayout.observeCurrentKeyboardLayout((layout) => console.log(layout))
subscription.dispose() // to unsubscribe later
```

To return characters for various modifier states based on a DOM 3
`KeyboardEvent.code` value and the current system keyboard layout, use
`getCurrentKeymap()`:
To return characters for various modifier states based on a DOM 3 `KeyboardEvent.code` value and the current system keyboard layout, use `getCurrentKeymap()`:

```js
const KeyboardLayout = require('keyboard-layout')
Expand All @@ -42,3 +32,29 @@ On a US layout, this returns:
}
*/
```


## Caveats

### All platforms

* Having an active layout change listener (via `onDidChangeCurrentKeyboardLayout` or `observeCurrentKeyboardLayout`) will not prevent the process from exiting.

### Linux

On Linux, there is minimal X11 support and somewhat more comprehensive Wayland support.

#### Both Wayland and X11

* There is no distinction between `getCurrentKeyboardLayout` and `getCurrentKeyboardLanguage`; the latter is an alias of the former.

#### X11

* `onDidChangeCurrentKeyboardLayout` and `observeCurrentKeyboardLayout` are no-ops; we do not receive notifications when the keyboard layout changes. If you want to detect if the keyboard layout has changed, you must poll periodically.
* `getCurrentKeymap` will return objects with only two properties: `unmodified` and `withShift`. Information is not available about which characters would be produced if `AltGr` or `AltGr+Shift` were pressed.

#### Wayland

* When Wayland support is enabled (as it will be if `libwayland-client` and `libxkbcommon` are present), this library will assume a Wayland environment is present, then fall back to X11 if Wayland setup fails in any way.
* In a Wayland environment, `observeCurrentKeyboardLayout` works. However…
* …the Wayland server (as implemented by your window manager) is the authority on when keyboard layouts change, and this might not align with your expectations. For instance, GNOME will change the active layout if you have several layouts and reorder them in your settings; it _will not_ change the active layout if you use keyboard shortcuts to switch “input sources” on the fly. Other window mangers may behave differently.
45 changes: 38 additions & 7 deletions binding.gyp
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,21 @@
"targets": [
{
"target_name": "keyboard-layout-manager",
"cflags!": ["-fno-exceptions"],
"cflags_cc!": ["-fno-exceptions"],
"xcode_settings": {
"GCC_ENABLE_CPP_EXCEPTIONS": "YES",
"CLANG_CXX_LIBRARY": "libc++",
"MACOSX_DEPLOYMENT_TARGET": "10.7",
},
"msvs_settings": {
"VCCLCompilerTool": {"ExceptionHandling": 1},
},
"sources": [
"src/keyboard-layout-manager.cc"
],
"include_dirs": [
"<!(node -e \"require('nan')\")",
"<!(node -p \"require('node-addon-api').include_dir\")",
"chrome_headers",
],
"conditions": [
Expand Down Expand Up @@ -48,21 +58,42 @@
"sources": [
"src/keyboard-layout-manager-linux.cc",
],
"link_settings": {
"libraries": [
"-lX11", "-lxkbfile"
]
}
"cflags_cc": ["-std=c++17"],
"variables": {
"wayland_available%": "<!(pkg-config --exists wayland-client && echo 1 || echo 0)"
},
"conditions": [
["wayland_available==1", {
"link_settings": {
"libraries": [
"-lX11",
"-lxkbfile",
"-lxkbcommon",
"-lwayland-client"
]
},
"defines": ["HAS_WAYLAND=1"]
}, {
"link_settings": {
"libraries": [
"-lX11",
"-lxkbfile"
]
},
"defines": ["HAS_WAYLAND=0"]
}]
]
}], # OS=="linux"
['OS=="freebsd"', {
"sources": [
"src/keyboard-layout-manager-linux.cc",
],
"include_dirs": [
"<!(node -p \"require('node-addon-api').include_dir\")",
"/usr/local/include", "/usr/local/include/X11",
],
"ldflags": [
"-lX11", "-lxkbfile", "-L/usr/local/lib",
"-lX11", "-lxkbfile", "-lxkbcommon", "-L/usr/local/lib",
],
}], # OS=="posix"
]
Expand Down
21 changes: 11 additions & 10 deletions lib/keyboard-layout.js
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Using newer JS capabilities and enhancing readability. 👍

Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ const emitter = new Emitter()
const keymapsByLayoutName = new Map()

const manager = new KeyboardLayoutManager(() => {
emitter.emit('did-change-current-keyboard-layout', getCurrentKeyboardLayout())
let current = getCurrentKeyboardLayout();
emitter.emit('did-change-current-keyboard-layout', current)
})

function getCurrentKeymap () {
Expand All @@ -29,13 +30,13 @@ function getCurrentKeyboardLanguage () {
}

function getInstalledKeyboardLanguages () {
var languages = {}
var languages = new Set();

for (var language of (manager.getInstalledKeyboardLanguages() || [])) {
languages[language] = true
languages.add(language)
}

return Object.keys(languages)
return Array.from(languages)
}

function onDidChangeCurrentKeyboardLayout (callback) {
Expand All @@ -48,10 +49,10 @@ function observeCurrentKeyboardLayout (callback) {
}

module.exports = {
getCurrentKeymap: getCurrentKeymap,
getCurrentKeyboardLayout: getCurrentKeyboardLayout,
getCurrentKeyboardLanguage: getCurrentKeyboardLanguage,
getInstalledKeyboardLanguages: getInstalledKeyboardLanguages,
onDidChangeCurrentKeyboardLayout: onDidChangeCurrentKeyboardLayout,
observeCurrentKeyboardLayout: observeCurrentKeyboardLayout
getCurrentKeymap,
getCurrentKeyboardLayout,
getCurrentKeyboardLanguage,
getInstalledKeyboardLanguages,
onDidChangeCurrentKeyboardLayout,
observeCurrentKeyboardLayout
}
Loading
Loading