© The Author(s), under exclusive license to APress Media, LLC , part of Springer Nature 2021
B. E. HollisterCore Blender Developmenthttps://doi.org/10.1007/978-1-4842-6415-7_3

3. ghost: Soul of the windowmanager Module

Brad E. Hollister1  
(1)
Computer Science Department, CSUDH, Carson, CA, USA
 

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

There is evidence in the codebase that Blender started as a GLUT1 application , before using ghost. Definitive proof is provided in comments from intern/ghost/GHOST_ISystem.h. However, what does ghost offer Blender? Its services are also summarized in GHOST_ISystem.h (Listing 3-1).
 * In short: everything that Blender needed from GLUT to run on all it's supported
 * operating systems and some extra's.
 * This includes :
 *
 * - Time(r) management.
 * - Display/window management (windows are only created on the main display).
 * - Event management.
 * - Cursor shape management (no custom cursors for now).
 * - Access to the state of the mouse buttons and the keyboard.
 * - Menus for windows with events generated when they are accessed (this is
 *   work in progress).
 * - Video mode switching.
 * - Copy/Paste buffers.
 * - System paths.
 *
 * Font management has been moved to a separate library.
 *
Listing 3-1

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

We will not go into the specifics of creating a window, or obtaining a rendering context for any particular operating system. Nevertheless, the requirements for a client program are as follows:
  1. 1.

    Obtain an application window.

     
  2. 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. 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

Figure 3-1 lists the header files for ghost. ghost is written in C++, unlike “core” Blender. Most of its headers declare an abstract class. Exceptions are GHOST_C-api.h, GHOST_Path-api.h, GHOST_Rect.h, and GHOST_Types.h.
../images/495914_1_En_3_Chapter/495914_1_En_3_Fig1_HTML.png
Figure 3-1

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_Types.h implements enums for state, and struct records for holding settings (Listing 3-2). GHOST_Types.h also contains enums defining key codes, returned from the operating system.
typedef enum {
  GHOST_kWindowStateNormal = 0,
  GHOST_kWindowStateMaximized,
  GHOST_kWindowStateMinimized,
  GHOST_kWindowStateFullScreen,
  GHOST_kWindowStateEmbedded,
  // GHOST_kWindowStateModified,
  // GHOST_kWindowStateUnModified,
} GHOST_TWindowState;
typedef struct {
  /** Number of pixels on a line. */
  GHOST_TUns32 xPixels;
  /** Number of lines. */
  GHOST_TUns32 yPixels;
  /** Numberof bits per pixel. */
  GHOST_TUns32 bpp;
  /** Refresh rate (in Hertz). */
  GHOST_TUns32 frequency;
} GHOST_DisplaySetting;
Listing 3-2

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.

ghost may leverage the Simple DirectMedia Layer (SDL). However, SDL can also be disabled. This is done via the CMake build variable WITH_SDL.
../images/495914_1_En_3_Chapter/495914_1_En_3_Fig2_HTML.jpg
Figure 3-2

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.

Last in the lineup of ghost dependencies, we have string (Figure 3-4). This is a C++ library wrapping much of the C string library (string.h).3
../images/495914_1_En_3_Chapter/495914_1_En_3_Fig3_HTML.jpg
Figure 3-3

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
Figure 3-4

The string module files. An object-oriented wrapper for the C string functions

Classes

Object categories having abstract base classes are
  • 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.

As the name implies, GHOST_ISystem is an abstract base class for an object-oriented representation of the operating system. We see its class diagram in Figure 3-5. GHOST_ISystem is instantiated as a singleton. It has a static factory member function called GHOST_ISystem::createSystem(), and a static accessor member function called GHOST_ISystem::getSystem(). Additionally, its constructor is protected, enforcing that only GHOST_ISystem::createSystem() be allowed to create an instance, when called from one of its child class (see Figure 3-5).
../images/495914_1_En_3_Chapter/495914_1_En_3_Fig5_HTML.jpg
Figure 3-5

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

