1

Тема: AHK: удерживание курсора мыши в определенном окне

Ортогональное перемещение мыши с помощью мышиного хука осуществили.
http://forum.script-coding.com/viewtopic.php?id=4570

Реально ли сделать так, что бы курсор не выходил за рамки определенного окна, например блокнота.
Т.е. достигнув, например, правой границы экрана, курсор "упирался" бы в край окна блокнота, при этом мог бы двигаться только вдоль границы.

2

Re: AHK: удерживание курсора мыши в определенном окне

Так такой же точно принцип.

   SetBatchLines, -1
   SetMouseDelay, -1
   CoordMode, Mouse
   OnExit, Exit

   hHook := DllCall("SetWindowsHookEx"
      , Int, WH_MOUSE_LL := 14
      , Int, RegisterCallback("LowLevelMouseProc", "Fast")
      , UInt, DllCall("GetModuleHandle", UInt, 0)
      , UInt, 0)
      
   Gui, -Caption +Border +LastFound
   Gui, Show, w500 h500
   WinGet, ID
   Return
   
GuiEscape:
GuiClose:
   ExitApp

Exit:
   DllCall("UnhookWindowsHookEx", UInt, hHook)
   ExitApp

LowLevelMouseProc(nCode, wParam, lParam)
{
   global ID
   WinGetPos, XW, YW, WW, HW, ahk_id %ID%
   
   if (nCode < 0 || wParam != 0x200)   ; WM_MOUSEMOVE = 0x200
      Return DllCall("CallNextHookEx", UInt, 0, Int, nCode, UInt, wParam, UInt, lParam)
      
   x := NumGet(lParam+0)
   y := NumGet(lParam+0, 4)
   
   x := x < XW ? XW : x > XW + WW ? XW + WW : x
   y := y < YW ? YW : y > YW + HW ? YW + HW : y
   
   MouseMove, x, y, 0
   Return 1
}
Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

3

Re: AHK: удерживание курсора мыши в определенном окне

Собственно по аналогии и пытался сделать, но не вышло.
Спасибо, teadrinker, как всегда выручаете

4

Re: AHK: удерживание курсора мыши в определенном окне

С наступающим всех!

Ещё один альтернативный вариант (без хука).

Esc::
ExitApp

F1::
hwnd:=DllCall("GetForegroundWindow")
; WS_OVERLAPPEDWINDOW - 0xCF0000 | WS_CAPTION - 0xC00000 | WS_SIZEBOX - 0x40000
WinSet, Style, -0xCF0000,ahk_id %hwnd%
VarSetCapacity(RECT,16,0)
DllCall("GetWindowRect","uint",hwnd,"uint",&RECT)
DllCall("ClipCursor","uint",&RECT)
return

F2::
DllCall("ClipCursor")
WinSet, Style, +0xCF0000,ahk_id %hwnd%
return

;~ ;первая попытка
;~ F1::
;~ lower_caption:=2
;~ hwnd:=WinActive("A")
;~ WinGetPos, xw,yw,ww,hw, ahk_id %hwnd%
;следующие две строки взяты на оф. форуме
;[url=http://www.autohotkey.com/forum/search.php?search_keywords=ClipCursor]search[/url]
;~ VarSetCapacity(R,16,0),  NumPut(xw,&R+0),NumPut(yw+lower_caption,&R+4),NumPut(xw+ww,&R+8),NumPut(yw+hw,&R+12)
;~ DllCall( "ClipCursor", UInt,&R )
;~ Return

;~ F2::
;~ DllCall( "ClipCursor" )
;~ return

;~ VarSetCapacity(lpPoint,8)
;~ if DllCall("GetCursorPos","uint",&lpPoint)
;~ xs:=NumGet(lpPoint,0),ys:=NumGet(lpPoint,4)
;~ DllCall("SetCursorPos","int",xs,"int",ys)

5

Re: AHK: удерживание курсора мыши в определенном окне

Вариант teadrinker рабочий, но почему-то не всегда срабатывает, например, когда курсор подводишь резко к краю экрана.
Вариант kirtech работает стабильнее.
Мне вообще это надо для окна игры, которая запускается в оконном режиме, что бы когда подводишь курсор к краю экрана, курсор оставался внутри окна.
В связи с этим такие вопросы:

1. Можно ли сделать так, что бы окно оставалось в первоначальном виде, т.е. без применения стиля WS_OVERLAPPEDWINDOW. Но при этом курсор удерживался бы именно внутри области окна, не заезжая на границы окна, которые позволяют его расягивать.

2. Если я hwnd получаю так hwnd:=WinExist("Нужное окно")
То после альт-таба, скрипт "сбивается", т.е. курсор уже не хочет удерживаться внутри окна. Если альт-таб не делать, то всё ок. Можно ли как-то решить это без применения таймеров и циклов?

6

Re: AHK: удерживание курсора мыши в определенном окне

InFlames пишет:

Вариант teadrinker рабочий, но почему-то не всегда срабатывает, например, когда курсор подводишь резко к краю экрана.

Ничего не понял. Как можно подвести курсор к краю экрана, если он находится в пределах окна? Может, у тебя код не совсем верный?
Вариант, предложенный коллегой kirtech, это не удержание курсора в пределах окна, это удержание курсора в пределах четырёхугольника на экране, соответствующего выбранному окну, т. е. если окно будет сдвинуто, курсор всё равно останется в пределах первоначального четырёхугольника.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

7

Re: AHK: удерживание курсора мыши в определенном окне

Я имел ввиду к краю окна.
Курсор находится, например, в правой части окна. Если я резко подведу курсор к левой части окна (дотронусь до края окна), то курсор, казалось бы, должен остаться у левого края (т.к. скрипт не дает выходить курсору за пределы окна). Но он возвращается к правому краю.
Я не знаю почему так происходит, но во втором варианте скрипта этого глюка нет.

8

Re: AHK: удерживание курсора мыши в определенном окне

У меня так не получается. Может, проблема в другой части кода? Если остались вопросы, приведи свой код.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

9

Re: AHK: удерживание курсора мыши в определенном окне

Использовал не модифицированный код из поста #2.

10

Re: AHK: удерживание курсора мыши в определенном окне

У меня этот код нормально работает на ХР и на W7. Курсор останавливается у края. Правда, на АНК64 не фурычит, но там нужно мудрить с размерами аргументов в DllCall'ах.

11

Re: AHK: удерживание курсора мыши в определенном окне

Обманул на счет не модифицированного кода, извиняюсь.
В GUI всё работает нормально. А вот в других окнах, если курсор подводить резко к левой части окна, то он как бы от нее отскакивает.

  #Persistent
  SetBatchLines, -1
   SetMouseDelay, -1
   CoordMode, Mouse
   OnExit, Exit

   hHook := DllCall("SetWindowsHookEx"
      , Int, WH_MOUSE_LL := 14
      , Int, RegisterCallback("LowLevelMouseProc", "Fast")
      , UInt, DllCall("GetModuleHandle", UInt, 0)
      , UInt, 0)
      
   WinGet, ID, ID, ahk_class Notepad++
   WinSet, Style, -0xCF0000,ahk_id %ID%

   Return
   
Esc::
   ExitApp

Exit:
WinSet, Style, +0xCF0000,ahk_id %ID%
   DllCall("UnhookWindowsHookEx", UInt, hHook)
   ExitApp

LowLevelMouseProc(nCode, wParam, lParam)
{
   global ID
   WinGetPos, XW, YW, WW, HW, ahk_id %ID%
   
   if (nCode < 0 || wParam != 0x200)   ; WM_MOUSEMOVE = 0x200
      Return DllCall("CallNextHookEx", UInt, 0, Int, nCode, UInt, wParam, UInt, lParam)
      
   x := NumGet(lParam+0)
   y := NumGet(lParam+0, 4)
   
   x := x < XW ? XW : x > XW + WW ? XW + WW : x
   y := y < YW ? YW : y > YW + HW ? YW + HW : y
   
   MouseMove, x, y, 0
   Return 1
}

12

Re: AHK: удерживание курсора мыши в определенном окне

Да, например, в Блокноте отскакивает.

13

Re: AHK: удерживание курсора мыши в определенном окне

У меня не отскакивает, ей-богу! Даже не понимаю, как такое может получаться, исходя из логики получения координат:

   x := x < XW ? XW : x > XW + WW ? XW + WW : x
   y := y < YW ? YW : y > YW + HW ? YW + HW : y
Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

14

Re: AHK: удерживание курсора мыши в определенном окне

Попозже попробую переписать, используя второй алгоритм.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

15

Re: AHK: удерживание курсора мыши в определенном окне

Отскакивает, похоже, просто при очень быстром движении, даже не доходя до края окна, от пустого места.

В настройках мыши (в ХР) есть галочка "Включить повышенную точность установки указателя". Вот когда я её убрал, отскоки прекратились. И что интересно — когда поставил обратно, они не вернулись. Даже перезагрузился для верности — не отскакивает. Больше вроде не менял ничего, так что странно.

16

Re: AHK: удерживание курсора мыши в определенном окне

Windows 7. Эта галка у меня не стояла. Включение/выключение этой опции никакого эффекта не дало.
Отскакивает только от левого и верхнего края окна.

17

Re: AHK: удерживание курсора мыши в определенном окне

InFlames пишет:

при этом курсор удерживался бы именно внутри области окна, не заезжая на границы окна, которые позволяют его расягивать.

По второму алгоритму для блокнота:

   OnExit, Exit
   
   Run, notepad.exe,,, PID
   WinWait, ahk_pid %PID%
   WinGet, hTargetWin, ID, ahk_pid %PID%
   
   VarSetCapacity(WI, 56)
   DllCall("GetWindowInfo", UInt, hTargetWin, UInt, &WI)
   WBorder := NumGet(WI, 48)
   HBorder := NumGet(WI, 52)
   Border := WBorder > HBorder ? WBorder : HBorder
   
   VarSetCapacity(RECT, 16)
   SetTimer, WGP, 10
   Return

WGP:
   if !WinExist("ahk_id " hTargetWin)
      ExitApp
      
   WinGet, AID, ID, A
   if (AID != hTargetWin)
   {
      if !NotActiveTarget
         DllCall("ClipCursor", UInt, 0), NotActiveTarget := 1, XPrev := ""
      Return
   }
   
   NotActiveTarget =
   WinGetPos, X, Y, W, H, ahk_id %hTargetWin%
   NumPut(X + Border, RECT), NumPut(Y + Border, RECT, 4)
   NumPut(X + W - Border, RECT, 8), NumPut(Y + H - Border, RECT, 12)
   DllCall("ClipCursor", UInt, &RECT)
   
   XPrev := X, YPrev := Y, WPrev := W, HPrev := H
   Return
   
Esc:: ExitApp

Exit:
   DllCall("ClipCursor", UInt, 0)
   ExitApp

Окно можно двигать за строку заголовка. Можно переключать активное окно с помощью Alt + Tab. При этом курсор высвобождается, при обратном переключении снова клипируется. При закрытии целевого окна скрипт завершается, высвобождая курсор.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

18

Re: AHK: удерживание курсора мыши в определенном окне

Понемногу начинаю разбираться в этих функциях WinAPI, но всё понять не могу.

В строке

NumPut(X + Border, RECT), NumPut(Y + Border, RECT, 4)

К верхней границе прибавил 22 пикселя, что бы курсор перемещался только в рамках рабочей области, а границы не затрагивал.

NumPut(X + Border, RECT), NumPut(Y + Border + 22, RECT, 4)

Пусть теперь невозможно перетащить окно, но мне оно и не надо, главное, что бы курсор находился в рабочей области окна.

Еще вопрос, можно ли сделать так:
1. Запустили скрипт (с замененной строкой +22) - курсор находится в рабочей области экрана.
2. Сделать альт-таб, что бы окно стало неактивным.
3. Перетащить окно за заголовок (обычный способ перетаскивания окон), не активируя окна
4. Активировать окно, нажав ЛКМ по рабочей области окна  (не по границам или заголовкам)
5. Опять альт-таб
6. Пока окно неактивно иметь возможность нажать на крестик, что бы закрыть окно.

Проще говоря, что бы при неактивном окне, кликая по заголовку или хватаясь за границы окна, что бы его растянуть, окно не активировалось. А активировалось в том случае, когда нажимаю ЛКМ в рабочую область.

Если такие запросы невыполнимы или чрезмерно надоедливы так и напишите

19

Re: AHK: удерживание курсора мыши в определенном окне

Это всё вряд ли.
Проще по горячей клавише останавливать таймер, высвобождать курсор, затем снова запускать. Тогда для слежения за существованием окна нужен отдельный таймер. Клавиша F11. Кстати, из предыдущего варианта две важных строчки выпали:

    if (X = XPrev && Y = YPrev && W = WPrev && H = HPrev)
        Return
   OnExit, Exit
   
   Run, notepad.exe,,, PID
   WinWait, ahk_pid %PID%
   WinGet, hTargetWin, ID, ahk_pid %PID%
   
   NumPut(WI, VarSetCapacity(WI, 56))
   DllCall("GetWindowInfo", UInt, hTargetWin, UInt, &WI)
   WBorder := NumGet(WI, 48)
   HBorder := NumGet(WI, 52)
   Border := WBorder > HBorder ? WBorder : HBorder
   
   VarSetCapacity(RECT, 16)
   SetTimer, WGP
   SetTimer, WinExist
   Return
   
WinExist:
   if !WinExist("ahk_id " hTargetWin)
      ExitApp
   Return

WGP:
   WinGet, AID, ID, A
   if (AID != hTargetWin)
   {
      if !NotActiveTarget
         DllCall("ClipCursor", UInt, 0), NotActiveTarget := 1, XPrev := ""
      Return
   }
   
   NotActiveTarget =
   WinGetPos, X, Y, W, H, ahk_id %hTargetWin%
   if (X = XPrev && Y = YPrev && W = WPrev && H = HPrev)
      Return
      
   NumPut(X + Border, RECT), NumPut(Y + Border + 22, RECT, 4)
   NumPut(X + W - Border, RECT, 8), NumPut(Y + H - Border, RECT, 12)
   DllCall("ClipCursor", UInt, &RECT)
   
   XPrev := X, YPrev := Y, WPrev := W, HPrev := H
   Return
   
F11::
   if (switch := !switch)
   {
      SetTimer, WGP, Off
      DllCall("ClipCursor", UInt, 0)
   }
   Else
      SetTimer, WGP, On
   Return
   
Esc:: ExitApp

Exit:
   DllCall("ClipCursor", UInt, 0)
   ExitApp
InFlames пишет:

К верхней границе прибавил 22 пикселя, что бы курсор перемещался только в рамках рабочей области, а границы не затрагивал.

Может, нужна клиентская область окна — всё кроме строки заголовка, строки меню и окантовки? Если да, то можно точно определить её границы, без всяких прибавлений с помощью функции GetWindowInfo.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

20

Re: AHK: удерживание курсора мыши в определенном окне

teadrinker
По моим подсчётам размер WINDOWINFO — 60 байт.
F11 отключает захват, но повторное нажатие — не включает, если положение окна за это время не изменилось.

21

Re: AHK: удерживание курсора мыши в определенном окне

teadrinker, да, нужно всё, кроме заголовка и окантовки. Строки меню в игре нет.
Впринципе вариант из поста #17 устраивает.
Вопрос в одном у всех ли окантовка окон с трех сторон по 8, сверху 30?
Или лучше определять это через WINDOWINFO ?
И подскажите от куда вы берете числа 48 и 52 для W и H границ?
Тут описана структура Windowinfo, но там 10 параметров.
На каждый параметр отводится свое кол-во байт? Как это узнать?

22 (изменено: YMP, 2011-01-06 14:29:19)

Re: AHK: удерживание курсора мыши в определенном окне

InFlames пишет:

И подскажите от куда вы берете числа 48 и 52 для W и H границ?
Тут описана структура Windowinfo, но там 10 параметров.
На каждый параметр отводится свое кол-во байт? Как это узнать?

Думаю, из интернета не проблема это узнать. DWORD = (U)INT = (U)LONG = 4 байта. WORD = ATOM = 2 байта. RECT — структура из 4 LONG, т.е. 16 байт.

Хэндлы и указатели имеют разную величину: для 32-битных программ они по 4 байта, для 64-битных по 8. Т.е. тут имеет значение разрядность версии AutoHotkey, которую Вы используете, а не операционной системы.

rcClient по идее как раз и содержит координаты клиентской области окна, так что заморачиваться с толщиной границ вроде незачем.

Вот немного упрощённый код для клиентской области.

   OnExit, Exit
   
   Run, notepad.exe,,, PID
   WinWait, ahk_pid %PID%
   WinGet, hTargetWin, ID, ahk_pid %PID%
   
   NumPut(WI, VarSetCapacity(WI, 60))
   
   SetTimer, WGP, 10
   SetTimer, WinExist
   Return
   
WinExist:
   if !WinExist("ahk_id " hTargetWin)
      ExitApp
   Return

WGP:
   IfWinNotActive, ahk_id %hTargetWin%
   {
      If !NotActiveTarget
        DllCall("ClipCursor", UInt, 0), NotActiveTarget := 1
      Return
   }
   NotActiveTarget =
   DllCall("GetWindowInfo", UInt, hTargetWin, UInt, &WI)
   DllCall("ClipCursor", UInt, &WI + 20)   
   Return
   
F11::
   if (switch := !switch)
   {
      SetTimer, WGP, Off
      DllCall("ClipCursor", UInt, 0)
   }
   Else
      SetTimer, WGP, On
   Return
   
Esc:: ExitApp

Exit:
   DllCall("ClipCursor", UInt, 0)
   ExitApp

23

Re: AHK: удерживание курсора мыши в определенном окне

YMP пишет:

Вот немного упрощённый код для клиентской области.

В таком варианте функция

DllCall("ClipCursor", UInt, &WI + 20)

бесполезно выполняется много раз подряд. На самом деле всё ещё проще, если не предполагается менять положение и размеры окна во время клипирования курсора:

   OnExit, Exit

   Run, notepad.exe,,, PID
   WinWait, ahk_pid %PID%
   WinGet, hTargetWin, ID, ahk_pid %PID%

   NumPut(WI, VarSetCapacity(WI, 60))
   SetTimer, WinExist
   Gosub, WGP
   Return

WinExist:
   if !WinExist("ahk_id " hTargetWin)
      ExitApp
      
   WinGet, AID, ID, A
   if (AID != hTargetWin)
   {
      if !NotActiveTarget
         DllCall("ClipCursor", UInt, 0), NotActiveTarget := 1
      Return
   }
   Else if (!switch && NotActiveTarget)
      Gosub, WGP
   Return

WGP:
   NotActiveTarget =
   DllCall("GetWindowInfo", UInt, hTargetWin, UInt, &WI)
   DllCall("ClipCursor", UInt, &WI + 20)
   Return

F11::
   if (switch := !switch)
      DllCall("ClipCursor", UInt, 0)
   Else
      Gosub, WGP
   Return

Esc:: ExitApp

Exit:
   DllCall("ClipCursor", UInt, 0)
   ExitApp
Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

24

Re: AHK: удерживание курсора мыши в определенном окне

Как заставить работать код из поста #22 или #23 на Windows7 64 бит?
Достаточно ли будет просто поменять строку

 NumPut(WI, VarSetCapacity(WI, 60))

на

 NumPut(WI, VarSetCapacity(WI, 120))

или что-то еще надо исправлять?

25

Re: AHK: удерживание курсора мыши в определенном окне

Я как раз сейчас на ней, они прекрасно работают. Или имеете в виду АНК_64?

26

Re: AHK: удерживание курсора мыши в определенном окне

Нет. Я имею ввиду basic версию.

27

Re: AHK: удерживание курсора мыши в определенном окне

Примеры прекрасно в ней работают под W7 64.