fix(autofill): AltGr et caracteres sans touche vers VNC
AltGr envoye en Ctrl gauche + Alt droit etendu, avec scancodes : "@" et "#" en AZERTY arrivaient sans AltGr. Un caractere sans touche dans la disposition locale est saute et compte dans le journal (copier a la place). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1448,9 +1448,15 @@ d'accord.
|
|||||||
recevait un `V` par caractere. Cible dont l'exe contient « vnc »
|
recevait un `V` par caractere. Cible dont l'exe contient « vnc »
|
||||||
(`TargetTypesByKeyCodes`) → **vraies touches** (`SendKeyCodeString` :
|
(`TargetTypesByKeyCodes`) → **vraies touches** (`SendKeyCodeString` :
|
||||||
`VkKeyScanEx` dans la disposition de la cible, + Maj/Ctrl/Alt, AltGr =
|
`VkKeyScanEx` dans la disposition de la cible, + Maj/Ctrl/Alt, AltGr =
|
||||||
Ctrl+Alt) ; un caractere sans touche repart en Unicode. Journal : `typed as
|
Ctrl+Alt). **AltGr = `VK_LCONTROL` + `VK_RMENU` etendu**, comme le vrai
|
||||||
key codes (VNC)` sur la ligne `Autofill waits`. Eprouve sur un mot de passe
|
clavier : un Ctrl+Alt generique etait lu par TigerVNC comme deux
|
||||||
simple ; caracteres speciaux dependants de la disposition du Mac distant.
|
modificateurs, et `@` (AltGr+0 en AZERTY) arrivait en `a`. Chaque touche porte
|
||||||
|
aussi son scancode (`MapVirtualKeyEx`). **Un caractere sans touche dans la
|
||||||
|
disposition locale** (arabe sur clavier francais) ne peut pas passer : il
|
||||||
|
est **saute et compte** - journal `N character(s) have no key on this
|
||||||
|
keyboard and were NOT typed - copy instead` ; la copie, que TigerVNC
|
||||||
|
synchronise, reste le recours. Eprouve vers un Mac : lettres, espaces, `@`,
|
||||||
|
`#` corrects, 19 caracteres arabes sautes et comptes.
|
||||||
|
|
||||||
- **UIPI** : SendInput vers process élevé bloqué. Notepad++ admin,
|
- **UIPI** : SendInput vers process élevé bloqué. Notepad++ admin,
|
||||||
regedit → autofill silencieusement no-op. Tester avec apps non-élevées.
|
regedit → autofill silencieusement no-op. Tester avec apps non-élevées.
|
||||||
|
|||||||
@@ -2111,53 +2111,71 @@ begin
|
|||||||
end;
|
end;
|
||||||
|
|
||||||
/// Le texte en VRAIES touches, dans la disposition clavier de la cible : chaque
|
/// Le texte en VRAIES touches, dans la disposition clavier de la cible : chaque
|
||||||
/// caractere devient sa touche plus Maj / Ctrl / Alt (AltGr = Ctrl+Alt) selon
|
/// caractere devient sa touche plus Maj / Ctrl / Alt selon VkKeyScanEx, avec
|
||||||
/// VkKeyScanEx. Un caractere sans touche dans cette disposition part en
|
/// son scancode (certains clients VNC lisent la position physique).
|
||||||
/// Unicode, faute de mieux.
|
/// ALTGR = Ctrl GAUCHE + Alt DROIT (touche etendue), comme le clavier le
|
||||||
procedure SendKeyCodeString(const AText: string; ALayout: HKL);
|
/// produit : un Ctrl+Alt generique etait lu par TigerVNC comme deux
|
||||||
|
/// modificateurs ordinaires, et "@" (AltGr+0 en AZERTY) arrivait en "a".
|
||||||
|
/// Un caractere sans touche dans cette disposition (de l'arabe sur un clavier
|
||||||
|
/// francais) ne peut pas passer par VNC : il est saute et compte - la copie,
|
||||||
|
/// que TigerVNC synchronise, reste le recours. Rend le nombre de sautes.
|
||||||
|
function SendKeyCodeString(const AText: string; ALayout: HKL): Integer;
|
||||||
var
|
var
|
||||||
LInputs: TArray<TInput>;
|
LInputs: TArray<TInput>;
|
||||||
LCount: Integer;
|
LCount: Integer;
|
||||||
procedure Key(AVk: Word; AUp: Boolean);
|
procedure Key(AVk: Word; AUp: Boolean; AExtended: Boolean = False);
|
||||||
begin
|
begin
|
||||||
if LCount >= Length(LInputs) then SetLength(LInputs, LCount + 16);
|
if LCount >= Length(LInputs) then SetLength(LInputs, LCount + 16);
|
||||||
FillChar(LInputs[LCount], SizeOf(TInput), 0);
|
FillChar(LInputs[LCount], SizeOf(TInput), 0);
|
||||||
LInputs[LCount].Itype := INPUT_KEYBOARD;
|
LInputs[LCount].Itype := INPUT_KEYBOARD;
|
||||||
LInputs[LCount].ki.wVk := AVk;
|
LInputs[LCount].ki.wVk := AVk;
|
||||||
|
LInputs[LCount].ki.wScan := MapVirtualKeyEx(AVk, 0 {MAPVK_VK_TO_VSC}, ALayout);
|
||||||
if AUp then LInputs[LCount].ki.dwFlags := KEYEVENTF_KEYUP;
|
if AUp then LInputs[LCount].ki.dwFlags := KEYEVENTF_KEYUP;
|
||||||
|
if AExtended then
|
||||||
|
LInputs[LCount].ki.dwFlags := LInputs[LCount].ki.dwFlags or KEYEVENTF_EXTENDEDKEY;
|
||||||
Inc(LCount);
|
Inc(LCount);
|
||||||
end;
|
end;
|
||||||
var
|
var
|
||||||
I: Integer;
|
I: Integer;
|
||||||
LScan: SHORT;
|
LScan: SHORT;
|
||||||
LVk, LMods: Byte;
|
LVk, LMods: Byte;
|
||||||
|
LShift, LCtrl, LAlt: Boolean;
|
||||||
begin
|
begin
|
||||||
|
Result := 0;
|
||||||
for I := 1 to Length(AText) do
|
for I := 1 to Length(AText) do
|
||||||
begin
|
begin
|
||||||
LScan := VkKeyScanEx(AText[I], ALayout);
|
LScan := VkKeyScanEx(AText[I], ALayout);
|
||||||
if LScan = -1 then
|
if LScan = -1 then
|
||||||
begin
|
begin
|
||||||
SendUnicodeString(AText[I]);
|
Inc(Result);
|
||||||
Continue;
|
Continue;
|
||||||
end;
|
end;
|
||||||
LVk := Lo(Word(LScan));
|
LVk := Lo(Word(LScan));
|
||||||
LMods := Hi(Word(LScan));
|
LMods := Hi(Word(LScan));
|
||||||
|
LShift := LMods and 1 <> 0;
|
||||||
|
LCtrl := LMods and 2 <> 0;
|
||||||
|
LAlt := LMods and 4 <> 0;
|
||||||
LCount := 0;
|
LCount := 0;
|
||||||
if LMods and 1 <> 0 then Key(VK_SHIFT, False);
|
if LShift then Key(VK_LSHIFT, False);
|
||||||
if LMods and 2 <> 0 then Key(VK_CONTROL, False);
|
if LCtrl then Key(VK_LCONTROL, False);
|
||||||
if LMods and 4 <> 0 then Key(VK_MENU, False);
|
// Alt seul = Alt gauche ; avec Ctrl, c'est AltGr = Alt droit, etendu.
|
||||||
|
if LAlt and LCtrl then Key(VK_RMENU, False, True)
|
||||||
|
else if LAlt then Key(VK_LMENU, False);
|
||||||
Key(LVk, False);
|
Key(LVk, False);
|
||||||
Key(LVk, True);
|
Key(LVk, True);
|
||||||
if LMods and 4 <> 0 then Key(VK_MENU, True);
|
if LAlt and LCtrl then Key(VK_RMENU, True, True)
|
||||||
if LMods and 2 <> 0 then Key(VK_CONTROL, True);
|
else if LAlt then Key(VK_LMENU, True);
|
||||||
if LMods and 1 <> 0 then Key(VK_SHIFT, True);
|
if LCtrl then Key(VK_LCONTROL, True);
|
||||||
|
if LShift then Key(VK_LSHIFT, True);
|
||||||
SendInput(LCount, @LInputs[0], SizeOf(TInput));
|
SendInput(LCount, @LInputs[0], SizeOf(TInput));
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure SendTextTo(const AText: string; AByKeys: Boolean; ALayout: HKL);
|
/// Rend le nombre de caracteres qui n'ont pas pu etre tapes (VNC seulement).
|
||||||
|
function SendTextTo(const AText: string; AByKeys: Boolean; ALayout: HKL): Integer;
|
||||||
begin
|
begin
|
||||||
if AByKeys then SendKeyCodeString(AText, ALayout)
|
Result := 0;
|
||||||
|
if AByKeys then Result := SendKeyCodeString(AText, ALayout)
|
||||||
else SendUnicodeString(AText);
|
else SendUnicodeString(AText);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
@@ -2380,6 +2398,7 @@ begin
|
|||||||
FLastFillWaits := Format('foreground=%d ms, modifiers=%d ms',
|
FLastFillWaits := Format('foreground=%d ms, modifiers=%d ms',
|
||||||
[LFgMs, LSw.ElapsedMilliseconds]);
|
[LFgMs, LSw.ElapsedMilliseconds]);
|
||||||
if LByKeys then FLastFillWaits := FLastFillWaits + ', typed as key codes (VNC)';
|
if LByKeys then FLastFillWaits := FLastFillWaits + ', typed as key codes (VNC)';
|
||||||
|
var LMissing := 0;
|
||||||
Sleep(FocusSettleDelayMs);
|
Sleep(FocusSettleDelayMs);
|
||||||
|
|
||||||
// Never type into our own window: if the target refused the foreground,
|
// Never type into our own window: if the target refused the foreground,
|
||||||
@@ -2403,19 +2422,19 @@ begin
|
|||||||
if AUsernameOnly then
|
if AUsernameOnly then
|
||||||
begin
|
begin
|
||||||
ClearFieldIfWanted;
|
ClearFieldIfWanted;
|
||||||
SendTextTo(AUsername, LByKeys, LLayout);
|
Inc(LMissing, SendTextTo(AUsername, LByKeys, LLayout));
|
||||||
Exit;
|
Exit;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
if AUsername = '' then
|
if AUsername = '' then
|
||||||
begin
|
begin
|
||||||
ClearFieldIfWanted;
|
ClearFieldIfWanted;
|
||||||
SendTextTo(APassword, LByKeys, LLayout);
|
Inc(LMissing, SendTextTo(APassword, LByKeys, LLayout));
|
||||||
Exit;
|
Exit;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
ClearFieldIfWanted;
|
ClearFieldIfWanted;
|
||||||
SendTextTo(AUsername, LByKeys, LLayout);
|
Inc(LMissing, SendTextTo(AUsername, LByKeys, LLayout));
|
||||||
Sleep(200);
|
Sleep(200);
|
||||||
// Real-VK send: a physically held Shift here turns Tab into Shift+Tab —
|
// Real-VK send: a physically held Shift here turns Tab into Shift+Tab —
|
||||||
// focus moves BACKWARD and the password lands in the username field.
|
// focus moves BACKWARD and the password lands in the username field.
|
||||||
@@ -2427,8 +2446,12 @@ begin
|
|||||||
// Tab lands somewhere that is not an editable field (a link, a reveal
|
// Tab lands somewhere that is not an editable field (a link, a reveal
|
||||||
// button, the document itself, as on the Reddit login form) it selects the
|
// button, the document itself, as on the Reddit login form) it selects the
|
||||||
// ENTIRE PAGE, and the password is then typed into nothing.
|
// ENTIRE PAGE, and the password is then typed into nothing.
|
||||||
SendTextTo(APassword, LByKeys, LLayout);
|
Inc(LMissing, SendTextTo(APassword, LByKeys, LLayout));
|
||||||
finally
|
finally
|
||||||
|
if LMissing > 0 then
|
||||||
|
FLastFillWaits := FLastFillWaits + Format(
|
||||||
|
', %d character(s) have no key on this keyboard and were NOT typed - copy instead',
|
||||||
|
[LMissing]);
|
||||||
// Re-register with the LIVE combo (user may have rebound it), not the
|
// Re-register with the LIVE combo (user may have rebound it), not the
|
||||||
// hardcoded default — otherwise a fill would reset new-entry to Ctrl+Shift+A.
|
// hardcoded default — otherwise a fill would reset new-entry to Ctrl+Shift+A.
|
||||||
if FNewEntryHotkeyRegistered then
|
if FNewEntryHotkeyRegistered then
|
||||||
|
|||||||
Reference in New Issue
Block a user