// Every variant section must define the types KEYPAD,
// FOUR_LEVEL_KEYPAD and FOUR_LEVEL_MIXED_KEYPAD:
//
// - KEYPAD is required by the XKB protocol.
// - KEYPAD and FOUR_LEVEL_KEYPAD are required by xkbcomp
//   and xkbcommon for automatic key type assignment.
// - KEYPAD and FOUR_LEVEL_MIXED_KEYPAD are used in the
//   codebase.

// Overview of the numpad behaviors by variant:
//
// | Variant      | No modifier | Shift   | NumLock | Shift + NumLock  |
// | ------------ | ----------- | ------- | ------- | ---------------- |
// | numlock_only | Level 1     | Level 1 | Level 2 | Level 2          |
// | microsoft    | Level 1     | Level 1 | Level 2 | Level 1          |
// | pc           | Level 1     | Level 2 | Level 2 | Level 1          |
// | mac          | Level 2     | Level 2 | Level 2 | Level 2          |
// | shift3       | Level 1     | Level 1 | Level 2 | Level 2 (KEYPAD) |
// |              |             |         |         | or 3 (*_KEYPAD)  |
//
// Types involving LevelThree additionaly have the following rules:
//
// FOUR_LEVEL_KEYPAD
//     Is similar to the KEYPAD scheme but considers also the LevelThree
//     modifier. If the LevelThree modifier applies, the symbol from the
//     third level is taken. If both Shift and LevelThree or NumLock and
//     LevelThree apply, the symbol from the fourth level is taken. If
//     all three (Shift+NumLock+LevelThree) apply, the symbol from the
//     third level is taken. This also is a shift-cancels-caps variant.
//
// FOUR_LEVEL_MIXED_KEYPAD
//     A four-level keypad scheme where the first two levels behave like
//     the KEYPAD scheme (with Shift and NumLock). The LevelThree modifier
//     acts as an override, providing access to two normally Shift-ed
//     levels: when LevelThree is active we ignore the NumLock state.
//     Intended for the digit area of the keypad.
//
// See also: docs/README.enhancing.

// NumLock enables digits; Shift does not interact with NumLock
// at the two lowest levels:
//
//     | NumLock | Shift | Level | Description            |
//     | ------- | ----- | ----- | ---------------------- |
//     | Off     | Off   | 1     | Navigation and editing |
//     | Off     | On    | 1     | Navigation and editing |
//     | On      | Off   | 2     | Digits                 |
//     | On      | On    | 2     | Digits                 |

default partial xkb_types "numlock_only_base" {
    virtual_modifiers  NumLock;

    type "KEYPAD" {
	modifiers = NumLock;
	map[None] = Level1;
	map[NumLock] = Level2;
	level_name[Level1] = "Base";
	level_name[Level2] = "Number";
    };
};

partial xkb_types "numlock_only_extra" {
    virtual_modifiers  NumLock, LevelThree;

    // A four-level keypad variant with “Shift cancels NumLock”
    // behavior only when combined with LevelThree.
    type "FOUR_LEVEL_KEYPAD" {
	modifiers = Shift + NumLock + LevelThree;
	map     [None] = Level1;
	map     [Shift] = Level1;
	preserve[Shift] = Shift;
	map     [NumLock] = Level2;
	map     [LevelThree] = Level3;
	map     [Shift+NumLock] = Level2;
	preserve[Shift+NumLock] = Shift;
	map     [Shift+LevelThree] = Level4;
	map     [NumLock+LevelThree] = Level4;
	map     [Shift+NumLock+LevelThree] = Level3;
	level_name[Level1] = "Base";
	level_name[Level2] = "NumLock";
	level_name[Level3] = "AltGr";
	level_name[Level4] = "Shift/NumLock AltGr";
    };

    // A four-level keypad variant. NumLock does not interact with
    // Shift nor LevelThree.
    type "FOUR_LEVEL_MIXED_KEYPAD" {
	modifiers = Shift + NumLock + LevelThree;
	map     [None] = Level1;
	map     [Shift] = Level1;
	preserve[Shift] = Shift;
	map     [NumLock] = Level2;
	map     [LevelThree] = Level3;
	map     [Shift+NumLock] = Level2;
	preserve[Shift+NumLock] = Shift;
	map     [Shift+LevelThree] = Level4;
	map     [NumLock+LevelThree] = Level3;
	map     [Shift+NumLock+LevelThree] = Level4;
	level_name[Level1] = "Base";
	level_name[Level2] = "NumLock";
	level_name[Level3] = "AltGr";
	level_name[Level4] = "Shift AltGr";
    };
};

partial xkb_types "numlock_only" {
    include "numpad(numlock_only_base)"
    include "numpad(numlock_only_extra)"
};

// NumLock and Shift each enable digits; both together cancel:
//
//     | NumLock | Shift | Level | Description            |
//     | ------- | ----- | ----- | ---------------------- |
//     | Off     | Off   | 1     | Navigation and editing |
//     | Off     | On    | 2     | Digits                 |
//     | On      | Off   | 2     | Digits                 |
//     | On      | On    | 1     | Navigation and editing |

partial xkb_types "pc" {
    virtual_modifiers  NumLock, LevelThree;

    // Canonical keypad type, as defined by the XKB protocol:
    // Shift selects the digits and cancels NumLock.
    // See: https://xorg.freedesktop.org/archive/X11R7.7/doc/kbproto/xkbproto.html#The_KEYPAD_Key_Type
    type "KEYPAD" {
	modifiers = Shift+NumLock;
	map[None] = Level1;
	map[Shift] = Level2;
	map[NumLock] = Level2;
	map[Shift+NumLock] = Level1;
	level_name[Level1] = "Base";
	level_name[Level2] = "Number";
    };

    // A four-level keypad variant with LevelThree and “Shift cancels NumLock”
    // behavior.
    type "FOUR_LEVEL_KEYPAD" {
	modifiers = Shift + NumLock + LevelThree;
	map[None] = Level1;
	map[Shift] = Level2;
	map[NumLock] = Level2;
	map[LevelThree] = Level3;
	map[Shift+NumLock] = Level1;
	map[Shift+LevelThree] = Level4;
	map[NumLock+LevelThree] = Level4;
	map[Shift+NumLock+LevelThree] = Level3;
	level_name[Level1] = "Base";
	level_name[Level2] = "NumLock";
	level_name[Level3] = "AltGr";
	level_name[Level4] = "Shift/NumLock AltGr";
    };

    // A four-level keypad variant. The first two levels are controlled by
    // NumLock and Shift (like in KEYPAD).  LevelThree overrides this mode,
    // with two additional shifted levels.
    type "FOUR_LEVEL_MIXED_KEYPAD" {
	modifiers = Shift + NumLock + LevelThree;
	map[None] = Level1;
	map[Shift] = Level2;
	map[NumLock] = Level2;
	map[LevelThree] = Level3;
	map[Shift+NumLock] = Level1;
	map[Shift+LevelThree] = Level4;
	map[NumLock+LevelThree] = Level3;
	map[Shift+NumLock+LevelThree] = Level4;
	level_name[Level1] = "Base";
	level_name[Level2] = "NumLock";
	level_name[Level3] = "AltGr";
	level_name[Level4] = "Shift AltGr";
    };
};

// Neither NumLock nor Shift affects digits selection:
//
//     | NumLock | Shift | Level | Description      |
//     | ------- | ----- | ----- | ---------------- |
//     | Off     | Off   | 2     | Digits           |
//     | Off     | On    | 2     | Digits           |
//     | On      | Off   | 2     | Digits           |
//     | On      | On    | 2     | Digits           |

