Screensaver issues on Mac OS using SDL >1.2.12

Hi,

I ported an OpenGL application from GLUT to SDL just recently which is
also meant to be used as a screensaver on Mac OS X. This application is
launched by a 3rd party software that successfully launches the old GLUT
version but fails to do so with the new SDL port.

The symptom I experience so far is that the launcher app starts the SDL
graphics app which in turn seems to cause the launcher to stop and
restart it again. This seems to happen as soon as the SDL app enters its
event loop (running two SDL timers, polling with SDL_WaitEvent).

Another finding that points towards an SDL issue is this: running my SDL
app in standalone mode causes other Mac OS screensavers (like “Flurry”)
to not being launched at all. As soon as you stop the app, screensavers
are launched again after the usual timeout.

So far I tried the ‘–enable-screensaver=“no”’ configure switch as well
as the SDL_VIDEO_ALLOW_SCREENSAVER environment variable (at runtime as
well as at compile time using SDL_putenv). Unfortunately none of this
mitigated the issue described above.

Has anyone witnessed similar problems using the svn 1.2 branch
(>1.2.12)? Does the “allow screensaver” feature actually work right now
on Mac OS (it works for me on windows)? Any idea what could be wrong or
what other things I could try?

On a side note: I also tried to use “AppStartSaver” (not open source) to
start my SDL app and it seems to work just fine. Can anyone confirm that
"AppStartSaver" wouldn’t work if SDL would still somehow emit its
no-screensaver-signals?

Also, is there any SDL-based screensaver implementation one could use as
a reference for Mac OS?

Lots of questions so thanks in advance,

Oliver

Erm, there’s a typo: I mean I tried ‘–enable-screensaver=“yes”’

Apologies,

Oliver

Oliver Bock wrote:> Hi,

I ported an OpenGL application from GLUT to SDL just recently which is
also meant to be used as a screensaver on Mac OS X. This application is
launched by a 3rd party software that successfully launches the old GLUT
version but fails to do so with the new SDL port.

The symptom I experience so far is that the launcher app starts the SDL
graphics app which in turn seems to cause the launcher to stop and
restart it again. This seems to happen as soon as the SDL app enters its
event loop (running two SDL timers, polling with SDL_WaitEvent).

Another finding that points towards an SDL issue is this: running my SDL
app in standalone mode causes other Mac OS screensavers (like “Flurry”)
to not being launched at all. As soon as you stop the app, screensavers
are launched again after the usual timeout.

So far I tried the ‘–enable-screensaver=“no”’ configure switch as well
as the SDL_VIDEO_ALLOW_SCREENSAVER environment variable (at runtime as
well as at compile time using SDL_putenv). Unfortunately none of this
mitigated the issue described above.

Has anyone witnessed similar problems using the svn 1.2 branch
(>1.2.12)? Does the “allow screensaver” feature actually work right now
on Mac OS (it works for me on windows)? Any idea what could be wrong or
what other things I could try?

On a side note: I also tried to use “AppStartSaver” (not open source) to
start my SDL app and it seems to work just fine. Can anyone confirm that
"AppStartSaver" wouldn’t work if SDL would still somehow emit its
no-screensaver-signals?

Also, is there any SDL-based screensaver implementation one could use as
a reference for Mac OS?

Lots of questions so thanks in advance,

Oliver


SDL mailing list
SDL at lists.libsdl.org
http://lists.libsdl.org/listinfo.cgi/sdl-libsdl.org

I ported an OpenGL application from GLUT to SDL just recently which is
also meant to be used as a screensaver on Mac OS X. This application is
launched by a 3rd party software that successfully launches the old GLUT
version but fails to do so with the new SDL port.

I had to do this for Feeding Frenzy (which installs a copy of itself as
a screensaver)…on Windows, it’s just the game .exe run with a
commandline switch; on Mac, there was a LOT of tapdancing involved.

Here’s what I did to make this work. I’m not sure it was the best
approach, but it was the only way I could find to make it happen. Maybe
some of this will be useful.

I think that, ultimately, there was only a very small portion of SDL
that needed to be used in the screen saver. The game used all sorts of
things, but I suspect that the screen saver really only used
SDL_GetTicks(). I never call SDL_SetVideoMode() for the screensaver. You
might be better off #ifdef’ing the SDL code if it’s just a few small
things, like timers; on deadline, I figured I’d risk leaving the SDL
calls in, rather than try to weed them all out.

So, uh, here’s the basic rundown:

  • You can’t link to SDL directly. I had it shipped with the game
    (@executable_path/libSDL.dylib), but “@executable_path” is meaningless
    for screensavers, since your saver is loaded into someone else’s process
    with a different path. I ended up using dlopen() on the SDL library at
    startup and picking out the symbols I needed, and then calling SDL_Init().

  • Your mainline doesn’t run. You have to supply an (Objective-C!)
    subclass of ScreenSaverView, which will be a little glue between the Mac
    interfaces and your own screensaver, which will be mostly unchanged.
    System Preferences will load your bundle and run the saver in a little
    preview window, and a different process will load it fullscreen as the
    actual screensaver later. This subclass also dictates whether System
    Preferences provides a configuration dialog for the saver, when it
    should redraw, etc.

  • You can use OpenGL, but you shouldn’t use any SDL calls that depend on
    the existance of a valid GL context. You might be able to use the SDL
    window id hack, but I’d avoid it. All of your effort is going to be in
    standard GL calls anyhow, and the GL context is prepared simply enough
    here, so there isn’t really any SDL video things you need.

  • You don’t swap buffers, you just call glFlush() …(apparently.)

  • link the saver with “-framework ScreenSaver -bundle” … I think you
    give it an Info.plist and put it all in “~/Library/Screen Savers” but I
    don’t have those details in front of me.

Here’s a cutdown version of my glue code:

#import <ScreenSaver/ScreenSaver.h>
#import <GL/gl.h>

@interface FeedingFrenzyScreenSaverView : ScreenSaverView
{
// So what do you need to make an OpenGL screen saver? Just an
NSOpenGLView (or subclass thereof)
// So we’ll put one in here.
NSOpenGLView *glView;

 // these were for the options dialog in System Preferences. Ignore.
 IBOutlet id configureSheet;
 IBOutlet id okButton;
 IBOutlet id cancelButton;
 IBOutlet id saverDropDown;
 IBOutlet id nextSaverText;

}

  • (IBAction)hitOK:(id)sender;
  • (IBAction)hitCancel:(id)sender;
  • (IBAction)changedSaver:(id)sender;
    @end

// This overrides NSOpenGLView to make sure the viewport is sane.
@implementation NSOpenGLView (FeedingFrenzyUpdateOverride)

  • (void) update
    {
    NSRect rectView = [self bounds];
    glViewport(0, 0, rectView.size.width, rectView.size.height);
    }
    @end

// this is used to load SDL.
int LoadDylibStubs(void);

@implementation FeedingFrenzyScreenSaverView

// this is your startup function.

  • (id)initWithFrame:(NSRect)frame isPreview:(BOOL)isPreview
    {
    if (!LoadDylibStubs())
    return NULL;

    self = [super initWithFrame:frame isPreview:isPreview];
    if (self)
    {
    // So we modify the setup routines just a little bit to get our
    // new OpenGL screensaver working.

       // Create the new frame
       NSRect newFrame = frame;
       // Slam the origin values
       newFrame.origin.x = 0.0;
       newFrame.origin.y = 0.0;
       // Now alloc and init the view, right from within the screen 
    

saver’s initWithFrame:
glView = [[NSOpenGLView alloc] initWithFrame:newFrame];

     // If the view is valid, we continue
     if(glView)
     {
         // Make sure we autoresize
         [self setAutoresizesSubviews:YES];

         long swapInt = 1;
         [[glView openGLContext] setValues:&swapInt 

forParameter:NSOpenGLCPSwapInterval]; // set to vbl sync

         // We make it a subview of the screensaver view
         [self addSubview:glView];
         [[glView openGLContext] makeCurrentContext];
         [self setAnimationTimeInterval:1/60.0];
     }
     else // Log an error if we fail here
         NSLog(@"Error: OpenGL Screen Saver failed to initialize 

NSOpenGLView!");
}

 // Finally return our newly-initialized self
 return self;

}

// this is called when the screen saver is actually starting (not
// initializing; that’s elsewhere.)

  • (void)startAnimation
    {
    [super startAnimation];
    [[glView openGLContext] makeCurrentContext];
    glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glFlush();

    if (firstView == NULL)
    firstView = self;

    if (firstView == self)
    GatsuScreenSaverInit();
    }

// this is when it’s ending

  • (void)stopAnimation
    {
    [super stopAnimation];
    [[glView openGLContext] makeCurrentContext];
    glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glFlush();
    GatsuScreenSaverShutdown();
    }

  • (void)drawRect:(NSRect)rect
    {
    }

// this is called when it wants you to redraw for the next frame. It’s
// called frequently.

  • (void)animateOneFrame
    {
    [[glView openGLContext] makeCurrentContext];
    GatsuScreenSaverUpdate();
    }

// Make this return NO if you don’t want a configure dialog in System
// Preferences.

  • (BOOL)hasConfigureSheet
    {
    return YES;
    }

  • (NSWindow*)configureSheet
    {
    if (configureSheet != nil)
    return configureSheet;

    // Build your configuration dialog here.

    return configureSheet; // let the user tweak!
    }

  • (IBAction)hitOK:(id)sender // user hit OK button.
    {
    ffSetSaverSelection([saverDropDown indexOfSelectedItem]);
    [NSApp endSheet:configureSheet]; // kill the dialog.
    }

  • (IBAction)hitCancel:(id)sender // user hit Cancel button.
    {
    [NSApp endSheet:configureSheet]; // just kill the dialog.
    }

  • (IBAction)changedSaver:(id)sender
    {
    // no-op.
    }
    @end

–ryan.

  • You can’t link to SDL directly. I had it shipped with the game
    (@executable_path/libSDL.dylib), but “@executable_path” is meaningless for
    screensavers, since your saver is loaded into someone else’s process with a
    different path. I ended up using dlopen() on the SDL library at startup and
    picking out the symbols I needed, and then calling SDL_Init().

FWIW, there’s another way around this in 10.4 and later. Instead of
@executable_path, you can also use @loader_path, which is relative to the
binary actually loading the lib. This allows you to keep dynamically
linked dependencies within the bundle in the same way as for normal apps.

// MartinOn Fri, 12 Sep 2008, Ryan C. Gordon wrote:

FWIW, there’s another way around this in 10.4 and later. Instead of
@executable_path, you can also use @loader_path, which is relative to the
binary actually loading the lib. This allows you to keep dynamically
linked dependencies within the bundle in the same way as for normal apps.

Oh, I had no idea; thanks for that tidbit!

–ryan.

Hi Ryan,

Thanks for your elaborate feedback!

Ryan C. Gordon wrote:

I think that, ultimately, there was only a very small portion of SDL
that needed to be used in the screen saver. The game used all sorts of
things, but I suspect that the screen saver really only used
SDL_GetTicks(). I never call SDL_SetVideoMode() for the screensaver. You
might be better off #ifdef’ing the SDL code if it’s just a few small
things, like timers; on deadline, I figured I’d risk leaving the SDL
calls in, rather than try to weed them all out.

You can use OpenGL, but you shouldn’t use any SDL calls that depend on
the existance of a valid GL context. You might be able to use the SDL
window id hack, but I’d avoid it. All of your effort is going to be in
standard GL calls anyhow, and the GL context is prepared simply enough
here, so there isn’t really any SDL video things you need.

Sorry, I forgot to point out that my application uses SDL to provide an
OpenGL context and to do event handling as well as window management.
All the rest is done in pure OpenGL. Please keep in mind that my SDL
application is just a “normal” windowed app that can be switched to
fullscreen mode. The screensaver itself is a 3rd party app that in turn
launches my SDL application. As far as SDL goes I use these methods:

Initialization:

  • SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER)
  • SDL_VideoModeOK()
  • SDL_GL_SetAttribute()
  • SDL_WM_SetCaption(); used before going to fullscreen (if requested)
  • SDL_ShowCursor(); cursor hidden in fullscreen mode
  • SDL_SetVideoMode(); windowed mode, or fullscreen as “screensaver”

