264 lines
18 KiB
Markdown
264 lines
18 KiB
Markdown
# KSwitcher
|
|
|
|
Native C++20 / Win32 rewrite of the Windows functionality of [Typlo](https://github.com/bitcodepro/Typlo). Windows 10/11 x64.
|
|
No .NET, Avalonia, WebView, Qt, external graphics engine, or separately installed C++ runtime.
|
|
The original [Typlo](https://github.com/bitcodepro/Typlo) project and its data remain separate.
|
|
|
|
**[User guide / Wiki](https://gitea.hrynco.com/hrynco/hrynco-kswitcher/wiki)** — setup, features, shortcuts, dictionaries, screenshots and troubleshooting (Ukrainian).
|
|
|
|
## Run
|
|
|
|
Run `build/KSwitcher.exe`, or extract `dist/KSwitcher-win-x64.zip` and run the executable.
|
|
The build also refreshes the release copy in `build/release/`. Exit KSwitcher before rebuilding.
|
|
The application lives in the notification area. Right-click its icon for Settings, Snippets and history.
|
|
Double-click the tray icon to open Settings, the bold default menu item.
|
|
In Settings, enable **Start with Windows** and **Start with Windows as administrator**
|
|
to use a per-user Task Scheduler logon task with highest privileges. Enabling or changing
|
|
the elevated task requests UAC approval if the app is not already elevated. No password is stored;
|
|
the task runs in the signed-in user's interactive session, including on battery power.
|
|
Disabling the administrator option restores ordinary per-user startup; disabling startup stops
|
|
future automatic launches. Saving unrelated settings does not recreate startup entries.
|
|
`KSwitcher.exe --settings` opens Settings, including when the app is already running.
|
|
Exit [Typlo](https://github.com/bitcodepro/Typlo) when using overlapping shortcuts to avoid competing keyboard hooks.
|
|
|
|
### Change a shortcut
|
|
|
|
1. Open **Settings** and click a shortcut field.
|
|
2. Press the desired combination. The field records the actual keys; no typing of names is needed.
|
|
3. Click **Save**. Duplicate shortcuts within KSwitcher and protected Windows combinations are reported.
|
|
|
|
This also works for each layout's **Direct layout shortcut**. Backspace or Delete without modifiers
|
|
clears a shortcut. Tab moves to the next field. The recorded shortcut is suppressed while the field
|
|
has focus, so recording the conversion shortcut does not perform a conversion.
|
|
All configured shortcuts use the existing keyboard hook and can replace ordinary Windows/app
|
|
shortcuts, including **Win+V** and **Win+Shift+S**, while KSwitcher is running. Only exact configured
|
|
combinations are intercepted. Unassigned shortcuts and simulated copy/paste pass through.
|
|
Choose another shortcut or exit KSwitcher to restore the original action. Windows protects
|
|
Ctrl+Alt+Delete and Win+L; these cannot be assigned.
|
|
|
|
Keyboard and mouse input forwarded by Microsoft Mouse Without Borders follows the same input
|
|
path as a local keyboard/mouse, including shortcuts, recent-word conversion and snippets.
|
|
KSwitcher marks its own generated input so copy/paste, selection, modifier releases and shell-mask
|
|
keys cannot trigger recursive commands or overwrite the recorded typing run. Other injected input
|
|
is accepted, including key-up events and VK-only keyboard events. Unicode packets do not provide
|
|
physical key positions and therefore reset recent-word tracking; selection conversion still works.
|
|
|
|
### Updates
|
|
|
|
Select **Update** in the tray menu to check the stable Releases of
|
|
[this repository](https://gitea.hrynco.com/hrynco/hrynco-kswitcher/releases).
|
|
A startup check also reports an available version, but never installs it automatically.
|
|
Confirm the offered version to download and install it. The updater uses the existing
|
|
`KSwitcher-win-x64.zip` and `SHA256SUMS.txt` assets, checks the checksum, package contents, x64
|
|
executable identity and version, then closes and restarts KSwitcher. Downloads and staging happen
|
|
before the application exits. If replacement or startup fails, it restores the previous files;
|
|
if recovery itself fails, it reports the retained backup directory.
|
|
|
|
Saved settings and clipboard history in `data/` or the local-app-data fallback are preserved.
|
|
Existing dictionary files are preserved too; missing dictionary files are installed. Save settings
|
|
edits before updating. The installation directory must be writable at KSwitcher's current privilege
|
|
level. A blocked network request, incomplete release or checksum mismatch leaves the running version
|
|
intact and is reported when Update is selected. Startup network failures remain non-interactive.
|
|
The helper uses Windows' built-in Windows PowerShell 5.1; no additional runtime is installed.
|
|
|
|
Settings and the other utility windows use the destination monitor's DPI for their initial frame,
|
|
controls and fonts. They can be resized, stay within a remaining monitor's work area after display
|
|
changes, and provide scrollbars when their content does not fit. Tab navigation scrolls the focused
|
|
field into view; Shift+wheel scrolls horizontally.
|
|
|
|
## Implemented
|
|
|
|
- Installed Windows keyboard layouts, configurable cycle/order and direct shortcuts; IMEs are switch-only.
|
|
- Selection conversion and verified recent input (physical scan codes, 2-second typing-run boundary).
|
|
- Same-process clipboard ownership plus verified WebView2 descendants, including the Teams fix.
|
|
- Teams recent-word conversion uses a single word-selection command and validates the copied range.
|
|
If the editor's word boundary differs from the recorded typing, it falls back to paced character
|
|
selection: Teams can coalesce a burst of Shift+Left events into just one character.
|
|
- Clipboard snapshot/restoration with concurrent-copy, foreground and input-version checks.
|
|
- Case cycling, Russian/Ukrainian transliteration, verified undo and repeated conversion.
|
|
- Text snippets across layouts, multiline insertion and a verified `{cursor}` position;
|
|
`{{cursor}}` inserts the literal marker. Snippets are stored as plaintext settings, as in [Typlo](https://github.com/bitcodepro/Typlo).
|
|
- Clipboard history for text, HTML/RTF, file lists, DIB and PNG images; plain-text paste;
|
|
full-text search, pinning, deletion, full text viewing and image previews.
|
|
- Image thumbnails and text/file icons appear directly in the history list. Only visible rows
|
|
load thumbnails; the 32-entry thumbnail cache is bounded to about 3.3 MiB and released on close.
|
|
- Text rows render RTF and basic clipboard HTML emphasis (bold, italic, underline, strike-through),
|
|
paragraphs, line breaks and list bullets using native Windows RichEdit. Previews are bounded to
|
|
4096 characters / 64 KiB RTF, with at most 32 cached text previews; original paste formats are unchanged.
|
|
HTML is parsed as text, without a browser, scripts or external resources. Complex web layouts and
|
|
stylesheet rules are not reproduced; preview text uses the list's colors for readability.
|
|
- Consecutive screenshot copies are compared by image content, ignoring changing OLE transport
|
|
metadata (including Snipping Tool's repeated clipboard publication).
|
|
- Windows OCR, entirely native and offline. Right-click an image in history and select
|
|
**Copy text from image**. Requires a Windows OCR language installed for the user profile.
|
|
- History exclusion flags and excluded processes, pause/resume, count/size limits.
|
|
- Per-user Windows DPAPI encryption for both history payloads and the preview index.
|
|
- Autostart, keep-screen-awake, idle-only F15 activity signal, settings import/export.
|
|
- Single instance and `--history`, `--convert`, `--case`, `--transliterate`, `--settings`,
|
|
`--snippets`, `--exit` commands. `--convert` cycles layouts.
|
|
|
|
Default shortcuts match [Typlo](https://github.com/bitcodepro/Typlo): Ctrl+Alt+Shift+Space (cycle), C (case), T (transliteration),
|
|
Z (undo), 1/2/3 (first layouts), and Ctrl+Shift+V (history).
|
|
|
|
History controls: Enter to paste, Shift+Enter for plain text, Ctrl+1…9 for quick paste,
|
|
Ctrl+P to pin/unpin, Delete to delete, Ctrl+O for the full item, Escape to hide.
|
|
Pressing the history shortcut a second time pastes the second-most-recent item.
|
|
|
|
## Data and migration
|
|
|
|
Data is in `data/` beside the executable, falling back to `%LOCALAPPDATA%/KSwitcher` when
|
|
the executable's directory is not writable. Each clipboard payload is an encrypted disk file;
|
|
only bounded previews/metadata stay in memory. Image data is loaded on demand and released when
|
|
the history popup is hidden. Settings **Write log** (`writeLog`, enabled by default) controls all
|
|
log writes. Disabling it also clears diagnostic context from memory; existing log files remain.
|
|
Successful operations stay in a memory buffer of at most 40 diagnostic events for 30 seconds.
|
|
Only failures or suspected repeated commands flush this context to `kswitcher.log`; flushed
|
|
context is discarded. Ordinary application errors are also logged when logging is enabled.
|
|
Incident context contains tracked physical input, the expected word, copied text, conversion result,
|
|
and layout IDs, correlated by request ID even when verification fails. Text fields use quoted,
|
|
escaped UTF-16 (`\uXXXX` for non-ASCII and control characters) for exact reproduction, with
|
|
original length and a truncation flag; each field is limited to 512 UTF-16 code units.
|
|
Manual conversion attempts in `kswitcher.log` have request IDs and explicit outcomes, including
|
|
busy, empty copy, verification mismatch, unchanged conversion, and cancelled paste. Requests
|
|
record the process, window, input version, and repeated attempts within ten seconds without new
|
|
input. A second repeated manual command triggers a suspicion, not proof of failure; intentional
|
|
layout cycling can also trigger it. An unchanged result containing no letters, or a direct command
|
|
to the detected current layout, is treated as expected. `paste-sent-unverified` means Windows
|
|
accepted the paste shortcut; it does not prove that the editor applied the replacement.
|
|
The previous 256 KiB log is retained as `kswitcher.log.1` when the current log rotates.
|
|
Open **View log…** from Settings or the tray menu to read logs inside KSwitcher.
|
|
Choose the current or previous log and press **Refresh** to reload. The ListView shows Time,
|
|
Attempt, Event, and Details columns. Select rows and use **Copy selected** or Ctrl+C to copy
|
|
tab-separated entries; Ctrl+A selects all rows. Viewing saved logs also works when logging is disabled.
|
|
**Clear log** clears only the selected current or previous file and refreshes the table.
|
|
Clearing the current log also discards buffered diagnostic context; new problems can still be logged.
|
|
|
|
Settings **Import** accepts a [Typlo](https://github.com/bitcodepro/Typlo) `settings.json`, preserving supported Windows layout IDs,
|
|
hotkeys, snippets, exclusions and limits. Import is staged until Save. Existing [Typlo](https://github.com/bitcodepro/Typlo) files are
|
|
never modified. [Typlo](https://github.com/bitcodepro/Typlo)'s encrypted SQLite history is **not migrated** to the new DPAPI file format.
|
|
DPAPI data belongs to the Windows account/machine; copying the portable folder is not a cross-account backup.
|
|
|
|
## Differences still to port
|
|
|
|
This is a working native implementation, not a claim of complete UI/release parity:
|
|
|
|
- UI currently uses English and the native Windows control appearance. [Typlo](https://github.com/bitcodepro/Typlo)'s ten-language UI and
|
|
explicit light/dark theme selector are not yet ported.
|
|
- No import of old encrypted clipboard history. Export/import applies to settings.
|
|
- The clipboard popup uses per-monitor DPI scaling and scrolls when space is limited; PNG/DIB previews use Windows WIC/GDI.
|
|
|
|
The Windows focus/injection rules still apply: an unelevated process cannot edit an elevated app.
|
|
Conversion is cancelled rather than pasting into a different window or replacing unverified recent text.
|
|
|
|
## Build
|
|
|
|
The easiest reproducible local route uses the pinned portable w64devkit toolchain:
|
|
|
|
```powershell
|
|
.\setup-toolchain.ps1
|
|
.\build.ps1 -Test
|
|
.\package.ps1
|
|
```
|
|
|
|
`setup-toolchain.ps1` downloads the compiler from its upstream GitHub release and verifies SHA-256.
|
|
`.tools/`, build objects, test executables and compiler headers are development-only. **Do not distribute
|
|
the entire source/build folder.** The package contains just the application and notices/documentation.
|
|
The vendored JSON header is nlohmann/json 3.12.0 (MIT); it is compiled into the executable.
|
|
|
|
Alternatively open this directory as a CMake project in Visual Studio with the C++ desktop workload
|
|
and Windows SDK installed, or use CMake with a Windows GCC toolchain. CMake/MSVC is provided but the
|
|
verified local build uses GCC/w64devkit.
|
|
|
|
## Automatic layout correction (experimental)
|
|
|
|
Enable **Automatically correct layout (experimental)** in Settings, then use **Auto-correction
|
|
options…** to select dictionary languages and edit excluded applications or words to never correct.
|
|
The feature is off by default. English and Ukrainian are selected initially; Russian is optional.
|
|
Target layouts must also be enabled in the layout cycle.
|
|
|
|
**Characters before a word** and **Characters after a word** configure punctuation to preserve around
|
|
the corrected word. Enter characters directly, without spaces or separators; a comma or semicolon
|
|
is itself a character. Defaults include parentheses, square/curly brackets, quotation marks and common
|
|
trailing punctuation, including colon and semicolon. Empty fields disable stripping on that side.
|
|
Up to three leading and six trailing characters (including the final space) are supported. Recognition
|
|
still requires a verified complete word; punctuation inside a word is not stripped. Correction runs
|
|
after Space, not immediately after a punctuation key. Save Settings to apply changes.
|
|
|
|
**Frequency rank limit** defaults to **0 (entire dictionary)**. A positive value allows corrections
|
|
only to words with a rank at or below that number (for example, 10000). All dictionary words still
|
|
protect already valid input, and ambiguous alternatives remain unchanged regardless of this limit.
|
|
**Open dictionary folder** opens the files beside the running executable. After editing, click OK
|
|
in the options window and Save in Settings to validate and reload the dictionaries without restarting.
|
|
Use UTF-8, one lowercase word followed by a Tab and a positive rank per line; keep words sorted and
|
|
unique. For a personal addition, rank 1 makes it eligible at every positive limit. Back up your edited
|
|
files before replacing dictionaries with a new distribution.
|
|
|
|
After Space and a 250 ms typing pause, KSwitcher checks the last physically typed word against local
|
|
dictionaries. For example, `ghbdsn ` can become `привіт `. Continuing to type cancels pending work.
|
|
It only corrects unknown words when exactly one alternative is a dictionary word.
|
|
**Minimum word length** defaults to 3 and can be set from 1 to 32 letters. Surrounding punctuation
|
|
does not count toward this minimum; the maximum word length is 32 letters.
|
|
Known words, ambiguous alternatives, ALL CAPS, mixed-case identifiers, numbers, URLs and email
|
|
addresses are left unchanged. Short trailing punctuation is retained when the editor selects it
|
|
together with the word. Actual selected text is verified before replacement. Layout switching follows
|
|
the existing switch-layout setting. The configured Undo shortcut restores the original and suppresses
|
|
that word for the rest of the session (until restart).
|
|
|
|
Password fields and inaccessible controls are skipped using Windows UI Automation. Editors, terminals
|
|
and password managers are excluded by default; process names can be edited in the options window.
|
|
Controls that do not expose an editable/document element or cannot select a complete word may not
|
|
support automatic correction. Dictionary recognition is heuristic, not an understanding of the sentence;
|
|
subtitle vocabulary is incomplete, and uncommon names or jargon can need personal exceptions.
|
|
|
|
No network service or language model is used. The three dictionary files occupy about 2.6 MB on disk;
|
|
the English/Ukrainian lookup data uses less than 3 MiB of RAM and is loaded on first automatic check.
|
|
Keep `dictionaries/` beside the executable when copying the app. These word lists derive from
|
|
[FrequencyWords](https://github.com/hermitdave/FrequencyWords) and are separately licensed under
|
|
CC BY-SA 4.0; attribution, pinned source revision and license are in `dictionaries/`.
|
|
|
|
## Latest compiled release
|
|
|
|
The [Release folder](Release/README.md) contains the latest portable Windows x64 ZIP and SHA-256 checksum.
|
|
Run `./package.ps1 -PublishRelease` after building and testing to refresh the tracked release archive.
|
|
|
|
## Verification
|
|
|
|
```powershell
|
|
.\build.ps1 -Test # Pure/native regression checks; does not touch the user's clipboard
|
|
.\build.ps1 -Ocr # Render a synthetic image, decode it with WIC and recognize it with Windows OCR
|
|
.\build.ps1 -Integration # Interactive, isolated test editor; avoid input for about 10 seconds
|
|
```
|
|
|
|
`-Test` also checks remote/own input separation, hidden Settings layout and scrolling, and updater
|
|
package validation, file preservation and rollback in temporary fixture directories. It does not
|
|
replace a running installation. Optional read-only network checks:
|
|
|
|
```powershell
|
|
.\build\KSwitcherInputTests.exe --update-check
|
|
powershell.exe -NoProfile -ExecutionPolicy Bypass -File .\tests\update.ps1 -Network
|
|
```
|
|
|
|
These validate the live release metadata/download/checksum in a temporary directory. They do not
|
|
prove hardware interoperability between two laptops or a physical monitor-disconnection sequence.
|
|
|
|
The interactive test requires installed English-US and Russian layouts. It opens its own editor,
|
|
tests real copy/paste, undo, repeat conversion, recent-input verification and snippet cursor placement,
|
|
then restores the clipboard. Test data is stored in its own temporary directory. It never sends messages
|
|
to Teams or other user applications. Do not run it while typing elsewhere.
|
|
|
|
`KSwitcher.exe --smoke-test` starts a normal tray instance and exits after ten seconds for idle-memory checks.
|
|
It still uses the normal instance lock; close a running KSwitcher first if measuring a fresh process.
|
|
|
|
## Structure
|
|
|
|
- `core.*`: conversion, transliteration, snippets, shortcut parsing/formatting, recent-input model.
|
|
- `platform.*`: Win32 layouts, clipboard, input, ownership, DPAPI and file primitives.
|
|
- `storage.*`: settings and encrypted, disk-backed history.
|
|
- `app.*`: message loop, hooks, background work and guarded text transactions.
|
|
- `ui.cpp`: native windows and shortcut recording.
|
|
- `ocr.*`: Windows WIC and OCR via the native Windows Runtime ABI.
|
|
- `tests/`: regression checks, real editor integration and OCR fixture.
|
|
|
|
Application code is MIT licensed; preserves the original [Typlo](https://github.com/bitcodepro/Typlo) attribution in `LICENSE`.
|
|
Dictionary data has its own CC BY-SA 4.0 license as described above.
|