workspace activation message was asynchronous, whether or not the BWindowScreen stopped drawing in time was pure luck (this also caused crashes with the VESA driver, as that one unmaps its frame buffer during mode switch). Introduced a new AS_DIRECT_SCREEN_LOCK protocol for this. * In the long term, we should let BWindowScreen use the same mechanism as BDirectWindows, though. * Removed superfluous locking in BDirectWindow::_InitData(). git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@32746 a95241bf-73f2-0310-859d-f6bbb57e9c96
643 lines
12 KiB
C++
643 lines
12 KiB
C++
/*
|
|
* Copyright 2003-2009, Haiku Inc.
|
|
* Authors:
|
|
* Stefano Ceccherini <[email protected]>
|
|
* Carwyn Jones <[email protected]>
|
|
*
|
|
* Distributed under the terms of the MIT License.
|
|
*/
|
|
|
|
|
|
#include <DirectWindow.h>
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
#include <Screen.h>
|
|
|
|
#include <clipping.h>
|
|
#include <AppServerLink.h>
|
|
#include <DirectWindowPrivate.h>
|
|
#include <ServerProtocol.h>
|
|
|
|
|
|
//#define DEBUG 1
|
|
#define OUTPUT printf
|
|
//#define OUTPUT debug_printf
|
|
|
|
|
|
// We don't need this kind of locking, since the directDaemonFunc
|
|
// doesn't access critical shared data.
|
|
#define DW_NEEDS_LOCKING 0
|
|
|
|
enum dw_status_bits {
|
|
DW_STATUS_AREA_CLONED = 0x1,
|
|
DW_STATUS_THREAD_STARTED = 0x2,
|
|
DW_STATUS_SEM_CREATED = 0x4
|
|
};
|
|
|
|
|
|
#if DEBUG
|
|
|
|
|
|
static void
|
|
print_direct_buffer_state(const direct_buffer_state &state)
|
|
{
|
|
char string[128];
|
|
int modeState = state & B_DIRECT_MODE_MASK;
|
|
if (modeState == B_DIRECT_START)
|
|
strcpy(string, "B_DIRECT_START");
|
|
else if (modeState == B_DIRECT_MODIFY)
|
|
strcpy(string, "B_DIRECT_MODIFY");
|
|
else if (modeState == B_DIRECT_STOP)
|
|
strcpy(string, "B_DIRECT_STOP");
|
|
|
|
if (state & B_CLIPPING_MODIFIED)
|
|
strcat(string, " | B_CLIPPING_MODIFIED");
|
|
if (state & B_BUFFER_RESIZED)
|
|
strcat(string, " | B_BUFFER_RESIZED");
|
|
if (state & B_BUFFER_MOVED)
|
|
strcat(string, " | B_BUFFER_MOVED");
|
|
if (state & B_BUFFER_RESET)
|
|
strcat(string, " | B_BUFFER_RESET");
|
|
|
|
OUTPUT("direct_buffer_state: %s\n", string);
|
|
}
|
|
|
|
|
|
static void
|
|
print_direct_driver_state(const direct_driver_state &state)
|
|
{
|
|
if (state == 0)
|
|
return;
|
|
|
|
char string[64];
|
|
if (state == B_DRIVER_CHANGED)
|
|
strcpy(string, "B_DRIVER_CHANGED");
|
|
else if (state == B_MODE_CHANGED)
|
|
strcpy(string, "B_MODE_CHANGED");
|
|
|
|
OUTPUT("direct_driver_state: %s\n", string);
|
|
}
|
|
|
|
|
|
#if DEBUG > 1
|
|
|
|
|
|
static void
|
|
print_direct_buffer_layout(const buffer_layout &layout)
|
|
{
|
|
char string[64];
|
|
if (layout == B_BUFFER_NONINTERLEAVED)
|
|
strcpy(string, "B_BUFFER_NONINTERLEAVED");
|
|
else
|
|
strcpy(string, "unknown");
|
|
|
|
OUTPUT("layout: %s\n", string);
|
|
}
|
|
|
|
|
|
static void
|
|
print_direct_buffer_orientation(const buffer_orientation &orientation)
|
|
{
|
|
char string[64];
|
|
switch (orientation) {
|
|
case B_BUFFER_TOP_TO_BOTTOM:
|
|
strcpy(string, "B_BUFFER_TOP_TO_BOTTOM");
|
|
break;
|
|
case B_BUFFER_BOTTOM_TO_TOP:
|
|
strcpy(string, "B_BUFFER_BOTTOM_TO_TOP");
|
|
break;
|
|
default:
|
|
strcpy(string, "unknown");
|
|
break;
|
|
}
|
|
|
|
OUTPUT("orientation: %s\n", string);
|
|
}
|
|
|
|
|
|
#endif // DEBUG > 2
|
|
|
|
|
|
static void
|
|
print_direct_buffer_info(const direct_buffer_info &info)
|
|
{
|
|
print_direct_buffer_state(info.buffer_state);
|
|
print_direct_driver_state(info.driver_state);
|
|
|
|
# if DEBUG > 1
|
|
OUTPUT("bits: %p\n", info.bits);
|
|
OUTPUT("pci_bits: %p\n", info.pci_bits);
|
|
OUTPUT("bytes_per_row: %ld\n", info.bytes_per_row);
|
|
OUTPUT("bits_per_pixel: %lu\n", info.bits_per_pixel);
|
|
OUTPUT("pixel_format: %d\n", info.pixel_format);
|
|
print_direct_buffer_layout(info.layout);
|
|
print_direct_buffer_orientation(info.orientation);
|
|
|
|
# if DEBUG > 2
|
|
// TODO: this won't work correctly with debug_printf()
|
|
printf("CLIPPING INFO:\n");
|
|
printf("clipping_rects count: %ld\n", info.clip_list_count);
|
|
|
|
printf("- window_bounds:\n");
|
|
BRegion region;
|
|
region.Set(info.window_bounds);
|
|
region.PrintToStream();
|
|
|
|
region.MakeEmpty();
|
|
for (uint32 i = 0; i < info.clip_list_count; i++)
|
|
region.Include(info.clip_list[i]);
|
|
|
|
printf("- clip_list:\n");
|
|
region.PrintToStream();
|
|
# endif
|
|
# endif
|
|
|
|
OUTPUT("\n");
|
|
}
|
|
|
|
|
|
#endif // DEBUG
|
|
|
|
|
|
// #pragma mark -
|
|
|
|
|
|
BDirectWindow::BDirectWindow(BRect frame, const char *title, window_type type,
|
|
uint32 flags, uint32 workspace)
|
|
:
|
|
BWindow(frame, title, type, flags, workspace)
|
|
{
|
|
_InitData();
|
|
}
|
|
|
|
|
|
BDirectWindow::BDirectWindow(BRect frame, const char *title, window_look look,
|
|
window_feel feel, uint32 flags, uint32 workspace)
|
|
:
|
|
BWindow(frame, title, look, feel, flags, workspace)
|
|
{
|
|
_InitData();
|
|
}
|
|
|
|
|
|
BDirectWindow::~BDirectWindow()
|
|
{
|
|
_DisposeData();
|
|
}
|
|
|
|
|
|
// #pragma mark - BWindow API implementation
|
|
|
|
|
|
BArchivable *
|
|
BDirectWindow::Instantiate(BMessage *data)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
|
|
status_t
|
|
BDirectWindow::Archive(BMessage *data, bool deep) const
|
|
{
|
|
return inherited::Archive(data, deep);
|
|
}
|
|
|
|
|
|
void
|
|
BDirectWindow::Quit()
|
|
{
|
|
inherited::Quit();
|
|
}
|
|
|
|
|
|
void
|
|
BDirectWindow::DispatchMessage(BMessage *message, BHandler *handler)
|
|
{
|
|
inherited::DispatchMessage(message, handler);
|
|
}
|
|
|
|
|
|
void
|
|
BDirectWindow::MessageReceived(BMessage *message)
|
|
{
|
|
inherited::MessageReceived(message);
|
|
}
|
|
|
|
|
|
void
|
|
BDirectWindow::FrameMoved(BPoint newPosition)
|
|
{
|
|
inherited::FrameMoved(newPosition);
|
|
}
|
|
|
|
|
|
void
|
|
BDirectWindow::WorkspacesChanged(uint32 oldWorkspaces, uint32 newWorkspaces)
|
|
{
|
|
inherited::WorkspacesChanged(oldWorkspaces, newWorkspaces);
|
|
}
|
|
|
|
|
|
void
|
|
BDirectWindow::WorkspaceActivated(int32 index, bool state)
|
|
{
|
|
inherited::WorkspaceActivated(index, state);
|
|
}
|
|
|
|
|
|
void
|
|
BDirectWindow::FrameResized(float newWidth, float newHeight)
|
|
{
|
|
inherited::FrameResized(newWidth, newHeight);
|
|
}
|
|
|
|
|
|
void
|
|
BDirectWindow::Minimize(bool minimize)
|
|
{
|
|
inherited::Minimize(minimize);
|
|
}
|
|
|
|
|
|
void
|
|
BDirectWindow::Zoom(BPoint recPosition, float recWidth, float recHeight)
|
|
{
|
|
inherited::Zoom(recPosition, recWidth, recHeight);
|
|
}
|
|
|
|
|
|
void
|
|
BDirectWindow::ScreenChanged(BRect screenFrame, color_space depth)
|
|
{
|
|
inherited::ScreenChanged(screenFrame, depth);
|
|
}
|
|
|
|
|
|
void
|
|
BDirectWindow::MenusBeginning()
|
|
{
|
|
inherited::MenusBeginning();
|
|
}
|
|
|
|
|
|
void
|
|
BDirectWindow::MenusEnded()
|
|
{
|
|
inherited::MenusEnded();
|
|
}
|
|
|
|
|
|
void
|
|
BDirectWindow::WindowActivated(bool state)
|
|
{
|
|
inherited::WindowActivated(state);
|
|
}
|
|
|
|
|
|
void
|
|
BDirectWindow::Show()
|
|
{
|
|
inherited::Show();
|
|
}
|
|
|
|
|
|
void
|
|
BDirectWindow::Hide()
|
|
{
|
|
inherited::Hide();
|
|
}
|
|
|
|
|
|
BHandler *
|
|
BDirectWindow::ResolveSpecifier(BMessage *msg, int32 index,
|
|
BMessage *specifier, int32 form, const char *property)
|
|
{
|
|
return inherited::ResolveSpecifier(msg, index, specifier, form, property);
|
|
}
|
|
|
|
|
|
status_t
|
|
BDirectWindow::GetSupportedSuites(BMessage *data)
|
|
{
|
|
return inherited::GetSupportedSuites(data);
|
|
}
|
|
|
|
|
|
status_t
|
|
BDirectWindow::Perform(perform_code d, void *arg)
|
|
{
|
|
return inherited::Perform(d, arg);
|
|
}
|
|
|
|
|
|
void
|
|
BDirectWindow::task_looper()
|
|
{
|
|
inherited::task_looper();
|
|
}
|
|
|
|
|
|
BMessage *
|
|
BDirectWindow::ConvertToMessage(void *raw, int32 code)
|
|
{
|
|
return inherited::ConvertToMessage(raw, code);
|
|
}
|
|
|
|
|
|
// #pragma mark - BDirectWindow specific API
|
|
|
|
|
|
void
|
|
BDirectWindow::DirectConnected(direct_buffer_info *info)
|
|
{
|
|
// implemented in subclasses
|
|
}
|
|
|
|
|
|
status_t
|
|
BDirectWindow::GetClippingRegion(BRegion *region, BPoint *origin) const
|
|
{
|
|
if (region == NULL)
|
|
return B_BAD_VALUE;
|
|
|
|
if (IsLocked() || !_LockDirect())
|
|
return B_ERROR;
|
|
|
|
if (fInDirectConnect) {
|
|
_UnlockDirect();
|
|
return B_ERROR;
|
|
}
|
|
|
|
// BPoint's coordinates are floats. We can only work
|
|
// with integers._DaemonStarter
|
|
int32 originX, originY;
|
|
if (origin == NULL) {
|
|
originX = 0;
|
|
originY = 0;
|
|
} else {
|
|
originX = (int32)origin->x;
|
|
originY = (int32)origin->y;
|
|
}
|
|
|
|
#ifndef HAIKU_TARGET_PLATFORM_DANO
|
|
// Since we are friend of BRegion, we can access its private members.
|
|
// Otherwise, we would need to call BRegion::Include(clipping_rect)
|
|
// for every clipping_rect in our clip_list, and that would be much
|
|
// more overkill than this (tested ).
|
|
if (!region->_SetSize(fBufferDesc->clip_list_count)) {
|
|
_UnlockDirect();
|
|
return B_NO_MEMORY;
|
|
}
|
|
region->fCount = fBufferDesc->clip_list_count;
|
|
region->fBounds = region->_ConvertToInternal(fBufferDesc->clip_bounds);
|
|
for (uint32 c = 0; c < fBufferDesc->clip_list_count; c++) {
|
|
region->fData[c] = region->_ConvertToInternal(
|
|
fBufferDesc->clip_list[c]);
|
|
}
|
|
|
|
// adjust bounds by the given origin point
|
|
region->OffsetBy(-originX, -originY);
|
|
#endif
|
|
|
|
_UnlockDirect();
|
|
|
|
return B_OK;
|
|
|
|
}
|
|
|
|
|
|
status_t
|
|
BDirectWindow::SetFullScreen(bool enable)
|
|
{
|
|
if (fIsFullScreen == enable)
|
|
return B_OK;
|
|
|
|
status_t status = B_ERROR;
|
|
if (Lock()) {
|
|
fLink->StartMessage(AS_DIRECT_WINDOW_SET_FULLSCREEN);
|
|
fLink->Attach<bool>(enable);
|
|
|
|
if (fLink->FlushWithReply(status) == B_OK
|
|
&& status == B_OK) {
|
|
fIsFullScreen = enable;
|
|
}
|
|
Unlock();
|
|
}
|
|
return status;
|
|
}
|
|
|
|
|
|
bool
|
|
BDirectWindow::IsFullScreen() const
|
|
{
|
|
return fIsFullScreen;
|
|
}
|
|
|
|
|
|
/*static*/ bool
|
|
BDirectWindow::SupportsWindowMode(screen_id id)
|
|
{
|
|
display_mode mode;
|
|
status_t status = BScreen(id).GetMode(&mode);
|
|
if (status == B_OK)
|
|
return (mode.flags & B_PARALLEL_ACCESS) != 0;
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
// #pragma mark - Private methods
|
|
|
|
|
|
/*static*/ int32
|
|
BDirectWindow::_daemon_thread(void *arg)
|
|
{
|
|
return static_cast<BDirectWindow *>(arg)->_DirectDaemon();
|
|
}
|
|
|
|
|
|
int32
|
|
BDirectWindow::_DirectDaemon()
|
|
{
|
|
while (!fDaemonKiller) {
|
|
// This sem is released by the app_server when our
|
|
// clipping region changes, or when our window is moved,
|
|
// resized, etc. etc.
|
|
status_t status;
|
|
do {
|
|
status = acquire_sem(fDisableSem);
|
|
} while (status == B_INTERRUPTED);
|
|
|
|
if (status != B_OK) {
|
|
fprintf(stderr, "DirectDaemon: failed to acquire direct sem: %s\n",
|
|
strerror(status));
|
|
return -1;
|
|
}
|
|
|
|
#if DEBUG
|
|
print_direct_buffer_info(*fBufferDesc);
|
|
#endif
|
|
|
|
if (_LockDirect()) {
|
|
if ((fBufferDesc->buffer_state & B_DIRECT_MODE_MASK)
|
|
== B_DIRECT_START)
|
|
fConnectionEnable = true;
|
|
|
|
fInDirectConnect = true;
|
|
DirectConnected(fBufferDesc);
|
|
fInDirectConnect = false;
|
|
|
|
if ((fBufferDesc->buffer_state & B_DIRECT_MODE_MASK)
|
|
== B_DIRECT_STOP)
|
|
fConnectionEnable = false;
|
|
|
|
_UnlockDirect();
|
|
}
|
|
|
|
// The app_server then waits (with a timeout) on this sem.
|
|
// If we aren't quick enough to release this sem, our app
|
|
// will be terminated by the app_server
|
|
if ((status = release_sem(fDisableSemAck)) != B_OK) {
|
|
fprintf(stderr, "DirectDaemon: failed to release sem: %s\n",
|
|
strerror(status));
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
bool
|
|
BDirectWindow::_LockDirect() const
|
|
{
|
|
// LockDirect() and UnlockDirect() are no-op on BeOS. I tried to call BeOS's
|
|
// version repeatedly, from the same thread and from different threads,
|
|
// nothing happened.
|
|
// They're not needed though, as the direct_daemon_thread doesn't change
|
|
// any shared data. They are probably here for future enhancements.
|
|
status_t status = B_OK;
|
|
|
|
#if DW_NEEDS_LOCKING
|
|
BDirectWindow *casted = const_cast<BDirectWindow *>(this);
|
|
|
|
if (atomic_add(&casted->fDirectLock, 1) > 0) {
|
|
do {
|
|
status = acquire_sem(casted->fDirectSem);
|
|
} while (status == B_INTERRUPTED);
|
|
}
|
|
|
|
if (status == B_OK) {
|
|
casted->fDirectLockOwner = find_thread(NULL);
|
|
casted->fDirectLockCount++;
|
|
}
|
|
#endif
|
|
|
|
return status == B_OK;
|
|
}
|
|
|
|
|
|
void
|
|
BDirectWindow::_UnlockDirect() const
|
|
{
|
|
#if DW_NEEDS_LOCKING
|
|
BDirectWindow *casted = const_cast<BDirectWindow *>(this);
|
|
|
|
if (atomic_add(&casted->fDirectLock, -1) > 1)
|
|
release_sem(casted->fDirectSem);
|
|
|
|
casted->fDirectLockCount--;
|
|
#endif
|
|
}
|
|
|
|
|
|
void
|
|
BDirectWindow::_InitData()
|
|
{
|
|
fConnectionEnable = false;
|
|
fIsFullScreen = false;
|
|
fInDirectConnect = false;
|
|
|
|
fInitStatus = 0;
|
|
|
|
status_t status = B_ERROR;
|
|
struct direct_window_sync_data syncData;
|
|
|
|
fLink->StartMessage(AS_DIRECT_WINDOW_GET_SYNC_DATA);
|
|
if (fLink->FlushWithReply(status) == B_OK && status == B_OK)
|
|
fLink->Read<direct_window_sync_data>(&syncData);
|
|
|
|
if (status != B_OK)
|
|
return;
|
|
|
|
#if DW_NEEDS_LOCKING
|
|
fDirectLock = 0;
|
|
fDirectLockCount = 0;
|
|
fDirectLockOwner = -1;
|
|
fDirectLockStack = NULL;
|
|
fDirectSem = create_sem(0, "direct sem");
|
|
if (fDirectSem > 0)
|
|
fInitStatus |= DW_STATUS_SEM_CREATED;
|
|
#endif
|
|
|
|
fSourceClippingArea = syncData.area;
|
|
fDisableSem = syncData.disable_sem;
|
|
fDisableSemAck = syncData.disable_sem_ack;
|
|
|
|
fClonedClippingArea = clone_area("cloned direct area", (void**)&fBufferDesc,
|
|
B_ANY_ADDRESS, B_READ_AREA, fSourceClippingArea);
|
|
|
|
if (fClonedClippingArea > 0) {
|
|
fInitStatus |= DW_STATUS_AREA_CLONED;
|
|
|
|
fDirectDaemonId = spawn_thread(_daemon_thread, "direct daemon",
|
|
B_DISPLAY_PRIORITY, this);
|
|
|
|
if (fDirectDaemonId > 0) {
|
|
fDaemonKiller = false;
|
|
if (resume_thread(fDirectDaemonId) == B_OK)
|
|
fInitStatus |= DW_STATUS_THREAD_STARTED;
|
|
else
|
|
kill_thread(fDirectDaemonId);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
BDirectWindow::_DisposeData()
|
|
{
|
|
// wait until the connection terminates: we can't destroy
|
|
// the object until the client receives the B_DIRECT_STOP
|
|
// notification, or bad things will happen
|
|
while (fConnectionEnable)
|
|
snooze(50000);
|
|
|
|
_LockDirect();
|
|
|
|
if (fInitStatus & DW_STATUS_THREAD_STARTED) {
|
|
fDaemonKiller = true;
|
|
// delete this sem, otherwise the Direct daemon thread
|
|
// will wait forever on it
|
|
delete_sem(fDisableSem);
|
|
status_t retVal;
|
|
wait_for_thread(fDirectDaemonId, &retVal);
|
|
}
|
|
|
|
#if DW_NEEDS_LOCKING
|
|
if (fInitStatus & DW_STATUS_SEM_CREATED)
|
|
delete_sem(fDirectSem);
|
|
#endif
|
|
|
|
if (fInitStatus & DW_STATUS_AREA_CLONED)
|
|
delete_area(fClonedClippingArea);
|
|
}
|
|
|
|
|
|
void BDirectWindow::_ReservedDirectWindow1() {}
|
|
void BDirectWindow::_ReservedDirectWindow2() {}
|
|
void BDirectWindow::_ReservedDirectWindow3() {}
|
|
void BDirectWindow::_ReservedDirectWindow4() {}
|