FAQFAQ   SearchSearch   MemberlistMemberlist   UsergroupsUsergroups   RegisterRegister 
 ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

Derelict on Mac OS X - another solution
Goto page Previous  1, 2, 3, 4, 5, 6  Next
 
Post new topic   Reply to topic     Forum Index -> Derelict
View previous topic :: View next topic  
Author Message
hugues



Joined: 09 Aug 2007
Posts: 20
Location: Brussels, Belgium

PostPosted: Sat Apr 05, 2008 4:38 pm    Post subject: SDL with Derelict on Mac OS X: not completely resolved yet Reply with quote

DerelictGL now compiles correctly on Mac OS X. I fixed the DerelictGLContext-related issues in revision 288.

Regarding the SDL_main problem, an easy and clean solution hasn't been found yet, though.

With Anders' code, the app needs to be statically linked to libSDLmain.a and linked to the AppKit framework (which defines the symbols used in libSDLmain.a and is linked to the Objective-C runtime). It seems to work fine, but quitting the app through the Quit command of the application menu causes a segfault. This doesn't happen when linking normally to SDL (i.e. without using Derelict).

Regarding Crispy's method, I wasn't able to simply link cocoabits.o to my test app. The linker complains about the following undefined symbols:
.objc_class_name_NSAutoreleasePool
_objc_msgSend
So I had to link at least to the Foundation framework (which defines NSAutoreleasePool and is linked to the Objective-C runtime) for it to work. Crispy, did you have the same situation? If linking to the Foundation framework is required, we can as well link to the AppKit framework, which defines NSApplicationLoad(), and the dlopen() code would not be needed. Little info: actually we don't need Cocoa here. The AppKit framework is enough, but it works also if we link to Cocoa because Cocoa is linked to AppKit.

Of course, since we are using Derelict and like dynamic loading, it will be better if we didn't had to link at all to any library. To do that, we need to put the code from cocoabits.m into a dynamic library that we link to the AppKit framework. We can then load this library, find the symbols and call the functions. We can as well put the NSApplicationLoad() call into it. Let's call this nsappbits.m:
Code:
#import <Foundation/NSAutoreleasePool.h>
#import <AppKit/NSApplication.h>

NSAutoreleasePool *globalPool = nil;

void nsAppInit()
{
    NSApplicationLoad();
    globalPool = [[NSAutoreleasePool alloc] init];
}

void nsAppCleanup()
{
   [globalPool release];
}

We build this with:
Code:
gcc -o nsappbits.dylib nsappbits.m -dynamiclib -framework AppKit

In our app, we define something like this:
Code:
import derelict.util.loader;

class Platform
{
    static this()
    {
        platformLib = null;
    }

    static ~this()
    {
        cleanup();
    }

    static void init()
    {
        version (darwin)
        {
            if (platformLib is null)
            {
                platformLib = Derelict_LoadSharedLib(PLATFORM_LIB_NAME);
                void function() nsAppInit = cast(void function()) Derelict_GetProc(platformLib, "nsAppInit");
                nsAppInit();
            }
        }
    }

    static void cleanup()
    {
        version (darwin)
        {
            if (platformLib !is null)
            {
                void function() nsAppCleanup = cast(void function()) Derelict_GetProc(platformLib, "nsAppCleanup");
                nsAppCleanup();
                Derelict_UnloadSharedLib(platformLib);
                platformLib = null;
            }
        }
    }

    private
    {
        static SharedLib platformLib;

        version (darwin)
        {
            static const char[] PLATFORM_LIB_NAME = "nsappbits.dylib";
        }
    }
}

We can now simply call Platform.init() and Platform.cleanup(). (Of course, all this "static" code is not very thread-safe (not at all, actually Wink) and we need to take care of the correct path of nsappbits.dylib.) This isn't the perfect solution yet, the issues explained in this previous post are still there, but it's at least already usable.

Actually, to solve the issues, we should adapt the code from SDLmain.m to create a dynamic library from which we could call something like nsAppInit() and nsAppCleanup(). Maybe this post from the SDL mailing list can help?
Back to top
View user's profile Send private message
revcompgeek



Joined: 24 Nov 2007
Posts: 27

PostPosted: Sat Apr 05, 2008 5:14 pm    Post subject: Reply with quote

Thank you. Applying what you said to the DMedia tutorials, I got the second one to work, but the third doesn't. My code is:
Code:
module derelict_test;

import std.string;
import std.stdio;
import derelict.sdl.sdl;
import derelict.opengl.gl;
import derelict.opengl.glu;
import platform;


// horizontal and vertical screen resolution
const int   xResolution     = 800;
const int   yResolution     = 600;

// number of bits per pixel used for display. 24 => true color
const int   bitsPerPixel    = 24;

// field of view => the angle our camera will see vertically
const float fov             = 90.f;

// distance of the near clipping plane
const float nearPlane       = .1f;

// distance of the far clipping plane
const float farPlane        = 100.f;



/**
  Setup some basic OpenGL parameters
*/
void setupGL() {

    // switch to the projection mode matrix
    glMatrixMode(GL_PROJECTION);

    // load the identity matrix for projection
    glLoadIdentity();

    // setup a perspective projection matrix
    gluPerspective(fov, cast(float)xResolution / yResolution, nearPlane, farPlane);

    // switch back to the modelview transformation matrix
    glMatrixMode(GL_MODELVIEW);

    // load the identity matrix for modelview
    glLoadIdentity();
}


void init() {
    // initialize SDL, GL and GLU Derelict modules
    DerelictSDL.load();
    writefln("test");
    DerelictGL.load();
    writefln("test 2");
    DerelictGLU.load();
    writefln("test 3");

    // initialize SDL's VIDEO module
    SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER);

    // enable double-buffering
    SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);

    // create our OpenGL window
    SDL_SetVideoMode(xResolution, yResolution, bitsPerPixel, SDL_OPENGL);
    SDL_WM_SetCaption(toStringz("My SDL Window"), null);

    setupGL();
}


// be nice and release all resources
void cleanup() {
    // tell SDL to quit
    SDL_Quit();

    // release GL, GLU and SDL's shared libs
    DerelictGLU.unload();
    DerelictGL.unload();
    DerelictSDL.unload();
}


void drawGLFrame() {
    glBegin(GL_TRIANGLES);
        glColor3f (1,    0,    0);
        glVertex3f(-1,  -1,   -2);

        glColor3f (0,    1,    0);
        glVertex3f(1,   -1,   -2);

        glColor3f (0,    0,    1);
        glVertex3f(0,    1,   -2);
    glEnd();
}


void main() {
    Platform.init();
    writefln("Test 1");
    init();
    writefln("Test 2");
    scope(exit) Platform.cleanup();
    scope(exit) cleanup();      // when we exit, perform cleanup

    mainLoop:
    while (true) {
        SDL_Event event;

        // handle all SDL events that we might've received in this loop iteration
        while (SDL_PollEvent(&event)) {
            switch (event.type) {
                // user has clicked on the window's close button
                case SDL_QUIT:
                    break mainLoop;

                // by default, we do nothing => break from the switch
                default:
                    break;
            }
        }


        // clear the screen. by default it clears to black
        glClear(GL_COLOR_BUFFER_BIT);

        // draw our stuff =)
        drawGLFrame();

        // swap the buffers, making our backbuffer the visible one
        SDL_GL_SwapBuffers();
    }
}


The output I get is:
Code:
Test 1
test
test 2
Error: Invalid library name


DerelictGLU.load() is not working. I am building with dsss.
Back to top
View user's profile Send private message
hugues



Joined: 09 Aug 2007
Posts: 20
Location: Brussels, Belgium

PostPosted: Sat Apr 05, 2008 5:36 pm    Post subject: DerelictGLU not working on Mac OS X Reply with quote

Indeed, the library paths for DerelictGLU for Mac OS X are empty. Actually, since the OpenGL framework contains libGLU.dylib, the GLU library is already loaded if the former is loaded and the GLU symbols should be loaded from it.

aldacron, is there a way to share dynamic libraries between the different Derelict parts? Or should we keep a list of already loaded libraries and not call dlopen() for those already in the list?
Back to top
View user's profile Send private message
aldacron



Joined: 05 May 2004
Posts: 1322
Location: Seoul, South Korea

PostPosted: Sun Apr 06, 2008 2:32 am    Post subject: Reply with quote

When I first realized this problem recently, I figured we would need to share the OpenGL lib handle with the GLU module. When I read this post just now, it dawned on me that this isn't necessary. Calling dlopen multiple times on the same library isn't an issue. I just consulted the spec to be sure:

Quote:
Only a single copy of an object file is brought into the address space, even if dlopen() is invoked multiple times in reference to the file, and even if different pathnames are used to reference the file.


