StrokesPlus.net
Welcome Guest! To enable all features please Login or Register.

Notification

Icon
Error

Options
Go to last post Go to first unread
thxnder  
#1 Posted : Thursday, September 2, 2021 10:48:00 AM(UTC)
thxnder

Rank: Newbie

Reputation:

Groups: Approved
Joined: 6/24/2021(UTC)
Posts: 4
China

Thanks: 1 times
Hello. I tried the sample code in the Global Actions -> Mouse Events -> Mouse Wheel tab. The code is awesome, but I ran into 2 problems. Here are the details:

==================================

Case 1:

This function seems to be an approach to prevent the mouse wheel event being consumed when the Consume checkbox is checked:

Code:
wheel.Control.PostMessage(host.cast(uint, 0x020A), new IntPtr(wheel.WParam), new IntPtr(wheel.LParam)); //pass mouse wheel message onto the original control


The problem is, some places cannot receive the mouse wheel event from this function, e.g., the WinX system volume control panel (as the following image shows), and the system start menu.

UserPostedImage

How can I pass the mouse wheel event to these places properly?

==================================

Case 2:

The sample code gets the captain of the activate window in this way:

Code:
wheel.Window.Process.MainModule.ModuleName


It works but sometimes failed, like when the mouse is on a combo list:

UserPostedImage

How can I determine whether the ModuleName can be accessed or not?

==================================

Could any one help me with these 2 question please? Thank you.
Rob  
#2 Posted : Thursday, September 2, 2021 1:40:36 PM(UTC)
Rob

Rank: Administration

Reputation:

Groups: Translators, Members, Administrators
Joined: 1/11/2018(UTC)
Posts: 1,036
United States
Location: Tampa, FL

Thanks: 22 times
Was thanked: 308 time(s) in 269 post(s)
I think that example code is from a long time ago, you should use this instead:
Code:
sp.MouseWheel(wheel.Point, false, wheel.Delta);

I don't know what that error says, but you can try getting the RootWindow first - otherwise, let me know what that error message translates to.
Code:
sp.ConsoleLog(wheel.Window.RootWindow.Process.MainModule.ModuleName);

But some applications may have an odd way they're utilizing the combo which could have it be something like Desktop as the owner, and you're getting access denied.
If your scripts are working fine, but you just don't want to see an error in these situations, you can wrap the code in a try/catch to ignore the error:
Code:
try {
    sp.ConsoleLog(wheel.Window.RootWindow.Process.MainModule.ModuleName);
} catch {}
thanks 1 user thanked Rob for this useful post.
thxnder on 9/3/2021(UTC)
thxnder  
#3 Posted : Friday, September 3, 2021 3:18:37 AM(UTC)
thxnder

Rank: Newbie

Reputation:

Groups: Approved
Joined: 6/24/2021(UTC)
Posts: 4
China

Thanks: 1 times
Hi, Rob. It's very kind of you to reply this topic. I really appreciate it.

1) The sp.MouseWheel( ) function works great! Thank you for telling me.

2) And I'm sorry for lack of detailed error message when I was asking the 2nd question. Here is the complement, if it counts:

I tried triggering the following 4 scripts seperately (by scrolling mouse wheel on the combo list in Windows Explorer) and got the results from Console (see the code comments).

Code:
sp.ConsoleLog(wheel.Window.Process);  // no error, user log: System.Diagnostics.Process (csrss)

sp.ConsoleLog(wheel.Window.RootWindow.Process);  // no error, user log: System.Diagnostics.Process (Idle)

sp.ConsoleLog(wheel.Window.Process.MainModule);  // ERROR, system log: [ScriptEngine.Execute() - ScriptEngineException] Error: Access Denied.

sp.ConsoleLog(wheel.Window.RootWindow.Process.MainModule);   // ERROR, system log: [ScriptEngine.Execute() - ScriptEngineException] Error: Unable to enumerate the process modules.


3) I believe the reason for the above errors is what you've said "some applications may have an odd way they're utilizing the combo which could have it be something like Desktop as the owner", though I don't understand the inner mechanism. And inspired by you, I have found another approach to detect the case rather than using try/catch (though I'm not sure if it is more efficient than using try/catch):

