ObjectiveC Accessibility API: UnMaximize Window - objective-c

I’m not sure if I am referring to this correctly, but when I use the word “UnMaximize”, I’m referring to:
When you click on the green button which is third on the top left of a
Chrome Window, it Maximizes the Window. When I use the word
“UnMaximize” above, I’m referring to the behavior that clicks that
button again so that it is no longer in full screen.
(By the way, what is the correct word for this in MacOS Terminology?)
I enjoy using the Easy Move+Resize App. While it can move Windows around, unfortunately, it has no effect on windows that are Maximized. Fortunately, the code is available on Github.
I’m curious if anyone can point me how to UnMaximize a Window using the Accessibility API
Does anyone what is the UnMaximize equivalent to kAXCloseButtonAttribute
I’m using MacOs 10.12 if that helps.

I’m grateful to #Willeke - Willeke for pointing me in the correct direction.
As mentioned in my question, I was looking at the code of the Easy Move+Resize App on GitHub. The problem with this code/app is that it does not work for Windows that are currently Maximized i.e. it tries to move these Windows, but it cannot, because they are fixed. (Note: This only has use and is relevant in a multi-monitor setup.) This app works correctly for Windows that are not Maximized.
Here, I am trying to add code that would UnMaximize a window in order to move it, and then Maximize it again after it has been moved. Obviously, the code below is in the context of this app, but I’m sure would be useful to users in other contexts.
I first added a wasMaximized property to EMRMoveResize.h
//EMRMoveResize.h
#property bool wasMaximized;
Next, I moved to EMRAppDelegate.m where the actual Event Callback code is. It should be noted that we are only concerned with moving i.e. only concerned with the Left Mouse Button. (This app uses the Right Mouse Button for resizing, which is not relavent when the Window has been maximized.) So, we are only concerned with kCGEventLeftMouseDown, kCGEventLeftMouseDragged and finally with kCGEventLeftMouseUp. In pseudo code, I have done something like:
If (LeftMouseDown) {
Find out if Window is Maximized
If (Window is Maximized) {
set the wasMaximized property
Click FullScreen Button to UnMaximize the Window in Order to Move it
}
The Window is now UnMaximized would now move as other windows in the LeftMouseDragged event, which I have not made any changes to. Finally,
If(LeftMouseUp) {
If(wasMaximized value was set) {
Click FullScreen Button again to Maximize the Window (Since it started out as Maximized)
Reset the wasMaximized property
}
}
Now for the snippets of code changes to EMRAppDelegate.m
if (type == kCGEventLeftMouseDown
|| type == kCGEventRightMouseDown) {
//..
//Skipped Unchanged Code
//..
//Find out if Window is Maximized
CFTypeRef TypeRef = nil;
if (AXUIElementCopyAttributeValue((AXUIElementRef)_clickedWindow, CFSTR("AXFullScreen"), &TypeRef)) {
if(Debug) NSLog(#"Could not get wasMaximized Value");
} else {
[moveResize setWasMaximized: CFBooleanGetValue(TypeRef)];
if(Debug) NSLog(CFBooleanGetValue(TypeRef) ? #"Updated Maximized to True" : #"Updated Maximized to False");
}
//Click FullScreen Button to UnMaximize the Window in Order to Move it
if([moveResize wasMaximized]) {
AXUIElementRef buttonRef = nil;
AXUIElementCopyAttributeValue(_clickedWindow, kAXFullScreenButtonAttribute, (CFTypeRef*)&buttonRef);
if(Debug) NSLog(#"buttonRef: %p", buttonRef);
AXUIElementPerformAction(buttonRef, kAXPressAction);
CFRelease(buttonRef);
}
//..
//Skipped Unchanged Code
//..
}
if (type == kCGEventLeftMouseUp
|| type == kCGEventRightMouseUp) {
//..
//Skipped Unchanged Code
//..
//Click FullScreen Button again to Maximize the Window (Since it started out as Maximized)
AXUIElementRef _clickedWindow = [moveResize window];
if([moveResize wasMaximized]) {
AXUIElementRef buttonRef = nil;
AXUIElementCopyAttributeValue(_clickedWindow, kAXFullScreenButtonAttribute, (CFTypeRef*)&buttonRef);
if(Debug) NSLog(#"buttonRef: %p", buttonRef);
AXUIElementPerformAction(buttonRef, kAXPressAction);
CFRelease(buttonRef);
[moveResize setWasMaximized: false];
}
//..
//Skipped Unchanged Code
//..
}
This worked for me. But I'm not an expert in Objective C or MacOS, so if you feel something can be improved, feel free to edit this post.

Related

Detect when Today Widget "Show More" is tapped or is expanded?

Is it possible to detect when the Today widget is expanded/contracted or 'Show More' button is tapped in Objective C?
There is no way to detect whether the Show More button has been tapped in Today Extension.
What you can do is determine if it's open with checking frame height.
You can add a boolean and set it in :
- (void)widgetActiveDisplayModeDidChange:(NCWidgetDisplayMode)activeDisplayMode withMaximumSize:(CGSize)maxSize {
if (activeDisplayMode == NCWidgetDisplayModeCompact) {
//non expanded
} else {
//expanded
}
}
You can also have a layout that doesn't need to know if it's opened or closed, that's what most apps do.
Hope this helps :)

How do I intercept downloads from a WKWebView?

I am making a Cocoa app that involves users selecting pictures from online for use within the app. I am also looking to collect contextual metadata from those downloads, such as the host the image came from, the website the user was visiting, the exact MIME type in the response headers, etc.
Basically I want to curate my user across the internet, downloading images and metadata into that user's account as I go. Until today I thought this would be impossible.
But just recently I was toying with a WKWebView and I tried right clicking on an image. I saw this...
Is there any way I could connect to that Download Image button, and get notifications when its clicked?
I did figure out how to make "Download Linked File" work and its a doozy that will not fit in a SO answer: https://github.com/kfix/MacPin/commit/9e5f925819f7f54ef29baff1e90783b820e683a3
However implementing those private delegate functions doesn't seem to allow "Download Image" to signal my WkWebView app in any way.
You can intercept those non-working "Download Image" and "Download Linked File" menu items by subclassing the WKWebView class and implementing the willOpenMenu method like this:
class MyWebView: WKWebView {
override func willOpenMenu(_ menu: NSMenu, with event: NSEvent) {
for menuItem in menu.items {
if menuItem.identifier == "WKMenuItemIdentifierDownloadImage" ||
menuItem.identifier == "WKMenuItemIdentifierDownloadLinkedFile" {
menuItem.action = #selector(menuClick(sender:))
menuItem.target = self
}
}
}
func menuClick(sender: AnyObject) {
if let menuItem = sender as? NSMenuItem {
Swift.print("Menu \(menuItem.title) clicked")
}
}
}
Instead of this you can also simply hide the menu items with menuItem.isHidden = true
Detecting the chosen menu item is one thing, but knowing what the user actually clicked in the WKWebView control is the next challenge :)

How to prevent window from loosing focus when receiving a grabbed key from X11

My window calls hide() when a QEvent::FocusOut is received. Simultaniously I want its visibility to be toggled if a hotkey is pressed. Now I have the following problem: Pressing the hotkey, registered with XGrabKex(...) seems to steel the focus of my app. Resulting in an unwanted behaviour. If my app is visible the hotkeyevent steels focus, which results in a QEvent::FocusOut, which hides my app, and after that the hotkey is received which toggles visibility (shows) my app. I.e. my app does not hide when pressing the hotkey.
Is there a way to tell the x window system to not steel the focus when a grabbed key is pressed? Or are there other possible solutions to this problem?
A couple of different methods.
Use XQueryKeymap to see which keys are pressed. For instance, when you get a FocusOut event, call XQueryKeymap and see if your hotkey is pressed. If it is not, hide the window; if it is, don't hide it and wait for the hotkey event.
Delay hiding on FocusOut by 100 or so milliseconds. Cancel hiding if you either get the hot key or get your focus back during this time interval.
Look also here for useful info.
Finally got it to work in a "proper" way:
bool MainWidget::nativeEvent(const QByteArray &eventType, void *message, long *)
{
#ifdef Q_OS_LINUX
if (eventType == "xcb_generic_event_t")
{
xcb_generic_event_t* event = static_cast<xcb_generic_event_t *>(message);
switch (event->response_type & 127)
{
case XCB_FOCUS_OUT: {
xcb_focus_out_event_t *fe = (xcb_focus_out_event_t *)event;
if ((fe->mode==XCB_NOTIFY_MODE_GRAB && fe->detail==XCB_NOTIFY_DETAIL_NONLINEAR)
|| (fe->mode==XCB_NOTIFY_MODE_NORMAL && fe->detail==XCB_NOTIFY_DETAIL_NONLINEAR ))
hide();
break;
}
}
}
#endif
return false;
}

How do find out if there is a full-screen app running on a specific NSScreen

In Cocoa/AppKit, given a screen from [NSScreen screens], how can I find out if there's a full-screen app running on that specific screen? I'm mostly interested in apps that use the Cocoa APIs for full-screen, but if there's a solution that also encompasses other types of full-screen apps, even better. The solution needs be able to pass Mac App Store approval.
My specific use case involves a menu bar app (NSStatusItem) and figuring out whether or not a menubar is shown at all on [NSScreen mainScreen] in order to allow a global keyboard shortcut to show either a popover positioning on the status item (if it's visible) or a floating window if there's no visible status item.
NSScreens themselves don't seem to expose any information about windows/apps, and NSRunningApplication doesn't expose this information either.
Are there perhaps Carbon APIs for finding this out? For example, if I have a list of windows, I could iterate through them and see if any window frames match the screens' frame exactly. On the other hand, there might be apps that have a frame like that but run underneath other apps (like the Backdrop app, https://itunes.apple.com/us/app/backdrop/id411461952?mt=12), so an approach like this would need to look at window levels.
You can try the CGWindowList API, such as CGWindowListCopyWindowInfo().
If you just want to know if the menu bar is showing, you should be able to check -[NSApplication currentSystemPresentationOptions] for NSApplicationPresentationAutoHideMenuBar or NSApplicationPresentationHideMenuBar. That method can also tell you if the active app is in Cocoa full-screen mode (NSApplicationPresentationFullScreen).
Here's a solution based on CGWindowListCopyWindowInfo in Swift.
func fullScreenWindows(fullScreen: Bool) -> [CGWindowID] {
var winList: [CGWindowID] = []
// if you want to get the windows in full screen, you MUST make sure the option excluding 'optionOnScreenOnly'
let option: CGWindowListOption = fullScreen ? .excludeDesktopElements : [.excludeDesktopElements, .optionOnScreenOnly]
guard let winArray: CFArray = CGWindowListCopyWindowInfo(option, kCGNullWindowID) else {
return winList
}
for i in 0..<CFArrayGetCount(winArray) {
// current window's info
let winInfo = unsafeBitCast(CFArrayGetValueAtIndex(winArray, i), to: CFDictionary.self)
// current window's bounds
guard let boundsDict = (winInfo as NSDictionary)[kCGWindowBounds],
let bounds = CGRect.init(dictionaryRepresentation: boundsDict as! CFDictionary) else {
continue
}
// to check the window is in full screen or not
guard __CGSizeEqualToSize(NSScreen.main!.frame.size, bounds.size) else {
continue
}
// current window's id
guard let winId = (winInfo as NSDictionary)[kCGWindowNumber] as? CGWindowID,
winId == kCGNullWindowID else {
continue
}
winList.append(winId)
}
return winList
}
Here's a solution based on CGWindowListCopyWindowInfo, as Ken Thomases suggested in his answer:
- (BOOL)fullScreenAppPresentOn:(NSScreen *)screen
{
// Get all of the visible windows (across all running applications)
NSArray<NSDictionary*> *windowInfoList = (__bridge_transfer id)CGWindowListCopyWindowInfo(kCGWindowListOptionOnScreenOnly | kCGWindowListExcludeDesktopElements, kCGNullWindowID);
// For each window, see if the bounds are the same size as the screen's frame
for (int windowInfoListIndex = 0; windowInfoListIndex < (int)windowsInfoList.count; windowInfoListIndex++)
{
NSDictionary *windowInfo = windowInfoList[windowInfoListIndex];
CFDictionaryRef windowInfoRef = (__bridge CFDictionaryRef) windowInfo[(__bridge NSString *)kCGWindowBounds];
CGRect windowBounds;
CGRectMakeWithDictionaryRepresentation(windowInfoRef, &windowBounds);
if (CGRectEqualToRect([screen frame], windowBounds))
{
return YES;
}
}
return NO;
}

makeKeyAndOrderFront doesn't do either

I've got an application that runs at startup as a menu in the OS X menu bar (i.e., as a background application). When you select an option from the menu, it transforms into a foreground application (creating a dock icon) and shows a window.
Here's the background/foreground code I'm using:
+(void) TransformToForegroundApplication {
ProcessSerialNumber psn = { 0, kCurrentProcess };
TransformProcessType(&psn, kProcessTransformToForegroundApplication);
}
+(void) TransformToBackgroundApplication {
ProcessSerialNumber psn = { 0, kCurrentProcess };
TransformProcessType(&psn, kProcessTransformToUIElementApplication);
}
Pretty standard stuff, from the looks of things. And it works fine, except for one small issue.
The problem is that after I call TransformToForegroundApplication, I call makeKeyAndOrderFront: on the window to make it come to the front of the Z-order and receive focus - but it does neither. It shows up in the background, underneath everything else. I need to either click on the dock icon, or minimize all of the windows in front of it and then click on that window, in order to bring it to the front.
Any ideas?
Probably you need to activate the application, which I presume TransformProcessType() will not do automatically, and know -makeKeyAndOrderFront: will not.
Have you tried calling [NSApp activateIgnoringOtherApps:YES]?