⛏️ index : haiku.git

/*
 * Copyright 2011, Axel DΓΆrfler, axeld@pinc-software.de.
 * Distributed under the terms of the MIT License.
 */


#include "CaptureWindow.h"

#include <stdio.h>

#include <Roster.h>
#include <Screen.h>

#include "Switcher.h"


static const bigtime_t kUpdateDelay = 50000;
	// 50 ms


class CaptureView : public BView {
public:
								CaptureView();
	virtual						~CaptureView();

			void				AddMoveOutNotification(BMessage* message);

	virtual	void				MouseMoved(BPoint point, uint32 transit,
									const BMessage* dragMessage);
	virtual void				KeyDown(const char* bytes, int32 numBytes);

private:
			void				_UpdateLast(const BPoint& point);
			void				_Notify(uint32 location, team_id team);
			void				_SendMovedOutNotification();

	static	team_id				_CurrentTeam();

private:
			uint32				fModifierMask;
			BPoint				fLastPoint;
			team_id				fLastTeam;
			bigtime_t			fLastEvent;

			uint32				fMovedOutWhat;
			BMessenger			fMovedOutMessenger;
			BRect				fMovedOutFrame;
};


CaptureView::CaptureView()
	:
	BView("main", 0),
	fModifierMask(B_CONTROL_KEY),
	fMovedOutWhat(0)
{
	SetEventMask(B_POINTER_EVENTS | B_KEYBOARD_EVENTS, B_NO_POINTER_HISTORY);
	_UpdateLast(BPoint(-1, -1));
}


CaptureView::~CaptureView()
{
}


void
CaptureView::AddMoveOutNotification(BMessage* message)
{
	if (fMovedOutWhat != 0)
		_SendMovedOutNotification();

	if (message->FindMessenger("target", &fMovedOutMessenger) == B_OK
		&& message->FindRect("frame", &fMovedOutFrame) == B_OK)
		fMovedOutWhat = (uint32)message->FindInt32("what");
}


void
CaptureView::MouseMoved(BPoint point, uint32 transit,
	const BMessage* dragMessage)
{
	ConvertToScreen(&point);

	if (fMovedOutWhat != 0 && !fMovedOutFrame.Contains(point))
		_SendMovedOutNotification();

	uint32 modifiers = ::modifiers();
	if ((modifiers & fModifierMask) == 0) {
		_UpdateLast(point);
		return;
	}

	// TODO: we will need to iterate over all existing screens to find the
	// right one!
	BScreen screen;
	BRect screenFrame = screen.Frame();

	uint32 location = kNowhere;
	if (point.x <= screenFrame.left && fLastPoint.x > screenFrame.left)
		location = kLeftEdge;
	else if (point.x >= screenFrame.right && fLastPoint.x < screenFrame.right)
		location = kRightEdge;
	else if (point.y <= screenFrame.top && fLastPoint.y > screenFrame.top)
		location = kTopEdge;
	else if (point.y >= screenFrame.bottom && fLastPoint.y < screenFrame.bottom)
		location = kBottomEdge;

	if (location != kNowhere)
		_Notify(location, fLastTeam);

	_UpdateLast(point);
}


void
CaptureView::KeyDown(const char* bytes, int32 numBytes)
{
	if ((::modifiers() & (B_CONTROL_KEY | B_SHIFT_KEY | B_OPTION_KEY
			| B_COMMAND_KEY)) != (B_COMMAND_KEY | B_CONTROL_KEY))
		return;

	uint32 location = kNowhere;

	switch (bytes[0]) {
		case '1':
			location = kLeftEdge;
			break;
		case '2':
			location = kRightEdge;
			break;
		case '3':
			location = kTopEdge;
			break;
		case '4':
			location = kBottomEdge;
			break;
	}

	if (location != kNowhere)
		_Notify(location, _CurrentTeam());
}


void
CaptureView::_UpdateLast(const BPoint& point)
{
	fLastPoint = point;

	bigtime_t now = system_time();

	// We update the currently active application only, if the mouse did
	// not move over it for a certain time - this is necessary only for
	// focus follow mouse.
	if (now > fLastEvent + kUpdateDelay)
		fLastTeam = _CurrentTeam();

	fLastEvent = now;
}


void
CaptureView::_Notify(uint32 location, team_id team)
{
	if (location == kNowhere)
		return;

	BMessage message(kMsgLocationTrigger);
	message.AddInt32("location", location);
	message.AddInt32("team", team);

	be_app->PostMessage(&message);
}


void
CaptureView::_SendMovedOutNotification()
{
	BMessage movedOut(fMovedOutWhat);
	fMovedOutMessenger.SendMessage(&movedOut);
	fMovedOutWhat = 0;
}


/*static*/ team_id
CaptureView::_CurrentTeam()
{
	app_info appInfo;
	status_t status = be_roster->GetActiveAppInfo(&appInfo);
	if (status == B_OK)
		return appInfo.team;

	return status;
}


// #pragma mark -


CaptureWindow::CaptureWindow()
	:
	BWindow(BRect(0, 0, 100, 100), "mouse capture", B_NO_BORDER_WINDOW_LOOK,
		B_NORMAL_WINDOW_FEEL, B_ASYNCHRONOUS_CONTROLS, B_ALL_WORKSPACES)
{
	fCaptureView = new CaptureView();
	AddChild(fCaptureView);
}


CaptureWindow::~CaptureWindow()
{
}


void
CaptureWindow::MessageReceived(BMessage* message)
{
	switch (message->what) {
		case kMsgHideWhenMouseMovedOut:
			fCaptureView->AddMoveOutNotification(message);
			break;
		default:
			BWindow::MessageReceived(message);
			break;
	}
}