How to display on dual screens on Mac OS X Lion - objective-c

I want my app to be displayed on both laptop screen and an external screen with two separated NSWindow, I can't find any document about how to implement it. Any hint?
Thanks

The OS X OpenGL Programming Guide shows the old way of making a full-screen window:
Create a screen-sized window on the display you want to take over:
NSRect mainDisplayRect = [[NSScreen mainScreen] frame];
NSWindow *fullScreenWindow = [[NSWindow alloc] initWithContentRect: mainDisplayRect styleMask:NSBorderlessWindowMask
backing:NSBackingStoreBuffered defer:YES];
Set the window level to be above the menu bar.:
[fullScreenWindow setLevel:NSMainMenuWindowLevel+1];
Perform any other window configuration you desire:
[fullScreenWindow setOpaque:YES];
[fullScreenWindow setHidesOnDeactivate:YES];
Create a view with a double-buffered OpenGL context and attach it to the window:
NSOpenGLPixelFormatAttribute attrs[] =
{
NSOpenGLPFADoubleBuffer,
0
};
NSOpenGLPixelFormat* pixelFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes:attrs];
NSRect viewRect = NSMakeRect(0.0, 0.0, mainDisplayRect.size.width, mainDisplayRect.size.height);
MyOpenGLView *fullScreenView = [[MyOpenGLView alloc] initWithFrame:viewRect pixelFormat: pixelFormat];
[fullScreenWindow setContentView: fullScreenView];
Show the window:
[fullScreenWindow makeKeyAndOrderFront:self];
You can use this method to create windows on each screen that you want to draw to. If you use this to create a window on only one screen, the other screen will continue to function normally, instead of being blacked out or covered in a stupid linen texture. Depending on your use, you may not want to setHidesOnDeactivate.
There are also lower-level APIs to take control of a screen completely and prevent the OS or any other application from drawing to the screen, but their use is seldom justified.
EDIT: If you want to have one GL context with the render spanning multiple screens, you need to create a single window with a NSRect that spans all the screens. If the screen resolutions aren't matched, this may result in part of your window not being visible, and low-end graphics chips may have some problems.

Related

OSX Fullscreen (Kiosk) Application with Custom Resolution

