blob: 06b2a289b787b6c9a3a91b32dce199dfa95f672a [file] [log] [blame]
// Copyright 2016 Samsung Electronics. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "starboard/shared/wayland/application_wayland.h"
#include <EGL/egl.h>
#include <poll.h>
#include <sys/eventfd.h>
#include "starboard/log.h"
#include "starboard/memory.h"
#include "starboard/shared/wayland/dev_input.h"
#include "starboard/shared/wayland/window_internal.h"
#include "starboard/time.h"
// YouTube Technical Requirement 2018 (2016/11/1 - Initial draft)
// 9.5 The device MUST dispatch the following key events, as appropriate:
// * Window.keydown
// * After a key is held down for 500ms, the Window.keydown event
// MUST repeat every 50ms until a user stops holding the key down.
// * Window.keyup
static const SbTime kKeyHoldTime = 500 * kSbTimeMillisecond;
static const SbTime kKeyRepeatTime = 50 * kSbTimeMillisecond;
namespace starboard {
namespace shared {
namespace wayland {
// Tizen application engine using the generic queue and a tizen implementation.
ApplicationWayland::ApplicationWayland(float video_pixel_ratio)
: video_pixel_ratio_(video_pixel_ratio),
seat_(NULL),
keyboard_(NULL),
key_repeat_event_id_(kSbEventIdInvalid),
key_repeat_interval_(kKeyHoldTime),
key_modifiers_(0) {}
SbWindow ApplicationWayland::CreateWindow(const SbWindowOptions* options) {
SB_DLOG(INFO) << "CreateWindow";
SbWindow window = new SbWindowPrivate(options, video_pixel_ratio_);
window_ = window;
// Video Plane
#if SB_CAN(USE_WAYLAND_VIDEO_WINDOW)
window->video_window = display_;
#else
window->video_window = elm_win_add(NULL, "Cobalt_Video", ELM_WIN_BASIC);
elm_win_title_set(window->video_window, "Cobalt_Video");
elm_win_autodel_set(window->video_window, EINA_TRUE);
evas_object_resize(window->video_window, window->width, window->height);
evas_object_hide(window->video_window);
#endif
// Graphics Plane
window->surface = wl_compositor_create_surface(compositor_);
window->shell_surface = wl_shell_get_shell_surface(shell_, window->surface);
wl_shell_surface_add_listener(window->shell_surface, &shell_surface_listener,
window);
window->tz_visibility =
tizen_policy_get_visibility(tz_policy_, window->surface);
tizen_visibility_add_listener(window->tz_visibility,
&tizen_visibility_listener, window);
tizen_policy_activate(tz_policy_, window->surface);
wl_shell_surface_set_title(window->shell_surface, "cobalt");
WindowRaise();
struct wl_region* region;
region = wl_compositor_create_region(compositor_);
wl_region_add(region, 0, 0, window->width, window->height);
wl_surface_set_opaque_region(window->surface, region);
wl_region_destroy(region);
window->egl_window =
wl_egl_window_create(window->surface, window->width, window->height);
return window;
}
bool ApplicationWayland::DestroyWindow(SbWindow window) {
SB_DLOG(INFO) << "DestroyWindow";
if (!SbWindowIsValid(window)) {
SB_DLOG(WARNING) << "wayland window destroy failed!!";
return false;
}
// Video Plane
#if !SB_CAN(USE_WAYLAND_VIDEO_WINDOW)
evas_object_hide(window->video_window);
#endif
window->video_window = NULL;
// Graphics Plane
tizen_visibility_destroy(window->tz_visibility);
wl_egl_window_destroy(window->egl_window);
wl_shell_surface_destroy(window->shell_surface);
wl_surface_destroy(window->surface);
return true;
}
void ApplicationWayland::Initialize() {
SB_DLOG(INFO) << "Initialize";
// Video Plane
elm_policy_set(ELM_POLICY_QUIT, ELM_POLICY_QUIT_LAST_WINDOW_CLOSED);
// Graphics Plane
display_ = wl_display_connect(NULL);
struct wl_registry* registry = wl_display_get_registry(display_);
wl_registry_add_listener(registry, &registry_listener, this);
wl_display_dispatch(display_);
wl_display_roundtrip(display_);
// Open wakeup event
wakeup_fd_ = eventfd(0, 0);
if (wakeup_fd_ == -1)
SB_DLOG(ERROR) << "wakeup_fd_ creation failed";
InitializeEgl();
}
void ApplicationWayland::Teardown() {
SB_DLOG(INFO) << "Teardown";
DeleteRepeatKey();
TerminateEgl();
wl_display_flush(display_);
wl_display_disconnect(display_);
// Close wakeup event
close(wakeup_fd_);
}
void ApplicationWayland::OnSuspend() {
TerminateEgl();
}
void ApplicationWayland::OnResume() {
InitializeEgl();
}
void ApplicationWayland::InitializeEgl() {
EGLDisplay egl_display = eglGetDisplay(display_);
SB_DLOG(INFO) << __FUNCTION__ << ": INITIALIZE egl_display=" << egl_display;
SB_DCHECK(egl_display);
eglInitialize(egl_display, NULL, NULL);
}
void ApplicationWayland::TerminateEgl() {
EGLDisplay egl_display = eglGetDisplay(display_);
SB_DLOG(INFO) << __FUNCTION__ << ": TERMINATE egl_display=" << egl_display;
SB_DCHECK(egl_display);
eglTerminate(egl_display);
}
bool ApplicationWayland::MayHaveSystemEvents() {
// SB_DCHECK(SbThreadIsValid(wayland_thread_));
return display_;
}
shared::starboard::Application::Event*
ApplicationWayland::PollNextSystemEvent() {
// if queue is empty, try to read fd and push event to queue
if (wl_display_prepare_read(display_) == 0) {
wl_display_read_events(display_);
}
// dispatch queue if any event
if (wl_display_dispatch_pending(display_) < 0) {
SB_DLOG(ERROR) << "wl_display_dispatch_pending Error";
return NULL;
}
return NULL;
}
shared::starboard::Application::Event*
ApplicationWayland::WaitForSystemEventWithTimeout(SbTime duration) {
if (wl_display_flush(display_) < 0) {
SB_DLOG(ERROR) << "wl_display_flush Error";
return NULL;
}
struct pollfd fds[2];
struct timespec timeout_ts;
int ret;
timeout_ts.tv_sec = duration / kSbTimeSecond;
timeout_ts.tv_nsec =
(duration % kSbTimeSecond) * kSbTimeNanosecondsPerMicrosecond;
// wait wayland event
fds[0].fd = wl_display_get_fd(display_);
fds[0].events = POLLIN;
fds[0].revents = 0;
// wait wakeup event by event injection
fds[1].fd = wakeup_fd_;
fds[1].events = POLLIN;
fds[1].revents = 0;
ret = ppoll(fds, 2, &timeout_ts, NULL);
if (timeout_ts.tv_sec > 0) // long-wait log
SB_DLOG(INFO) << "WaitForSystemEventWithTimeout : wakeup " << ret << " 0("
<< fds[0].revents << ") 1(" << fds[1].revents << ")";
if (ret > 0 && fds[1].revents & POLLIN) { // clear wakeup event
uint64_t u;
read(wakeup_fd_, &u, sizeof(uint64_t));
}
// TODO : print log for too short waiting(under 2ms) to prevent abnormal fd
// events.
return NULL;
}
void ApplicationWayland::WakeSystemEventWait() {
uint64_t u = 1;
write(wakeup_fd_, &u, sizeof(uint64_t));
}
void ApplicationWayland::CreateRepeatKey() {
if (!key_repeat_state_) {
return;
}
if (key_repeat_interval_) {
key_repeat_interval_ = kKeyRepeatTime;
}
CreateKey(key_repeat_key_, key_repeat_state_, true);
}
void ApplicationWayland::DeleteRepeatKey() {
if (key_repeat_event_id_ != kSbEventIdInvalid) {
SbEventCancel(key_repeat_event_id_);
key_repeat_event_id_ = kSbEventIdInvalid;
}
}
void ApplicationWayland::CreateKey(int key, int state, bool is_repeat) {
SbInputData* data = new SbInputData();
SbMemorySet(data, 0, sizeof(*data));
data->window = window_;
data->type = (state == 0 ? kSbInputEventTypeUnpress : kSbInputEventTypePress);
data->device_type = kSbInputDeviceTypeRemote;
data->device_id = 1; // kKeyboardDeviceId;
data->key = KeyCodeToSbKey(key);
data->key_location = KeyCodeToSbKeyLocation(key);
data->key_modifiers = key_modifiers_;
Inject(new Event(kSbEventTypeInput, data,
&Application::DeleteDestructor<SbInputData>));
DeleteRepeatKey();
if (is_repeat && state) {
key_repeat_key_ = key;
key_repeat_state_ = state;
key_repeat_event_id_ = SbEventSchedule([](void* window) {
ApplicationWayland* application =
reinterpret_cast<ApplicationWayland*>(window);
application->CreateRepeatKey();
}, this, key_repeat_interval_);
} else {
key_repeat_interval_ = kKeyHoldTime;
}
}
void ApplicationWayland::WindowRaise() {
if (tz_policy_)
tizen_policy_raise(tz_policy_, window_->surface);
if (window_->shell_surface)
wl_shell_surface_set_toplevel(window_->shell_surface);
}
void ApplicationWayland::Pause(void* context, EventHandledCallback callback) {
Application::Pause(context, callback);
ScopedLock lock(observers_mutex_);
std::for_each(observers_.begin(), observers_.end(),
[](StateObserver* i) { i->OnAppPause(); });
}
void ApplicationWayland::Unpause(void* context, EventHandledCallback callback) {
Application::Unpause(context, callback);
ScopedLock lock(observers_mutex_);
std::for_each(observers_.begin(), observers_.end(),
[](StateObserver* i) { i->OnAppUnpause(); });
}
void ApplicationWayland::RegisterObserver(StateObserver* observer) {
ScopedLock lock(observers_mutex_);
observers_.push_back(observer);
}
void ApplicationWayland::UnregisterObserver(StateObserver* observer) {
ScopedLock lock(observers_mutex_);
auto it = std::find_if(
observers_.begin(), observers_.end(),
[observer](const StateObserver* i) { return observer == i; });
if (it == observers_.end())
return;
observers_.erase(it);
}
void ApplicationWayland::Deeplink(char* payload) {
const size_t payload_size = strlen(payload) + 1;
char* copied_payload = new char[payload_size];
snprintf(copied_payload, payload_size, "%s", payload);
Inject(new Event(kSbEventTypeLink, copiedPayload,
[](void* data) { delete[] reinterpret_cast<char*>(data); }));
}
} // namespace wayland
} // namespace shared
} // namespace starboard