capturing right-click+left-click with autohotkey; unexpected behaviour - windows

I want to capture the key event "right mouse button pressed, then left mouse button pressed". No problem in autohotkey. However I am having trouble with still allowing the right-mouse key to work alone.
1) this works:
RButton & LButton::
Send X
Return
works as expected:
If I press right mouse button, then left mouse button, "X" is sent to the active window
right-click event is captured by Authotkey: no context menu appears when I press the right mouse button alone. This is the intended outcome
2) this works
~RButton & LButton::
Send Y
Return
works as expected:
If I press right mouse button, then left mouse button, "Y" is sent to the active window
right-click event is not captured by Authotkey: context menu does appear when I press the right mouse button alone or together with the left button. This is the intended outcome
3) Now I want to do different things depending on the active window.
this does not work (careful: this will disable righ-click in every application)
#If WinActive("ahk_class MozillaWindowClass")
RButton & LButton::
Send X
Return
#If !WinActive("ahk_class MozillaWindowClass")
~RButton & LButton::
Send Y
Return
does not work as expected:
in Firefox left-right sends X, in other applications left-right sends Y
however, right-click is disabled in every application
What am I doing wrong here?
edit:
the goal is this: I want a global hotkey on Right+left-click with RButton & LButton . In specific applications that I have tested for compatibility, I want right+left click to suppress sending right-click, and then send right-click manually using autohotkey. However, since some applications might have trouble processing mouseevents sent by autohotkey, in all untested applications I want to use ~RButton & LButton with the ~ to pass throught right-click events

Here's one that supports right click dragging!
Hotkey, LButton, off
#IfWinActive ahk_class MozillaWindowClass
RButton & LButton::
Send X
Return
RButton::return
#IfWinNotActive ahk_class MozillaWindowClass
~$RButton::
Hotkey, LButton, on
while GetKeyState("RButton", "P") {
continue
}
Hotkey, LButton, off
Return
LButton::Send Y
Return
It handles RButton manually. When RButton is pressed, it enables the LButton hotkey and waits for RButton to be released before deactivating it. The RButton hotkey uses ~, which passes the click through normally.
LButton is disabled at the start by the line at the top.
Another way would have been to send {RButton Down} at the start of the hotkey and {RButton Up} at the end.
In response to your edit, the only programs that reject Autohotkey's sent events should be those that rely on low level hooks... The real trouble with the method down at the bottom is it only sends a single click, not processing holding the button. This method, and sending down and up separately, should both do that properly.
The bug with active window described at the bottom of this answer still exists, but that's a problem with the #IfWin[Not]Active.
Old stuff
See the documentation on the ampersand (emphasis mine):
You can define a custom combination of two keys (except joystick buttons) by using " & " between them. In the below example, you would hold down Numpad0 then press the second key to trigger the hotkey:
Numpad0 & Numpad1::MsgBox You pressed Numpad1 while holding down Numpad0.
Numpad0 & Numpad2::Run Notepad
In the above example, Numpad0 becomes a prefix key; but this also causes Numpad0 to lose its original/native function when it is pressed by itself. To avoid this, a script may configure Numpad0 to perform a new action such as one of the following:
Numpad0::WinMaximize A ; Maximize the active/foreground window.
Numpad0::Send {Numpad0} ; Make the release of Numpad0 produce a Numpad0 keystroke. See comment below.
The presence of one of the above hotkeys causes the release of Numpad0 to perform the indicated action, but only if you did not press any other keys while Numpad0 was being held down.
So, following that example:
#If WinActive("ahk_class MozillaWindowClass")
RButton & LButton::
Send X
Return
RButton::return
#If !WinActive("ahk_class MozillaWindowClass")
RButton & LButton::
Send Y
Return
RButton::Send {RButton}
Note RButton requires a variant that does nothing in WinActive, at least with my testing (see below): RButton::return
Since I'm using Autohotkey standard, not Autohotkey_L, I don't have #If and the above was untested. The following I did test, and it works.
#IfWinActive ahk_class MozillaWindowClass
RButton & LButton::
Send X
Return
RButton::return
#IfWinNotActive ahk_class MozillaWindowClass
RButton & LButton::
Send Y
Return
RButton::Send {RButton}
An interesting bug I've noticed is the second (NotActive) variant applies occasionally to Firefox:
Another window is active
RButton down is sent
Firefox is not active, so the second variant is processed
<delay> (RButton is held down, though the delay could be imperceptible, in the order of milliseconds, to infinite)
Firefox becomes active
<delay> (still held down)
RButton up is sent, which sends RButton as per the documentation. Because Firefox became active in the delay between the active window check and when RButton is sent, RButton is sent to Firefox.
This happens when both Firefox and another window are visible, and the other window is the active one at the time of the click.
I've tried to fix this bug by adding an extra IfWinNotActive check within the RButton hotkey, but it did not seem to work.

Related

Delphi Windows - determine close request source

Is it possible to determine the source of a close request in a Windows application (Delphi)?
Background: I have an option to route close requests to minimizing the window to keep the app running "in Background".
procedure TfrmMain.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
begin
... different checking stuff e.g. unsaved changes
// Redirect Close to minimize but allow close if requested from minimized state
if FMinimize and (WindowState <> wsMinimized) then
begin
logger.debug('... closing main form redirected to minimize');
WindowState := wsMinimized;
CanClose := false;
exit;
end;
end;
This works well and allows closing the window by right clicking in the taskbar if already minimized. As icing of the cake I would like to determine if the close request came from right clicking the taskbar icon to close immediately even if the window is not already minimized. Is there a way to determine the source of the close request?
There is no difference between closing a window via the X button in its top-right corner, vs the Close window option on the right-click menu of the window's Taskbar button, vs the ALT-F4 keystroke. They all represent the same close command. If any of those options are invoked, the window will receive a WM_SYSCOMMAND(SC_CLOSE) message, which if passed to DefWindowProc() will generate a WM_CLOSE message that will trigger the Form's OnCloseQuery event. So no, there is no way to differentiate the source of the initial WM_SYSCOMMAND message. Only that the user wishes for the window to close.
That being said, you might try having your Form intercept the WM_NCHITTEST and WM_SYSKEYDOWN messages to detect when the user is clicking on the X button or pressing ALT-F4 on the keyboard. You can use those messages to set flags that your OnCloseQuery event can look at. That is about the only way I can think of right now to differentiate between Taskbar vs non-Taskbar closures.

How to close/minimize/maximize or send keys to window or popup with Autohotkey?

How to use Autohotkey to automatically close, minimize, maximize or send keys to a window as soon as it pops up? I can detect a dialog and close it with this:
WinWaitActive, TITLE
WinClose, TITLE
But this doesn't work if the window isn't open on script execution.
This is a very common task AHK is used for.
First you need the title of the window you want to address. Read How to get the title of a window with AHK?.
The code
For the basic functionalitiy of closing a window we need Loop, WinWaitActive and WinClose.
Example for a Firefox window with Stack Overflow open.
Loop {
WinWaitActive, Stack Overflow - Mozilla Firefox
WinClose,
}
Explanation
The Loop repeats the process to close the window multiple times. WinWaitActive waits until the the window gets activated (pops up) and WinClose closes it.
Hint: If you don't specify a specifiy window title like with WindowClose the last found window, which is the one from WinWaitActive is used.
minimize/maximize
Instead of WinClose use WinMaximize or WinMinimize to perform the corresponding action.
Sending Keys
If you want to send specific keys (e.g. Enter) to the window use Send
Loop {
WinWaitActive, Stack Overflow - Mozilla Firefox
send {Enter}
}
Additions
If the basic version does not work or you want to create an more advanced script, here are some possible modifications.
More Force
If WinClose does not work try WinKill or Send, !{F4} to use more force.
As Admin
Admin rights might be necessary to close the window, use this code snippet on top of your script to make sure it runs with full access.
If not A_IsAdmin ;force the script to run as admin
{
Run *RunAs "%A_ScriptFullPath%"
ExitApp
}
Other matching methods
On default the window title has to be an exact match. To change this behavior and allow partial or start with matches use SetTitleMatchMode on top of your script, e.g. SetTitlematchMode, 2 for partial match.
Instead of title, the window class (ahk_class) or .exe (ahk_exe) from Window Spy can be used.
WinWaitActive, ahk_class MozillaWindowClass
or
WinWaitActive, ahk_exe firefox.exe
Select the one which suits your needs carefully to only react to the correct window.

