1 /*************************************************************************/
2 /*  haiku_direct_window.cpp                                              */
3 /*************************************************************************/
4 /*                       This file is part of:                           */
5 /*                           GODOT ENGINE                                */
6 /*                      https://godotengine.org                          */
7 /*************************************************************************/
8 /* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur.                 */
9 /* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md)    */
10 /*                                                                       */
11 /* Permission is hereby granted, free of charge, to any person obtaining */
12 /* a copy of this software and associated documentation files (the       */
13 /* "Software"), to deal in the Software without restriction, including   */
14 /* without limitation the rights to use, copy, modify, merge, publish,   */
15 /* distribute, sublicense, and/or sell copies of the Software, and to    */
16 /* permit persons to whom the Software is furnished to do so, subject to */
17 /* the following conditions:                                             */
18 /*                                                                       */
19 /* The above copyright notice and this permission notice shall be        */
20 /* included in all copies or substantial portions of the Software.       */
21 /*                                                                       */
22 /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
23 /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
24 /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
25 /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
26 /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
27 /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
28 /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
29 /*************************************************************************/
30 #include <UnicodeChar.h>
31 
32 #include "haiku_direct_window.h"
33 #include "key_mapping_haiku.h"
34 #include "main/main.h"
35 #include "os/keyboard.h"
36 
HaikuDirectWindow(BRect p_frame)37 HaikuDirectWindow::HaikuDirectWindow(BRect p_frame) :
38 		BDirectWindow(p_frame, "Godot", B_TITLED_WINDOW, B_QUIT_ON_WINDOW_CLOSE) {
39 	last_mouse_pos_valid = false;
40 	last_buttons_state = 0;
41 	last_button_mask = 0;
42 	last_key_modifier_state = 0;
43 }
44 
~HaikuDirectWindow()45 HaikuDirectWindow::~HaikuDirectWindow() {
46 	delete update_runner;
47 }
48 
SetHaikuGLView(HaikuGLView * p_view)49 void HaikuDirectWindow::SetHaikuGLView(HaikuGLView *p_view) {
50 	view = p_view;
51 }
52 
StartMessageRunner()53 void HaikuDirectWindow::StartMessageRunner() {
54 	update_runner = new BMessageRunner(BMessenger(this),
55 			new BMessage(REDRAW_MSG), 1000000 / 30 /* 30 fps */);
56 }
57 
StopMessageRunner()58 void HaikuDirectWindow::StopMessageRunner() {
59 	delete update_runner;
60 }
61 
SetInput(InputDefault * p_input)62 void HaikuDirectWindow::SetInput(InputDefault *p_input) {
63 	input = p_input;
64 }
65 
SetMainLoop(MainLoop * p_main_loop)66 void HaikuDirectWindow::SetMainLoop(MainLoop *p_main_loop) {
67 	main_loop = p_main_loop;
68 }
69 
QuitRequested()70 bool HaikuDirectWindow::QuitRequested() {
71 	main_loop->notification(MainLoop::NOTIFICATION_WM_QUIT_REQUEST);
72 	return false;
73 }
74 
DirectConnected(direct_buffer_info * info)75 void HaikuDirectWindow::DirectConnected(direct_buffer_info *info) {
76 	view->DirectConnected(info);
77 	view->EnableDirectMode(true);
78 }
79 
MessageReceived(BMessage * message)80 void HaikuDirectWindow::MessageReceived(BMessage *message) {
81 	switch (message->what) {
82 		case REDRAW_MSG:
83 			if (Main::iteration() == true) {
84 				view->EnableDirectMode(false);
85 				Quit();
86 			}
87 			break;
88 
89 		default:
90 			BDirectWindow::MessageReceived(message);
91 	}
92 }
93 
DispatchMessage(BMessage * message,BHandler * handler)94 void HaikuDirectWindow::DispatchMessage(BMessage *message, BHandler *handler) {
95 	switch (message->what) {
96 		case B_MOUSE_DOWN:
97 		case B_MOUSE_UP:
98 			HandleMouseButton(message);
99 			break;
100 
101 		case B_MOUSE_MOVED:
102 			HandleMouseMoved(message);
103 			break;
104 
105 		case B_MOUSE_WHEEL_CHANGED:
106 			HandleMouseWheelChanged(message);
107 			break;
108 
109 		case B_KEY_DOWN:
110 		case B_KEY_UP:
111 			HandleKeyboardEvent(message);
112 			break;
113 
114 		case B_MODIFIERS_CHANGED:
115 			HandleKeyboardModifierEvent(message);
116 			break;
117 
118 		case B_WINDOW_RESIZED:
119 			HandleWindowResized(message);
120 			break;
121 
122 		case LOCKGL_MSG:
123 			view->LockGL();
124 			break;
125 
126 		case UNLOCKGL_MSG:
127 			view->UnlockGL();
128 			break;
129 
130 		default:
131 			BDirectWindow::DispatchMessage(message, handler);
132 	}
133 }
134 
HandleMouseButton(BMessage * message)135 void HaikuDirectWindow::HandleMouseButton(BMessage *message) {
136 	BPoint where;
137 	if (message->FindPoint("where", &where) != B_OK) {
138 		return;
139 	}
140 
141 	uint32 modifiers = message->FindInt32("modifiers");
142 	uint32 buttons = message->FindInt32("buttons");
143 	uint32 button = buttons ^ last_buttons_state;
144 	last_buttons_state = buttons;
145 
146 	// TODO: implement the mouse_mode checks
147 	//if (mouse_mode == MOUSE_MODE_CAPTURED) {
148 	//	event.xbutton.x=last_mouse_pos.x;
149 	//	event.xbutton.y=last_mouse_pos.y;
150 	//}
151 
152 	InputEvent mouse_event;
153 	mouse_event.ID = ++event_id;
154 	mouse_event.type = InputEvent::MOUSE_BUTTON;
155 	mouse_event.device = 0;
156 
157 	mouse_event.mouse_button.mod = GetKeyModifierState(modifiers);
158 	mouse_event.mouse_button.button_mask = GetMouseButtonState(buttons);
159 	mouse_event.mouse_button.x = where.x;
160 	mouse_event.mouse_button.y = where.y;
161 	mouse_event.mouse_button.global_x = where.x;
162 	mouse_event.mouse_button.global_y = where.y;
163 
164 	switch (button) {
165 		default:
166 		case B_PRIMARY_MOUSE_BUTTON:
167 			mouse_event.mouse_button.button_index = 1;
168 			break;
169 
170 		case B_SECONDARY_MOUSE_BUTTON:
171 			mouse_event.mouse_button.button_index = 2;
172 			break;
173 
174 		case B_TERTIARY_MOUSE_BUTTON:
175 			mouse_event.mouse_button.button_index = 3;
176 			break;
177 	}
178 
179 	mouse_event.mouse_button.pressed = (message->what == B_MOUSE_DOWN);
180 
181 	if (message->what == B_MOUSE_DOWN && mouse_event.mouse_button.button_index == 1) {
182 		int32 clicks = message->FindInt32("clicks");
183 
184 		if (clicks > 1) {
185 			mouse_event.mouse_button.doubleclick = true;
186 		}
187 	}
188 
189 	input->parse_input_event(mouse_event);
190 }
191 
HandleMouseMoved(BMessage * message)192 void HaikuDirectWindow::HandleMouseMoved(BMessage *message) {
193 	BPoint where;
194 	if (message->FindPoint("where", &where) != B_OK) {
195 		return;
196 	}
197 
198 	Point2i pos(where.x, where.y);
199 	uint32 modifiers = message->FindInt32("modifiers");
200 	uint32 buttons = message->FindInt32("buttons");
201 
202 	if (!last_mouse_pos_valid) {
203 		last_mouse_position = pos;
204 		last_mouse_pos_valid = true;
205 	}
206 
207 	Point2i rel = pos - last_mouse_position;
208 
209 	InputEvent motion_event;
210 	motion_event.ID = ++event_id;
211 	motion_event.type = InputEvent::MOUSE_MOTION;
212 	motion_event.device = 0;
213 
214 	motion_event.mouse_motion.mod = GetKeyModifierState(modifiers);
215 	motion_event.mouse_motion.button_mask = GetMouseButtonState(buttons);
216 	motion_event.mouse_motion.x = pos.x;
217 	motion_event.mouse_motion.y = pos.y;
218 	input->set_mouse_pos(pos);
219 	motion_event.mouse_motion.global_x = pos.x;
220 	motion_event.mouse_motion.global_y = pos.y;
221 	motion_event.mouse_motion.speed_x = input->get_mouse_speed().x;
222 	motion_event.mouse_motion.speed_y = input->get_mouse_speed().y;
223 
224 	motion_event.mouse_motion.relative_x = rel.x;
225 	motion_event.mouse_motion.relative_y = rel.y;
226 
227 	last_mouse_position = pos;
228 
229 	input->parse_input_event(motion_event);
230 }
231 
HandleMouseWheelChanged(BMessage * message)232 void HaikuDirectWindow::HandleMouseWheelChanged(BMessage *message) {
233 	float wheel_delta_y = 0;
234 	if (message->FindFloat("be:wheel_delta_y", &wheel_delta_y) != B_OK) {
235 		return;
236 	}
237 
238 	InputEvent mouse_event;
239 	mouse_event.ID = ++event_id;
240 	mouse_event.type = InputEvent::MOUSE_BUTTON;
241 	mouse_event.device = 0;
242 
243 	mouse_event.mouse_button.button_index = wheel_delta_y < 0 ? 4 : 5;
244 	mouse_event.mouse_button.mod = GetKeyModifierState(last_key_modifier_state);
245 	mouse_event.mouse_button.button_mask = last_button_mask;
246 	mouse_event.mouse_button.x = last_mouse_position.x;
247 	mouse_event.mouse_button.y = last_mouse_position.y;
248 	mouse_event.mouse_button.global_x = last_mouse_position.x;
249 	mouse_event.mouse_button.global_y = last_mouse_position.y;
250 
251 	mouse_event.mouse_button.pressed = true;
252 	input->parse_input_event(mouse_event);
253 
254 	mouse_event.ID = ++event_id;
255 	mouse_event.mouse_button.pressed = false;
256 	input->parse_input_event(mouse_event);
257 }
258 
HandleKeyboardEvent(BMessage * message)259 void HaikuDirectWindow::HandleKeyboardEvent(BMessage *message) {
260 	int32 raw_char = 0;
261 	int32 key = 0;
262 	int32 modifiers = 0;
263 
264 	if (message->FindInt32("raw_char", &raw_char) != B_OK) {
265 		return;
266 	}
267 
268 	if (message->FindInt32("key", &key) != B_OK) {
269 		return;
270 	}
271 
272 	if (message->FindInt32("modifiers", &modifiers) != B_OK) {
273 		return;
274 	}
275 
276 	InputEvent event;
277 	event.ID = ++event_id;
278 	event.type = InputEvent::KEY;
279 	event.device = 0;
280 	event.key.mod = GetKeyModifierState(modifiers);
281 	event.key.pressed = (message->what == B_KEY_DOWN);
282 	event.key.scancode = KeyMappingHaiku::get_keysym(raw_char, key);
283 	event.key.echo = message->HasInt32("be:key_repeat");
284 	event.key.unicode = 0;
285 
286 	const char *bytes = NULL;
287 	if (message->FindString("bytes", &bytes) == B_OK) {
288 		event.key.unicode = BUnicodeChar::FromUTF8(&bytes);
289 	}
290 
291 	//make it consistent accross platforms.
292 	if (event.key.scancode == KEY_BACKTAB) {
293 		event.key.scancode = KEY_TAB;
294 		event.key.mod.shift = true;
295 	}
296 
297 	input->parse_input_event(event);
298 }
299 
HandleKeyboardModifierEvent(BMessage * message)300 void HaikuDirectWindow::HandleKeyboardModifierEvent(BMessage *message) {
301 	int32 old_modifiers = 0;
302 	int32 modifiers = 0;
303 
304 	if (message->FindInt32("be:old_modifiers", &old_modifiers) != B_OK) {
305 		return;
306 	}
307 
308 	if (message->FindInt32("modifiers", &modifiers) != B_OK) {
309 		return;
310 	}
311 
312 	int32 key = old_modifiers ^ modifiers;
313 
314 	InputEvent event;
315 	event.ID = ++event_id;
316 	event.type = InputEvent::KEY;
317 	event.device = 0;
318 	event.key.mod = GetKeyModifierState(modifiers);
319 	event.key.pressed = ((modifiers & key) != 0);
320 	event.key.scancode = KeyMappingHaiku::get_modifier_keysym(key);
321 	event.key.echo = false;
322 	event.key.unicode = 0;
323 
324 	input->parse_input_event(event);
325 }
326 
HandleWindowResized(BMessage * message)327 void HaikuDirectWindow::HandleWindowResized(BMessage *message) {
328 	int32 width = 0;
329 	int32 height = 0;
330 
331 	if ((message->FindInt32("width", &width) != B_OK) || (message->FindInt32("height", &height) != B_OK)) {
332 		return;
333 	}
334 
335 	current_video_mode->width = width;
336 	current_video_mode->height = height;
337 }
338 
GetKeyModifierState(uint32 p_state)339 inline InputModifierState HaikuDirectWindow::GetKeyModifierState(uint32 p_state) {
340 	last_key_modifier_state = p_state;
341 	InputModifierState state;
342 
343 	state.shift = (p_state & B_SHIFT_KEY) != 0;
344 	state.control = (p_state & B_CONTROL_KEY) != 0;
345 	state.alt = (p_state & B_OPTION_KEY) != 0;
346 	state.meta = (p_state & B_COMMAND_KEY) != 0;
347 
348 	return state;
349 }
350 
GetMouseButtonState(uint32 p_state)351 inline int HaikuDirectWindow::GetMouseButtonState(uint32 p_state) {
352 	int state = 0;
353 
354 	if (p_state & B_PRIMARY_MOUSE_BUTTON) {
355 		state |= 1 << 0;
356 	}
357 
358 	if (p_state & B_SECONDARY_MOUSE_BUTTON) {
359 		state |= 1 << 1;
360 	}
361 
362 	if (p_state & B_TERTIARY_MOUSE_BUTTON) {
363 		state |= 1 << 2;
364 	}
365 
366 	last_button_mask = state;
367 
368 	return state;
369 }
370