From BlenderWiki

Jump to: navigation, search

Window Manager


Blender has its own full functional window manager. The subdividision UI concept Blender was based on demanded development of such a system.

On the top level of the window manager you can find the "Screen", which actually just is the "window" in the OS Desktop environment. The Screen again is subdivided into "Areas", which act as a "sub window". (Back then I've choosen the words "Screen" and "Area" mostly to prevent confusement with much the abused word "window".)

In the very first incarnation of Blender (1.0) control over drawing of Areas was left to the "sub window" feature the Irix window manager offered. This is available in X11 and other windowmangers too btw. After a while I decided to drop this, mostly because using many sub-windows within a main window is handled extremely slow. Speed is comparable to a single application trying to manage like 10-20 separate windows, causing a lot of system overhead. One full redraw of a new Screen setup took seconds (a slowness you can now still watch in Modo btw, they apparently didn't copy Blender completely yet ;-).

Instead I found a way to completely mimic sub-windows with OpenGL features. By using the glScissor() and glViewPort() combo you can effectively achieve that. Code for that resides in the src/mywindow.c file. Note that there you find still find wrappers for the former sub-window system, introducing a 'bWindow' layer.

So in this stage, the first confusion was already introduced, mixing up "Area" features with "bWindow" ones... and to make the whole system even more interesting, the port to Glut was added on top of that again, and later on the port to Ghost. The wrapping from "bWindow" to GhostWindow is done with another "Window" being defined in ghostwinlay.c. So we now have the very interesting series of wrappers:

System Window <-> GHOST_Window <-> Window <-> bWindow <-> ScrArea

(OS specific) (Ghost lib) (ghostwinlay.c) (mywindow.c) (editscreen.c)

Especially the way how the renderwindow is hacked into this system is a total nightmare, with even code comments like "Do not ever release this after 2.23!"). This was part of a restructuring process never even finished...

The obvious wrapper series should be something like this:

System Window <-> GHOST_Window <-> Screen

Making the "render output window" just an Area in a Screen, or as a new (system) Window with a Screen, and thus allowing multiple Screens to be opened in separate windows (dual monitor purpose for example).

Areas and Spaces

Within the Blender universe the "active window" therefore is just the active Area, the part within the subdivided Screen where input goes to or output has to be drawn. The editscreen.c code manages this, defining which area is currently active for input, and what areas need to be redrawn, and in what situations a full or partial swapbuffers should be done.

For that reason Areas have their own event queues, and (function pointers) callbacks for queue handling and drawing. The Screen system reads the main queues, distributes it to the areas, executes the queue callbacks, and for proper swapbuffering also can execute redraws. This all goes well balanced, buffering queue overloading to prevent 100 redraws to be executed 100 times as well.

The Blender "Space" is another new definition to prevent the word "window". Decided was to fully separate the application that runs within an Area from the Screen/Area system itself. This allows each Area to transparently run any application, which is an important feature in Blender (changing "window" type). A space is simply definined by the following items;

  • a SpaceXXX struct to hold persistant data. Data that's relevant for saving in a file, for copying or restoring.
  • functions to allocate, free or copy SpaceXXX structs
  • a winhandle() callback for handling the Area events
  • a windraw() callback for the full (re)drawing
  • a winchange() callback for reinitializing windowsize or other changes

Each area is allowed to store a SpaceXXX struct for each Space type (in a list), which enables proper restoring when applications change in an area. For example when you invoke the SpaceFile, it can safely return to its previous SpaceView3D type without any change in its settings.

Another important aspect is that any Space is supposed to run fully local. It is not (should not) be aware of any other editors that are open, nor read data from it, nor access them to change data. The Spaces should be treated as individual applications, storing settings in their own SpaceXXX structs, and only working on what the Blender database is providing, or what the Screen defines to be the active context (like active Scene, Object, etc).

(Read the notes on the Blender architecture for more about this).

There's still no good protocol or document on adding new Space types, but in theory it is possible to hook up any application with Blender this way. Using the callback structure it can even be plug-inned... think for example what would happen to have a web browser or text editor or terminal running there. Your own deskop environment! :)

Blender is far from perfect, so you can also find ancient abuses of the system, especially in using global settings... like for example Edit Mode or FaceSelect. The implementation of tools in the Buttons window is another exception, the tools actually work in a 3d window, but are accessed from another Space type. Cleaning up such issues is also part of the 2.3 UI project, with attempts to keep the used context as clean as obvious as possible.

In most situations it works well though, with individual Spaces only communicating with each other by sending out events to the Screen manager, which distributes it over the other Areas again.

The ugly struct Global

The goal of any good structured program is to limit usage of global variables to an absolute minimum (yes, lessons learnt :). One reason why it's good to have them in struct Global is as punishment, since it forces a full recompile. Another reason is to keep things together that way, prevents unpleasant surprises.

Since - right now - only 1 screen is in use or displayed in a time, Blender's Screen manager puts "helpful" global variables in the G struct too.

What you can see all over the code is usage of "G.vd" or "G.sipo" and so on... these are the pointers to the *active* SpaceXXX structure, and is being set by the Screen manager on mousemoves and when the Areas handle their queues or redraws. Any code that uses these variables has to be 100% certain belong to its own Space type. No G.vd (View3D) access should be allowed in Buttons, or Fileselect, or Script space, since that's undefined then (or can even be zero). It's a common mistake in Blender code - I even violated it sometimes - and it always were reasons for bugs or crashes. The Python Window module also abuses that, and this should be removed a.s.a.p. :)

These variables actually belong to the "Screen context", and will be recoded to go there once.

How to move on There's clearly work to be done on the following areas:

  • remove the ridiculous 5 level deep wrapping of the "window" concept.
  • design a new method for Spaces to handle queues (using dynamic event handlers)
  • design a method for how Python scripts can be attached to any Space type (probably by just becoming an event handler!)