From the last chapter we sent the message AS_CREATE_WINDOW
from the client BWindow::_InitData()
function.
The message was sent with FlushWithReply().
The message is received by the _DispatchMessage(…)
function at the location: src/servers/app/ServerApp.cpp
void
ServerApp::_DispatchMessage(int32 code, BPrivate::LinkReceiver& link)
{
switch (code) {
// other cases.....
case AS_CREATE_WINDOW:
case AS_CREATE_OFFSCREEN_WINDOW:
{
port_id clientReplyPort = -1;
status_t status = _CreateWindow(code, link, clientReplyPort);
// if sucessful, ServerWindow::Run() will already have replied
if (status < B_OK) {
// window creation failed, we need to notify the client
BPrivate::LinkSender reply(clientReplyPort);
reply.StartMessage(status);
reply.Flush();
}
break;
}
// other cases.....
}
The _DispatchMessage()
function handles incoming messages received by the application server from client application. This function uses a switch
statement to examine the message code (code
) and determine how to handle the incoming message. Each case in the switch
statement corresponds to a different type of message.
This cases handles message related to the creation of windows (AS_CREATE_WINDOW
) and offscreen windows (AS_CREATE_OFFSCREEN_WINDOW
). When one of these messages is received, the function calls the _CreateWindow()
method to create the window.
Let's demystify it:
status_t status = _CreateWindow(code, link, clientReplyPort);
The _CreateWindow()
method is responsible for parsing the message received via link
, creating the window accordingly, and setting status
to indicate whether the creation was successful or not.
Let's jump to _CreateWindow()
:
_CreateWindow(…)
Defined in the src/servers/app/ServerApp.cpp
status_t
ServerApp::_CreateWindow(int32 code, BPrivate::LinkReceiver& link,
port_id& clientReplyPort)
{
// Attached data:
// 1) int32 bitmap token (only for AS_CREATE_OFFSCREEN_WINDOW)
// 2) BRect window frame
// 3) uint32 window look
// 4) uint32 window feel
// 5) uint32 window flags
// 6) uint32 workspace index
// 7) int32 BHandler token of the window
// 8) port_id window's reply port
// 9) port_id window's looper port
// 10) const char * title
BRect frame;
int32 bitmapToken;
uint32 look;
uint32 feel;
uint32 flags;
uint32 workspaces;
int32 token;
port_id looperPort;
char* title;
if (code == AS_CREATE_OFFSCREEN_WINDOW)
link.Read<int32>(&bitmapToken);
link.Read<BRect>(&frame);
link.Read<uint32>(&look);
link.Read<uint32>(&feel);
link.Read<uint32>(&flags);
link.Read<uint32>(&workspaces);
link.Read<int32>(&token);
link.Read<port_id>(&clientReplyPort);
link.Read<port_id>(&looperPort);
if (link.ReadString(&title) != B_OK)
return B_ERROR;
if (!frame.IsValid()) {
// make sure we pass a valid rectangle to ServerWindow
frame.right = frame.left + 1;
frame.bottom = frame.top + 1;
}
status_t status = B_NO_MEMORY;
ServerWindow *window = NULL;
if (code == AS_CREATE_OFFSCREEN_WINDOW) {
ServerBitmap* bitmap = GetBitmap(bitmapToken);
if (bitmap != NULL) {
window = new (nothrow) OffscreenServerWindow(title, this,
clientReplyPort, looperPort, token, bitmap);
} else
status = B_ERROR;
} else {
window = new (nothrow) ServerWindow(title, this, clientReplyPort,
looperPort, token);
STRACE(("\nServerApp %s: New Window %s (%g:%g, %g:%g)\n",
Signature(), title, frame.left, frame.top,
frame.right, frame.bottom));
}
free(title);
// NOTE: the reply to the client is handled in ServerWindow::Run()
if (window != NULL) {
status = window->Init(frame, (window_look)look, (window_feel)feel,
flags, workspaces);
if (status == B_OK) {
status = window->Run();
if (status != B_OK) {
syslog(LOG_ERR, "ServerApp::_CreateWindow() - failed to run "
"the window thread\n");
}
}
if (status != B_OK)
delete window;
}
return status;
}
Parameters:
code
: An integer representing the type of window to create (eitherAS_CREATE_WINDOW
orAS_CREATE_OFFSCREEN_WINDOW
).link
: A reference to aBPrivate::LinkReceiver
object, used for receiving data related to the window creation message from the client application.clientReplyPort
: A refernce to aport_id
variable that will be used to reply to the client application with the status of the window creation process.
// Attached data:
// 1) int32 bitmap token (only for AS_CREATE_OFFSCREEN_WINDOW)
// 2) BRect window frame
// 3) uint32 window look
// 4) uint32 window feel
// 5) uint32 window flags
// 6) uint32 workspace index
// 7) int32 BHandler token of the window
// 8) port_id window's reply port
// 9) port_id window's looper port
// 10) const char * title
This comment explains the data that is expected to be attached to the message received from the client application. It includes information such as the window's frame, appearance, behavior, flags, workspace index, handler token, reply port, looper port, and title.
BRect frame;
int32 bitmapToken;
uint32 look;
uint32 feel;
uint32 flags;
uint32 workspaces;
int32 token;
port_id looperPort;
char* title;
These variables are declared to hold the data extracted from the received message. Each variable corresponds to one of the data items mentioned in the comment above.
if (code == AS_CREATE_OFFSCREEN_WINDOW)
link.Read<int32>(&bitmapToken);
link.Read<BRect>(&frame);
link.Read<uint32>(&look);
link.Read<uint32>(&feel);
link.Read<uint32>(&flags);
link.Read<uint32>(&workspaces);
link.Read<int32>(&token);
link.Read<port_id>(&clientReplyPort);
link.Read<port_id>(&looperPort);
if (link.ReadString(&title) != B_OK)
return B_ERROR;
This section reads the data from the received message using the Read() method of the BPrivate::LinkReceiver
object. It reads each data item in the order specified in the comment above and assigns them to the corresponding variables.
if (!frame.IsValid()) {
// make sure we pass a valid rectangle to ServerWindow
frame.right = frame.left + 1;
frame.bottom = frame.top + 1;
}
This code ensures that the window frame is valid. If the frame is not valid (e.g., has zero width or height), it adjusts the frame to be a minimum size of 1x1.
status_t status = B_NO_MEMORY;
ServerWindow *window = NULL;
if (code == AS_CREATE_OFFSCREEN_WINDOW) {
ServerBitmap* bitmap = GetBitmap(bitmapToken);
if (bitmap != NULL) {
window = new (nothrow) OffscreenServerWindow(title, this,
clientReplyPort, looperPort, token, bitmap);
} else
status = B_ERROR;
} else {
window = new (nothrow) ServerWindow(title, this, clientReplyPort,
looperPort, token);
STRACE(("\nServerApp %s: New Window %s (%g:%g, %g:%g)\n",
Signature(), title, frame.left, frame.top,
frame.right, frame.bottom));
}
Here, based on the code, the function decides whether to create a regular window or an offscreen window. If creating an offscreen window, it retrieves the bitmap associated with the provided token using GetBitmap(). Then, it creates either an OffscreenServerWindow
or a ServerWindow
object accordingly. The STRACE() call is for debugging and tracing purposes.
We will explore the constructor ServerWindow(.....)
in the down section, once we explain this complete function.
free(title);
This line frees the memory allocated for the window title string.
// NOTE: the reply to the client is handled in ServerWindow::Run()
if (window != NULL) {
status = window->Init(frame, (window_look)look, (window_feel)feel,
flags, workspaces);
if (status == B_OK) {
status = window->Run();
if (status != B_OK) {
syslog(LOG_ERR, "ServerApp::_CreateWindow() - failed to run "
"the window thread\n");
}
}
if (status != B_OK)
delete window;
}
This section initializes and runs the created window. First, it calls the Init() method of the window object to initialize it with the provided parameters. If initialization is successful (status == B_OK
), it then calls the Run() method to start the window's event loop. If initialization or running the window fails, it deletes the window object.
return status;
}
Finally, the function returns the status of the window creation process to the caller. This status indicates whether the creation was successful or if an error occurred during the process.
This constructor function ServerWindow(. . . . .)
is called from the _CreateWindow
function above.
This is defined in the file src/servers/app/ServerWindow.cpp
.
/*! Sets up the basic BWindow counterpart - you have to call Init() before
you can actually use it, though.
*/
ServerWindow::ServerWindow(const char* title, ServerApp* app,
port_id clientPort, port_id looperPort, int32 clientToken)
:
MessageLooper(title && *title ? title : "Unnamed Window"),
fTitle(NULL),
fDesktop(app->GetDesktop()),
fServerApp(app),
fWindowAddedToDesktop(false),
fClientTeam(app->ClientTeam()),
fMessagePort(-1),
fClientReplyPort(clientPort),
fClientLooperPort(looperPort),
fClientToken(clientToken),
fCurrentView(NULL),
fCurrentDrawingRegion(),
fCurrentDrawingRegionValid(false),
fIsDirectlyAccessing(false)
{
STRACE(("ServerWindow(%s)::ServerWindow()\n", title));
SetTitle(title);
fServerToken = BPrivate::gDefaultTokens.NewToken(B_SERVER_TOKEN, this);
BMessenger::Private(fFocusMessenger).SetTo(fClientTeam,
looperPort, B_PREFERRED_TOKEN);
BMessenger::Private(fHandlerMessenger).SetTo(fClientTeam,
looperPort, clientToken);
fEventTarget.SetTo(fFocusMessenger);
fDeathSemaphore = create_sem(0, "window death");
}
This constructor initializes the ServerWindow
object. It takes several parameters:
title
: The title of the window.app
: A pointer to theServerApp
object representing the server application.clientPort
: The port ID to which messages will be sent to the communicate with the client application.looperPort
: The port ID of the looper port associated with the client application.clientToken
: A token representing the client application's handler.
Let's tear it apart:
MessageLooper(title && *title ? title : "Unnamed Window"),
It initializes the MessageLooper
base class with the provided window title, or a default title if no title is provided.
fTitle(NULL),
fDesktop(app->GetDesktop()),
fServerApp(app),
fWindowAddedToDesktop(false),
fClientTeam(app->ClientTeam()),
fMessagePort(-1),
fClientReplyPort(clientPort),
fClientLooperPort(looperPort),
fClientToken(clientToken),
fCurrentView(NULL),
fCurrentDrawingRegion(),
fCurrentDrawingRegionValid(false),
fIsDirectlyAccessing(false)
These lines initializes member variables of the ServerWindow
class:
fTitle
: A pointer to the window's title string.fDesktop
: A pointer to the desktop object associated with the server application.fServerApp
: A pointer to theServerApp
object representing the server application.fWindowAddedToDesktop
: A Boolean flag indicating whether the window has been added to the desktop.fClientTeam
: The team ID of the client application.fMessagePort
: The port ID for the message port associated with the window.fClientReplyPort
: The port ID to which replies to client messages will be sent.fClientLooperPort
: The port ID of the looper port associated with the client application.-
fClientToken
: The token representing the client application's handler. fCurrentView
: A pointer to the current view being drawn (active view).fCurrentDrawingRegion
: The current drawing region.fCurrentDrawingRegionValid
: A Boolean flag indicating whether the drawing region is valid.fIsDirectlyAccessing
: A flag indicating whether the server application is directly accessing the window.
{
STRACE(("ServerWindow(%s)::ServerWindow()\n", title));
SetTitle(title);
fServerToken = BPrivate::gDefaultTokens.NewToken(B_SERVER_TOKEN, this);
BMessenger::Private(fFocusMessenger).SetTo(fClientTeam,
looperPort, B_PREFERRED_TOKEN);
BMessenger::Private(fHandlerMessenger).SetTo(fClientTeam,
looperPort, clientToken);
fEventTarget.SetTo(fFocusMessenger);
fDeathSemaphore = create_sem(0, "window death");
}
This part of the constructor performs additional initialization steps:
- It traces the construction of the window with a debug message.
- It sets the title of the window using the SetTitle() method.
- It generates a new server token for the window.
- It sets up messengers for focus and handler communication with the client application.
- It initializes the event target to the focus messenger.
- It creates a semaphore named "window death" to handle the destruction of the window.
Overall, this constructor sets up the basic attributes and communication channels for a ServerWindow
instance within the server application.
Now, the ServerWindow
instance is created. After this in _CreateWindow
there is an call to Init(. . . . .)
through ServerWindow
instance. As shown below:
status = window->Init(frame, (window_look)look, (window_feel)feel,
flags, workspaces);
Let's scan it up:
status_t
ServerWindow::Init(BRect frame, window_look look, window_feel feel,
uint32 flags, uint32 workspace)
{
if (!App()->AddWindow(this)) {
fServerApp = NULL;
return B_NO_MEMORY;
}
if (fTitle == NULL)
return B_NO_MEMORY;
// fMessagePort is the port to which the app sends messages for the server
fMessagePort = create_port(100, fTitle);
if (fMessagePort < B_OK)
return fMessagePort;
fLink.SetSenderPort(fClientReplyPort);
fLink.SetReceiverPort(fMessagePort);
// We cannot call MakeWindow in the constructor, since it
// is a virtual function!
fWindow.SetTo(MakeWindow(frame, fTitle, look, feel, flags, workspace));
if (!fWindow.IsSet() || fWindow->InitCheck() != B_OK) {
fWindow.Unset();
return B_NO_MEMORY;
}
if (!fWindow->IsOffscreenWindow()) {
fDesktop->AddWindow(fWindow.Get());
fWindowAddedToDesktop = true;
}
return B_OK;
}
This function takes five parameters:
frame
: The initial frame (position and size) of the window.look
: The appearance style of the window.feel
: The behavior style of the window.flags
: Flags specifying additional attributes or behavior of the window.workspace
: The index of the workspace where the window should appear.
if (!App()->AddWindow(this)) {
fServerApp = NULL;
return B_NO_MEMORY;
}
This section attempts to add the window to the server application's list of windows. If the addition fails (e.g., due to memory allocation issues), it sets fServerApp
to NULL
and returns B_NO_MEMORY
.
if (fTitle == NULL)
return B_NO_MEMORY;
If the title of the window is NULL
, it returns B_NO_MEMORY
.
// fMessagePort is the port to which the app sends messages for the server
fMessagePort = create_port(100, fTitle);
if (fMessagePort < B_OK)
return fMessage
It creates a message port (fMessagePort
) for the window to receive messages. If creating the port fails, it returns the error code.
fLink.SetSenderPort(fClientReplyPort);
fLink.SetReceiverPort(fMessagePort);
It configures the fLink
object to use the client reply port as the sender port and the message port as the receiver port. This link is used for communication between the client and the server.
fWindow.SetTo(MakeWindow(frame, fTitle, look, feel, flags, workspace));
if (!fWindow.IsSet() || fWindow->InitCheck() != B_OK) {
fWindow.Unset();
return B_NO_MEMORY;
}
It creates the window using the MakeWindow()
method, passing the provided parameters. If creating the window fails or if the window's initialization check fails, it unsets the window and returns B_NO_MEMORY
.
In the next section we will inspect MakeWindow()
.
if (!fWindow->IsOffscreenWindow()) {
fDesktop->AddWindow(fWindow.Get());
fWindowAddedToDesktop = true;
}
If the window is not an offscreen window, it adds the window to the desktop using the AddWindow()
method of the desktop. It also sets fWindowAddedToDesktop
to true.
return B_OK;
}
Finally, if everything succeeds, it returns B_OK
to indicate successful initialization of the window.
MakeWindow(. . . . . .)
It is defined in the file src/servers/app/ServerWindow.cpp
Window*
ServerWindow::MakeWindow(BRect frame, const char* name,
window_look look, window_feel feel, uint32 flags, uint32 workspace)
{
// The non-offscreen ServerWindow uses the DrawingEngine instance from
// the desktop.
return new(std::nothrow) ::Window(frame, name, look, feel, flags,
workspace, this, fDesktop->HWInterface()->CreateDrawingEngine());
}
This method creates and returns a Window
object based on the provided parameters.
It takes the following parameters:
frame
: The initial frame (position and size) of the window.name
: The name or title of the window.look
: The appearance style of the window.feel
: The behavior style of the window.flags
: Additional flags specifying window behavior.workspace
: The index of the workspace where the window should appear.
// The non-offscreen ServerWindow uses the DrawingEngine instance from
// the desktop.
return new(std::nothrow) ::Window(frame, name, look, feel, flags,
workspace, this, fDesktop->HWInterface()->CreateDrawingEngine());
}
It creates a new Window object using the provided parameters. The Window constructor initializes the window with the specified frame, name, appearance, behavior, flags, workspace index, and a pointer to the ServerWindow
instance (this
). Additionally, it provides a drawing engine created by the desktop's hardware interface (HWInterface()
).
The new(std::nothrow
) syntax is used to allocate memory for the Window object without throwing an exception if the allocation fails. Instead, it returns a null pointer if the allocation fails.
::Window(. . . . . . . . )
This constructor function is defined in src/servers/app/Window.cpp
Window::Window(const BRect& frame, const char *name,
window_look look, window_feel feel, uint32 flags, uint32 workspaces,
::ServerWindow* window, DrawingEngine* drawingEngine)
:
fTitle(name),
fFrame(frame),
fScreen(NULL),
fVisibleRegion(),
fVisibleContentRegion(),
fDirtyRegion(),
fContentRegion(),
fEffectiveDrawingRegion(),
fVisibleContentRegionValid(false),
fContentRegionValid(false),
fEffectiveDrawingRegionValid(false),
fRegionPool(),
fWindow(window),
fDrawingEngine(drawingEngine),
fDesktop(window->Desktop()),
fCurrentUpdateSession(&fUpdateSessions[0]),
fPendingUpdateSession(&fUpdateSessions[1]),
fUpdateRequested(false),
fInUpdate(false),
fUpdatesEnabled(false),
// Windows start hidden
fHidden(true),
// Hidden is 1 or more
fShowLevel(1),
fMinimized(false),
fIsFocus(false),
fLook(look),
fFeel(feel),
fWorkspaces(workspaces),
fCurrentWorkspace(-1),
fMinWidth(1),
fMaxWidth(32768),
fMinHeight(1),
fMaxHeight(32768),
fWorkspacesViewCount(0)
{
_InitWindowStack();
// make sure our arguments are valid
if (!IsValidLook(fLook))
fLook = B_TITLED_WINDOW_LOOK;
if (!IsValidFeel(fFeel))
fFeel = B_NORMAL_WINDOW_FEEL;
SetFlags(flags, NULL);
if (fLook != B_NO_BORDER_WINDOW_LOOK && fCurrentStack.IsSet()) {
// allocates a decorator
::Decorator* decorator = Decorator();
if (decorator != NULL) {
decorator->GetSizeLimits(&fMinWidth, &fMinHeight, &fMaxWidth,
&fMaxHeight);
}
}
if (fFeel != kOffscreenWindowFeel)
fWindowBehaviour.SetTo(gDecorManager.AllocateWindowBehaviour(this));
// do we need to change our size to let the decorator fit?
// _ResizeBy() will adapt the frame for validity before resizing
if (feel == kDesktopWindowFeel) {
// the desktop window spans over the whole screen
// TODO: this functionality should be moved somewhere else
// (so that it is always used when the workspace is changed)
uint16 width, height;
uint32 colorSpace;
float frequency;
if (Screen() != NULL) {
Screen()->GetMode(width, height, colorSpace, frequency);
// TODO: MOVE THIS AWAY!!! ResizeBy contains calls to virtual methods!
// Also, there is no TopView()!
fFrame.OffsetTo(B_ORIGIN);
// ResizeBy(width - frame.Width(), height - frame.Height(), NULL);
}
}
STRACE(("Window %p, %s:\n", this, Name()));
STRACE(("\tFrame: (%.1f, %.1f, %.1f, %.1f)\n", fFrame.left, fFrame.top,
fFrame.right, fFrame.bottom));
STRACE(("\tWindow %s\n", window ? window->Title() : "NULL"));
}
This constructor initializes a Window object with the provided parameters.
It takes the following parameters:
- frame: The initial frame (position and size) of the window.
- name: The name or title of the window.
- look: The appearance style of the window.
- feel: The behavior style of the window.
- flags: Additional flags specifying window behavior.
- workspaces: The index of the workspace where the window should appear.
- window: A pointer to the ServerWindow instance that owns the window.
- drawingEngine: A pointer to the drawing engine associated with the window.
:
fTitle(name),
fFrame(frame),
fScreen(NULL),
fVisibleRegion(),
fVisibleContentRegion(),
fDirtyRegion(),
fContentRegion(),
fEffectiveDrawingRegion(),
fVisibleContentRegionValid(false),
fContentRegionValid(false),
fEffectiveDrawingRegionValid(false),
fRegionPool(),
fWindow(window),
fDrawingEngine(drawingEngine),
fDesktop(window->Desktop()),
fCurrentUpdateSession(&fUpdateSessions[0]),
fPendingUpdateSession(&fUpdateSessions[1]),
fUpdateRequested(false),
fInUpdate(false),
fUpdatesEnabled(false),
// Windows start hidden
fHidden(true),
// Hidden is 1 or more
fShowLevel(1),
fMinimized(false),
fIsFocus(false),
fLook(look),
fFeel(feel),
fWorkspaces(workspaces),
fCurrentWorkspace(-1),
fMinWidth(1),
fMaxWidth(32768),
fMinHeight(1),
fMaxHeight(32768),
fWorkspacesViewCount(0)
This part initializes various member variables of the Window class, including:
fTitle
: The title of the window.fFrame
: The frame (position and size) of the window.fScreen
: A pointer to the screen associated with the window.- fVisibleRegion, fVisibleContentRegion, fDirtyRegion: Regions representing the visible part, visible content, and dirty areas of the window, respectively.
- fContentRegion, fEffectiveDrawingRegion: Regions representing the content area and the effective drawing region of the window, respectively.
- Various flags and state variables related to window updates, visibility, focus, etc.
- fLook, fFeel: The appearance and behavior styles of the window.
- fWorkspaces: The index of the workspace where the window should appear.
- Size limits for the window (fMinWidth, fMaxWidth, fMinHeight, fMaxHeight).
_InitWindowStack();
This line initializes the window stack, which manages the order of windows on the screen.
// make sure our arguments are valid
if (!IsValidLook(fLook))
fLook = B_TITLED_WINDOW_LOOK;
if (!IsValidFeel(fFeel))
fFeel = B_NORMAL_WINDOW_FEEL;
SetFlags(flags, NULL);
if (fLook != B_NO_BORDER_WINDOW_LOOK && fCurrentStack.IsSet()) {
// allocates a decorator
::Decorator* decorator = Decorator();
if (decorator != NULL) {
decorator->GetSizeLimits(&fMinWidth, &fMinHeight, &fMaxWidth,
&fMaxHeight);
}
}
if (fFeel != kOffscreenWindowFeel)
fWindowBehaviour.SetTo(gDecorManager.AllocateWindowBehaviour(this));
- Ensures that the provided look and feel styles are valid. If not, it sets default values.
- It sets window flags based on the provided flags.
- If the window has a border and a decorator is available, it retrieves size limits from the decorator.
- It allocates a window behavior if the window is not an offscreen window.
if (feel == kDesktopWindowFeel) {
// the desktop window spans over the whole screen
uint16 width, height;
uint32 colorSpace;
float frequency;
if (Screen() != NULL) {
Screen()->GetMode(width, height, colorSpace, frequency);
fFrame.OffsetTo(B_ORIGIN);
}
}
If the window's feel is kDesktopWindowFeel
, it adjusts the frame to span the entire screen.
Now, in the next chapter we trace all these function right from the _InitWindowStack()