UNIKBD.LIB
UNIKBD.LIB is an import library for UNIKBD.DLL, the OS/2 Unicode Keyboard Translation API distributed with the IBM OS/2 Developer's Toolkit as part of the ULS (Universal Language Support) subsystem introduced in OS/2 Warp 4. It translates physical keyboard scan codes into Unicode characters (UniChar / UCS-2), handling dead key composition, shift state tracking, and NLS (Far East) lock states across all supported keyboard layouts.
File size: 2,048 bytes. Copyright: IBM Corporation 1996. Include file: unikbd.h (which includes unidef.h). Activation: include unikbd.h directly; no feature-test macro is required.
Overview
OS/2's classic keyboard API (KBD* Dos calls and PM WM_CHAR messages) returns characters in the system code page. UNIKBD.DLL provides an alternative translation layer that produces Unicode code points regardless of the system locale, making it suitable for:
- Internationalized text editors and input frameworks
- Applications that must accept input from multiple keyboard layouts simultaneously
- IME (Input Method Editor) implementations that bridge physical keystrokes into complex scripts
- Applications porting from Win32 that use
ToUnicode/VkKeyScan
The API works on a handle-per-layout model: the caller opens a named keyboard layout with UniCreateKeyboard, maintains a shift state with UniUpdateShiftState, and translates individual scan codes with UniTranslateKey. Dead key sequences (e.g., pressing acute ´ then e to produce é) are resolved through a separate UniTranslateDeadkey call.
Include Files
| Header | Contents | DLL |
|---|---|---|
unikbd.h |
Keyboard translation API: KHAND, VSCAN, VDKEY, USHIFTSTATE, INKEYEVENT, KEYBOARDINFO, all Uni*Keyboard / UniTranslate* prototypes |
UNIKBD.DLL
|
unidef.h |
UniChar type, LocaleObject, full ULS locale API (UniCreateLocaleObject, UniStr*, UniQuery*, etc.) |
UCONV.DLL / UNIAPI.DLL
|
#include <unikbd.h>
unikbd.h automatically includes unidef.h; it is not necessary to include it separately.
Core Types
UniChar
The fundamental Unicode character type, defined in unidef.h:
typedef unsigned short UniChar; /* UCS-2 code point (16-bit) */
OS/2's ULS API uses UCS-2, the 16-bit fixed-width subset of Unicode. Code points above U+FFFF (requiring surrogate pairs in UTF-16) are not handled by this API.
KHAND
typedef unsigned int KHAND; /* Handle to a keyboard translate table */
An opaque handle returned by UniCreateKeyboard and passed to all other API functions. Identifies a specific loaded keyboard layout and its internal translation table.
VSCAN
typedef unsigned char VSCAN; /* Virtual scan code (hardware-independent) */
A normalized scan code value. OS/2 maps raw PS/2 or USB scan codes to VSCAN values before presenting them to the translation API.
VDKEY
typedef unsigned short VDKEY; /* Virtual key or dead key code */
Either a standard virtual key value (VK_*, values 0–0x0FFF) or a dead key value (DK_*, values 0x1000–0x1FFF). A value of 0 means "no virtual/dead key".
USHIFTSTATE
Holds the current modifier and lock state of the keyboard:
typedef struct {
ULONG Shift; /* Actual current physical shift state */
ULONG Effective; /* Effective state used for translation */
ULONG Led; /* Keyboard LED indicators */
} USHIFTSTATE;
Shift tracks which modifier keys are physically held down; Effective reflects the logical state used for character translation (e.g., Caps Lock inverts the effective shift for letters). Led indicates which indicator LEDs should be lit.
INKEYEVENT
Input event structure passed to shift state update:
typedef struct {
USHORT ldev; /* Logical device: 0 = real keyboard */
BYTE makebreak; /* KEYEV_MAKE, KEYEV_BREAK, KEYEV_MAKEBREAK, KEYEV_REPEAT */
VSCAN scan; /* Virtual scan code of the key */
ULONG time; /* Event timestamp (ms) */
} INKEYEVENT;
KEYBOARDINFO
Returned by UniQueryKeyboard:
typedef struct {
ULONG len; /* sizeof(KEYBOARDINFO) */
USHORT kbid; /* Keyboard architecture ID */
USHORT version; /* Layout version number */
BYTE language[2]; /* Primary language code (ISO 639) */
BYTE country[2]; /* Country code (ISO 3166) */
USHORT flags; /* KBDF_* flags */
USHORT resv; /* Reserved */
UniChar description[32]; /* Human-readable layout description (Unicode) */
} KEYBOARDINFO;
API Reference
Lifecycle
APIRET CALLCONV UniCreateKeyboard(KHAND *pkhand, KBDNAME *name, ULONG mode)- Opens a keyboard layout by name and returns a handle.
nameis a Unicode string naming the keyboard layout (e.g.,L"US",L"GR"for German,L"JP"for Japanese).modeis reserved and must be 0. ReturnsULS_SUCCESS(0) on success;*pkhandreceives the handle.
APIRET CALLCONV UniDestroyKeyboard(KHAND khand)- Closes a keyboard handle and releases all resources associated with the loaded layout. After this call
khandis invalid.
UniInitKeyboard()- Initializes the UNIKBD subsystem. Present in the library but not documented in the public header; called internally before the first
UniCreateKeyboard. Applications do not normally need to call this directly.
APIRET CALLCONV UniQueryKeyboard(KHAND khand, KEYBOARDINFO *kbdinfo)- Fills
kbdinfowith metadata about the keyboard layout associated withkhand: language and country codes, layout flags, keyboard architecture ID, and a Unicode description string. Caller must setkbdinfo->len = sizeof(KEYBOARDINFO)before calling.
Shift State Management
APIRET CALLCONV UniUpdateShiftState(KHAND khand, USHIFTSTATE *state, VSCAN scan, BYTE makebreak)- Updates the shift state
*statebased on a single key event. Should be called for every key make and break event, including modifier keys (Shift, Ctrl, Alt, CapsLock, etc.). The updatedstateis then passed toUniTranslateKey.
APIRET CALLCONV UniResetShiftState(KHAND khand, USHIFTSTATE *state, ULONG type)- Resets the shift state according to
type:
| Constant | Value | Effect |
|---|---|---|
KEYEV_SET |
0 | Set shift state to the value supplied in *state
|
KEYEV_RELEASE |
1 | Release all currently pressed (non-locked) modifier keys |
KEYEV_ZERO |
2 | Release all pressed and locked keys; reset to fully unshifted state |
Character Translation
APIRET CALLCONV UniTranslateKey(KHAND khand, ULONG eshift, VSCAN scan, UniChar *unichar, VDKEY *vdkey, BYTE *bscan)- Translates a scan code to a Unicode character given the current effective shift state.
| Parameter | Direction | Description |
|---|---|---|
khand |
in | Keyboard layout handle |
eshift |
in | Effective shift state from USHIFTSTATE.Effective
|
scan |
in | Virtual scan code of the pressed key |
unichar |
out | Unicode code point produced, or 0 if the key produces no character (dead key or modifier) |
vdkey |
out | Virtual key code (VK_*) or dead key code (DK_*); 0 if neither applies |
bscan |
out | Base scan code (the scan code stripped of extended-key bits) |
If the key is a dead key, *unichar will be 0 and *vdkey will contain a DK_* value. The application should store this dead key and pass it to UniTranslateDeadkey on the next keystroke.
APIRET CALLCONV UniTranslateDeadkey(KHAND khand, VDKEY dead, UniChar inchar, UniChar *outchar, VDKEY *newdeadkey)- Resolves a dead key plus a base character into a composed Unicode character.
| Parameter | Direction | Description |
|---|---|---|
khand |
in | Keyboard layout handle |
dead |
in | Dead key code from the previous UniTranslateKey call (e.g., DK_ACUTE)
|
inchar |
in | Base character from the current UniTranslateKey call (e.g., L'e')
|
outchar |
out | Composed Unicode character (e.g., U+00E9 é) or the base character if no composition exists |
newdeadkey |
out | Non-zero if the combination itself produces another dead key (rare double-dead sequences) |
If inchar is a space, most layouts produce the spacing version of the diacritic (e.g., DK_ACUTE + space → ´ U+00B4).
APIRET CALLCONV UniUntranslateKey(KHAND khand, UniChar unichar, VDKEY vdkey, VSCAN *pscan, ULONG *eshift)- Reverse translation: given a Unicode character and optional virtual key hint, returns the scan code and effective shift state that would produce it. Useful for injecting synthetic key events or implementing keyboard macro playback.
| Parameter | Direction | Description |
|---|---|---|
khand |
in | Keyboard layout handle |
unichar |
in | Target Unicode character to produce |
vdkey |
in | Virtual key hint (0 if not known) |
pscan |
out | Scan code that produces the character |
eshift |
out | Effective shift state needed |
Dead Key Constants
Dead keys produce no character on their own; they modify the next keystroke. Ranges: 0x1000–0x1FFF.
| Constant | Value | Diacritic | Example composition |
|---|---|---|---|
DK_ACUTE |
0x1001 | Acute accent ´ | DK_ACUTE + e → é (U+00E9)
|
DK_GRAVE |
0x1002 | Grave accent ` | DK_GRAVE + a → à (U+00E0)
|
DK_DIERESIS / DK_UMLAUT |
0x1003 | Diaeresis / Umlaut ¨ | DK_DIERESIS + u → ü (U+00FC)
|
DK_CIRCUMFLEX |
0x1004 | Circumflex ^ | DK_CIRCUMFLEX + o → ô (U+00F4)
|
DK_TILDE |
0x1005 | Tilde ~ | DK_TILDE + n → ñ (U+00F1)
|
DK_CEDILLA |
0x1006 | Cedilla ¸ | DK_CEDILLA + c → ç (U+00E7)
|
DK_MACRON |
0x1007 | Macron ¯ | DK_MACRON + a → ā (U+0101)
|
DK_BREVE |
0x1008 | Breve ˘ | DK_BREVE + a → ă (U+0103)
|
DK_OGONEK |
0x1009 | Ogonek ˛ | DK_OGONEK + a → ą (U+0105)
|
DK_DOT |
0x100A | Dot above ˙ | DK_DOT + z → ż (U+017C)
|
DK_BAR |
0x100B | Bar / stroke | DK_BAR + l → ł (U+0142)
|
DK_RING |
0x100C | Ring above ˚ | DK_RING + a → å (U+00E5)
|
DK_CARON / DK_HACEK |
0x100D | Caron / Háček ˇ | DK_CARON + s → š (U+0161)
|
DK_HUNGARUMLAUT |
0x100E | Double acute ˝ | DK_HUNGARUMLAUT + o → ő (U+0151)
|
DK_ACUTEDIA |
0x100F | Acute + diaeresis | Combined form |
DK_PSILI |
0x1010 | Greek smooth breathing | Greek polytonic |
DK_DASIA |
0x1011 | Greek rough breathing | Greek polytonic |
DK_OVERLINE |
0x1012 | Overline | Phonetic notation |
DK_UNDERDOT |
0x1013 | Dot below | Transliteration (Arabic, Sanskrit) |
DK_MIN (0x1000) and DK_MAX (0x1FFF) bracket the full dead key range. Values in the range 0x1014–0x1FFF are reserved for additional dead key types.
Shift State Constants
KBD_* flags (USHIFTSTATE.Shift and .Effective)
| Constant | Value | Meaning |
|---|---|---|
KBD_SHIFT |
0x00000001 | Shift key held |
KBD_CONTROL |
0x00000002 | Ctrl key held |
KBD_ALT |
0x00000004 | Alt key held |
KBD_ALTCTRLSHIFT |
0x00000007 | All three: Alt+Ctrl+Shift |
KBD_ALTGR |
0x00000008 | AltGr key (right Alt / Ctrl+Alt on some layouts) |
KBD_NLS1 / KBD_WIDE |
0x00000010 | NLS lock 1; Japanese: wide (zenkaku) mode |
KBD_NLS2 / KBD_KATAKANA |
0x00000020 | NLS lock 2; Japanese: katakana; Korean: jamo; Taiwan: phonetic |
KBD_NLS3 / KBD_HIRAGANA |
0x00000040 | NLS lock 3; Japanese: hiragana; Korean: hangeul; Taiwan: Tsang-Jye |
KBD_NLS4 / KBD_ROMANJI |
0x00000080 | NLS lock 4; Japanese: rōmaji; Korean: hanja cursor |
KBD_SCROLLLOCK |
0x00000100 | Scroll Lock active |
KBD_NUMLOCK |
0x00000200 | Num Lock active |
KBD_CAPSLOCK |
0x00000400 | Caps Lock active |
KBD_EXTRALOCK |
0x00000800 | Extra lock (layout-specific) |
KBD_APPL |
0x00001000 | Windows Application key held |
KBD_DBCS |
0x00008000 | DBCS (double byte) input mode active |
KBD_EFFECTIVE |
0x0000FFFF | Mask for all effective state bits |
KBD_LEFTSHIFT |
0x00010000 | Left Shift specifically held |
KBD_RIGHTSHIFT |
0x00020000 | Right Shift specifically held |
KBD_LEFTCONTROL |
0x00040000 | Left Ctrl held |
KBD_RIGHTCONTROL |
0x00080000 | Right Ctrl held |
KBD_LEFTALT |
0x00100000 | Left Alt held |
KBD_RIGHTALT |
0x00200000 | Right Alt held (= AltGr on ISO layouts) |
KBD_LEFTWINDOWS |
0x00400000 | Left Windows key held |
KBD_RIGHTWINDOWS |
0x00800000 | Right Windows key held |
KBD_NOROMANJI |
0x04000000 | Japanese: no-rōmaji mode |
KBD_KANJI |
0x08000000 | Kanji conversion mode active |
KBD_DEADKEY |
0x10000000 | Dead key is pending |
KBD_WAIT |
0x20000000 | Input subsystem busy |
KBD_HOLD |
0x40000000 | Key held (repeat mode) |
KBD_LOCK |
0x80000000 | Lock active |
Keyboard Layout Flags (KEYBOARDINFO.flags)
| Constant | Value | Meaning |
|---|---|---|
KBDF_DEFAULTVKEY |
0x0001 | Use default virtual key assignments |
KBDF_NOCTRLSHIFT |
0x0002 | Ctrl+Shift is treated as Ctrl (no separate Ctrl+Shift layer) |
KBDF_NOALTGR |
0x0004 | Layout does not use AltGr |
KBDF_SHIFTALTGR |
0x0010 | AltGr and Shift+AltGr are separate layers |
KBDF_DEADGOOD |
0x0020 | Invalid dead key combinations output the second character as-is |
KBDF_DEADPRIVATE |
0x0040 | Dead key results use only private-use Unicode code points |
KBDF_SYSTEM |
0x8000 | IBM-supplied system layout |
KBDF_INTERNATIONAL |
0x4000 | Full Unicode character range supported |
KBDF_DVORAK |
0x2000 | Dvorak (alternate letter arrangement) |
KBDF_NATIONAL |
0x1000 | National letter key arrangement |
KBDF_ISOKEYS |
0x0800 | Use ISO key name icons |
KBDF_LAYOUT101 |
0x0000 | 84/101-key (US) physical layout |
KBDF_LAYOUT102 |
0x0100 | 85/102-key (ISO) physical layout |
KBDF_LAYOUT106 |
0x0200 | 89/106-key (Japanese) physical layout |
KBDF_LAYOUT103 |
0x0300 | 86/103-key (Korean) physical layout |
KBDF_LAYOUT100 |
0x0400 | 83/100-key (XT) physical layout |
Make/Break Constants
| Constant | Value | Meaning |
|---|---|---|
KEYEV_MAKEBREAK |
0 | Event contains both make (press) and break (release) |
KEYEV_MAKE |
1 | Key pressed (make event) |
KEYEV_BREAK |
2 | Key released (break event) |
KEYEV_REPEAT |
3 | Auto-repeat event |
Build Instructions
IBM VisualAge C++ / ILINK
icc -O2 -Gm -c myapp.c ilink /PM:PM myapp.obj os2386.lib unikbd.lib
OpenWatcom
wcl386 -bt=os2 -mf -c myapp.c wlink system os2v2_pm file myapp.obj library os2386.lib library unikbd.lib
EMX/GCC
gcc -Zomf -c myapp.c gcc -Zomf -o myapp.exe myapp.o -los2386 -lunikbd
Code Examples
Translating a keystroke to Unicode
#include <unikbd.h>
#include <stdio.h>
int main(void)
{
KHAND khand;
USHIFTSTATE state = {0};
UniChar uc;
VDKEY vdkey;
BYTE bscan;
VDKEY pendingDead = 0;
APIRET rc;
/* Open the US keyboard layout */
KBDNAME name[] = { 'U', 'S', 0 };
rc = UniCreateKeyboard(&khand, name, 0);
if (rc != 0) { fprintf(stderr, "UniCreateKeyboard rc=%lu\n", rc); return 1; }
/* --- Simulate pressing Shift+A (scan 0x1E = A key) --- */
/* 1. Press Left Shift */
UniUpdateShiftState(khand, &state, 0x2A /* Left Shift scan */, KEYEV_MAKE);
/* 2. Press A key */
UniUpdateShiftState(khand, &state, 0x1E /* A scan */, KEYEV_MAKE);
rc = UniTranslateKey(khand, state.Effective, 0x1E, &uc, &vdkey, &bscan);
if (rc == 0) {
if (vdkey >= DK_MIN && vdkey <= DK_MAX) {
/* Dead key: store it, wait for next key */
pendingDead = vdkey;
} else if (uc != 0) {
wprintf(L"Character: U+%04X\n", uc); /* U+0041 'A' */
}
}
UniDestroyKeyboard(khand);
return 0;
}
Handling a dead key sequence
#include <unikbd.h>
#include <stdio.h>
void TranslateWithDeadKey(KHAND khand, USHIFTSTATE *pState,
VSCAN scan1_dead, VSCAN scan2_base)
{
UniChar uc1, uc2, ucOut;
VDKEY dk1, dk2, dkNew;
BYTE bs;
/* First key: should produce a dead key */
UniTranslateKey(khand, pState->Effective, scan1_dead, &uc1, &dk1, &bs);
if (dk1 < DK_MIN || dk1 > DK_MAX) {
wprintf(L"Not a dead key\n");
return;
}
/* Second key: the base character */
UniTranslateKey(khand, pState->Effective, scan2_base, &uc2, &dk2, &bs);
/* Compose: dead key + base char -> precomposed character */
UniTranslateDeadkey(khand, dk1, uc2, &ucOut, &dkNew);
wprintf(L"Composed: U+%04X\n", ucOut);
/* e.g., DK_TILDE(scan) + n(scan) -> U+00F1 ñ */
}
Querying a keyboard layout
#include <unikbd.h>
#include <stdio.h>
void ShowKeyboardInfo(KHAND khand)
{
KEYBOARDINFO info;
info.len = sizeof(KEYBOARDINFO);
if (UniQueryKeyboard(khand, &info) == 0) {
printf("Layout ID : %u\n", info.kbid);
printf("Version : %u\n", info.version);
printf("Language : %c%c\n", info.language[0], info.language[1]);
printf("Country : %c%c\n", info.country[0], info.country[1]);
printf("Flags : 0x%04X\n", info.flags);
/* Print Unicode description */
int i;
printf("Description : ");
for (i = 0; i < 32 && info.description[i]; i++)
printf("%c", (char)info.description[i]); /* ASCII range only */
printf("\n");
if (info.flags & KBDF_LAYOUT102) printf(" 102-key ISO layout\n");
if (info.flags & KBDF_INTERNATIONAL) printf(" Full Unicode range\n");
if (info.flags & KBDF_NOALTGR) printf(" No AltGr layer\n");
}
}