2016-06-18 12:46:12 +00:00
|
|
|
/*************************************************************************/
|
|
|
|
/* haiku_direct_window.cpp */
|
|
|
|
/*************************************************************************/
|
|
|
|
/* This file is part of: */
|
|
|
|
/* GODOT ENGINE */
|
2017-08-27 12:16:55 +00:00
|
|
|
/* https://godotengine.org */
|
2016-06-18 12:46:12 +00:00
|
|
|
/*************************************************************************/
|
2017-01-01 21:01:57 +00:00
|
|
|
/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
|
2017-04-07 22:11:42 +00:00
|
|
|
/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */
|
2016-06-18 12:46:12 +00:00
|
|
|
/* */
|
|
|
|
/* Permission is hereby granted, free of charge, to any person obtaining */
|
|
|
|
/* a copy of this software and associated documentation files (the */
|
|
|
|
/* "Software"), to deal in the Software without restriction, including */
|
|
|
|
/* without limitation the rights to use, copy, modify, merge, publish, */
|
|
|
|
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
|
|
|
/* permit persons to whom the Software is furnished to do so, subject to */
|
|
|
|
/* the following conditions: */
|
|
|
|
/* */
|
|
|
|
/* The above copyright notice and this permission notice shall be */
|
|
|
|
/* included in all copies or substantial portions of the Software. */
|
|
|
|
/* */
|
|
|
|
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
|
|
|
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
|
|
|
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
|
|
|
|
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
|
|
|
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
|
|
|
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
|
|
|
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
|
|
|
/*************************************************************************/
|
2015-07-11 21:52:47 +00:00
|
|
|
#include <UnicodeChar.h>
|
|
|
|
|
2015-06-11 19:57:41 +00:00
|
|
|
#include "haiku_direct_window.h"
|
2015-07-11 21:52:47 +00:00
|
|
|
#include "key_mapping_haiku.h"
|
2017-03-05 15:44:50 +00:00
|
|
|
#include "main/main.h"
|
|
|
|
#include "os/keyboard.h"
|
2015-06-11 19:57:41 +00:00
|
|
|
|
|
|
|
HaikuDirectWindow::HaikuDirectWindow(BRect p_frame)
|
2017-03-05 15:44:50 +00:00
|
|
|
: BDirectWindow(p_frame, "Godot", B_TITLED_WINDOW, B_QUIT_ON_WINDOW_CLOSE) {
|
2015-06-16 18:52:24 +00:00
|
|
|
last_mouse_pos_valid = false;
|
|
|
|
last_buttons_state = 0;
|
2015-06-18 19:41:33 +00:00
|
|
|
last_button_mask = 0;
|
2015-06-21 19:18:27 +00:00
|
|
|
last_key_modifier_state = 0;
|
2015-06-11 19:57:41 +00:00
|
|
|
}
|
|
|
|
|
2015-06-16 18:52:24 +00:00
|
|
|
HaikuDirectWindow::~HaikuDirectWindow() {
|
2016-01-04 10:46:16 +00:00
|
|
|
delete update_runner;
|
2015-06-11 19:57:41 +00:00
|
|
|
}
|
|
|
|
|
2017-03-05 15:44:50 +00:00
|
|
|
void HaikuDirectWindow::SetHaikuGLView(HaikuGLView *p_view) {
|
2015-06-11 19:57:41 +00:00
|
|
|
view = p_view;
|
|
|
|
}
|
|
|
|
|
2015-06-16 18:52:24 +00:00
|
|
|
void HaikuDirectWindow::StartMessageRunner() {
|
|
|
|
update_runner = new BMessageRunner(BMessenger(this),
|
2017-03-05 15:44:50 +00:00
|
|
|
new BMessage(REDRAW_MSG), 1000000 / 30 /* 30 fps */);
|
2015-06-11 19:57:41 +00:00
|
|
|
}
|
|
|
|
|
2015-06-16 18:52:24 +00:00
|
|
|
void HaikuDirectWindow::StopMessageRunner() {
|
|
|
|
delete update_runner;
|
|
|
|
}
|
2015-06-11 19:57:41 +00:00
|
|
|
|
2017-03-05 15:44:50 +00:00
|
|
|
void HaikuDirectWindow::SetInput(InputDefault *p_input) {
|
2015-06-16 18:52:24 +00:00
|
|
|
input = p_input;
|
|
|
|
}
|
|
|
|
|
2017-03-05 15:44:50 +00:00
|
|
|
void HaikuDirectWindow::SetMainLoop(MainLoop *p_main_loop) {
|
2015-06-19 22:59:32 +00:00
|
|
|
main_loop = p_main_loop;
|
|
|
|
}
|
|
|
|
|
2015-06-16 18:52:24 +00:00
|
|
|
bool HaikuDirectWindow::QuitRequested() {
|
2015-06-19 22:59:32 +00:00
|
|
|
main_loop->notification(MainLoop::NOTIFICATION_WM_QUIT_REQUEST);
|
|
|
|
return false;
|
2015-06-11 19:57:41 +00:00
|
|
|
}
|
|
|
|
|
2017-03-05 15:44:50 +00:00
|
|
|
void HaikuDirectWindow::DirectConnected(direct_buffer_info *info) {
|
2015-06-17 19:27:45 +00:00
|
|
|
view->DirectConnected(info);
|
2015-06-11 19:57:41 +00:00
|
|
|
view->EnableDirectMode(true);
|
|
|
|
}
|
|
|
|
|
2017-03-05 15:44:50 +00:00
|
|
|
void HaikuDirectWindow::MessageReceived(BMessage *message) {
|
2015-06-16 18:52:24 +00:00
|
|
|
switch (message->what) {
|
|
|
|
case REDRAW_MSG:
|
2015-06-19 22:59:32 +00:00
|
|
|
if (Main::iteration() == true) {
|
|
|
|
view->EnableDirectMode(false);
|
|
|
|
Quit();
|
|
|
|
}
|
2015-06-16 18:52:24 +00:00
|
|
|
break;
|
|
|
|
|
2015-06-17 19:27:45 +00:00
|
|
|
default:
|
2015-06-16 18:52:24 +00:00
|
|
|
BDirectWindow::MessageReceived(message);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-03-05 15:44:50 +00:00
|
|
|
void HaikuDirectWindow::DispatchMessage(BMessage *message, BHandler *handler) {
|
2015-06-16 18:52:24 +00:00
|
|
|
switch (message->what) {
|
|
|
|
case B_MOUSE_DOWN:
|
|
|
|
case B_MOUSE_UP:
|
2015-07-02 15:41:32 +00:00
|
|
|
HandleMouseButton(message);
|
2015-06-16 18:52:24 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case B_MOUSE_MOVED:
|
2015-07-02 15:41:32 +00:00
|
|
|
HandleMouseMoved(message);
|
2015-06-16 18:52:24 +00:00
|
|
|
break;
|
|
|
|
|
2015-06-21 19:18:27 +00:00
|
|
|
case B_MOUSE_WHEEL_CHANGED:
|
2015-07-02 15:41:32 +00:00
|
|
|
HandleMouseWheelChanged(message);
|
|
|
|
break;
|
|
|
|
|
2015-07-11 21:52:47 +00:00
|
|
|
case B_KEY_DOWN:
|
|
|
|
case B_KEY_UP:
|
|
|
|
HandleKeyboardEvent(message);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case B_MODIFIERS_CHANGED:
|
|
|
|
HandleKeyboardModifierEvent(message);
|
|
|
|
break;
|
|
|
|
|
2015-07-02 15:41:32 +00:00
|
|
|
case B_WINDOW_RESIZED:
|
|
|
|
HandleWindowResized(message);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case LOCKGL_MSG:
|
|
|
|
view->LockGL();
|
|
|
|
break;
|
|
|
|
|
|
|
|
case UNLOCKGL_MSG:
|
|
|
|
view->UnlockGL();
|
2015-06-21 19:18:27 +00:00
|
|
|
break;
|
|
|
|
|
2015-06-16 18:52:24 +00:00
|
|
|
default:
|
|
|
|
BDirectWindow::DispatchMessage(message, handler);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-03-05 15:44:50 +00:00
|
|
|
void HaikuDirectWindow::HandleMouseButton(BMessage *message) {
|
2015-06-16 18:52:24 +00:00
|
|
|
BPoint where;
|
|
|
|
if (message->FindPoint("where", &where) != B_OK) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-06-17 19:27:45 +00:00
|
|
|
uint32 modifiers = message->FindInt32("modifiers");
|
2015-06-16 18:52:24 +00:00
|
|
|
uint32 buttons = message->FindInt32("buttons");
|
|
|
|
uint32 button = buttons ^ last_buttons_state;
|
|
|
|
last_buttons_state = buttons;
|
|
|
|
|
|
|
|
// TODO: implement the mouse_mode checks
|
2017-01-14 11:26:56 +00:00
|
|
|
/*
|
|
|
|
if (mouse_mode == MOUSE_MODE_CAPTURED) {
|
|
|
|
event.xbutton.x=last_mouse_pos.x;
|
|
|
|
event.xbutton.y=last_mouse_pos.y;
|
|
|
|
}
|
|
|
|
*/
|
2015-06-17 19:27:45 +00:00
|
|
|
|
2017-05-20 15:38:03 +00:00
|
|
|
Ref<InputEvent> mouse_event;
|
|
|
|
mouse_event.type = Ref<InputEvent>::MOUSE_BUTTON;
|
2015-06-16 18:52:24 +00:00
|
|
|
mouse_event.device = 0;
|
|
|
|
|
2015-06-17 19:27:45 +00:00
|
|
|
mouse_event.mouse_button.mod = GetKeyModifierState(modifiers);
|
2017-05-20 15:38:03 +00:00
|
|
|
mouse_event->get_button_mask() = GetMouseButtonState(buttons);
|
|
|
|
mouse_event->get_pos().x = where.x;
|
|
|
|
mouse_event->get_pos().y = where.y;
|
2015-06-16 18:52:24 +00:00
|
|
|
mouse_event.mouse_button.global_x = where.x;
|
|
|
|
mouse_event.mouse_button.global_y = where.y;
|
|
|
|
|
|
|
|
switch (button) {
|
|
|
|
default:
|
|
|
|
case B_PRIMARY_MOUSE_BUTTON:
|
2017-05-20 15:38:03 +00:00
|
|
|
mouse_event->get_button_index() = 1;
|
2015-06-16 18:52:24 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case B_SECONDARY_MOUSE_BUTTON:
|
2017-05-20 15:38:03 +00:00
|
|
|
mouse_event->get_button_index() = 2;
|
2015-06-16 18:52:24 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case B_TERTIARY_MOUSE_BUTTON:
|
2017-05-20 15:38:03 +00:00
|
|
|
mouse_event->get_button_index() = 3;
|
2015-06-16 18:52:24 +00:00
|
|
|
break;
|
|
|
|
}
|
2015-06-17 19:27:45 +00:00
|
|
|
|
2017-05-20 15:38:03 +00:00
|
|
|
mouse_event->is_pressed() = (message->what == B_MOUSE_DOWN);
|
2015-06-16 18:52:24 +00:00
|
|
|
|
2017-05-20 15:38:03 +00:00
|
|
|
if (message->what == B_MOUSE_DOWN && mouse_event->get_button_index() == 1) {
|
2015-06-16 18:52:24 +00:00
|
|
|
int32 clicks = message->FindInt32("clicks");
|
2015-06-17 19:27:45 +00:00
|
|
|
|
2015-06-16 18:52:24 +00:00
|
|
|
if (clicks > 1) {
|
2017-03-05 15:44:50 +00:00
|
|
|
mouse_event.mouse_button.doubleclick = true;
|
2015-06-16 18:52:24 +00:00
|
|
|
}
|
2015-06-17 19:27:45 +00:00
|
|
|
}
|
2015-06-16 18:52:24 +00:00
|
|
|
|
|
|
|
input->parse_input_event(mouse_event);
|
|
|
|
}
|
|
|
|
|
2017-03-05 15:44:50 +00:00
|
|
|
void HaikuDirectWindow::HandleMouseMoved(BMessage *message) {
|
2015-06-16 18:52:24 +00:00
|
|
|
BPoint where;
|
|
|
|
if (message->FindPoint("where", &where) != B_OK) {
|
|
|
|
return;
|
|
|
|
}
|
2015-06-17 19:27:45 +00:00
|
|
|
|
2015-06-16 18:52:24 +00:00
|
|
|
Point2i pos(where.x, where.y);
|
2015-06-17 19:27:45 +00:00
|
|
|
uint32 modifiers = message->FindInt32("modifiers");
|
|
|
|
uint32 buttons = message->FindInt32("buttons");
|
|
|
|
|
2015-06-16 18:52:24 +00:00
|
|
|
if (!last_mouse_pos_valid) {
|
2015-06-18 19:41:33 +00:00
|
|
|
last_mouse_position = pos;
|
2015-06-17 19:27:45 +00:00
|
|
|
last_mouse_pos_valid = true;
|
2015-06-16 18:52:24 +00:00
|
|
|
}
|
|
|
|
|
2015-06-18 19:41:33 +00:00
|
|
|
Point2i rel = pos - last_mouse_position;
|
2015-06-16 18:52:24 +00:00
|
|
|
|
2017-05-20 15:38:03 +00:00
|
|
|
Ref<InputEvent> motion_event;
|
|
|
|
motion_event.type = Ref<InputEvent>::MOUSE_MOTION;
|
2015-06-16 18:52:24 +00:00
|
|
|
motion_event.device = 0;
|
|
|
|
|
2015-06-17 19:27:45 +00:00
|
|
|
motion_event.mouse_motion.mod = GetKeyModifierState(modifiers);
|
2017-05-20 15:38:03 +00:00
|
|
|
motion_event->get_button_mask() = GetMouseButtonState(buttons);
|
2015-06-16 18:52:24 +00:00
|
|
|
motion_event.mouse_motion.x = pos.x;
|
|
|
|
motion_event.mouse_motion.y = pos.y;
|
2017-03-29 15:29:38 +00:00
|
|
|
input->set_mouse_position(pos);
|
2015-06-16 18:52:24 +00:00
|
|
|
motion_event.mouse_motion.global_x = pos.x;
|
|
|
|
motion_event.mouse_motion.global_y = pos.y;
|
2017-01-13 22:23:42 +00:00
|
|
|
motion_event.mouse_motion.speed_x = input->get_last_mouse_speed().x;
|
|
|
|
motion_event.mouse_motion.speed_y = input->get_last_mouse_speed().y;
|
2015-06-16 18:52:24 +00:00
|
|
|
|
2017-05-20 15:38:03 +00:00
|
|
|
motion_event->get_relative().x = rel.x;
|
|
|
|
motion_event->get_relative().y = rel.y;
|
2015-06-16 18:52:24 +00:00
|
|
|
|
2015-06-18 19:41:33 +00:00
|
|
|
last_mouse_position = pos;
|
2015-06-16 18:52:24 +00:00
|
|
|
|
|
|
|
input->parse_input_event(motion_event);
|
|
|
|
}
|
2015-06-17 19:27:45 +00:00
|
|
|
|
2017-03-05 15:44:50 +00:00
|
|
|
void HaikuDirectWindow::HandleMouseWheelChanged(BMessage *message) {
|
2015-06-21 19:18:27 +00:00
|
|
|
float wheel_delta_y = 0;
|
|
|
|
if (message->FindFloat("be:wheel_delta_y", &wheel_delta_y) != B_OK) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-05-20 15:38:03 +00:00
|
|
|
Ref<InputEvent> mouse_event;
|
|
|
|
mouse_event.type = Ref<InputEvent>::MOUSE_BUTTON;
|
2015-06-21 19:18:27 +00:00
|
|
|
mouse_event.device = 0;
|
|
|
|
|
2017-05-20 15:38:03 +00:00
|
|
|
mouse_event->get_button_index() = wheel_delta_y < 0 ? 4 : 5;
|
2015-06-21 19:18:27 +00:00
|
|
|
mouse_event.mouse_button.mod = GetKeyModifierState(last_key_modifier_state);
|
2017-05-20 15:38:03 +00:00
|
|
|
mouse_event->get_button_mask() = last_button_mask;
|
|
|
|
mouse_event->get_pos().x = last_mouse_position.x;
|
|
|
|
mouse_event->get_pos().y = last_mouse_position.y;
|
2015-06-21 19:18:27 +00:00
|
|
|
mouse_event.mouse_button.global_x = last_mouse_position.x;
|
|
|
|
mouse_event.mouse_button.global_y = last_mouse_position.y;
|
|
|
|
|
2017-05-20 15:38:03 +00:00
|
|
|
mouse_event->is_pressed() = true;
|
2015-06-21 19:18:27 +00:00
|
|
|
input->parse_input_event(mouse_event);
|
|
|
|
|
2017-05-20 15:38:03 +00:00
|
|
|
mouse_event->is_pressed() = false;
|
2015-06-21 19:18:27 +00:00
|
|
|
input->parse_input_event(mouse_event);
|
|
|
|
}
|
|
|
|
|
2017-03-05 15:44:50 +00:00
|
|
|
void HaikuDirectWindow::HandleKeyboardEvent(BMessage *message) {
|
2015-07-11 21:52:47 +00:00
|
|
|
int32 raw_char = 0;
|
|
|
|
int32 key = 0;
|
|
|
|
int32 modifiers = 0;
|
|
|
|
|
|
|
|
if (message->FindInt32("raw_char", &raw_char) != B_OK) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (message->FindInt32("key", &key) != B_OK) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (message->FindInt32("modifiers", &modifiers) != B_OK) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-05-20 15:38:03 +00:00
|
|
|
Ref<InputEvent> event;
|
|
|
|
event.type = Ref<InputEvent>::KEY;
|
2015-07-11 21:52:47 +00:00
|
|
|
event.device = 0;
|
|
|
|
event.key.mod = GetKeyModifierState(modifiers);
|
2017-05-20 15:38:03 +00:00
|
|
|
event->is_pressed() = (message->what == B_KEY_DOWN);
|
|
|
|
event->get_scancode() = KeyMappingHaiku::get_keysym(raw_char, key);
|
|
|
|
event->is_echo() = message->HasInt32("be:key_repeat");
|
2015-07-11 21:52:47 +00:00
|
|
|
event.key.unicode = 0;
|
|
|
|
|
2017-03-05 15:44:50 +00:00
|
|
|
const char *bytes = NULL;
|
2015-07-11 21:52:47 +00:00
|
|
|
if (message->FindString("bytes", &bytes) == B_OK) {
|
|
|
|
event.key.unicode = BUnicodeChar::FromUTF8(&bytes);
|
|
|
|
}
|
|
|
|
|
2017-03-24 20:45:31 +00:00
|
|
|
//make it consistent across platforms.
|
2017-05-20 15:38:03 +00:00
|
|
|
if (event->get_scancode() == KEY_BACKTAB) {
|
|
|
|
event->get_scancode() = KEY_TAB;
|
|
|
|
event->get_shift() = true;
|
2015-07-11 21:52:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
input->parse_input_event(event);
|
|
|
|
}
|
|
|
|
|
2017-03-05 15:44:50 +00:00
|
|
|
void HaikuDirectWindow::HandleKeyboardModifierEvent(BMessage *message) {
|
2015-07-11 21:52:47 +00:00
|
|
|
int32 old_modifiers = 0;
|
|
|
|
int32 modifiers = 0;
|
|
|
|
|
|
|
|
if (message->FindInt32("be:old_modifiers", &old_modifiers) != B_OK) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (message->FindInt32("modifiers", &modifiers) != B_OK) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
int32 key = old_modifiers ^ modifiers;
|
|
|
|
|
2017-05-20 15:38:03 +00:00
|
|
|
Ref<InputEvent> event;
|
|
|
|
event.type = Ref<InputEvent>::KEY;
|
2015-07-11 21:52:47 +00:00
|
|
|
event.device = 0;
|
|
|
|
event.key.mod = GetKeyModifierState(modifiers);
|
2017-05-20 15:38:03 +00:00
|
|
|
event->is_pressed() = ((modifiers & key) != 0);
|
|
|
|
event->get_scancode() = KeyMappingHaiku::get_modifier_keysym(key);
|
|
|
|
event->is_echo() = false;
|
2015-07-11 21:52:47 +00:00
|
|
|
event.key.unicode = 0;
|
|
|
|
|
|
|
|
input->parse_input_event(event);
|
|
|
|
}
|
|
|
|
|
2017-03-05 15:44:50 +00:00
|
|
|
void HaikuDirectWindow::HandleWindowResized(BMessage *message) {
|
2015-07-02 15:41:32 +00:00
|
|
|
int32 width = 0;
|
|
|
|
int32 height = 0;
|
|
|
|
|
|
|
|
if ((message->FindInt32("width", &width) != B_OK) || (message->FindInt32("height", &height) != B_OK)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
current_video_mode->width = width;
|
|
|
|
current_video_mode->height = height;
|
|
|
|
}
|
|
|
|
|
2015-06-17 19:27:45 +00:00
|
|
|
inline InputModifierState HaikuDirectWindow::GetKeyModifierState(uint32 p_state) {
|
2015-06-21 19:18:27 +00:00
|
|
|
last_key_modifier_state = p_state;
|
2015-06-17 19:27:45 +00:00
|
|
|
InputModifierState state;
|
|
|
|
|
|
|
|
state.shift = (p_state & B_SHIFT_KEY) != 0;
|
|
|
|
state.control = (p_state & B_CONTROL_KEY) != 0;
|
|
|
|
state.alt = (p_state & B_OPTION_KEY) != 0;
|
|
|
|
state.meta = (p_state & B_COMMAND_KEY) != 0;
|
|
|
|
|
|
|
|
return state;
|
|
|
|
}
|
|
|
|
|
2015-06-18 19:41:33 +00:00
|
|
|
inline int HaikuDirectWindow::GetMouseButtonState(uint32 p_state) {
|
|
|
|
int state = 0;
|
2015-06-17 19:27:45 +00:00
|
|
|
|
|
|
|
if (p_state & B_PRIMARY_MOUSE_BUTTON) {
|
|
|
|
state |= 1 << 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (p_state & B_SECONDARY_MOUSE_BUTTON) {
|
|
|
|
state |= 1 << 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (p_state & B_TERTIARY_MOUSE_BUTTON) {
|
|
|
|
state |= 1 << 2;
|
|
|
|
}
|
|
|
|
|
2015-06-18 19:41:33 +00:00
|
|
|
last_button_mask = state;
|
|
|
|
|
2015-06-17 19:27:45 +00:00
|
|
|
return state;
|
|
|
|
}
|