Attached is my suggested fix for the keyboard bug. The behavior should be the same as before, except when losing the focus (FocusOut, LeaveNotify) all keys are released (normal keys first, modifiers at last), and on re-entering (KeymapNotify) pressed (modifiers first, other keys afterwards). I'll explain some more when I submit it. Please test on different Unices, especially with multiple windows/displays.
m.
Index: include/osgViewer/api/X11/GraphicsWindowX11 =================================================================== --- include/osgViewer/api/X11/GraphicsWindowX11 (revision 7855) +++ include/osgViewer/api/X11/GraphicsWindowX11 (working copy) @@ -44,9 +44,11 @@ _currentCursor(0), _initialized(false), _realized(false), - _timeOfLastCheckEvents(-1.0) + _timeOfLastCheckEvents(-1.0), + _lastEventType(0) { _traits = traits; + memset(_keyMap, 0, 32); init(); @@ -159,6 +161,7 @@ void transformMouseXY(float& x, float& y); void adaptKey(XKeyEvent& keyevent, int& keySymbol, unsigned int& modifierMask); + int getModifierMask(); bool _valid; Display* _display; @@ -177,7 +180,9 @@ bool _ownsWindow; double _timeOfLastCheckEvents; + int _lastEventType; + char _keyMap[32]; std::map<MouseCursor,Cursor> _mouseCursorMap; }; Index: src/osgViewer/GraphicsWindowX11.cpp =================================================================== --- src/osgViewer/GraphicsWindowX11.cpp (revision 7855) +++ src/osgViewer/GraphicsWindowX11.cpp (working copy) @@ -173,6 +173,23 @@ return s_x11KeyboardMap.remapKey(key); } +// Functions to handle key maps of type char[32] as contained in +// a KeymapNotify event structure or returned by XQueryKeymap(). +static inline bool keyMapGetKey(char* map, unsigned int key) +{ + return (map[(key & 0xff) / 8] & (1 << (key & 7))) != 0; +} + +static inline void keyMapSetKey(char* map, unsigned int key) +{ + map[(key & 0xff) / 8] |= (1 << (key & 7)); +} + +static inline void keyMapClearKey(char* map, unsigned int key) +{ + map[(key & 0xff) / 8] &= ~(1 << (key & 7)); +} + GraphicsWindowX11::~GraphicsWindowX11() { close(true); @@ -693,7 +710,8 @@ XSelectInput( _eventDisplay, _window, ExposureMask | StructureNotifyMask | KeyPressMask | KeyReleaseMask | - PointerMotionMask | ButtonPressMask | ButtonReleaseMask); + PointerMotionMask | ButtonPressMask | ButtonReleaseMask | + KeymapStateMask | FocusChangeMask | LeaveWindowMask ); XFlush( _eventDisplay ); XSync( _eventDisplay, 0 ); @@ -917,6 +935,119 @@ osg::notify(osg::INFO)<<"GravityNotify event recieved"<<std::endl; break; + case FocusIn : + { + osg::notify(osg::INFO)<<"FocusIn event recieved"<<std::endl; + // Needed to get the following KeymapNotify event. + break; + } + + case LeaveNotify : + case FocusOut : + { + osg::notify(osg::INFO)<<"FocusOut/LeaveNotify event recieved"<<std::endl; + + // Build map of all registered modifier keycodes + XModifierKeymap *mkm = XGetModifierMapping(display); + std::map<KeyCode,bool> modifier; + KeyCode *m = mkm->modifiermap; + for (int i = 0; i < mkm->max_keypermod * 8; i++, m++) + { + if (*m) modifier[*m] = true; + } + + // Send fake-releases in two passes for all keys reported as pressed. + // (pass 0: normal keys, pass 1: modifier keys) + for (int pass = 0; pass < 2; pass++) + { + for (unsigned int key = 8; key < 256; key++) + { + if (!keyMapGetKey(_keyMap, key)) continue; + bool isModifier = modifier.find(key) != modifier.end(); + if (pass == 0 && !isModifier || pass == 1 && isModifier) + { + XKeyEvent event; + event.type = KeyRelease; + event.serial = 0; + event.send_event = True; + event.display = display; + event.window = _window; + event.subwindow = 0; + event.time = eventTime; + event.x = 0; + event.y = 0; + event.x_root = 0; + event.y_root = 0; + event.state = getModifierMask(); + event.keycode = key; + event.same_screen = True; + + int keySymbol = 0; + unsigned int modifierMask = 0; + adaptKey(event, keySymbol, modifierMask); + getEventQueue()->keyRelease(keySymbol, eventTime); + keyMapClearKey(_keyMap, key); + } + } + } + break; + } + + case KeymapNotify : + { + osg::notify(osg::INFO)<<"KeymapNotify event received"<<std::endl; + + // KeymapNotify is guaranteed to directly follow either a FocusIn + // or an Enter event. We are only interested in the FocusIn case. + if (_lastEventType != FocusIn) break; + + // Build map of all registered modifier keycodes. + XModifierKeymap *mkm = XGetModifierMapping(display); + std::map<KeyCode,bool> modifier; + KeyCode *m = mkm->modifiermap; + for (int i = 0; i < mkm->max_keypermod * 8; i++, m++) + { + if (*m) modifier[*m] = true; + } + + // Send fake-presses in two passes for all keys reported as pressed. + // (pass 0: modifier keys, pass 1: normal keys) + for (int pass = 0; pass < 2; pass++) + { + for (int key = 8; key < 256; key++) + { + if (!keyMapGetKey(ev.xkeymap.key_vector, key)) continue; + if (keyMapGetKey(_keyMap, key)) continue; + bool isModifier = modifier.find(key) != modifier.end(); + if (pass == 0 && isModifier || pass == 1 && !isModifier) + { + XKeyEvent event; + event.type = KeyPress; + event.serial = 0; + event.send_event = True; + event.display = display; + event.window = _window; + event.subwindow = 0; + event.time = eventTime; + event.x = 0; + event.y = 0; + event.x_root = 0; + event.y_root = 0; + event.state = getModifierMask(); + event.keycode = key; + event.same_screen = True; + + int keySymbol = 0; + unsigned int modifierMask = 0; + adaptKey(event, keySymbol, modifierMask); + getEventQueue()->keyPress(keySymbol, eventTime); + keyMapSetKey(_keyMap, key); + } + } + } + break; + } + case UnmapNotify : osg::notify(osg::INFO)<<"UnmapNotify event recieved"<<std::endl; break; @@ -1087,11 +1218,11 @@ Time relativeTime = ev.xmotion.time - firstEventTime; eventTime = baseTime + static_cast<double>(relativeTime)*0.001; + keyMapSetKey(_keyMap, ev.xkey.keycode); int keySymbol = 0; unsigned int modifierMask = 0; adaptKey(ev.xkey, keySymbol, modifierMask); - //getEventQueue()->getCurrentEventState()->setModKeyMask(modifierMask); getEventQueue()->keyPress(keySymbol, eventTime); break; } @@ -1118,20 +1249,21 @@ } } #endif + keyMapClearKey(_keyMap, ev.xkey.keycode); int keySymbol = 0; unsigned int modifierMask = 0; adaptKey(ev.xkey, keySymbol, modifierMask); - //getEventQueue()->getCurrentEventState()->setModKeyMask(modifierMask); getEventQueue()->keyRelease(keySymbol, eventTime); break; } default: - osg::notify(osg::NOTICE)<<"Other event"<<std::endl; + osg::notify(osg::NOTICE)<<"Other event "<<ev.type<<std::endl; break; } + _lastEventType = ev.type; } if (windowX != _traits->x || @@ -1197,9 +1329,8 @@ { Display* display = _eventDisplay; - static XComposeStatus state; unsigned char keybuf[32]; - XLookupString( &keyevent, (char *)keybuf, sizeof(keybuf), NULL, &state ); + XLookupString( &keyevent, (char *)keybuf, sizeof(keybuf), NULL, NULL ); modifierMask = 0; if( keyevent.state & ShiftMask ) @@ -1227,8 +1358,6 @@ modifierMask |= osgGA::GUIEventAdapter::MODKEY_META; } - keySymbol = keybuf[0]; - KeySym ks = XKeycodeToKeysym( display, keyevent.keycode, 0 ); int remappedKey = remapX11Key(ks); if (remappedKey & 0xff00) @@ -1241,10 +1370,25 @@ // normal ascii key keySymbol = keybuf[0]; } - - } +int GraphicsWindowX11::getModifierMask() +{ + int mask = 0; + char keymap[32]; + XQueryKeymap(_eventDisplay, keymap); + XModifierKeymap *mkm = XGetModifierMapping(_eventDisplay); + for (int i = 0; i < mkm->max_keypermod * 8; i++) + { + unsigned int key = mkm->modifiermap[i]; + if (key && keyMapGetKey(keymap, key)) + { + mask |= 1 << (i / mkm->max_keypermod); + } + } + return mask; +} + void GraphicsWindowX11::requestWarpPointer(float x,float y) { Display* display = _eventDisplay; // getDisplayToUse();
_______________________________________________ osg-users mailing list osg-users@lists.openscenegraph.org http://lists.openscenegraph.org/listinfo.cgi/osg-users-openscenegraph.org