From dcbd38596ef17a985934975294f61e33819b019c Mon Sep 17 00:00:00 2001 From: serenitatis <30504381+serenitatis@users.noreply.github.com> Date: Fri, 19 Jun 2026 21:44:28 +0300 Subject: [PATCH] =?UTF-8?q?=D0=9D=D0=B0=D1=87=D0=B0=D0=BB=D1=8C=D0=BD?= =?UTF-8?q?=D1=8B=D0=B9=20=D0=BA=D0=BE=D0=BC=D0=BC=D0=B8=D1=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 330 ++++++++++++++++++++++++++++++++ LICENSE | 21 ++ README.md | 6 + Switchy/Switchy.rc | 186 ++++++++++++++++++ Switchy/Switchy.vcxproj | 151 +++++++++++++++ Switchy/Switchy.vcxproj.filters | 37 ++++ Switchy/icon.ico | Bin 0 -> 370070 bytes Switchy/main.c | 253 ++++++++++++++++++++++++ Switchy/resource.h | 36 ++++ SwitchyAlt.sln | 31 +++ 10 files changed, 1051 insertions(+) create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 README.md create mode 100644 Switchy/Switchy.rc create mode 100644 Switchy/Switchy.vcxproj create mode 100644 Switchy/Switchy.vcxproj.filters create mode 100644 Switchy/icon.ico create mode 100644 Switchy/main.c create mode 100644 Switchy/resource.h create mode 100644 SwitchyAlt.sln diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3e759b7 --- /dev/null +++ b/.gitignore @@ -0,0 +1,330 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# User-specific files +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUNIT +*.VisualState.xml +TestResult.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ +**/Properties/launchSettings.json + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_i.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# JustCode is a .NET coding add-in +.JustCode + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# JetBrains Rider +.idea/ +*.sln.iml + +# CodeRush +.cr/ + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..e5d4ac9 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 Max Ignatiev + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..4601f8b --- /dev/null +++ b/README.md @@ -0,0 +1,6 @@ +# SwitchyAlt + +Переключатель раскладки клавиатуры про помощи правой клавиши Alt. +Нажатие левого и правого Alt пристанавливает работу приложения до его перезапуска. + +Основано на https://github.com/erryox/Switchy \ No newline at end of file diff --git a/Switchy/Switchy.rc b/Switchy/Switchy.rc new file mode 100644 index 0000000..d758c85 --- /dev/null +++ b/Switchy/Switchy.rc @@ -0,0 +1,186 @@ +// Microsoft Visual C++ generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "winres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// () resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_RUS) +LANGUAGE LANG_RUSSIAN, SUBLANG_DEFAULT +#pragma code_page(1251) + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""winres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDI_ICON1 ICON "icon.ico" + + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_ABOUTBOX DIALOGEX 0, 0, 201, 223 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION " " +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + DEFPUSHBUTTON "",IDOK,144,201,50,14 + CTEXT "IDC_STATIC_BASEDON",IDC_STATIC_BASEDON,7,185,187,10 + CTEXT "IDC_STATIC_APPINFO",IDC_STATIC_APPINFO,7,7,186,22 + EDITTEXT IDC_INFO,7,31,187,149,ES_MULTILINE | ES_READONLY | WS_VSCROLL +END + + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO +BEGIN + IDD_ABOUTBOX, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 194 + TOPMARGIN, 7 + BOTTOMMARGIN, 215 + END +END +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// AFX_DIALOG_LAYOUT +// + +IDD_ABOUTBOX AFX_DIALOG_LAYOUT +BEGIN + 0 +END + + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +VS_VERSION_INFO VERSIONINFO + FILEVERSION 1,0,0,0 + PRODUCTVERSION 1,0,0,0 + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x40004L + FILETYPE 0x1L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "041904b0" + BEGIN + VALUE "CompanyName", "serenitatis" + VALUE "FileDescription", "SwitchyAlt" + VALUE "FileVersion", "1.0.0.0" + VALUE "InternalName", "SwitchyAlt.exe" + VALUE "LegalCopyright", "2026, serenitatis" + VALUE "OriginalFilename", "SwitchyAlt.exe" + VALUE "ProductName", "SwitchyAlt" + VALUE "ProductVersion", "1.0.0.0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x419, 1200 + END +END + + +///////////////////////////////////////////////////////////////////////////// +// +// String Table +// + +STRINGTABLE +BEGIN + IDS_ABOUT_TEXT " Alt.\r\n Alt .\r\n\r\n******************************************\r\n \r\n\r\n1.0\r\n- ." + IDS_APP_MUTEX_ERROR " SwitchyAlt" + IDS_APP_ALREADY_RUNNING " SwitchyAlt" + IDS_ERROR_REG_CLASS " " + IDS_ERROR_CREATE_WINDOW " " + IDS_ERROR_TRAY_ICON " " +END + +STRINGTABLE +BEGIN + IDS_ERROR_SETHOOK " SetWindowsHookEx" + IDS_MENU_PAUSE "" + IDS_MENU_RESUME "" + IDS_MENU_ABOUT " " + IDS_MENU_EXIT "" + IDS_TRAY_TOOLTIP "SwitchyAlt" + IDS_ABOUT_TITLE " " + IDS_BASEDON " https://github.com/erryox/Switchy" + IDS_APPINFO "SwitchyAlt 1.0\r\n 2026, serenitatis." +END + +#endif // () resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/Switchy/Switchy.vcxproj b/Switchy/Switchy.vcxproj new file mode 100644 index 0000000..9b4ec64 --- /dev/null +++ b/Switchy/Switchy.vcxproj @@ -0,0 +1,151 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 16.0 + {16A46215-C3FC-4F84-966D-22B97CADE8C0} + SwitchyAlt + 10.0 + SwitchyAlt + + + + Application + true + v143 + MultiByte + + + Application + false + v143 + true + MultiByte + + + Application + true + v142 + MultiByte + + + Application + false + v142 + true + MultiByte + + + + + + + + + + + + + + + + + + + + + + + Level3 + Disabled + true + true + + + + + Console + + + + + Level3 + Disabled + true + true + %(AdditionalIncludeDirectories) + + + Windows + + + + + Level3 + MaxSpeed + true + true + true + true + + + + + Windows + true + true + mainCRTStartup + + + + + Level3 + MaxSpeed + true + true + true + true + + + + + Windows + true + true + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Switchy/Switchy.vcxproj.filters b/Switchy/Switchy.vcxproj.filters new file mode 100644 index 0000000..fe13d42 --- /dev/null +++ b/Switchy/Switchy.vcxproj.filters @@ -0,0 +1,37 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;ipp;xsd + + + + + Source Files + + + + + Header Files + + + + + Resource Files + + + + + Resource Files + + + \ No newline at end of file diff --git a/Switchy/icon.ico b/Switchy/icon.ico new file mode 100644 index 0000000000000000000000000000000000000000..4043c8af331b0c5883cfe5390e16c62dc1270323 GIT binary patch literal 370070 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

