user1677899
user1677899

Reputation: 91

Xcode with OpenGL: new windows do not automatically become active context

I have a strange problem when I create a Documents Based Xcode project with a NSOpenGLView. The application works fine with a single document, responding to mouse clicks. When you choose File/New to open up a second document a new front window appears on top, but mouse clicks are sent to the previous (background) object. You can move the new window and clicks are still sent to the previous object. However, if you resize the new window or click back and forth with the previous window all works well. So the problem seems to be that the new (front) window does not become the active context. Can you suggest a solution? I have placed the Xcode 4.4 code (44kb) at http://www.mccauslandcenter.sc.edu/CRNL/sw/GLdoc.zip.

  1. Create a new Document based application
  2. Add the MyOpenGLView.m and MyOpenGLView.h files to project
  3. For Targets/BuildPhases, add OpenGL.framework to "Link Binary With Libraries"
  4. For Targets/BuildPhases, add MyOpenGLView.m to "Compile Sources"
  5. In interface builder a.) select Document's Window and in the Attributes inspector turn off "One shot" b.) add a OpenGL View from the Object Library c.) with the Identity inspector, set the Class of newly created NSOpenGLView to MyOpenGLView
  6. Run the project

----START MyOpenGLView.h

#import <Cocoa/Cocoa.h>
#import <GLUT/GLUT.h>

@interface MyOpenGLView : NSOpenGLView
{
    NSTimer* timer; //animation timer
    NSPoint mouseloc;
    NSPoint screenSize;
    int hourglassSize;
    BOOL updateGL;
}

@end

----START MyOpenGLView.m

// document based OpenGL application inspired by http://www.alecjacobson.com/weblog/?p=2110
#import "MyOpenGLView.h"

@implementation MyOpenGLView

-(void)prepareOpenGL
{
    //NSLog(@"preparing");
}

- (void)reshape
{
    //NSLog(@"reshaping");
    NSRect rectView = [self bounds];
    screenSize.x = rectView.size.width;
    screenSize.y = rectView.size.height;
}

void enter2D (int width, int height) //Enter2D = reshapeGL
{
    glDisable(GL_DEPTH_TEST);
    glViewport(0, 0, width, height);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glOrtho(0, width, 0, height,-1,1); //map view to match pixel size, e.g. gluOrtho2D(0, width, 0, height);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    glEnable (GL_BLEND); //allow transparent objects
    glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
}

void drawTriangle (int sz, int left, int bottom)
{
    glLoadIdentity ();
    glTranslatef(left,bottom,0.0f);
    glBegin(GL_TRIANGLES);
    glVertex3f( sz/2, sz, 0.0f);// Top
    glVertex3f(0.0,0.0, 0.0); // Bottom Left
    glVertex3f( sz,0.0f, 0.0f);  // Bottom Right
    glEnd(); // Finished Drawing The Triangle
}

void drawHourGlass (int sz, NSPoint center)
{
    glLoadIdentity ();
    glTranslatef(center.x,center.y,0.0f);
    glBegin(GL_TRIANGLES); //lower triangle
    glVertex3f( 0.0, 0.0, 0.0f); // Top
    glVertex3f(-sz,-sz, 0.0);// Bottom Left
    glVertex3f( sz,-sz, 0.0f);// Bottom Right
    glEnd(); 
    glBegin(GL_TRIANGLES); //upper triangle
    glVertex3f( 0.0, 0.0, 0.0f); // bottom
    glVertex3f(-sz,sz, 0.0);// Top Left
    glVertex3f( sz,sz, 0.0f);// Top Right
    glEnd();
}

-(void)drawRect:(NSRect)rect
{
    updateGL = FALSE;
    //NSLog(@" %f %f",rect.size.width,rect.size.height);
    enter2D(screenSize.x,screenSize.y);
    glClearColor(0.8,0.9,1,1); //background - blue sky
    glClear(GL_COLOR_BUFFER_BIT);
    glColor4f(0.6,0.3,0.1,1.0); //mountains - brown opaque
    drawTriangle(100,10,0);
    drawTriangle(70,80,0);
    glColor4f(0.1,0.1,0.1,0.7); //crosshair - gray, slightly translucent
    drawHourGlass (hourglassSize, mouseloc);
    glFlush();     // Flush all OpenGL calls
}

- (void)mouseDown:(NSEvent *)event
{
    mouseloc = [self convertPoint:[event locationInWindow] fromView:nil];
    updateGL = TRUE;
}

int constrain (int size)
{
    if (size < 5)
        return 5;
    else if (size > 50)
        return 50;
    else
        return size;
}

- (void) magnifyWithEvent:(NSEvent *)event;
{
    if ([event magnification] > 0)
        hourglassSize = hourglassSize + 5;
    else
        hourglassSize = hourglassSize - 5;
    hourglassSize = constrain(hourglassSize);
    updateGL = TRUE;
}

- (void)scrollWheel:(NSEvent *)event
{
    if (event.deltaY < 0)
       hourglassSize = hourglassSize + 5;
    if (event.deltaY > 0)
        hourglassSize = hourglassSize - 5;
    hourglassSize = constrain(hourglassSize);
    updateGL = TRUE;
}

- (void)animationTimer:(NSTimer *)timer
{
    if (updateGL == TRUE)
        [self drawRect:[self bounds]];
}

-(BOOL)acceptsFirstResponder { return YES; }
-(BOOL)becomeFirstResponder { return YES; }
-(BOOL)resignFirstResponder { return YES; }

- (void) awakeFromNib
{
    // [[self window] setAcceptsMouseMovedEvents:YES];
    mouseloc.x = 30;
    mouseloc.y = 40;
    hourglassSize = 30;
    timer = [NSTimer timerWithTimeInterval:(1.0f/60.0f) target:self selector:@selector(animationTimer:) userInfo:nil repeats:YES];
    [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
    // ensure timer fires during resize
    [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSEventTrackingRunLoopMode];
    updateGL = TRUE;
}

@end

Upvotes: 1

Views: 739

Answers (1)

user1677899
user1677899

Reputation: 91

For others who encounter this problem (which is present in some of the older demos Apple distributes), with modern versions of OSX (10.4 and later) you use CVDisplayLink instead of a timer to update OpenGL views.

http://developer.apple.com/library/mac/#qa/qa1385/_index.html

Alec Jacobson outlines this solution here http://www.alecjacobson.com/weblog/?p=2185

Upvotes: 1

Related Questions