prevent windows from opening (windowless application) - objective-c

I want to make a windowless application. The problem is that I'm using a framework that shows some alert boxes sometimes, and I'm not able to suppress them (at least not without a crash).
I've set LSBackgroundOnly to 1 (in the info.plist app file), but this doesn't seem to work. Any chance I'm missing something here (maybe some other settings that I need to set)?
I've also tried creating a category for NSWindow:
Overwritten alloc and allocWithZone: so that no window gets created. This works on some situations, but starting a modal session throws exception (because sending a nil parameter to beginModalSessionForWindow:).
Tried overwriting NSApplication's beginModalSessionForWindow: and returned nil everytime, but this leads to EXC_BADACCESS somewhere in the framework (runModalSession: and endModalSession: were also overwritten, but the crash is before any of them being called).
Any other ideas will be welcome.
In case it helps, the framework that I'm using is Twain.Framework, so if instead anyone knows a way to block all UI from twain (this includes error alerts shown when a device is not connected) it would be great if you share.

It's not clear what you're hoping for. Are you hoping for an app that doesn't normally display windows but does allow the third-party framework to show its alerts? Or are you hoping to find a way to prevent the framework from showing its alerts so your app absolutely never has any windows?
If it's the former, you're probably looking for LSUIElement. That's an app which can have some UI elements, but doesn't show in the Dock or the Command-Tab application switcher and doesn't have a menu bar.