v0^Q~punzxaj?GH_}6nF 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 G_x}Npn&E>0 literal 0 HcmV?d00001 diff --git a/Switchy/main.c b/Switchy/main.c new file mode 100644 index 0000000..ef9c24e --- /dev/null +++ b/Switchy/main.c @@ -0,0 +1,253 @@ +#include +#include +#include +#include "resource.h" + +#pragma comment(lib, "comctl32.lib") +#pragma comment(lib, "user32.lib") +#pragma comment(lib, "shell32.lib") + +#pragma comment(linker, \ + "\"/manifestdependency:type='win32' " \ + "name='Microsoft.Windows.Common-Controls' " \ + "version='6.0.0.0' " \ + "processorArchitecture='*' " \ + "publicKeyToken='6595b64144ccf1df' " \ + "language='*'\"") + +#define WM_TRAYNOTIFY (WM_USER + 100) +#define ID_TRAYICON 1 +#define IDM_EXIT 1001 +#define IDM_TOGGLE 1002 +#define IDM_ABOUT 1003 + +HHOOK hHook = NULL; +HWND hWndHidden = NULL; +NOTIFYICONDATA nid = { 0 }; +BOOL enabled = TRUE; +BOOL keystrokeRightAltProcessed = FALSE; + +HINSTANCE g_hInstance = NULL; + +LPCTSTR LoadStr(UINT id) +{ + static TCHAR buf[1024]; + buf[0] = TEXT('\0'); + LoadString(g_hInstance, id, buf, _countof(buf)); + return buf; +} + +void ShowError(UINT id) +{ + MessageBox(NULL, LoadStr(id), TEXT("SwitchyAlt"), MB_OK | MB_ICONERROR); +} + +void SwitchLayout() +{ + HWND hwndForeground = GetForegroundWindow(); + if (!hwndForeground) return; + + char className[256]; + GetClassNameA(hwndForeground, className, sizeof(className)); + + BOOL isRDP = (strstr(className, "TscShellContainerClass") != NULL); + BOOL isConsole = (strstr(className, "ConsoleWindowClass") != NULL); + + if (isRDP || isConsole) + { + INPUT inputs[4] = { 0 }; + for (int i = 0; i < 4; i++) inputs[i].type = INPUT_KEYBOARD; + inputs[0].ki.wVk = VK_MENU; inputs[0].ki.wScan = 0x38; + inputs[1].ki.wVk = VK_SHIFT; inputs[1].ki.wScan = 0x2A; + inputs[2].ki.wVk = VK_SHIFT; inputs[2].ki.wScan = 0x2A; inputs[2].ki.dwFlags = KEYEVENTF_KEYUP; + inputs[3].ki.wVk = VK_MENU; inputs[3].ki.wScan = 0x38; inputs[3].ki.dwFlags = KEYEVENTF_KEYUP; + SendInput(4, inputs, sizeof(INPUT)); + } + else + { + GUITHREADINFO gti = { sizeof(GUITHREADINFO) }; + HWND targetWnd = hwndForeground; + + if (GetGUIThreadInfo(0, >i) && gti.hwndFocus != NULL) + { + targetWnd = gti.hwndFocus; + } + + PostMessage(targetWnd, WM_INPUTLANGCHANGEREQUEST, 0, (LPARAM)HKL_NEXT); + } +} + +LRESULT CALLBACK LowLevelKeyboardProc(int nCode, WPARAM wParam, LPARAM lParam) +{ + if (nCode != HC_ACTION) + return CallNextHookEx(hHook, nCode, wParam, lParam); + + KBDLLHOOKSTRUCT* p = (KBDLLHOOKSTRUCT*)lParam; + + if (p->flags & LLKHF_INJECTED) + return CallNextHookEx(hHook, nCode, wParam, lParam); + + if (p->vkCode == VK_RMENU) + { + if (wParam == WM_KEYDOWN || wParam == WM_SYSKEYDOWN) + { + if (!keystrokeRightAltProcessed) + { + keystrokeRightAltProcessed = TRUE; + + if (GetAsyncKeyState(VK_LMENU) & 0x8000) + { + enabled = !enabled; + return 1; + } + + if (enabled) + { + SwitchLayout(); + } + } + return 1; + } + + if (wParam == WM_KEYUP || wParam == WM_SYSKEYUP) + { + keystrokeRightAltProcessed = FALSE; + return 1; + } + } + + return CallNextHookEx(hHook, nCode, wParam, lParam); +} + +INT_PTR CALLBACK AboutDlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) +{ + switch (message) + { + case WM_INITDIALOG: + SetDlgItemText(hDlg, IDC_INFO, LoadStr(IDS_ABOUT_TEXT)); + SetDlgItemText(hDlg, IDC_STATIC_APPINFO, LoadStr(IDS_APPINFO)); + SetDlgItemText(hDlg, IDC_STATIC_BASEDON, LoadStr(IDS_BASEDON)); + return (INT_PTR)TRUE; + + case WM_COMMAND: + if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL) + { + EndDialog(hDlg, LOWORD(wParam)); + return (INT_PTR)TRUE; + } + break; + } + return (INT_PTR)FALSE; +} + +LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + switch (msg) + { + case WM_TRAYNOTIFY: + if (lParam == WM_RBUTTONUP) + { + POINT pt; + GetCursorPos(&pt); + + HMENU hMenu = CreatePopupMenu(); + if (hMenu) + { + AppendMenu(hMenu, MF_STRING, IDM_TOGGLE, + enabled ? LoadStr(IDS_MENU_PAUSE) : LoadStr(IDS_MENU_RESUME)); + + AppendMenu(hMenu, MF_STRING, IDM_ABOUT, LoadStr(IDS_MENU_ABOUT)); + AppendMenu(hMenu, MF_SEPARATOR, 0, NULL); + AppendMenu(hMenu, MF_STRING, IDM_EXIT, LoadStr(IDS_MENU_EXIT)); + + SetForegroundWindow(hwnd); + UINT cmd = TrackPopupMenu(hMenu, TPM_RETURNCMD | TPM_NONOTIFY, pt.x, pt.y, 0, hwnd, NULL); + DestroyMenu(hMenu); + + if (cmd == IDM_TOGGLE) enabled = !enabled; + if (cmd == IDM_ABOUT) + DialogBox(g_hInstance, MAKEINTRESOURCE(IDD_ABOUTBOX), hwnd, AboutDlgProc); + if (cmd == IDM_EXIT) + PostQuitMessage(0); + } + } + break; + + case WM_DESTROY: + PostQuitMessage(0); + break; + + default: + return DefWindowProc(hwnd, msg, wParam, lParam); + } + return 0; +} + +int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrev, LPSTR lpCmd, int nShow) +{ + g_hInstance = hInstance; + + HANDLE hMutex = CreateMutex(NULL, FALSE, TEXT("SwitchyAltUnique")); + if (GetLastError() == ERROR_ALREADY_EXISTS) + { + ShowError(IDS_APP_MUTEX_ERROR); + return 1; + } + + WNDCLASSEX wc = { sizeof(WNDCLASSEX) }; + wc.lpfnWndProc = WndProc; + wc.hInstance = hInstance; + wc.lpszClassName = TEXT("SwitchyHiddenClass"); + + if (!RegisterClassEx(&wc)) + { + ShowError(IDS_ERROR_REG_CLASS); + return 1; + } + + hWndHidden = CreateWindowEx(0, wc.lpszClassName, TEXT("SwitchyAlt"), + 0, 0, 0, 0, 0, HWND_MESSAGE, NULL, hInstance, NULL); + + if (!hWndHidden) + { + ShowError(IDS_ERROR_CREATE_WINDOW); + return 1; + } + + InitCommonControls(); + + nid.cbSize = sizeof(NOTIFYICONDATA); + nid.hWnd = hWndHidden; + nid.uID = ID_TRAYICON; + nid.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP; + nid.uCallbackMessage = WM_TRAYNOTIFY; + nid.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_ICON1)); + lstrcpy(nid.szTip, LoadStr(IDS_TRAY_TOOLTIP)); + + if (!Shell_NotifyIcon(NIM_ADD, &nid)) + { + ShowError(IDS_ERROR_TRAY_ICON); + } + + hHook = SetWindowsHookEx(WH_KEYBOARD_LL, LowLevelKeyboardProc, hInstance, 0); + if (!hHook) + { + ShowError(IDS_ERROR_SETHOOK); + Shell_NotifyIcon(NIM_DELETE, &nid); + DestroyWindow(hWndHidden); + return 1; + } + + MSG msg; + while (GetMessage(&msg, NULL, 0, 0)) + { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + + UnhookWindowsHookEx(hHook); + Shell_NotifyIcon(NIM_DELETE, &nid); + DestroyWindow(hWndHidden); + + return (int)msg.wParam; +} \ No newline at end of file diff --git a/Switchy/resource.h b/Switchy/resource.h new file mode 100644 index 0000000..70d00e9 --- /dev/null +++ b/Switchy/resource.h @@ -0,0 +1,36 @@ +//{{NO_DEPENDENCIES}} +// , Microsoft Visual C++. +// Switchy.rc +// +#define IDI_ICON1 102 +#define IDD_ABOUTBOX 104 +#define IDS_ABOUT_TEXT 106 +#define IDS_APP_MUTEX_ERROR 107 +#define IDS_APP_ALREADY_RUNNING 108 +#define IDS_ERROR_REG_CLASS 109 +#define IDS_ERROR_CREATE_WINDOW 110 +#define IDS_ERROR_TRAY_ICON 111 +#define IDS_ERROR_SETHOOK 112 +#define IDS_MENU_PAUSE 113 +#define IDS_MENU_RESUME 114 +#define IDS_MENU_ABOUT 115 +#define IDS_MENU_EXIT 116 +#define IDS_TRAY_TOOLTIP 117 +#define IDS_ABOUT_TITLE 118 +#define IDS_BASEDON 119 +#define IDS_APPINFO 120 +#define IDC_CUSTOM1 1002 +#define IDC_INFO 1004 +#define IDC_STATIC_BASEDON 1005 +#define IDC_STATIC_APPINFO 1007 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 121 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1008 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/SwitchyAlt.sln b/SwitchyAlt.sln new file mode 100644 index 0000000..69c7922 --- /dev/null +++ b/SwitchyAlt.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.29306.81 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Switchy", "Switchy\Switchy.vcxproj", "{16A46215-C3FC-4F84-966D-22B97CADE8C0}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {16A46215-C3FC-4F84-966D-22B97CADE8C0}.Debug|x64.ActiveCfg = Debug|x64 + {16A46215-C3FC-4F84-966D-22B97CADE8C0}.Debug|x64.Build.0 = Debug|x64 + {16A46215-C3FC-4F84-966D-22B97CADE8C0}.Debug|x86.ActiveCfg = Debug|Win32 + {16A46215-C3FC-4F84-966D-22B97CADE8C0}.Debug|x86.Build.0 = Debug|Win32 + {16A46215-C3FC-4F84-966D-22B97CADE8C0}.Release|x64.ActiveCfg = Release|x64 + {16A46215-C3FC-4F84-966D-22B97CADE8C0}.Release|x64.Build.0 = Release|x64 + {16A46215-C3FC-4F84-966D-22B97CADE8C0}.Release|x86.ActiveCfg = Release|Win32 + {16A46215-C3FC-4F84-966D-22B97CADE8C0}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {3A751753-9F42-4929-A0D8-8B9011CE60FB} + EndGlobalSection +EndGlobal