Path: blob/trunk/cpp/imehandler/windows/src/winapihandler.cc
2868 views
/*1Licensed to the Software Freedom Conservancy (SFC) under one2or more contributor license agreements. See the NOTICE file3distributed with this work for additional information4regarding copyright ownership. The SFC licenses this file5to you under the Apache License, Version 2.0 (the "License");6you may not use this file except in compliance with the License.7You may obtain a copy of the License at89http://www.apache.org/licenses/LICENSE-2.01011Unless required by applicable law or agreed to in writing, software12distributed under the License is distributed on an "AS IS" BASIS,13WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.14See the License for the specific language governing permissions and15limitations under the License.1617Author: [email protected]18*/1920#include "winapihandler.h"2122/*23#ifdef _MSC_VER24#include "stdafx.h"25#endif26*/2728#include <Objbase.h>29#include <Msctf.h>30#include <Windows.h>3132#include <sstream>33#include <iostream>34#include <algorithm>35#include <iomanip>3637// Windows registry constants.38#define MAX_KEY_LENGTH 25539#define MAX_VALUE_NAME 1638340#define MAX_LAYOUT_NAME_LENGTH 2554142WinapiHandler::WinapiHandler() {43// Open the registry and load the lookup table for available layouts.44LoadAvailableLayouts();45}4647std::string WinapiHandler::GetLayoutName(std::string hkl) const {48std::string name = "";49std::vector<keyboard_layout>::const_iterator it = keyboard_layouts.begin();50// First we look for existing entries without change.51while (it != keyboard_layouts.end() && (it->first != hkl)) { it++; }52if (it != keyboard_layouts.end()) {53name = it->second;54} else {55// If we didn't find it, then look if the name must be changed.56// For instance 04090409 is loaded as 00000409 (US).57if (hkl.find("0000") != 0) {58hkl = "0000"+hkl.substr(4);59it = keyboard_layouts.begin();60while (it != keyboard_layouts.end() && (it->first != hkl)) { it++; }61if (it != keyboard_layouts.end()) {62name = it->second;63}64}65}66return name;67}6869std::string WinapiHandler::GetLayoutHkl(const std::string& name) const {70std::string hkl = "";71std::vector<keyboard_layout>::const_iterator it = keyboard_layouts.begin();72while (it != keyboard_layouts.end() && (it->second != name)) { it++; }73if (it != keyboard_layouts.end()) {74hkl = it->first;75}76return hkl;77}7879void WinapiHandler::LoadAvailableLayouts() {80HKEY key;81if (RegOpenKeyEx(HKEY_LOCAL_MACHINE,82TEXT("SYSTEM\\CurrentControlSet\\Control\\Keyboard Layouts"),830, KEY_ENUMERATE_SUB_KEYS|KEY_QUERY_VALUE, &key) == ERROR_SUCCESS) {84TCHAR hkl_string[MAX_KEY_LENGTH]; // Buffer for subkey name.85DWORD key_name_size; // Size of name string.86DWORD nb_subkeys = 0; // Number of subkeys (layouts).87TCHAR layout_name[MAX_LAYOUT_NAME_LENGTH]; // Buffer for layout name.8889DWORD ret_code;90// Get the number of layouts.91ret_code = RegQueryInfoKey(key, // Key handle.92NULL, // Buffer for class name.93NULL, // Size of class string.94NULL, // Reserved.95&nb_subkeys, // Number of subkeys.96NULL, // Longest subkey size.97NULL, // Longest class string.98NULL, // Number of values for this key.99NULL, // Longest value name.100NULL, // Longest value data.101NULL, // Security descriptor.102NULL); // Last write time.103104if (ret_code == ERROR_SUCCESS) {105// Enumerate the subkeys, until RegEnumKeyEx fails.106if (nb_subkeys > 0) {107keyboard_layouts.clear();108keyboard_layouts.reserve(nb_subkeys);109110for (unsigned int i = 0; i < nb_subkeys; i++) {111key_name_size = MAX_KEY_LENGTH;112ret_code = RegEnumKeyEx(key, i, hkl_string, &key_name_size,113NULL, NULL, NULL, NULL);114if (ret_code == ERROR_SUCCESS) {115DWORD nb_bytes = sizeof(layout_name);116if (RegGetValue(key, hkl_string, "Layout Text", RRF_RT_ANY, NULL,117&layout_name, &nb_bytes) == ERROR_SUCCESS) {118keyboard_layout k = keyboard_layout(hkl_string, layout_name);119keyboard_layouts.push_back(k);120} else {121std::cerr << "error getting key modifiers" << std::endl;122}123}124}125}126RegCloseKey(key);127} else {128std::cerr << "could not access the registry" << std::endl;129}130}131}132133/*134* Returns the name of the engine currently active.135*/136std::string WinapiHandler::GetActiveEngine() const {137char * keyboard_name = new char[KL_NAMELENGTH];138GetKeyboardLayoutName(keyboard_name);139std::string engine_name = GetLayoutName(keyboard_name);140delete keyboard_name;141return engine_name;142}143144// http://msdn.microsoft.com/en-us/library/ms927178.aspx145std::string WinapiHandler::GetToggleKeys() const {146return "";147}148149bool WinapiHandler::IsActivated() const {150return true;151}152153/*154* Activates the first input among the loaded ones.155*/156void WinapiHandler::Deactivate() {157std::vector<std::string> engines = GetLoadedEngines();158if (engines.size() > 0) {159ActivateEngine(engines[0]);160} else {161std::cerr << "No default engine could be activated" << std::endl;162}163}164165/*166* Return the key combination as written in the list that enables167* the selection of such keys on the control panel.168*/169std::string WinapiHandler::GetNextEngineKeys() const {170std::string keys = "";171HKEY key;172if(RegOpenKeyEx(HKEY_CURRENT_USER, TEXT("Keyboard Layout"), 0,173KEY_QUERY_VALUE, &key) == ERROR_SUCCESS) {174char val[2];175DWORD nb_bytes = sizeof(val);176if(RegGetValue(key, "Toggle", "Hotkey", RRF_RT_REG_SZ, NULL, &val,177&nb_bytes) == ERROR_SUCCESS) {178switch (atoi(val)) {179case 1:180keys = "LEFT ALT+SHIFT";181break;182case 2:183keys = "CTRL+SHIFT";184break;185case 3:186keys = "NOT ASSIGNED";187break;188case 4:189keys = "GRAVE ACCENT";190break;191}192}193RegCloseKey(key);194}195return keys;196}197198/*199* Returns the HKL of loaded engines.200* see http://msdn.microsoft.com/en-us/goglobal/bb688135.aspx201* see http://blogs.msdn.com/b/michkap/archive/2004/12/16/318271.aspx202*/203std::vector<std::string> WinapiHandler::GetLoadedEngines() const {204std::vector<std::string> loaded_engines;205int nb_keyboards = GetKeyboardLayoutList(0, 0);206HKL* keyboards = new HKL[nb_keyboards];207GetKeyboardLayoutList(nb_keyboards, keyboards);208std::stringstream ss;209for (int i = 0 ; i < nb_keyboards ; i++) {210ss << keyboards[i];211std::string keyboard_name = ss.str();212// On x64 machines 8 useless characters are included...213// TODO(timothe): fix that at compile time instead of the "if" here.214if ( keyboard_name.size() > 8 ) {215keyboard_name = keyboard_name.substr(8);216}217loaded_engines.push_back(GetLayoutName(keyboard_name));218// Reset the string stream.219ss.str("");220}221delete keyboards;222return loaded_engines;223}224225std::vector<std::string> WinapiHandler::GetAvailableEngines() const {226std::vector<std::string> available_engines;227available_engines.reserve(keyboard_layouts.size());228for(std::vector<keyboard_layout>::const_iterator it =229keyboard_layouts.begin() ; it != keyboard_layouts.end() ; it++) {230available_engines.push_back(it->second);231}232return available_engines;233}234235int WinapiHandler::LoadEngines(const std::vector<std::string>& engine_names) {236int nb_loaded_engines = 0;237if (!engine_names.empty()) {238// First we get the currently loaded engines, to unload the unused239// ones afterwards.240std::vector<std::string> loaded_engines = GetLoadedEngines();241242// Then we load the new ones, for now it is the same as activating them243for (std::vector<std::string>::const_iterator it = engine_names.begin() ;244it != engine_names.end() ; it++) {245// std::cout <<"Loading keyboard " << it->c_str() << std::endl;246// XXX: bug: this never returns NULL as the documentation says...247if (LoadKeyboardLayout(GetLayoutHkl(*it).c_str(), 0) != NULL) {248++nb_loaded_engines;249}250}251252// Then we unload the new ones so that there was always at least one engine253// active (windows does not accept unloading the engines first, then254// activate the new ones)255for (std::vector<std::string>::const_iterator it =256loaded_engines.begin() ; it != loaded_engines.end() ; it++) {257if (std::find(engine_names.begin(), engine_names.end(), *it) ==258engine_names.end()) {259// We need to activate it to get the HKL...260ActivateEngine(*it);261HKL hkl = GetKeyboardLayout(0);262// And we activate another one to be able to unload it263ActivateEngine(engine_names[0]);264if (!UnloadKeyboardLayout(hkl)) {265std::cerr << "could not unload keyboard layout " << *it;266std::cerr << std::endl;267}268}269}270}271return nb_loaded_engines;272}273274bool WinapiHandler::ActivateEngine(const std::string& engine) {275std::string hkl = GetLayoutHkl(engine);276if (hkl != "") {277return LoadKeyboardLayout(hkl.c_str(),278KLF_ACTIVATE|KLF_REPLACELANG|KLF_SETFORPROCESS)279!= NULL;280}281return false;282}283284std::string WinapiHandler::GetModifiers(DWORD value) {285std::stringstream poss;286std::stringstream modss;287//if ((value & MOD_LEFT) != 0 ) {288//poss << "LEFT";289//}290//if ((value & MOD_RIGHT) != 0) {291//if (!poss.str().empty()) poss << "/";292//poss << "RIGHT";293//}294295if ((value & MOD_ALT) != 0) {296if (!modss.str().empty()) modss << "+";297modss << "ALT";298}299if ((value & MOD_CONTROL) != 0) {300if(!modss.str().empty()) modss << "+";301modss << "CTRL";302}303if ((value & MOD_SHIFT) != 0) {304if (!modss.str().empty()) modss << "+";305modss << "SHIFT";306}307308if ((value & MOD_ON_KEYUP) != 0) {309modss << "(ON_KEY_UP)";310}311312if ((value & MOD_IGNORE_ALL_MODIFIER) != 0) {313modss << "(MOD_IGNORE_ALL_MODIFIER)";314}315316std::string pos = poss.str();317std::string mods = modss.str();318return pos+mods;319}320321std::string WinapiHandler::GetSwitchingKeysForEngine(std::string engine) {322std::string keys = "";323HKEY key;324if (RegOpenKeyEx(HKEY_CURRENT_USER,325TEXT("Control Panel\\Input Method\\Hot Keys"),3260,327KEY_ENUMERATE_SUB_KEYS|KEY_QUERY_VALUE, &key)328==ERROR_SUCCESS) {329TCHAR subkey_buffer[MAX_KEY_LENGTH];330DWORD subname_size = 0;331DWORD nb_subkeys = 0;332333// Get the class name and the value count.334if (RegQueryInfoKey(key, // key handle335NULL, // buffer for class name336NULL, // size of class string337NULL, // reserved338&nb_subkeys, // number of subkeys339NULL, // longest subkey size340NULL, // longest class string341NULL, // number of values for this key342NULL, // longest value name343NULL, // longest value data344NULL, // security descriptor345NULL) // last write time346== ERROR_SUCCESS) {347// Enumerate the subkeys, until RegEnumKeyEx fails.348if (nb_subkeys > 0) {349for (unsigned int i = 0; i < nb_subkeys; i++) {350subname_size = MAX_KEY_LENGTH;351if (RegEnumKeyEx(key, i, subkey_buffer, &subname_size,352NULL, NULL, NULL, NULL) == ERROR_SUCCESS) {353DWORD modifiers = 0;354DWORD target_IME = 0;355DWORD virtual_key = 0;356DWORD nb_bytes = sizeof(modifiers);357if (RegGetValue(key, subkey_buffer, "Target IME", RRF_RT_ANY,358NULL, &target_IME, &nb_bytes) == ERROR_SUCCESS359&& (target_IME != 0)) {360std::stringstream hkl;361hkl << std::hex << std::setw(8) << std::setfill('0') <<362target_IME;363std::string layout_hkl = GetLayoutHkl(engine);364if (layout_hkl.find("0000") == 0) {365layout_hkl = layout_hkl.substr(4)+ layout_hkl.substr(4);366}367if (hkl.str() == layout_hkl) {368RegGetValue(key, subkey_buffer, "Key Modifiers", RRF_RT_ANY,369NULL, &modifiers, &nb_bytes);370RegGetValue(key, subkey_buffer, "Virtual Key", RRF_RT_ANY,371NULL, &virtual_key, &nb_bytes);372// Reset the string stream to build the resulting string.373hkl.str("");374hkl << GetModifiers(modifiers) << "+" <<375static_cast<char>(virtual_key);376keys = hkl.str();377}378}379}380}381}382}383RegCloseKey(key);384}385return keys;386}387388/*389Also look at:390HWND hWnd = GetForegroundWindow();391DWORD threadId = GetWindowThreadProcessId(hWnd, NULL);392393unsigned int hkl = 0;394std::stringstream ss;395ss << std::hex << jp2;396ss >> hkl;397PostMessage(hWnd, WM_INPUTLANGCHANGEREQUEST, 0, hkl);398399But this indices a latency in activating the engines that make the tests400flacky and fails...401*/402403404