#![allow(unreachable_patterns)]

use libc::c_char;
use std::ffi::{CStr, CString};
use std::mem::transmute;

use crate::sys;
use sys::keycode::*;

#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
#[repr(u32)]
pub enum Keycode {
    ScancodeMask = SDLK_SCANCODE_MASK.0,
    Unknown = SDLK_UNKNOWN.0,
    Return = SDLK_RETURN.0,
    Escape = SDLK_ESCAPE.0,
    Backspace = SDLK_BACKSPACE.0,
    Tab = SDLK_TAB.0,
    Space = SDLK_SPACE.0,
    Exclaim = SDLK_EXCLAIM.0,
    DblApostrophe = SDLK_DBLAPOSTROPHE.0,
    Hash = SDLK_HASH.0,
    Dollar = SDLK_DOLLAR.0,
    Percent = SDLK_PERCENT.0,
    Ampersand = SDLK_AMPERSAND.0,
    Apostrophe = SDLK_APOSTROPHE.0,
    LeftParen = SDLK_LEFTPAREN.0,
    RightParen = SDLK_RIGHTPAREN.0,
    Asterisk = SDLK_ASTERISK.0,
    Plus = SDLK_PLUS.0,
    Comma = SDLK_COMMA.0,
    Minus = SDLK_MINUS.0,
    Period = SDLK_PERIOD.0,
    Slash = SDLK_SLASH.0,
    _0 = SDLK_0.0,
    _1 = SDLK_1.0,
    _2 = SDLK_2.0,
    _3 = SDLK_3.0,
    _4 = SDLK_4.0,
    _5 = SDLK_5.0,
    _6 = SDLK_6.0,
    _7 = SDLK_7.0,
    _8 = SDLK_8.0,
    _9 = SDLK_9.0,
    Colon = SDLK_COLON.0,
    Semicolon = SDLK_SEMICOLON.0,
    Less = SDLK_LESS.0,
    Equals = SDLK_EQUALS.0,
    Greater = SDLK_GREATER.0,
    Question = SDLK_QUESTION.0,
    At = SDLK_AT.0,
    LeftBracket = SDLK_LEFTBRACKET.0,
    Backslash = SDLK_BACKSLASH.0,
    RightBracket = SDLK_RIGHTBRACKET.0,
    Caret = SDLK_CARET.0,
    Underscore = SDLK_UNDERSCORE.0,
    Grave = SDLK_GRAVE.0,
    A = SDLK_A.0,
    B = SDLK_B.0,
    C = SDLK_C.0,
    D = SDLK_D.0,
    E = SDLK_E.0,
    F = SDLK_F.0,
    G = SDLK_G.0,
    H = SDLK_H.0,
    I = SDLK_I.0,
    J = SDLK_J.0,
    K = SDLK_K.0,
    L = SDLK_L.0,
    M = SDLK_M.0,
    N = SDLK_N.0,
    O = SDLK_O.0,
    P = SDLK_P.0,
    Q = SDLK_Q.0,
    R = SDLK_R.0,
    S = SDLK_S.0,
    T = SDLK_T.0,
    U = SDLK_U.0,
    V = SDLK_V.0,
    W = SDLK_W.0,
    X = SDLK_X.0,
    Y = SDLK_Y.0,
    Z = SDLK_Z.0,
    LeftBrace = SDLK_LEFTBRACE.0,
    Pipe = SDLK_PIPE.0,
    RightBrace = SDLK_RIGHTBRACE.0,
    Tilde = SDLK_TILDE.0,
    Delete = SDLK_DELETE.0,
    PlusMinus = SDLK_PLUSMINUS.0,
    CapsLock = SDLK_CAPSLOCK.0,
    F1 = SDLK_F1.0,
    F2 = SDLK_F2.0,
    F3 = SDLK_F3.0,
    F4 = SDLK_F4.0,
    F5 = SDLK_F5.0,
    F6 = SDLK_F6.0,
    F7 = SDLK_F7.0,
    F8 = SDLK_F8.0,
    F9 = SDLK_F9.0,
    F10 = SDLK_F10.0,
    F11 = SDLK_F11.0,
    F12 = SDLK_F12.0,
    PrintScreen = SDLK_PRINTSCREEN.0,
    ScrollLock = SDLK_SCROLLLOCK.0,
    Pause = SDLK_PAUSE.0,
    Insert = SDLK_INSERT.0,
    Home = SDLK_HOME.0,
    PageUp = SDLK_PAGEUP.0,
    End = SDLK_END.0,
    PageDown = SDLK_PAGEDOWN.0,
    Right = SDLK_RIGHT.0,
    Left = SDLK_LEFT.0,
    Down = SDLK_DOWN.0,
    Up = SDLK_UP.0,
    NumLockClear = SDLK_NUMLOCKCLEAR.0,
    KpDivide = SDLK_KP_DIVIDE.0,
    KpMultiply = SDLK_KP_MULTIPLY.0,
    KpMinus = SDLK_KP_MINUS.0,
    KpPlus = SDLK_KP_PLUS.0,
    KpEnter = SDLK_KP_ENTER.0,
    Kp1 = SDLK_KP_1.0,
    Kp2 = SDLK_KP_2.0,
    Kp3 = SDLK_KP_3.0,
    Kp4 = SDLK_KP_4.0,
    Kp5 = SDLK_KP_5.0,
    Kp6 = SDLK_KP_6.0,
    Kp7 = SDLK_KP_7.0,
    Kp8 = SDLK_KP_8.0,
    Kp9 = SDLK_KP_9.0,
    Kp0 = SDLK_KP_0.0,
    KpPeriod = SDLK_KP_PERIOD.0,
    Application = SDLK_APPLICATION.0,
    Power = SDLK_POWER.0,
    KpEquals = SDLK_KP_EQUALS.0,
    F13 = SDLK_F13.0,
    F14 = SDLK_F14.0,
    F15 = SDLK_F15.0,
    F16 = SDLK_F16.0,
    F17 = SDLK_F17.0,
    F18 = SDLK_F18.0,
    F19 = SDLK_F19.0,
    F20 = SDLK_F20.0,
    F21 = SDLK_F21.0,
    F22 = SDLK_F22.0,
    F23 = SDLK_F23.0,
    F24 = SDLK_F24.0,
    Execute = SDLK_EXECUTE.0,
    Help = SDLK_HELP.0,
    Menu = SDLK_MENU.0,
    Select = SDLK_SELECT.0,
    Stop = SDLK_STOP.0,
    Again = SDLK_AGAIN.0,
    Undo = SDLK_UNDO.0,
    Cut = SDLK_CUT.0,
    Copy = SDLK_COPY.0,
    Paste = SDLK_PASTE.0,
    Find = SDLK_FIND.0,
    Mute = SDLK_MUTE.0,
    VolumeUp = SDLK_VOLUMEUP.0,
    VolumeDown = SDLK_VOLUMEDOWN.0,
    KpComma = SDLK_KP_COMMA.0,
    KpEqualsAs400 = SDLK_KP_EQUALSAS400.0,
    AltErase = SDLK_ALTERASE.0,
    SysReq = SDLK_SYSREQ.0,
    Cancel = SDLK_CANCEL.0,
    Clear = SDLK_CLEAR.0,
    Prior = SDLK_PRIOR.0,
    Return2 = SDLK_RETURN2.0,
    Separator = SDLK_SEPARATOR.0,
    Out = SDLK_OUT.0,
    Oper = SDLK_OPER.0,
    ClearAgain = SDLK_CLEARAGAIN.0,
    CrSel = SDLK_CRSEL.0,
    ExSel = SDLK_EXSEL.0,
    Kp00 = SDLK_KP_00.0,
    Kp000 = SDLK_KP_000.0,
    ThousandsSeparator = SDLK_THOUSANDSSEPARATOR.0,
    DecimalSeparator = SDLK_DECIMALSEPARATOR.0,
    CurrencyUnit = SDLK_CURRENCYUNIT.0,
    CurrencySubunit = SDLK_CURRENCYSUBUNIT.0,
    KpLeftParen = SDLK_KP_LEFTPAREN.0,
    KpRightParen = SDLK_KP_RIGHTPAREN.0,
    KpLeftBrace = SDLK_KP_LEFTBRACE.0,
    KpRightBrace = SDLK_KP_RIGHTBRACE.0,
    KpTab = SDLK_KP_TAB.0,
    KpBackspace = SDLK_KP_BACKSPACE.0,
    KpA = SDLK_KP_A.0,
    KpB = SDLK_KP_B.0,
    KpC = SDLK_KP_C.0,
    KpD = SDLK_KP_D.0,
    KpE = SDLK_KP_E.0,
    KpF = SDLK_KP_F.0,
    KpXor = SDLK_KP_XOR.0,
    KpPower = SDLK_KP_POWER.0,
    KpPercent = SDLK_KP_PERCENT.0,
    KpLess = SDLK_KP_LESS.0,
    KpGreater = SDLK_KP_GREATER.0,
    KpAmpersand = SDLK_KP_AMPERSAND.0,
    KpDblAmpersand = SDLK_KP_DBLAMPERSAND.0,
    KpVerticalBar = SDLK_KP_VERTICALBAR.0,
    KpDblVerticalBar = SDLK_KP_DBLVERTICALBAR.0,
    KpColon = SDLK_KP_COLON.0,
    KpHash = SDLK_KP_HASH.0,
    KpSpace = SDLK_KP_SPACE.0,
    KpAt = SDLK_KP_AT.0,
    KpExclam = SDLK_KP_EXCLAM.0,
    KpMemStore = SDLK_KP_MEMSTORE.0,
    KpMemRecall = SDLK_KP_MEMRECALL.0,
    KpMemClear = SDLK_KP_MEMCLEAR.0,
    KpMemAdd = SDLK_KP_MEMADD.0,
    KpMemSubtract = SDLK_KP_MEMSUBTRACT.0,
    KpMemMultiply = SDLK_KP_MEMMULTIPLY.0,
    KpMemDivide = SDLK_KP_MEMDIVIDE.0,
    KpPlusMinus = SDLK_KP_PLUSMINUS.0,
    KpClear = SDLK_KP_CLEAR.0,
    KpClearEntry = SDLK_KP_CLEARENTRY.0,
    KpBinary = SDLK_KP_BINARY.0,
    KpOctal = SDLK_KP_OCTAL.0,
    KpDecimal = SDLK_KP_DECIMAL.0,
    KpHexadecimal = SDLK_KP_HEXADECIMAL.0,
    LCtrl = SDLK_LCTRL.0,
    LShift = SDLK_LSHIFT.0,
    LAlt = SDLK_LALT.0,
    LGui = SDLK_LGUI.0,
    RCtrl = SDLK_RCTRL.0,
    RShift = SDLK_RSHIFT.0,
    RAlt = SDLK_RALT.0,
    RGui = SDLK_RGUI.0,
    Mode = SDLK_MODE.0,
    Sleep = SDLK_SLEEP.0,
    Wake = SDLK_WAKE.0,
    ChannelIncrement = SDLK_CHANNEL_INCREMENT.0,
    ChannelDecrement = SDLK_CHANNEL_DECREMENT.0,
    MediaPlay = SDLK_MEDIA_PLAY.0,
    MediaPause = SDLK_MEDIA_PAUSE.0,
    MediaRecord = SDLK_MEDIA_RECORD.0,
    MediaFastForward = SDLK_MEDIA_FAST_FORWARD.0,
    MediaRewind = SDLK_MEDIA_REWIND.0,
    MediaNextTrack = SDLK_MEDIA_NEXT_TRACK.0,
    MediaPreviousTrack = SDLK_MEDIA_PREVIOUS_TRACK.0,
    MediaStop = SDLK_MEDIA_STOP.0,
    MediaEject = SDLK_MEDIA_EJECT.0,
    MediaPlayPause = SDLK_MEDIA_PLAY_PAUSE.0,
    MediaSelect = SDLK_MEDIA_SELECT.0,
    AcNew = SDLK_AC_NEW.0,
    AcOpen = SDLK_AC_OPEN.0,
    AcClose = SDLK_AC_CLOSE.0,
    AcExit = SDLK_AC_EXIT.0,
    AcSave = SDLK_AC_SAVE.0,
    AcPrint = SDLK_AC_PRINT.0,
    AcProperties = SDLK_AC_PROPERTIES.0,
    AcSearch = SDLK_AC_SEARCH.0,
    AcHome = SDLK_AC_HOME.0,
    AcBack = SDLK_AC_BACK.0,
    AcForward = SDLK_AC_FORWARD.0,
    AcStop = SDLK_AC_STOP.0,
    AcRefresh = SDLK_AC_REFRESH.0,
    AcBookmarks = SDLK_AC_BOOKMARKS.0,
    SoftLeft = SDLK_SOFTLEFT.0,
    SoftRight = SDLK_SOFTRIGHT.0,
    Call = SDLK_CALL.0,
    EndCall = SDLK_ENDCALL.0,
}

