Back to index

unity  6.0.0
KeyboardUtil.cpp
Go to the documentation of this file.
00001 // -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*-
00002 /*
00003  * Copyright (C) 2011 Canonical Ltd
00004  *
00005  * This program is free software: you can redistribute it and/or modify
00006  * it under the terms of the GNU General Public License version 3 as
00007  * published by the Free Software Foundation.
00008  *
00009  * This program is distributed in the hope that it will be useful,
00010  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012  * GNU General Public License for more details.
00013  *
00014  * You should have received a copy of the GNU General Public License
00015  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
00016  *
00017  * Authored by: Jason Smith <jason.smith@canonical.com>
00018  */
00019 
00020 #include <gdk/gdk.h>
00021 #include <string.h>
00022 #include <cmath>
00023 
00024 #include "KeyboardUtil.h"
00025 
00026 namespace unity {
00027 namespace ui {
00028 
00029 KeyboardUtil::KeyboardUtil(Display *display)
00030   : display_(display)
00031 {
00032   unsigned int fetch_mask = XkbGBN_KeyNamesMask | XkbGBN_ClientSymbolsMask | XkbGBN_GeometryMask;
00033   keyboard_ = XkbGetKeyboard (display, fetch_mask, XkbUseCoreKbd);
00034 }
00035 
00036 KeyboardUtil::~KeyboardUtil()
00037 {
00038   XkbFreeKeyboard (keyboard_, 0, True);
00039 }
00040 
00041 bool KeyboardUtil::FindKeyInGeometry(XkbGeometryPtr geo, char *key_name, int& res_section, XkbBoundsRec& res_bounds) const
00042 {
00043   // seems that Xkb does not give null terminated strings... was painful
00044   int name_length = XkbKeyNameLength;
00045 
00046   int sections_in_geometry = geo->num_sections;
00047   for (int i = 0; i < sections_in_geometry; i++)
00048   {
00049     XkbSectionPtr section = &geo->sections[i];
00050 
00051     int rows_in_section = section->num_rows;
00052     for (int j = 0; j < rows_in_section; j++)
00053     {
00054       XkbRowPtr row = &section->rows[j];
00055       int keys_in_row = row->num_keys;
00056       for (int k = 0; k < keys_in_row; k++)
00057       {
00058         XkbKeyPtr key = &row->keys[k];
00059         if (!strncmp (key->name.name, key_name, name_length))
00060         {
00061           res_section = i;
00062           res_bounds = GetAbsoluteKeyBounds(key, row, section, geo);
00063           return true;
00064         }
00065         // end key
00066       }
00067       // end row
00068     }
00069     // end section
00070   }
00071 
00072   return false;
00073 }
00074 
00075 bool KeyboardUtil::CompareOffsets(int current_x, int current_y, int best_x, int best_y) const
00076 {
00077   // never EVER prefer something higher on the keyboard than what we have
00078   if (current_y > best_y)
00079     return false;
00080 
00081   if (current_x < best_x || current_y < best_y)
00082     return true;
00083 
00084   return false;
00085 }
00086 
00087 guint KeyboardUtil::ConvertKeyToKeycode(XkbKeyPtr key) const
00088 {
00089   int min_code = keyboard_->min_key_code;
00090   int max_code = keyboard_->max_key_code;
00091 
00092   for (int i = min_code; i < max_code; i++)
00093   {
00094     if (!strncmp(key->name.name, keyboard_->names->keys[i].name, XkbKeyNameLength))
00095       return i;
00096   }
00097   return 0;
00098 }
00099 
00100 XkbBoundsRec KeyboardUtil::GetAbsoluteKeyBounds(XkbKeyPtr key, XkbRowPtr row, XkbSectionPtr section, XkbGeometryPtr geo) const
00101 {
00102   XkbShapePtr shape = XkbKeyShape(geo, key);
00103 
00104   XkbBoundsRec result;
00105   result = shape->bounds;
00106 
00107   int x_offset = 0;
00108   int y_offset = 0;
00109   int i = 0;
00110   while (&row->keys[i] != key)
00111   {
00112     XkbKeyPtr local_key = &row->keys[i];
00113     XkbShapePtr local_shape = XkbKeyShape(geo, local_key);
00114 
00115     if (row->vertical)
00116       y_offset += local_shape->bounds.y2 - local_shape->bounds.y1;
00117     else
00118       x_offset += local_shape->bounds.x2 - local_shape->bounds.x1;
00119 
00120     i++;
00121   }
00122 
00123   result.x1 += row->left + section->left + key->gap + x_offset;
00124   result.x2 += row->left + section->left + key->gap + x_offset;
00125   result.y1 += row->top + section->top + key->gap + y_offset;
00126   result.y2 += row->top + section->top + key->gap + y_offset;
00127 
00128   return result;
00129 }
00130 
00131 bool KeyboardUtil::FindKeyInSectionAboveBounds(XkbGeometryPtr geo, int section_index, XkbBoundsRec const& target_bounds, guint &keycode) const
00132 {
00133   XkbKeyPtr best = NULL;
00134   int best_x_offset = G_MAXINT;
00135   int best_y_offset = G_MAXINT;
00136 
00137   int sections_in_geometry = geo->num_sections;
00138   for (int k = 0; k < sections_in_geometry; k++)
00139   {
00140 
00141   XkbSectionPtr section = &geo->sections[section_index];
00142   int rows_in_section = section->num_rows;
00143   for (int i = 0; i < rows_in_section; i++)
00144   {
00145     XkbRowPtr row = &section->rows[i];
00146 
00147     int keys_in_row = row->num_keys;
00148     for (int j = 0; j < keys_in_row; j++)
00149     {
00150       XkbKeyPtr key = &row->keys[j];
00151       XkbBoundsRec bounds = GetAbsoluteKeyBounds (key, row, section, geo);
00152 
00153       // make sure we are actually over the target bounds
00154       int center = (bounds.x1 + bounds.x2) / 2;
00155       if (center < target_bounds.x1 || center > target_bounds.x2)
00156         continue;
00157 
00158       // make sure the key is actually above our target.
00159       int current_y_offset = target_bounds.y1 - bounds.y2;
00160       if (current_y_offset < 0)
00161         continue;
00162 
00163       int current_x_offset = std::abs(center - (target_bounds.x1 + target_bounds.x2) / 2);
00164 
00165       if (CompareOffsets(current_x_offset, current_y_offset, best_x_offset, best_y_offset))
00166       {
00167         best = key;
00168         best_x_offset = current_x_offset;
00169         best_y_offset = current_y_offset;
00170        }
00171     }
00172   }
00173   }
00174 
00175   if (best)
00176   {
00177     keycode = ConvertKeyToKeycode(best);
00178     return true;
00179   }
00180   return false;
00181 }
00182 
00183 guint KeyboardUtil::GetKeycodeAboveKeySymbol(KeySym key_symbol) const
00184 {
00185   guint result = 0;
00186 
00187   int code = XKeysymToKeycode(display_, key_symbol);
00188 
00189   if (!code)
00190     return result;
00191 
00192   if (keyboard_->min_key_code > code || keyboard_->max_key_code < code)
00193     return result;
00194 
00195   char* key_str = keyboard_->names->keys[code].name;
00196 
00197 
00198   int key_section;
00199   XkbBoundsRec key_bounds;
00200   bool found_key = FindKeyInGeometry(keyboard_->geom, key_str, key_section, key_bounds);
00201 
00202   if (found_key)
00203   {
00204     guint maybe;
00205     found_key = FindKeyInSectionAboveBounds(keyboard_->geom, key_section, key_bounds, maybe);
00206 
00207     if (found_key)
00208       result = maybe;
00209   }
00210 
00211   return result;
00212 }
00213 
00214 bool KeyboardUtil::IsPrintableKeySymbol(KeySym sym)
00215 {
00216   bool printable_key = false;
00217 
00218   if (sym == XK_Delete || sym == XK_BackSpace || sym == XK_Return)
00219   {
00220     printable_key = true;
00221   }
00222   else
00223   {
00224     unsigned int unicode = gdk_keyval_to_unicode(sym);
00225     printable_key = g_unichar_isprint(unicode);
00226   }
00227 
00228   return printable_key;
00229 }
00230 
00231 bool KeyboardUtil::IsMoveKeySymbol(KeySym sym)
00232 {
00233   bool move_key = false;
00234 
00235   if (sym >= XK_Home && sym <= XK_Begin)
00236   {
00237     move_key = true;
00238   }
00239 
00240   return move_key;
00241 }
00242 
00243 }
00244 }