OSX xscreensaver port

I've got a tricky problem doing the OSX xscreensaver port, and I'm looking for suggestions. Basically, the problem is that the Mac screen saver framework runs every saver in the same address space, and so, when you have two screen savers that share some common global names, they stomp on each other. I'm not sure how to fix this...

So here's the rough structure:

  1. qix.c defines draw_qix(), etc.
  2. deco.c defines draw_deco(), etc.
  3. XScreenSaverView.m defines a subclass of ScreenSaverView that invokes the appropriate draw_ routine.
  4. Qix.saver is built from qix.o and XScreenSaverView.o.
  5. Deco.saver is built from deco.o and XScreenSaverView.o.

Now, step 3 up there is the tricky bit. How do I find the appropriate draw routine?

My first attempt was, through the magic of macros, to have both qix.o and attraction.o define the same global variable, xscreensaver_function_table. That works when the screen saver activates for real, but doesn't work when switching savers in System Preferences: what happens is, the first bundle loaded wins. So if Qix is the selected saver, and you switch to Deco, you still get Qix's version of xscreensaver_function_table, and Deco doesn't run.

So then I thought, well, if the code in XScreenSaverView.m knew the name of the saver it was defining, then I could have it look up the symbol by name: it could look for qix_function_table or deco_function_table as appropriate, and both could co-exist in the same address space. CFBundleGetDataPointerForName() seems to be made for this: given a bundle and a string, it gives you the address of the variable of that name.

So, I have two instances of XScreenSaverView, and I want to know which bundle they were instantiated from. [NSBundle bundleForClass:[self class]] tells me which bundle defined the class, which is the first one loaded (and both instances get the same answer, and malfunction in the same familiar way).

The only idea I have at this point is to define a custom subclass of XScreenSaverView for each saver, that looked like:

    @interface XScreenSaverQixView : XScreenSaverView {}
    @implementation XScreenSaverQixView
    - (NSString *) whoami { return @"Qix"; }
    ...or, more directly:
    - (struct table *) table { return qix_function_table; }

But that's annoying because it means auto-generating 200+ source files, which just kind of rubs me the wrong way.

Any other suggestions?

Tags: , , , ,

