Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| eb30701e07 | |||
| 855719863e |
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
cmake_minimum_required(VERSION 3.24)
|
cmake_minimum_required(VERSION 3.24)
|
||||||
project(KSwitcher VERSION 0.2.1 LANGUAGES CXX RC)
|
project(KSwitcher VERSION 0.2.3 LANGUAGES CXX RC)
|
||||||
if(NOT WIN32)
|
if(NOT WIN32)
|
||||||
message(FATAL_ERROR "KSwitcher uses native Windows APIs")
|
message(FATAL_ERROR "KSwitcher uses native Windows APIs")
|
||||||
endif()
|
endif()
|
||||||
|
|||||||
@@ -112,7 +112,7 @@ only bounded previews/metadata stay in memory. Image data is loaded on demand an
|
|||||||
the history popup is hidden. Settings **Write log** (`writeLog`, enabled by default) controls all
|
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.
|
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.
|
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
|
Only failures flush this context to `kswitcher.log`; flushed
|
||||||
context is discarded. Ordinary application errors are also logged when logging is enabled.
|
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,
|
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,
|
and layout IDs, correlated by request ID even when verification fails. Text fields use quoted,
|
||||||
@@ -121,8 +121,8 @@ original length and a truncation flag; each field is limited to 512 UTF-16 code
|
|||||||
Manual conversion attempts in `kswitcher.log` have request IDs and explicit outcomes, including
|
Manual conversion attempts in `kswitcher.log` have request IDs and explicit outcomes, including
|
||||||
busy, empty copy, verification mismatch, unchanged conversion, and cancelled paste. Requests
|
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
|
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
|
input. Successful repeated commands do not flush the log; intentional layout cycling is normal.
|
||||||
layout cycling can also trigger it. An unchanged result containing no letters, or a direct command
|
An unchanged result containing no letters, or a direct command
|
||||||
to the detected current layout, is treated as expected. `paste-sent-unverified` means Windows
|
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.
|
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.
|
The previous 256 KiB log is retained as `kswitcher.log.1` when the current log rotates.
|
||||||
|
|||||||
Binary file not shown.
+4
-14
@@ -1,21 +1,11 @@
|
|||||||
# Latest Windows release
|
# Latest Windows release
|
||||||
|
|
||||||
## v0.2.1 — 2026-10-28
|
## v0.2.3 — 2026-10-28
|
||||||
|
|
||||||
- Accept keyboard and mouse input forwarded by Microsoft Mouse Without Borders, including hotkey
|
- Fix overlapping Settings controls that obscured the Next layout and Change case labels.
|
||||||
releases, repeated presses, recent-word conversion and snippets. Mark KSwitcher's own generated
|
- Stop treating successful repeated conversions as diagnostic incidents. Failed operations still flush their diagnostic context to the log.
|
||||||
input to prevent recursive commands and preserve text-operation validation.
|
|
||||||
- Add **Update** to the tray menu and a non-installing startup check against this repository's
|
|
||||||
Releases. Verify the x64 ZIP, SHA-256, executable identity and version before closing; restart
|
|
||||||
automatically and roll back on replacement/startup failure. Keep settings, history and existing dictionaries.
|
|
||||||
- Fix initial window sizing on high-DPI monitors. Size the frame and controls from the same monitor
|
|
||||||
DPI, recover windows after display changes, and provide resizing, scrolling and focus visibility
|
|
||||||
when the available work area is smaller than the contents.
|
|
||||||
|
|
||||||
Verified: 168 native checks, 51 input/layout checks (including hidden Settings at 240 DPI),
|
Verified: 168 native checks, 51 input/layout checks, and 26 updater/metadata checks.
|
||||||
24 offline updater checks, live release download/checksum/package validation, and visible Settings
|
|
||||||
at 250% scaling. Two-laptop Mouse Without Borders use and physically disconnecting a monitor still
|
|
||||||
require hardware verification.
|
|
||||||
|
|
||||||
Download **KSwitcher-win-x64.zip**, extract the entire archive to a writable folder,
|
Download **KSwitcher-win-x64.zip**, extract the entire archive to a writable folder,
|
||||||
and launch `KSwitcher.exe`. Keep `dictionaries/` next to the executable.
|
and launch `KSwitcher.exe`. Keep `dictionaries/` next to the executable.
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
e33c372d1401c5500f3b07b38c10aff9667a5b1db9f6e613420e7878202853b3 KSwitcher-win-x64.zip
|
ef3f21574f02edeb63661109056480506041372f1fa4ed40b07cdd2f544d79b5 KSwitcher-win-x64.zip
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ try {
|
|||||||
& $compiler @common -c "src/$name.cpp" -o "build/$name.o"
|
& $compiler @common -c "src/$name.cpp" -o "build/$name.o"
|
||||||
if ($LASTEXITCODE) { throw "Compilation failed: $name" }
|
if ($LASTEXITCODE) { throw "Compilation failed: $name" }
|
||||||
}
|
}
|
||||||
& windres resources/app.rc -O coff -o build/app-res.o
|
& windres --codepage=65001 resources/app.rc -O coff -o build/app-res.o
|
||||||
if ($LASTEXITCODE) { throw 'Resource compilation failed' }
|
if ($LASTEXITCODE) { throw 'Resource compilation failed' }
|
||||||
New-Item -ItemType Directory -Force build/release | Out-Null
|
New-Item -ItemType Directory -Force build/release | Out-Null
|
||||||
& $compiler -mwindows -municode -static -s '-Wl,--gc-sections' build/core.o build/platform.o build/autostart.o build/autocorrect.o build/storage.o build/ocr.o build/richtext.o build/update.o build/app.o build/ui.o build/main.o build/app-res.o @libs -o $executablePath
|
& $compiler -mwindows -municode -static -s '-Wl,--gc-sections' build/core.o build/platform.o build/autostart.o build/autocorrect.o build/storage.o build/ocr.o build/richtext.o build/update.o build/app.o build/ui.o build/main.o build/app-res.o @libs -o $executablePath
|
||||||
|
|||||||
+4
-3
@@ -1,11 +1,12 @@
|
|||||||
|
#pragma code_page(65001)
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
#include "resource.h"
|
#include "resource.h"
|
||||||
IDI_KSWITCHER ICON "kswitcher.ico"
|
IDI_KSWITCHER ICON "kswitcher.ico"
|
||||||
1 RT_MANIFEST "app.manifest"
|
1 RT_MANIFEST "app.manifest"
|
||||||
201 RCDATA "update.ps1"
|
201 RCDATA "update.ps1"
|
||||||
1 VERSIONINFO
|
1 VERSIONINFO
|
||||||
FILEVERSION 0,2,1,0
|
FILEVERSION 0,2,3,0
|
||||||
PRODUCTVERSION 0,2,1,0
|
PRODUCTVERSION 0,2,3,0
|
||||||
FILEOS VOS_NT_WINDOWS32
|
FILEOS VOS_NT_WINDOWS32
|
||||||
FILETYPE VFT_APP
|
FILETYPE VFT_APP
|
||||||
BEGIN
|
BEGIN
|
||||||
@@ -15,7 +16,7 @@ BEGIN
|
|||||||
BEGIN
|
BEGIN
|
||||||
VALUE "FileDescription", "KSwitcher — native keyboard and clipboard utility"
|
VALUE "FileDescription", "KSwitcher — native keyboard and clipboard utility"
|
||||||
VALUE "ProductName", "KSwitcher"
|
VALUE "ProductName", "KSwitcher"
|
||||||
VALUE "FileVersion", "0.2.1"
|
VALUE "FileVersion", "0.2.3"
|
||||||
END
|
END
|
||||||
END
|
END
|
||||||
BLOCK "VarFileInfo"
|
BLOCK "VarFileInfo"
|
||||||
|
|||||||
+5
-10
@@ -619,9 +619,6 @@ void App::transform(int command, int snippet)
|
|||||||
L" window=" + std::to_wstring(reinterpret_cast<uintptr_t>(target)) +
|
L" window=" + std::to_wstring(reinterpret_cast<uintptr_t>(target)) +
|
||||||
L" version=" + std::to_wstring(currentVersion) +
|
L" version=" + std::to_wstring(currentVersion) +
|
||||||
L" repeat=" + std::to_wstring(repeatAttempts));
|
L" repeat=" + std::to_wstring(repeatAttempts));
|
||||||
if (repeatAttempts >= 2)
|
|
||||||
diagnosticEvent(L"Transform suspicion id=" + std::to_wstring(attempt) +
|
|
||||||
L" reason=repeated-command-without-input (may be intentional cycling)");
|
|
||||||
}
|
}
|
||||||
if (busy.exchange(true))
|
if (busy.exchange(true))
|
||||||
{
|
{
|
||||||
@@ -643,8 +640,7 @@ void App::transform(int command, int snippet)
|
|||||||
version = inputVersion;
|
version = inputVersion;
|
||||||
}
|
}
|
||||||
enqueue(
|
enqueue(
|
||||||
[this, target, recent, previous, config, available, version, command, snippet, attempt, manual, requestedAt,
|
[this, target, recent, previous, config, available, version, command, snippet, attempt, manual, requestedAt]
|
||||||
suspicious = manual && repeatAttempts >= 2]
|
|
||||||
{
|
{
|
||||||
struct BusyReset
|
struct BusyReset
|
||||||
{
|
{
|
||||||
@@ -679,7 +675,6 @@ void App::transform(int command, int snippet)
|
|||||||
uint64_t version, attempt;
|
uint64_t version, attempt;
|
||||||
ULONGLONG start;
|
ULONGLONG start;
|
||||||
bool enabled;
|
bool enabled;
|
||||||
bool suspicious;
|
|
||||||
std::wstring &reason;
|
std::wstring &reason;
|
||||||
std::wstring &text, &result, &expected;
|
std::wstring &text, &result, &expected;
|
||||||
int &hint, &to;
|
int &hint, &to;
|
||||||
@@ -693,10 +688,10 @@ void App::transform(int command, int snippet)
|
|||||||
L" source=" + std::to_wstring(hint) + L" target=" + std::to_wstring(to) +
|
L" source=" + std::to_wstring(hint) + L" target=" + std::to_wstring(to) +
|
||||||
L" expected=" + diagnosticText(expected) + L" copied=" + diagnosticText(text) +
|
L" expected=" + diagnosticText(expected) + L" copied=" + diagnosticText(text) +
|
||||||
L" result=" + diagnosticText(result),
|
L" result=" + diagnosticText(result),
|
||||||
suspicious || (reason != L"paste-sent-unverified" && reason != L"layout-only" &&
|
reason != L"paste-sent-unverified" && reason != L"layout-only" &&
|
||||||
reason != L"unchanged-expected"));
|
reason != L"unchanged-expected");
|
||||||
}
|
}
|
||||||
} audit{*this, target, version, attempt, requestedAt, manual, suspicious, reason, text, result, expected, hint, to};
|
} audit{*this, target, version, attempt, requestedAt, manual, reason, text, result, expected, hint, to};
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
auto valid = [&]
|
auto valid = [&]
|
||||||
@@ -1134,7 +1129,7 @@ void App::trayMenu()
|
|||||||
break;
|
break;
|
||||||
case 16:
|
case 16:
|
||||||
MessageBoxW(window,
|
MessageBoxW(window,
|
||||||
L"KSwitcher 0.2.1\nNative C++ / Win32 keyboard and clipboard utility\n\nBased on "
|
L"KSwitcher 0.2.2\nNative C++ / Win32 keyboard and clipboard utility\n\nBased on "
|
||||||
L"Typlo by bitcodepro. MIT license.\nHistory is encrypted for your Windows "
|
L"Typlo by bitcodepro. MIT license.\nHistory is encrypted for your Windows "
|
||||||
L"account.\nNo .NET, WebView or graphics framework.",
|
L"account.\nNo .NET, WebView or graphics framework.",
|
||||||
L"About KSwitcher", MB_OK);
|
L"About KSwitcher", MB_OK);
|
||||||
|
|||||||
+2
-2
@@ -1623,8 +1623,8 @@ void showSettings(App &app, bool visible)
|
|||||||
HWND w = makeWindow(app, L"KSwitcher.Settings", L"KSwitcher settings", settingsProc, 780, 660);
|
HWND w = makeWindow(app, L"KSwitcher.Settings", L"KSwitcher settings", settingsProc, 780, 660);
|
||||||
app.settingsWindow = w;
|
app.settingsWindow = w;
|
||||||
label(w, L"Text conversion", 18, 16);
|
label(w, L"Text conversion", 18, 16);
|
||||||
checkbox(w, L"Convert recently typed text when nothing is selected", 201, 18, 43, 440);
|
checkbox(w, L"Convert recently typed text when nothing is selected", 201, 18, 43, 390);
|
||||||
checkbox(w, L"Switch Windows input layout after conversion", 202, 18, 70, 440);
|
checkbox(w, L"Switch Windows input layout after conversion", 202, 18, 70, 390);
|
||||||
checkbox(w, L"Record clipboard history", 203, 18, 106);
|
checkbox(w, L"Record clipboard history", 203, 18, 106);
|
||||||
checkbox(w, L"Start with Windows", 204, 18, 133);
|
checkbox(w, L"Start with Windows", 204, 18, 133);
|
||||||
checkbox(w, L"Start with Windows as administrator", 206, 18, 160, 395);
|
checkbox(w, L"Start with Windows as administrator", 206, 18, 160, 395);
|
||||||
|
|||||||
+1
-1
@@ -2,7 +2,7 @@
|
|||||||
#include "platform.h"
|
#include "platform.h"
|
||||||
namespace ks
|
namespace ks
|
||||||
{
|
{
|
||||||
inline constexpr wchar_t AppVersion[] = L"0.2.1";
|
inline constexpr wchar_t AppVersion[] = L"0.2.3";
|
||||||
inline constexpr UINT UpdateMessage = WM_APP + 7;
|
inline constexpr UINT UpdateMessage = WM_APP + 7;
|
||||||
struct UpdateResult
|
struct UpdateResult
|
||||||
{
|
{
|
||||||
|
|||||||
+5
-1
@@ -7,6 +7,10 @@ function Reject([scriptblock]$Action,[string]$Name) { $rejected=$false; try { &
|
|||||||
$root=Join-Path ([IO.Path]::GetTempPath()) ('KSwitcher-update-tests-'+[guid]::NewGuid().ToString('N'))
|
$root=Join-Path ([IO.Path]::GetTempPath()) ('KSwitcher-update-tests-'+[guid]::NewGuid().ToString('N'))
|
||||||
[IO.Directory]::CreateDirectory($root) | Out-Null
|
[IO.Directory]::CreateDirectory($root) | Out-Null
|
||||||
try {
|
try {
|
||||||
|
$binaryInfo = [Diagnostics.FileVersionInfo]::GetVersionInfo((Resolve-Path -LiteralPath $Executable).Path)
|
||||||
|
$binaryVersion = Get-StableVersion $binaryInfo.FileVersion
|
||||||
|
Assert ($null -ne $binaryVersion) 'executable has a stable version'
|
||||||
|
Assert ($binaryInfo.FileDescription -ceq ('KSwitcher ' + [char]0x2014 + ' native keyboard and clipboard utility')) 'executable description preserves Unicode em dash'
|
||||||
Assert ((Get-StableVersion 'v0.10.0') -gt (Get-StableVersion 'v0.2.9')) 'numeric versions'
|
Assert ((Get-StableVersion 'v0.10.0') -gt (Get-StableVersion 'v0.2.9')) 'numeric versions'
|
||||||
Assert ($null -eq (Get-StableVersion 'v1.0.0-beta')) 'prerelease rejected'
|
Assert ($null -eq (Get-StableVersion 'v1.0.0-beta')) 'prerelease rejected'
|
||||||
Assert ($null -eq (Get-StableVersion 'v1.0.0;whoami')) 'invalid version rejected'
|
Assert ($null -eq (Get-StableVersion 'v1.0.0;whoami')) 'invalid version rejected'
|
||||||
@@ -36,7 +40,7 @@ try {
|
|||||||
((Get-FileHash -LiteralPath $archive -Algorithm SHA256).Hash+' KSwitcher-win-x64.zip') | Set-Content -LiteralPath $sum
|
((Get-FileHash -LiteralPath $archive -Algorithm SHA256).Hash+' KSwitcher-win-x64.zip') | Set-Content -LiteralPath $sum
|
||||||
Assert-Checksum $archive $sum; ++$script:checks
|
Assert-Checksum $archive $sum; ++$script:checks
|
||||||
$stage=Join-Path $root 'stage'
|
$stage=Join-Path $root 'stage'
|
||||||
Expand-Release $archive $stage ([version]'0.2.1'); ++$script:checks
|
Expand-Release $archive $stage $binaryVersion; ++$script:checks
|
||||||
Reject { Expand-Release $archive (Join-Path $root 'wrong-version') ([version]'9.0.0') } 'version mismatch rejected'
|
Reject { Expand-Release $archive (Join-Path $root 'wrong-version') ([version]'9.0.0') } 'version mismatch rejected'
|
||||||
('0'*64+' KSwitcher-win-x64.zip') | Set-Content -LiteralPath $sum
|
('0'*64+' KSwitcher-win-x64.zip') | Set-Content -LiteralPath $sum
|
||||||
Reject { Assert-Checksum $archive $sum } 'corrupt package rejected'
|
Reject { Assert-Checksum $archive $sum } 'corrupt package rejected'
|
||||||
|
|||||||
Reference in New Issue
Block a user