Mouse drag in embbeded Win32 window

In a Qt dialog I have an embedded native Win32 window. As in any standard Win32, I define my own message queue, where, per default, I forward all events to the parent window, and in case an event of interest arrives, I perform some extra work.
My problem is that when I press the left mouse button, then I get the WM_LBUTTONDOWN as expected, but if I keep it pressed, then I get no more mouse clicks events, that is, I get the WM_MOUSE messages, but the mask (wParam), or calling GetKeyState, do not indicate that the mouse key is kept pressed.
The window is created with following parameters,
dwExStyle = WS_EX_TRANSPARENT;
dwStyle &= ~(WS_BORDER| WS_CAPTION | WS_DLGFRAME | WS_THICKFRAME);
CreateWindowExW(0,"Window","Name",dwStyle,
0,0,512,512,
hwndParent,NULL,hInstance,NULL )
When this native window is not embedded in any dialog, it works correctly.
I could also embed this window in a .NET dialog window and observe the same problem.
Any clue what could be going wrong?
Did you mean WM_MOUSEMOVE? Did you try capturing the mouse first?
WM_LBUTTONDOWN is sent only once per click. You'll have to use a boolean to keep track of whether the button is pressed or released during the WM_MOUSEMOVE events. Make use of the WM_LBUTTONDOWN and WM_LBUTTONUP messages together to keep track of this.

return key generates IDOK for button with focus

I did a SetFocus to a button in a dialog. The button gets the dashed outline. When the user presses the return key, the dialog get a IDOK message rather than a message from the button were I set the focus. The same thing happens under other circumstances.
Why is this happening? And how can I cause the return to act as a button press?
Plain c++ windows app, no MFC, no NET.
Feature, not a bug. The [Enter] key operates the button that's marked as the default button for a dialog. Either with the DEFPUSHBUTTON in the .rc file or the BS_DEFPUSHBUTTON style flag. Which is typically the "OK" button so getting IDOK back is expected. The [Escape] key is special that way too, typically the [Cancel] button. This is bound to ring a bell if you think back on how you used dialogs before.
You click a button that has the focus by pressing the space bar instead.
In another SO question I found KB article that might help you:
If a dialog box or one of its controls currently has the input focus,
then pressing the ENTER key causes Windows to send a WM_COMMAND
message with the idItem (wParam) parameter set to the ID of the
default command button. If the dialog box does not have a default
command button, then the idItem parameter is set to IDOK by default.
When an application receives the WM_COMMAND message with idItem set to
the ID of the default command button, the focus remains with the
control that had the focus before the ENTER key was pressed. Calling
GetFocus() at this point returns the handle of the control that had
the focus before the ENTER key was pressed. The application can check
this control handle and determine whether it belongs to any of the
edit controls in the dialog box. If it does, then the user was
entering data into one of the edit controls and after doing so,
pressed ENTER. At this point, the application can send the
WM_NEXTDLGCTL message to the dialog box to move the focus to the next
control.
According to MSDN,
Dialog Box Keyboard Interface
The system provides a special keyboard interface for dialog boxes that carries out special processing for several keys. The interface generates messages that correspond to certain buttons in the dialog box or changes the input focus from one control to another. Following are the keys used in this interface and their respective actions.
...
ENTER: Sends a WM_COMMAND message to the dialog box procedure. The wParam parameter is set to IDOK or control identifier of the default push button.
Since the system intercepts and processes ENTER key pressed directly through the dialog, you'll need to handle it in your dialog box procedure by calling GetFocus() to first see which control has the focus, and perform the appropriate action for that particular control.

During XGrabKey(board), discover which window had been focused

A program has called XGrabKey() to make a hotkey.
The user presses that key combination (while another window is focused).
The program receives control to do something in response to the key combination. Meanwhile, the program has been temporarily focused (because of the effects of XGrabKey (see man XGrabKey, man XGrabKeyboard)).
I want the program to create a synthetic X event (a keypress or mouse click) to the originally focused window. In some cases this means I need to focus that window before sending it the event (Firefox ignores synthetic events when it is not focused), which means I need to know which window it is. How can I find out which window it is?
Wait for the next FocusOut event, verify that the mode is set to NotifyUngrab, get the focus with XGetInputFocus(), and send away your synthetic events.

Resources