How Haiku Window is drawn, Source Code Journey

Ever wondered how window that we see in the operating system which we drag here and there, is drawn. Well, then you are at right place. In this series we will demystify internals through source code in the haiku OS.

#1 Simple Application

app.cpp

#include <Application.h>
#include <Window.h>

TestApp::TestApp(void)
	: BApplication("application/x-vnd.MyFirstApp")
{
	BRect frame(100, 100, 500, 400);
	
	Window* myWindow = new Window(frame, "My First App", B_TITLED_WINDOW, B_QUIT_ON_WINDOW_CLOSE, B_ALL_WORKSPACES);
	
	myWindow->Show();
	
}
	
int main(){
	TestApp *app = new TestApp();
	app->Run();
	
	delete app;
	
	return 0;
}

app.h

#ifndef _TST_H

#define _TST_H



#include <Application.h>

class TestApp : public BApplication

{

public:

	TestApp(void);

	

};





#endif

Above is the simple Test App that will output a window as given in the screenshot below:

image-97.png

Now, lets start our journey by diving into the code for drawing window.

Window Creation:

BRect frame(100, 100, 500, 400);

This line creates a BRect object named frame, representing the initial frame (position and size) of the application's main window. The parameters specify coordinates of the window:

100 = left

100 = top

500 = right

400 = bottom

BWindow* myWindow = new BWindow(frame, "My First App", B_TITLED_WINDOW, B_QUIT_ON_WINDOW_CLOSE, B_ALL_WORKSPACES);

This line creates a new Window object, representing the main application window.


#2 Window Interface Constructor

This BWindow constructor is defined in src/kits/interface/Window.cpp.

BWindow::BWindow(BRect frame, const char* title, window_type type,
		uint32 flags, uint32 workspace)
	:
	BLooper(title, B_DISPLAY_PRIORITY)
{
	window_look look;
	window_feel feel;
	_DecomposeType(type, &look, &feel);

	_InitData(frame, title, look, feel, flags, workspace);
}

This BWindow constructor takes these paramters:

  • frame: The initial position and size of the window.
  • title: The title of the window.
  • type: The type of window we need to create, It is an enum.
// This enum is defined in headers/os/interface/Window.h
enum window_type {
	B_UNTYPED_WINDOW					= 0,
	B_TITLED_WINDOW						= 1,
	B_MODAL_WINDOW						= 3,
	B_DOCUMENT_WINDOW					= 11,
	B_BORDERED_WINDOW					= 20,
	B_FLOATING_WINDOW					= 21
};
  • flags: Additional flags specifying the window behavior (e.g., whether the window is resizable or has a close button).
// This enum is too defined in headers/os/interface/Window.h
// window flags
enum {
	B_NOT_MOVABLE						= 0x00000001,
	B_NOT_CLOSABLE						= 0x00000020,
	B_NOT_ZOOMABLE						= 0x00000040,
	B_NOT_MINIMIZABLE					= 0x00004000,
	B_NOT_RESIZABLE						= 0x00000002,
	B_NOT_H_RESIZABLE					= 0x00000004,
	B_NOT_V_RESIZABLE					= 0x00000008,
	B_AVOID_FRONT						= 0x00000080,
	B_AVOID_FOCUS						= 0x00002000,
	B_WILL_ACCEPT_FIRST_CLICK			= 0x00000010,
	B_OUTLINE_RESIZE					= 0x00001000,
	B_NO_WORKSPACE_ACTIVATION			= 0x00000100,
	B_NOT_ANCHORED_ON_ACTIVATE			= 0x00020000,
	B_ASYNCHRONOUS_CONTROLS				= 0x00080000,
	B_QUIT_ON_WINDOW_CLOSE				= 0x00100000,
	B_SAME_POSITION_IN_ALL_WORKSPACES	= 0x00200000,
	B_AUTO_UPDATE_SIZE_LIMITS			= 0x00400000,
	B_CLOSE_ON_ESCAPE					= 0x00800000,
	B_NO_SERVER_SIDE_WINDOW_MODIFIERS	= 0x00000200
};
  • workspace: Specified the workspace(s) where the window should be visible.

Now, let's try to understand what this constructor is doing:

Creates two object of window_look and window_feel

// These enum are defined in headers/os/interface/Window.h
enum window_look {
	B_BORDERED_WINDOW_LOOK				= 20,
	B_NO_BORDER_WINDOW_LOOK				= 19,
	B_TITLED_WINDOW_LOOK				= 1,
	B_DOCUMENT_WINDOW_LOOK				= 11,
	B_MODAL_WINDOW_LOOK					= 3,
	B_FLOATING_WINDOW_LOOK				= 7
};

enum window_feel {
	B_NORMAL_WINDOW_FEEL				= 0,
	B_MODAL_SUBSET_WINDOW_FEEL			= 2,
	B_MODAL_APP_WINDOW_FEEL				= 1,
	B_MODAL_ALL_WINDOW_FEEL				= 3,
	B_FLOATING_SUBSET_WINDOW_FEEL		= 5,
	B_FLOATING_APP_WINDOW_FEEL			= 4,
	B_FLOATING_ALL_WINDOW_FEEL			= 6
};
_DecomposeType(type, &look, &feel);

It is responsible for decomposing the window_type parameter passed to the BWindow constructor into separate window_look and window_feel components.

void
BWindow::_DecomposeType(window_type type, window_look* _look,
	window_feel* _feel) const
{
	switch (type) {
		case B_DOCUMENT_WINDOW:
			*_look = B_DOCUMENT_WINDOW_LOOK;
			*_feel = B_NORMAL_WINDOW_FEEL;
			break;

		case B_MODAL_WINDOW:
			*_look = B_MODAL_WINDOW_LOOK;
			*_feel = B_MODAL_APP_WINDOW_FEEL;
			break;

		case B_FLOATING_WINDOW:
			*_look = B_FLOATING_WINDOW_LOOK;
			*_feel = B_FLOATING_APP_WINDOW_FEEL;
			break;

		case B_BORDERED_WINDOW:
			*_look = B_BORDERED_WINDOW_LOOK;
			*_feel = B_NORMAL_WINDOW_FEEL;
			break;

		case B_TITLED_WINDOW:
		case B_UNTYPED_WINDOW:
		default:
			*_look = B_TITLED_WINDOW_LOOK;
			*_feel = B_NORMAL_WINDOW_FEEL;
			break;
	}
}

This function takes window_look and window_feel as pointer variable. On the basis of type parameter and sets the appropriate window_look and window_feel.

	_InitData(frame, title, look, feel, flags, workspace);

This line calls the _InitData() function to initialize the internal data structures of the BWindow object based on the provided parameters:

  • frame: The initial position and size of the window.
  • title: The title of the window.
  • look and feel: The appearance and behavior of the window, determined by decomposing the type parameter.
  • flags: Additional flags specifying window behavior (e.g., whether the window is resizable or has a close button).
  • workspace: specifies the workspace(s) where the window should be visible.
void
BWindow::_InitData(BRect frame, const char* title, window_look look,
	window_feel feel, uint32 flags,	uint32 workspace, int32 bitmapToken)
{
	STRACE(("BWindow::InitData()\n"));

	if (be_app == NULL) {
		debugger("You need a valid BApplication object before interacting with "
			"the app_server");
		return;
	}

	frame.left = roundf(frame.left);
	frame.top = roundf(frame.top);
	frame.right = roundf(frame.right);
	frame.bottom = roundf(frame.bottom);

	fFrame = frame;

	if (title == NULL)
		title = "";

	fTitle = strdup(title);

	_SetName(title);

	fFeel = feel;
	fLook = look;
	fFlags = flags | B_ASYNCHRONOUS_CONTROLS;

	fInTransaction = bitmapToken >= 0;
	fUpdateRequested = false;
	fActive = false;
	fShowLevel = 1;

	fTopView = NULL;
	fFocus = NULL;
	fLastMouseMovedView	= NULL;
	fKeyMenuBar = NULL;
	fDefaultButton = NULL;

	// Shortcut 'Q' is handled in _HandleKeyDown() directly, as its message
	// get sent to the application, and not one of our handlers.
	// It is only installed for non-modal windows, though.
	fNoQuitShortcut = IsModal();

	if ((fFlags & B_NOT_CLOSABLE) == 0 && !IsModal()) {
		// Modal windows default to non-closable, but you can add the
		// shortcut manually, if a different behaviour is wanted
		AddShortcut('W', B_COMMAND_KEY, new BMessage(B_QUIT_REQUESTED));
	}

	// Edit modifier keys

	AddShortcut('X', B_COMMAND_KEY, new BMessage(B_CUT), NULL);
	AddShortcut('C', B_COMMAND_KEY, new BMessage(B_COPY), NULL);
	AddShortcut('V', B_COMMAND_KEY, new BMessage(B_PASTE), NULL);
	AddShortcut('A', B_COMMAND_KEY, new BMessage(B_SELECT_ALL), NULL);

	// Window modifier keys

	AddShortcut('M', B_COMMAND_KEY | B_CONTROL_KEY,
		new BMessage(_MINIMIZE_), NULL);
	AddShortcut('Z', B_COMMAND_KEY | B_CONTROL_KEY,
		new BMessage(_ZOOM_), NULL);
	AddShortcut('Z', B_SHIFT_KEY | B_COMMAND_KEY | B_CONTROL_KEY,
		new BMessage(_ZOOM_), NULL);
	AddShortcut('H', B_COMMAND_KEY | B_CONTROL_KEY,
		new BMessage(B_HIDE_APPLICATION), NULL);
	AddShortcut('F', B_COMMAND_KEY | B_CONTROL_KEY,
		new BMessage(_SEND_TO_FRONT_), NULL);
	AddShortcut('B', B_COMMAND_KEY | B_CONTROL_KEY,
		new BMessage(_SEND_BEHIND_), NULL);

	// We set the default pulse rate, but we don't start the pulse
	fPulseRate = 500000;
	fPulseRunner = NULL;

	fIsFilePanel = false;

	fMenuSem = -1;

	fMinimized = false;

	fMaxZoomHeight = 32768.0;
	fMaxZoomWidth = 32768.0;
	fMinHeight = 0.0;
	fMinWidth = 0.0;
	fMaxHeight = 32768.0;
	fMaxWidth = 32768.0;

	fLastViewToken = B_NULL_TOKEN;

	// TODO: other initializations!
	fOffscreen = false;

	// Create the server-side window

	port_id receivePort = create_port(B_LOOPER_PORT_DEFAULT_CAPACITY,
		"w<app_server");
	if (receivePort < B_OK) {
		// TODO: huh?
		debugger("Could not create BWindow's receive port, used for "
				 "interacting with the app_server!");
		delete this;
		return;
	}

	STRACE(("BWindow::InitData(): contacting app_server...\n"));

	// let app_server know that a window has been created.
	fLink = new(std::nothrow) BPrivate::PortLink(
		BApplication::Private::ServerLink()->SenderPort(), receivePort);
	if (fLink == NULL) {
		// Zombie!
		return;
	}

	{
		BPrivate::AppServerLink lockLink;
			// we're talking to the server application using our own
			// communication channel (fLink) - we better make sure no one
			// interferes by locking that channel (which AppServerLink does
			// implicetly)

		if (bitmapToken < 0) {
			fLink->StartMessage(AS_CREATE_WINDOW);
		} else {
			fLink->StartMessage(AS_CREATE_OFFSCREEN_WINDOW);
			fLink->Attach<int32>(bitmapToken);
			fOffscreen = true;
		}

		fLink->Attach<BRect>(fFrame);
		fLink->Attach<uint32>((uint32)fLook);
		fLink->Attach<uint32>((uint32)fFeel);
		fLink->Attach<uint32>(fFlags);
		fLink->Attach<uint32>(workspace);
		fLink->Attach<int32>(_get_object_token_(this));
		fLink->Attach<port_id>(receivePort);
		fLink->Attach<port_id>(fMsgPort);
		fLink->AttachString(title);

		port_id sendPort;
		int32 code;
		if (fLink->FlushWithReply(code) == B_OK
			&& code == B_OK
			&& fLink->Read<port_id>(&sendPort) == B_OK) {
			// read the frame size and its limits that were really
			// enforced on the server side

			fLink->Read<BRect>(&fFrame);
			fLink->Read<float>(&fMinWidth);
			fLink->Read<float>(&fMaxWidth);
			fLink->Read<float>(&fMinHeight);
			fLink->Read<float>(&fMaxHeight);

			fMaxZoomWidth = fMaxWidth;
			fMaxZoomHeight = fMaxHeight;
		} else
			sendPort = -1;

		// Redirect our link to the new window connection
		fLink->SetSenderPort(sendPort);
		STRACE(("Server says that our send port is %ld\n", sendPort));
	}

	STRACE(("Window locked?: %s\n", IsLocked() ? "True" : "False"));

	_CreateTopView();
}
  • frame: The initial position and size of the window.
  • title: The title of the window.
  • look: The appearance of the window, determined by the window look.
  • feel: The behavior of the window, determined by the window feel.
  • flags: Additional flags specifying window behavior (e.g., whether the window is resizable or has a close button).
  • workspace: Specifies the workspace(s) where the window should be visible.
  • bitmapToken: Token for an offscreen bitmap, used for offscreen drawing (optional).
    frame.left = roundf(frame.left);
    frame.top = roundf(frame.top);
    frame.right = roundf(frame.right);
    frame.bottom = roundf(frame.bottom);

