| // Copyright 2018 The Cobalt Authors. 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 <string.h> |
| #include <sys/eventfd.h> |
| #include <unistd.h> |
| |
| #include "starboard/common/log.h" |
| #include "starboard/memory.h" |
| #include "starboard/shared/starboard/audio_sink/audio_sink_internal.h" |
| #include "starboard/shared/wayland/window_internal.h" |
| #include "starboard/time.h" |
| |
| namespace starboard { |
| namespace shared { |
| namespace wayland { |
| |
| namespace { |
| |
| // registry_listener |
| void GlobalObjectAvailable(void* data, |
| struct wl_registry* registry, |
| uint32_t name, |
| const char* interface, |
| uint32_t version) { |
| ApplicationWayland* app_wl = reinterpret_cast<ApplicationWayland*>(data); |
| SB_DCHECK(app_wl); |
| app_wl->OnGlobalObjectAvailable(registry, name, interface, version); |
| } |
| |
| void GlobalObjectRemove(void*, struct wl_registry*, uint32_t) {} |
| |
| static struct wl_registry_listener registry_listener = {&GlobalObjectAvailable, |
| &GlobalObjectRemove}; |
| |
| } // namespace |
| |
| // Tizen application engine using the generic queue and a tizen implementation. |
| ApplicationWayland::ApplicationWayland(float video_pixel_ratio) |
| : video_pixel_ratio_(video_pixel_ratio) { |
| SbAudioSinkPrivate::Initialize(); |
| } |
| |
| void ApplicationWayland::InjectInputEvent(SbInputData* data) { |
| Inject(new Event(kSbEventTypeInput, data, |
| &Application::DeleteDestructor<SbInputData>)); |
| } |
| |
| bool ApplicationWayland::OnGlobalObjectAvailable(struct wl_registry* registry, |
| uint32_t name, |
| const char* interface, |
| uint32_t version) { |
| if (strcmp(interface, "wl_compositor") == 0) { |
| compositor_ = static_cast<wl_compositor*>( |
| wl_registry_bind(registry, name, &wl_compositor_interface, 1)); |
| return true; |
| } |
| if (strcmp(interface, "wl_shell") == 0) { |
| shell_ = static_cast<wl_shell*>( |
| wl_registry_bind(registry, name, &wl_shell_interface, 1)); |
| return true; |
| } |
| return dev_input_.OnGlobalObjectAvailable(registry, name, interface, version); |
| } |
| |
| SbWindow ApplicationWayland::CreateWindow(const SbWindowOptions* options) { |
| SB_DLOG(INFO) << "CreateWindow"; |
| SbWindow window = |
| new SbWindowPrivate(compositor_, shell_, options, video_pixel_ratio_); |
| dev_input_.SetSbWindow(window); |
| return window; |
| } |
| |
| bool ApplicationWayland::DestroyWindow(SbWindow window) { |
| SB_DLOG(INFO) << "DestroyWindow"; |
| if (!SbWindowIsValid(window)) { |
| SB_DLOG(WARNING) << "wayland window destroy failed!!"; |
| return false; |
| } |
| dev_input_.SetSbWindow(kSbWindowInvalid); |
| delete window; |
| return true; |
| } |
| |
| void ApplicationWayland::Initialize() { |
| SB_DLOG(INFO) << "Initialize"; |
| |
| // Graphics Plane |
| display_ = wl_display_connect(NULL); |
| struct wl_registry* registry = wl_display_get_registry(display_); |
| wl_registry_add_listener(registry, ®istry_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"; |
| dev_input_.DeleteRepeatKey(); |
| |
| TerminateEgl(); |
| |
| wl_display_flush(display_); |
| wl_display_disconnect(display_); |
| |
| SbAudioSinkPrivate::TearDown(); |
| // 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::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, copied_payload, |
| [](void* data) { delete[] reinterpret_cast<char*>(data); })); |
| } |
| |
| } // namespace wayland |
| } // namespace shared |
| } // namespace starboard |