haiku: Actually remove BDirectWindow and fix OpenGL handling.

This provides the other half of 05c72b113c830392f8b08532317ef07972b29a2e.
This commit is contained in:
waddlesplash 2022-02-05 21:23:40 -05:00 committed by Ryan C. Gordon
parent 9403543671
commit 423feac69b
1 changed files with 235 additions and 183 deletions

View File

@ -39,7 +39,6 @@ extern "C" {
#include <AppKit.h> #include <AppKit.h>
#include <Cursor.h> #include <Cursor.h>
#include <InterfaceKit.h> #include <InterfaceKit.h>
#include <game/DirectWindow.h>
#if SDL_VIDEO_OPENGL #if SDL_VIDEO_OPENGL
#include <opengl/GLView.h> #include <opengl/GLView.h>
#endif #endif
@ -58,19 +57,48 @@ enum WinCommands {
BWIN_SET_TITLE, BWIN_SET_TITLE,
BWIN_SET_BORDERED, BWIN_SET_BORDERED,
BWIN_SET_RESIZABLE, BWIN_SET_RESIZABLE,
BWIN_FULLSCREEN BWIN_FULLSCREEN,
BWIN_UPDATE_FRAMEBUFFER,
BWIN_MINIMUM_SIZE_WINDOW
}; };
// non-OpenGL framebuffer view
class SDL_BView: public BView
{
public:
SDL_BView(BRect frame, const char* name, uint32 resizingMode)
: BView(frame, name, resizingMode, B_WILL_DRAW),
fBitmap(NULL)
{
}
class SDL_BWin:public BDirectWindow void Draw(BRect dirty)
{
if (fBitmap != NULL)
DrawBitmap(fBitmap, B_ORIGIN);
}
void SetBitmap(BBitmap *bitmap)
{
fBitmap = bitmap;
}
private:
BBitmap *fBitmap;
};
class SDL_BWin: public BWindow
{ {
public: public:
/* Constructor/Destructor */ /* Constructor/Destructor */
SDL_BWin(BRect bounds, window_look look, uint32 flags) SDL_BWin(BRect bounds, window_look look, uint32 flags)
: BDirectWindow(bounds, "Untitled", look, B_NORMAL_WINDOW_FEEL, flags) : BWindow(bounds, "Untitled", look, B_NORMAL_WINDOW_FEEL, flags)
{ {
_last_buttons = 0; _last_buttons = 0;
_cur_view = NULL;
_SDL_View = NULL;
#if SDL_VIDEO_OPENGL #if SDL_VIDEO_OPENGL
_SDL_GLView = NULL; _SDL_GLView = NULL;
_gl_type = 0; _gl_type = 0;
@ -79,59 +107,88 @@ class SDL_BWin:public BDirectWindow
_inhibit_resize = false; _inhibit_resize = false;
_mouse_focused = false; _mouse_focused = false;
_prev_frame = NULL; _prev_frame = NULL;
_fullscreen = NULL;
/* Handle framebuffer stuff */ /* Handle framebuffer stuff */
_connected = _connection_disabled = false;
_buffer_created = _buffer_dirty = false;
_trash_window_buffer = false;
_buffer_locker = new BLocker(); _buffer_locker = new BLocker();
_bitmap = NULL; _bitmap = NULL;
_clips = NULL;
_num_clips = 0;
#ifdef DRAWTHREAD
_draw_thread_id = spawn_thread(HAIKU_DrawThread, "drawing_thread",
B_NORMAL_PRIORITY, (void*) this);
resume_thread(_draw_thread_id);
#endif
} }
virtual ~ SDL_BWin() virtual ~ SDL_BWin()
{ {
Lock(); Lock();
_connection_disabled = true;
int32 result; if (_SDL_View != NULL && _SDL_View != _cur_view) {
delete _SDL_View;
_SDL_View = NULL;
}
#if SDL_VIDEO_OPENGL #if SDL_VIDEO_OPENGL
if (_SDL_GLView) { if (_SDL_GLView) {
_SDL_GLView->UnlockGL(); if (((SDL_BApp*)be_app)->GetCurrentContext() == _SDL_GLView)
RemoveChild(_SDL_GLView); /* Why was this outside the if ((SDL_BApp*)be_app)->SetCurrentContext(NULL);
statement before? */ if (_SDL_GLView == _cur_view)
RemoveChild(_SDL_GLView);
_SDL_GLView = NULL;
// _SDL_GLView deleted by HAIKU_GL_DeleteContext
} }
#endif #endif
Unlock(); Unlock();
#if SDL_VIDEO_OPENGL
if (_SDL_GLView) {
delete _SDL_GLView;
}
#endif
delete _prev_frame; delete _prev_frame;
/* Clean up framebuffer stuff */ /* Clean up framebuffer stuff */
_buffer_locker->Lock(); _buffer_locker->Lock();
#ifdef DRAWTHREAD
wait_for_thread(_draw_thread_id, &result);
#endif
SDL_free(_clips);
delete _buffer_locker; delete _buffer_locker;
} }
void SetCurrentView(BView *view)
{
if (_cur_view != view) {
if (_cur_view != NULL)
RemoveChild(_cur_view);
_cur_view = view;
if (_cur_view != NULL)
AddChild(_cur_view);
}
}
void UpdateCurrentView()
{
if (_SDL_GLView != NULL) {
SetCurrentView(_SDL_GLView);
} else if (_SDL_View != NULL) {
SetCurrentView(_SDL_View);
} else {
SetCurrentView(NULL);
}
}
SDL_BView *CreateView() {
Lock();
if (_SDL_View == NULL) {
_SDL_View = new SDL_BView(Bounds(), "SDL View", B_FOLLOW_ALL_SIDES);
UpdateCurrentView();
}
Unlock();
return _SDL_View;
}
void RemoveView() {
Lock();
if(_SDL_View != NULL) {
SDL_BView *oldView = _SDL_View;
_SDL_View = NULL;
UpdateCurrentView();
delete oldView;
}
Unlock();
}
/* * * * * OpenGL functionality * * * * */ /* * * * * OpenGL functionality * * * * */
#if SDL_VIDEO_OPENGL #if SDL_VIDEO_OPENGL
virtual BGLView *CreateGLView(Uint32 gl_flags) { BGLView *CreateGLView(Uint32 gl_flags) {
Lock(); Lock();
if (_SDL_GLView == NULL) { if (_SDL_GLView == NULL) {
_SDL_GLView = new BGLView(Bounds(), "SDL GLView", _SDL_GLView = new BGLView(Bounds(), "SDL GLView",
@ -139,92 +196,29 @@ class SDL_BWin:public BDirectWindow
(B_WILL_DRAW | B_FRAME_EVENTS), (B_WILL_DRAW | B_FRAME_EVENTS),
gl_flags); gl_flags);
_gl_type = gl_flags; _gl_type = gl_flags;
UpdateCurrentView();
} }
AddChild(_SDL_GLView);
_SDL_GLView->EnableDirectMode(false); /* Disable direct mode */
_SDL_GLView->LockGL(); /* "New" GLViews are created */
Unlock(); Unlock();
return (_SDL_GLView); return _SDL_GLView;
} }
virtual void RemoveGLView() { void RemoveGLView() {
Lock(); Lock();
if(_SDL_GLView) { if(_SDL_GLView != NULL) {
_SDL_GLView->UnlockGL(); if (((SDL_BApp*)be_app)->GetCurrentContext() == _SDL_GLView)
RemoveChild(_SDL_GLView); ((SDL_BApp*)be_app)->SetCurrentContext(NULL);
_SDL_GLView = NULL;
UpdateCurrentView();
// _SDL_GLView deleted by HAIKU_GL_DeleteContext
} }
Unlock(); Unlock();
} }
virtual void SwapBuffers(void) { void SwapBuffers(void) {
_SDL_GLView->UnlockGL();
_SDL_GLView->LockGL();
_SDL_GLView->SwapBuffers(); _SDL_GLView->SwapBuffers();
} }
#endif #endif
/* * * * * Framebuffering* * * * */
virtual void DirectConnected(direct_buffer_info *info) {
if(!_connected && _connection_disabled) {
return;
}
/* Determine if the pixel buffer is usable after this update */
_trash_window_buffer = _trash_window_buffer
|| ((info->buffer_state & B_BUFFER_RESIZED)
|| (info->buffer_state & B_BUFFER_RESET)
|| (info->driver_state == B_MODE_CHANGED));
LockBuffer();
switch(info->buffer_state & B_DIRECT_MODE_MASK) {
case B_DIRECT_START:
_connected = true;
case B_DIRECT_MODIFY:
if (info->clip_list_count > _num_clips)
{
if(_clips) {
SDL_free(_clips);
_clips = NULL;
}
}
_num_clips = info->clip_list_count;
if (_clips == NULL)
_clips = (clipping_rect *)SDL_malloc(_num_clips*sizeof(clipping_rect));
if(_clips) {
SDL_memcpy(_clips, info->clip_list,
_num_clips*sizeof(clipping_rect));
_bits = (uint8*) info->bits;
_row_bytes = info->bytes_per_row;
_bounds = info->window_bounds;
_bytes_per_px = info->bits_per_pixel / 8;
_buffer_dirty = true;
}
break;
case B_DIRECT_STOP:
_connected = false;
break;
}
#if SDL_VIDEO_OPENGL
if(_SDL_GLView) {
_SDL_GLView->DirectConnected(info);
}
#endif
/* Call the base object directconnected */
BDirectWindow::DirectConnected(info);
UnlockBuffer();
}
/* * * * * Event sending * * * * */ /* * * * * Event sending * * * * */
/* Hook functions */ /* Hook functions */
virtual void FrameMoved(BPoint origin) { virtual void FrameMoved(BPoint origin) {
@ -235,10 +229,10 @@ class SDL_BWin:public BDirectWindow
_PostWindowEvent(msg); _PostWindowEvent(msg);
/* Perform normal hook operations */ /* Perform normal hook operations */
BDirectWindow::FrameMoved(origin); BWindow::FrameMoved(origin);
} }
virtual void FrameResized(float width, float height) { void FrameResized(float width, float height) {
/* Post a message to the BApp so that it can handle the window event */ /* Post a message to the BApp so that it can handle the window event */
BMessage msg(BAPP_WINDOW_RESIZED); BMessage msg(BAPP_WINDOW_RESIZED);
@ -247,10 +241,10 @@ class SDL_BWin:public BDirectWindow
_PostWindowEvent(msg); _PostWindowEvent(msg);
/* Perform normal hook operations */ /* Perform normal hook operations */
BDirectWindow::FrameResized(width, height); BWindow::FrameResized(width, height);
} }
virtual bool QuitRequested() { bool QuitRequested() {
BMessage msg(BAPP_WINDOW_CLOSE_REQUESTED); BMessage msg(BAPP_WINDOW_CLOSE_REQUESTED);
_PostWindowEvent(msg); _PostWindowEvent(msg);
@ -258,13 +252,13 @@ class SDL_BWin:public BDirectWindow
return false; return false;
} }
virtual void WindowActivated(bool active) { void WindowActivated(bool active) {
BMessage msg(BAPP_KEYBOARD_FOCUS); /* Mouse focus sold separately */ BMessage msg(BAPP_KEYBOARD_FOCUS); /* Mouse focus sold separately */
msg.AddBool("focusGained", active); msg.AddBool("focusGained", active);
_PostWindowEvent(msg); _PostWindowEvent(msg);
} }
virtual void Zoom(BPoint origin, void Zoom(BPoint origin,
float width, float width,
float height) { float height) {
BMessage msg(BAPP_MAXIMIZE); /* Closest thing to maximization Haiku has */ BMessage msg(BAPP_MAXIMIZE); /* Closest thing to maximization Haiku has */
@ -275,13 +269,13 @@ class SDL_BWin:public BDirectWindow
_prev_frame = new BRect(Frame()); _prev_frame = new BRect(Frame());
/* Perform normal hook operations */ /* Perform normal hook operations */
BDirectWindow::Zoom(origin, width, height); BWindow::Zoom(origin, width, height);
} }
/* Member functions */ /* Member functions */
virtual void Show() { void Show() {
while(IsHidden()) { while(IsHidden()) {
BDirectWindow::Show(); BWindow::Show();
} }
_shown = true; _shown = true;
@ -289,25 +283,33 @@ class SDL_BWin:public BDirectWindow
_PostWindowEvent(msg); _PostWindowEvent(msg);
} }
virtual void Hide() { void Hide() {
BDirectWindow::Hide(); BWindow::Hide();
_shown = false; _shown = false;
BMessage msg(BAPP_HIDE); BMessage msg(BAPP_HIDE);
_PostWindowEvent(msg); _PostWindowEvent(msg);
} }
virtual void Minimize(bool minimize) { void Minimize(bool minimize) {
BDirectWindow::Minimize(minimize); BWindow::Minimize(minimize);
int32 minState = (minimize ? BAPP_MINIMIZE : BAPP_RESTORE); int32 minState = (minimize ? BAPP_MINIMIZE : BAPP_RESTORE);
BMessage msg(minState); BMessage msg(minState);
_PostWindowEvent(msg); _PostWindowEvent(msg);
} }
void ScreenChanged(BRect screenFrame, color_space depth)
{
if (_fullscreen) {
MoveTo(screenFrame.left, screenFrame.top);
ResizeTo(screenFrame.Width(), screenFrame.Height());
}
}
/* BView message interruption */ /* BView message interruption */
virtual void DispatchMessage(BMessage * msg, BHandler * target) void DispatchMessage(BMessage * msg, BHandler * target)
{ {
BPoint where; /* Used by mouse moved */ BPoint where; /* Used by mouse moved */
int32 buttons; /* Used for mouse button events */ int32 buttons; /* Used for mouse button events */
@ -356,7 +358,7 @@ class SDL_BWin:public BDirectWindow
} }
} }
break; break;
case B_UNMAPPED_KEY_DOWN: /* modifier keys are unmapped */ case B_UNMAPPED_KEY_DOWN: /* modifier keys are unmapped */
if (msg->FindInt32("key", &key) == B_OK) { if (msg->FindInt32("key", &key) == B_OK) {
_KeyEvent((SDL_Scancode)key, NULL, 0, SDL_PRESSED); _KeyEvent((SDL_Scancode)key, NULL, 0, SDL_PRESSED);
@ -376,15 +378,15 @@ class SDL_BWin:public BDirectWindow
- CTRL+Q to close window (and other shortcuts) - CTRL+Q to close window (and other shortcuts)
- PrintScreen to make screenshot into /boot/home - PrintScreen to make screenshot into /boot/home
- etc.. */ - etc.. */
/* BDirectWindow::DispatchMessage(msg, target); */ /* BWindow::DispatchMessage(msg, target); */
break; break;
} }
BDirectWindow::DispatchMessage(msg, target); BWindow::DispatchMessage(msg, target);
} }
/* Handle command messages */ /* Handle command messages */
virtual void MessageReceived(BMessage* message) { void MessageReceived(BMessage* message) {
switch (message->what) { switch (message->what) {
/* Handle commands from SDL */ /* Handle commands from SDL */
case BWIN_SET_TITLE: case BWIN_SET_TITLE:
@ -396,12 +398,18 @@ class SDL_BWin:public BDirectWindow
case BWIN_RESIZE_WINDOW: case BWIN_RESIZE_WINDOW:
_ResizeTo(message); _ResizeTo(message);
break; break;
case BWIN_SET_BORDERED: case BWIN_SET_BORDERED: {
_SetBordered(message); bool bEnabled;
if (message->FindBool("window-border", &bEnabled) == B_OK)
_SetBordered(bEnabled);
break; break;
case BWIN_SET_RESIZABLE: }
_SetResizable(message); case BWIN_SET_RESIZABLE: {
bool bEnabled;
if (message->FindBool("window-resizable", &bEnabled) == B_OK)
_SetResizable(bEnabled);
break; break;
}
case BWIN_SHOW_WINDOW: case BWIN_SHOW_WINDOW:
Show(); Show();
break; break;
@ -417,12 +425,34 @@ class SDL_BWin:public BDirectWindow
case BWIN_RESTORE_WINDOW: case BWIN_RESTORE_WINDOW:
_Restore(); _Restore();
break; break;
case BWIN_FULLSCREEN: case BWIN_FULLSCREEN: {
_SetFullScreen(message); bool fullscreen;
if (message->FindBool("fullscreen", &fullscreen) == B_OK)
_SetFullScreen(fullscreen);
break; break;
}
case BWIN_MINIMUM_SIZE_WINDOW:
_SetMinimumSize(message);
break;
case BWIN_UPDATE_FRAMEBUFFER: {
BMessage* pendingMessage;
while ((pendingMessage
= MessageQueue()->FindMessage(BWIN_UPDATE_FRAMEBUFFER, 0))) {
MessageQueue()->RemoveMessage(pendingMessage);
delete pendingMessage;
}
if (_bitmap != NULL) {
if (_SDL_View != NULL && _cur_view == _SDL_View)
_SDL_View->Draw(Bounds());
else if (_SDL_GLView != NULL && _cur_view == _SDL_GLView) {
_SDL_GLView->CopyPixelsIn(_bitmap, B_ORIGIN);
}
}
break;
}
default: default:
/* Perform normal message handling */ /* Perform normal message handling */
BDirectWindow::MessageReceived(message); BWindow::MessageReceived(message);
break; break;
} }
@ -433,19 +463,9 @@ class SDL_BWin:public BDirectWindow
/* Accessor methods */ /* Accessor methods */
bool IsShown() { return _shown; } bool IsShown() { return _shown; }
int32 GetID() { return _id; } int32 GetID() { return _id; }
uint32 GetRowBytes() { return _row_bytes; }
int32 GetFbX() { return _bounds.left; }
int32 GetFbY() { return _bounds.top; }
bool ConnectionEnabled() { return !_connection_disabled; }
bool Connected() { return _connected; }
clipping_rect *GetClips() { return _clips; }
int32 GetNumClips() { return _num_clips; }
uint8* GetBufferPx() { return _bits; }
int32 GetBytesPerPx() { return _bytes_per_px; }
bool CanTrashWindowBuffer() { return _trash_window_buffer; }
bool BufferExists() { return _buffer_created; }
bool BufferIsDirty() { return _buffer_dirty; }
BBitmap *GetBitmap() { return _bitmap; } BBitmap *GetBitmap() { return _bitmap; }
BView *GetCurView() { return _cur_view; }
SDL_BView *GetView() { return _SDL_View; }
#if SDL_VIDEO_OPENGL #if SDL_VIDEO_OPENGL
BGLView *GetGLView() { return _SDL_GLView; } BGLView *GetGLView() { return _SDL_GLView; }
Uint32 GetGLType() { return _gl_type; } Uint32 GetGLType() { return _gl_type; }
@ -453,12 +473,9 @@ class SDL_BWin:public BDirectWindow
/* Setter methods */ /* Setter methods */
void SetID(int32 id) { _id = id; } void SetID(int32 id) { _id = id; }
void SetBufferExists(bool bufferExists) { _buffer_created = bufferExists; }
void LockBuffer() { _buffer_locker->Lock(); } void LockBuffer() { _buffer_locker->Lock(); }
void UnlockBuffer() { _buffer_locker->Unlock(); } void UnlockBuffer() { _buffer_locker->Unlock(); }
void SetBufferDirty(bool bufferDirty) { _buffer_dirty = bufferDirty; } void SetBitmap(BBitmap *bitmap) { _bitmap = bitmap; if (_SDL_View != NULL) _SDL_View->SetBitmap(bitmap); }
void SetTrashBuffer(bool trash) { _trash_window_buffer = trash; }
void SetBitmap(BBitmap *bitmap) { _bitmap = bitmap; }
private: private:
@ -564,7 +581,10 @@ private:
) { ) {
return; return;
} }
MoveTo(x, y); if (_fullscreen)
_non_fullscreen_frame.OffsetTo(x, y);
else
MoveTo(x, y);
} }
void _ResizeTo(BMessage *msg) { void _ResizeTo(BMessage *msg) {
@ -575,27 +595,48 @@ private:
) { ) {
return; return;
} }
ResizeTo(w, h); if (_fullscreen) {
_non_fullscreen_frame.right = _non_fullscreen_frame.left + w;
_non_fullscreen_frame.bottom = _non_fullscreen_frame.top + h;
} else
ResizeTo(w, h);
} }
void _SetBordered(BMessage *msg) { void _SetBordered(bool bEnabled) {
bool bEnabled; if (_fullscreen)
if(msg->FindBool("window-border", &bEnabled) != B_OK) { _bordered = bEnabled;
return; else
} SetLook(bEnabled ? B_TITLED_WINDOW_LOOK : B_NO_BORDER_WINDOW_LOOK);
SetLook(bEnabled ? B_TITLED_WINDOW_LOOK : B_NO_BORDER_WINDOW_LOOK);
} }
void _SetResizable(BMessage *msg) { void _SetResizable(bool bEnabled) {
bool bEnabled; if (_fullscreen)
if(msg->FindBool("window-resizable", &bEnabled) != B_OK) { _resizable = bEnabled;
else {
if (bEnabled) {
SetFlags(Flags() & ~(B_NOT_RESIZABLE | B_NOT_ZOOMABLE));
} else {
SetFlags(Flags() | (B_NOT_RESIZABLE | B_NOT_ZOOMABLE));
}
}
}
void _SetMinimumSize(BMessage *msg) {
float maxHeight;
float maxWidth;
float _;
int32 minHeight;
int32 minWidth;
// This is a bit convoluted, we only want to set the minimum not the maximum
// But there is no direct call to do that, so store the maximum size beforehand
GetSizeLimits(&_, &maxWidth, &_, &maxHeight);
if (msg->FindInt32("window-w", &minWidth) != B_OK)
return; return;
} if (msg->FindInt32("window-h", &minHeight) != B_OK)
if (bEnabled) { return;
SetFlags(Flags() & ~(B_NOT_RESIZABLE | B_NOT_ZOOMABLE)); SetSizeLimits((float)minWidth, maxWidth, (float)minHeight, maxHeight);
} else { UpdateSizeLimits();
SetFlags(Flags() | (B_NOT_RESIZABLE | B_NOT_ZOOMABLE));
}
} }
void _Restore() { void _Restore() {
@ -603,23 +644,42 @@ private:
Minimize(false); Minimize(false);
} else if(IsHidden()) { } else if(IsHidden()) {
Show(); Show();
} else if (_fullscreen) {
} else if(_prev_frame != NULL) { /* Zoomed */ } else if(_prev_frame != NULL) { /* Zoomed */
MoveTo(_prev_frame->left, _prev_frame->top); MoveTo(_prev_frame->left, _prev_frame->top);
ResizeTo(_prev_frame->Width(), _prev_frame->Height()); ResizeTo(_prev_frame->Width(), _prev_frame->Height());
} }
} }
void _SetFullScreen(BMessage *msg) { void _SetFullScreen(bool fullscreen) {
bool fullscreen; if (fullscreen != _fullscreen) {
if( if (fullscreen) {
msg->FindBool("fullscreen", &fullscreen) != B_OK BScreen screen(this);
) { BRect screenFrame = screen.Frame();
return; printf("screen frame: "); screenFrame.PrintToStream(); printf("\n");
_bordered = Look() != B_NO_BORDER_WINDOW_LOOK;
_resizable = !(Flags() & B_NOT_RESIZABLE);
_non_fullscreen_frame = Frame();
_SetBordered(false);
_SetResizable(false);
MoveTo(screenFrame.left, screenFrame.top);
ResizeTo(screenFrame.Width(), screenFrame.Height());
_fullscreen = fullscreen;
} else {
_fullscreen = fullscreen;
MoveTo(_non_fullscreen_frame.left, _non_fullscreen_frame.top);
ResizeTo(_non_fullscreen_frame.Width(), _non_fullscreen_frame.Height());
_SetBordered(_bordered);
_SetResizable(_resizable);
}
} }
SetFullScreen(fullscreen);
} }
/* Members */ /* Members */
BView* _cur_view;
SDL_BView* _SDL_View;
#if SDL_VIDEO_OPENGL #if SDL_VIDEO_OPENGL
BGLView * _SDL_GLView; BGLView * _SDL_GLView;
Uint32 _gl_type; Uint32 _gl_type;
@ -632,23 +692,15 @@ private:
bool _inhibit_resize; bool _inhibit_resize;
BRect *_prev_frame; /* Previous position and size of the window */ BRect *_prev_frame; /* Previous position and size of the window */
bool _fullscreen;
// valid only if fullscreen
BRect _non_fullscreen_frame;
bool _bordered;
bool _resizable;
/* Framebuffer members */ /* Framebuffer members */
bool _connected, BLocker *_buffer_locker;
_connection_disabled, BBitmap *_bitmap;
_buffer_created,
_buffer_dirty,
_trash_window_buffer;
uint8 *_bits;
uint32 _row_bytes;
clipping_rect _bounds;
BLocker *_buffer_locker;
clipping_rect *_clips;
uint32 _num_clips;
int32 _bytes_per_px;
thread_id _draw_thread_id;
BBitmap *_bitmap;
}; };