GHOST_System inherits from GHOST_ISystem, an abstract class. GHOST_System therefore implements members found in concrete platform classes. Two examples are GHOST_System::getMilliSeconds() and GHOST_System::installTimer(). Figure 3-6 shows diagrams for two of these “system” classes: GHOST_SystemWin32 and GHOST_SystemX11. These represent Windows and X11 systems, respectively.
../images/495914_1_En_3_Chapter/495914_1_En_3_Fig6_HTML.jpg
Figure 3-6

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.

The following are “manager” classes, not exposed outside of GHOST, with header files located in intern/ghost/intern/:
  • GHOST_WindowManager

  • GHOST_DisplayManager

  • GHOST_EventManager

  • GHOST_NDOFManager

  • GHOST_TimeManager

Some of these classes are extended for a platform, for example, GHOST_DisplayManager and GHOST_NDOFManager.8 Another class category is events. GHOST_Button represents a mouse button, used for event state management. GHOST_Button encapsulates such things as “middle mouse-button up.” Classes representing events, derived from GHOST_Event, are
  • GHOST_EventButton

  • GHOST_EventCursor

  • GHOST_EventDragDrop

  • GHOST_EventKey

C-API

Blender is written in C. However, ghost is a C++ library. Thus, Blender needs to interface ghost using a function-only interface. This is where intern/ghost/intern/GHOST_C-api.cpp and its associated header intern/ghost/GHOST_C-api.h are involved. They work as an adapter for the two-way C to C++ communication. The function prototypes from GHOST_C-api.h are akin to the interface encountered with GLUT and GLFW. ghost, however, must be passed an object representing state. There are handles for each of the following types:
  • 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.

The handle declarations are made at the top of the C API header file intern/ghost/GHOST_C-api.h (Listing 3-3). Listing 3-4 shows macro template definitions from intern/ghost/GHOST-Types.h.
...
GHOST_DECLARE_HANDLE(GHOST_SystemHandle);
GHOST_DECLARE_HANDLE(GHOST_TimerTaskHandle);
GHOST_DECLARE_HANDLE(GHOST_WindowHandle);
GHOST_DECLARE_HANDLE(GHOST_EventHandle);
GHOST_DECLARE_HANDLE(GHOST_RectangleHandle);
GHOST_DECLARE_HANDLE(GHOST_EventConsumerHandle);
GHOST_DECLARE_HANDLE(GHOST_ContextHandle);
...
Listing 3-3

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

...
#else
#  define GHOST_DECLARE_HANDLE(name) \
    typedef struct name##__ { \
      int unused; \
    } * name
#endif
Listing 3-4

GHOST_Types.h’s “unguarded” memory version of C struct definitions’ handles. Macro condition for reference counting is omitted for brevity

Representative ghost C API functions using the GHOST_SystemHandle are shown in Listing 3-5. GHOST_SystemHandle is a proxy for child classes derived from GHOST_System (intern/ghost/intern/GHOST_System.cpp), for example, GHOST_SystemX11, GHOST_SystemWin32, GHOST_SystemCocoa, etc. Each is implemented in its own file, located at intern/ghost/intern/. The derived ghost system types are named according to the native systems’ API: X11, Win32, or Cocoa.
extern GHOST_SystemHandle GHOST_CreateSystem(void);
extern GHOST_TUns8 GHOST_GetNumDisplays(GHOST_SystemHandle systemhandle);
extern void GHOST_GetMainDisplayDimensions(GHOST_SystemHandle systemhandle, GHOST_TUns32 *width, GHOST_TUns32 *height);
extern void GHOST_GetAllDisplayDimensions(GHOST_SystemHandle systemhandle,GHOST_TUns32 *width, GHOST_TUns32 *height);
extern GHOST_WindowHandle GHOST_CreateWindow(GHOST_SystemHandle systemhandle, const char *title, GHOST_TInt32 left, GHOST_TInt32 top, GHOST_TUns32 width, GHOST_TUns32 height, GHOST_TWindowState state, GHOST_TDrawingContextType type, GHOST_GLSettings glSettings);
Listing 3-5

A few ghost C API functions using the GHOST_SystemHandle. GHOST_SystemHandle shown in boldface

An example of the C API function is shown in Listing 3-6. We can see GHOST_SystemHandle cast to its C++ object type. Then, GHOST_ISystem::createWindow() is passed the C API’s parameters. Finally, we encounter GHOST_Window, cast to GHOST_WindowHandle, for the client C program. Notice how GHOST_CreateWindow() takes its parameters and passes them along to GHOST_ISystem::createWindow(). This member function is resolved to the runtime type for the platform, that is, GHOST_SystemX11, GHOST_SystemWin32, GHOST_SystemCocoa, etc. After this, a call to the appropriate member function from a GHOST_Window-derived class (e.g., GHOST_WindowX11, GHOST_WindowWin32, GHOST_WindowCocoa) is made. Eventually GHOST calls the platform (e.g., API for X11, Win32, Cocoa, etc.).
GHOST_WindowHandle GHOST_CreateWindow(GHOST_SystemHandle systemhandle,
                    const char *title,
                    GHOST_TInt32 left,
                    GHOST_TInt32 top,
                    GHOST_TUns32 width,
                    GHOST_TUns32 height,
                    GHOST_TWindowState state,
                    GHOST_TDrawingContextType type,
                    GHOST_GLSettings glSettings)
{
   GHOST_ISystem *system = (GHOST_ISystem*)
                            systemhandle;
   return (GHOST_WindowHandle)system->createWindow(
         title, left, top, width, height, state,
         type, glSettings, false, false);
}
Listing 3-6

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

Note

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_IWindow *GHOST_SystemX11::createWindow(
     const STR_String &title,
     GHOST_TInt32 left,
     GHOST_TInt32 top,
     GHOST_TUns32 width,
     GHOST_TUns32 height,
     GHOST_TWindowState state,
     GHOST_TDrawingContextType type,
     GHOST_GLSettings glSettings,
     const bool exclusive,
     const bool is_dialog,
     const GHOST_IWindow *parentWindow)
{
  GHOST_WindowX11 *window = NULL;
  if (!m_display)
    return 0;
  window = new GHOST_WindowX11(this,
                    m_display,
                    title,
                    left,
                    top,
                    width,
                    height,
                    state,
                    (GHOST_WindowX11*)
                      parentWindow,
                    type,
                    is_dialog,
                    ((glSettings.flags &
                      GHOST_glStereoVisual) != 0),
                    exclusive,
                    ((glSettings.flags &
                      GHOST_glAlphaBackground) != 0),
                    (glSettings.flags &
                       GHOST_glDebugContext) != 0);
...
Listing 3-7

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::GHOST_WindowX11(GHOST_SystemX11 *system,
      Display *display,
      const STR_String &title,
      GHOST_TInt32 left,
      GHOST_TInt32 top,
      GHOST_TUns32 width,
      GHOST_TUns32 height,
      GHOST_TWindowState state,
      GHOST_WindowX11 *parentWindow,
      GHOST_TDrawingContextType type,
      const bool is_dialog,
      const bool stereoVisual,
      const bool exclusive,
      const bool alphaBackground,
      const bool is_debug) : GHOST_Window(width, height, state, stereoVisual, exclusive),
      m_display(display),
      m_visualInfo(NULL),
      m_fbconfig(NULL),
      m_normal_state(GHOST_kWindowStateNormal),
      m_system(system),
      m_invalid_window(false),
      m_empty_cursor(None),
      m_custom_cursor(None),
      m_visible_cursor(None),
      m_taskbar("blender.desktop"),
...
/* create the window! */
  if ((parentWindow == 0) || is_dialog) {
    m_window = XCreateWindow(m_display,
                             RootWindow(m_display,
                             m_visualInfo->screen),
                             left,
                             top,
                             width,
                             height,
                             0, /* no border. */
                             m_visualInfo->depth,
                             InputOutput,
                             m_visualInfo->visual,
                             xattributes_valuemask,
                             &xattributes);
Listing 3-8

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.

Listing 3-9 shows main(). There, we see the event loop and how events are processed using ghost. The registered callback processEvent() is called by ghost. Notice the client program does not contain code specific to an operating system API. Nor does it directly create an OpenGL rendering context, as that is all accomplished by a call to GHOST_CreateWindow().
int main(int argc, char **argv)
{
  GHOST_GLSettings glSettings = {0};
  char *title1 = "Main Window";
  GHOST_EventConsumerHandle consumer =
     GHOST_CreateEventConsumer(processEvent, NULL);
  /* Create the system */
  shSystem = GHOST_CreateSystem();
  GHOST_AddEventConsumer(shSystem, consumer);
  if (shSystem) {
    /* Create the main window */
    sMainWindow = GHOST_CreateWindow(shSystem,
                                       title1,
                                           10,
                                           64,
                                          320,
                                          200,
                     GHOST_kWindowStateNormal,
              GHOST_kDrawingContextTypeOpenGL,
                                   glSettings);
    if (!sMainWindow) {
      printf("could not create main window\n");
      exit(-1);
    }
    ...
    /* Enter main loop */
    while (!sExitRequested) {
      if (!GHOST_ProcessEvents(shSystem, 0)) {
#ifdef WIN32
        /* If there were no events, be nice to other applications */
        Sleep(10);
#endif
      }
      GHOST_DispatchEvents(shSystem);
    }
  }
  ...
  return 0;
}
Listing 3-9

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

Figure 3-7 shows files just under the windowmanager directory (source/blender/windowmanager). We focus our attention on the WM_* API and also how windowmanager maps ghost’s functionality to other parts of Blender.
../images/495914_1_En_3_Chapter/495914_1_En_3_Fig7_HTML.jpg
Figure 3-7

windowmanager’s top-level headers and subfolders located in [repository root]/source/blender/windowmanager

Note

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.

windowmanager files containing headers from ghost:
  • 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

In Listing 3-10, we see the initialization of ghost via WM_init(),10 defined in wm_init_exit.c. A bContext parameter holds the state of the entire Blender application—much as a rendering context stores OpenGL state. It is passed to wm_ghost_init(). There, this variable is passed to GHOST_CreateEventConsumer() . See Listing 3-11.
/* only called once, for startup */
void WM_init(bContext *C, int argc, const char **argv)
{
  if (!G.background) {
    wm_ghost_init(C); /* note: it assigns C to ghost! */
    wm_init_cursor_data();
    BKE_sound_jack_sync_callback_set(sound_jack_sync_callback);
  }
  GHOST_CreateSystemPaths();
  ...
Listing 3-10

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()

void wm_ghost_init(bContext *C)
{
  if (!g_system) {
    GHOST_EventConsumerHandle consumer;
    if (C != NULL) {
      consumer = GHOST_CreateEventConsumer
                  (ghost_event_proc, C);
    }
    g_system = GHOST_CreateSystem();
    GHOST_SystemInitDebug(g_system, G.debug & G_DEBUG_GHOST);
    if (C != NULL) {
      GHOST_AddEventConsumer(g_system, consumer);
    }
    if (wm_init_state.native_pixels) {
      GHOST_UseNativePixels();
    }
    GHOST_UseWindowFocus(wm_init_state.window_focus);
    WM_init_tablet_api();
  }
}
Listing 3-11

The wm_ghost_init() function from windowmanager/intern/wm_window.c

GHOST_CreateEventConsumer() registers an application callback ghost_event_proc. The global variable g_system, a GHOST_SystemHandle instance pointing back to the appropriate class type instance, for the operating system is used in wm_window_process_events(). This occurs in wm_window.c during the event loop from wm.c—see Chapter 1 (Listing 1-7). We can see wm_window_process_events() in Listing 3-12. This sequence of C API calls is analogous to the minimal C program shown in Listing 3-9, but spread over multiple source files and functions in the windowmanager module.
void wm_window_process_events(const bContext *C)
{
  int hasevent;
  BLI_assert(BLI_thread_is_main());
  hasevent = GHOST_ProcessEvents(g_system, 0); /* 0 is no wait */
  if (hasevent) {
    GHOST_DispatchEvents(g_system);
  }
  hasevent |= wm_window_timer(C);
  /* no event, we sleep 5 milliseconds */
  if (hasevent == 0) {
    PIL_sleep_ms(5);
  }
}
Listing 3-12

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.