// On Mac keypads, level 1 is unreachable
partial xkb_types "mac" {
    virtual_modifiers LevelThree;

    type "KEYPAD" {
	modifiers = None;
	map[None] = Level2;
	level_name[Level2] = "Number";
    };

    // A four-level keypad variant.
    type "FOUR_LEVEL_KEYPAD" {
	modifiers = Shift + LevelThree;
	map[None] = Level2;
	map[Shift] = Level2;
	preserve[Shift] = Shift;
	map[LevelThree] = Level3;
	map[Shift+LevelThree] = Level4;
	level_name[Level1] = "Unreachable";
	level_name[Level2] = "Number";
	level_name[Level3] = "AltGr";
	level_name[Level4] = "Shift AltGr";
    };

    // Identical to FOUR_LEVEL_KEYPAD
    type "FOUR_LEVEL_MIXED_KEYPAD" {
	modifiers = Shift + LevelThree;
	map[None] = Level2;
	map[Shift] = Level2;
	preserve[Shift] = Shift;
	map[LevelThree] = Level3;
	map[Shift+LevelThree] = Level4;
	level_name[Level1] = "Unreachable";
	level_name[Level2] = "Number";
	level_name[Level3] = "AltGr";
	level_name[Level4] = "Shift AltGr";
    };
};

// NumLock enables digits; Shift cancels NumLock but never enables digits
//
//     | NumLock | Shift | Level | Description            |
//     | ------- | ----- | ----- | ---------------------- |
//     | Off     | Off   | 1     | Navigation and editing |
//     | Off     | On    | 1     | Navigation and editing |
//     | On      | Off   | 2     | Digits                 |
//     | On      | On    | 1     | Navigation and editing |

// On Windows keypads, Shift cancels NumLock but Shift alone does not enable digits.
partial xkb_types "microsoft" {
    virtual_modifiers  NumLock, LevelThree;

    type "KEYPAD" {
	modifiers = Shift + NumLock;
	map[None] = Level1;
	map[Shift] = Level1;
	preserve[Shift] = Shift;
	map[NumLock] = Level2;
	map[Shift+NumLock] = Level1;
	level_name[Level1] = "Base";
	level_name[Level2] = "Number";
    };

    // A four-level keypad variant. Shift alone does not enable digits,
    // but Shift cancels NumLock.
    type "FOUR_LEVEL_KEYPAD" {
	modifiers = Shift + NumLock + LevelThree;
	map[None] = Level1;
	map[Shift] = Level1;
	preserve[Shift] = Shift;
	map[NumLock] = Level2;
	map[LevelThree] = Level3;
	map[Shift+NumLock] = Level1;
	map[Shift+LevelThree] = Level4;
	map[NumLock+LevelThree] = Level4;
	map[Shift+NumLock+LevelThree] = Level3;
	level_name[Level1] = "Base";
	level_name[Level2] = "NumLock";
	level_name[Level3] = "AltGr";
	level_name[Level4] = "Shift/NumLock AltGr";
    };

    // A four-level keypad variant. The first two levels are controlled by NumLock.
    // LevelThree overrides this mode, with two additional shifted levels.
    type "FOUR_LEVEL_MIXED_KEYPAD" {
	modifiers = Shift + NumLock + LevelThree;
	map[None] = Level1;
	map[Shift] = Level1;
	preserve[Shift] = Shift;
	map[NumLock] = Level2;
	map[LevelThree] = Level3;
	map[Shift+NumLock] = Level1;
	map[Shift+LevelThree] = Level4;
	map[NumLock+LevelThree] = Level3;
	map[Shift+NumLock+LevelThree] = Level4;
	level_name[Level1] = "Base";
	level_name[Level2] = "NumLock";
	level_name[Level3] = "AltGr";
	level_name[Level4] = "Shift AltGr";
    };
};

// NumLock enables digits; NumLock+Shift enables digits (KEYPAD)
// or auxiliary symbols (*_KEYPAD):
//
//     | NumLock | Shift | Level | Description                  |
//     | ------- | ----- | ----- | ---------------------------- |
//     | Off     | Off   | 1     | Navigation and editing       |
//     | Off     | On    | 1     | Navigation and editing       |
//     | On      | Off   | 2     | Digits                       |
//     | On      | On    | 2     | Digits (KEYPAD)              |
//     |         |       | 3     | Auxiliary symbols (*_KEYPAD) |

