Reputation: 6947
I'm working on a screenshot Mac app. I'm trying to rebuilt what happens when you press Cmd-Ctrl-Shift-4: the cross hair cursor and the selection rectangle for the screenshot.
I'm using a custom borderless NSWindow on top of all other windows. I disabled the cursor to draw my own along with the selection rectangle.
My problem is that as soon as I click & drag to capture a screenshot, my app gets activated (because the click is intercepted by my shielding window).
Is there a way how I can receive the click in my custom view/window without having my app get activated?
I tried using an NSPanel
with the NSNonactivatingPanelMask
flag, but in this case, I have a problem with the cursor: I can't draw my own when another app is active, because I can't hide the cursor for other apps...
Upvotes: 7
Views: 1627
Reputation: 11
One more idea, you can override - (BOOL)_isNonactivatingPanel
private method in NSWindow
subclass:
@implementation MyWindow
- (BOOL)_isNonactivatingPanel
{
return YES;
}
@end
Voila, you got behaviour similar to NSPanel
:)
Upvotes: 1
Reputation: 2270
Actually, I have a new, better answer to this question involving more undocumented goodies. Here it is for future posterity:
There is an undocumented method on NSWindow that does exactly what you want:
@interface NSWindow (Private)
- (void )_setPreventsActivation:(bool)preventsActivation;
@end
[myWindow _setPreventsActivation:true];
This stops the window from activating both itself and its application when the user clicks on it.
The standard warnings about using undocumented APIs of course apply: Apple may change this at some point (although it's been around for many OS X versions so there's a good chance they won't) and using this may get your app rejected from the Mac app store.
Upvotes: 3
Reputation: 2270
For what it's worth, there's another way to make the cursor invisible globally other than creating a giant window. It involves some undocumented APIs if that's something you can use:
extern "C" {
typedef int CGSConnection;
void CGSSetConnectionProperty(int, int, const void *, const void *);
int CGSMainConnectionID();
}
void allowHidingCursorForBackgroundOnlyApp()
{
CFStringRef propertyString = CFStringCreateCopy(NULL, CFSTR("SetsCursorInBackground"));
CGSSetConnectionProperty(CGSMainConnectionID(), CGSMainConnectionID(), propertyString, kCFBooleanTrue);
CFRelease((CFTypeRef)propertyString);
}
Combine that with judicious use of event taps to capture and filter out mouse clicks, and you can create the same effect as the built-in screen shot feature.
Upvotes: 2
Reputation: 7534
I pray that there is a better way to do this now, but when I had to do something similar I ended up letting my window/view ignore all mouse input, then I used a CGEventTap
(see Quarts Event Services documentation) to capture mouse events globally(without removing them from the event queue). I them mapped them manually to my window, created a custom copy NSEvent
and manually dispatched it to my window.
The huge downside here (aside from complexity) is that I recall needing to run as root to be able to install the event tap. However, I think there is a way to get permission though universal access.
I'm completely unsure if dispatching a custom NSEvent
directly to the window will have the same side effect of activating your application; especially since many things have changed since 10.6... I would suggest a simple test to see if this is feasible before pursuing it.
Upvotes: 0