I am trying to manipulate the windows of macOS. I have found the following URLs:
Move other windows on Mac OS X using Accessibility API
Getting Window Number through OSX Accessibility API (Core Graphics)
set the size and position of all windows on the screen in swift
How to set an external application always being frontmost on OS X? (Unanswered)
How to convert a Carbon AXUIElementRef to Cocoa NSWindow (Unanswered)
Setting the window level over Accessibility (Unanswered)
How to create an AXUIElementRef from an NSView or NSWindow? (Unanswered)
The links allow for accessing the various windows of an application via the Accessibility API of macOS; more specifically, they use the AXUIElementRef element to re-position windows.
When creating an app for macOS, I have successfully used the following code ...
#implementation CustomWindow
- (id)initWithContentRect:(NSRect)contentRect styleMask:(NSWindowStyleMask)windowStyle backing:(NSBackingStoreType)bufferingType defer:(BOOL)deferCreation
{
self = [super initWithContentRect:contentRect styleMask:windowStyle backing:bufferingType defer:deferCreation];
if(self)
{
[self setLevel:kCGDesktopWindowLevel - 1];
[self setCollectionBehavior:
(NSWindowCollectionBehaviorCanJoinAllSpaces |
NSWindowCollectionBehaviorStationary |
NSWindowCollectionBehaviorIgnoresCycle)];
}
return self;
}
// ...
#end
... to put a window on the Desktop level of the screen. What I am looking for is a way to manipulate the same Window level property or the like via a custom Windows Manager application. In other words, I would like to code a windows manager that can manipulate the levels the windows of other applications on screen.
Am, I correct in my findings that this can't be done? Can it be done via Core Graphics?
In addition, I want to change the application that is key and in focus, but I think this can be done.
You can't really manipulate the CGWindowLevel of a window (all normal windows are kCGNormalWindowLevel, for example) but if you're looking to change the order of a normal-level window relative to others then you're probably looking for Accessibility's kAXRaiseAction, which you can see in action here.
If you're just looking to see what all the windows on the system are (and their CGWindowLevels, then CGWindowListCopyWindowInfo will give you that information.
Related
How to identify actual touchBar hardware is available in an Mac book using obj c code so that i can provide touchbar menu options.
From the Apple documentation :
There is no need, and no API, for your app to know whether or not there is a Touch Bar available. Whether your app is running on a machine that supports the Touch Bar or not, your app’s onscreen user interface (UI) appears and behaves the same way.
To check if touch bar is available (to improve UI/UX for example) you should implement the delegate and set a Bool like :
// Declare a class variable
#property (nonatomic, assign) BOOL isTouchBarAvailable;
#available(OSX 10.12.1, *)
// A bellow version can not be installed on a new MacBook.
// Like an iPhone 7 can't have iOS9 installed.
- (NSTouchBar *)makeTouchBar
{
// ... here the code to make the bar
self.isTouchBarAvailable = YES
return touchBar
}
Source : https://developer.apple.com/reference/appkit/nstouchbar?language=objc
NSTouchBar can work in software as well as hardware, as show by the simulator in Xcode. It doesn't require the hardware to work. However, the Xcode 8 release notes tells you how to test if the OS can support any kind of Touch Bar functions.
https://developer.apple.com/library/content/releasenotes/DeveloperTools/RN-Xcode/Introduction.html
Xcode 8.1 supports Touch Bar for Macs that include it, and supports adding Touch Bar functionality to your app. (28859277)
Before using Touch Bar functionality in your app, confirm that the app is running on a macOS version that includes support for Touch Bar using a runtime check.
For example, the following Objective-C code performs a runtime check to make sure NSTouchBar is available:
NSClassFromString(#"NSTouchBar") != nil
In Swift code, do an availability check for macOS 10.12.1, and a runtime check for Touch Bar class availability. For example:
NSClassFromString("NSTouchBar") != nil
If you want some control you can use isAutomaticCustomizeTouchBarMenuItemEnabled:
import Cocoa
#NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {
func applicationDidFinishLaunching(_ aNotification: Notification) {
// Here we just opt-in for allowing our instance of the
// NSTouchBar class to be customized throughout the app.
if #available(OSX 10.12.2, *) {
NSApplication.shared().isAutomaticCustomizeTouchBarMenuItemEnabled = true
}
}
/*
Whether or not a menu item to customize the touch bar can be automatically
added to the main menu. It will only actually be added when hardware
or simulator is present. Defaults to NO. Setting this property to YES
is the recommended way to add the customization menu item.
*/
I'm writing a Cocoa app that needs to be able to capture keyboard events even when not focused. (It's controlling another app via the Apple Scripting Bridge).
I have tried the solution here: OSX: Detect system-wide keyDown events?
It compiles fine, but doesn't actually do anything. I'm putting the code in my init method.
I also can't get CGEventTap to work either. Any suggestions?
Here's my code:
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
[NSEvent addGlobalMonitorForEventsMatchingMask:(NSKeyDownMask) handler:^(NSEvent *event){
NSLog(#"test");
}];
}
Also, I'm aware that assistive devices needs turned on. Unfortunately when I go into the settings it only lists specific apps. Mine isn't one of them.
Use AXIsProcessTrustedWithOptions to request access. Then you'll show up in System Preferences.
Is there any posible way to know if a Mac is in background or foreground?
In iOS we have the following methods:
applicationWillEnterForeground
applicationWillenterBackground
But is there any way to know if a Mac is active or inactive?
It depends on your application layout, but in general use an NSApplication delegate and implement:
- (void)applicationWillBecomeActive:(NSNotification *)aNotification
as well as its counterpart
- (void)applicationWillResignActive:(NSNotification *)aNotification
There are other notifications as well. See the linked document above. Also note that OS X allows real multitasking and thus background/foreground usually refer to having focus or not having focus.
I'm using addGlobalMonitorForEventsMatchingMask to listen to events in Cocoa:
[NSEvent addGlobalMonitorForEventsMatchingMask:NSLeftMouseDraggedMask
handler:^(NSEvent *event) {
NSLog(#"Dragged...");
}];
Though I would like to know if I'm dragging/moving a window (and which window that is, I can find the focused window though when holding command and dragging a window it doesn't get focus as far as I know.)
So, can I detect whether or not I'm dragging a window?
Update:
I now have a class: "SATest : NSObject <NSWindowDelegate>" in which I implement the windowDidMove method (and later perhaps windowWillMove too.) Though, now the next step would be attaching this to a window, right? So my question now is: How do I attach the delegate to all windows of all apps?
Update 2:
I now can find a list of all open windows on the screen:
AXUIElementRef _systemWideElement;
_systemWideElement = AXUIElementCreateSystemWide();
CFArrayRef _windows;
AXUIElementCopyAttributeValues(_systemWideElement, kAXWindowsAttribute, 0, 100, &_windows);
Now I have to iterate over the windows, and of each get it's NSWindow so I can add my delegate to it: [window setDelegate:self];
Update 3: To be clear, this question is about detecting the dragging of ALL windows of ALL apps. Not only the windows of my own app.
Also, I'm very new to this event and window management stuff, so no need to keep your answer short I'm happy to read a lot :P
Thanks!
-P
To find out if a window is being dragged you need to have an object that acts as the delegate of the window by responding to the following messages of the NSWindowDelegate protocol:
windowWillMove - this tells the delegate that the window is about to move.
windowDidMove - this tells the delegate that the window has moved.
You can retrieve the NSWindow object in question by sending object to the notification parameter sent to these methods:
e.g.
NSWindow draggedWindow = [notification object];
More information can be found here.
Update:
In response to your request about getting this information for all windows the NSApplication class provides a method which returns an array of all windows owned by the application. The typical way of getting this information is to use one of the NSApplicationDelegate methods to get a reference to your application object.
For example, in your app delegate (pseudocode):
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
NSApplication * app = [aNotification object];
// you now have a reference to your application.
// and can iterate over the collection of windows and call
// [window setDelegate:self]; for each window.
}
Note that you will need to add / remove you delegates as windows are added and removed. The best method for doing this is – applicationDidUpdate:.
This should be enough to get you started solving your problem.
As suggested by Benjamin the answer lies in the accessibility API. I was looking around in this for a while, even before I asked this question, but never got it to do what I wanted. I now have found a pretty nice solution.
At a high-level I do the following:
Listen to the mouse down event and remember both on which window you clicked and it's location.
Listen to the mouse up event and check if the location has changed, if so you know you moved a window
You can do something similar for the size if you also want to know if you resized. There might be a better solution, but after days of trying stuff this is the only way I got it to work the way I wanted.
Hope this helps anyone who was looking for something similar.
-Pablo
I have created a fairly simple screensaver that runs on Mac OS 10.6.5 without issue.
The configuration screen has accumulated quite a few different options and I'm trying to implement my own preview on the configureSheet window so the user (just me, currently) can immediately see the effect of a change without having to OK and Test each change.
I've added an NSView to the configureSheet and set the custom class in Interface Builder to my ScreenSaverView subclass. I know that drawRect: is firing, because I can remove the condition for clearing the view to black, and my custom preview no longer appears with the black background.
Here is that function (based on several fine tutorials on the Internet):
- (void)drawRect:(NSRect)rect
{
if ( shouldDrawBackground )
{
[super drawRect:rect];
shouldDrawBackground = NO;
}
if (pausing == NO)
[spiroForm drawForm];
}
The spiroForm class simply draws itself into the ScreenSaverView frame using NSBezierPath and, as mentioned, is not problematical for the actual screensaver or the built-in System Preferences preview. The custom preview (configureView) frame is passed into the init method for, um, itself (since its custom class is my ScreenSaverView subclass.) The -initWithFrame method is called in configureSheet before returning the configureSheet object to the OS:
[configureView initWithFrame:[configureView bounds] isPreview:YES];
Maybe I don't have to do that? It was just something I tried to see if it was required for drawing.
I eventually added a delegate to the configureSheet to try triggering the startAnimation and stopAnimation functions of my preview via windowWillBeginSheet and windowWillEndSheet notifications, but those don't appear to be getting called for some reason. The delegate is declared as NSObject <NSWindowDelegate> and I set the delegate in the configureSheet method before returning the configureSheet object.
I've been working on this for days, but haven't been able to find anything about how the OS manages the ScreenSaverView objects (which I think is what I'm trying to emulate by running my own copy.)
Does anybody have any suggestions on how to manage this or if Apple documents it somewhere that I haven't found? This isn't really required for the screensaver to work, I just think it would be fun (I also looked for a way to use the OS preview, but it's blocked while the configureSheet is activated.)
OK, there are a couple of 'duh' moments involved with the solution:
First of all, I was setting the delegate for the sheet notifications to the sheet itself. The window that the sheet belongs to gets the notifications.
Secondly, that very window that the sheet belongs to is owned by System Preferences, I don't see any way to set my delegate class as a delegate to that window, so the whole delegate thing doesn't appear to be a viable solution.
I ended up subclassing NSWindow for the configureSheet so that I could start and stop animation on my preview by over-riding the makeKeyWindow and close methods.
- (void) makeKeyWindow
{
if (myPreview != nil)
if ( ! [myPreview isAnimating])
{
[myPreview startAnimation];
}
[super makeKeyWindow];
}
I also had to add an IBOutlet for my preview object itself and connect it in Interface Builder.
Still working out a couple of issues, but now when I click on my screensaver Options button, my configureSheet drops down and displays its own preview while you set options. Sheesh. The hoops I jump through for these little niceties. Anyway, I like it. Onward and upward.