I have an OS X Metal application that I would like to run in fullscreen at a non-native resolution, e.g. my native resolution is 1920x1080 and I would like to render the app in fullscreen at 1024x768. (I don't believe that using Metal to draw things impacts this question other than I am unable to use the OpenGL-specific NSView functions.)
My renderer uses a back buffer with a hard-coded size of 1024x768.
When I create my window with bounds = 1024x768 I get a fullscreen window, but my content is drawn centered and it does not stretch to fill the whole screen.
When I create my window with bounds = 1920x1080 I get a fullscreen window, but my content is drawn in the upper-left corner and incorrectly scaled (because of the ratio mismatch between the two resolutions).
Using [NSView - enterFullScreenMode:withOptions:] yielded the same results. Setting [NSView autoresizingMask] didn't change anything either.
Ideally I want the window to be the size of the screen and have the low-resolution back buffer be stretched to fill the whole window. What am I missing that would allow me to do that?
The relevant app initialization from my NSResponder <NSApplicationDelegate>:
// Create the window
self.Window = [NSWindow alloc];
[self.Window initWithContentRect:bounds
styleMask:NSBorderlessWindowMask
backing:NSBackingStoreBuffered
defer:YES];
// Create the view
self.View = [NSView alloc];
[self.View initWithFrame:bounds];
[self.View setWantsLayer:YES]; // The generated layer is CAMetalLayer
// Associate the view with the window
[self.Window setContentView:self.View];
// Misc
[self.Window makeKeyAndOrderFront:self.Window];
[self.Window setAcceptsMouseMovedEvents:YES];
[self.Window setHidesOnDeactivate:YES];
// Fullscreen
[self.Window setCollectionBehavior:NSWindowCollectionBehaviorFullScreenPrimary];
[self.Window toggleFullScreen:nil];
[self.Window makeFirstResponder:self.View];
Thanks.
The layer that backs a view is constrained to have its frame equal to that view's bounds. By default, the drawableSize of a CAMetalLayer is equal to its bounds times its contents scale. However, you can set it to any size you desire by explicitly setting the layer's drawableSize.

Show up a custom UI somewhere on screen

I've programmed a custom UI which looks like this: http://www.youtube.com/watch?v=XLsrVVhEs94
Currently it is only working within a NSView itself but I want it to show up in every corner of my screen.
So I programmatically created an NSWindow like so [[NSWindow alloc] initWithContentRect:windowRect styleMask:NSBorderlessWindowMask backing:NSBackingStoreBuffered
defer:NO];
This works great but there is a problem: Every time I open this UI I can barley see that the NSWindow was just created. I can see a shadow that has the size of the NSWindow and after that it disappears (because of [NSColor clearColor]). I think that [NSColor clearColor] applies too slow to the just created NSWindow.
The NSWindow was set up with [window setOpaque:NO] so it is transparent.
Is there another way to display a custom UI somewhere on my screen?
- Timo
I think you want to set the defer to YES. Referring to the documentation, the defer property will either create the window immediately, or defer it until it is displayed on the screen. In this case you can then set all the window properties, add subviews, etc before showing on the screen.
NSWindow *myWin = [[NSWindow alloc] initWithContentRect:windowRect styleMask:NSBorderlessWindowMask backing:NSBackingStoreBuffered defer:YES];
... do window setup here ...
[myWin orderFront:self];
Additionally if that still flickers, you can call 'display' on the window so it draws all the subviews into it's buffer first (including your clear), then call orderFront.

How to programmatically move child windows to external monitors on mac osx

I need to be able to force a child window to open on a specific external display. Example: main window resides on monitor 0, when child window is created, it opens on monitor 1 or 2.
I'm able to get the number of displays and their CGDirectDisplayID's like this:
NSRect frame;
NSScreen *screen;
CGDirectDisplayID displays[4];
CGDisplayCount displayCount;
screen = [window frame]
frame = [screen frame];
err = CGGetDisplaysWithRect((CGRect){NSMinX(frame), NSMinY(frame), NSWidth(frame), NSHeight(frame)}, 4, displays, &displayCount);
but I'm not able to force a window there unless I manually drag it over after it's been instantiated.
Any ideas? I've seen applications that do this, but can't find any code examples or documentation.
You can set child window's origin point. This code is not tested, but should work:
NSArray *screens = [NSScreen screens];
NSPoint newFrameOriginPoint;
if ([screens count]>1){
NSRect secondScreenRect = [[screens objectAtIndex:1] visibleRect];
newFrameOriginPoint = NSMakePoint(NSMinX(secondScreenRect), NSMinY(secondScreenRect));
[childWindow setFrameOrigin:newFrameOriginPoint];
}
[childWindow makeKeyAndOrderFront:nil];

NSScrollView in a NSWindow

I have an NSScrollView inside an NSWindow, but it seems to be disabled. It looks like it would work, but the scrollbars are unresponsive to the mouse or the scroll wheel.
When I put the exact same NSScrollView inside a window on a new XCode project, it works perfect. There is something about the way I am making the window that is preventing the scroll from working.
I've been able to simplify it to this example:
//Make a window
NSWindow* myWindow = [[NSWindow alloc] initWithContentRect:NSMakeRect(300, 300, 300, 300)
styleMask:NSTitledWindowMask
backing:NSBackingStoreRetained
defer:NO];
//Make a scroll view
NSScrollView *scrollview = [[NSScrollView alloc] initWithFrame:NSMakeRect(0, 0, 300, 300)];
[scrollview setHasVerticalScroller:YES];
[scrollview setAcceptsTouchEvents:YES];
[myWindow setContentView:scrollview];
//Add something big to the scroll view
NSButton* btn = [[[NSButton alloc] initWithFrame:NSMakeRect(0, 0, 600, 900)] autorelease];
[scrollview setDocumentView:btn];
//Show the window
[NSApp arrangeInFront:self];
[myWindow makeKeyAndOrderFront:self];
[NSApp activateIgnoringOtherApps:YES];
Any ideas?
Your problem, based on some experimentation I just did, seems to be with specifying NSBackingStoreRetained. The docs say:
You should not use this mode. It combines the limitations of NSBackingStoreNonretained with the memory use of NSBackingStoreBuffered.
They also say:
In Mac OS X 10.5 and later, requests for retained windows will result in the window system creating a buffered window, as that better matches actual use.
This does not seem to be accurate; switching the buffer: argument to NSBackingStoreBuffered made the window and scroll view behave as expected for me. (The docs also say not to use NSBackingStoreNonRetained, and indeed, it seemed to have problems similar to NSBackingStoreRetained.)

Drawing a Window at Coordinates or Drawing outside of NSStatus Item

I am trying to display something off the side of a NSStatusItem. I think I could do this in two ways:
Display a transparent window with the image I need at the coordinates of the mouse cursor.
OR
Use a custom NSStatusItem and move the controls/images in the view to the left so they are actually off the status item
The problem is, setting the NSRect frame negative (-200,0,100,50) doesn't seem to actually work. So, how can I render things outside of the bounds of the status item (think the CSS overflow property) or render a transparent window at specific coordinates?
The system will prevent you from drawing outside the status item, but using a transparent window will work.
NSRect rect; //The location of the window
NSWindow *win = [[NSWindow alloc] initWithContentRect:rect styleMask:NSBorderlessWindowMask backing:NSBackingStoreBuffered defer:YES];
[win setOpaque:NO];
[win setBackgroundColor:[NSColor clearColor]];
//or
[win setContentView:myView];
Here, myView is a custom view which will be the background of the window. In order for the window to be transparent, you either have to set the background color to clear or use a custom content view which only draws where it is not transparent. You will probably want to use a floating window so that it stays on top. Be careful not to cover up something important because your window could intercept events intended for something underneath it.