This block of code rounds the coordinates of the window frame to integer values. Haiku OS expects window coordinates to be integers.

    fFrame = frame;

This line assigns the rounded frame to the fFrame member variable of the BWindow object.

    if (title == NULL)
        title = "";

    fTitle = strdup(title);

    _SetName(title);

This block of code handles the window title. If the title is NULL, it assigns an empty string to it. Then, it allocates memory for the title copies the provided title into it. Finally, it sets the the internal name of the window using the _SetName() function.

//! Rename the handler and its thread
void
BWindow::_SetName(const char* title)
{
	if (title == NULL)
		title = "";

	// we will change BWindow's thread name to "w>window title"

	char threadName[B_OS_NAME_LENGTH];
	strcpy(threadName, "w>");
#ifdef __HAIKU__
	strlcat(threadName, title, B_OS_NAME_LENGTH);
#else
	int32 length = strlen(title);
	length = min_c(length, B_OS_NAME_LENGTH - 3);
	memcpy(threadName + 2, title, length);
	threadName[length + 2] = '\0';
#endif

	// change the handler's name
	SetName(threadName);

	// if the message loop has been started...
	if (Thread() >= B_OK)
		rename_thread(Thread(), threadName);
}
    fFeel = feel;
    fLook = look;
    fFlags = flags | B_ASYNCHRONOUS_CONTROLS;

These lines assigns the provided window feel, window look, and flags to their respective member variables of the BWindow object. The B_ASYNCHRONOUS_CONTROLS flags is added to the provided flags.

    fInTransaction = bitmapToken >= 0;
    fUpdateRequested = false;
    fActive = false;
    fShowLevel = 1;

These lines initializes various state variables of the BWindow object, including whether it's currently in a transaction (i.e., updating its contents), whether an update has been requested, whether it's active, and its show level.

    fTopView = NULL;
    fFocus = NULL;
    fLastMouseMovedView = NULL;
    fKeyMenuBar = NULL;
    fDefaultButton = NULL;

These lines initializes various pointers used for managing views, focus, the last view to receive mouse movement, the key menu bar, and the default button.

   	// Shortcut 'Q' is handled in _HandleKeyDown() directly, as its message
	// get sent to the application, and not one of our handlers.
	// It is only installed for non-modal windows, though.
	fNoQuitShortcut = IsModal();

	if ((fFlags & B_NOT_CLOSABLE) == 0 && !IsModal()) {
		// Modal windows default to non-closable, but you can add the
		// shortcut manually, if a different behaviour is wanted
		AddShortcut('W', B_COMMAND_KEY, new BMessage(B_QUIT_REQUESTED));
	}

This block of code sets up a keyboard shortcut for quitting the application (Cmd + W) if the window is not modal and not marked as not closable.

These lines set up keyboard shortcuts for common actions like quitting the application (B_QUIT_REQUESTED). The handling of the 'Q' shortcut is explained in a comment, and a shortcut for quitting the application ('W') is added if the window is not modal and is not marked as unclosable.

	// Edit modifier keys

	AddShortcut('X', B_COMMAND_KEY, new BMessage(B_CUT), NULL);
	AddShortcut('C', B_COMMAND_KEY, new BMessage(B_COPY), NULL);
	AddShortcut('V', B_COMMAND_KEY, new BMessage(B_PASTE), NULL);
	AddShortcut('A', B_COMMAND_KEY, new BMessage(B_SELECT_ALL), NULL);

	// Window modifier keys

	AddShortcut('M', B_COMMAND_KEY | B_CONTROL_KEY,
		new BMessage(_MINIMIZE_), NULL);
	AddShortcut('Z', B_COMMAND_KEY | B_CONTROL_KEY,
		new BMessage(_ZOOM_), NULL);
	AddShortcut('Z', B_SHIFT_KEY | B_COMMAND_KEY | B_CONTROL_KEY,
		new BMessage(_ZOOM_), NULL);
	AddShortcut('H', B_COMMAND_KEY | B_CONTROL_KEY,
		new BMessage(B_HIDE_APPLICATION), NULL);
	AddShortcut('F', B_COMMAND_KEY | B_CONTROL_KEY,
		new BMessage(_SEND_TO_FRONT_), NULL);
	AddShortcut('B', B_COMMAND_KEY | B_CONTROL_KEY,
		new BMessage(_SEND_BEHIND_), NULL);