So it's safe to add the same paths to the glu module that are used by the opengl module. I'll do that right now and get it into the trunk.
_________________
The One With D | The One With Aldacron | D Bits
Back to top
View user's profile Send private message Send e-mail
hugues



Joined: 09 Aug 2007
Posts: 20
Location: Brussels, Belgium

PostPosted: Sun Apr 06, 2008 3:40 am    Post subject: Sharing OpenGL handle between GL and GLU on Mac OS X Reply with quote

aldacron wrote:
Calling dlopen multiple times on the same library isn't an issue. I just consulted the spec to be sure:

Quote:
Only a single copy of an object file is brought into the address space, even if dlopen() is invoked multiple times in reference to the file, and even if different pathnames are used to reference the file.


So it's safe to add the same paths to the glu module that are used by the opengl module.
Good catch Wink. The dlclose() man on Mac OS X also states:
Quote:
dlclose() releases a reference to the dynamic library or bundle referenced by handle. If the reference count drops to 0, the bundle is removed from the address space, and handle is rendered invalid.
So it's also safe to call dlclose() multiple times on the same handle.
Back to top
View user's profile Send private message
afb



Joined: 26 Jan 2005
Posts: 137
Location: Sweden

PostPosted: Fri Apr 18, 2008 12:46 am    Post subject: Re: Derelict on Mac OS X - another solution Reply with quote

aldacron wrote:
hugues wrote:

I noticed that the first path in each header is now ./Frameworks/LIBNAME.framework/LIBNAME. Shouldn't it be ../Frameworks/LIBNAME.framework/LIBNAME (with two dots), like Crispy suggests?


That's a copy-pasted typo. Thanks for catching it.


Actually it should be @executable_path/../Frameworks to make it search in the Frameworks directory of the current application bundle.
Back to top
View user's profile Send private message
afb



Joined: 26 Jan 2005
Posts: 137
Location: Sweden

PostPosted: Fri Apr 18, 2008 12:55 am    Post subject: Re: SDLmain on Mac OS X Reply with quote

