Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Apple Silicon MacOS getActiveWindowTitle() large latency #84

Open
Sky-Unmodal opened this issue Feb 26, 2024 · 3 comments
Open

Apple Silicon MacOS getActiveWindowTitle() large latency #84

Sky-Unmodal opened this issue Feb 26, 2024 · 3 comments

Comments

@Sky-Unmodal
Copy link

Following is the code I used in my case:

import pywinctl as pwc
import sys
import time
from pynput import mouse

def on_click(x, y, button, pressed):
     start = time.time()
     if pressed:
         print(f"Mouse button {button} pressed at position ({x}, {y})")
         try:
             print(pwc.getActiveWindow().title)
             print("Latency:",time.time() - start)
         except AttributeError:
             print("No active window")
     else:
         print(f"Mouse button {button} released at position ({x}, {y})")

 with mouse.Listener(on_click=on_click) as listener:
        listener.join()

I ran this on a M1 Macbook pro. The latency is around 400ms to 500ms to just get the active window.

I ran this on another windows machine, the same code gave a latency around 1ms to almost none latency.

May I ask why does the performance vary this much? Or is there something I should do in MacOS for it run smoother?

@Kalmat
Copy link
Owner

Kalmat commented Feb 27, 2024

Hi! As you can read in the README page... (HAHAHAHA! Nobody does, I know, including myself!)

***Important macOS notice ***

macOS doesn't "like" controlling windows from other apps, so there are two separate classes you can use:
- To control your own application's windows: MacOSNSWindow() is based on NSWindow Objects (you have to pass the NSApp() and the NSWindow() objects reference).
- To control other applications' windows: MacOSWindow() is based on Apple Script, so it is non-standard, slower and, in some cases, tricky (uses window name as reference, which may change or be duplicate), but it's working fine in most cases. You will likely need to grant permissions on Settings -> Security&Privacy -> Accessibility. ***Notice some applications will have limited Apple Script support or no support at all, so some or even all methods may fail!***

This is really annoying, but this is all we can do when on macOS, sorry for the bad news. The difference in performance is because, on macOS, you have to open a terminal, load the script interpreter, run the script, return the values... 400-500ms as result.

I hope you can manage to find a workaround. Just let me know!

@Sky-Unmodal
Copy link
Author

Hi Kalmat! Thanks for the timely response. I realized that the getActiveWindowTitle() function for macos is using Applescript and I actually tried running a less complex Applescript to get the app's title in Python. The latency for that is still too high for me, around 250ms.

Later, I found an alternative to get the application name (not the window name but the app's name) using AppKit.

def get_active_application_macos():
    from AppKit import NSWorkspace
    active_app = NSWorkspace.sharedWorkspace().frontmostApplication().localizedName()
    return active_app

With this, I can get the the current application's name within 1ms latency on a Macbook M1 pro.
I am not sure about whether we can control that app using AppKit as my goal is just getting the current active app on macos.

Thanks!

@Kalmat
Copy link
Owner

Kalmat commented Feb 29, 2024

Hi again! Thank you for your help!

I forgot to mention that pywinctl.getActiveWindow().title actually runs two scripts (one to get the active window, and the other to get its title). It can be improved by using pywinctl.getActiveWindowTitle() which only runs one script, but still too slow...

In addition to that, PyWinCtl is intended to get info and control windows, not apps. You can use getApp() method, but it is just the name of the parent app, not an object you can use in any other processes.

As far as I could find, the only way to manipulate other apps' windows in macOS is using AppleScript. The AppKit app object will not let you change any property. Besides, getting the title of the app can be enough for mono-window apps, but not for multi-window apps (e.g. TextEdit will return 'TextEdit' when invoking app.localizedName(), but the active window title will be something like "TextEdit - " + "the-name-of-the-doc-you-are-editing").

In your case, if you just need the name of the active app, you can use the snippet you attached, which is way faster!! Just bear in mind that for non-English languages the localizedName() might be different (e.g. in Spanish localizedName() method for the app "Contacts" will return "Contactos")

Thank you again for your help and interest! Just let me know any thought or improvement you can find!!!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants