/* * Copyright 2001-2009, Haiku. * Distributed under the terms of the MIT License. * * Authors: * DarkWyrm * Stephan Aßmus */ /*! BView/BWindow combination HWInterface implementation */ #include "ViewHWInterface.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "BBitmapBuffer.h" #include "PortLink.h" #include "ServerConfig.h" #include "ServerCursor.h" #include "UpdateQueue.h" #ifdef DEBUG_DRIVER_MODULE # include # define STRACE(x) printf x #else # define STRACE(x) ; #endif const unsigned char kEmptyCursor[] = { 16, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; static const bool kDefaultDoubleBuffered = true; enum { MSG_UPDATE = 'updt' }; const char* string_for_color_space(color_space format) { const char* name = ""; switch (format) { case B_RGBA64: name = "B_RGBA64"; break; case B_RGBA64_BIG: name = "B_RGBA64_BIG"; break; case B_RGB48: name = "B_RGB48"; break; case B_RGB48_BIG: name = "B_RGB48_BIG"; break; case B_RGB32: name = "B_RGB32"; break; case B_RGBA32: name = "B_RGBA32"; break; case B_RGB32_BIG: name = "B_RGB32_BIG"; break; case B_RGBA32_BIG: name = "B_RGBA32_BIG"; break; case B_RGB24: name = "B_RGB24"; break; case B_RGB24_BIG: name = "B_RGB24_BIG"; break; case B_CMAP8: name = "B_CMAP8"; break; case B_GRAY8: name = "B_GRAY8"; break; case B_GRAY1: name = "B_GRAY1"; break; default: break; } return name; } static int32 run_app_thread(void* cookie) { if (BApplication* app = (BApplication*)cookie) { app->Lock(); app->Run(); delete app; } return 0; } //#define INPUTSERVER_TEST_MODE 1 class CardView : public BView { public: CardView(BRect bounds); virtual ~CardView(); virtual void AttachedToWindow(); virtual void Draw(BRect updateRect); virtual void MessageReceived(BMessage* message); // CardView void SetBitmap(const BBitmap* bitmap); void ForwardMessage(BMessage* message = NULL); private: port_id fInputPort; const BBitmap* fBitmap; }; class CardWindow : public BWindow { public: CardWindow(BRect frame); virtual ~CardWindow(); virtual void MessageReceived(BMessage* message); virtual bool QuitRequested(); // CardWindow void SetBitmap(const BBitmap* bitmap); void Invalidate(const BRect& area); private: CardView* fView; BRegion fUpdateRegion; BLocker fUpdateLock; }; class CardMessageFilter : public BMessageFilter { public: CardMessageFilter(CardView* view); virtual filter_result Filter(BMessage* message, BHandler** _target); private: CardView* fView; }; // #pragma mark - CardView::CardView(BRect bounds) : BView(bounds, "graphics card view", B_FOLLOW_ALL, B_WILL_DRAW), fBitmap(NULL) { SetViewColor(B_TRANSPARENT_32_BIT); #ifndef INPUTSERVER_TEST_MODE fInputPort = create_port(200, SERVER_INPUT_PORT); #else fInputPort = create_port(100, "ViewInputDevice"); #endif #ifdef ENABLE_INPUT_SERVER_EMULATION AddFilter(new CardMessageFilter(this)); #endif } CardView::~CardView() { } void CardView::AttachedToWindow() { } void CardView::Draw(BRect updateRect) { if (fBitmap != NULL) DrawBitmapAsync(fBitmap, updateRect, updateRect); } /*! These functions emulate the Input Server by sending the *exact* same kind of messages to the server's port. Being we're using a regular window, it would make little sense to do anything else. */ void CardView::ForwardMessage(BMessage* message) { if (message == NULL) message = Window()->CurrentMessage(); if (message == NULL) return; // remove some fields that potentially mess up our own message processing BMessage copy = *message; copy.RemoveName("screen_where"); copy.RemoveName("be:transit"); copy.RemoveName("be:view_where"); copy.RemoveName("be:cursor_needed"); copy.RemoveName("_view_token"); size_t length = copy.FlattenedSize(); char stream[length]; if (copy.Flatten(stream, length) == B_OK) write_port(fInputPort, 0, stream, length); } void CardView::MessageReceived(BMessage* message) { switch (message->what) { default: BView::MessageReceived(message); break; } } void CardView::SetBitmap(const BBitmap* bitmap) { if (bitmap != fBitmap) { fBitmap = bitmap; if (Parent()) Invalidate(); } } // #pragma mark - CardMessageFilter::CardMessageFilter(CardView* view) : BMessageFilter(B_ANY_DELIVERY, B_ANY_SOURCE), fView(view) { } filter_result CardMessageFilter::Filter(BMessage* message, BHandler** target) { switch (message->what) { case B_KEY_DOWN: case B_UNMAPPED_KEY_DOWN: case B_KEY_UP: case B_UNMAPPED_KEY_UP: case B_MOUSE_DOWN: case B_MOUSE_UP: case B_MOUSE_WHEEL_CHANGED: if (message->what == B_MOUSE_DOWN) fView->SetMouseEventMask(B_POINTER_EVENTS); fView->ForwardMessage(message); return B_SKIP_MESSAGE; case B_MOUSE_MOVED: { int32 transit; if (message->FindInt32("be:transit", &transit) == B_OK && transit == B_ENTERED_VIEW) { // A bug in R5 prevents this call from having an effect if // called elsewhere, and calling it here works, if we're lucky :-) BCursor cursor(kEmptyCursor); fView->SetViewCursor(&cursor, true); } fView->ForwardMessage(message); return B_SKIP_MESSAGE; } } return B_DISPATCH_MESSAGE; } // #pragma mark - CardWindow::CardWindow(BRect frame) : BWindow(frame, "Haiku App Server", B_TITLED_WINDOW, B_NOT_ZOOMABLE | B_NOT_RESIZABLE | B_NO_SERVER_SIDE_WINDOW_MODIFIERS), fUpdateRegion(), fUpdateLock("update lock") { fView = new CardView(Bounds()); AddChild(fView); fView->MakeFocus(); // make it receive key events } CardWindow::~CardWindow() { } void CardWindow::MessageReceived(BMessage* msg) { STRACE("CardWindow::MessageReceived()\n"); switch (msg->what) { case MSG_UPDATE: STRACE("MSG_UPDATE\n"); // invalidate all areas in the view that need redrawing if (fUpdateLock.LockWithTimeout(2000LL) >= B_OK) { /* int32 count = fUpdateRegion.CountRects(); for (int32 i = 0; i < count; i++) { fView->Invalidate(fUpdateRegion.RectAt(i)); }*/ BRect frame = fUpdateRegion.Frame(); if (frame.IsValid()) { fView->Invalidate(frame); // fView->Invalidate(); } fUpdateRegion.MakeEmpty(); fUpdateLock.Unlock(); } else { // see you next time } break; default: BWindow::MessageReceived(msg); break; } STRACE("CardWindow::MessageReceived() - exit\n"); } bool CardWindow::QuitRequested() { port_id serverport = find_port(SERVER_PORT_NAME); if (serverport >= 0) { BPrivate::PortLink link(serverport); link.StartMessage(B_QUIT_REQUESTED); link.Flush(); } else printf("ERROR: couldn't find the app_server's main port!"); // we don't quit on ourself, we let us be Quit()! return false; } void CardWindow::SetBitmap(const BBitmap* bitmap) { fView->SetBitmap(bitmap); } void CardWindow::Invalidate(const BRect& frame) { if (LockWithTimeout(1000000) >= B_OK) { fView->Invalidate(frame); Unlock(); } } // #pragma mark - ViewHWInterface::ViewHWInterface() : HWInterface(), fBackBuffer(NULL), fFrontBuffer(NULL), fWindow(NULL) { SetAsyncDoubleBuffered(kDefaultDoubleBuffered); fDisplayMode.virtual_width = 640; fDisplayMode.virtual_height = 480; fDisplayMode.space = B_RGBA32; } ViewHWInterface::~ViewHWInterface() { if (fWindow) { fWindow->Lock(); fWindow->Quit(); } be_app->Lock(); be_app->Quit(); } status_t ViewHWInterface::Initialize() { return B_OK; } status_t ViewHWInterface::Shutdown() { return B_OK; } status_t ViewHWInterface::SetMode(const display_mode& mode) { AutoWriteLocker _(this); status_t ret = B_OK; // prevent from doing the unnecessary if (fBackBuffer.IsSet() && fFrontBuffer.IsSet() && fDisplayMode.virtual_width == mode.virtual_width && fDisplayMode.virtual_height == mode.virtual_height && fDisplayMode.space == mode.space) return ret; // check if we support the mode display_mode* modes; uint32 modeCount, i; if (GetModeList(&modes, &modeCount) != B_OK) return B_NO_MEMORY; for (i = 0; i < modeCount; i++) { // we only care for the bare minimum if (modes[i].virtual_width == mode.virtual_width && modes[i].virtual_height == mode.virtual_height && modes[i].space == mode.space) { // take over settings fDisplayMode = modes[i]; break; } } delete[] modes; if (i == modeCount) return B_BAD_VALUE; BRect frame(0.0, 0.0, fDisplayMode.virtual_width - 1, fDisplayMode.virtual_height - 1); // create the window if we don't have one already if (!fWindow) { // if the window has not been created yet, the BApplication // has not been created either, but we need one to display // a real BWindow in the test environment. // be_app->Run() needs to be called in another thread if (be_app == NULL) { BApplication* app = new BApplication( "application/x-vnd.Haiku-test-app_server"); app->Unlock(); thread_id appThread = spawn_thread(run_app_thread, "app thread", B_NORMAL_PRIORITY, app); if (appThread >= B_OK) ret = resume_thread(appThread); else ret = appThread; if (ret < B_OK) return ret; } fWindow = new CardWindow(frame.OffsetToCopy(BPoint(50.0, 50.0))); // fire up the window thread but don't show it on screen yet fWindow->Hide(); fWindow->Show(); } if (fWindow->Lock()) { // just to be save fWindow->SetBitmap(NULL); // free and reallocate the bitmaps while the window is locked, // so that the view does not accidentally draw a freed bitmap fBackBuffer.Unset(); fFrontBuffer.Unset(); // NOTE: backbuffer is always B_RGBA32, this simplifies the // drawing backend implementation tremendously for the time // being. The color space conversion is handled in CopyBackToFront() // TODO: Above not true anymore for single buffered mode!!! // -> fall back to double buffer for fDisplayMode.space != B_RGB32 // as intermediate solution... bool doubleBuffered = true; if ((color_space)fDisplayMode.space != B_RGB32 && (color_space)fDisplayMode.space != B_RGBA32) doubleBuffered = true; BBitmap* frontBitmap = new BBitmap(frame, 0, (color_space)fDisplayMode.space); fFrontBuffer.SetTo(new BBitmapBuffer(frontBitmap)); status_t err = fFrontBuffer->InitCheck(); if (err < B_OK) { fFrontBuffer.Unset(); ret = err; } if (err >= B_OK && doubleBuffered) { // backbuffer is always B_RGBA32 // since we override IsDoubleBuffered(), the drawing buffer // is in effect also always B_RGBA32. BBitmap* backBitmap = new BBitmap(frame, 0, B_RGBA32); fBackBuffer.SetTo(new BBitmapBuffer(backBitmap)); err = fBackBuffer->InitCheck(); if (err < B_OK) { fBackBuffer.Unset(); ret = err; } } _NotifyFrameBufferChanged(); if (ret >= B_OK) { // clear out buffers, alpha is 255 this way // TODO: maybe this should handle different color spaces in different ways if (fBackBuffer.IsSet()) memset(fBackBuffer->Bits(), 255, fBackBuffer->BitsLength()); memset(fFrontBuffer->Bits(), 255, fFrontBuffer->BitsLength()); // change the window size and update the bitmap used for drawing fWindow->ResizeTo(frame.Width(), frame.Height()); fWindow->SetBitmap(fFrontBuffer->Bitmap()); } // window is hidden when this function is called the first time if (fWindow->IsHidden()) fWindow->Show(); fWindow->Unlock(); } else { ret = B_ERROR; } return ret; } void ViewHWInterface::GetMode(display_mode* mode) { if (mode && ReadLock()) { *mode = fDisplayMode; ReadUnlock(); } } status_t ViewHWInterface::GetDeviceInfo(accelerant_device_info* info) { // We really don't have to provide anything here because this is strictly // a software-only driver, but we'll have some fun, anyway. if (ReadLock()) { info->version = 100; sprintf(info->name, "Haiku, Inc. ViewHWInterface"); sprintf(info->chipset, "Haiku, Inc. Chipset"); sprintf(info->serial_no, "3.14159265358979323846"); info->memory = 134217728; // 128 MB, not that we really have that much. :) info->dac_speed = 0xFFFFFFFF; // *heh* ReadUnlock(); } return B_OK; } status_t ViewHWInterface::GetFrameBufferConfig(frame_buffer_config& config) { if (!fFrontBuffer.IsSet()) return B_ERROR; config.frame_buffer = fFrontBuffer->Bits(); config.frame_buffer_dma = NULL; config.bytes_per_row = fFrontBuffer->BytesPerRow(); return B_OK; } status_t ViewHWInterface::GetModeList(display_mode** _modes, uint32* _count) { AutoReadLocker _(this); #if 1 // setup a whole bunch of different modes const struct resolution { int32 width, height; } resolutions[] = { {640, 480}, {800, 600}, {1024, 768}, {1152, 864}, {1280, 960}, {1280, 1024}, {1400, 1050}, {1600, 1200} }; uint32 resolutionCount = sizeof(resolutions) / sizeof(resolutions[0]); const uint32 colors[] = {B_CMAP8, B_RGB15, B_RGB16, B_RGB32}; uint32 count = resolutionCount * 4; display_mode* modes = new(std::nothrow) display_mode[count]; if (modes == NULL) return B_NO_MEMORY; *_modes = modes; *_count = count; int32 index = 0; for (uint32 i = 0; i < resolutionCount; i++) { for (uint32 c = 0; c < 4; c++) { modes[index].virtual_width = resolutions[i].width; modes[index].virtual_height = resolutions[i].height; modes[index].space = colors[c]; modes[index].h_display_start = 0; modes[index].v_display_start = 0; modes[index].timing.h_display = resolutions[i].width; modes[index].timing.v_display = resolutions[i].height; modes[index].timing.h_total = 22000; modes[index].timing.v_total = 22000; modes[index].timing.pixel_clock = ((uint32)modes[index].timing.h_total * modes[index].timing.v_total * 60) / 1000; modes[index].flags = B_PARALLEL_ACCESS; index++; } } #else // support only a single mode, useful // for testing a specific mode display_mode *modes = new(nothrow) display_mode[1]; modes[0].virtual_width = 640; modes[0].virtual_height = 480; modes[0].space = B_CMAP8; *_modes = modes; *_count = 1; #endif return B_OK; } status_t ViewHWInterface::GetPixelClockLimits(display_mode* mode, uint32* low, uint32* high) { return B_ERROR; } status_t ViewHWInterface::GetTimingConstraints(display_timing_constraints* constraints) { return B_ERROR; } status_t ViewHWInterface::ProposeMode(display_mode* candidate, const display_mode* low, const display_mode* high) { // We should be able to get away with this because we're not dealing with // any specific hardware. This is a Good Thing(TM) because we can support // any hardware we wish within reasonable expectaions and programmer // laziness. :P return B_OK; } status_t ViewHWInterface::SetDPMSMode(uint32 state) { AutoWriteLocker _(this); return BScreen().SetDPMS(state); } uint32 ViewHWInterface::DPMSMode() { AutoReadLocker _(this); return BScreen().DPMSState(); } uint32 ViewHWInterface::DPMSCapabilities() { AutoReadLocker _(this); return BScreen().DPMSCapabilites(); } status_t ViewHWInterface::SetBrightness(float brightness) { AutoReadLocker _(this); return BScreen().SetBrightness(brightness); } status_t ViewHWInterface::GetBrightness(float* brightness) { AutoReadLocker _(this); return BScreen().GetBrightness(brightness); } sem_id ViewHWInterface::RetraceSemaphore() { return -1; } status_t ViewHWInterface::WaitForRetrace(bigtime_t timeout) { // Locking shouldn't be necessary here - R5 should handle this for us. :) BScreen screen; return screen.WaitForRetrace(timeout); } RenderingBuffer* ViewHWInterface::FrontBuffer() const { return fFrontBuffer.Get(); } RenderingBuffer* ViewHWInterface::BackBuffer() const { return fBackBuffer.Get(); } bool ViewHWInterface::IsDoubleBuffered() const { if (fFrontBuffer.IsSet()) return fBackBuffer.IsSet(); return false; } status_t ViewHWInterface::Invalidate(const BRect& frame) { status_t ret = HWInterface::Invalidate(frame); if (ret >= B_OK && fWindow && !IsDoubleBuffered()) fWindow->Invalidate(frame); return ret; } status_t ViewHWInterface::CopyBackToFront(const BRect& frame) { status_t ret = HWInterface::CopyBackToFront(frame); if (ret >= B_OK && fWindow) fWindow->Invalidate(frame); return ret; }