These lines set up additional keyboard shortcuts for common actions related to text editing (B_CUT, B_COPY, B_PASTE, B_SELECT_ALL) and window management (_MINIMIZE_, _ZOOM_, B_HIDE_APPLICATION, _SEND_TO_FRONT_, _SEND_BEHIND_).

	// We set the default pulse rate, but we don't start the pulse
	fPulseRate = 500000;
	fPulseRunner = NULL;

	fIsFilePanel = false;

	fMenuSem = -1;

	fMinimized = false;

	fMaxZoomHeight = 32768.0;
	fMaxZoomWidth = 32768.0;
	fMinHeight = 0.0;
	fMinWidth = 0.0;
	fMaxHeight = 32768.0;
	fMaxWidth = 32768.0;

	fLastViewToken = B_NULL_TOKEN;

	// TODO: other initializations!
	fOffscreen = false;

These lines initialize additional member variables of the BWindow object, including pulse rate, window dimensions, file panel status, and other properties.

	// Create the server-side window

	port_id receivePort = create_port(B_LOOPER_PORT_DEFAULT_CAPACITY,
		"w<app_server");
	if (receivePort < B_OK) {
		// TODO: huh?
		debugger("Could not create BWindow's receive port, used for "
				 "interacting with the app_server!");
		delete this;
		return;
	}

This section of the function creates a port for receiving messages from the app_server, which is responsible for managing windows and user interface elements in Haiku OS.

	STRACE(("BWindow::InitData(): contacting app_server...\n"));

	// let app_server know that a window has been created.
	fLink = new(std::nothrow) BPrivate::PortLink(
		BApplication::Private::ServerLink()->SenderPort(), receivePort);
	if (fLink == NULL) {
		// Zombie!
		return;
	}

These lines establish communication with the app_server by creating a BPrivate::PortLink object (fLink) that connects to the app_server's sender port. This allows the BWindow object to send messages to the app_server and receive responses.

	{
		BPrivate::AppServerLink lockLink;
			// we're talking to the server application using our own
			// communication channel (fLink) - we better make sure no one
			// interferes by locking that channel (which AppServerLink does
			// implicetly)

		if (bitmapToken < 0) {
			fLink->StartMessage(AS_CREATE_WINDOW);
		} else {
			fLink->StartMessage(AS_CREATE_OFFSCREEN_WINDOW);
			fLink->Attach<int32>(bitmapToken);
			fOffscreen = true;
		}

		fLink->Attach<BRect>(fFrame);
		fLink->Attach<uint32>((uint32)fLook);
		fLink->Attach<uint32>((uint32)fFeel);
		fLink->Attach<uint32>(fFlags);
		fLink->Attach<uint32>(workspace);
		fLink->Attach<int32>(_get_object_token_(this));
		fLink->Attach<port_id>(receivePort);
		fLink->Attach<port_id>(fMsgPort);
		fLink->AttachString(title);

		port_id sendPort;
		int32 code;
		if (fLink->FlushWithReply(code) == B_OK
			&& code == B_OK
			&& fLink->Read<port_id>(&sendPort) == B_OK) {
			// read the frame size and its limits that were really
			// enforced on the server side

			fLink->Read<BRect>(&fFrame);
			fLink->Read<float>(&fMinWidth);
			fLink->Read<float>(&fMaxWidth);
			fLink->Read<float>(&fMinHeight);
			fLink->Read<float>(&fMaxHeight);

			fMaxZoomWidth = fMaxWidth;
			fMaxZoomHeight = fMaxHeight;
		} else
			sendPort = -1;

		// Redirect our link to the new window connection
		fLink->SetSenderPort(sendPort);
		STRACE(("Server says that our send port is %ld\n", sendPort));
	}

This part of the function constructs a message to create a new window on the app_server. Depending on whether an offscreen bitmap is specified (bitmapToken >= 0), the message type is set to create either a regular window or an offscreen window. Various parameters, such as the window frame, appearance, behavior, flags, workspace, and port IDs, are attached to the message. The message is then sent to the app_server, and the function waits for a reply containing information about the created window, such as its frame size and limits.

fLink->FlushWithReply(code) = This transfer the control to the app server i.e ServerApp.cpp

FlushWithReply() = Will wait until the target replies unless a timeout value is specified in microseconds.

	STRACE(("Window locked?: %s\n", IsLocked() ? "True" : "False"));

	_CreateTopView();
}

This line of code prints a trace message indicating whether the window is locked. After initializing the window data, the _CreateTopView() function is called to create the top-level view of the window.