Event handling:

  • SDL_EventState()
  • SDL_AddTimer(); two user events (render event, data update event)
  • SDL_PushEvent(); pushing user events
  • SDL_WaitEvent()

Rendering:

  • SDL_GL_SwapBuffers()

On exit:

  • SDL_FreeSurface()
  • SDL_Quit()
  • You can’t link to SDL directly. I had it shipped with the game
    (@executable_path/libSDL.dylib), but “@executable_path” is meaningless
    for screensavers, since your saver is loaded into someone else’s process
    with a different path. I ended up using dlopen() on the SDL library at
    startup and picking out the symbols I needed, and then calling SDL_Init().

My application is (has to be) linked statically.

  • Your mainline doesn’t run. You have to supply an (Objective-C!)
    subclass of ScreenSaverView, which will be a little glue between the Mac
    interfaces and your own screensaver, which will be mostly unchanged.
    System Preferences will load your bundle and run the saver in a little
    preview window, and a different process will load it fullscreen as the
    actual screensaver later. This subclass also dictates whether System
    Preferences provides a configuration dialog for the saver, when it
    should redraw, etc.

  • link the saver with “-framework ScreenSaver -bundle” … I think you
    give it an Info.plist and put it all in “~/Library/Screen Savers” but
    I don’t have those details in front of me.

As far as I understand both of this applies to the “glue” only, which is
in my case provided by the 3rd party app which just launches the actual
SDL application.

  • You don’t swap buffers, you just call glFlush() …(apparently.)

I do SDL_GL_SwapBuffers() after glFlush() in every render step.

Here’s one thing I noticed in the meantime: when I run my application in
windowed mode (standalone), it doesn’t prevent a given screensaver
like “Flurry” from being launched. This indicates that there’re no
events generated by SDL that could cause the problem I experience.
However, when I run it in fullscreen mode (still standalone), "Flurry"
doesn’t start. I verified that by watching for its task via ssh as it
could have been launched on a lower z-layer than my running app - it
wasn’t.

Do you have any idea what the difference is between windowed and
fullscreen mode? Does SDL behave differently in that case (generating
events?) or is this something that Mac OS notices somehow and prevents
subsequent screensaver launches…? The same code works just fine on
Windows when cross-compiled with MinGW.

Thanks,

Oliver