1/*
2 * Copyright 2011, Axel Dörfler, axeld@pinc-software.de.
3 * Distributed under the terms of the MIT License.
4 */
5
6
7#include "CaptureWindow.h"
8
9#include <stdio.h>
10
11#include <Roster.h>
12#include <Screen.h>
13
14#include "Switcher.h"
15
16
17static const bigtime_t kUpdateDelay = 50000;
18	// 50 ms
19
20
21class CaptureView : public BView {
22public:
23								CaptureView();
24	virtual						~CaptureView();
25
26			void				AddMoveOutNotification(BMessage* message);
27
28	virtual	void				MouseMoved(BPoint point, uint32 transit,
29									const BMessage* dragMessage);
30	virtual void				KeyDown(const char* bytes, int32 numBytes);
31
32private:
33			void				_UpdateLast(const BPoint& point);
34			void				_Notify(uint32 location, team_id team);
35			void				_SendMovedOutNotification();
36
37	static	team_id				_CurrentTeam();
38
39private:
40			uint32				fModifierMask;
41			BPoint				fLastPoint;
42			team_id				fLastTeam;
43			bigtime_t			fLastEvent;
44
45			uint32				fMovedOutWhat;
46			BMessenger			fMovedOutMessenger;
47			BRect				fMovedOutFrame;
48};
49
50
51CaptureView::CaptureView()
52	:
53	BView("main", 0),
54	fModifierMask(B_CONTROL_KEY),
55	fMovedOutWhat(0)
56{
57	SetEventMask(B_POINTER_EVENTS | B_KEYBOARD_EVENTS, B_NO_POINTER_HISTORY);
58	_UpdateLast(BPoint(-1, -1));
59}
60
61
62CaptureView::~CaptureView()
63{
64}
65
66
67void
68CaptureView::AddMoveOutNotification(BMessage* message)
69{
70	if (fMovedOutWhat != 0)
71		_SendMovedOutNotification();
72
73	if (message->FindMessenger("target", &fMovedOutMessenger) == B_OK
74		&& message->FindRect("frame", &fMovedOutFrame) == B_OK)
75		fMovedOutWhat = (uint32)message->FindInt32("what");
76}
77
78
79void
80CaptureView::MouseMoved(BPoint point, uint32 transit,
81	const BMessage* dragMessage)
82{
83	ConvertToScreen(&point);
84
85	if (fMovedOutWhat != 0 && !fMovedOutFrame.Contains(point))
86		_SendMovedOutNotification();
87
88	uint32 modifiers = ::modifiers();
89	if ((modifiers & fModifierMask) == 0) {
90		_UpdateLast(point);
91		return;
92	}
93
94	// TODO: we will need to iterate over all existing screens to find the
95	// right one!
96	BScreen screen;
97	BRect screenFrame = screen.Frame();
98
99	uint32 location = kNowhere;
100	if (point.x <= screenFrame.left && fLastPoint.x > screenFrame.left)
101		location = kLeftEdge;
102	else if (point.x >= screenFrame.right && fLastPoint.x < screenFrame.right)
103		location = kRightEdge;
104	else if (point.y <= screenFrame.top && fLastPoint.y > screenFrame.top)
105		location = kTopEdge;
106	else if (point.y >= screenFrame.bottom && fLastPoint.y < screenFrame.bottom)
107		location = kBottomEdge;
108
109	if (location != kNowhere)
110		_Notify(location, fLastTeam);
111
112	_UpdateLast(point);
113}
114
115
116void
117CaptureView::KeyDown(const char* bytes, int32 numBytes)
118{
119	if ((::modifiers() & (B_CONTROL_KEY | B_SHIFT_KEY | B_OPTION_KEY
120			| B_COMMAND_KEY)) != (B_COMMAND_KEY | B_CONTROL_KEY))
121		return;
122
123	uint32 location = kNowhere;
124
125	switch (bytes[0]) {
126		case '1':
127			location = kLeftEdge;
128			break;
129		case '2':
130			location = kRightEdge;
131			break;
132		case '3':
133			location = kTopEdge;
134			break;
135		case '4':
136			location = kBottomEdge;
137			break;
138	}
139
140	if (location != kNowhere)
141		_Notify(location, _CurrentTeam());
142}
143
144
145void
146CaptureView::_UpdateLast(const BPoint& point)
147{
148	fLastPoint = point;
149
150	bigtime_t now = system_time();
151
152	// We update the currently active application only, if the mouse did
153	// not move over it for a certain time - this is necessary only for
154	// focus follow mouse.
155	if (now > fLastEvent + kUpdateDelay)
156		fLastTeam = _CurrentTeam();
157
158	fLastEvent = now;
159}
160
161
162void
163CaptureView::_Notify(uint32 location, team_id team)
164{
165	if (location == kNowhere)
166		return;
167
168	BMessage message(kMsgLocationTrigger);
169	message.AddInt32("location", location);
170	message.AddInt32("team", team);
171
172	be_app->PostMessage(&message);
173}
174
175
176void
177CaptureView::_SendMovedOutNotification()
178{
179	BMessage movedOut(fMovedOutWhat);
180	fMovedOutMessenger.SendMessage(&movedOut);
181	fMovedOutWhat = 0;
182}
183
184
185/*static*/ team_id
186CaptureView::_CurrentTeam()
187{
188	app_info appInfo;
189	status_t status = be_roster->GetActiveAppInfo(&appInfo);
190	if (status == B_OK)
191		return appInfo.team;
192
193	return status;
194}
195
196
197// #pragma mark -
198
199
200CaptureWindow::CaptureWindow()
201	:
202	BWindow(BRect(0, 0, 100, 100), "mouse capture", B_NO_BORDER_WINDOW_LOOK,
203		B_NORMAL_WINDOW_FEEL, B_ASYNCHRONOUS_CONTROLS, B_ALL_WORKSPACES)
204{
205	fCaptureView = new CaptureView();
206	AddChild(fCaptureView);
207}
208
209
210CaptureWindow::~CaptureWindow()
211{
212}
213
214
215void
216CaptureWindow::MessageReceived(BMessage* message)
217{
218	switch (message->what) {
219		case kMsgHideWhenMouseMovedOut:
220			fCaptureView->AddMoveOutNotification(message);
221			break;
222		default:
223			BWindow::MessageReceived(message);
224			break;
225	}
226}
227