24 Responses:

  1. fdaapproved says:

    Heh, you could construct that class on the fly,

    void __attribute__((constructor)) build_my_class(void) {
    /* autogenerate bundle's principal class here using objc_* API */

    It seems like there's GOT to be a better way though.

  2. strangehours says:

    Without knowing how the OSX version is laid out (is the sourcecode available yet?)...

    If XScreenSaverView.m is quick to compile, could you - rather than linking with the .o - for each saver:

    #define SAVER_NAME Qix
    #include "XScreenSaverView.m"

    and then have XScreenSaverView.m define a class based on SAVER_NAME? You could also set up that #define in xcode based upon the target, if you have a target per saver.

    • jwz says:

      Well, I can't include XScreenSaverView.m in qix.c, because it's C, not ObjC. But, I could set up the Qix project to build its own version of the XScreenSaverView.o file, I guess. Still kind of a pain in the butt, especially since I haven't yet figured out how to automate the creation of XCode targets via AppleScript or whatever...

      • strangehours says:

        Could you create a qix.m file that included XScreenSaverView.m and qix.c? I guess you're back to the problem of lots of autogenerated files (although maybe slightlly cleaner/more automatable this way).

  3. ch says:

    could you use the dynamic linking to control the namespace (i.e. dlopen(..., RTLD_LOCAL))?

    i don't know OSX, so I fear I might be increasing the noise floor here, but this trick works on Linux with glibc and ELF objects.

  4. nerdware says:

    You have a language semantics problem. It looks like C can't do what you want. The C macro language can't help you, either. Maybe another macro processor, like M4, could do it.

    I'd use a single custom subclass of XScreenSaverView and make that dispatch to an interpreter for a more dynamic language. My choice would be Scheme, but the language itself is just a detail, as long as it has the required semantics.

    Whatever method you use, it shouldn't involve generating 200 source files. 200 lines of macros (M4, Scheme, whatever) should be sufficient.

      • nerdware says:

        My prefered script interpreter is Guile, but there no doubt other options. The language, esp the macros, should be more than sufficient to solve your problem.

        Alternately, you could write a specialised interpreter for your problem and create each "class" with a single function call. All it needs to know is what makes each instance distinct from the others. Classic data-driven programming like this is possible even in C.

        • cananian says:

          Wow. You managed to not solve the problem *and* gratuitously plug your favorite programming language. Bonus points because your favorite programming language is "functional"!

          Anyone have a way to solve the problem which doesn't involve embedding lisp?

          • nerdware says:

            In what way does Data-Driven Programming fail to solve the problem? Code is also data. If a workable solution (generating 200 files) is too ugly, don't use files. I gave two examples of doing this without generating files, both of them using macro languages.

            In what way did I plug any specific language? I thought I was plugging macros! Both M4 and Scheme have very powerful macro languages, so I gave them as examples. Guile is a Scheme implementation that is designed to embedded inside applications. I even suggested that there are alternatives. Should I have listed every example I know of? Would that help? I doubt it.

            So I gave a few examples of macro languages that I'm familiar with. You didn't explain how they'd both fail to generate code. M4 can certainly generate C code, just like any other text. One modest macro and 200 lines of macro invocations could generate a single C source file. How would this fail to work? There's no code that you can write in Guile that can't be generated by a Guile macro. Can you explain how Guile would fail?

            And finally, I mentioned that you can even do data-driven programming in C. Once you understand how an interpreter for a Turing-Equivalent language works, writing an interpreter for a non-Turing-Equivalent problem is trivial. Scheme isn't the only Turing-Equivalent language with embeddable interpreters, but you didn't mention that. You could've asked, "Why not use Python?" Well, if Python has a more convenient/powerful/elegant macro language than C (if it is easier to use to solve JWZ's problem than C's macro language), then Python will do the job nicely.

            The moderator of comp.compilers frequent recommends TCL, but I'm not sure he's advocating it as much as giving a presciptive answer (paraphrased: "Don't write your own interpreter, use an existing one - like TCL"). Like John Levine, I gave a presciptive answer. That answer used Guile because 1) it's an embeddable interpreter 2) it has a Turing-Equivalent macro language, which happens to be Scheme. If Python's macro language is Python,
            then that would do the job just as well, perhaps better if you happen to be familiar with Python.

            At no point did I say, "You must use M4 or Scheme."

      • driusan says:

        I think that's his way of saying "I don't know the answer to your question, but I'll use the opportunity to pretend I know what I'm talking about and that my favourite (language/editor/linux distribution/paradigm) du jour will do it for you." Either that, or a parody.

        Sweet jesus, please let it be a parody. I mean, it took 1 sentence for him to use the phrase "language semantics problem," followed by the claim that C can't do what you want. It has to be, right?

        (*I* don't know the answer to your question, either, but I'm not going to pretend to.)

        • nerdware says:

          Where did I say that "C can't do what you want"? It looks like JWZ is saying that he can do it in C, but the way he'd have to do it would be too ugly. Generating 200 files is certainly ugly.

          I made it very clear that you can do Data-Driven Programming in C. Of course you can. That's basic CS. C is a Turing-Equivalent language, so of course you can do it in C. I don't understand why JWZ thinks he can't do it with the C macro language, but if that's too hard or too ugly, he could use an alternative macro language like M4. I don't see why he can't generate a single C file with 200 definitions in it, but I'm guessing that he has a good reason. Perhaps you know better and can explain that reason to me.

          Anyway, he asked a question, so I gave an answer. I didn't say he needed it. I'm simply assuming he wants an answer because he asked the question.

          • beerfrick says:

            > Where did I say that "C can't do what you want"?

            "You have a language semantics problem. It looks like C can't do what you want."

            right there

            • nerdware says:

              JWZ said, "But that's annoying because it means auto-generating 200+ source files, which just kind of rubs me the wrong way."

              So I said, "It looks like C can't do what you want."

              Maybe JWZ can explain why he doesn't like auto-generating 200 C files, but my point is that you don't need to do that if you use a suitable macro language. Perhaps the C macro language rubs JWZ "the wrong way", in which case I suggest that he try to find a macro language without that problem.

              "Any other suggestions?" -- JWZ

              So I suggested a few alternatives to get him started. ;) He asked for suggestions, and I gave him a few. He doesn't seem to have a problem with that - why do you?

      • sapp3r says:

        that portion of the comments reads like a thread from usenet, circa the late eighties. or more recently on slashdot, as written by people who are still pissed that no one who'd seen the usenet thread would listen to them.

        although, it does sort of amuse me to see people giving jwz advice about a topic he's reasonably (sic) familiar with. (i remember seeing a comment about jwz made by someone else a number of years ago: "i knew jamie back when he was still just another pissed off lisp programmer" ... .)

  5. bitjuggler says:

    ...fork()? Or is that bad in this context for some reason?

  6. taffer says:

    I think you're looking for NSLookupSymbolInImage, which I found in the Mac OS X ABI Dynamic Loader Reference doc on Apple's dev site.

    • jwz says:

      I think that does what CFBundleGetDataPointerForName() does; the problem isn't looking up a symbol, it's figuring out which symbol to look up. "Who am I".

  7. mark242 says:

    Doesn't C have the equivalent of an eval() function?