Code:
// Detect if the target process is what we cannot access:
sp.ConsoleLog(wheel.Window.RootWindow.Process.Id === System.Diagnostics.Process.GetProcessesByName("Idle")[0].Id);
// The"idle" process's id seems to always be zero, so the right side of the equal may be replaced by 0:
sp.ConsoleLog(wheel.Window.RootWindow.Process.Id === 0);
// This one also works:
sp.ConsoleLog(wheel.Window.Process.Id === System.Diagnostics.Process.GetProcessesByName("csrss")[0].Id); 


4) Now I have a new question just out of curiosity :) Since we already have an simple approach (sp.MouseWheel( )) to prevent mouse wheel events consumed when the Consume checkbox is checked, I wonder if we have an cooresponding simple approach to consume mouse wheel events when the Consume checkbox is left UNCHECKED? (I use the word "simple" with no intention of using Mouse Event Subscription because that is complicated to me)

Edited by user Friday, September 3, 2021 3:22:50 AM(UTC)  | Reason: Not specified

Rob  
#4 Posted : Friday, September 3, 2021 12:25:58 PM(UTC)
Rob

Rank: Administration

Reputation:

Groups: Translators, Members, Administrators
Joined: 1/11/2018(UTC)
Posts: 1,036
United States
Location: Tampa, FL

Thanks: 22 times
Was thanked: 308 time(s) in 269 post(s)
Quote:
4) I wonder if we have an corresponding simple approach to consume mouse wheel events when the Consume checkbox is left UNCHECKED?

Unfortunately not.

The script is executed asynchronously regardless of whether consume is checked or unchecked, so the script itself cannot control whether the event should be consumed, since S+ has already told Windows to either allow or consume the event based on whether consume is checked or not.

When consume is checked, S+ immediately tells Windows to prevent the event and returns from the mouse hook proc - when consume is unchecked, S+ immediately tells Windows to pass the event along as normal. The script is simply being notified that the event occurred after it has happened.

When an app like S+ is hooking the mouse like this, it needs to be very quick to tell Windows to allow or prevent the mouse event. If the app takes too long or has errors, Windows may evict the hook without notice (so S+ is no longer hooking the mouse, but doesn't know; nothing works) or simple things like a script error could cause the system to be become unstable.

That's the reason for the event hook subscription functionality, as it's for advanced users who understand this and accept the risks. For normal users, I want to ensure that computer operation always works properly.

There are plenty of ways you should be able to have consume checked and allow normal operation, however for mouse wheel events, this can have a negative effect of being slower than normal, since each wheel scroll has to execute a script.
I recommend using exclusion zones to alleviate this issue. For example, I have the wheel script set to consume and have the script perform different actions based on the mouse being along the edges of the screen. So I have an exclusion zone defined which ignores the main screen area for mouse wheel events. Meaning, when I'm just scrolling a page in the main area of the screen, the script is never executed - it only executes when I'm along the edge (top/bottom/left/right) where my script does certain things for different apps.

Perhaps if you can give me some additional details regarding how you intend to use the wheel script, I can assist you with finding the right balance.
thxnder  
#5 Posted : Friday, September 3, 2021 5:25:09 PM(UTC)
thxnder

Rank: Newbie

Reputation:

Groups: Approved
Joined: 6/24/2021(UTC)
Posts: 4
China

Thanks: 1 times
Hi Rob, thank you so much for your explanations, which shed the light for me. Now I have a better understanding of how this amazing software works. Here is what I learned from your introduction:
=====================================
From OS's perspective: when it receives a mouse event from device, it notifies S+ and will not wait for its respond for too long.
From S+'s perspective (using by a normal user): when it is notified by OS with the mouse event, it has to quickly replies "let it go" or "block it.". The answer is given immediately based on the following 3 conditions:
1) the mouse is not in any exclusion zone;
2) the Enable checkbox in Mouse Event tab is checked;
3) the Consume checkbox in Mouse Event tab is checked.
Only if all three conditions are true, the answer is "block it", otherwise the answer is "let it go".
Then, after S+ sends the answer to OS, if condition 1) and 2) are both true, S+ will execute the script, which may have a negative effect on performance.
=====================================
So, since all I want is to just trigger the script when the mouse is scrolling at the edge of screen, I can set an exclusion zone covering most screen area except the edge just as you suggested. By this means, the script will not be executed too often and the perfomance is saved. Genius idea! Thank you for informing me, Rob.
Here is my script (with the Global Actions -> Enable checkbox and Consume checkbox checked and Options -> Exclusion Zones set), for those who may need:

Code:
// Case1: Is the mouse on the right edge of screen?
if( wheel.X >= (parseInt(wheel.Window.Rectangle.Right) - 20) ) {
    sp.SendVKey( wheel.Delta>0 ? vk.VOLUME_UP : vk.VOLUME_DOWN ); // tune volume
}
// Case2: Is the mouse on the left edge of screen?
else if ( wheel.X <= (parseInt(wheel.Window.Rectangle.Left) + 20) ) {
    /** In this case, I would like to change the brightness of screen.
         The approach I use here doesn't require the two brightness setting DLLs in this forum because my device doesn't support either or them.
         Instead, it relies on a software called gama panel.
         To use this script, one have to create 7 profiles corresponding to 7 brightness levels in the gama panel software, and assign hotkeys (CTRL+SHIFT+ALT+F1~F7) for them.
    **/
    if (System.Diagnostics.Process.GetProcessesByName('gapa').Length === 0) { // Execute the gamma panel if it is not running
        sp.Run('cmd /c "cd /d D:\\gamma-panel\\ && start gapa.exe"'); // Assume that the path of the software is D:\gamma-panel\gapa.exe
        sp.Sleep(100);
    }
    if ( typeof(BrightnessLevel)==="undefined" ) { var BrightnessLevel = 7 }; // Create a global variable to log brightness level
    BrightnessLevel = BrightnessLevel + ( wheel.Delta>0 ? 1 : -1 );
    if ( BrightnessLevel < 1 )  { BrightnessLevel = 1 };
    if ( BrightnessLevel > 7 )  { BrightnessLevel = 7 }; 
    sp.SendKeys("^+%{F" + BrightnessLevel + "}"); // Switch to corresponding profile via hotkey

    let info = new DisplayTextInfo();
        info.Title = "Brightness tuning";
        info.Message = "Current Brightness Level is: " + BrightnessLevel;
        info.TitleAlignment = 'Center';
        info.MessageAlignment = 'Left';
        info.Duration = 500;
        info.Opacity = 0.7;
        info.Location = 'bottomright';
        info.TitleFont = new Font('Segoe UI', 12, host.flags(FontStyle.Bold));
        info.MessageFont = new Font('Segoe UI Semibold', 12);
        info.BackColor = 'black';
        info.Padding = 15;
        info.FadeSteps = 18;
        info.UsePrimaryScreen = false;
    sp.DisplayTextClose();
    sp.DisplayText(info);
}
// Case3: Is the mouse on the top area of screen?
else {
    let proc = wheel.Window.RootWindow.Process;
    let appName = (proc.Id===0 ? "" : proc.MainModule.ModuleName); // Access the application's name, unless the process id equals 0 (the zero means System.Diagnostics.Process.GetProcessesByName("Idle")[0].Id)
    if(  ["firefox.exe", "msedge.exe", "chrome.exe"].includes(appName)  &&  parseInt(wheel.Y) <= (parseInt(wheel.Window.Rectangle.Top)+64)   ) {  // Is the mouse in the top 64 pixel area of the browser window?
        sp.SendKeys( wheel.Delta>0 ? "^+{TAB}" : "^{TAB}" ); // Navigate among tabs
    } else {
        sp.MouseWheel(wheel.Point, false, wheel.Delta); // Send a mouse wheel event since the original one has been consumed
    }
};


The script works on my computer, except the code in case 2 seems too long, so I feel like wrapping them into a global function in the Load/Unload tab in next step.
Users browsing this topic
Forum Jump  
You cannot post new topics in this forum.
You cannot reply to topics in this forum.
You cannot delete your posts in this forum.
You cannot edit your posts in this forum.
You cannot create polls in this forum.
You cannot vote in polls in this forum.