// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "ui/events/keycodes/keyboard_code_conversion_xkb.h"

#include "build/build_config.h"
#include "ui/events/keycodes/dom/dom_key.h"

#ifndef XK_dead_greek
#define XK_dead_greek 0xfe8c
#endif

namespace ui {

// We support dead keys beyond those with predefined keysym names,
// expressed as numeric constants with |kDeadKeyFlag| set.
// Xkbcommon accepts and does not use any bits in 0x0E000000.
//
constexpr xkb_keysym_t kDeadKeyFlag = 0x08000000;
constexpr int32_t kUnicodeMax = 0x10FFFF;

DomKey NonPrintableXKeySymToDomKey(xkb_keysym_t keysym) {
  switch (keysym) {
    case XKB_KEY_BackSpace:
      return DomKey::BACKSPACE;
    case XKB_KEY_Tab:
    case XKB_KEY_KP_Tab:
    case XKB_KEY_ISO_Left_Tab:
      return DomKey::TAB;
    case XKB_KEY_Clear:
    case XKB_KEY_KP_Begin:
    case XKB_KEY_XF86Clear:
      return DomKey::CLEAR;
    case XKB_KEY_Return:
    case XKB_KEY_KP_Enter:
      return DomKey::ENTER;
    case XKB_KEY_Linefeed:
      return DomKey::ENTER;
    case XKB_KEY_Pause:
      return DomKey::PAUSE;
    case XKB_KEY_Scroll_Lock:
      return DomKey::SCROLL_LOCK;
    case XKB_KEY_Escape:
      return DomKey::ESCAPE;
    case XKB_KEY_Multi_key:
      return DomKey::COMPOSE;
    case XKB_KEY_Kanji:
      return DomKey::KANJI_MODE;
    case XKB_KEY_Muhenkan:
      return DomKey::NON_CONVERT;
    case XKB_KEY_Henkan_Mode:
      return DomKey::CONVERT;
    case XKB_KEY_Romaji:
      return DomKey::ROMAJI;
    case XKB_KEY_Hiragana:
      return DomKey::HIRAGANA;
    case XKB_KEY_Katakana:
      return DomKey::KATAKANA;
    case XKB_KEY_Hiragana_Katakana:
      return DomKey::HIRAGANA_KATAKANA;
    case XKB_KEY_Zenkaku:
      return DomKey::ZENKAKU;
    case XKB_KEY_Hankaku:
      return DomKey::HANKAKU;
    case XKB_KEY_Zenkaku_Hankaku:
      return DomKey::ZENKAKU_HANKAKU;
    case XKB_KEY_Kana_Lock:
      return DomKey::KANA_MODE;
    case XKB_KEY_Eisu_Shift:
    case XKB_KEY_Eisu_toggle:
      return DomKey::ALPHANUMERIC;
    case XKB_KEY_Hangul:
      return DomKey::HANGUL_MODE;
    case XKB_KEY_Hangul_Hanja:
      return DomKey::HANJA_MODE;
    case XKB_KEY_Codeinput:
      return DomKey::CODE_INPUT;
    case XKB_KEY_SingleCandidate:
      return DomKey::SINGLE_CANDIDATE;
    case XKB_KEY_MultipleCandidate:
      return DomKey::ALL_CANDIDATES;
    case XKB_KEY_PreviousCandidate:
      return DomKey::PREVIOUS_CANDIDATE;
    case XKB_KEY_Home:
    case XKB_KEY_KP_Home:
      return DomKey::HOME;
    case XKB_KEY_Left:
    case XKB_KEY_KP_Left:
      return DomKey::ARROW_LEFT;
    case XKB_KEY_Up:
    case XKB_KEY_KP_Up:
      return DomKey::ARROW_UP;
    case XKB_KEY_Right:
    case XKB_KEY_KP_Right:
      return DomKey::ARROW_RIGHT;
    case XKB_KEY_Down:
    case XKB_KEY_KP_Down:
      return DomKey::ARROW_DOWN;
    case XKB_KEY_Prior:
    case XKB_KEY_KP_Prior:
      return DomKey::PAGE_UP;
    case XKB_KEY_Next:
    case XKB_KEY_KP_Next:
    case XKB_KEY_XF86ScrollDown:
      return DomKey::PAGE_DOWN;
    case XKB_KEY_End:
    case XKB_KEY_KP_End:
    case XKB_KEY_XF86ScrollUp:
      return DomKey::END;
    case XKB_KEY_Select:
      return DomKey::SELECT;
    case XKB_KEY_Print:
      return DomKey::PRINT;
    case XKB_KEY_Execute:
      return DomKey::EXECUTE;
    case XKB_KEY_Insert:
    case XKB_KEY_KP_Insert:
      return DomKey::INSERT;
    case XKB_KEY_Undo:
      return DomKey::UNDO;
    case XKB_KEY_Redo:
      return DomKey::REDO;
    case XKB_KEY_Menu:
      return DomKey::CONTEXT_MENU;
    case XKB_KEY_Find:
      return DomKey::FIND;
    case XKB_KEY_Cancel:
      return DomKey::CANCEL;
    case XKB_KEY_Help:
      return DomKey::HELP;
    case XKB_KEY_Break:
    case XKB_KEY_3270_Attn:
      return DomKey::ATTN;
    case XKB_KEY_Mode_switch:
      return DomKey::MODE_CHANGE;
    case XKB_KEY_Num_Lock:
      return DomKey::NUM_LOCK;
    case XKB_KEY_F1:
    case XKB_KEY_KP_F1:
      return DomKey::F1;
    case XKB_KEY_F2:
    case XKB_KEY_KP_F2:
      return DomKey::F2;
    case XKB_KEY_F3:
    case XKB_KEY_KP_F3:
      return DomKey::F3;
    case XKB_KEY_F4:
    case XKB_KEY_KP_F4:
      return DomKey::F4;
    case XKB_KEY_F5:
      return DomKey::F5;
    case XKB_KEY_F6:
      return DomKey::F6;
    case XKB_KEY_F7:
      return DomKey::F7;
    case XKB_KEY_F8:
      return DomKey::F8;
    case XKB_KEY_F9:
      return DomKey::F9;
    case XKB_KEY_F10:
      return DomKey::F10;
    case XKB_KEY_F11:
      return DomKey::F11;
    case XKB_KEY_F12:
      return DomKey::F12;
    case XKB_KEY_XF86Tools:  // XKB 'inet' mapping of F13
    case XKB_KEY_F13:
      return DomKey::F13;
    case XKB_KEY_F14:
    case XKB_KEY_XF86Launch5:  // XKB 'inet' mapping of F14
      return DomKey::F14;
    case XKB_KEY_F15:
    case XKB_KEY_XF86Launch6:  // XKB 'inet' mapping of F15
      return DomKey::F15;
    case XKB_KEY_F16:
    case XKB_KEY_XF86Launch7:  // XKB 'inet' mapping of F16
      return DomKey::F16;
    case XKB_KEY_F17:
    case XKB_KEY_XF86Launch8:  // XKB 'inet' mapping of F17
      return DomKey::F17;
    case XKB_KEY_F18:
    case XKB_KEY_XF86Launch9:  // XKB 'inet' mapping of F18
      return DomKey::F18;
    case XKB_KEY_F19:
      return DomKey::F19;
    case XKB_KEY_F20:
      return DomKey::F20;
    case XKB_KEY_F21:
      return DomKey::F21;
    case XKB_KEY_F22:
      return DomKey::F22;
    case XKB_KEY_F23:
      return DomKey::F23;
    case XKB_KEY_F24:
      return DomKey::F24;
    case XKB_KEY_Shift_L:
    case XKB_KEY_Shift_R:
      return DomKey::SHIFT;
    case XKB_KEY_Control_L:
    case XKB_KEY_Control_R:
      return DomKey::CONTROL;
    case XKB_KEY_Caps_Lock:
      return DomKey::CAPS_LOCK;
#if defined(OS_CHROMEOS)
    case XKB_KEY_Meta_L:
    case XKB_KEY_Meta_R:
    case XKB_KEY_Alt_L:
    case XKB_KEY_Alt_R:
      // The Shift+Alt generates a KeySym for the Meta key. On ChromeOS the Meta
      // key is not used and we should still get the Alt key. crbug.com/541468.
      return DomKey::ALT;
#else
    case XKB_KEY_Meta_L:
    case XKB_KEY_Meta_R:
      return DomKey::META;
    case XKB_KEY_Alt_L:
    case XKB_KEY_Alt_R:
      return DomKey::ALT;
#endif  // defined(OS_CHROMEOS)
    case XKB_KEY_Super_L:
    case XKB_KEY_Super_R:
      return DomKey::META;
    case XKB_KEY_Hyper_L:
    case XKB_KEY_Hyper_R:
      return DomKey::HYPER;
    case XKB_KEY_Delete:
      return DomKey::DEL;
    case XKB_KEY_SunProps:
      return DomKey::PROPS;
    case XKB_KEY_XF86Next_VMode:
      return DomKey::VIDEO_MODE_NEXT;
    case XKB_KEY_XF86MonBrightnessUp:
      return DomKey::BRIGHTNESS_UP;
    case XKB_KEY_XF86MonBrightnessDown:
      return DomKey::BRIGHTNESS_DOWN;
    case XKB_KEY_XF86Standby:
    case XKB_KEY_XF86Sleep:
    case XKB_KEY_XF86Suspend:
      return DomKey::STANDBY;
    case XKB_KEY_XF86AudioLowerVolume:
      return DomKey::AUDIO_VOLUME_DOWN;
    case XKB_KEY_XF86AudioMute:
      return DomKey::AUDIO_VOLUME_MUTE;
    case XKB_KEY_XF86AudioRaiseVolume:
      return DomKey::AUDIO_VOLUME_UP;
    case XKB_KEY_XF86AudioPlay:
      return DomKey::MEDIA_PLAY_PAUSE;
    case XKB_KEY_XF86AudioStop:
      return DomKey::MEDIA_STOP;
    case XKB_KEY_XF86AudioPrev:
      return DomKey::MEDIA_TRACK_PREVIOUS;
    case XKB_KEY_XF86AudioNext:
      return DomKey::MEDIA_TRACK_NEXT;
    case XKB_KEY_XF86HomePage:
      return DomKey::BROWSER_HOME;
    case XKB_KEY_XF86Mail:
      return DomKey::LAUNCH_MAIL;
    case XKB_KEY_XF86Search:
      return DomKey::BROWSER_SEARCH;
    case XKB_KEY_XF86AudioRecord:
      return DomKey::MEDIA_RECORD;
    case XKB_KEY_XF86Calculator:
      return DomKey::LAUNCH_CALCULATOR;
    case XKB_KEY_XF86Calendar:
      return DomKey::LAUNCH_CALENDAR;
    case XKB_KEY_XF86Back:
      return DomKey::BROWSER_BACK;
    case XKB_KEY_XF86Forward:
      return DomKey::BROWSER_FORWARD;
    case XKB_KEY_XF86Stop:
      return DomKey::BROWSER_STOP;
    case XKB_KEY_XF86Refresh:
    case XKB_KEY_XF86Reload:
      return DomKey::BROWSER_REFRESH;
    case XKB_KEY_XF86PowerOff:
      return DomKey::POWER_OFF;
    case XKB_KEY_XF86WakeUp:
      return DomKey::WAKE_UP;
    case XKB_KEY_XF86Eject:
      return DomKey::EJECT;
    case XKB_KEY_XF86ScreenSaver:
      return DomKey::LAUNCH_SCREEN_SAVER;
    case XKB_KEY_XF86WWW:
      return DomKey::LAUNCH_WEB_BROWSER;
    case XKB_KEY_XF86Favorites:
      return DomKey::BROWSER_FAVORITES;
    case XKB_KEY_XF86AudioPause:
      return DomKey::MEDIA_PAUSE;
    case XKB_KEY_XF86AudioMedia:
    case XKB_KEY_XF86Music:
      return DomKey::LAUNCH_MUSIC_PLAYER;
    case XKB_KEY_XF86MyComputer:
    case XKB_KEY_XF86Explorer:
      return DomKey::LAUNCH_MY_COMPUTER;
    case XKB_KEY_XF86AudioRewind:
      return DomKey::MEDIA_REWIND;
    case XKB_KEY_XF86CD:
    case XKB_KEY_XF86Video:
      return DomKey::LAUNCH_MEDIA_PLAYER;
    case XKB_KEY_XF86Close:
      return DomKey::CLOSE;
    case XKB_KEY_XF86Copy:
    case XKB_KEY_SunCopy:
      return DomKey::COPY;
    case XKB_KEY_XF86Cut:
    case XKB_KEY_SunCut:
      return DomKey::CUT;
    case XKB_KEY_XF86Display:
      return DomKey::DISPLAY_SWAP;
    case XKB_KEY_XF86Excel:
      return DomKey::LAUNCH_SPREADSHEET;
    case XKB_KEY_XF86LogOff:
      return DomKey::LOG_OFF;
    case XKB_KEY_XF86New:
      return DomKey::NEW;
    case XKB_KEY_XF86Open:
    case XKB_KEY_SunOpen:
      return DomKey::OPEN;
    case XKB_KEY_XF86Paste:
    case XKB_KEY_SunPaste:
      return DomKey::PASTE;
    case XKB_KEY_XF86Reply:
      return DomKey::MAIL_REPLY;
    case XKB_KEY_XF86Save:
      return DomKey::SAVE;
    case XKB_KEY_XF86Send:
      return DomKey::MAIL_SEND;
    case XKB_KEY_XF86Spell:
      return DomKey::SPELL_CHECK;
    case XKB_KEY_XF86SplitScreen:
      return DomKey::SPLIT_SCREEN_TOGGLE;
    case XKB_KEY_XF86Word:
    case XKB_KEY_XF86OfficeHome:
      return DomKey::LAUNCH_WORD_PROCESSOR;
    case XKB_KEY_XF86ZoomIn:
      return DomKey::ZOOM_IN;
    case XKB_KEY_XF86ZoomOut:
      return DomKey::ZOOM_OUT;
    case XKB_KEY_XF86WebCam:
      return DomKey::LAUNCH_WEB_CAM;
    case XKB_KEY_XF86MailForward:
      return DomKey::MAIL_FORWARD;
    case XKB_KEY_XF86AudioForward:
      return DomKey::MEDIA_FAST_FORWARD;
    case XKB_KEY_XF86AudioRandomPlay:
      return DomKey::RANDOM_TOGGLE;
    case XKB_KEY_XF86Subtitle:
      return DomKey::SUBTITLE;
    case XKB_KEY_XF86Hibernate:
      return DomKey::HIBERNATE;
    case XKB_KEY_3270_EraseEOF:
      return DomKey::ERASE_EOF;
    case XKB_KEY_3270_Play:
      return DomKey::PLAY;
    case XKB_KEY_3270_ExSelect:
      return DomKey::EX_SEL;
    case XKB_KEY_3270_CursorSelect:
      return DomKey::CR_SEL;
    case XKB_KEY_3270_PrintScreen:
      return DomKey::PRINT_SCREEN;
    case XKB_KEY_ISO_Level3_Shift:
      return DomKey::ALT_GRAPH;
    case XKB_KEY_ISO_Level3_Latch:
      return DomKey::ALT_GRAPH_LATCH;
    case XKB_KEY_ISO_Level5_Shift:
      return DomKey::SHIFT_LEVEL5;
    case XKB_KEY_ISO_Next_Group:
      return DomKey::GROUP_NEXT;
    case XKB_KEY_ISO_Prev_Group:
      return DomKey::GROUP_PREVIOUS;
    case XKB_KEY_ISO_First_Group:
      return DomKey::GROUP_FIRST;
    case XKB_KEY_ISO_Last_Group:
      return DomKey::GROUP_LAST;
    case XKB_KEY_dead_grave:
      // combining grave accent
      return DomKey::DeadKeyFromCombiningCharacter(0x0300);
    case XKB_KEY_dead_acute:
      // combining acute accent
      return DomKey::DeadKeyFromCombiningCharacter(0x0301);
    case XKB_KEY_dead_circumflex:
      // combining circumflex accent
      return DomKey::DeadKeyFromCombiningCharacter(0x0302);
    case XKB_KEY_dead_tilde:
      // combining tilde
      return DomKey::DeadKeyFromCombiningCharacter(0x0303);
    case XKB_KEY_dead_macron:
      // combining macron
      return DomKey::DeadKeyFromCombiningCharacter(0x0304);
    case XKB_KEY_dead_breve:
      // combining breve
      return DomKey::DeadKeyFromCombiningCharacter(0x0306);
    case XKB_KEY_dead_abovedot:
      // combining dot above
      return DomKey::DeadKeyFromCombiningCharacter(0x0307);
    case XKB_KEY_dead_diaeresis:
      // combining diaeresis
      return DomKey::DeadKeyFromCombiningCharacter(0x0308);
    case XKB_KEY_dead_abovering:
      // combining ring above
      return DomKey::DeadKeyFromCombiningCharacter(0x030A);
    case XKB_KEY_dead_doubleacute:
      // combining double acute accent
      return DomKey::DeadKeyFromCombiningCharacter(0x030B);
    case XKB_KEY_dead_caron:
      // combining caron
      return DomKey::DeadKeyFromCombiningCharacter(0x030C);
    case XKB_KEY_dead_cedilla:
      // combining cedilla
      return DomKey::DeadKeyFromCombiningCharacter(0x0327);
    case XKB_KEY_dead_ogonek:
      // combining ogonek
      return DomKey::DeadKeyFromCombiningCharacter(0x0328);
    case XKB_KEY_dead_iota:
      // combining greek ypogegrammeni
      return DomKey::DeadKeyFromCombiningCharacter(0x0345);
    case XKB_KEY_dead_voiced_sound:
      // combining voiced sound mark
      return DomKey::DeadKeyFromCombiningCharacter(0x3099);
    case XKB_KEY_dead_semivoiced_sound:
      // combining semi-voiced sound mark
      return DomKey::DeadKeyFromCombiningCharacter(0x309A);
    case XKB_KEY_dead_belowdot:
      // combining dot below
      return DomKey::DeadKeyFromCombiningCharacter(0x0323);
    case XKB_KEY_dead_hook:
      // combining hook above
      return DomKey::DeadKeyFromCombiningCharacter(0x0309);
    case XKB_KEY_dead_horn:
      // combining horn
      return DomKey::DeadKeyFromCombiningCharacter(0x031B);
    case XKB_KEY_dead_stroke:
      // combining long solidus overlay
      return DomKey::DeadKeyFromCombiningCharacter(0x0338);
    case XKB_KEY_dead_abovecomma:
      // combining comma above
      return DomKey::DeadKeyFromCombiningCharacter(0x0313);
    case XKB_KEY_dead_abovereversedcomma:
      // combining reversed comma above
      return DomKey::DeadKeyFromCombiningCharacter(0x0314);
    case XKB_KEY_dead_doublegrave:
      // combining double grave accent
      return DomKey::DeadKeyFromCombiningCharacter(0x030F);
    case XKB_KEY_dead_belowring:
      // combining ring below
      return DomKey::DeadKeyFromCombiningCharacter(0x0325);
    case XKB_KEY_dead_belowmacron:
      // combining macron below
      return DomKey::DeadKeyFromCombiningCharacter(0x0331);
    case XKB_KEY_dead_belowcircumflex:
      // combining circumflex accent below
      return DomKey::DeadKeyFromCombiningCharacter(0x032D);
    case XKB_KEY_dead_belowtilde:
      // combining tilde below
      return DomKey::DeadKeyFromCombiningCharacter(0x0330);
    case XKB_KEY_dead_belowbreve:
      // combining breve below
      return DomKey::DeadKeyFromCombiningCharacter(0x032E);
    case XKB_KEY_dead_belowdiaeresis:
      // combining diaeresis below
      return DomKey::DeadKeyFromCombiningCharacter(0x0324);
    case XKB_KEY_dead_invertedbreve:
      // combining inverted breve
      return DomKey::DeadKeyFromCombiningCharacter(0x0311);
    case XKB_KEY_dead_belowcomma:
      // combining comma below
      return DomKey::DeadKeyFromCombiningCharacter(0x0326);
    case XKB_KEY_dead_currency:
      // currency sign
      return DomKey::DeadKeyFromCombiningCharacter(0x00A4);
    case XKB_KEY_dead_greek:
      // greek question mark
      return DomKey::DeadKeyFromCombiningCharacter(0x037E);
    default:
      if (keysym & kDeadKeyFlag) {
        int32_t character = keysym & ~kDeadKeyFlag;
        if ((character >= 0) && (character <= kUnicodeMax)) {
          return DomKey::DeadKeyFromCombiningCharacter(character);
        }
      }
      return DomKey::NONE;
  }
}
DomKey XKeySymToDomKey(xkb_keysym_t keysym, base::char16 character) {
  DomKey dom_key = NonPrintableXKeySymToDomKey(keysym);
  if (dom_key != DomKey::NONE)
    return dom_key;
  return DomKey::FromCharacter(character);
}

}  // namespace ui