partial xkb_types "shift3" {
    virtual_modifiers Alt, NumLock;

    // KEYPAD: 2 levels, Shift and NumLock operate as two independent modifiers
    include "numpad(numlock_only_base)"

    // Swiss-German style numeric keypad: Shift and NumLock operate as
    // two independent modifiers, similar to how some keys behave with
    // Shift and CapsLock on the Swiss-German layout. However, since we
    // want shift state for the navigation and editing keys, only three
    // levels are used from the key mappings.
    // Closest type is actually FOUR_LEVEL_X, but most numpad layouts use
    // FOUR_LEVEL_KEYPAD and FOUR_LEVEL_MIXED_KEYPAD, so those are the ones
    // we are going to override identically.

    type "FOUR_LEVEL_KEYPAD" {
	modifiers = Shift + NumLock + Control + Alt;

	// NumLock Off => navigation and editing keys
	map[None] = Level1;

	// NumLock Off + Shift pressed => selection using navigation keys
	preserve[Shift] = Shift;

	// NumLock On => decimal digits
	map[NumLock] = Level2;

	// NumLock On + Shift pressed => auxiliary symbols,
	// e.g. hexadecimal digits
	map[Shift+NumLock] = Level3;

	// Ctrl and Alt, regardless of NumLock and Shift => command keys
	preserve[Control] = Control;
	preserve[Alt] = Alt;
	preserve[Shift+Control] = Shift + Control;
	preserve[Shift+Alt] = Shift + Alt;
	map     [NumLock+Control] = Level2;
	preserve[NumLock+Control] = Control;
	map     [NumLock+Alt] = Level2;
	preserve[NumLock+Alt] = Alt;
	map     [Control+Alt] = Level4;

	map     [Shift+NumLock+Control] = Level3;
	preserve[Shift+NumLock+Control] = Control;
	map     [Shift+NumLock+Alt] = Level3;
	preserve[Shift+NumLock+Alt] = Alt;
	map     [Shift+Control+Alt] = Level4;
	preserve[Shift+Control+Alt] = Shift;
	map     [NumLock+Control+Alt] = Level2;
	preserve[NumLock+Control+Alt] = Control+Alt;

	map     [Shift+NumLock+Control+Alt] = Level3;
	preserve[Shift+NumLock+Control+Alt] = Control+Alt;

	level_name[Level1] = "Base";
	level_name[Level2] = "NumLock";
	level_name[Level3] = "Shift+NumLock";
	level_name[Level4] = "Ctrl+Alt";
    };

    type "FOUR_LEVEL_MIXED_KEYPAD" {
	modifiers = Shift + NumLock + Control + Alt;

	// NumLock Off => navigation and editing keys
	map[None] = Level1;

	// NumLock Off + Shift pressed => selection using navigation keys
	preserve[Shift] = Shift;

	// NumLock On => decimal digits
	map[NumLock] = Level2;

	// NumLock On + Shift pressed => auxiliary symbols,
	// e.g. hexadecimal digits
	map[Shift+NumLock] = Level3;

	// Ctrl and Alt, regardless of NumLock and Shift => command keys
	preserve[Control] = Control;
	preserve[Alt] = Alt;
	preserve[Shift+Control] = Shift + Control;
	preserve[Shift+Alt] = Shift + Alt;
	map     [NumLock+Control] = Level2;
	preserve[NumLock+Control] = Control;
	map     [NumLock+Alt] = Level2;
	preserve[NumLock+Alt] = Alt;
	map     [Control+Alt] = Level4;

	map     [Shift+NumLock+Control] = Level3;
	preserve[Shift+NumLock+Control] = Control;
	map     [Shift+NumLock+Alt] = Level3;
	preserve[Shift+NumLock+Alt] = Alt;
	map     [Shift+Control+Alt] = Level4;
	preserve[Shift+Control+Alt] = Shift;
	map     [NumLock+Control+Alt] = Level2;
	preserve[NumLock+Control+Alt] = Control+Alt;

	map     [Shift+NumLock+Control+Alt] = Level3;
	preserve[Shift+NumLock+Control+Alt] = Control+Alt;

	level_name[Level1] = "Base";
	level_name[Level2] = "NumLock";
	level_name[Level3] = "Shift+NumLock";
	level_name[Level4] = "Ctrl+Alt";
    };
};
