diff --git a/Lang/_en_US baseline.txt b/Lang/_en_US baseline.txt index c7f9ef85..4ed1bacc 100644 Binary files a/Lang/_en_US baseline.txt and b/Lang/_en_US baseline.txt differ diff --git a/Lang/de_DE.ini b/Lang/de_DE.ini index 156ba8ba..6a5c1cff 100644 Binary files a/Lang/de_DE.ini and b/Lang/de_DE.ini differ diff --git a/MakefileX64 b/MakefileX64 index 031bc265..42274027 100644 --- a/MakefileX64 +++ b/MakefileX64 @@ -16,7 +16,7 @@ CFLAGS=-Os -fno-stack-check -fno-stack-protector -fno-ident -fomit-frame-pointer default: AltSnap.exe hooks.dll hooks.dll : hooks.c hooks.h hooksr.o unfuck.h nanolibc.h zones.c snap.c - $(CC) -o hooks.dll hooks.c hooksr.o $(CFLAGS) -mdll -eDllMain -Wl,--kill-at + $(CC) -o hooks.dll hooks.c hooksr.o $(CFLAGS) -mdll -eDllMain -Wl,--kill-at -lmagnification AltSnap.exe : altsnapr.o altsnap.c hooks.h tray.c config.c languages.h languages.c unfuck.h nanolibc.h $(CC) -o AltSnap.exe altsnap.c altsnapr.o $(CFLAGS) -Wl,--tsaware,--disable-reloc-section -mwindows -lcomctl32 -ladvapi32 -lshell32 -eunfuckWinMain diff --git a/config.c b/config.c index 9a5ef088..d53460bf 100644 --- a/config.c +++ b/config.c @@ -892,6 +892,7 @@ INT_PTR CALLBACK MousePageDialogProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM static const struct actiondl scroll_actions[] = { {TEXT("AltTab"), L10NIDX(input_actions_alttab) }, {TEXT("Volume"), L10NIDX(input_actions_volume) }, + {TEXT("Magnify"), L10NIDX(input_actions_magnify) }, {TEXT("Transparency"), L10NIDX(input_actions_transparency) }, {TEXT("Zoom"), L10NIDX(input_actions_zoom) }, {TEXT("Zoom2"), L10NIDX(input_actions_zoom2) }, diff --git a/hooks.c b/hooks.c index 92640e7c..5f91aa30 100644 --- a/hooks.c +++ b/hooks.c @@ -7,6 +7,8 @@ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #include "hooks.h" +#include +#include static void MoveWindowAsync(HWND hwnd, int x, int y, int w, int h); static BOOL CALLBACK EnumMonitorsProc(HMONITOR, HDC, LPRECT , LPARAM ); static LRESULT CALLBACK MenuWindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam); @@ -3146,6 +3148,220 @@ static void ActionVolume(int delta) Send_KEY(delta>0? VK_VOLUME_UP: VK_VOLUME_DOWN); } +///////////////////////////////////////////////////////////////////////////// +// Window magnification functionality +#define MAGNIFY_HOST_CLASS_NAME TEXT("MagnifierWindow") +#define MAGNIFY_CTRL_CLASS_NAME TEXT("Magnifier") +#define MAG_HOST_EX_STYLES (WS_EX_TOPMOST | WS_EX_LAYERED | WS_EX_TRANSPARENT | WS_EX_TOOLWINDOW) +#define MAGNIFY_TIMER_ID (WM_APP + 1) +#define MAGNIFY_TIMER_INTERVAL 16 // 60 FPS +#define MAX_MAGNIFICATION_FACTOR 10.0f +#define MS_SHOWMAGNIFIEDCURSOR 0x0001 + +LRESULT CALLBACK MagHostWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); + +BOOL magInitialized = FALSE; +HWND magHwndHost = NULL; +HWND magHwndCtrl = NULL; +HWND magHwndTarget = NULL; +POINT magCenterPt = {0, 0}; +float magFactor = 1.0f; + +BOOL GetClientRectToScreen(HWND hwnd, RECT *rc) +{ + if (!GetClientRect(hwnd, rc)) + return FALSE; + return ClientToScreen(hwnd, (LPPOINT)&rc->left) && ClientToScreen(hwnd, (LPPOINT)&rc->right); +} +void DisableMagnification() +{ + if (magHwndHost) + ShowWindow(magHwndHost, SW_HIDE); + magFactor = 1.0f; + magHwndTarget = NULL; +} +void SetupMagnification(HWND hwndTarget) +{ + if (!magInitialized) { + if (!MagInitialize()) + return; + + WNDCLASSEX wcex = {0}; + wcex.cbSize = sizeof(WNDCLASSEX); + wcex.style = CS_HREDRAW | CS_VREDRAW; + wcex.lpfnWndProc = MagHostWndProc; + wcex.hInstance = hinstDLL; + wcex.hCursor = LoadCursor(NULL, IDC_ARROW); + wcex.lpszClassName = MAGNIFY_HOST_CLASS_NAME; + RegisterClassEx(&wcex); + + magInitialized = TRUE; + } + + RECT tgtRc; + GetClientRectToScreen(hwndTarget, &tgtRc); + + // setup or update host window + if (magHwndHost == NULL) { + magHwndHost = CreateWindowEx(MAG_HOST_EX_STYLES, + MAGNIFY_HOST_CLASS_NAME, TEXT("Magnifier Host"), + WS_VISIBLE | WS_POPUP, + tgtRc.left, tgtRc.top, tgtRc.right - tgtRc.left, tgtRc.bottom - tgtRc.top, NULL, NULL, hinstDLL, NULL); + if (magHwndHost == NULL) { + return; + } + + SetTimer(magHwndHost, MAGNIFY_TIMER_ID, MAGNIFY_TIMER_INTERVAL, NULL); + SetLayeredWindowAttributes(magHwndHost, 0, 255, LWA_ALPHA); + } + else if (hwndTarget != magHwndTarget) { + DisableMagnification(); + SetWindowPos(magHwndHost, NULL, tgtRc.left, tgtRc.top, tgtRc.right - tgtRc.left, tgtRc.bottom - tgtRc.top, 0); + } + + // setup or update magnifier control window + if (magHwndCtrl == NULL) { + magHwndCtrl = CreateWindow(MAGNIFY_CTRL_CLASS_NAME, TEXT("Magnifier Control"), + WS_CHILD | WS_VISIBLE | MS_SHOWMAGNIFIEDCURSOR, + 0, 0, tgtRc.right - tgtRc.left, tgtRc.bottom - tgtRc.top, magHwndHost, NULL, hinstDLL, NULL ); + } + else if (hwndTarget != magHwndTarget) { + SetWindowPos(magHwndCtrl, NULL, 0, 0, tgtRc.right - tgtRc.left, tgtRc.bottom - tgtRc.top, 0); + } + + magHwndTarget = hwndTarget; +} +void CleanupMagnification() +{ + if (!magInitialized) + return; + + DisableMagnification(); + MagUninitialize(); + + if (magHwndCtrl && IsWindow(magHwndCtrl)) { + DestroyWindow(magHwndCtrl); + } + magHwndCtrl = NULL; + + if (magHwndHost && IsWindow(magHwndHost)) { + KillTimer(magHwndHost, MAGNIFY_TIMER_ID); + DestroyWindow(magHwndHost); + } + magHwndHost = NULL; + + UnregisterClass(MAGNIFY_HOST_CLASS_NAME, hinstDLL); +} +void UpdateMagnification() +{ + RECT tgtRc; + GetClientRectToScreen(magHwndTarget, &tgtRc); + + // allow to reposition the magnification center in case the hotkey is pressed AND the mouse is over the target window + if (IsHotkeyDown()) { + POINT pt; + GetCursorPos(&pt); + if (pt.x >= tgtRc.left && pt.x <= tgtRc.right && pt.y >= tgtRc.top && pt.y <= tgtRc.bottom) { + magCenterPt = pt; + } + } + + // make sure the window is still on top + SetWindowLong(magHwndHost, GWL_EXSTYLE, MAG_HOST_EX_STYLES); + + // calculate positon of the magnifier control window + int width = tgtRc.right - tgtRc.left; + int height = tgtRc.bottom - tgtRc.top; + int magWidth = (int)(round(width / magFactor)); + int magHeight = (int)(round(height / magFactor)); + int magHalfWidth = magWidth / 2; + int magHalfHeight = magHeight / 2; + + float fx = 0.0f; + int x = magCenterPt.x - tgtRc.left; + if (x < magHalfWidth || width == magWidth) + fx = 0.0f; + else if (x > width - magHalfWidth) + fx = 1.0f; + else + fx = (float)(x - magHalfWidth) / (float)(width - magWidth); + + float fy = 0.0f; + int y = magCenterPt.y - tgtRc.top; + if (y < magHalfHeight || height == magHeight) + fy = 0.0f; + else if (y > height - magHalfHeight) + fy = 1.0f; + else + fy = (float)(y - magHalfHeight) / (float)(height - magHeight); + + RECT ctrlRc; + ctrlRc.left = tgtRc.left + (int)round((width - magWidth) * fx); + ctrlRc.top = tgtRc.top + (int)round((height - magHeight) * fy); + ctrlRc.right = ctrlRc.left + magWidth; + ctrlRc.bottom = ctrlRc.top + magHeight; + + MagSetWindowSource(magHwndCtrl, ctrlRc); + + MAGTRANSFORM matrix; + memset(&matrix, 0, sizeof(matrix)); + matrix.v[0][0] = magFactor; + matrix.v[1][1] = magFactor; + matrix.v[2][2] = 1.0f; + MagSetWindowTransform(magHwndCtrl, &matrix); + + ShowWindow(magHwndHost, SW_SHOW); +} +LRESULT CALLBACK MagHostWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + switch (message) + { + case WM_TIMER: + if (wParam == MAGNIFY_TIMER_ID) { + if (!magHwndTarget || magFactor <= 1.01f) { + DisableMagnification(); + return 0; + } + + RECT tgtRc, hostRc; + GetClientRectToScreen(magHwndTarget, &tgtRc); + GetClientRectToScreen(hWnd, &hostRc); + if (hostRc.right != tgtRc.right || hostRc.bottom != tgtRc.bottom || hostRc.left != tgtRc.left || hostRc.top != tgtRc.top) { + // the window has been moved or resized + // -> disable magnification + DisableMagnification(); + } + else { + UpdateMagnification(); + } + } + break; + + default: + return DefWindowProc(hWnd, message, wParam, lParam); + } + return 0; +} +static int ActionMagnification(HWND hwnd, short delta) +{ + SetupMagnification(hwnd); + + // TODO: configure step sizes? + magFactor += delta * 0.002f; + magFactor = (float)((int)(magFactor * 10)) / 10.0f; + + if (magFactor < 1.0f) + magFactor = 1.0f; + if (magFactor > MAX_MAGNIFICATION_FACTOR) + magFactor = MAX_MAGNIFICATION_FACTOR; + + GetCursorPos(&magCenterPt); + + // actual magnification is done in the timer function of the host window + + return 1; +} + ///////////////////////////////////////////////////////////////////////////// // Windows 2000+ Only static int ActionTransparency(HWND hwnd, short delta) @@ -4787,6 +5003,7 @@ static int DoWheelActions(HWND hwnd, enum action action) case AC_ALTTAB: ActionAltTab(state.prevpt, state.delta, /*laser=0*/0 , state.shift?EnumStackedWindowsProc:EnumAltTabWindows); break; case AC_VOLUME: ActionVolume(state.delta); break; + case AC_MAGNIFY: ret = ActionMagnification(hwnd, state.delta); break; case AC_TRANSPARENCY: ret = ActionTransparency(hwnd, state.delta); break; case AC_LOWER: ActionLower(hwnd, state.delta, state.shift, state.ctrl); break; case AC_MAXIMIZE: ActionMaxRestMin(hwnd, state.delta); break; @@ -6058,6 +6275,8 @@ __declspec(dllexport) void WINAPI Unload() freeallinputSequences(); + CleanupMagnification(); + free(monitors); free(hwnds); free(wnds); diff --git a/hooks.h b/hooks.h index 39314fa7..188acde3 100644 --- a/hooks.h +++ b/hooks.h @@ -138,6 +138,7 @@ ACVALUE(AC_ALTTAB, "AltTab", ZO) \ ACVALUE(AC_VOLUME, "Volume", 00) \ /* ACVALUE(AC_BRIGHTNESS, "Brightness", 00) */ \ + ACVALUE(AC_MAGNIFY, "Magnify", 00) \ ACVALUE(AC_TRANSPARENCY, "Transparency", 00) \ ACVALUE(AC_HSCROLL, "HScroll", 00) \ ACVALUE(AC_ZOOM, "Zoom", MR) \ diff --git a/languages.h b/languages.h index b7025e8c..03e6a33e 100644 --- a/languages.h +++ b/languages.h @@ -198,6 +198,7 @@ struct strings { TCHAR *input_actions_movetnedge; TCHAR *input_actions_minallother; + TCHAR *input_actions_magnify; TCHAR *input_actions_transparency; TCHAR *input_actions_zoom; TCHAR *input_actions_zoom2; @@ -437,6 +438,7 @@ static const char* l10n_inimapping[] = { "InputActionExtendTNEdge", "InputActionMoveTNEdge", "InputActionMinAllOther", + "InputActionMagnify", "InputActionTransparency", "InputActionZoom", "InputActionZoom2", @@ -680,6 +682,7 @@ static const struct strings en_US = { /* extendtnedge */ TEXT("Extend to next edge"), /* movedtnedge */ TEXT("Move to next edge"), /* minallother */ TEXT("Minimize &other windows"), + /* magnify */ TEXT("Magnify"), /* transparency */ TEXT("Transparency"), /* zoom */ TEXT("Zoom window"), /* zoom2 */ TEXT("Zoom window 2"),