Search code examples
cocoafullscreenosx-mountain-lion

Lion/Mountain Lion window on second monitor to display video or other information like output monitor


I'm trying to write a native OSX app to be a sort of advertisement/multimedia player that will distribute its content to multiple screens around a building. The output monitors are connected to either an iMac or Macbook Pro secondary output and are all the same resolution (1280x720) and are physically connected using good quality cables and a VGA splitter/booster box.

So what i'm trying to set up is the primary screen with the dock and menu bar will have the playlist. The secondary monitor will have the video output - i'm using AVFoundation for video playback and have already done various tests to get a programmable video playing.

I want to have the second monitor just initially have a black screen with an image on it so that when nothing is playing that's what people will see.

I was reading this article here on SO: How to display on dual screens on Mac OS X Lion

However, I can't seem to get it to work for me and there's a number of similar ones with conflicting example code so i'm asking more of a "what am I doing wrong?".

The test application so far is created as a simple Cocoa Application in xcode (non document) and this is the code i'm trying to get to work based on the example link:

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
    // Insert code here to initialize your application
    NSUInteger c = [[NSScreen screens] count];
    NSLog(@"Number of screens = %li", (unsigned long)c);
    if ([[NSScreen screens] count] > 1) {
        NSLog(@"Attempting to setup second screen");
        NSScreen *screen = [[NSScreen screens] objectAtIndex:1];
        NSRect fs = [screen frame];
        NSLog(@"Second screen frame = x=%f, y=%f, w=%f, h=%f", fs.origin.x, fs.origin.y, fs.size.width, fs.size.height);
        NSRect ss = fs;
        NSWindow *win2 = [[NSWindow alloc] initWithContentRect:ss
                                                     styleMask:NSBorderlessWindowMask
                                                       backing:NSBackingStoreBuffered
                                                         defer:YES];

        [win2 setBackgroundColor:[NSColor blackColor]];
        [win2 setLevel:NSMainMenuWindowLevel+1];
        [win2 setOpaque:YES];
        [win2 setHidesOnDeactivate:YES];

        NSOpenGLPixelFormatAttribute attrs[] =
        {
            NSOpenGLPFADoubleBuffer,
            0
        };
        NSOpenGLPixelFormat *pixelFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes:attrs];

        NSRect viewRect = NSMakeRect(0.0, 0.0, fs.size.width, fs.size.height);
        NSOpenGLView *fullScreenView = [[NSOpenGLView alloc] initWithFrame:viewRect pixelFormat: pixelFormat];

        [win2 setContentView:fullScreenView];
        [win2 makeKeyAndOrderFront:self];

        NSButton *testButton = [[NSButton alloc] initWithFrame: NSMakeRect(50.0f, 50.0f, 100.0f, 50.0f)];
        [testButton setTarget:self];
        [testButton setAction:@selector(closeExternalWindow)];
        [fullScreenView addSubview:testButton];
        NSLog(@"Should have a black window with a button on it on monitor 2");
    }

}

I have added OpenGL.framework to the project and included in my .h file.

On running, all that happens is the primary window appears on the main screen and nothing changes on the secondary. What i'm expecting in this test is a black screen with a single button on it on the secondary and the primary window to appear on the main screen.

Really appreciate understanding what i'm not understanding.

Thanks


Solution

  • I've managed to get it working. It wasn't obvious at first but what I saw was the screen would clicker momentarily and then go back to the desktop or whatever was underneath it.

    The issue is in the code sample above which creates the 2nd window - in the snippet, i'm declaring

    NSWindow *win2 = [[NSWindow alloc] initWithContentRect:ss
                                                         styleMask:NSBorderlessWindowMask
                                                           backing:NSBackingStoreBuffered
                                                             defer:YES];
    

    The issue is that it's being declared locally and i'm using ARC so as soon as the execution of the current function comes to an end, win2 disappears from memory and removes itself from the screen.

    This was fixed by putting an instance variable in the .h file

    NSWindow *win2;
    

    and then removing NSWindow from the alloc/init line.

    Although I thought i'd read the documentation thoroughly enough what I forget is that Apple remind people to read fundamental documentation so they don't have to keep repeating themselves - delve a bit deeper into the concepts of windows etc and it's all written there.

    I now have a window on my second monitor - ok, as an NSBorderlessWindowMask type I can't place controls on it but I can add an NSImageView to it - test completed and now onto the next phase for this application.