hugues wrote:
Indeed, I found your page thanks to the other post that aldacron refers to. That is great for programs that want to use SDL and OpenGL by linking normally to the libraries (for users who don't understand how great is Derelict Wink). Your page was a little hard to find. Is there a central place where all D headers to popular libraries can be found? It would be great if there was a clear link to such a place on the Digital Mars "D Links" page.


Like you say, "everyone" is using Derelict instead.

At one point in time, http://d-programming-language.org/ was suggested as an entry point to the D language and not just the reference compiler implementation - but then it died. And since everyone is using DMD and Bud and Derelict and DWT and so on, there's no interest in pushing any cross-platform alternatives. At least that is my personal impression...

Then again, now with D2 nobody is using D either.
Back to top
View user's profile Send private message
hugues



Joined: 09 Aug 2007
Posts: 20
Location: Brussels, Belgium

PostPosted: Tue Apr 22, 2008 2:54 pm    Post subject: Re: Derelict on Mac OS X - another solution Reply with quote

afb wrote:
Actually it should be @executable_path/../Frameworks to make it search in the Frameworks directory of the current application bundle.
That's right, indeed (see Mach-O Programming Topics: Loading Code at Runtime). This of course only works for applications inside a bundle, with frameworks correctly built as "Private Frameworks", as explained on that page. It is also possible to get the bundle path (or the executable path) with the Core Foundation functions CFBundleGetMainBundle(), CFBundleCopyBundleURL()/CFBundleCopyExecutableURL() and CFURLCopyFileSystemPath(), that work even for unbundled applications. The question is: should we support unbundled applications (to allow for small applications with all needed dynamic libraries as .dylib in the same directory as the executable)?

Meanwhile, we should already correct the paths as you suggest.
Back to top
View user's profile Send private message
doob



Joined: 06 Jan 2007
Posts: 367

PostPosted: Wed Sep 17, 2008 7:59 am    Post subject: Reply with quote

If I understand this topic correctly (and if it hasn't already been solved) the problem (or at least one of them) is that we don't want an extra static/shared library on osx. After I've been doing the dwt-cocoa port for a while I've got insight in how Objective-C (objc) works and it's possible to do what's needed without an extra library written in objc.

For example, looking at the first post in this topic:
Code:
void createAutoreleasePool() {
   globalPool = [[NSAutoreleasePool alloc] init];
}

The above objc code is translated into something like this (written in D):
Code:
void createAutoreleasePool() {
   char* alloc = sel_registerName("alloc");
   char* init = sel_registerName("init");

   Class c = objc_getClass("NSAutoreleasePool");
   id obj = objc_msgSend(cast(id)c, alloc);
   globalPool = objc_msgSend(obj, init);
}

"objc_msgSend", "objc_getClass" and "sel_registerName" are regular C functions that you can bind and "id" and "Class" are types that you can bind, all objc classes can be casted to the type "id". I already have all the bindings for the necessary objc functions and types (they just need to be extracted from the project I'm working on). If you want I can start to help to get this to work.
Back to top
View user's profile Send private message
aldacron



Joined: 05 May 2004
Posts: 1322
Location: Seoul, South Korea

PostPosted: Wed Sep 17, 2008 9:40 pm    Post subject: Reply with quote

doob wrote:
If I understand this topic correctly (and if it hasn't already been solved) the problem (or at least one of them) is that we don't want an extra static/shared library on osx. After I've been doing the dwt-cocoa port for a while I've got insight in how Objective-C (objc) works and it's possible to do what's needed without an extra library written in objc.

For example, looking at the first post in this topic:
Code:
void createAutoreleasePool() {
   globalPool = [[NSAutoreleasePool alloc] init];
}

The above objc code is translated into something like this (written in D):
Code:
void createAutoreleasePool() {
   char* alloc = sel_registerName("alloc");
   char* init = sel_registerName("init");

   Class c = objc_getClass("NSAutoreleasePool");
   id obj = objc_msgSend(cast(id)c, alloc);
   globalPool = objc_msgSend(obj, init);
}

"objc_msgSend", "objc_getClass" and "sel_registerName" are regular C functions that you can bind and "id" and "Class" are types that you can bind, all objc classes can be casted to the type "id". I already have all the bindings for the necessary objc functions and types (they just need to be extracted from the project I'm working on). If you want I can start to help to get this to work.


If you can get this to work, that would be great. Ideally, this would happen without any user intervention, such as in the loader function of the SDL struct, after the shared library has finished loading but before the function returns. Then any cleanup code would go in the static module destructor.
_________________
The One With D | The One With Aldacron | D Bits
Back to top
View user's profile Send private message Send e-mail
hugues



Joined: 09 Aug 2007
Posts: 20
Location: Brussels, Belgium

PostPosted: Thu Sep 18, 2008 2:22 pm    Post subject: Objective-C in D? Reply with quote

It’s funny that we have activity on this topic now. I didn't work on the problem since my last post until last week, where I played with SDLmain.m to try to be able to use it without the main() redefinition hack. I found a way to do it, as explained in this post. So now I have exactly the same behavior as using SDL in C with SDLmain.a: the app opens up correctly even when not in a bundle and launched from the command line, the Quit menu function works, etc.

I wanted to make a dynamic library only linked to Cocoa that would contain the SDLmain code. The problem is that the SDLmain code calls SDL functions (notably SDL_PushEvent() to post an SDL_QUIT event when the application receives a terminate message) and so must also be linked to SDL, or to some other code that could find the symbol at runtime. Either way is messy regarding derelict's principles. The idea of creating a dynamic library is finally not a good idea.

What to do now? The Cocoa initialization code has to be somewhere. It could be included in the SDL lib, what would have solved our problem a while ago, but it is not, at least not yet. We could modify SDL to include it, but this would mean that derelict wouldn’t be able to work with the official SDL distribution on the Mac, which is a bad idea.

The best solution (for now) seems to be to have this code included directly into derelict. It is Objective-C, derelict is D and should be able to be used by directly importing the code, without needing a separate build.

doob, your post is opening new hopes Wink. Do you think we could re-code SDLmain in derelict with the method you suggest?

This is the code to be included:
http://www.libsdl.org/cgi/viewvc.cgi/tags/SDL/release-1.2.13/src/main/macosx/SDLMain.m?revision=3510&view=markup

Hugues De Keyzer
Back to top
View user's profile Send private message
doob



Joined: 06 Jan 2007
Posts: 367

PostPosted: Fri Sep 19, 2008 4:39 am    Post subject: Re: Objective-C in D? Reply with quote

hugues wrote:
doob, your post is opening new hopes Wink. Do you think we could re-code SDLmain in derelict with the method you suggest?

This is the code to be included:
http://www.libsdl.org/cgi/viewvc.cgi/tags/SDL/release-1.2.13/src/main/macosx/SDLMain.m?revision=3510&view=markup

Hugues De Keyzer


Yes I think it's possible, you can do everything you can do in objc with the runtime functions I mentioned. It's a little tricky to create a new class that extends a objc class this way but it's possible. I will start to work on this as soon as I have time to.
Back to top
View user's profile Send private message
hugues



Joined: 09 Aug 2007
Posts: 20
Location: Brussels, Belgium

PostPosted: Fri Sep 19, 2008 10:29 am    Post subject: Re: Objective-C in D? Reply with quote

doob wrote:
Yes I think it's possible, you can do everything you can do in objc with the runtime functions I mentioned. It's a little tricky to create a new class that extends a objc class this way but it's possible. I will start to work on this as soon as I have time to.


That's great! If you need any help with this, I'm available.
Back to top
View user's profile Send private message
doob



Joined: 06 Jan 2007
Posts: 367

PostPosted: Sat Sep 20, 2008 4:48 am    Post subject: Re: Objective-C in D? Reply with quote

hugues wrote:
That's great! If you need any help with this, I'm available.


I could use a sample code to test with
Back to top
View user's profile Send private message
hugues



Joined: 09 Aug 2007
Posts: 20
Location: Brussels, Belgium

PostPosted: Sun Sep 21, 2008 3:30 pm    Post subject: Re: Objective-C in D? Reply with quote

doob wrote:
I could use a sample code to test with

Here is a simple test program.
Code:
import derelict.opengl.gl;
import derelict.util.exception;
import derelict.sdl.sdl;
import std.stdio;
import std.string;

int main(char[][] arg)
{
    DerelictSDL.load();

    writefln("Initializing SDL…");

    if (SDL_Init(SDL_INIT_VIDEO) == -1)
    {
        throw new Exception(format("Error initializing SDL: %s", toString(SDL_GetError())));
    }

    writefln("Loading OpenGL library…");
    DerelictGL.load();
    writefln("Loaded OpenGL library.");

    GLVersion glv;

    try
    {
        SDL_Surface* screen;

        screen = SDL_SetVideoMode(1024, 576, 24, SDL_OPENGL);

        if (!screen)
        {
            throw new Exception(format("Unable to set video mode: %s", toString(SDL_GetError())));
        }

        SDL_WM_SetCaption(toStringz("Derelict Test"), null);

        try
        {
            writefln("Checking for available OpenGL version…");
            glv = DerelictGL.availableVersion();
            writefln("Check for available OpenGL version complete.");
        }
        catch (SharedLibProcLoadException e)
        {
            writefln("Problem loading OpenGL dynamic library: %s", e.toString());
        }

        writefln("Loaded OpenGL version: %s", DerelictGL.versionString(glv));

        glClearColor(0.0, 0.125, 0.25, 0);

        bool run = true;

        do
        {
            SDL_Event event;

            while (SDL_PollEvent(&event))
            {
                switch (event.type)
                {
                    case SDL_KEYDOWN:
                    {
                        switch (event.key.keysym.sym)
                        {
                            case SDLK_ESCAPE:
                            {
                                writefln("Escape pressed");
                                run = false;
                                break;
                            }
                            default:
                            {
                                break;
                            }
                        }

                        break;
                    }
                    case SDL_QUIT:
                    {
                        writefln("Quit event");
                        run = false;
                        break;
                    }
                    default:
                    {
                    }
                }

            }

            glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

            SDL_GL_SwapBuffers();
        }
        while (run)
    }
    finally
    {
        writefln("Closing SDL…");
        SDL_Quit();
    }

    writefln("Execution successful.");

    DerelictGL.unload();

    DerelictSDL.unload();

    return 0;
}

This should show a dark blue window named "Derelict Test" that you can quit by pressing Esc. You should also be able to quit by choosing Quit from the app menu. Hide should also work.

Your code should be included in DerelictSDL.load(). To load the Cocoa framework and get function pointers, you should do something like this:
Code:
import derelict.util.loader;

SharedLib cocoa = Derelict_LoadSharedLib("Cocoa.framework/Cocoa");
char* function(char*) sel_registerName = cast(char* function(char*)) Derelict_GetProc(cocoa, "sel_registerName");

Hope this helps. If you need more info, don't hesitate to ask.

Hugues De Keyzer
Back to top
View user's profile Send private message
Display posts from previous:   
Post new topic   Reply to topic     Forum Index -> Derelict All times are GMT - 6 Hours
Goto page Previous  1, 2, 3, 4, 5, 6  Next
Page 2 of 6

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2005 phpBB Group