I have set up a global hotkey with RegisterEventHotkey
. When the user presses it, it gets the currently focused window with CGWindowListCopyWindowInfo
, and then I need to set it always on top.
If the current window is in my process (from which I am executing the code) I can simply convert the windowNumber
from CGWindowListCopyWindowInfo
to a NSWindow
and do setLevel
:
nswin = [NSApp windowWithWindowNumber:windowNumber]
[nswin setLevel: Int(CGWindowLevelForKey(kCGFloatingWindowLevelKey))]
My Problem I am not able to do this if the currently focused window is not in my process. Can you please show me how?
Stuff I tried:
- This app here named "Afloat" used "SIMBL" to accomplish this. From any window you can hit Cmd + A and it will set always on top. However I am trying to do with C/ObjC from my normal desktop app without the use of helpers like SIMBL.
I come across
CGSSetWindowLevel
inCGPrivate.h
- undocumented stuff - https://gist.github.com/Noitidart/3664c5c2059c9aa6779f#file-cgsprivate-h-L63 - However I recall I tried something like this in the past but would get an error as I tried to connect to a window that wasn't in the calling process.-
Only the owner of a window can manipulate it. So, Apple has the concept of a universal owner that owns all windows and can manipulate them all. There can only be one universal owner at a time (the Dock).
Maybe, is there anyway to pretend for my calling process to temporarily be the dock? Maybe
CGSGetConnectionIDForPSN
for the dock then use that connection?
-
My use: I'm trying to replicate the functionality my open source, free, browser addon - https://addons.mozilla.org/en-US/firefox/addon/topick/ - so my calling process if Firefox. It works on Windows and Linux right now, and just need to figure out how to do it in mac for non-Firefox windows.
It seems you want to make an external process's window stay on top of all other applications, while the code I provide here does not accomplish exactly what you are looking for, it is at least somewhat similar, and might be good enough for what you need, depending on your use case. In this example I demonstrate how to keep a
CGWindowID
on top of a specificNSWindow *
. Note - theNSWindow *
is the parent window, and it will need to be owned by your app, but theCGWindowID
used for the child window can belong to any application). If you want theNSWindow *
to be the child window, change theNSWindowBelow
option toNSWindowAbove
.There is a tiny problem with this solution and that is some minor flickering here and there, when the parent window is attempting to gain focus but then loses it immediately - the flicker happens very quickly and intermittently, perhaps it can be overlooked if you are super desperate.
Anyway, the code is...
cocoa.mm
subclass.mm
subclass.h
I went an extra mile and added some functions to help you retrieve the appropriate
CGWindowID
based on the frontmostCGWindowID
, and if you know the correctCGWindowID
beforehand, via AppleScript, or however you prefer, you may bring it to the front usingcocoa_wid_to_top(wid)
, (if the user permits), however this doesn't play well with processes owning multiple visible windows simultaneously, because it brings all windows owned by the process id associated with the givenCGWindowID
to the top, so you might not have theCGWindowID
you wanted to be on the absolute top of the window stack necessarily. The reason you may want the window to be brought on top of the stack is due to the fact there are cases in which a window may open that you would want to make a child window but it appeared on screen underneath your parent window, thus forcing you to click it before the parent/child relationship of windows can effectively take place.Documentation below...
NSWindow *cocoa_window_from_wid(CGWindowID wid);
Returns anNSWindow *
from a givenCGWindowID
, provided theCGWindowID
belongs to the current app, otherwise an invalidCGWindowID
is returned, which can be represented with the constantkCGNullWindowID
.CGWindowID cocoa_wid_from_window(NSWindow *window);
Returns aCGWindowID
from a givenNSWindow *
, provided theNSWindow *
belongs to the current app, otherwise I believe you will get a segfault. That's what happens in my testing when you know the value of anNSWindow *
and attempt to use it in an app that it doesn't belong to, so don't even try.bool cocoa_wid_exists(CGWindowID wid);
Returnstrue
if the a window based on a specifiedCGWindowID
exists, excluding your screensaver and desktop elements,false
if it doesn't.pid_t cocoa_pid_from_wid(CGWindowID wid);
A helper function forcocoa_wid_to_top(wid)
which returns the process id, (orpid_t
), associated with the givenCGWindowID
.unsigned long cocoa_get_wid_or_pid(bool wid);
Returns the frontmostCGWindowID
ifwid
istrue
, otherwise the frontmost process id, (orpid_t
), is the result. Note the return typeunsigned long
can be safely casted to and from aCGWindowID
orpid_t
as needed.void cocoa_wid_to_top(CGWindowID wid);
Attempts to bring all windows that belong to the process id, (orpid_t
), associated with the givenCGWindowID
to be the topmost app.Now for the most important function...
void cocoa_wid_set_pwid(CGWindowID wid, CGWindowID pwid);
Assigns a parent window based on a specifiedCGWindowID
to the given child window associated with the properCGWindowID
. The parent window id, (orpwid
), must be owned by the current app, while the child window id, (orwid
), may belong to any application, excluding the screensaver and desktop elements. If the parent or child window ceases to exist, they lose their parent and child relationship to avoid recycledCGWindowID
's from inheriting the relationship. If the parent or childCGWindowID
doesn't exist, they will be set tokCGNullWindowID
, which reliably ends the relationship.Note this code has been tested in Catalina and indeed works as advertised at the time of writing.
To use the cocoa functions I provided in your C or C++ code you may do this in a header: