Compare commits
3
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
60625103f8 | ||
|
|
aa02f2cacb | ||
|
|
6a1e39efa9 |
No files matched your search
+1
-1
@@ -1,5 +1,5 @@
|
||||
cmake_minimum_required(VERSION 3.24)
|
||||
project(KSwitcher VERSION 0.6.78 LANGUAGES CXX RC)
|
||||
project(KSwitcher VERSION 0.6.79 LANGUAGES CXX RC)
|
||||
if(NOT WIN32)
|
||||
message(FATAL_ERROR "KSwitcher uses native Windows APIs")
|
||||
endif()
|
||||
|
||||
+11
@@ -44,6 +44,7 @@ Far Manager є зразком для оформлення файлового м
|
||||
- Короткий Alt перемикає головне меню Files при відпусканні. F9 також перемикає меню. Після закриття фокус повертається до активної панелі. Alt з літерою верхнього рівня спрацьовує одразу при натисканні літери. Alt+O відкриває Options, а відпускання Alt після комбінації не перемикає меню.
|
||||
- Коли головне меню відкрите, L, F, C, O та R відкривають Left, Files, Commands, Options та Right. У підменю підсвічена літера викликає відповідну команду. Літери унікальні в межах поточного рівня. Усталені мнемоніки мають пріоритет, зокрема X для Exit.
|
||||
- Глобальне скорочення **Open KSwitcher Files** можна змінити або очистити в налаштуваннях. Для профілю без цього поля пропонується Ctrl+Alt+F, якщо Windows реєструє комбінацію і вона вільна серед прив’язок KSwitcher.
|
||||
- Глобальне скорочення повертає до відкритого вікна Files із тієї ж збірки або запускає Files, якщо такого вікна немає. Пункт **Files** у треї відкриває нове вікно.
|
||||
|
||||
- Ctrl+Shift+0–9 відкриває діалог Title і Path із поточною папкою. OK додає запис без дублювання, Escape скасовує. Title необов’язковий, порожнє значення показує шлях. Записи LAN зберігають ID ПК, а редагування Path залишає запис на тому самому ПК.
|
||||
- Ctrl+0–9 одразу відкриває єдину папку. Для кількох папок відкривається компактний попап Far з одним заголовком Folder shortcuts у подвійній рамці та коротким рядом клавіш у нижній рамці. Ширина залежить від найдовшого запису й обмежується розміром вікна, а висота — кількістю видимих рядків. Нижня рамка показує Ins Del F4 Ctrl+Up Ctrl+Down. Insert додає поточну папку через Title і Path, Delete відв’язує, F4 редагує, Ctrl+Up і Ctrl+Down змінюють і зберігають порядок. Enter відкриває папку. Escape або клік поза попапом закриває його й повертає фокус до панелі. Commands → Folder shortcuts дає вибрати зайняту цифру й відкрити той самий попап навіть для єдиного запису. Brief і Wide доступні в меню Left/Right та Options.
|
||||
@@ -65,6 +66,16 @@ Far Manager є зразком для оформлення файлового м
|
||||
|
||||
## Панелі
|
||||
|
||||
- Якщо команда у вбудованому PowerShell змінює поточну папку,
|
||||
панель, з якої надіслано команду, переходить до цієї папки.
|
||||
|
||||
- Коли список LAN-дисків змінює розмір меню Change drive, рамка й список
|
||||
змінюють позицію разом. Рамка повністю перемальовується після зміни
|
||||
розміру, а список має однаковий внутрішній відступ зліва і справа.
|
||||
- Тайм-аут запиту дисків відображається як Connection timed out. Якщо
|
||||
ввімкнено Write log, Files записує помилки запитів пар і дисків в окремий
|
||||
журнал `data/kswitcher-files.log`. Системні помилки містять код Windows.
|
||||
|
||||
- Застосовані налаштування Files одразу підхоплюють інші відкриті вікна.
|
||||
Apply зберігає поля, змінені в поточній чернетці. Якщо два вікна одночасно
|
||||
редагували набір стилів файлів, відкрий Options заново, щоб побачити свіжі правила.
|
||||
|
||||
Binary file not shown.
@@ -1,5 +1,17 @@
|
||||
# Latest Windows release
|
||||
|
||||
## v0.6.79 — 2026-11-14
|
||||
|
||||
In KSwitcher Files, changing directory in PowerShell updates the panel
|
||||
that sent the command. The global Files shortcut brings an open window to
|
||||
the foreground. Change drive redraws its frame when LAN drives arrive and
|
||||
shows a short timeout message. Files writes LAN lookup errors to its own log.
|
||||
|
||||
**Українською.** Зміна каталогу в PowerShell оновлює панель, з якої
|
||||
надіслано команду. Глобальна клавіша Files повертає до відкритого вікна.
|
||||
Change drive перемальовує рамку після появи LAN-дисків і показує коротке
|
||||
повідомлення про тайм-аут. Files записує помилки LAN у власний журнал.
|
||||
|
||||
## v0.6.78 — 2026-11-14
|
||||
|
||||
The Windows package includes KSwitcher Files, a two-panel file manager with
|
||||
|
||||
@@ -1 +1 @@
|
||||
d7362104941853bb30646fa44175686ed3d2fd3abcf1fd83daee04dd1d5c6912 KSwitcher-win-x64.zip
|
||||
328f04dbf1a2088f89616faaf07f6dd89da34fecda20c915a7e8a21f13cb80db KSwitcher-win-x64.zip
|
||||
+51
-51
@@ -2,130 +2,130 @@
|
||||
"schema": 1,
|
||||
"files": [
|
||||
{
|
||||
"size": 2341376,
|
||||
"path": "KSwitcher.exe",
|
||||
"sha256": "1e67e8e15eada80c902764f14a12a0bd370080709a94d454cc3c565dd3de1136",
|
||||
"size": 2340864
|
||||
"sha256": "0ab0b2a3468d0a5849aba37e14f2e86bf863c9ed85f1c3862367ac9b1e1f8290"
|
||||
},
|
||||
{
|
||||
"size": 2341888,
|
||||
"path": "KSwitcher.Config.exe",
|
||||
"sha256": "26e9f5db4b6f79408e30fb2650f1a8318b5c17e39ae2bccf614901b815ca6606",
|
||||
"size": 2341376
|
||||
"sha256": "7a6d642e7bba3b6b8c25085c569b19db2ef9abded07beb0d522cef939e2ab853"
|
||||
},
|
||||
{
|
||||
"size": 3319808,
|
||||
"path": "KSwitcher.Screenshot.exe",
|
||||
"sha256": "d9593c6ff103070f1f72b93c0f350ba4782d9df814090565f48bd0aa34125059",
|
||||
"size": 3319296
|
||||
"sha256": "8071665518302aeb40c9f668c5bb944f2ac0012c3f57a16d55303619fe20a49f"
|
||||
},
|
||||
{
|
||||
"size": 1825280,
|
||||
"path": "KSwitcher.Files.exe",
|
||||
"sha256": "22f897cfdc3207350e3edb63f33747d71a0f9d60c7a8d3771f642ee589ed6ce1",
|
||||
"size": 1815552
|
||||
"sha256": "f96261fde331f028d27f3fe7b268f78971f96a05a65d9dd515f76c3d5047f2ef"
|
||||
},
|
||||
{
|
||||
"size": 194912,
|
||||
"path": "WebView2Loader.dll",
|
||||
"sha256": "d3fce90bdeecb08596e377dea6a316354dd8949e32ff09b89d51be00c14e21b6",
|
||||
"size": 194912
|
||||
"sha256": "d3fce90bdeecb08596e377dea6a316354dd8949e32ff09b89d51be00c14e21b6"
|
||||
},
|
||||
{
|
||||
"size": 1103,
|
||||
"path": "terminal/addon-fit-LICENSE.txt",
|
||||
"sha256": "e256f01188af527e4d06d21d06fbf785ae9c50d4b328bf03cbe0ba7f0aa4228f",
|
||||
"size": 1103
|
||||
"sha256": "e256f01188af527e4d06d21d06fbf785ae9c50d4b328bf03cbe0ba7f0aa4228f"
|
||||
},
|
||||
{
|
||||
"size": 1521,
|
||||
"path": "terminal/addon-fit.js",
|
||||
"sha256": "ba3ea256ce0620a0992a197d6c9baea64823fc93d8da07a9e366ca9943c18527",
|
||||
"size": 1521
|
||||
"sha256": "ba3ea256ce0620a0992a197d6c9baea64823fc93d8da07a9e366ca9943c18527"
|
||||
},
|
||||
{
|
||||
"size": 527,
|
||||
"path": "terminal/index.html",
|
||||
"sha256": "084d97a727b778fb10d31adcd557ee7840a38377ed60e0ef0f3ece93b349b1ef",
|
||||
"size": 527
|
||||
"sha256": "084d97a727b778fb10d31adcd557ee7840a38377ed60e0ef0f3ece93b349b1ef"
|
||||
},
|
||||
{
|
||||
"size": 3481,
|
||||
"path": "terminal/terminal.js",
|
||||
"sha256": "142624d166314c4539dd14f922c17dca3d2bc7203f84aff2df03a196602c4b09",
|
||||
"size": 3174
|
||||
"sha256": "f677e61d7d90d3023839ee131ddabed7a5c3510b18d47cc9a3c19353913ca5cc"
|
||||
},
|
||||
{
|
||||
"size": 1261,
|
||||
"path": "terminal/xterm-LICENSE.txt",
|
||||
"sha256": "b569f629d00f2626a8100df2a1798210535621e42164dfd426a6fe5aac7b0ccd",
|
||||
"size": 1261
|
||||
"sha256": "b569f629d00f2626a8100df2a1798210535621e42164dfd426a6fe5aac7b0ccd"
|
||||
},
|
||||
{
|
||||
"size": 7112,
|
||||
"path": "terminal/xterm.css",
|
||||
"sha256": "854a7c0fb70e8b1a083c16797ab827299fb18744f5ad34f227b48337e33293c6",
|
||||
"size": 7112
|
||||
"sha256": "854a7c0fb70e8b1a083c16797ab827299fb18744f5ad34f227b48337e33293c6"
|
||||
},
|
||||
{
|
||||
"size": 488663,
|
||||
"path": "terminal/xterm.js",
|
||||
"sha256": "14903579ff54664cd72f8e8699e6961a6272c21863ec1c3b118cdc8af5d4a972",
|
||||
"size": 488663
|
||||
"sha256": "14903579ff54664cd72f8e8699e6961a6272c21863ec1c3b118cdc8af5d4a972"
|
||||
},
|
||||
{
|
||||
"size": 4401,
|
||||
"path": "LICENSE",
|
||||
"sha256": "d3c81140cce01c26bf6cbf52aa40e0825e1fa35b9a91d1387170a65d2597082c",
|
||||
"size": 4401
|
||||
"sha256": "d3c81140cce01c26bf6cbf52aa40e0825e1fa35b9a91d1387170a65d2597082c"
|
||||
},
|
||||
{
|
||||
"size": 1075,
|
||||
"path": "json-LICENSE.txt",
|
||||
"sha256": "c0d068392ea65358b798b8c165103560f06e9e3b38c4ab4e2d8810a7b931af86",
|
||||
"size": 1075
|
||||
"sha256": "c0d068392ea65358b798b8c165103560f06e9e3b38c4ab4e2d8810a7b931af86"
|
||||
},
|
||||
{
|
||||
"size": 93885,
|
||||
"path": "README.md",
|
||||
"sha256": "df6d40d0eb66ad7da8db1cffe6bd211de70dfee474cc228e041c821b3940718b",
|
||||
"size": 93885
|
||||
"sha256": "df6d40d0eb66ad7da8db1cffe6bd211de70dfee474cc228e041c821b3940718b"
|
||||
},
|
||||
{
|
||||
"size": 11418,
|
||||
"path": "LAN-PREVIEW.md",
|
||||
"sha256": "2c13d3ed4761e679c352fb1ade24a1ec3060ad2862376ede801cdee5fcc3bada",
|
||||
"size": 11418
|
||||
"sha256": "2c13d3ed4761e679c352fb1ade24a1ec3060ad2862376ede801cdee5fcc3bada"
|
||||
},
|
||||
{
|
||||
"size": 682867,
|
||||
"path": "dictionaries/en.txt",
|
||||
"sha256": "5f267c141c3d6837870e1c098f86b89a113ba300343119432644ead88f720c21",
|
||||
"size": 682867
|
||||
"sha256": "5f267c141c3d6837870e1c098f86b89a113ba300343119432644ead88f720c21"
|
||||
},
|
||||
{
|
||||
"size": 20138,
|
||||
"path": "dictionaries/LICENSE-CC-BY-SA-4.0.txt",
|
||||
"sha256": "28a9529c7d0bb4dc51f4bf5c116a3d16ef247a052f7591466768ddf563fd1cf5",
|
||||
"size": 20138
|
||||
"sha256": "28a9529c7d0bb4dc51f4bf5c116a3d16ef247a052f7591466768ddf563fd1cf5"
|
||||
},
|
||||
{
|
||||
"size": 2265,
|
||||
"path": "dictionaries/README.md",
|
||||
"sha256": "3017bf44d3be9d9dfab21bd6778aa52b7947e61acf9ce2bc86b41d6e949ac311",
|
||||
"size": 2265
|
||||
"sha256": "3017bf44d3be9d9dfab21bd6778aa52b7947e61acf9ce2bc86b41d6e949ac311"
|
||||
},
|
||||
{
|
||||
"size": 1060364,
|
||||
"path": "dictionaries/ru.txt",
|
||||
"sha256": "9ef922c7699569304a428f6aba0fd13a21341c0d0cb9561f37ef83c41577a28b",
|
||||
"size": 1060364
|
||||
"sha256": "9ef922c7699569304a428f6aba0fd13a21341c0d0cb9561f37ef83c41577a28b"
|
||||
},
|
||||
{
|
||||
"size": 1280,
|
||||
"path": "dictionaries/sources.json",
|
||||
"sha256": "98e5b3fe7304e37705c9e5ff48b0b399ccb16e806736d21f09921b99e5f2a408",
|
||||
"size": 1280
|
||||
"sha256": "98e5b3fe7304e37705c9e5ff48b0b399ccb16e806736d21f09921b99e5f2a408"
|
||||
},
|
||||
{
|
||||
"size": 940799,
|
||||
"path": "dictionaries/uk.txt",
|
||||
"sha256": "d30e990d6ec2983c04f3644a3c61c6c668e8c542323f5931916fdd55b916eebf",
|
||||
"size": 940799
|
||||
"sha256": "d30e990d6ec2983c04f3644a3c61c6c668e8c542323f5931916fdd55b916eebf"
|
||||
},
|
||||
{
|
||||
"size": 993,
|
||||
"path": "tools/README.txt",
|
||||
"sha256": "a34b5161a8315af037e1ec71d816bfc688f7c364141d4ee2347cf0515ef0c82a",
|
||||
"size": 993
|
||||
"sha256": "a34b5161a8315af037e1ec71d816bfc688f7c364141d4ee2347cf0515ef0c82a"
|
||||
},
|
||||
{
|
||||
"size": 134656,
|
||||
"path": "tools/StayHere.exe",
|
||||
"sha256": "6ef5cf64708d62b7fc63c3c793cc99188a513433ff4774cae01dc014f6664376",
|
||||
"size": 134656
|
||||
"sha256": "6ef5cf64708d62b7fc63c3c793cc99188a513433ff4774cae01dc014f6664376"
|
||||
},
|
||||
{
|
||||
"size": 2796,
|
||||
"path": "tools/StayHere-README.md",
|
||||
"sha256": "ba214e2aca7405e698b3ab21ceb1f41ce088edd996b3e30e4a9d58ff496f9413",
|
||||
"size": 2796
|
||||
"sha256": "ba214e2aca7405e698b3ab21ceb1f41ce088edd996b3e30e4a9d58ff496f9413"
|
||||
}
|
||||
],
|
||||
"version": "0.6.78"
|
||||
"version": "0.6.79"
|
||||
}
|
||||
+3
-3
@@ -9,8 +9,8 @@ IDC_SCREENSHOT_PENCIL CURSOR "icons/lucide/pencil.cur"
|
||||
1 RT_MANIFEST "app.manifest"
|
||||
201 RCDATA "update.ps1"
|
||||
1 VERSIONINFO
|
||||
FILEVERSION 0,6,78,0
|
||||
PRODUCTVERSION 0,6,78,0
|
||||
FILEVERSION 0,6,79,0
|
||||
PRODUCTVERSION 0,6,79,0
|
||||
FILEOS VOS_NT_WINDOWS32
|
||||
FILETYPE VFT_APP
|
||||
BEGIN
|
||||
@@ -20,7 +20,7 @@ BEGIN
|
||||
BEGIN
|
||||
VALUE "FileDescription", "KSwitcher — native keyboard and clipboard utility"
|
||||
VALUE "ProductName", "KSwitcher"
|
||||
VALUE "FileVersion", "0.6.78"
|
||||
VALUE "FileVersion", "0.6.79"
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
|
||||
@@ -7,6 +7,16 @@ const terminal = new Terminal({
|
||||
theme: { background: '#181818', foreground: '#e8e8e8' },
|
||||
});
|
||||
const fit = new FitAddon.FitAddon();
|
||||
terminal.parser.registerOscHandler(633, data => {
|
||||
const prefix = 'ks-directory:';
|
||||
if (!data.startsWith(prefix)) return false;
|
||||
try {
|
||||
bridge.postMessage('directory:' + decodeURIComponent(data.slice(prefix.length)));
|
||||
} catch {
|
||||
bridge.postMessage('directory:');
|
||||
}
|
||||
return true;
|
||||
});
|
||||
terminal.loadAddon(fit);
|
||||
const host = document.getElementById('terminal');
|
||||
terminal.open(host);
|
||||
|
||||
+23
-1
@@ -143,6 +143,28 @@ ULONG_PTR snippetReplayMarker()
|
||||
}
|
||||
constexpr int OpenFiles = 25, FilesHotkeyFirst = 0x4B10, FilesHotkeySecond = 0x4B11;
|
||||
|
||||
bool activateFilesWindow()
|
||||
{
|
||||
HWND files{};
|
||||
EnumWindows([](HWND candidate, LPARAM context) -> BOOL
|
||||
{
|
||||
wchar_t name[32]{};
|
||||
if(!IsWindowVisible(candidate) || !GetClassNameW(candidate,name,std::size(name)) ||
|
||||
wcscmp(name,L"KSwitcher.Files") || !configurationPeer(candidate,L"KSwitcher.Files.exe"))
|
||||
return TRUE;
|
||||
*reinterpret_cast<HWND *>(context)=candidate;
|
||||
return FALSE;
|
||||
},reinterpret_cast<LPARAM>(&files));
|
||||
if(!files) return false;
|
||||
if(IsIconic(files)) ShowWindowAsync(files,SW_RESTORE);
|
||||
if(!SetForegroundWindow(files))
|
||||
{
|
||||
FLASHWINFO flash{sizeof(flash),files,FLASHW_TRAY,3,0};
|
||||
FlashWindowEx(&flash);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
std::vector<std::pair<int, std::wstring>> shortcuts(const Settings &s)
|
||||
{
|
||||
std::vector<std::pair<int, std::wstring>> out{{Toggle, s.toggle},
|
||||
@@ -3037,7 +3059,7 @@ LRESULT CALLBACK App::windowProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp)
|
||||
case WM_HOTKEY:
|
||||
if (app->filesHotkeyId && wp == static_cast<WPARAM>(app->filesHotkeyId))
|
||||
{
|
||||
app->dispatch(OpenFiles);
|
||||
if(!activateFilesWindow()) app->dispatch(OpenFiles);
|
||||
}
|
||||
else if (wp != FilesHotkeyFirst && wp != FilesHotkeySecond) app->dispatch((int)wp);
|
||||
return 0;
|
||||
|
||||
+68
-11
@@ -6,6 +6,7 @@
|
||||
#include <commdlg.h>
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <deque>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <future>
|
||||
@@ -269,6 +270,8 @@ bool terminalViewRequested{};
|
||||
HWND terminalInput{},terminalOutput{},terminalPrompt{};
|
||||
constexpr UINT_PTR terminalTimer=2;
|
||||
bool terminalVisible{};
|
||||
std::deque<int> terminalCommandPanels;
|
||||
std::wstring terminalDirectory;
|
||||
int terminalRows{};
|
||||
struct CommandEntry { std::wstring date, command; };
|
||||
std::vector<CommandEntry> terminalHistory;
|
||||
@@ -797,6 +800,8 @@ void showTerminal(HWND owner,bool show)
|
||||
SetFocus(terminalInput);
|
||||
}
|
||||
|
||||
void open(Panel &panel,const fs::path &directory,bool offerElevation);
|
||||
|
||||
bool startTerminal(HWND owner)
|
||||
{
|
||||
if(terminal.alive()) return true;
|
||||
@@ -819,6 +824,20 @@ bool startTerminal(HWND owner)
|
||||
traceTerminal(events[phase],height,rows,viewport,base,offset);
|
||||
}
|
||||
}
|
||||
else if(message.starts_with(L"directory:"))
|
||||
{
|
||||
const auto directory=message.substr(10);
|
||||
if(!terminalCommandPanels.empty())
|
||||
{
|
||||
const int panel=terminalCommandPanels.front();
|
||||
terminalCommandPanels.pop_front();
|
||||
std::error_code error;
|
||||
if(panel>=0 && !panels[panel].peer && directory!=terminalDirectory &&
|
||||
!directory.empty() && fs::is_directory(directory,error))
|
||||
open(panels[panel],directory,false);
|
||||
}
|
||||
terminalDirectory=directory;
|
||||
}
|
||||
else if(message==L"focus-input")
|
||||
{
|
||||
if(GetForegroundWindow()==owner) SetFocus(terminalInput);
|
||||
@@ -855,7 +874,12 @@ bool startTerminal(HWND owner)
|
||||
setStatus(L"Select a local panel to start the terminal.");
|
||||
return false;
|
||||
}
|
||||
if(terminal.start(panels[active].directory.c_str(),terminalSize)) return true;
|
||||
if(terminal.start(panels[active].directory.c_str(),terminalSize))
|
||||
{
|
||||
terminalCommandPanels={-1};
|
||||
terminalDirectory.clear();
|
||||
return true;
|
||||
}
|
||||
setStatus(L"Could not start PowerShell.");
|
||||
return false;
|
||||
}
|
||||
@@ -907,6 +931,7 @@ LRESULT CALLBACK terminalInputKeys(HWND input,UINT message,WPARAM wp,LPARAM lp,U
|
||||
std::wstring value(GetWindowTextLengthW(input)+1,L'\0');
|
||||
value.resize(GetWindowTextW(input,value.data(),static_cast<int>(value.size())));
|
||||
if(!terminal.write(value+L"\r")) { setStatus(L"Could not send PowerShell input."); return 0; }
|
||||
terminalCommandPanels.push_back(active);
|
||||
if(value.find_first_not_of(L" \t\r\n")!=std::wstring::npos) rememberCommand(value);
|
||||
terminalHistoryIndex=terminalHistory.size();
|
||||
SetWindowTextW(input,L"");
|
||||
@@ -1215,10 +1240,19 @@ void queryDrivePeers()
|
||||
{
|
||||
COPYDATASTRUCT request{FilesLanPeers,0,nullptr};
|
||||
DWORD_PTR result{};
|
||||
SendMessageTimeoutW(drivePeerService,WM_COPYDATA,reinterpret_cast<WPARAM>(GetParent(picker)),
|
||||
reinterpret_cast<LPARAM>(&request),SMTO_ABORTIFHUNG|SMTO_ERRORONEXIT,350,&result);
|
||||
if(result!=1) drivePeers.clear();
|
||||
SetLastError(ERROR_SUCCESS);
|
||||
const auto delivered=SendMessageTimeoutW(drivePeerService,WM_COPYDATA,
|
||||
reinterpret_cast<WPARAM>(GetParent(picker)),reinterpret_cast<LPARAM>(&request),
|
||||
SMTO_ABORTIFHUNG|SMTO_ERRORONEXIT,350,&result);
|
||||
const auto error=GetLastError();
|
||||
if(!delivered || result!=1)
|
||||
{
|
||||
drivePeers.clear();
|
||||
ks::log(L"LAN Files peer lookup failed: delivered="+std::to_wstring(delivered!=0)+
|
||||
L", reply="+std::to_wstring(result)+L", Windows error="+std::to_wstring(error));
|
||||
}
|
||||
}
|
||||
else ks::log(L"LAN Files peer lookup failed: local KSwitcher window not found");
|
||||
drivePeerService=nullptr;
|
||||
}
|
||||
|
||||
@@ -1258,8 +1292,17 @@ void layoutDrivePicker(int selected,bool focus)
|
||||
static_cast<int>(SendMessageW(picker,LB_GETCOUNT,0,0)))*textHeight;
|
||||
const int x=panel.left+(panel.right-panel.left-width)/2;
|
||||
const int y=panel.top+(panel.bottom-panel.top-height)/2;
|
||||
SetWindowPos(pickerFrame,HWND_TOP,x-inset,y-vertical,width+2*inset,height+2*vertical,SWP_SHOWWINDOW|SWP_NOACTIVATE);
|
||||
SetWindowPos(picker,HWND_TOP,x,y,width,height,SWP_SHOWWINDOW|SWP_NOACTIVATE);
|
||||
constexpr UINT flags=SWP_SHOWWINDOW|SWP_NOACTIVATE|SWP_NOCOPYBITS;
|
||||
auto layout=BeginDeferWindowPos(2);
|
||||
if(layout) layout=DeferWindowPos(layout,pickerFrame,HWND_TOP,
|
||||
x-inset,y-vertical,width+2*inset,height+2*vertical,flags);
|
||||
if(layout) layout=DeferWindowPos(layout,picker,HWND_TOP,x,y,width,height,flags);
|
||||
if(!layout || !EndDeferWindowPos(layout))
|
||||
{
|
||||
SetWindowPos(pickerFrame,HWND_TOP,x-inset,y-vertical,width+2*inset,height+2*vertical,flags);
|
||||
SetWindowPos(picker,HWND_TOP,x,y,width,height,flags);
|
||||
}
|
||||
RedrawWindow(pickerFrame,nullptr,nullptr,RDW_INVALIDATE|RDW_UPDATENOW);
|
||||
SendMessageW(picker,LB_SETCURSEL,selected,0);
|
||||
if(focus) SetFocus(picker);
|
||||
}
|
||||
@@ -1347,10 +1390,15 @@ void pollDriveQueries(HWND owner)
|
||||
try {query.drives=query.pending.get();}
|
||||
catch(const std::system_error &e)
|
||||
{
|
||||
query.error=query.peer.filesBrowse?fileError(e):L"Check build / TCP 45885";
|
||||
ks::log(L"LAN Files drive query failed: Windows error "+std::to_wstring(e.code().value()));
|
||||
if(e.code()==std::error_code(WSAETIMEDOUT,std::system_category()))
|
||||
query.error=L"Connection timed out";
|
||||
else
|
||||
query.error=query.peer.filesBrowse?fileError(e):L"Check build / TCP 45885";
|
||||
}
|
||||
catch(const std::exception &)
|
||||
{
|
||||
ks::log(L"LAN Files drive query failed: unexpected error");
|
||||
query.error=query.peer.filesBrowse?L"Cannot reach TCP 45885":L"Check build / TCP 45885";
|
||||
}
|
||||
changed=true;
|
||||
@@ -3805,15 +3853,14 @@ LRESULT CALLBACK filesWindow(HWND window, UINT message, WPARAM wp, LPARAM lp)
|
||||
FillRect(item.hDC,&line,static_cast<HBRUSH>(GetStockObject(DC_BRUSH)));
|
||||
}
|
||||
}
|
||||
if (item.CtlType==ODT_LISTBOX)
|
||||
if (item.CtlType==ODT_LISTBOX && item.hwndItem!=picker)
|
||||
{
|
||||
RECT track{};
|
||||
GetWindowRect(item.hwndItem,&track);
|
||||
auto owner=item.hwndItem==picker?pickerFrame:window;
|
||||
MapWindowPoints(nullptr,owner,reinterpret_cast<POINT*>(&track),2);
|
||||
MapWindowPoints(nullptr,window,reinterpret_cast<POINT*>(&track),2);
|
||||
track.left=track.right;
|
||||
track.right+=5;
|
||||
InvalidateRect(owner,&track,FALSE);
|
||||
InvalidateRect(window,&track,FALSE);
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
@@ -3885,6 +3932,16 @@ LRESULT CALLBACK filesWindow(HWND window, UINT message, WPARAM wp, LPARAM lp)
|
||||
|
||||
int WINAPI wWinMain(HINSTANCE instance, HINSTANCE, PWSTR, int show)
|
||||
{
|
||||
bool writeLog=true;
|
||||
try
|
||||
{
|
||||
const auto settings=ks::dataDirectory()/L"settings.json";
|
||||
if(std::filesystem::exists(settings))
|
||||
writeLog=Json::parse(ks::readFile(settings,1024*1024)).value("writeLog",true);
|
||||
}
|
||||
catch(const std::exception &) {}
|
||||
ks::setLogFilename(L"kswitcher-files.log");
|
||||
ks::setLoggingEnabled(writeLog);
|
||||
int count{};
|
||||
if(auto args=CommandLineToArgvW(GetCommandLineW(),&count))
|
||||
{
|
||||
|
||||
@@ -49,7 +49,7 @@ public:
|
||||
startup.StartupInfo.cb=sizeof(startup);
|
||||
startup.lpAttributeList=attributes;
|
||||
PROCESS_INFORMATION info{};
|
||||
wchar_t command[]=L"powershell.exe -NoLogo -NoProfile -NoExit -Command \"Remove-Module PSReadLine -ErrorAction SilentlyContinue; function global:prompt { ([string][char]27) + '[?25l' }; Clear-Host\"";
|
||||
wchar_t command[]=L"powershell.exe -NoLogo -NoProfile -NoExit -Command \"Remove-Module PSReadLine -ErrorAction SilentlyContinue; function global:prompt { $location = Get-Location; $path = if ($location.Provider.Name -eq 'FileSystem') { [Uri]::EscapeDataString($location.ProviderPath) }; ([string][char]27) + ']633;ks-directory:' + $path + [char]7 + [char]27 + '[?25l' }; Clear-Host\"";
|
||||
wchar_t system[MAX_PATH]{};
|
||||
const auto length=GetSystemDirectoryW(system,MAX_PATH);
|
||||
const auto shell=std::wstring(system)+L"\\WindowsPowerShell\\v1.0\\powershell.exe";
|
||||
|
||||
+7
-1
@@ -737,6 +737,7 @@ namespace
|
||||
{
|
||||
std::recursive_mutex logGate;
|
||||
bool loggingEnabled = false;
|
||||
std::wstring logFilename = L"kswitcher.log";
|
||||
DiagnosticBuffer diagnosticBuffer;
|
||||
}
|
||||
size_t DiagnosticBuffer::expire(ULONGLONG now)
|
||||
@@ -832,6 +833,11 @@ void diagnosticOperation(const std::string &json)
|
||||
record["context"] = utf8(diagnosticBuffer.record(L"Operation complete", true, GetTickCount64()));
|
||||
log(L"Operation " + wide(record.dump(-1, ' ', true, nlohmann::json::error_handler_t::replace)));
|
||||
}
|
||||
void setLogFilename(std::wstring filename)
|
||||
{
|
||||
std::lock_guard lock(logGate);
|
||||
logFilename = std::move(filename);
|
||||
}
|
||||
void log(const std::wstring &message)
|
||||
{
|
||||
std::lock_guard lock(logGate);
|
||||
@@ -839,7 +845,7 @@ void log(const std::wstring &message)
|
||||
return;
|
||||
try
|
||||
{
|
||||
auto path = dataDirectory() / L"kswitcher.log";
|
||||
auto path = dataDirectory() / logFilename;
|
||||
std::error_code ec;
|
||||
auto size = std::filesystem::file_size(path, ec);
|
||||
if (!ec && size > 256 * 1024)
|
||||
|
||||
@@ -94,6 +94,7 @@ class DiagnosticBuffer
|
||||
void clear() { entries.clear(); }
|
||||
};
|
||||
void setLoggingEnabled(bool enabled);
|
||||
void setLogFilename(std::wstring filename);
|
||||
void expireDiagnostics(ULONGLONG now);
|
||||
void diagnosticEvent(const std::wstring &message, bool problem = false);
|
||||
void diagnosticOperation(const std::string &json);
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@
|
||||
#include "platform.h"
|
||||
namespace ks
|
||||
{
|
||||
inline constexpr wchar_t AppVersion[] = L"0.6.78";
|
||||
inline constexpr wchar_t AppVersion[] = L"0.6.79";
|
||||
inline constexpr UINT UpdateMessage = WM_APP + 7;
|
||||
inline constexpr UINT UpdateCheckIntervalMs = 6 * 60 * 60 * 1000;
|
||||
inline constexpr UINT UpdateNotificationRetryMs = 5 * 60 * 1000;
|
||||
|
||||
Reference in new issue
Block a user