Back to index

lightning-sunbird  0.9+nobinonly
nsKeyboardUtils.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
00002  *
00003  * ***** BEGIN LICENSE BLOCK *****
00004  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00005  *
00006  * The contents of this file are subject to the Mozilla Public License Version
00007  * 1.1 (the "License"); you may not use this file except in compliance with
00008  * the License. You may obtain a copy of the License at
00009  * http://www.mozilla.org/MPL/
00010  *
00011  * Software distributed under the License is distributed on an "AS IS" basis,
00012  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00013  * for the specific language governing rights and limitations under the
00014  * License.
00015  *
00016  * The Original Code is mozilla.org Code.
00017  *
00018  * The Initial Developer of the Original Code is
00019  * Brian Stell <bstell@netscape.com>.
00020  * Portions created by the Initial Developer are Copyright (C) 2001
00021  * the Initial Developer. All Rights Reserved.
00022  *
00023  * Contributor(s):
00024  *
00025  * Alternatively, the contents of this file may be used under the terms of
00026  * either the GNU General Public License Version 2 or later (the "GPL"), or
00027  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00028  * in which case the provisions of the GPL or the LGPL are applicable instead
00029  * of those above. If you wish to allow use of your version of this file only
00030  * under the terms of either the GPL or the LGPL, and not to allow others to
00031  * use your version of this file under the terms of the MPL, indicate your
00032  * decision by deleting the provisions above and replace them with the notice
00033  * and other provisions required by the GPL or the LGPL. If you do not delete
00034  * the provisions above, a recipient may use your version of this file under
00035  * the terms of any one of the MPL, the GPL or the LGPL.
00036  *
00037  * ***** END LICENSE BLOCK ***** */
00038 
00039 #include <X11/Xlib.h>
00040 #ifdef HAVE_X11_XKBLIB_H
00041 #   if (defined (__digital__) && defined (__unix__)) || defined(_AIX) || defined(__hpux) || defined (__osf__)
00042 #   define explicit Explicit
00043 #   include <X11/XKBlib.h>
00044 #   undef explicit
00045 #else
00046 #   include <X11/XKBlib.h>
00047 #endif
00048 #endif
00049 #include <X11/keysym.h>
00050 #include <gdk/gdkx.h>
00051 #include <string.h>
00052 #include "nsKeyboardUtils.h"
00053 #include "nspr.h"
00054 #include "nsWindow.h"
00055 #include "nsPrintfCString.h"
00056 
00057 //
00058 // xkbms: X KeyBoard Mode Switch
00059 //
00060 // Mode_switch during XGrabKeyboard workaround
00061 //
00062 // During a keyboard grab some (all?) X servers do not
00063 // set the Mode_switch bit in the XKeyEvent state value
00064 // even if the user is pressing the Mode_switch key.
00065 // Thus some important keysym mappings are not available
00066 // during a keyboard grab; eg: German keyboards use 
00067 // Mode_switch (AltGr) and Q for '@' which is an 
00068 // important key for entering email addresses.
00069 // Finnish keyboards use dead_tilde for '~' which is an
00070 // important key for entering user's home page URLs
00071 //
00072 // This workaround turns off the keyboard grab when
00073 // the Mode_switch down.
00074 //
00075 // Note:
00076 // Mode_switch MappingNotify should be called when the
00077 // keyboard mapping changes (a MappingNotify event) but
00078 // the XMappingNotify event has a null window and gdk only allows an
00079 // application to filter events with a non-null window
00080 //
00081 
00082 // Xlib should define this!
00083 #define MODIFIERMAP_ROW_SIZE 8
00084 
00085 PRUint32 nsXKBModeSwitch::gModeSwitchKeycode1 = 0;
00086 PRUint32 nsXKBModeSwitch::gModeSwitchKeycode2 = 0;
00087 PRBool   nsXKBModeSwitch::gGrabDuringPopup = PR_TRUE;
00088 PRBool   nsXKBModeSwitch::gUnGrabDuringModeSwitch = PR_TRUE;
00089 PRBool   nsXKBModeSwitch::gModeSwitchDown = PR_FALSE;
00090 gint     nsXKBModeSwitch::gOwnerEvents;
00091 guint32  nsXKBModeSwitch::gGrabTime;
00092 
00093 
00094 void 
00095 nsXKBModeSwitch::ControlWorkaround(gboolean grab_during_popup,
00096                                    gboolean ungrab_during_mode_switch)
00097 {
00098 #ifdef DEBUG_bzbarsky
00099   NS_WARNING("nsXKBModeSwitch::ControlWorkaround:");
00100   NS_WARNING(nsPrintfCString("    grab_during_popup = %d", grab_during_popup).get());
00101   NS_WARNING(nsPrintfCString("    ungrab_during_mode_switch = %d", ungrab_during_mode_switch).get());
00102 #endif
00103   gGrabDuringPopup = grab_during_popup;
00104   gUnGrabDuringModeSwitch = ungrab_during_mode_switch;
00105 
00106   //
00107   // This really should be called whenever a MappingNotify
00108   // event happens but gdk does not support that.
00109   //
00110   HandleMappingNotify();
00111 }
00112 
00113 gint
00114 nsXKBModeSwitch::GrabKeyboard(GdkWindow *win, gint owner_events, guint32 time)
00115 {
00116   // if grab is disabled pretend it succeeded
00117   if (!gGrabDuringPopup) {
00118     return GrabSuccess;
00119   }
00120 
00121   gint retval;
00122   retval = gdk_keyboard_grab(win, owner_events, time);
00123   if (retval == GrabSuccess) {
00124     gOwnerEvents = owner_events;
00125     gGrabTime = time;
00126   }
00127   else {
00128     gOwnerEvents = 0;
00129     gGrabTime = 0;
00130   }
00131  
00132   return retval;
00133 }
00134 
00135 void
00136 nsXKBModeSwitch::HandleMappingNotify()
00137 {
00138   XModifierKeymap *xmodmap;
00139   int i, j, max_keypermod;
00140 
00141   // since the mapping could change we (re)initialize variables
00142   Init();
00143 
00144   Display *lGdkDisplay = GDK_DISPLAY();
00145   if (!lGdkDisplay)
00146     return;
00147   xmodmap = XGetModifierMapping(lGdkDisplay);
00148   if (!xmodmap)
00149     return;
00150 
00151   max_keypermod = MIN(xmodmap->max_keypermod,5);
00152   for (i=0; i<max_keypermod; i++) {
00153     for (j=0; j<MODIFIERMAP_ROW_SIZE; j++) {
00154       KeyCode keycode;
00155       KeySym keysym;
00156       char *keysymName;
00157       keycode = *((xmodmap->modifiermap)+(i*MODIFIERMAP_ROW_SIZE)+j);
00158       if (!keycode)
00159         continue;
00160       keysym = XKeycodeToKeysym(GDK_DISPLAY(), keycode, 0);
00161       if (!keysym)
00162         continue;
00163       keysymName = XKeysymToString(keysym);
00164       if (!keysymName)
00165         continue;
00166       if (!strcmp(keysymName,"Mode_switch")) {
00167         if (!gModeSwitchKeycode1)
00168           gModeSwitchKeycode1 = keycode;
00169         else if (!gModeSwitchKeycode2)
00170           gModeSwitchKeycode2 = keycode;
00171       }
00172     }
00173   }
00174   XFreeModifiermap(xmodmap);
00175 
00176 #ifdef DEBUG_bzbarsky
00177   if (!gModeSwitchKeycode1) {
00178     NS_WARNING("\n\nnsXKBModeSwitch::HandleMappingNotify: no Mode_switch\n\n");
00179   }
00180   NS_WARNING("\n\nnsXKBModeSwitch::HandleMappingNotify:");
00181   NS_WARNING(nsPrintfCString("    gModeSwitchKeycode1 = %d", gModeSwitchKeycode1).get());
00182   NS_WARNING(nsPrintfCString("    gModeSwitchKeycode2 = %d", gModeSwitchKeycode2).get());
00183 #endif
00184 
00185 #if defined(HAVE_X11_XKBLIB_H) && \
00186   defined(XkbMajorVersion) && defined(XbMinorVersion)
00187   {
00188     gint xkb_major = XkbMajorVersion;
00189     gint xkb_minor = XkbMinorVersion;
00190     if (XkbLibraryVersion (&xkb_major, &xkb_minor)) {
00191       xkb_major = XkbMajorVersion;
00192       xkb_minor = XkbMinorVersion;
00193       if (XkbQueryExtension (gdk_display, NULL, NULL, NULL,
00194                                &xkb_major, &xkb_minor)) {
00195       }
00196     }
00197   }
00198 #endif
00199 
00200 }
00201 
00202 void
00203 nsXKBModeSwitch::Init()
00204 {
00205     gModeSwitchKeycode1 = 0;
00206     gModeSwitchKeycode2 = 0;
00207     gModeSwitchDown = FALSE;
00208 }
00209 
00210 void
00211 nsXKBModeSwitch::HandleKeyPress(XKeyEvent *xke)
00212 {
00213   // if grab is completely disabled then there is nothing to do
00214   if (!gGrabDuringPopup) {
00215     return;
00216   }
00217 
00218   // check for a Mode_switch keypress
00219   if ((xke->keycode == gModeSwitchKeycode1) 
00220       || (xke->keycode == gModeSwitchKeycode2)) {
00221     gModeSwitchDown = TRUE;
00222     NS_WARNING("nsXKBModeSwitch::HandleKeyPress: Mode_switch is down");
00223     nsWindow *win = nsWindow::GetGrabWindow();
00224     if (!win)
00225       return;
00226     if (win->GrabInProgress()) {
00227       if (gUnGrabDuringModeSwitch) {
00228         gdk_keyboard_ungrab(GDK_CURRENT_TIME);
00229         NS_WARNING("\n\n*** ungrab keyboard ***\n\n");
00230       }
00231     }
00232   }
00233 }
00234 
00235 void
00236 nsXKBModeSwitch::HandleKeyRelease(XKeyEvent *xke)
00237 {
00238   // if grab is completely disabled then there is nothing to do
00239   if (!gGrabDuringPopup) {
00240     return;
00241   }
00242 
00243   // check for a Mode_switch keyrelease
00244   if ((xke->keycode == gModeSwitchKeycode1) 
00245           || (xke->keycode == gModeSwitchKeycode2)) {
00246     gModeSwitchDown = FALSE;
00247     NS_WARNING("nsXKBModeSwitch::HandleKeyPress: Mode_switch is up");
00248     nsWindow *win = nsWindow::GetGrabWindow();
00249     if (!win)
00250       return;
00251     if (win->GrabInProgress()) {
00252       if (gUnGrabDuringModeSwitch) {
00253         if (!win->GetGdkGrabWindow())
00254           return;
00255         gdk_keyboard_grab(win->GetGdkGrabWindow(), gOwnerEvents, gGrabTime);
00256         NS_WARNING("\n\n*** re-grab keyboard ***\n\n");
00257       }
00258     }
00259   }
00260 }
00261 
00262 void
00263 nsXKBModeSwitch::UnGrabKeyboard(guint32 time)
00264 {
00265   // if grab is completely disabled then there is nothing to do
00266   if (!gGrabDuringPopup) {
00267     return;
00268   }
00269 
00270   gdk_keyboard_ungrab(time);
00271   gOwnerEvents = 0;
00272   gGrabTime = 0;
00273 }
00274