Release v0.6.0: screenshot notifications with previews and responsive clipboard history
This commit is contained in:
+44
-4
@@ -1,5 +1,8 @@
|
||||
#include "screenshot.h"
|
||||
#include "screenshot_notifications.h"
|
||||
#include <commctrl.h>
|
||||
#include <shellapi.h>
|
||||
#include <roapi.h>
|
||||
|
||||
namespace {
|
||||
constexpr wchar_t ControllerClass[]=L"KSwitcher.Screenshot.Controller";
|
||||
@@ -18,6 +21,18 @@ LRESULT CALLBACK controller(HWND window,UINT message,WPARAM wp,LPARAM lp)
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
if(host && message==WM_COPYDATA) {
|
||||
auto data=(COPYDATASTRUCT *)lp;
|
||||
if(!data || data->dwData!=73 || data->cbData!=37*sizeof(wchar_t) || !data->lpData) return FALSE;
|
||||
std::wstring token((const wchar_t *)data->lpData,36);
|
||||
if(!ks::validScreenshotToken(token)) return FALSE;
|
||||
try { ks::showScreenshotEditor(*host,ks::loadNotifiedScreenshot(token)); }
|
||||
catch(const std::exception &e) {
|
||||
MessageBoxW(window,ks::wide(e.what()).c_str(),L"Screenshot",MB_OK|MB_ICONINFORMATION);
|
||||
PostMessageW(window,ks::ScreenshotIdle,0,0);
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
if(host && message==ks::ScreenshotIdle && !host->windows) {
|
||||
DestroyWindow(window);
|
||||
PostQuitMessage(0);
|
||||
@@ -29,6 +44,25 @@ LRESULT CALLBACK controller(HWND window,UINT message,WPARAM wp,LPARAM lp)
|
||||
|
||||
int WINAPI wWinMain(HINSTANCE instance,HINSTANCE,PWSTR,int)
|
||||
{
|
||||
std::wstring token;
|
||||
int argc{}; auto argv=CommandLineToArgvW(GetCommandLineW(),&argc);
|
||||
if(argc>=2 && std::wstring(argv[1])==L"--toast-activated") {
|
||||
LocalFree(argv); return ks::runScreenshotToastActivator();
|
||||
}
|
||||
if(argc==2 && std::wstring(argv[1])==L"--register-notifications") {
|
||||
LocalFree(argv); CoInitializeEx(nullptr,COINIT_APARTMENTTHREADED);
|
||||
int result=0; try { ks::registerScreenshotNotifications(); } catch(...) { result=1; }
|
||||
CoUninitialize(); return result;
|
||||
}
|
||||
if(argc>1) {
|
||||
if(argc==3 && std::wstring(argv[1])==L"--open") {
|
||||
std::wstring uri=argv[2]; const std::wstring prefix=L"kswitcher-screenshot:";
|
||||
if(uri.starts_with(prefix)) token=uri.substr(prefix.size());
|
||||
}
|
||||
if(!ks::validScreenshotToken(token)) { LocalFree(argv); return 1; }
|
||||
}
|
||||
LocalFree(argv);
|
||||
COPYDATASTRUCT open{73,DWORD((token.size()+1)*sizeof(wchar_t)),token.data()};
|
||||
HANDLE mutex=CreateMutexW(nullptr,FALSE,L"Local\\KSwitcher.Screenshot.Singleton");
|
||||
if(!mutex) return 1;
|
||||
// Wait through startup/shutdown races; the controller accepts requests only while alive.
|
||||
@@ -40,30 +74,36 @@ int WINAPI wWinMain(HINSTANCE instance,HINSTANCE,PWSTR,int)
|
||||
DWORD pid{}; GetWindowThreadProcessId(existing,&pid);
|
||||
AllowSetForegroundWindow(pid);
|
||||
DWORD_PTR accepted{};
|
||||
if(SendMessageTimeoutW(existing,ks::ScreenshotRequest,0,0,SMTO_ABORTIFHUNG,2000,&accepted) && accepted) {
|
||||
if(SendMessageTimeoutW(existing,token.empty()?ks::ScreenshotRequest:WM_COPYDATA,0,
|
||||
token.empty()?0:(LPARAM)&open,SMTO_ABORTIFHUNG,2000,&accepted) && accepted) {
|
||||
CloseHandle(mutex); return 0;
|
||||
}
|
||||
}
|
||||
Sleep(50);
|
||||
}
|
||||
if(!acquired) { CloseHandle(mutex); return 1; }
|
||||
CoInitializeEx(nullptr,COINIT_APARTMENTTHREADED);
|
||||
RoInitialize(RO_INIT_SINGLETHREADED);
|
||||
INITCOMMONCONTROLSEX controls{sizeof(controls),ICC_STANDARD_CLASSES|ICC_BAR_CLASSES};
|
||||
InitCommonControlsEx(&controls);
|
||||
ks::ScreenshotHost host{instance};
|
||||
host.notify=ks::notifyScreenshot;
|
||||
WNDCLASSW cls{}; cls.hInstance=instance; cls.lpfnWndProc=controller; cls.lpszClassName=ControllerClass;
|
||||
RegisterClassW(&cls);
|
||||
host.window=CreateWindowW(ControllerClass,L"KSwitcher Screenshot",0,0,0,0,0,nullptr,nullptr,instance,&host);
|
||||
int result=1;
|
||||
if(host.window) {
|
||||
PostMessageW(host.window,ks::ScreenshotRequest,0,0);
|
||||
// Toast protocol activation may run unelevated while the editor was launched by an elevated resident.
|
||||
// WM_COPYDATA accepts only a validated cache GUID, never a path or executable command.
|
||||
ChangeWindowMessageFilterEx(host.window,WM_COPYDATA,MSGFLT_ALLOW,nullptr);
|
||||
if(token.empty()) PostMessageW(host.window,ks::ScreenshotRequest,0,0);
|
||||
else SendMessageW(host.window,WM_COPYDATA,0,(LPARAM)&open);
|
||||
MSG message{};
|
||||
while((result=GetMessageW(&message,nullptr,0,0))>0) {
|
||||
TranslateMessage(&message); DispatchMessageW(&message);
|
||||
}
|
||||
result=result<0?1:0;
|
||||
}
|
||||
CoUninitialize();
|
||||
RoUninitialize();
|
||||
ReleaseMutex(mutex); CloseHandle(mutex);
|
||||
return result;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user