This chapter illustrates the dividing line between “core” Blender source code and one of the most fundamental of Blender’s internal support libraries called ghost. We will uncover the connection between ghost and windowmanager, the module that provides the underpinnings for Blender’s window-based application. Importantly, Blender is also an OpenGL program. We outline the required steps client programs must undertake to obtain a platform-specific window, along with an associated rendering context. Additionally, we discuss how you may write your own OpenGL application on top of ghost. Following this, we show how the windowmanager itself calls ghost. This allows the windowmanager module to abstract the windowing system, which is part of the underlying operating system.
The Generic Handy Operating System Toolkit (ghost)
A standard practice is to label software artifacts with nifty acronyms, often with somewhat obscure reference to earlier programs. ghost is no exception. The title of this section is the full acronym for ghost : Generic Handy Operating System Toolkit.
Overview
ghost’s abbreviated introductory description from GHOST_ISystem.h. Services are shown in boldface for emphasis. Incidentally, ghost does not create menu items, as these are constructed using OpenGL and by checking mouse position in Blender’s native UI code
We will see that ghost directly interfaces with Xlib (X11), Mac OS, or MicroSoft Windows in order to create an application window. Native OS windows are the receivers of events from an operating system. Thus, ghost captures these events, and an application built upon ghost must then implement an appropriate handler routine. We will show examples in an upcoming section.
Both GLUT and GLFW2 take over an application’s event loop, known as “inversion of control.” We have already seen in Chapter 1 that Blender’s event loop is in the windowmanager module (source/blender/windowmanager/intern/wm.c). As a comparison, GLFW is considered a “framework,” as the library itself runs the event loop. A client application using GLFW must register callback functions with it. In contrast, the windowmanager module maintains the event loop for Blender and is written on top of ghost.3
Initialization
- 1.
Obtain an application window.
- 2.
Obtain an OpenGL rendering context from the windowing system, for the display (bound by the constraints of the physical display and rendering hardware).
- 3.
Make the rendering context current.
The first step involves getting the display’s characteristics and mapping this to the application’s window.
Applications may have more than one rendering context. A rendering context represents an instance of the OpenGL state machine. It contains version information, pixel-type data, and the resolution required to instantiate it. All of this is accomplished via an intermediary: GLX, AGL, or WGL. Which one that is used depends on the operating system.
In the third step, applications must tell the operating system when to swap the render buffers. For instance, on X11, this is done with a call to glXSwapBuffers(). On Windows platforms, wglSwapBuffers() must be used instead. OpenGL, itself, does not perform this.
Like GLUT and GLFW, ghost deals directly with the platform’s API. An application using ghost only interacts with ghost, not the platform API. Thus, ghost makes Blender portable and simplifies the duties of the “core” codebase.
Header Files
![../images/495914_1_En_3_Chapter/495914_1_En_3_Fig1_HTML.png](../images/495914_1_En_3_Chapter/495914_1_En_3_Fig1_HTML.png)
Top-level ghost header files. The majority of these files provide the interface to ghost. ghost is written in C++. Headers with an “I” preceding the second word in their title, for example, GHOST_IWindow.h, declare an abstract class
In the case of GHOST_C-api.h, we have a set of function prototypes whose definitions access the underlying class-based objects using an empty struct called a “handle.” Two examples are GHOST_SystemHandle and GHOST_WindowHandle. GHOST_Path-api.h exports the API for directory information. Directory access is performed by the operating system’s programming interface. However, on POSIX (Portable Operating System Interface for Unix), the user (home), binary, and system directories all should be accessed using ghost, with member functions defined in a source file for the appropriate platform.
GHOST_SystemPathsUnix::getUserDir() is a deprecated member function from GHOST_SystmePathsUnix.cpp (located in intern/ghost/intern/). It works only for Blender versions before 2.64.4 More recent versions use freedesktop.org.5 Why would an application need this information? In order to set a starting directory for the Blender file browser, when saving or loading files (e.g., blend files), etc.
GHOST_Rect.h provides the declaration for the GHOST_Rect class. GHOST_Rect implements operations for rectangle objects. Member functions include, but are not limited to, GHOST_Rect::getHeight(), GHOST_Rect::getWidth(), and GHOST_Rect::isValid(). GHOST_Rect is used for mouse coordinate calculations.
GHOST_TWindowState and GHOST_DisplaySetting, both from GHOST_Types.h
Dependencies
ghost began as an internal library, whose only dependencies were operating system APIs external to Blender’s repository.6 ghost’s internal dependencies are shown in Figure 3-2. Only “drag-and-drop” references source/blender/imbuf. The dependency arises from ghost/intern/GHOST_EventDragDrop.h, where both IMB_imbuf.h and IMB_imbuf_types.h are included. ghost’s use of intern/utfconv is for Windows builds. ghost also depends on intern/libmv for motion tracking, when configured for 3D mouse support on Mac OS. This can be disabled with CMake’s build variable WITH_INPUT_NDOF.
![../images/495914_1_En_3_Chapter/495914_1_En_3_Fig2_HTML.jpg](../images/495914_1_En_3_Chapter/495914_1_En_3_Fig2_HTML.jpg)
Dependency graph for ghost (excluding tests). Arrows point in the direction of dependency. Annotations on directed edges count the count references.7 Red outlined directories have further dependencies not shown. The dependency on the imbuf module is not shown explicitly, but rather as the general dependency on the [repository root]/source directory
Accounting for intern/utconf, source/blender/imbuf, and intern/libmv, we are left with intern/string and intern/glew-mx. Figures 3-3 and 3-4 show the directory layout for these dependencies.
GLEW (Graphics Library Extension Wrangler) is an external open source library, used for OpenGL extension functions. The library gathers extensions for an application, as its name suggests. Blender’s glew-mx extends GLEW, supporting extensions for multiple rendering contexts. GLEW itself has limited support for this.
![../images/495914_1_En_3_Chapter/495914_1_En_3_Fig3_HTML.jpg](../images/495914_1_En_3_Chapter/495914_1_En_3_Fig3_HTML.jpg)
The intern/glew-mx files. Used by ghost to access GLEW and glew-mx functionality
![../images/495914_1_En_3_Chapter/495914_1_En_3_Fig4_HTML.jpg](../images/495914_1_En_3_Chapter/495914_1_En_3_Fig4_HTML.jpg)
The string module files. An object-oriented wrapper for the C string functions
Classes
System
Window
Context
Event
Timer
These are the elements of ghost’s duties. They take care of platform specifics regarding window and rendering context creation for Blender, or even possibly a separate application using ghost. Figure 3-1 shows a listing of the header files in ghost, where interfaces are noted by the inclusion of the letter “I” in the file name. While the interface classes contain pure virtual member function declarations, even GHOST_Rect declares its functions virtual, opening the possibility that this class could be derived from.
![../images/495914_1_En_3_Chapter/495914_1_En_3_Fig5_HTML.jpg](../images/495914_1_En_3_Chapter/495914_1_En_3_Fig5_HTML.jpg)
Class diagram for the abstract base class GHOST_ISystem. The + modifier denotes public members, the # modifier a protected member. This is an interface. It cannot be instantiated. The constructor GHOST_ISystem() is protected. This allows derived classes, using the factory function createSystem(), to instantiate a child class representing an operating system
![../images/495914_1_En_3_Chapter/495914_1_En_3_Fig6_HTML.jpg](../images/495914_1_En_3_Chapter/495914_1_En_3_Fig6_HTML.jpg)
GHOST_SystemWin32 and GHOST_SystemX11 are two of the concrete “system” classes from intern/ghost/intern/. Their constructor/destructor are public, unlike the parent classes
The pattern for GHOST_ISystem is similar to the other ghost types Window, Context, Event, and Timer. Note that ghost contains more than just these primary classes.
ghost has its own “window manager” class called GHOST_WindowManager. The main difference between the windowmanager module and GHOST_WindowManager is Blender’s module-level concerns. GHOST_WindowManager overlooks windows created at the level of GHOST (the operating system-level windows such as X11 or MacOS). These are returned from the platform API and wrapped by GHOST_WindowWin32, GHOST_WindowX11, GHOST_WindowCocoa, etc. Blender’s windowmanager also implements a Blender-specific application window.
GHOST_WindowManager
GHOST_DisplayManager
GHOST_EventManager
GHOST_NDOFManager
GHOST_TimeManager
GHOST_EventButton
GHOST_EventCursor
GHOST_EventDragDrop
GHOST_EventKey
C-API
GHOST_System
GHOST_TimerTask
GHOST_WindowHandle
GHOST_EventHandle
GHOST_RectangleHandle
GHOST_EventConsumerHandle
GHOST_ContextHandle
ghost’s C API will become more clear, when we cover a sample OpenGL application written in C. We mentioned that the GHOST_C-api.cpp functions use “handles,” or more specifically, C struct pointers for the ghost object types. These pointers are then cast to their C++ class object counterparts.
In C programs using ghost, handles are C struct pointers. In the C++ API implementation, these are cast to C++ object pointers. Parameters passed to the C API functions are transferred to the member functions of the corresponding C++ class. The C struct pointer types are in boldface
GHOST_Types.h’s “unguarded” memory version of C struct definitions’ handles. Macro condition for reference counting is omitted for brevity
A few ghost C API functions using the GHOST_SystemHandle. GHOST_SystemHandle shown in boldface
GHOST_CreateWindow() is implemented in intern/ghost/intern/GHOST_C-api.cpp. Here, we see a GHOST_SystemHandle cast to its C++ pointer type GHOST_ISystem. We also see the return type, a pointer to GHOST_IWindow, cast to GHOST_WindowHandle
ghost’s C API functions work similarly to GHOST_CreateWindow(). They cast a handle parameter and then make a polymorphic call on GHOST_ISystem. The correct system class is responsible for working with an object of the type representing the current action (such as a Window, Event, etc.). Member function(s) then call the appropriate platform API. This is the abstraction mechanism ghost provides client C programs, allowing them to be written without concern for the underlying operating system. Listings 3-7 and 3-8 show GHOST_SystemX11’s process for creating a window.
GHOST_SystemX11::createWindow() is called by GHOST_CreateWindow() . Subsequently it calls GHOST_WindowX11’s constructor. This code is implemented by intern/ghost/intern/GHOST_SystemX11.cpp
GHOST_WindowX11’s constructor. Much of the XVisualInfo and XSetWindowsAttributes setup is not shown. This is where ghost’s abstraction ends and calls to X11 are made. The call to XCreateWindow (an X11 API call) is in boldface
Minimal OpenGL Program Written in C Using ghost
In order to best understand the ghost C API, let us look at a sample OpenGL program ’s event loop, and ghost initialization. The full sample program and required CMake files are included with the source code for this book. This provides a foundation for understanding the windowmanager module.
A minimal OpenGL application’s main() function, modified from intern/ghost/test/gears/GHOST_C-Test.c.9 Each call to ghost is in boldface. We focus on the instantiation of the “event consumer,” or callback, that is invoked when GHOST_DispatchEvent() is called to “pump” the operating system for the events assigned to the application window created via GHOST_CreateWindow(). The main() function is abbreviated, excluding shader registration, OpenGL initialization, timer callback registration, and disposing of the system object by the appropriate calls to ghost
windowManager Revisit
Let us continue our journey of Blender’s “core” codebase. We look at windowmanager , its use as an abstraction layer, and its interface provided by WM_api.h. We will trace the initialization of the application window and the entry into the event loop. ghost is responsible for events, but only at a low level relative to windowmanager.
We saw an example of operators in Chapter 1, a Blender construct to manage event handling. The windowmanager provides a central hub to integrate higher abstraction by the Blender program.
windowManager’s ghost-Related Files
![../images/495914_1_En_3_Chapter/495914_1_En_3_Fig7_HTML.jpg](../images/495914_1_En_3_Chapter/495914_1_En_3_Fig7_HTML.jpg)
windowmanager’s top-level headers and subfolders located in [repository root]/source/blender/windowmanager
Figure 3-7 shows that windowmanager’s header layout is very similar to the other modules, including ghost. It has a “types” header and an “API” header. All functions external to a module are prefaced by capitals, for example, ghost and the windowmanager have GHOST_* and WM_* prefixes for their API, respectively.
wm_init_exit.c
wm_window.c
wm_draw.c
wm_event_system.c
wm_files.c (contains GHOST_Path-api.h)
wm_platform_support.c
wm_playanim.c
wm_stereo.c
wm_window_private.h (includes GHOST_Types.h only)
Each of these files includes GHOST_C-api.h, which we inspected earlier. As GHOST_C-api.h includes GHOST_Types.h, separate inclusion is unnecessary. GHOST_C-api.h provides access to the ghost API.
ghost Initialization and Event Registration
WM_init() calls wm_ghost_init() to initialize ghost. It passes a pointer to the bContext struct discussed in Chapter 2. There is a direct call to the ghost API, which uses GHOST_CreateSystemPaths()
The wm_ghost_init() function from windowmanager/intern/wm_window.c
The wm_window_process_events() function found in windowmanager/intern/wm_window.c. This function encapsulates Blender’s event “pump”
There is an upcoming discussion, in a later chapter, on how windows are managed by the use of the wmWindowManager struct. The wmWindowManager struct is defined in DNA_windowmanager_types.h, from the makesdna module.
Summary
This chapter covered ghost. How it abstracts the operating system and OpenGL rendering contexts were essential topics. We first reviewed the steps necessary to write an OpenGL program and how those steps are operating system dependent. GLUT and GLFW are lightweight platform-abstracting “toolkits” that help to offload such steps from a client program. GLEW obtains extension functions for an OpenGL hardware implementation. GLEW, together with GLFW or GLUT, is often used by small OpenGL applications.
Blender does not leverage GLUT—as it once did—but uses ghost and ghost’s helper libraries, glew-mx and string. Both of these are maintained internally by Blender’s repository. In addition to ghost’s file structure and classes, we looked at its C API. Blender is a C program, and ghost is written in C++. Therefore, ghost has a C API to allow Blender to interface with it.