AutoHotkey MouseMove not centering properly - windows

I'm running the below code and I expect the mouse to move to the center of the currently active window when I hit comma.....instead it is moving to different points on the screen, depending on where the window is on the screen. It only centers the mouse properly when the window is positioned at the top left (x=0, y=0).
#NoEnv
SendMode Input
#WinActivateForce
Sysget, Mon2, Monitor, 2
,::
WinGetActiveStats, Title, Width, Height, X, Y
{
MsgBox, The active window "%Title%" is %Width% wide`, %Height% tall`, and positioned at %X%`,%Y%.
;center_x:=X+(Width*.5)
;center_y:=Y+(Height*.5)
MouseMove, X+(Width*.5), Y+(Height*.5), 90
}
Return

Check out CoordMode in the documentation.
Sets coordinate mode for various commands to be relative to either the
active window or the screen.
CoordMode, ToolTip|Pixel|Mouse|Caret|Menu [, Screen|Window|Client]
The default CoordMode is Screen which is why you get two different locations. Set the CoordMode to Window to assure that your mouse centering works on the active window.
You can set it for the entire script by calling it during the Auto-Execute section of the script.

The following script will move mouse to Active Window on dual screen system.
I could not get it to work until I put in the sleep line, WinGetPos was getting info before the window had moved.
~#+right::
~#+left::
Sleep,1000
WinGetPos, X, Y, width, height, A
center_x:=width/2
center_y:=height/2
MouseMove,center_x,center_y,
return

I would be sure that Width and Height are the actual dimensions of the window and not the screen resolution. Then check X and Y to ensure they're the actual top-left corner of the active window.
If the width and height are not from the actual window, i.e. the screen size, then this is expected behaivor. Perhaps you could show us the calling function to get a better idea of where those parameters are coming from.

the problem was that MousMove uses the coordinates of the window by default, so I changed the MouseMove line to the following:
MouseMove, Width*.5, Height*.5
All is good.

Tried it all nothing works. The trick is to use DllCall("SetCursorPos", int, x, int, y).
Here is the code to move mouse to center of window. Works on multi-montior and non-fullscreen windows.
MoveMouseInCenterOfActiveWindow:
CoordMode,Mouse,Screen
WinGetPos, winTopL_x, winTopL_y, width, height, A
;MouseMove, X, Y, 0 ; does not work with multi-monitor (always off)
winCenter_x := winTopL_x + width/2
winCenter_y := winTopL_y + height/2
DllCall("SetCursorPos", int, winCenter_x, int, winCenter_y)
Tooltip winTopL_x:%winTopL_x% winTopL_y:%winTopL_y% winCenter_x:%winCenter_x% winCenter_y:%winCenter_y%
return

Related

How to stick window to left half part of screen?

I am using xlib to set window to certain position using XMoveResizeWindow function
All works fine, but when I want to 'stick' my window to left part of screen, I get the wrong position and size: there is a space between window and left, top and bottom screen borders.
To find out 'right' coords & dimensions of window stuck to left manually, I have written a watcher program which continuously calls XGetGeometry and prints results to screen, so I got these results of a 'stuck' window:
X: -40
Y: 14
W: 2000
H: 2426
When I pass these values to XMoveResizeWindow function, I still get the wrong position.
I have extended my watcher program to print all available properties of the window to see which will be changed when I manually 'stick' the window, but nothing changed except max_width and max_height fields of WM_NORMAL_HINTS property.
If you have an idea what am I doing wrong, please let me know.

Win32 Api WM_MOUSEMOVE moving mouse quick

I have created a small window by win api. This window is a child to the window of another thread.
I want allow user to move my window by moving mouse with presed right button. When I move my mouse in normal speed my window moves without problem. But when I move very quick some very strange messages recieved by window. For example,
P WM_MOUSEMOVE fwKeys:MK_BUTTON xPos:-32703 yPos:9
As you see the xPos drops down to a -32000. It happens almost instantly after I move my mouse quick. I have no idea how windows can send me such a message.
Why that message could be sended and how to fix it?
I am using a SetCaption function, so my window recievs messages even if mouse stays outside.
Upd. Solved. The problem was in my inherent maths. Getting hi and lowword from lParam wasn't proper.
I had the same issue while implementing dragging support, making rapid small circles in the middle of the screen with the mouse while left button is pressed, generates some out of range positions (like -32000 or -64000, despite proper handling of the lParam coordinates conversion). This looked like a bug to me, so I worked around it by clipping x and y to current screen size in pixels for the max values and allowing negative values down to a negative screen size box.
This is a code extract for better comprehension (written in Red/System):
WM_MOUSEMOVE [
lParam: msg/lParam
x: GET_X_LPARAM(lParam)
y: GET_Y_LPARAM(lParam)
if any [
x < (0 - screen-size-x)
y < (0 - screen-size-y)
x > screen-size-x
y > screen-size-y
][
return 0 ;-- ignore this event
]
...
]
That solved it for me.

Autohotkey Mousemove Wrong Monitor

I'm using mousegetpos to get the current mouse position. I click somewhere else. Then I try to restore the original postion with mousemove. The mouse moves to a different monitor. I tried the alternative method dllcall, with no success. How do I move the mouse back to the original monitor?
It's easier to help if you post your code - then people can see where you're going wrong.
This works fine for me when pressing the Ctrl-T hotkey:
CoordMode, Mouse, Screen
^t::
MouseGetPos, x, y
; Do Stuff Here.
MouseMove, x, y
return
The CoordMode, Mouse, Screen line sets the coordinates relative to the entire screen rather than the active window. I've tested this on my multiple monitor setup and the mouse goes back to the original location every time, even across monitors. Let me know if it's not working for you.
Also, just to make things a little smoother, you can set the mouse speed to '0' before moving the mouse with:
SetDefaultMouseSpeed, 0
This makes the mouse appear to move instantly which looks a little cleaner in most scripts.
I can confirm that Gary's answer works perfectly for anybody else out there having similar problems. Thanks, Gary!
I was myself having a problem like this with Breakaway Audio Enhancer...
For anybody that uses or knows Breakaway, you have to double-click on the toolbar (in the taskbar) to mute it. The way Breakaway works with the sound pipeline other standard AHK mute scripts won't work, so moving the mouse to the toolbar and double-clicking is really the only method of muting. I wanted Caps Lock to mute (or unmute) audio and preferably have the mouse return to where it originally was.
I've had countless problems trying to get this to work with multiple monitors until Gary's post, so here is my solution for anybody else having similar issues:
Capslock::
BlockInput On
CoordMode, Mouse, Screen
MouseGetPos, xpos, ypos
MouseClick, left, 42, 965, 2 ;change the co-ordinates to match your system
MouseMove, xpos, ypos
SetDefaultMouseSpeed, 0
BlockInput Off
Return

win32: detect if start menu is auto-hidden?

I want to position a window at the bottom of the screen. If the start menu is present, I want it to lie along the top of the start menu. If it is not (or it is auto-hidden), I still want it to be in the same position as it would be if the start menu was there, meaning there will be a gap of a few pixels.
Currently I get the monitor work area, position the window at the bottom, and always offset by 20 pixels or so. If the start menu isn't there, this works well. If it is, however, the work area also shrinks (as it should), and I end up double-offsetting.
How would I fix the issue?
To get the work area of the screen not obscured by the system taskbar or by application desktop toolbars, you can use SystemParametersInfo() with SPI_GETWORKAREA as uiAction parameter. The pvParam parameter must point to a RECT structure that receives the coordinates of the work area, expressed in virtual screen coordinates. For example:
RECT rectWorkArea;
SystemParametersInfo(SPI_GETWORKAREA, 0, &rectWorkArea, 0);
As you said in the comment, to get the bounding rectangle of the taskbar, we can call SHAppBarMessage(ABM_GETTASKBARPOS, ...)
To determine the position of the taskbar (whether it is currently at the bottom, top, left, or right of the screen), you could use the following calculation:
type
TTaskBarPos = (_TOP, _BOTTOM, _LEFT, _RIGHT);
var
iScrW, iScrH: Longint;
iScrW := GetSystemMetrics(SM_CXSCREEN);
iScrH := GetSystemMetrics(SM_CXSCREEN);
if (rectTaskbar.Top > iScrH div 2) and (rectTaskbar.Right >= iScrW) then
Result := _BOTTOM
else if (rectTaskbar.Top < iScrH div 2) and (rectTaskbar.Bottom <= iScrW div 2) then
Result := _TOP
else if (rectTaskbar.Left < iScrW div 2) and (rectTaskbar.Top <= 0) then
Result := _LEFT
else
Result := _RIGHT;
They should be enough to solve your current problem. However, if you need to know (for another reason) the current taskbar settings of the autohide and always-on-top states, you can use SHAppBarMessage(ABM_GETSTATE, ...).
If you need to be notified that that the taskbar's autohide or always-on-top state has changed, you have to intercept ABN_STATECHANGE message.
Are you are using or have access to .NET in your project?
If so, you can use the Screen.PrimaryScreen.WorkingArea.Height property to determine the bottom of the screen excluding the task bar.
You can also grab the total screen height by getting the Screen.PrimaryScreen.Bounds.Height property (which includes the task bar in the total height value).
Comparing these values, if they're the same, the task bar isn't present. Otherwise, the task bar is and you can adjust accordingly.

How do you get CreateWindowEx() to create the window on a specific monitor?

I've determined that I can use GetSystemMetrics(SM_CMONITORS) to query the number of attached monitors, but is there any way to control what monitor CreateWindowEx() uses for the window?
Yes, by the "x" and "y" arguments. Use EnumDisplayMonitors (pass two nulls) to enumerate the monitors. Your MonitorEnumProc callback gets a RECT* to the monitor's display rectangle. You'd get a negative RECT.right if a monitor is located at the left of your main one.
Each monitor simply displays some part of the desktop, so showing the window on a particular monitor is a matter of moving the window to the part of the desktop displayed by that monitor. When you call CreateWindowEx (or CreateWindow) you can specify x and y coordinates for the window, so displaying it on a particular monitor simply means specifying coordinates that fall within the area displayed by that monitor.
You can find the work areas for the monitors on a system with GetMonitorInfo.
The x and y parameters specify the location of the new window. This point can be anywhere on the virtual screen (all the monitor rectangles combined).
If you want to create the window on the same monitor as another window you can call MonitorFromWindow. Otherwise can enumerate all the monitors with EnumDisplayMonitors.
Either way, once you have a HMONITOR handle you must then call GetMonitorInfo. Your x and y parameters should be a value inside the bounds of the rcWork member in the monitor info struct. You would normally choose values that puts your window in the center of this rectangle.
It is important to use the workarea rectangle and not the full monitor rectangle because you don't want your window to appear underneath the Taskbar and other always-on-top appbars.

Resources