impl Keycode {
    /// Creates a Keycode from a u32 value.
    ///
    /// This matches SDL3's SDL_Keycode type which is u32.
    pub fn from_u32(n: u32) -> Option<Keycode> {
        use self::Keycode::*;
        let n = SDL_Keycode(n);

        Some(match n {
            SDLK_SCANCODE_MASK => ScancodeMask,
            SDLK_UNKNOWN => Unknown,
            SDLK_RETURN => Return,
            SDLK_ESCAPE => Escape,
            SDLK_BACKSPACE => Backspace,
            SDLK_TAB => Tab,
            SDLK_SPACE => Space,
            SDLK_EXCLAIM => Exclaim,
            SDLK_DBLAPOSTROPHE => DblApostrophe,
            SDLK_HASH => Hash,
            SDLK_DOLLAR => Dollar,
            SDLK_PERCENT => Percent,
            SDLK_AMPERSAND => Ampersand,
            SDLK_APOSTROPHE => Apostrophe,
            SDLK_LEFTPAREN => LeftParen,
            SDLK_RIGHTPAREN => RightParen,
            SDLK_ASTERISK => Asterisk,
            SDLK_PLUS => Plus,
            SDLK_COMMA => Comma,
            SDLK_MINUS => Minus,
            SDLK_PERIOD => Period,
            SDLK_SLASH => Slash,
            SDLK_0 => _0,
            SDLK_1 => _1,
            SDLK_2 => _2,
            SDLK_3 => _3,
            SDLK_4 => _4,
            SDLK_5 => _5,
            SDLK_6 => _6,
            SDLK_7 => _7,
            SDLK_8 => _8,
            SDLK_9 => _9,
            SDLK_COLON => Colon,
            SDLK_SEMICOLON => Semicolon,
            SDLK_LESS => Less,
            SDLK_EQUALS => Equals,
            SDLK_GREATER => Greater,
            SDLK_QUESTION => Question,
            SDLK_AT => At,
            SDLK_LEFTBRACKET => LeftBracket,
            SDLK_BACKSLASH => Backslash,
            SDLK_RIGHTBRACKET => RightBracket,
            SDLK_CARET => Caret,
            SDLK_UNDERSCORE => Underscore,
            SDLK_GRAVE => Grave,
            SDLK_A => A,
            SDLK_B => B,
            SDLK_C => C,
            SDLK_D => D,
            SDLK_E => E,
            SDLK_F => F,
            SDLK_G => G,
            SDLK_H => H,
            SDLK_I => I,
            SDLK_J => J,
            SDLK_K => K,
            SDLK_L => L,
            SDLK_M => M,
            SDLK_N => N,
            SDLK_O => O,
            SDLK_P => P,
            SDLK_Q => Q,
            SDLK_R => R,
            SDLK_S => S,
            SDLK_T => T,
            SDLK_U => U,
            SDLK_V => V,
            SDLK_W => W,
            SDLK_X => X,
            SDLK_Y => Y,
            SDLK_Z => Z,
            SDLK_LEFTBRACE => LeftBrace,
            SDLK_PIPE => Pipe,
            SDLK_RIGHTBRACE => RightBrace,
            SDLK_TILDE => Tilde,
            SDLK_DELETE => Delete,
            SDLK_PLUSMINUS => PlusMinus,
            SDLK_CAPSLOCK => CapsLock,
            SDLK_F1 => F1,
            SDLK_F2 => F2,
            SDLK_F3 => F3,
            SDLK_F4 => F4,
            SDLK_F5 => F5,
            SDLK_F6 => F6,
            SDLK_F7 => F7,
            SDLK_F8 => F8,
            SDLK_F9 => F9,
            SDLK_F10 => F10,
            SDLK_F11 => F11,
            SDLK_F12 => F12,
            SDLK_PRINTSCREEN => PrintScreen,
            SDLK_SCROLLLOCK => ScrollLock,
            SDLK_PAUSE => Pause,
            SDLK_INSERT => Insert,
            SDLK_HOME => Home,
            SDLK_PAGEUP => PageUp,
            SDLK_END => End,
            SDLK_PAGEDOWN => PageDown,
            SDLK_RIGHT => Right,
            SDLK_LEFT => Left,
            SDLK_DOWN => Down,
            SDLK_UP => Up,
            SDLK_NUMLOCKCLEAR => NumLockClear,
            SDLK_KP_DIVIDE => KpDivide,
            SDLK_KP_MULTIPLY => KpMultiply,
            SDLK_KP_MINUS => KpMinus,
            SDLK_KP_PLUS => KpPlus,
            SDLK_KP_ENTER => KpEnter,
            SDLK_KP_1 => Kp1,
            SDLK_KP_2 => Kp2,
            SDLK_KP_3 => Kp3,
            SDLK_KP_4 => Kp4,
            SDLK_KP_5 => Kp5,
            SDLK_KP_6 => Kp6,
            SDLK_KP_7 => Kp7,
            SDLK_KP_8 => Kp8,
            SDLK_KP_9 => Kp9,
            SDLK_KP_0 => Kp0,
            SDLK_KP_PERIOD => KpPeriod,
            SDLK_APPLICATION => Application,
            SDLK_POWER => Power,
            SDLK_KP_EQUALS => KpEquals,
            SDLK_F13 => F13,
            SDLK_F14 => F14,
            SDLK_F15 => F15,
            SDLK_F16 => F16,
            SDLK_F17 => F17,
            SDLK_F18 => F18,
            SDLK_F19 => F19,
            SDLK_F20 => F20,
            SDLK_F21 => F21,
            SDLK_F22 => F22,
            SDLK_F23 => F23,
            SDLK_F24 => F24,
            SDLK_EXECUTE => Execute,
            SDLK_HELP => Help,
            SDLK_MENU => Menu,
            SDLK_SELECT => Select,
            SDLK_STOP => Stop,
            SDLK_AGAIN => Again,
            SDLK_UNDO => Undo,
            SDLK_CUT => Cut,
            SDLK_COPY => Copy,
            SDLK_PASTE => Paste,
            SDLK_FIND => Find,
            SDLK_MUTE => Mute,
            SDLK_VOLUMEUP => VolumeUp,
            SDLK_VOLUMEDOWN => VolumeDown,
            SDLK_KP_COMMA => KpComma,
            SDLK_KP_EQUALSAS400 => KpEqualsAs400,
            SDLK_ALTERASE => AltErase,
            SDLK_SYSREQ => SysReq,
            SDLK_CANCEL => Cancel,
            SDLK_CLEAR => Clear,
            SDLK_PRIOR => Prior,
            SDLK_RETURN2 => Return2,
            SDLK_SEPARATOR => Separator,
            SDLK_OUT => Out,
            SDLK_OPER => Oper,
            SDLK_CLEARAGAIN => ClearAgain,
            SDLK_CRSEL => CrSel,
            SDLK_EXSEL => ExSel,
            SDLK_KP_00 => Kp00,
            SDLK_KP_000 => Kp000,
            SDLK_THOUSANDSSEPARATOR => ThousandsSeparator,
            SDLK_DECIMALSEPARATOR => DecimalSeparator,
            SDLK_CURRENCYUNIT => CurrencyUnit,
            SDLK_CURRENCYSUBUNIT => CurrencySubunit,
            SDLK_KP_LEFTPAREN => KpLeftParen,
            SDLK_KP_RIGHTPAREN => KpRightParen,
            SDLK_KP_LEFTBRACE => KpLeftBrace,
            SDLK_KP_RIGHTBRACE => KpRightBrace,
            SDLK_KP_TAB => KpTab,
            SDLK_KP_BACKSPACE => KpBackspace,
            SDLK_KP_A => KpA,
            SDLK_KP_B => KpB,
            SDLK_KP_C => KpC,
            SDLK_KP_D => KpD,
            SDLK_KP_E => KpE,
            SDLK_KP_F => KpF,
            SDLK_KP_XOR => KpXor,
            SDLK_KP_POWER => KpPower,
            SDLK_KP_PERCENT => KpPercent,
            SDLK_KP_LESS => KpLess,
            SDLK_KP_GREATER => KpGreater,
            SDLK_KP_AMPERSAND => KpAmpersand,
            SDLK_KP_DBLAMPERSAND => KpDblAmpersand,
            SDLK_KP_VERTICALBAR => KpVerticalBar,
            SDLK_KP_DBLVERTICALBAR => KpDblVerticalBar,
            SDLK_KP_COLON => KpColon,
            SDLK_KP_HASH => KpHash,
            SDLK_KP_SPACE => KpSpace,
            SDLK_KP_AT => KpAt,
            SDLK_KP_EXCLAM => KpExclam,
            SDLK_KP_MEMSTORE => KpMemStore,
            SDLK_KP_MEMRECALL => KpMemRecall,
            SDLK_KP_MEMCLEAR => KpMemClear,
            SDLK_KP_MEMADD => KpMemAdd,
            SDLK_KP_MEMSUBTRACT => KpMemSubtract,
            SDLK_KP_MEMMULTIPLY => KpMemMultiply,
            SDLK_KP_MEMDIVIDE => KpMemDivide,
            SDLK_KP_PLUSMINUS => KpPlusMinus,
            SDLK_KP_CLEAR => KpClear,
            SDLK_KP_CLEARENTRY => KpClearEntry,
            SDLK_KP_BINARY => KpBinary,
            SDLK_KP_OCTAL => KpOctal,
            SDLK_KP_DECIMAL => KpDecimal,
            SDLK_KP_HEXADECIMAL => KpHexadecimal,
            SDLK_LCTRL => LCtrl,
            SDLK_LSHIFT => LShift,
            SDLK_LALT => LAlt,
            SDLK_LGUI => LGui,
            SDLK_RCTRL => RCtrl,
            SDLK_RSHIFT => RShift,
            SDLK_RALT => RAlt,
            SDLK_RGUI => RGui,
            SDLK_MODE => Mode,
            SDLK_SLEEP => Sleep,
            SDLK_WAKE => Wake,
            SDLK_CHANNEL_INCREMENT => ChannelIncrement,
            SDLK_CHANNEL_DECREMENT => ChannelDecrement,
            SDLK_MEDIA_PLAY => MediaPlay,
            SDLK_MEDIA_PAUSE => MediaPause,
            SDLK_MEDIA_RECORD => MediaRecord,
            SDLK_MEDIA_FAST_FORWARD => MediaFastForward,
            SDLK_MEDIA_REWIND => MediaRewind,
            SDLK_MEDIA_NEXT_TRACK => MediaNextTrack,
            SDLK_MEDIA_PREVIOUS_TRACK => MediaPreviousTrack,
            SDLK_MEDIA_STOP => MediaStop,
            SDLK_MEDIA_EJECT => MediaEject,
            SDLK_MEDIA_PLAY_PAUSE => MediaPlayPause,
            SDLK_MEDIA_SELECT => MediaSelect,
            SDLK_AC_NEW => AcNew,
            SDLK_AC_OPEN => AcOpen,
            SDLK_AC_CLOSE => AcClose,
            SDLK_AC_EXIT => AcExit,
            SDLK_AC_SAVE => AcSave,
            SDLK_AC_PRINT => AcPrint,
            SDLK_AC_PROPERTIES => AcProperties,
            SDLK_AC_SEARCH => AcSearch,
            SDLK_AC_HOME => AcHome,
            SDLK_AC_BACK => AcBack,
            SDLK_AC_FORWARD => AcForward,
            SDLK_AC_STOP => AcStop,
            SDLK_AC_REFRESH => AcRefresh,
            SDLK_AC_BOOKMARKS => AcBookmarks,
            SDLK_SOFTLEFT => SoftLeft,
            SDLK_SOFTRIGHT => SoftRight,
            SDLK_CALL => Call,
            SDLK_ENDCALL => EndCall,
            _ => return None,
        })
    }

