From aac4f88bb54fa39c41f2b33db61bdaa8cbb8cc56 Mon Sep 17 00:00:00 2001 From: Ian Obermiller Date: Sat, 10 Feb 2018 21:42:44 -0800 Subject: [PATCH 1/2] Update MacOS title logic - Switch from the opaque scpt file to a normal JS file using JavaScript for Automation. - Grab the first window (which is the active one) and get its name as the title. This worked across all apps I tried, including Sublime, Atom, Chrome, Safari, and iTerm - Clean up the api to the macos.py file (have it return the dict directly) - Fix a typo in README --- .gitignore | 1 + README.md | 6 ++--- aw_watcher_window/lib.py | 6 +---- aw_watcher_window/macos.py | 35 ++++++++++++++++++--------- aw_watcher_window/printAppTitle.scpt | Bin 3278 -> 0 bytes 5 files changed, 27 insertions(+), 21 deletions(-) delete mode 100644 aw_watcher_window/printAppTitle.scpt diff --git a/.gitignore b/.gitignore index f13f69ce..99a8446f 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ __pycache__ build dist *.swp +.mypy_cache diff --git a/README.md b/README.md index 2a420a49..27e813dc 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,4 @@ -aw-watcher-window -================= +# aw-watcher-window Cross-platform window-Watcher for Linux (X11), macOS, Windows. @@ -7,6 +6,5 @@ Cross-platform window-Watcher for Linux (X11), macOS, Windows. ### Note to macOS users -To log current window title the teminal needs access to macOS accessibility API. +To log current window title the terminal needs access to macOS accessibility API. This can be enabled in System Preferences > Security & Privacy > Accessibility, then add the Terminal to this list. If this is not enabled the watcher can only log current application, and not window title. - diff --git a/aw_watcher_window/lib.py b/aw_watcher_window/lib.py index 010fb802..b0c42736 100644 --- a/aw_watcher_window/lib.py +++ b/aw_watcher_window/lib.py @@ -18,11 +18,7 @@ def get_current_window_linux() -> Optional[dict]: def get_current_window_macos() -> Optional[dict]: from . import macos - info = macos.getInfo() - app = macos.getApp(info) - title = macos.getTitle(info) - - return {"title": title, "appname": app} + return macos.getInfo() def get_current_window_windows() -> Optional[dict]: diff --git a/aw_watcher_window/macos.py b/aw_watcher_window/macos.py index 9cc04d9a..46dda4d2 100644 --- a/aw_watcher_window/macos.py +++ b/aw_watcher_window/macos.py @@ -1,17 +1,28 @@ -import subprocess -from subprocess import PIPE -import os +from typing import Optional +from AppKit import NSWorkspace +from Quartz import ( + CGWindowListCopyWindowInfo, + kCGWindowListOptionOnScreenOnly, + kCGNullWindowID +) +def getInfo() -> Optional[dict]: + app_name = '' + title = '' -def getInfo() -> str: - cmd = ["osascript", os.path.join(os.path.dirname(os.path.realpath(__file__)), "printAppTitle.scpt")] - p = subprocess.run(cmd, stdout=PIPE) - return str(p.stdout, "utf8").strip() + app = NSWorkspace.sharedWorkspace().activeApplication() + if app: + pid = app['NSApplicationProcessIdentifier'] -def getApp(info) -> str: - return info.split('","')[0][1:] + options = kCGWindowListOptionOnScreenOnly + window_list = CGWindowListCopyWindowInfo(options, kCGNullWindowID) + for window in window_list: + if pid == window['kCGWindowOwnerPID']: + # We could use app['NSApplicationName'], but this value is more + # accurate and matches other methods (like applescript) + app_name = window['kCGWindowOwnerName'] + title = window.get('kCGWindowName', u'') + break - -def getTitle(info) -> str: - return info.split('","')[1][:-1] + return {"appname": app_name, "title": title} diff --git a/aw_watcher_window/printAppTitle.scpt b/aw_watcher_window/printAppTitle.scpt deleted file mode 100644 index d004b036791ed371a0841c31edf9af5670c9fb1f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3278 zcmb_e`*##Y7XG?tgwUPpq>~^4SsbEKc7Qx2vXE?m0Kuq0kR&ES9(IO?3`}NnX2!7m z=AW=<|BXHSgCe`W7i2{QAH)Zst5FeDp5g;VRIXojPlV%|WIe~4Q{U9>x>fg4_ui^i zUT$kwOGm7}roK*h2SBt$1O`k*3=W_;Hg@v6Zc!6a&fJ7aieLy8Bu?aKlP&~?#_6Ga{MYgr1OiUr`5AwY&KVU{3>|M$9 z*37PaGN1BCiB3(J@;#`|Bz6n7B1BRgv&mKYPOcbo6)@yG4OcX$&0Y+p2nEgv9Qjth zQE*^p19KuML8&4b0|9Nob>PBy3}r6Lv_nc^WP&NkWm_(ftD+&_YU$r->59D^6^al@ zkD?t@fQE8%*3#Vg-A_PhnVH$41Ohqsk8lY#V;t!am z2xbG%s|dyNC2q9{QEM>a4JzCknnO3q7ZLdahQchE&4^H|3dAs55sZ53;Gk44S#n7) zP3E>K%np~jD3{MI`J9K`m&pv}vyb=+Gq1biqAeH4Jo_26pbEDsLSR89RlOb6ieRQG zw34RWfjbo;P+~f6kqeew&`K~s6z&WKRbslFx8?j8f)uK+D|F75a}!3|ZB%TIoK=_; zp1>@6{Evt$LgWD|R*f~7i+OTJ@h>=|IarDLs8NK#lqRe|E$S2@Fr^7CsK;H35SX$Q zE%KQqpV5>dFST0XE`3Nq6PC)SSYQ#NmY2_G6&8e^ZpIQcIN(V>hPz$d%_C_+4?2-V z9x2f6b=R%@#FkISjX;GO9d0+#!bMmpr{$Ew!Z2?1mp@^VBA5*Z9YI4s#$t;QwGzxa zg~g#tamFY7^SP=KO^OiMT|@Q%jAlg$?5@WG`N)!wezv=EOoF{+r7-iGG?i^dks8VPF(E+XXiXb*3-Rg zG9vHGdzOr#vc}6LvVJxji?fBZe}x_!@;*~j-qXv^=;iTH%UP(B|DwerFa)!SoT4s4 z+A~+=U0dFrD756P-fmi`bCmvHUV`PurJ_*!?}<5oCvJ6eAAO50p=?)!U_+p#;B_XlFFLw-7+tIe`6a z$-bAcWuP4nBuU+xah!cF~m`tbdJg4VUWKP%wW9fSWD<=<6MTb z{p4o)ZgqQv5tn_i~AIzKyShQU_H8jl-@-1*D69`zL&SrIz=eZ-FN`&6~WLq<2pLE z!yzzRJ6%>Mi#|=zV;RI0)`v_Tco1C+E{RE~!bVgnE)TM)8$I%dEpJSaO^RhX zklM@D-443-hCME;gQd4)(g&Na4maI|%`$Au@C0@7zHrMe=tHlAUcKc87aORf-m=P+ zH}!EG)4>jw!Ad1y|x@2UJ~w$aF9 zY&RLk`VrJ`GEf-dvd@%#+8GD5GhWaJZ`I)Q1?0-Vv0R=wHHE`GFb6*7fH1acXgwvho!BwzpJa= zgX^KJ`cSop^6LG>is~K3m|Eg6-Wd8jT Date: Tue, 27 Mar 2018 14:45:49 +0200 Subject: [PATCH 2/2] Removed .scpt from PyInstaller datas argument --- aw-watcher-window.spec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aw-watcher-window.spec b/aw-watcher-window.spec index be81152e..8793f0af 100644 --- a/aw-watcher-window.spec +++ b/aw-watcher-window.spec @@ -6,7 +6,7 @@ block_cipher = None a = Analysis(['aw_watcher_window/__main__.py'], pathex=[], binaries=None, - datas=[("aw_watcher_window/printAppTitle.scpt", "aw_watcher_window")], + datas=[], hiddenimports=[], hookspath=[], runtime_hooks=[],