I managed to make it 'windowless' (some windows are still shown, but at least they're not visible, and they're automatically closed). I only handled modal windows, since these where shown in my situation.
I made a category for NSWindow, so that all windows are invisible, and can't become key or main windows:
-(BOOL)canBecomeKeyWindow{
[self setOpaque:NO];
self.alphaValue = 0;
return NO;
}
-(BOOL)canBecomeMainWindow{
[self setOpaque:NO];
self.alphaValue = 0;
return NO;
}
Subclassed NSApplication:
-(NSInteger)runModalForWindow:(NSWindow *)aWindow{
return NSCancelButton;
}
-(NSModalSession)beginModalSessionForWindow:(NSWindow *)aWindow{
NSModalSession session = [super beginModalSessionForWindow:aWindow];
[aWindow performSelector:#selector(close) withObject:nil afterDelay:1];
return session;
}
However, this didn't help much. In case of runModalForWindow: everything was good, since the owner of the window received a response. In case of beginModalSessionForWindow: the application got stuck, most probably because the window's owner was waiting for a specific event (e.g. button click) which it never received.

Related

Why does [NSRunningApplication activateWithOptions:] return NO

I am having a problem where [NSRunningApplication activateWithOptions:] returns NO when there is no obvious reason that it should fail to activate an application.
This is not a duplicate of -[NSRunningApplication activateWithOptions:] not working
The accepted answer to that question is to use NSApplicationActivateIgnoringOtherApps instead of NSApplicationActivateAllWindows but that is not an acceptable answer as the two options are intended to activate an application in a different manner as the option values imply.
There are places within my code where [NSRunningApplication activateWithOptions:] does work, however when calling it via an EventTap, it almost always returns NO regardless of using NSApplicationActivateAllWindows or NSApplicationActivateIgnoringOtherApps as the option argument. The reason I say "almost" always returns NO is because the very first time it is called, it does work. All 2nd and subsequent calls fail.
I am basically handling a single-click event on an Application icon in the Dock. I am using an EventTap installed on the Dock process that was created with CGEventTapCreateForPSN(). Within the event tap callback, I have successfully determined the process identifier of the icon that was clicked on in the Dock. The process id was obtained using the Accessibility API.
Now I need to activate the application, so I call:
BOOL result = NO;
NSRunningApplication *currentApp = [NSRunningApplication runningApplicationWithProcessIdentifier:pid];
if (currentApp) {
result = [currentApp activateWithOptions:NSApplicationActivateAllWindows];
}
In the above example, I have verified that currentApp is valid, and that bundleIdentifier, processIdentifier and a few other instance vars in the RunningApplication object are correct.
As mentioned above, regardless of the NSApplicationActivationOptions used, the application does not activate, and the method returns NO. Again, it does occasionally work on the very first click, but fails on all subsequent clicks on any application's dock icon.
Can anyone explain why such a simple method is failing? and possibly why it would work only the first time it is called? and why it always works when not executed from an EventTap?
I have spent several hours trying to resolve the issue, and the only thing that always works is to use the deprecated method SetFrontProcessWithOptions(&psn, 0); instead of [NSRunningApplication activateWithOptions:NSApplicationActivateAllWindows].
I have tried wrapping the call to [NSRunningApplication activateWithOptions:] within dispatch_async, I have also tried using an operation queue from within the EventTap, neither of which solves the problem. Finally, I have tried sending a notification and calling [NSRunningApplication activateWithOptions:] from within the notification callback, which also fails. This is the code in my event tap:
case kCGEventLeftMouseDown:
if (cgClickCount == 1) {
NSNumber *pidNumber = [NSNumber numberWithLong:actualPid];
NSDictionary *userInfo = [NSDictionary dictionaryWithObjectsAndKeys:pidNumber, #"pidNumber", nil];
[[NSNotificationCenter defaultCenter] postNotificationName:#"DockApplicationClickedNotification" object:self userInfo:userInfo];
return NULL; /* swallows event */
}
break;
Any assistance is greatly appreciated! A debug screen grab is attached that shows how the 1st attempt to activate an app by clicking its dock icon is successfully, and how subsequent clicks fail to activate the app.
UPDATE and half-solved:
I have determined why [NSRunningApplication activateWithOptions:] works on the 1st attempt and fails on 2nd and subsequent attempts. The first time I click on an application icon in the dock, my application is the active app. On subsequent clicks on an app icon, some other application is active. So the problem here seems to be that [NSRunningApplication activateWithOptions:] will only work if my application is the frontmost process.
If I add [NSApp activateIgnoringOtherApps:YES]; before the call to [NSRunningApplication activateWithOptions:], it consistently works as intended, however, there is a menubar flicker due to activating my app only to then activate a different app. So the question becomes:
Is there any way to get [NSRunningApplication activateWithOptions:] to work when my application is not the frontmost app? and is activateWithOptions: supposed to work this way?

Define a controller for NSDocument for document-based application

I'm not very sure how Document-Based Applications works.
I've created some actions for NSObject in the Mainmenu.xib. One of this is called when the user click on "File>new":
-(IBAction) newDocument:(id)sender{
Document* newDoc =[[Document alloc] init];
[[NSDocumentController sharedDocumentController]addDocument:newDoc];
[newDoc addWindowController: [[NSWindowController alloc] initWithWindowNibName:[newDoc windowNibName] owner:newDoc]];
[newDoc showWindows];
}
I've also this code inside the openDocument:(id) sender action that does the same but of course loading data to define the application workspace.
If I run the application it show a blank document without to call newDocument action. I don't know how to stop default blank document and to set newDocument: to be called.
Then if i do openDocument: too (so I've two documents, one blank and one not) and I do some operation on the second document it also replicate in the first blank one.
I've double check delegates, file owners, and also if the - (void)windowDidBecomeMain:(NSNotification *)notification return different pointers and all seem to be ok.
Probably I've not understood document based application work flow but I've read the Apple guide and other istructions. What do I miss?
An IBAction method is called, when the user did something. So this is not called from the system at app launch.
You can customize the behavior at app launch with -applicationShouldOpenUntitledFile: (NSApplicationDelegate) and – this is probably your next question – -applicationShouldHandleReopen:hasVisibleWindows: (NSApplicationDelegate). Changing the behavior in both cases is not recommended.
Looking to your action method, I see no reason, why you want to customize it.
A instance of your document class is created automatically.
You can create a window controller for it in your document subclass. This is documented.
Just let NSDocumentController do the work for you. What is the problem of the default behavior?
No. I thought to be confused instead the only problem was about releasing observer notification. When you call the close message for a NSDocument notification observers still persist. Working in ARC I miss this point.
So This is the solution at my issue. Thank you anyway.

Chromium tabs – can't update tab icon

I'm trying out chromium-tabs. I have an issue where the icon of a tab never gets properly updated after it gets created for the first time. In my CTTabContents subclass, I have:
- (void)tabDidBecomeSelected {
NSLog(#"selected");
[self setIcon:[NSImage imageNamed:#"default"]];
}
- (void)tabDidResignSelected {
NSLog(#"resign selected");
[self setIcon:[NSImage imageNamed:#"notification"]];
}
This should change the icon if the tab resigns its selected status to a different one. But it doesn't. The icon never changes. Note that I've tried the same exact calls in (id)initWithBaseTabContents:(CTTabContents *)baseContents andJid:(NSString *)jid andStatus:(NSString *)status where they work fine, so the calls fail only on an update of the icon later.
I've also made sure the functions are getting called (I see the log statements). What's the problem here? Also, is this the fork that Chrome actually uses? It obviously works in Chrome, which is strange... Am I doing something wrong?
I even tried doing [[[self.browser.windowController window] contentView] setNeedsDisplay:YES]; in the tabDidBecomeSelected functions, to force the whole window to redraw, in case their code for updating the icon fails, but still no luck.

Cocoa window dragging

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

Plugin objects in WebView getting destroyed?

Here's my situation:
I've written a simple web browser. Tabbed browsing was easy enough to get working once I wrapped my head around the concepts and figured how to perform operations on specific tabs. It works well and is pretty reliable under most circumstances.
A problem has plagued me, however, and I cannot figure out its cause.
Let's say I open a new tab and navigate to YouTube. I click on a video and the flash player loads. The video plays and all works fine. I now create another new tab and navigate to some site. The audio from the youtube player stops completely.
When I switch back to the youtube tab, the page will all still be there just as it was except the player has to reload completely, as if I had just reloaded the page. This seems to apply to other plugin types as well.
What's causing this?
Garbage collection is not enabled and as far as I know I'm creating the web views properly. Is there some silly, simple little thing that I missed somewhere along the line?
The - (void)setHostWindow:(NSWindow *)hostWindow method on WebView is probably what you're looking for.
I had the same problem with the flash in a WebView reloading whenever the syle mask of the enclosing window was changed. Wrapping the call to setStyleMask fixed the problem, as follows:
NSWindow *hostWindow = [[NSWindow alloc] init];
[self.webView setHostWindow:hostWindow];
[[self windowForSheet] setStyleMask:styleMask];
[self.webView setHostWindow:nil];
[hostWindow release];
The documentation for the method isn't stellar, but it does explicitly state a having a WebView inside a NSTabView as one of the use cases:
This method sets the receiver’s host window to hostWindow. Your application should only use this method if a web view is going to be removed from its window temporarily, and you want the web view to continue operating (for example, you don’t want to interrupt a load in progress). Since the receiver retains hostWindow, it is your responsibility to set the host window to nil before closing the window to avoid a retain loop.
For example, you might invoke this method if you attach a web view to an NSTabView object (as in a tabbed browser implementation). The NSTabView object takes views out of the window when they are not in the active tab, so you need to invoke this method before the web view is removed from its window. If you don't invoke this method, plug-ins will stop operating when the web view is removed from its window.
I had a similar problem but with a window with a webView that is closed and restored. Unfortunately the solution by #mlwelles did not solve the problem alone.
What did solve however is removing the webView from the window before it closes (proper "timing" is important). What I came up with is something like this:
id contentView;
id tmpHostWindow;
[window setDelegate:self];
- (BOOL)windowShouldClose:(NSNotification *)notification
{
// set temporary hostWindow on WebView and remove it from
// the closed window to prevent stopping flash plugin
// (windowWillClose would be better but that doesn't always work)
tmpHostWindow = [[NSWindow alloc] init];
[webView setHostWindow:tmpHostWindow];
[window setContentView:nil];
[contentView removeFromSuperview];
return TRUE;
}
- (void)windowDidBecomeKey:(NSNotification *)notification
{
// restore "hidden" webview
// (would be better to do it in applicationShouldHandleReopen
// but that seems to be too early (has no effect)
if ([window contentView] != contentView) {
[window setContentView:contentView];
[webView setHostWindow:nil];
tmpHostWindow = nil;
}
}