taviso / hotcorner

Tiny Hot Corners for Windows 10
GNU General Public License v3.0
380 stars 69 forks source link

Coordinate changes #7

Open cryptoxx opened 7 years ago

cryptoxx commented 7 years ago

What are left-bottom coordinates ? Tried everything and nothing work so far. These are default left-top: static const RECT kHotCorner = { .top = -20, .left = 20, .right = +20, .bottom = +20, };

taviso commented 7 years ago

Probably something like this, if you don't mind hardcoding the resolution:

static const RECT kHotCorner = {
    .top = YourVerticalResolution - 20,
    .left = -20,
    .right = +20,
    .bottom = YourVerticalResolution + 20,
};

If you don't want to hardcode it (perhaps you change resolution sometimes), you can get your resolution dynamically with GetWindowRect(), e.g. remove const from kHotCorner add then something like this to the top of WinMain():

    GetWindowRect(GetDesktopWindow(), &kHotCorner);

    kHotCorner.top = kHotCorner.bottom - 20;
    kHotCorner.bottom += 20;
    kHotCorner.left = -20;
    kHotCorner.right = +20;

Hope that helps!

cryptoxx commented 7 years ago

thanks. Switching corners with hardcoded res. is working well. Eg. bottom-left@1080p:

.top = +1070,
.left = -20,
.right = +20,
.bottom = +1100,

however I'm using notebook with 4K build-in display in work (native resolution) and I'm connecting external FHD monitor at home. I need to test GetWindowRect out because hardcoded resolution is not working in this case - obviously.

EDIT: Unfortunately, i can't make dynamic resolution works when corner is changed from default to any other. This may be an issue for notebook users with external displays if they decide to use other corner.

taviso commented 7 years ago

You want the corner to be moved when you change resolution automatically?

I understand now. Hmm, to do this I think you will need to handle WM_DISPLAYCHANGE, this isn't difficult, try this. First add a WindowProc above WinMain(), e.g.:

LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
        if (uMsg == WM_DISPLAYCHANGE) {
            GetWindowRect(GetDesktopWindow(), &kHotCorner);
            kHotCorner.top = kHotCorner.bottom - 20;
            kHotCorner.bottom += 20;
            kHotCorner.left = -20;
            kHotCorner.right = +20;
            return 0;
        }

        return DefWindowProc(hwnd, uMsg, wParam, lParam);
}

Now you need to create an invisible message-only window to get windows messages, just add this to the top of WinMain()

    HWND Window;

    Window = CreateWindowEx(0, "Message", NULL, 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL);

    SetWindowLong(Window, GWL_WNDPROC, WindowProc);

Now when you change resolution, the corner will move. Does that work?

taviso commented 7 years ago

I should add these to a wiki page in case someone else wants the same thing as you.

Joaoha commented 4 years ago

Hi

How can I change this to be the upper right corner of the screen? LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { if (uMsg == WM_DISPLAYCHANGE) { GetWindowRect(GetDesktopWindow(), &kHotCorner); kHotCorner.top = kHotCorner.bottom - 20; kHotCorner.bottom += 20; kHotCorner.left = -20; kHotCorner.right = +20; return 0; }

    return DefWindowProc(hwnd, uMsg, wParam, lParam);

}

taviso commented 4 years ago

I think you just want something like this:

    GetWindowRect(GetDesktopWindow(), &kHotCorner);
    kHotCorner.bottom   = 20;
    kHotCorner.left     = kHotCorner.right - 20;
Joaoha commented 4 years ago

Doesn't seem to work... cl /nologo /O2 /c hotcorner.c hotcorner.c hotcorner.c(26): error C2099: initializer is not a constant hotcorner.c(152): warning C4047: 'function': 'LONG' differs in levels of indirection from 'LRESULT (__stdcall *)(HWND,UINT,WPARAM,LPARAM)' hotcorner.c(152): warning C4024: 'SetWindowLongA': different types for formal and actual parameter 3 NMAKE : fatal error U1077: '"C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\VC\Tools\MSVC\14.26.28801\bin\HostX86\x86\cl.EXE"' : return code '0x2' Stop.

Thanks

taviso commented 4 years ago

It's just saying you need to remove the const before you change it (at the top).

Joaoha commented 4 years ago

ok, I've changed it but it still activates on top LEFT corner, not Top Right corner.



#include <stdlib.h>
#include <windows.h>

#pragma comment(lib, "USER32")
#pragma comment(linker, "/SUBSYSTEM:WINDOWS")

#define KEYDOWN(k) ((k) & 0x80)

// This is a **very** minimal hotcorner app, written in C. Maybe its not the
// optimal way to do this, but it works for me.
//
// Zero state is stored anywhere, no registry keys or configuration files.
//
// - If you want to configure something, edit the code.
// - If you want to uninstall it, just delete it.
//
// Tavis Ormandy <taviso@cmpxchg8b.com> December, 2016
//
// https://github.com/taviso/hotcorner
//

// If the mouse enters this rectangle, activate the hot corner function.
// There are some hints about changing corners here
//      https://github.com/taviso/hotcorner/issues/7#issuecomment-269367351
static RECT kHotCorner = {
  .top    = -20,
  .left   = -20,
  .right  = +20,
  .bottom = +20,
};

// Input to inject when corner activated (Win+Tab by default).
static const INPUT kCornerInput[] = {
    { INPUT_KEYBOARD, .ki = { VK_LWIN, .dwFlags = 0 }},
    { INPUT_KEYBOARD, .ki = { VK_TAB,  .dwFlags = 0 }},
    { INPUT_KEYBOARD, .ki = { VK_TAB,  .dwFlags = KEYEVENTF_KEYUP }},
    { INPUT_KEYBOARD, .ki = { VK_LWIN, .dwFlags = KEYEVENTF_KEYUP }},
};

// How long cursor has to linger in the kHotCorner RECT to trigger input.
static const DWORD kHotDelay = 100;

// You can exit the application using the hot key CTRL+ALT+C by default, if it
// interferes with some application you're using (e.g. a full screen game).
static const DWORD kHotKeyModifiers = MOD_CONTROL | MOD_ALT;
static const DWORD kHotKey = 'C';

static HANDLE CornerThread = INVALID_HANDLE_VALUE;

// This thread runs when the cursor enters the hot corner, and waits to see if the cursor stays in the corner.
// If the mouse leaves while we're waiting, the thread is just terminated.
static DWORD WINAPI CornerHotFunc(LPVOID lpParameter)
{
    BYTE KeyState[256];
    POINT Point;

    Sleep(kHotDelay);

    // Check if a mouse putton is pressed, maybe a drag operation?
    if (GetKeyState(VK_LBUTTON) < 0 || GetKeyState(VK_RBUTTON) < 0) {
        return 0;
    }

    // Check if any modifier keys are pressed.
    if (GetKeyboardState(KeyState)) {
        if (KEYDOWN(KeyState[VK_SHIFT]) || KEYDOWN(KeyState[VK_CONTROL])
          || KEYDOWN(KeyState[VK_MENU]) || KEYDOWN(KeyState[VK_LWIN])
          || KEYDOWN(KeyState[VK_RWIN])) {
            return 0;
        }
    }

    // Verify the corner is still hot
    if (GetCursorPos(&Point) == FALSE) {
        return 1;
    }

    // Check co-ordinates.
    if (PtInRect(&kHotCorner, Point)) {
        #pragma warning(suppress : 4090)
        if (SendInput(_countof(kCornerInput), kCornerInput, sizeof(INPUT)) != _countof(kCornerInput)) {
            return 1;
        }
    }

    return 0;
}

static LRESULT CALLBACK MouseHookCallback(int nCode, WPARAM wParam, LPARAM lParam)
{
    MSLLHOOKSTRUCT *evt = (MSLLHOOKSTRUCT *) lParam;

    // If the mouse hasn't moved, we're done.
    if (wParam != WM_MOUSEMOVE)
        goto finish;

    // Check if the cursor is hot or cold.
    if (!PtInRect(&kHotCorner, evt->pt)) {

        // The corner is cold, and was cold before.
        if (CornerThread == INVALID_HANDLE_VALUE)
            goto finish;

        // The corner is cold, but was previously hot.
        TerminateThread(CornerThread, 0);

        CloseHandle(CornerThread);

        // Reset state.
        CornerThread = INVALID_HANDLE_VALUE;

        goto finish;
    }

    // The corner is hot, check if it was already hot.
    if (CornerThread != INVALID_HANDLE_VALUE) {
        goto finish;
    }

    // Check if a mouse putton is pressed, maybe a drag operation?
    if (GetKeyState(VK_LBUTTON) < 0 || GetKeyState(VK_RBUTTON) < 0) {
        goto finish;
    }

    // The corner is hot, and was previously cold. Here we start a thread to
    // monitor if the mouse lingers.
    CornerThread = CreateThread(NULL, 0, CornerHotFunc, NULL, 0, NULL);

finish:
    return CallNextHookEx(NULL, nCode, wParam, lParam);
}

LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
  if (uMsg == WM_DISPLAYCHANGE) {
    GetWindowRect(GetDesktopWindow(), &kHotCorner);
    kHotCorner.top = kHotCorner.bottom - 20;
    kHotCorner.bottom = 20;
    kHotCorner.left = kHotCorner.right - 20;
    kHotCorner.right = +20;
    return 0;
  }

  return DefWindowProc(hwnd, uMsg, wParam, lParam);
}

int CALLBACK WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
  HWND Window;
  Window = CreateWindowEx(0, "Message", NULL, 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL);
  SetWindowLong(Window, GWL_WNDPROC, WindowProc);

    MSG Msg;
    HHOOK MouseHook;

    if (!(MouseHook = SetWindowsHookEx(WH_MOUSE_LL, MouseHookCallback, NULL, 0)))
        return 1;

    RegisterHotKey(NULL, 1, kHotKeyModifiers, kHotKey);

    while (GetMessage(&Msg, NULL, 0, 0)) {
        if (Msg.message == WM_HOTKEY) {
            break;
        }
        DispatchMessage(&Msg);
    }

    UnhookWindowsHookEx(MouseHook);

    return Msg.wParam;
}```
taviso commented 4 years ago

You didn't call the code to change the corner unless there's WM_DISPLAYCHANGE event, just copy that code into the top of WinMain() and it will work.

i.e., copy these lines:

GetWindowRect(GetDesktopWindow(), &kHotCorner);
kHotCorner.top = kHotCorner.bottom - 20;
kHotCorner.bottom = 20;
kHotCorner.left = kHotCorner.right - 20;
kHotCorner.right = +20;

And put them after this line:

HHOOK MouseHook;

(Note: please put three backticks (```) around code on github or it's really hard to read!)

Like this: ``` Code goes here ```