Jump to content

UNIKBD.LIB

From EDM2

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. name is a Unicode string naming the keyboard layout (e.g., L"US", L"GR" for German, L"JP" for Japanese). mode is reserved and must be 0. Returns ULS_SUCCESS (0) on success; *pkhand receives the handle.
APIRET CALLCONV UniDestroyKeyboard(KHAND khand)
Closes a keyboard handle and releases all resources associated with the loaded layout. After this call khand is 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 kbdinfo with metadata about the keyboard layout associated with khand: language and country codes, layout flags, keyboard architecture ID, and a Unicode description string. Caller must set kbdinfo->len = sizeof(KEYBOARDINFO) before calling.

Shift State Management

APIRET CALLCONV UniUpdateShiftState(KHAND khand, USHIFTSTATE *state, VSCAN scan, BYTE makebreak)
Updates the shift state *state based on a single key event. Should be called for every key make and break event, including modifier keys (Shift, Ctrl, Alt, CapsLock, etc.). The updated state is then passed to UniTranslateKey.
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");
    }
}

See Also