Results 1 to 19 of 19

Thread: Clanlib 0.9 on Fltk

  1. #1
    Lesser Knight
    Join Date
    Aug 2008
    Location
    Folsom, California USA
    Posts
    37

    Default Clanlib 0.9 on Fltk

    As I'm looking through 0.9, it seems to me that one of the design intentions is to integrate with the platform windowing system. I see a fair amount of tooth-gnashing going on with issues like message pumps, window management, etc.

    I got to wondering if perhaps Clanlib could be written on top of an existing, mature GUI framework. I have some experience with wxWidgets, but that seemed perhaps a bit too much. I took a look at Fltk, which is billed as a "light" cross-platform GUI toolkit that supports OpenGL. Looking through it, I didn't see anything right away that would seem to rule it out.

    I think perhaps the existing Clanlib 0.9 code could be ported to use Fltk in place of the existing platform-specific DisplayWindowProvider layer with little change to the user API. Going a step further and actually exposing fltk as the windowing API, I think users could then take advantage of its entire widget infrastructure to produce native GUIs while embedding OpenGL draw targets managed by Clanlib as widgets.

    I realize this is a rather radical change of direction that the developers probably won't wish to undertake "officially", at least not without an extensive practical demonstration of compelling benefits. But it is an area I would like to pursue myself, since I am envisioning an application that will blend native GUI with OpenGL rendering. So, I'd appreciate any opinions and insight that more experienced Clanlib developers can offer as to this idea's feasibility and what the likely obstacles might be. I'm still much too ignorant about Clanlib to know if this is a dumb idea.

    Thanks,

    --- Kevin

  2. #2
    ClanLib Developer
    Join Date
    Sep 2006
    Location
    Denmark
    Posts
    554

    Default

    I cannot really comment too much on what the good and bad parts about the fltk library are, since I haven't really studied it. However the reason I chose to write my own GUI toolkit instead of looking for one is partly because I have a personal interest in the challenges involved in writing one, and partly because the other toolkits I do know about have their share of issues.

    The design goals of the GUI toolkit in ClanLib has always been focused on both allowing 'normal' applications to be written in them, as well as allowing you to do a 'game' user interface with it. The way we allow this is by building the concept of a window manager into the GUI toolkit itself, so one WM may create OS windows for each top-level component you create, while another WM may keep all component windows within one single OS window. Since games themselves can write their own window manager, you could place each top-level component on different walls in a 3D world, perhaps post-processing them with shaders and so on.

    Another design difference between clanGUI and most other GUIs I know of is the way we use CSS for our theming. It is not because we are the first to use CSS, but basically I really liked the idea behind the Mozilla XUL project, but when I went looking at the endless list of dependencies I gave up on simply trying to use their toolkit. Other toolkits that embrace web technologies, such as XAML, is also approaching it completely wrong in my opinion. It is not because those toolkits are useless, but clanGUI is kind of my attempt at making a 'next generation' UI toolkit.

    It is true though that one of the weaknesses of clanGUI compared to other toolkits currently is the lack of widgets. We will probably never cover as many widgets as the biggest toolkits out there, but I do think we will get a lot of user contributions here if just we manage to make it easy enough to write them. Right now we haven't even had a single release of 0.9 and Harry still already managed to write a lot of the most common widgets in use today.

    All this said, I personally would not mind clanDisplay being improved in such a way that would allow it to use alternative sources for window creation and message pumping. Such improvements would allow you to hook a clanDisplay window into most conventional toolkits, improving peoples options, but its fairly low on my priority list.

  3. #3
    Lesser Knight
    Join Date
    Aug 2008
    Location
    Folsom, California USA
    Posts
    37

    Default

    I wasn't really thinking of replacing clanGUI; that's still very useful for GL target displays and allows for a lot of fancy effects and customization that a native GUI just can't do. I'm mainly imagining Fltk as you describe in your last paragraph, handling the details of OS-level window management and message pumping with the added bonus of being able to implement native GUIs if that was desirable for a certain application.

    --- Kevin

  4. #4
    Lesser Knight
    Join Date
    Aug 2008
    Location
    Folsom, California USA
    Posts
    37

    Default

    I was thinking a bit about how to make Clanlib support third-party back-ends in an optional way as Magnus mentions. It seems to me that you'd have to be able to provide a factory type of some sort at initialization time. Would CL_SetupDisplay() be the right place to do it?

    Something like this:

    Code:
    CL_SetupDisplay::CL_SetupDisplay();
    CL_SetupDisplay::CL_SetupDisplay(const CL_DisplayProviderFactory & factory);
    The default constructor would use the "default" platform provider factory, while the overload would allow a reference to a different one to be specified.This would allow existing client code to continue working as before while allowing the substitution of a desired provider plugin.

    Of course, existing platform providers would need to provide a factory instance for each provider (Win32, X11, etc.) and set up the necessary conditional compiles to initialize the correct static "default" provider factory and actually use it instead of hard-coding the provider constructors. As a side benefit, it might become possible to completely "privatize" the platform providers, eliminating their headers and helping to prevent the introduction of platform-specific dependencies in other code.

    Does this sound plausible?

    --- Kevin

  5. #5
    ClanLib Developer
    Join Date
    Sep 2006
    Location
    Denmark
    Posts
    554

    Default

    clanDisplay uses CL_Target and the related target providers (CL_DisplayWindowProvider, CL_GraphicContextProvider, etc.) as its means to implement target specific behavior. I think it is probably better to leave CL_SetupDispaly alone and instead add any extra provider and callback support to CL_Target.

    One example of how such a thing could be set up:

    Code:
    CL_SetupDisplay setup_display;
    CL_SetupGL setup_gl;
    CL_Target target = CL_Display::get_current_target();
    target.func_hook_whatever().set(this, &MyClass::on_hook_whatever);

  6. #6
    ClanLib Developer
    Join Date
    Sep 2006
    Location
    Bergen, Norway
    Posts
    588

    Default

    If possible, perhaps wrap this in a CL_SetupFltk class?

  7. #7
    Lesser Knight
    Join Date
    Aug 2008
    Location
    Folsom, California USA
    Posts
    37

    Default

    The thing that concerns me is CL_Display::set_current_target(). Most GUI toolkits are going to assume they're in control of the event pump. That's why I think it needs to be done at setup time for CL_Display, since this choice will affect things globally. Once you've decided to be a Fltk app, you have to do certain things the Fltk way. Same for Wx, Qt, etc. I don't think it's going to be practical to switch back and forth between message pump implementations at runtime. On the other hand, choosing to be a Fltk app shouldn't affect whether you choose to have a GL or DX or GDI display target, or any combination thereof.

    Setting things at CL_DisplayTarget construction implies you can do just, that, switching between different targets at will. Same for the CL_SetupFltk idea; even if that did the necessary internal surgery, you could screw things up thoroughly by instantiating both CL_SetupFltk and CL_SetupWx, for example. Having the provider factory set at CL_Display makes it clear that you have to pick just one type of provider for the entire app's display system, and that provider won't have to unwind anything else that might also have been set up.

    Speaking of which, certain toolkits won't be compatible with CL_Application. wxApp, for example, also implements the main() function. I don't think that's a real problem; just use wxApp instead of CL_Application if you're going to write a WxClanlib application. But again, it affects things globally and requires you to do certain things differently throughout your code.

    Am I misunderstanding the purpose of the CL_Display static class?

    --- Kevin

  8. #8
    Lesser Knight
    Join Date
    Aug 2008
    Location
    Folsom, California USA
    Posts
    37

    Default

    The other thing I'm wondering is if it is really necessary to fully wrap Fltk in Clanlib. Perhaps what I really need is an adapter interface to take a Fltk widget and extract a CL_GraphicContext object from it. Given that, you could then do any sort of Clanlib drawing you liked onto a target provided by some other framework, am I correct?

    If desired, I suppose you could also extract an adapted CL_InputContext object from the widget and do your input processing the Clanlib way. But I think this might be optional. Am I correct that input processing could be delegated entirely to any third-party library, while still drawing to the gc in response to that library's event handling methods? In other words, is the usual call to CL_DisplayMessageQueuerocess() required for Clanlib drawing operations to work if the message pump was being operated by some alternate method?

    --- Kevin

  9. #9
    ClanLib Developer
    Join Date
    Sep 2006
    Location
    Denmark
    Posts
    554

    Default

    Booting the application is not really a problem, since as you say yourself an application can just simply chose not to use clanApplication. Even though I originally made the clanApplication library, I do not really use it myself (as you can see on some of the tests and examples). The code that initializes clanlib are in the CL_SetupXX classes and not CL_ClanApplication.

    clanDisplay as a library is basically one big abstraction. It contains all the shared code and the convenience functions required to make an application enjoy using the abstraction and minimize the work requirements on creating new targets. It does this by having one set of classes that are intended for the application (CL_DisplayWindow, CL_GraphicContext, CL_InputDevice, etc.) and then a similar set of abstract interfaces that each target implement (CL_DisplayWindowProvider, CL_GraphicContextProvider, etc.). Things that can be implemented without pushing it to the target are implemented in clanDisplay and the rest is forwarded.

    The class CL_Target is the class which represents an implementation of the clanDisplay abstraction faced towards the application, and CL_TargetProvider is what a target needs to implement to support the abstraction. To avoid having to pass a CL_Target to all the base constructors, such as CL_DisplayWindow, a single is marked as the current target via CL_Display::set_current_target and retrieved by clanDisplay various places using get_current_target.

    The target providers can be subclasses into three categories: window management, input management and graphics management. These three systems work almost independently, but the current target abstraction dictates that the window provider (CL_DisplayWindowProvider) provides the two other (CL_InputContextProvider and CL_GraphicContextProvider) via the dispwindow.get_ic() and dispwindow.get_gc() functions.

    This relationship between the three groupings can be split, but it requires some planning. Window management and input management are linked together in that both require processing and handling of messages from the windowing system.

    The current InputContext design and implementation is pretty old code and somewhat broken by design, since it doesn't follow the basic principle of "GetMessage+DispatchMessage->HandlerCallback". If it did, it would not be important whether wx, MFC or even Qt was doing the actual message pumping, since at least the Win32 message queue model would direct the messages to the right callbacks. It has been pending for quite a time to redo the input context backend, but its a bit tricky to do it right. The problem is that DirectInput (which I'm suspecting some MS architects will remove/rewrite any minute now, as usual) require polling to emit some the signals defined in the clanDisplay IC abstraction. It is mostly joysticks and maybe the tablet code that may be causing trouble here.

    Anyway, since you were more asking about using the graphics management part of clanDisplay together with fltk, that should actually not be that difficult to fix. CL_GraphicContextProvider just need an OpenGL context to operate, and then it needs to know the size of the frame buffer it operates on. It needs to know the size to perform its 2d mapping, and currently the provider holds a pointer to a CL_DisplayWindowProvider that it accesses whenever it needs to know this. You have to add a system which allows it to get this knowledge from elsewhere (i.e. by asking fltk about the client size of the window) and then add a class in clanGL that helps you construct the CL_GraphicContext.

    Code:
    class CL_OpenGLGraphicContext : CL_GraphicContext
    {
    public:
      typedef CL_Callback_1<CL_Size> SizeCallback;
    
    #ifdef WIN32
      CL_OpenGLGraphicContext(HWND wnd, HGLRC glcontext, SizeCallback cb)
      : CL_GraphicContext(new CL_OpenGLGraphicContextWGL(wnd, glcontext, cb))
     {
     }
    #else
      CL_OpenGLGraphicContext(Drawable drawable, GLContext glcontext, SizeCallback cb);
      : CL_GraphicContext(new CL_OpenGLGraphicContextX11(drawable, glcontext, cb))
     {
     }
    #endif
    };
    The catch about the above code is that it assumes you have an easy way to do two things:

    • Create the OpenGL context via WGL/GLX/AGL
    • Perform the platform specific WGLSwapBuffers() call


    I don't know fltk, but I do know that in many other toolkits you often can create an OpenGL widget, which usually means that the toolkit itself has some code to create and manage the WGL/GLX/AGL code just like CL_DisplayWindow also does. I imagine they also might have a function that performs the buffer flip. Otherwise you can always call those functions manually without too much trouble.

    If you do not use CL_DisplayWindow, CL_InputContext or clanGUI, then you do not have to pump the message queue via the clanlib functions, which should solve your problem with fltk or wx currently doing that.

  10. #10
    Lesser Knight
    Join Date
    Aug 2008
    Location
    Folsom, California USA
    Posts
    37

    Default

    Thanks for the detailed and informative response. It's very helpful.

    I do think the way for me to proceed is to write an adapter to produce a CL_GraphicContext out of a third-party window object. It's nice that the graphic operations are separated from the event system.

    It does bother me a bit that clanGUI wouldn't work without the messages. Does clanGUI actually depend on the ClanLib message pump directly, or does it connect slots to well-known signals? Do you think there is a reasonable way to pump the necessary messages into clanGUI from a third-party source without a wholesale redesign of the input system? It's not an immediate concern as is the GL drawing, but something I'd eventually like to make work.

    --- Kevin

  11. #11
    ClanLib Developer
    Join Date
    Sep 2006
    Location
    Denmark
    Posts
    554

    Default

    clanGUI suffers from the problem that I did not really deeply understand the messy details of message queues before starting that problem.

    Basically what has gone wrong is that I assumed that a message queue was just a list of messages that got populated using PostMessage and emptied using GetMessage (for Win32). It turned out to be quite a bit more complicated than that - in fact, it got complicated enough so that Raymond Chen wrote several chapters in his book about how Windows pumps its messages!

    Anyway, the consequence of this has been that I figured it wouldn't be a problem if clanGUI had its own message queue, which then got filled by messages from the windowing system whenever it was empty. That is, I analyzed the situation and decided it wouldn't be a problem if the two queues were simply chained.

    Oh boy that was a mistake.

    My current knowledge about message queues has convinced me that clanGUI absolutely must use the normal message queue for its purposes, at least for Win32. The reason for this is that when you receive a message like WM_PAINT, Windows expects you to dispatch it using DispatchMessage, which again means that Windows expects you to handle the paint message in the window procedure. This conflicts with the current CL_GUIManager::get_message() because it wants to return that message as a CL_GUIMessagePaint object, which is then to be dispatched via CL_GUIManager::dispatch_message().

    The consequences of this conflict is easiest seen with paint message. For instance, we had a problem with drop shadows not working properly with clanGUI popup menus (it used the Win32 window class style CS_SHADOW). If the drawing isn't directly handled in the window procedure the shadow gets all messed up. This is caused by clanGUI rendering the window beneath the popup, then Win32 renders the drop shadow for the popup, and then clanGUI renders the popup window. If we do not perform the paint for the window beneath directly in its window procedure, Win32 will start painting the drop shadow before we paint the background. The end result is that the drop shadow doesn't always appear.

    The only way to solve these issues are to strictly follow the "GetMessage+DispatchMessage->WindowProc" rule, which means that when clanGUI dispatches a message, that dispatch goes to DispatchMessage and then that finally ends up in the render callbacks for the right CL_GUIComponent objects. It is my plan to perform this patch sometime in the near future, but right now I'm on vacation in southern California so it won't happen until I get home. The patch is also a bit tricky because it affects a lot of objects in the base of clanGUI: CL_GUIManager, CL_GUIMessage and CL_GUIComponent. After this patch it should be possible to rely on an external message pump for clanGUI as well.
    Last edited by Judas; 09-17-2008 at 07:13 AM. Reason: Some typos

  12. #12
    Lesser Knight
    Join Date
    Aug 2008
    Location
    Folsom, California USA
    Posts
    37

    Default

    So, having reviewed the code associated with graphic context providers, I think I know what I need to do. The roadblock at present is the fact that CL_OpenGLGraphicContextProvider requires a pointer to a CL_OpenGLWindowProvider object on construction. This appears to be excessive binding. As Magnus pointed out, the GC provider really only needs access to the platform-dependent window handles and the height and width of the window. Passing in a complete CL_OpenGLWindowProvider introduces unnecessary dependencies on the underlying window creation types such as x11_window along with all of its window-creation and message-pumping baggage. So, I propose to change the CL_OpenGLGraphicContextProvider to require a reference to a new type that only offers the window-specific data that it actually needs. Off the top of my head, I'll call it CL_GLRenderWindowProvider. It will be an abstract base class to be implemented in its various platform-specific flavors, both by Clanlib for its present DisplayWindow system, and also by external GUI libs. For the present code, it will be the job of the internal window types to produce an appropriate object, which will be a very simple wrapper around themselves.

    Here's a quick sketch of what I'm thinking:

    Code:
    // New constructor
    CL_OpenGLGraphicContextProvider::CL_OpenGLGraphicContextProvider(const CL_GLRenderWindowProvider & render_window);
    
    ...
    
    //Public interface type:
    class  CL_GLRenderWindowProvider
    public:
      virtual ~ CL_GLRenderWindowProvider();
      virtual drawable_type get_drawable() const = 0;
      virtual context_type get_context() const = 0;
      virtual int get_width() const = 0;
      virtual int get_height() const = 0;
    };
    
    ...
    
    // Private implementation for x11_window:
    class x11_render_window : public CL_GLRenderWindowProvider
    {
      public:
         x11_render_window(const x11_window & window);
      private:
        const x11_window & _window
      public:
        // [[Virtual overrides to pass through to wrapped _window]]
    };
    
    ...
    
    // Add to x11_window:
      private:
         const x11_render_window * _render_window;
      public:
         const x11_render_window & get_render_window() const {return *_render_window;}
    
    ...
    
    // New internal GC creation example for X11:
    x11_window.create(opengl_visual_info, screen_bpp, site, desc);
    if (create_provider_flag)
    {
      gc = CL_GraphicContext(new CL_OpenGLGraphicContextProvider(x11_window.get_render_window());
      setup_swap_interval_pointers();
    }
    Something like this will allow me to implement a Fltk or wxWidgets-specific subclass of CL_GLRenderWindowProvider and use it to construct a CL_OpenGLGraphicContextProvider object without having to involve all the DisplayWindow stuff.

    Any obvious problems with this idea?

    Thanks,

    --- Kevin

  13. #13
    ClanLib Developer
    Join Date
    Sep 2006
    Location
    Denmark
    Posts
    554

    Default

    Seems alright to me.

  14. #14
    Lesser Knight
    Join Date
    Aug 2008
    Location
    Folsom, California USA
    Posts
    37

    Default

    OK, here's the changes I made to Clanlib to support this. Patch attached, diffed from r1815.

    As I mentioned above, basically I've rewired CL_GraphicProvider to depend on an abstract CL_RenderWindowProvider adapter type rather than directly on specific WindowProvider types. This makes it possible to expunge all platform- and window-specific code from the graphic provider. This in turn allows you to write subclasses of CL_RenderWindowProvider that can interface with other windowing libraries such as Fltk and wxWidgets that have their own OpenGL widget types, without requiring access to those library's GL context handles and such.

    I also took the opportunity to clean up any GCC warnings in the touched files.

    This builds cleanly on Win32 and Linux. I can't say if it builds on Mac, but it looks to me like that's broken anyway. I ran some of the tests, the ones that build anyway, and a couple of my own projects, and as far as I can tell GL rendering still works as before.

    It's a fairly large and complicated patch. Questions, comments, criticism all welcome.

    --- Kevin
    Attached Files Attached Files

  15. #15
    ClanLib Developer
    Join Date
    May 2007
    Posts
    1,824

    Default

    It looks good to me.

    I do have a single question though:

    In opengl_window_provider_wgl.cpp:

    Why did you add:
    Code:
    namespace
    {
    	...
    }
    For consistency, i do not believe it should be there. As clanlib is always in the global namespace.

  16. #16
    Lesser Knight
    Join Date
    Aug 2008
    Location
    Folsom, California USA
    Posts
    37

    Default

    > Why did you add: <an anonymous namespace>

    That's the standard C++ syntax for designating symbols with internal linkage. Nothing ever needs direct access to type RenderWindowProvider_WGL outside module opengl_window_provider_wgl.cpp. Putting it into an anonymous namespace signals the compiler that it should not be linked by external modules and ensures the name will never clash with any other symbol in a different module. A clash is unlikely for this particular symbol, but every large program I've ever worked on has come up with a symbol clash at least once where multiple people define generically named module-scope helper types (something like "sort_helper") in the global namespace. It's particularly distressing for people who aren't so familiar with compiler internals when the clash comes from inside some library you're linking to --- like Clanlib. :-)

    So, its just a habit for me. Removing the anonymous namespaces likely won't break anything, but doing so is slightly less "ISO standard". The only effect will be to cause those symbols to be available for external linking, which is unnecessary.

    I didn't prefix RenderWindowProvider_WGL with 'CL_' for the same reason; its not a type that will be available for public reference.

    --- Kevin

  17. #17
    ClanLib Developer
    Join Date
    May 2007
    Posts
    1,824

    Default

    Thanks. There's still lots for me to learn with C++

  18. #18
    ClanLib Developer
    Join Date
    May 2007
    Posts
    1,824

    Default

    Patch Applied, many many thanks

    Looking at the namespaces: http://msdn.microsoft.com/en-us/libr...k5(VS.80).aspx
    "You can declare an unnamed namespace as a superior alternative to the use of global static variable declarations."

    That is really going to help me at work .I also assume it will aid in compiler optimization

  19. #19
    Lesser Knight
    Join Date
    Aug 2008
    Location
    Folsom, California USA
    Posts
    37

    Default

    Quote Originally Posted by rombust View Post
    many many thanks
    Thank me in a couple of weeks, after it becomes reasonably certain that it hasn't seriously broken anything.

    Thanks for applying it. Any difficulties with the patch?

    --- Kevin

Similar Threads

  1. Replies: 7
    Last Post: 08-26-2008, 09:57 PM
  2. Installing ClanLib 0.8 and ClanLib 0.9 on linux
    By rombust in forum Official ClanLib SDK Forums
    Replies: 4
    Last Post: 07-15-2008, 09:51 AM

Bookmarks

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •