Monday, October 4, 2010

Inside The Blender C API - Part2

In the "main" function of "creator.c" the function call "WM_keymap_init(C)" ('C' blender-context) setups basic mouse and keyboard handling. Calls to CTX_wm_manager(C) which returns a pointer to the wmWindowManager struct contained by C. WM_keymap_init, a new wmKeyConfig struct is created by WM_keyconfig_new, passed to: wm_window_keymap, ED_spacetypes_keymap, and WM_keyconfig_userdef. Finally the wmKeyConfig is assigned to the wmWindowManager struct as `defaultconf`.

Blender Context.

bContext

:
blender/source/blender/blenkernel/BKE_context.h

C contains: thread index, window manager struct, data-context-struct, and eval struct. The window manager struct contains: a manager, window, screen, area, region, menu, and store. The data-context-struct contains the scene and python context.

blenkernel note:


Many functions defined in blender/source/blender/blendkernel are prefixed with BKE, but not all. CTX_create and CTX_wm_manager are two examples of blendkernel functions that do not start with BKE.

DNA_windowmanager_types.h


The wmWindowManager struct and wmKeyConfig are defined in blender/source/blender/makesdna/DNA_windowmanager_types.h. Other important window manager related structs also define here are: wmWindow and wmOperator. The wmWindow struct contains a pointer to a wmEvent struct named `eventstate`, and a list named `queue` that contains all events. Two other event lists inside wmWindow are: `handlers` and `modalhandlers`.

WM_types.h and wm_event_types.h


Both of these headers are in: blender/source/blender/windowmanager/. wmEvent and other window event related things are defined here, the structs are in WM_types.h and the enums are in wm_event_types.h. Below is the definition of wmEvent:

/* each event should have full modifier state */
/* event comes from eventmanager and from keymap */
typedef struct wmEvent {
struct wmEvent *next, *prev;
short type; /* event code itself (short, is also in keymap) */
short val; /* press, release, scrollvalue */
short x, y; /* mouse pointer position, screen coord */
short mval[2]; /* region mouse position, name convention pre 2.5 :) */
short unicode; /* future, ghost? */
char ascii; /* from ghost */
char pad;
/* previous state */
short prevtype;
short prevval;
short prevx, prevy;
double prevclicktime;
short prevclickx, prevclicky;
/* modifier states */
short shift, ctrl, alt, oskey; /* oskey is apple or windowskey, value denotes order of pressed */
short keymodifier; /* rawkey modifier */
short pad1;
/* keymap item, set by handler (weak?) */
const char *keymap_idname;
/* custom data */
short custom; /* custom data type, stylus, 6dof, see wm_event_types.h */
short customdatafree;
int pad2;
void *customdata; /* ascii, unicode, mouse coords, angles, vectors, dragdrop info */
} wmEvent;

Inside Blender

Blender is made up of: 800 C files (.c), 1000 C header files (.h), 600 C++ files (.cpp), and 38 C++ header files (.hpp). Total lines of code about 1M.

SVN Check-Ins: (snapshot of source files, not total commits this year)
campbellbarton 1081
nexyon 156
blendix 75
gsrb3d 47
jesterking 31
broken 24
nazgul 20
dfelinto 20
aligorith 20

Inside The Blender C API: - Part 1



Diving into the source code you will find that the `main` function is in: blender/source/creator/creator.c
This mostly sets things up and calls the mainloop, which is actually WM_main found in: blender/source/blender/windowmanager/intern/wm.c
Blender uses the directory convention: `intern` and `extern`, which apparently defines which part of a library should be hidden or exposed to other libraries. Another convention (not always followed) is functions that begin with capital letters are exposed to other libraries.
WM_main only calls four functions in its while loop:
/* get events from ghost, handle window events, add to window queues */
wm_window_process_events(C);
/* per window, all events to the window, screen, area and region handlers */
wm_event_do_handlers(C);
/* events have left notes about changes, we handle and cache it */
wm_event_do_notifiers(C);
/* execute cached changes draw */
wm_draw_update(C);

These functions are defined in:
blender/source/blender/windowmanager/WM_types.h
blender/source/blender/windowmanager/wm_window.h
blender/source/blender/windowmanager/wm_event_system.h
blender/source/blender/windowmanager/wm_draw.h


Libblender - Getting Started:


Before trying the ctypes wrapper for libblender, you will need to compile blender as a shared library - on Windows this is a 'dll' file, and on Linux this is a 'so' file. First modify 'blender/CMakeLists.txt' and turn off both the game engine and python. Then edit 'blender/source/creator/CMakeLists.txt' and change:
ADD_EXECUTABLE(blender ${EXETYPE} ${EXESRC})
to:
ADD_LIBRARY(blender SHARED ${EXETYPE} ${EXESRC})
Finally, run cmake. If you have never used cmake to build blender, you need to create a new build directory outside of the blender source tree. On linux you would then run:
cmake -G "Unix Makefiles" ../blender
make


The lastest ctypes wrapper for libblender exposes enough of the C API to control the main loop. Converting the `main` and `WM_main` into ctypes looks like following:

import os, sys, time
from ctypes_libblender import *
from ctypes import *

def main():
_argv = ''
for arg in sys.argv: _argv += arg + ' '
argc = len(sys.argv)
argv = ctypes.pointer(ctypes.c_char_p(_argv))
C = CTX_create() # bContext pointer
BLI_threadapi_init()
RNA_init()
RE_engines_init()
pluginapi_force_ref();
init_nodesystem();
initglobals()
IMB_init()
syshandle = SYS_GetSystem()
GEN_init_messaging_system()
ba = BLI_argsInit(argc, argv)
setupArguments(C, ba, syshandle);
BLI_argsParse(ba, 1, None, None); # required, segfaults without this
sound_init_once();
init_def_material();
print ('WM_init...'); time.sleep(1)
WM_init(C, argc, argv); # some less random crash happening here
BLI_where_is_temp( c_char_p('/tmp'), 1 )
CTX_py_init_set(C, 1);
WM_keymap_init(C); # (this segfaults if BLI_where_is_temp is not called above)
time.sleep(1) # can segfault if we don't wait for the window to appear?
while True:
wm_window_process_events(C);
wm_event_do_handlers(C);
wm_event_do_notifiers(C);
wm_draw_update(C);