diff --git a/web/history.md b/web/history.md index 2eca4007e1..78caae1d4d 100644 --- a/web/history.md +++ b/web/history.md @@ -8,6 +8,9 @@ * New API functions have been added: `detachFromControl`, `enableControl`, `disableControl`, `setKeyboardForControl`. * Removed old code directed at legacy browsers no longer supported. (#212) * Refactored code paths relating to KMW's embedding in the iOS and Android apps. (#211) +* Added support for L/R Alt and Ctrl modifiers for keyboards. (#9) (#52) +* Added support for use of the Caps Lock state within keyboards if specified by a keyboard designer. + ## 2017-07-10 2.0.473 stable * 2.0 stable release build. diff --git a/web/samples/chirality/chirality.js b/web/samples/chirality/chirality.js index 1304658df7..8d52a6674d 100644 --- a/web/samples/chirality/chirality.js +++ b/web/samples/chirality/chirality.js @@ -11,7 +11,7 @@ function Keyboard_chirality() { 'shift': new Array("~", "!", "@", "#", "$", "%", "^", "&", "*", "(", ")", "_", "+", "", "", "", "Q", "W", "E", "R", "T", "Y", "U", "I", "O", "P", "{", "}", "|", "", "", "", "A", "S", "D", "F", "G", "H", "J", "K", "L", ":", "\"", "", "", "", "", "", "", "Z", "X", "C", "V", "B", "N", "M", "<", ">", "?", "", "", "", "", "", ""), 'leftctrl': new Array("", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "ʍ", "ə", "ɹ", "ð", "ʏ", "ʉ", "ɨ", "ɵ", "ʘ", "", "", "", "", "", "", "ɑ", "ʃ", "", "ɸ", "ɣ", "ɥ", "ɟ", "", "ɬ", "ː", "", "", "", "", "", "", "", "ʒ", "χ", "ç", "ʋ", "β", "ɲ", "", "", "", "", "", "", "", "", "", ""), 'leftctrl-shift': new Array("", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "ʡ", "", "ɘ", "ʀ", "θ", "", "ɯ", "ɪ", "", "", "", "", "", "", "", "", "ɒ", "ᶘ", "", "", "ɢ", "ʜ", "", "", "ʟ", "ː", "", "", "", "", "", "", "", "ᶚ", "", "", "", "ʙ", "ɴ", "", "", "", "ʔ", "", "", "", "", "", ""), - 'leftalt': new Array("", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "ɛ", "ɽ", "ʈ", "ɥ", "ʊ", "", "ɔ", "", "", "", "", "", "", "", "æ", "", "ɖ", "", "ɡ", "ɦ", "ʝ", "", "ɭ", "", "", "", "", "", "", "", "", "ʐ", "", "ɕ", "", "", "ɳ", "", "", "", "", "", "", "", "", "", ""), + 'leftalt': new Array("", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "ɛ", "ɽ", "ʈ", "ɥ", "ʊ", "", "ɔ", "", "", "", "", "", "", "", "æ", "", "ɖ", "", "", "ɦ", "ʝ", "", "ɭ", "", "", "", "", "", "", "", "", "ʐ", "", "ɕ", "", "", "ɳ", "", "", "", "", "", "", "", "", "", ""), 'rightalt': new Array("", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "ʠ", "ɰ", "ɜ", "ɾ", "ƭ", "", "ʌ", "", "ø", "ƥ", "", "", "", "", "", "", "ɐ", "σ", "ɗ", "", "ɠ", "ħ", "ʄ", "ƙ", "ɮ", "", "", "", "", "", "", "", "", "ʑ", "", "ƈ", "", "ɓ", "ŋ", "ɱ", "", "", "", "", "", "", "", "", ""), 'leftalt-shift': new Array("", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "ʢ", "", "œ", "ɻ", "", "", "", "ᵻ", "ɞ", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "ʎ", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""), 'rightalt-shift': new Array("", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "ɶ", "ʁ", "", "", "ᵾ", "ᵼ", "ɤ", "", "", "", "", "", "", "", "ᴂ", "", "", "", "ʛ", "ɧ", "", "", "ɺ", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "") diff --git a/web/source/keymanweb.js b/web/source/keymanweb.js index 9bf0426387..0e81a3da9e 100644 --- a/web/source/keymanweb.js +++ b/web/source/keymanweb.js @@ -2821,39 +2821,7 @@ if(!window['tavultesoft']['keymanweb']['initialized']) { prevModState & 0x000C); // Condition 2 } - // Set our modifier state tracking variable for the next round. - keymanweb.modStateFlags = curModState; - console.log(e, ", location: ", e.location); - - // For European keyboards, not all browsers properly send both key-up events for the AltGr combo. - var altGrMask = osk.modifierCodes['RALT'] | osk.modifierCodes['LCTRL']; - if((prevModState & altGrMask) == altGrMask && (curModState & altGrMask) != altGrMask) { - // We just released AltGr - make sure it's all released. - curModState &= ~ altGrMask; - } - // Perform basic filtering for Windows-based ALT_GR emulation on European keyboards. - if(curModState & osk.modifierCodes['RALT']) { - curModState &= ~osk.modifierCodes['LCTRL']; - } - - // Stage 2 - map the modifier set to the appropriate keystroke's modifiers. - if(keymanweb.isChiral()) { - s.Lmodifiers = curModState; - - // Note for future - embedding a kill switch here or in keymanweb.osk.emulatesAltGr would facilitate disabling - // AltGr / Right-alt simulation. - if(osk.emulatesAltGr() && (s.Lmodifiers & osk.modifierBitmasks['ALT_GR_SIM']) == osk.modifierBitmasks['ALT_GR_SIM']) { - s.Lmodifiers ^= osk.modifierBitmasks['ALT_GR_SIM']; - s.Lmodifiers |= osk.modifierCodes['RALT']; - } - } else { - s.Lmodifiers = - (e.shiftKey ? 0x10 : 0) | - ((curModState & (osk.modifierCodes["LCTRL"] | osk.modifierCodes["RCTRL"])) ? 0x20 : 0) | - ((curModState & (osk.modifierCodes["LALT"] | osk.modifierCodes["RALT"])) ? 0x40 : 0); - } - - // Stage 3 - detect state key information. It can be looked up per keypress with no issue. + // Stage 2 - detect state key information. It can be looked up per keypress with no issue. s.Lstates = 0; if(e.getModifierState("CapsLock")) { @@ -2874,6 +2842,41 @@ if(!window['tavultesoft']['keymanweb']['initialized']) { s.Lstates |= osk.modifierCodes["NO_SCROLL_LOCK"]; } + // We need these states to be tracked as well for proper OSK updates. + curModState |= s.Lstates; + + // Stage 3 - Set our modifier state tracking variable and perform basic AltGr-related management. + s.LmodifierChange = keymanweb.modStateFlags != curModState; + keymanweb.modStateFlags = curModState; + + // For European keyboards, not all browsers properly send both key-up events for the AltGr combo. + var altGrMask = osk.modifierCodes['RALT'] | osk.modifierCodes['LCTRL']; + if((prevModState & altGrMask) == altGrMask && (curModState & altGrMask) != altGrMask) { + // We just released AltGr - make sure it's all released. + curModState &= ~ altGrMask; + } + // Perform basic filtering for Windows-based ALT_GR emulation on European keyboards. + if(curModState & osk.modifierCodes['RALT']) { + curModState &= ~osk.modifierCodes['LCTRL']; + } + + // Stage 4 - map the modifier set to the appropriate keystroke's modifiers. + if(keymanweb.isChiral()) { + s.Lmodifiers = curModState & osk.modifierBitmasks['CHIRAL']; + + // Note for future - embedding a kill switch here or in keymanweb.osk.emulatesAltGr would facilitate disabling + // AltGr / Right-alt simulation. + if(osk.emulatesAltGr() && (s.Lmodifiers & osk.modifierBitmasks['ALT_GR_SIM']) == osk.modifierBitmasks['ALT_GR_SIM']) { + s.Lmodifiers ^= osk.modifierBitmasks['ALT_GR_SIM']; + s.Lmodifiers |= osk.modifierCodes['RALT']; + } + } else { + s.Lmodifiers = + (e.shiftKey ? 0x10 : 0) | + ((curModState & (osk.modifierCodes["LCTRL"] | osk.modifierCodes["RCTRL"])) ? 0x20 : 0) | + ((curModState & (osk.modifierCodes["LALT"] | osk.modifierCodes["RALT"])) ? 0x40 : 0); + } + // The 0x6F used to be 0x60 - this adjustment now includes the chiral alt and ctrl modifiers in that check. s.LisVirtualKeyCode = (typeof e.charCode != 'undefined' && e.charCode != null && (e.charCode == 0 || (s.Lmodifiers & 0x6F) != 0)); s.LisVirtualKey = s.LisVirtualKeyCode || e.type != 'keypress'; @@ -2927,16 +2930,14 @@ if(!window['tavultesoft']['keymanweb']['initialized']) { if(Levent.initKeyEvent) { Levent.initKeyEvent('keypress',true,true,null,false,false,false,false,0,32); - Levent._blockKMW = true; // Tell keymanweb._KeyPress to ignore this simulated event. + Levent._kmw_block = true; // Tell keymanweb._KeyPress to ignore this simulated event. // Nasty problems arise from the simulated keystroke if we don't temporarily block the handler. - keymanweb.blockKeypress = true; Pelem.dispatchEvent(Levent); Levent=document.createEvent('KeyboardEvent'); Levent.initKeyEvent('keypress',true,true,null,false,false,false,false,8,0); - Levent._blockKMW = true; // Tell keymanweb._KeyPress to ignore this simulated event. + Levent._kmw_block = true; // Tell keymanweb._KeyPress to ignore this simulated event. Pelem.dispatchEvent(Levent); - keymanweb.blockKeypress = false; } } } @@ -2996,6 +2997,11 @@ if(!window['tavultesoft']['keymanweb']['initialized']) { keymanweb._NotifyKeyboard(Levent.Lcode,Levent.Ltarg,1); return osk._UpdateVKShift(Levent, Levent.Lcode-15, 1); // I2187 } + + if(Levent.LmodifierChange) { + keymanweb._NotifyKeyboard(0,Levent.Ltarg,1); + osk._UpdateVKShift(Levent, 0, 1); + } // I1207 if((Ldv=Levent.Ltarg.ownerDocument) && (Ldv=Ldv.selection) && (Levent.Lcode<33 || Levent.Lcode>40)) @@ -3096,7 +3102,7 @@ if(!window['tavultesoft']['keymanweb']['initialized']) { * Description Processes keypress event (does not pass data to keyboard) */ keymanweb._KeyPress = function(e) { - if(e._blockKMW) { // A custom event property added for bugfix-oriented simulated keypress events. + if(e._kmw_block) { // A custom event property added for bugfix-oriented simulated keypress events. return false; } @@ -3164,6 +3170,11 @@ if(!window['tavultesoft']['keymanweb']['initialized']) { return osk._UpdateVKShift(Levent, Levent.Lcode-15, 1); // I2187 } + if(Levent.LmodifierChange){ + keymanweb._NotifyKeyboard(0,Levent.Ltarg,0); + osk._UpdateVKShift(Levent, 0, 1); // I2187 + } + // I736 start var Ldv; if((Ldv=Levent.Ltarg.ownerDocument) && (Ldv=Ldv.selection) && Ldv.type != 'control') // I1479 - avoid createRange on controls @@ -4244,10 +4255,6 @@ if(!window['tavultesoft']['keymanweb']['initialized']) { observationConfig = { subtree: true, attributes: true, attributeOldValue: true, attributeFilter: ['class', 'readonly']}; new MutationObserver(keymanweb._EnablementMutationObserverCore).observe(observationTarget, observationConfig); - // Handlers for intercepting changes in modifiers and state keys when no input element is focused. - util.attachDOMEvent(window, 'blur', keymanweb._documentBlur, true); - util.attachDOMEvent(window, 'focus', keymanweb._documentRefocus, true); - // Set exposed initialization flag to 2 to indicate deferred initialization also complete keymanweb['initialized']=2; } @@ -4803,30 +4810,6 @@ if(!window['tavultesoft']['keymanweb']['initialized']) { return 'Keyboard_us:eng'; } - keymanweb._documentBlur = function(e) { - util.attachDOMEvent(window, 'mousemove', keymanweb._refocusMouseBlur, false); - } - - keymanweb._refocusMouseBlur = function(e) { - //console.log("Creepy."); - if(keymanweb.refocusTimer) { - window.clearTimeout(keymanweb.refocusTimer); - } - - keymanweb.refocusTimer = window.setTimeout(function() { - window.clearTimeout(keymanweb.refocusTimer); - - e.keyCode = osk.keyCodes['K_CAPS']; // A safe keycode to use; has no output. - var Levent = keymanweb._GetKeyEventProperties(e); - - osk._UpdateVKShift(Levent, Levent.Lcode-15, 1); - }, 50); // Chosen to throttle the messaging rate. - } - - keymanweb._documentRefocus = function(e) { - util.detachDOMEvent(window, 'mousemove', keymanweb._refocusMouseBlur, false); - } - util.attachDOMEvent(window, 'focus', keymanweb._ResetVKShift,false); // I775 util.attachDOMEvent(window, 'blur', keymanweb._ResetVKShift,false); // I775