    /// Creates a Keycode from an i32 value.
    ///
    /// # Deprecated
    /// This method is deprecated. Use `from_u32` instead, as SDL3's SDL_Keycode is u32.
    #[deprecated(
        since = "0.18.0",
        note = "Use from_u32 instead. SDL3's SDL_Keycode is u32."
    )]
    pub fn from_i32(n: i32) -> Option<Keycode> {
        Self::from_u32(n as u32)
    }

    pub fn to_ll(self) -> SDL_Keycode {
        SDL_Keycode(self as u32)
    }
}

use std::fmt;

impl fmt::Display for Keycode {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        write!(f, "{}", self.name())
    }
}

impl From<Keycode> for SDL_Keycode {
    fn from(k: Keycode) -> SDL_Keycode {
        k.to_ll()
    }
}

use crate::keyboard::Scancode;

impl Keycode {
    /// Gets the virtual key from a scancode. Returns None if there is no corresponding virtual key.
    #[doc(alias = "SDL_GetKeyFromScancode")]
    pub fn from_scancode(
        scancode: Scancode,
        modstate: SDL_Keymod,
        key_event: bool,
    ) -> Option<Keycode> {
        unsafe {
            let keycode_id = sys::keyboard::SDL_GetKeyFromScancode(
                transmute::<u32, sys::scancode::SDL_Scancode>(scancode as u32),
                modstate,
                key_event,
            );
            if keycode_id == sys::keycode::SDLK_UNKNOWN {
                None
            } else {
                Keycode::from_u32(keycode_id.0)
            }
        }
    }

    #[doc(alias = "SDL_GetKeyFromName")]
    pub fn from_name(name: &str) -> Option<Keycode> {
        unsafe {
            match CString::new(name) {
                Ok(name) => {
                    let keycode_id =
                        sys::keyboard::SDL_GetKeyFromName(name.as_ptr() as *const c_char);
                    if keycode_id == sys::keycode::SDLK_UNKNOWN {
                        None
                    } else {
                        Keycode::from_u32(keycode_id.0)
                    }
                }
                // string contains a nul byte - it won't match anything.
                Err(_) => None,
            }
        }
    }

    #[doc(alias = "SDL_GetKeyName")]
    pub fn name(self) -> String {
        // The name string pointer's contents _might_ change, depending on the last call to SDL_GetKeyName.
        // Knowing this, we must always return a new string.
        unsafe {
            let buf = sys::keyboard::SDL_GetKeyName(self.to_ll());
            CStr::from_ptr(buf as *const _).to_str().unwrap().to_owned()
        }
    }
}
