diff options
Diffstat (limited to 'views/widget/native_widget_wayland.cc')
-rw-r--r-- | views/widget/native_widget_wayland.cc | 677 |
1 files changed, 677 insertions, 0 deletions
diff --git a/views/widget/native_widget_wayland.cc b/views/widget/native_widget_wayland.cc new file mode 100644 index 0000000..ec462c6 --- /dev/null +++ b/views/widget/native_widget_wayland.cc @@ -0,0 +1,677 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "views/widget/native_widget_wayland.h" + +#include <cairo.h> +#include <cairo-gl.h> +#include <EGL/egl.h> +#include <GL/gl.h> +#include <wayland-egl.h> + +#include <algorithm> +#include <list> + +#include "base/memory/scoped_ptr.h" +#include "ui/base/view_prop.h" +#include "ui/gfx/canvas_skia_paint.h" +#include "ui/gfx/compositor/compositor.h" +#include "ui/gfx/gl/gl_surface.h" +#include "ui/gfx/gl/gl_surface_egl.h" +#include "ui/wayland/events/wayland_event.h" +#include "ui/wayland/wayland_display.h" +#include "ui/wayland/wayland_input_device.h" +#include "ui/wayland/wayland_screen.h" +#include "ui/wayland/wayland_window.h" +#include "views/ime/input_method_wayland.h" +#include "views/views_delegate.h" +#include "views/widget/native_widget_views.h" +#include "views/widget/root_view.h" +#include "views/widget/tooltip_manager_views.h" + +using ui::ViewProp; + +namespace views { + +namespace { + +// Links the WaylandWidget to its NativeWidget. +const char* const kNativeWidgetKey = "__VIEWS_NATIVE_WIDGET__"; + +} // namespace + +NativeWidgetWayland::NativeWidgetWayland( + internal::NativeWidgetDelegate* delegate) + : delegate_(delegate), + ALLOW_THIS_IN_INITIALIZER_LIST(close_widget_factory_(this)), + ownership_(Widget::InitParams::NATIVE_WIDGET_OWNS_WIDGET), + has_mouse_capture_(false), + wayland_display_( + ui:: WaylandDisplay::GetDisplay(gfx::GLSurfaceEGL::GetNativeDisplay())), + wayland_window_(new ui::WaylandWindow(this, wayland_display_)), + surface_data_key_(), + damage_area_() { +} + +NativeWidgetWayland::~NativeWidgetWayland() { + if (ownership_ == Widget::InitParams::NATIVE_WIDGET_OWNS_WIDGET) + delete delegate_; + + if (!View::get_use_acceleration_when_possible()) { + cairo_surface_destroy(cairo_surface_); + cairo_device_destroy(device_); + } + + if (egl_window_) + wl_egl_window_destroy(egl_window_); + + if (wayland_window_) + delete wayland_window_; +} + +void NativeWidgetWayland::InitNativeWidget(const Widget::InitParams& params) { + // Cannot create a window with a size smaller than 1x1 + allocation_.set_width(std::max(params.bounds.width(), 1)); + allocation_.set_height(std::max(params.bounds.height(), 1)); + + egl_window_ = wl_egl_window_create(wayland_window_->surface(), + allocation_.width(), + allocation_.height(), + wayland_display_->visual()); + + SetNativeWindowProperty(kNativeWidgetKey, this); + + if (View::get_use_acceleration_when_possible()) { + if (Widget::compositor_factory()) { + compositor_ = (*Widget::compositor_factory())(); + } else { + compositor_ = ui::Compositor::Create(egl_window_, allocation_.size()); + } + if (compositor_.get()) + delegate_->AsWidget()->GetRootView()->SetPaintToLayer(true); + } else { + surface_ = gfx::GLSurface::CreateViewGLSurface(false, egl_window_); + context_ = gfx::GLContext::CreateGLContext(NULL, surface_.get()); + + if (!context_->MakeCurrent(surface_.get())) + DLOG(ERROR) << "Failed to make surface current"; + + device_ = cairo_egl_device_create(gfx::GLSurfaceEGL::GetHardwareDisplay(), + context_->GetHandle()); + if (cairo_device_status(device_) != CAIRO_STATUS_SUCCESS) + DLOG(ERROR) << "Failed to create cairo egl device"; + + cairo_surface_ = cairo_gl_surface_create_for_egl(device_, + surface_->GetHandle(), + allocation_.width(), + allocation_.height()); + cairo_surface_set_user_data(cairo_surface_, + &surface_data_key_, + this, + NULL); + } + + delegate_->OnNativeWidgetCreated(); + + if (params.type != Widget::InitParams::TYPE_TOOLTIP) { + // TODO(dnicoara) Enable this once it works with Wayland + /* + views::TooltipManagerViews* manager = new views::TooltipManagerViews( + static_cast<internal::RootView*>(GetWidget()->GetRootView())); + tooltip_manager_.reset(manager); + */ + } + + // TODO(dnicoara) This should be removed when we can specify the (x, y) + // coordinates for a window. We use fullscreen since it will center the + // window rather than give it random (x, y) coordinates. + wayland_window_->set_fullscreen(true); + Show(); + OnPaint(allocation_); +} + +NonClientFrameView* NativeWidgetWayland::CreateNonClientFrameView() { + return NULL; +} + +void NativeWidgetWayland::UpdateFrameAfterFrameChange() { + NOTIMPLEMENTED(); +} + +bool NativeWidgetWayland::ShouldUseNativeFrame() const { + NOTIMPLEMENTED(); + return false; +} + +void NativeWidgetWayland::FrameTypeChanged() { + // Called when the Theme has changed, so forward the event to the root + // widget + GetWidget()->ThemeChanged(); + GetWidget()->GetRootView()->SchedulePaint(); +} + +Widget* NativeWidgetWayland::GetWidget() { + return delegate_->AsWidget(); +} + +const Widget* NativeWidgetWayland::GetWidget() const { + return delegate_->AsWidget(); +} + +gfx::NativeView NativeWidgetWayland::GetNativeView() const { + return wayland_window_; +} + +gfx::NativeWindow NativeWidgetWayland::GetNativeWindow() const { + return wayland_window_; +} + +Widget* NativeWidgetWayland::GetTopLevelWidget() { + NativeWidgetPrivate* native_widget = GetTopLevelNativeWidget(GetNativeView()); + return native_widget ? native_widget->GetWidget() : NULL; +} + +const ui::Compositor* NativeWidgetWayland::GetCompositor() const { + return compositor_.get(); +} + +ui::Compositor* NativeWidgetWayland::GetCompositor() { + return compositor_.get(); +} + +void NativeWidgetWayland::MarkLayerDirty() { +} + +void NativeWidgetWayland::CalculateOffsetToAncestorWithLayer( + gfx::Point* offset, + View** ancestor) { +} + +void NativeWidgetWayland::ViewRemoved(View* view) { + NOTIMPLEMENTED(); +} + +void NativeWidgetWayland::SetNativeWindowProperty(const char* name, + void* value) { + // Remove the existing property (if any). + for (ViewProps::iterator i = props_.begin(); i != props_.end(); ++i) { + if ((*i)->Key() == name) { + props_.erase(i); + break; + } + } + + if (value) + props_.push_back(new ViewProp(wayland_window_, name, value)); +} + +void* NativeWidgetWayland::GetNativeWindowProperty(const char* name) const { + return ViewProp::GetValue(wayland_window_, name); +} + +TooltipManager* NativeWidgetWayland::GetTooltipManager() const { + return tooltip_manager_.get(); +} + +bool NativeWidgetWayland::IsScreenReaderActive() const { + return false; +} + +void NativeWidgetWayland::SendNativeAccessibilityEvent( + View* view, + ui::AccessibilityTypes::Event event_type) { + NOTIMPLEMENTED(); +} + +void NativeWidgetWayland::SetMouseCapture() { + NOTIMPLEMENTED(); + has_mouse_capture_ = true; +} + +void NativeWidgetWayland::ReleaseMouseCapture() { + NOTIMPLEMENTED(); + has_mouse_capture_ = false; +} + +bool NativeWidgetWayland::HasMouseCapture() const { + NOTIMPLEMENTED(); + return has_mouse_capture_; +} + +InputMethod* NativeWidgetWayland::CreateInputMethod() { + return new InputMethodWayland(this); +} + +void NativeWidgetWayland::CenterWindow(const gfx::Size& size) { + NOTIMPLEMENTED(); +} + +void NativeWidgetWayland::GetWindowBoundsAndMaximizedState( + gfx::Rect* bounds, + bool* maximized) const { + NOTIMPLEMENTED(); +} + +void NativeWidgetWayland::SetWindowTitle(const std::wstring& title) { +} + +void NativeWidgetWayland::SetWindowIcons(const SkBitmap& window_icon, + const SkBitmap& app_icon) { +} + +void NativeWidgetWayland::SetAccessibleName(const std::wstring& name) { +} + +void NativeWidgetWayland::SetAccessibleRole( + ui::AccessibilityTypes::Role role) { +} + +void NativeWidgetWayland::SetAccessibleState( + ui::AccessibilityTypes::State state) { +} + +void NativeWidgetWayland::BecomeModal() { + NOTIMPLEMENTED(); +} + +gfx::Rect NativeWidgetWayland::GetWindowScreenBounds() const { + return GetClientAreaScreenBounds(); +} + +gfx::Rect NativeWidgetWayland::GetClientAreaScreenBounds() const { + return allocation_; +} + +gfx::Rect NativeWidgetWayland::GetRestoredBounds() const { + return GetWindowScreenBounds(); +} + +void NativeWidgetWayland::SetBounds(const gfx::Rect& bounds) { + saved_allocation_ = allocation_; + allocation_ = bounds; + + // TODO(dnicoara) This needs to be updated to include (x, y). + wl_egl_window_resize(egl_window_, + allocation_.width(), + allocation_.height(), + 0, 0); + if (!View::get_use_acceleration_when_possible()) { + cairo_gl_surface_set_size(cairo_surface_, + allocation_.width(), + allocation_.height()); + } + + if (compositor_.get()) + compositor_->WidgetSizeChanged(allocation_.size()); + delegate_->OnNativeWidgetSizeChanged(allocation_.size()); +} + +void NativeWidgetWayland::SetSize(const gfx::Size& size) { + gfx::Rect new_alloc = allocation_; + new_alloc.set_size(size); + + SetBounds(new_alloc); +} + +void NativeWidgetWayland::SetBoundsConstrained(const gfx::Rect& bounds, + Widget* other_widget) { + // TODO(dnicoara) Need to take into account |other_widget|. + SetBounds(bounds); +} + +void NativeWidgetWayland::MoveAbove(gfx::NativeView native_view) { + NOTIMPLEMENTED(); +} + +void NativeWidgetWayland::MoveToTop() { + NOTIMPLEMENTED(); +} + +void NativeWidgetWayland::SetShape(gfx::NativeRegion shape) { + NOTIMPLEMENTED(); +} + +void NativeWidgetWayland::Close() { + Hide(); + if (close_widget_factory_.empty()) { + MessageLoop::current()->PostTask(FROM_HERE, + close_widget_factory_.NewRunnableMethod( + &NativeWidgetWayland::CloseNow)); + } +} + +void NativeWidgetWayland::CloseNow() { + delegate_->OnNativeWidgetDestroying(); + delegate_->OnNativeWidgetDestroyed(); + if (ownership_ == Widget::InitParams::NATIVE_WIDGET_OWNS_WIDGET) + delete this; +} + +void NativeWidgetWayland::EnableClose(bool enable) { + NOTIMPLEMENTED(); +} + +void NativeWidgetWayland::Show() { + wayland_window_->SetVisible(true); + delegate_->OnNativeWidgetVisibilityChanged(true); +} + +void NativeWidgetWayland::Hide() { + wayland_window_->SetVisible(false); + delegate_->OnNativeWidgetVisibilityChanged(false); +} + +void NativeWidgetWayland::ShowMaximizedWithBounds( + const gfx::Rect& restored_bounds) { + Show(); + Maximize(); + saved_allocation_ = restored_bounds; +} + +void NativeWidgetWayland::ShowWithState(ShowState state) { + NOTIMPLEMENTED(); +} + +bool NativeWidgetWayland::IsVisible() const { + return wayland_window_->IsVisible(); +} + +void NativeWidgetWayland::Activate() { + NOTIMPLEMENTED(); +} + +void NativeWidgetWayland::Deactivate() { + NOTIMPLEMENTED(); +} + +bool NativeWidgetWayland::IsActive() const { + NOTIMPLEMENTED(); + return true; +} + +void NativeWidgetWayland::SetAlwaysOnTop(bool always_on_top) { + NOTIMPLEMENTED(); +} + +void NativeWidgetWayland::Maximize() { + std::list<ui::WaylandScreen*> screens = wayland_display_->GetScreenList(); + + if (screens.empty()) + return; + + // TODO(dnicoara) We need to intersect the current coordinates with the + // screen ones and decide the correct screen to fullscreen on. + ui::WaylandScreen* screen = screens.front(); + + SetBounds(screen->GetAllocation()); +} + +void NativeWidgetWayland::Minimize() { + NOTIMPLEMENTED(); +} + +bool NativeWidgetWayland::IsMaximized() const { + NOTIMPLEMENTED(); + return true; +} + +bool NativeWidgetWayland::IsMinimized() const { + NOTIMPLEMENTED(); + return false; +} + +void NativeWidgetWayland::Restore() { + NOTIMPLEMENTED(); +} + +void NativeWidgetWayland::SetFullscreen(bool fullscreen) { + gfx::Rect new_allocation = allocation_; + + if (fullscreen) { + std::list<ui::WaylandScreen*> screens = wayland_display_->GetScreenList(); + + if (screens.empty()) + return; + + // TODO(dnicoara) What does it mean to be fullscreen when having multiple + // monitors? If we're going fullscreen only on one screen then we need to + // intersect the current coordinates with the screen ones and decide the + // correct screen to fullscreen on. + ui::WaylandScreen* screen = screens.front(); + new_allocation = screen->GetAllocation(); + } else { + new_allocation = saved_allocation_; + } + + wayland_window_->set_fullscreen(fullscreen); + SetBounds(new_allocation); +} + +bool NativeWidgetWayland::IsFullscreen() const { + return wayland_window_->fullscreen(); +} + +void NativeWidgetWayland::SetOpacity(unsigned char opacity) { + NOTIMPLEMENTED(); +} + +void NativeWidgetWayland::SetUseDragFrame(bool use_drag_frame) { + NOTIMPLEMENTED(); +} + +bool NativeWidgetWayland::IsAccessibleWidget() const { + NOTIMPLEMENTED(); + return true; +} + +void NativeWidgetWayland::RunShellDrag(View* view, + const ui::OSExchangeData& data, + int operation) { + NOTIMPLEMENTED(); +} + +gboolean NativeWidgetWayland::IdleRedraw(void* ptr) { + NativeWidgetWayland* widget = static_cast<NativeWidgetWayland*>(ptr); + gfx::Rect damage_area = widget->damage_area_; + widget->damage_area_ = gfx::Rect(); + + widget->OnPaint(damage_area); + + return FALSE; +} + +void NativeWidgetWayland::SchedulePaintInRect(const gfx::Rect& rect) { + if (damage_area_.IsEmpty()) + g_idle_add(NativeWidgetWayland::IdleRedraw, this); + + damage_area_ = damage_area_.Union(rect); +} + +void NativeWidgetWayland::SetCursor(gfx::NativeCursor cursor) { + NOTIMPLEMENTED(); +} + + +void NativeWidgetWayland::ClearNativeFocus() { + NOTIMPLEMENTED(); +} + + +void NativeWidgetWayland::FocusNativeView(gfx::NativeView native_view) { + NOTIMPLEMENTED(); +} + +bool NativeWidgetWayland::ConvertPointFromAncestor( + const Widget* ancestor, gfx::Point* point) const { + NOTREACHED(); + return false; +} + +// Overridden from NativeWidget +gfx::AcceleratedWidget NativeWidgetWayland::GetAcceleratedWidget() { + return egl_window_; +} + + +// Overridden from internal::InputMethodDelegate +void NativeWidgetWayland::DispatchKeyEventPostIME(const KeyEvent& key) { + NOTIMPLEMENTED(); + delegate_->OnKeyEvent(key); +} + +///////////////////////////////////////////////////////////////////////////// +// NativeWidgetWayland, private, event handlers + +void NativeWidgetWayland::OnPaint(gfx::Rect damage_area) { + if (!delegate_->OnNativeWidgetPaintAccelerated(damage_area)) { + // This is required since the CanvasSkiaPaint damages the surface + // in the destructor so we need to have this done before calling + // swapbuffers. + { + cairo_rectangle_int_t region = damage_area.ToCairoRectangle(); + gfx::CanvasSkiaPaint canvas(cairo_surface_, ®ion); + if (!canvas.is_empty()) { + canvas.set_composite_alpha(false); + delegate_->OnNativeWidgetPaint(&canvas); + } + } + + // Have cairo swap buffers, then let Wayland know of the damaged area. + cairo_gl_surface_swapbuffers(cairo_surface_); + wl_surface_damage(wayland_window_->surface(), + damage_area.x(), damage_area.y(), + damage_area.width(), damage_area.height()); + } +} + +void NativeWidgetWayland::OnMotionNotify(ui::WaylandEvent event) { + MouseEvent mouse_event(&event); + delegate_->OnMouseEvent(mouse_event); +} + +void NativeWidgetWayland::OnButtonNotify(ui::WaylandEvent event) { + if (event.button.button == ui::SCROLL_UP || + event.button.button == ui::SCROLL_DOWN) { + MouseWheelEvent mouse_event(&event); + delegate_->OnMouseEvent(mouse_event); + } else { + MouseEvent mouse_event(&event); + delegate_->OnMouseEvent(mouse_event); + } +} + +void NativeWidgetWayland::OnKeyNotify(ui::WaylandEvent event) { + KeyEvent key_event(&event); + InputMethod* input_method = GetWidget()->GetInputMethodDirect(); + if (input_method) + input_method->DispatchKeyEvent(key_event); + else + DispatchKeyEventPostIME(key_event); +} + +void NativeWidgetWayland::OnPointerFocus(ui::WaylandEvent event) { + MouseEvent mouse_event(&event); + delegate_->OnMouseEvent(mouse_event); +} + +void NativeWidgetWayland::OnKeyboardFocus(ui::WaylandEvent event) { + InputMethod* input_method = GetWidget()->GetInputMethodDirect(); + if (input_method) { + if (event.keyboard_focus.state) + input_method->OnFocus(); + else + input_method->OnBlur(); + } +} + +void NativeWidgetWayland::OnGeometryChange(ui::WaylandEvent event) { + SetSize(gfx::Size(event.geometry_change.width, + event.geometry_change.height)); +} + +///////////////////////////////////////////////////////////////////////////// +// Widget + +// static +bool Widget::ConvertRect(const Widget* source, + const Widget* target, + gfx::Rect* rect) { + DCHECK(source); + DCHECK(target); + DCHECK(rect); + + gfx::NativeView source_widget = source->GetNativeView(); + gfx::NativeView target_widget = target->GetNativeView(); + if (source_widget == target_widget) + return true; + + if (!source_widget || !target_widget) + return false; + + NOTIMPLEMENTED(); + return false; +} + +namespace internal { + +///////////////////////////////////////////////////////////////////////////// +// NativeWidget + +// static +NativeWidgetPrivate* NativeWidgetPrivate::CreateNativeWidget( + internal::NativeWidgetDelegate* delegate) { + if (Widget::IsPureViews() && + ViewsDelegate::views_delegate->GetDefaultParentView()) { + return new NativeWidgetViews(delegate); + } + return new NativeWidgetWayland(delegate); +} + +// static +NativeWidgetPrivate* NativeWidgetPrivate::GetNativeWidgetForNativeView( + gfx::NativeView native_view) { + return reinterpret_cast<NativeWidgetWayland*>( + ViewProp::GetValue(native_view, kNativeWidgetKey)); +} + +// static +NativeWidgetPrivate* NativeWidgetPrivate::GetNativeWidgetForNativeWindow( + gfx::NativeWindow native_window) { + return GetNativeWidgetForNativeView(native_window); +} + +// static +NativeWidgetPrivate* NativeWidgetPrivate::GetTopLevelNativeWidget( + gfx::NativeView native_view) { + // TODO(dnicoara) What would be the best way to implement this? + // Since there isn't any actual parenting concept in Wayland, we could + // implement it using WaylandWindow->SetParent/GetParent calls. + return GetNativeWidgetForNativeView(native_view); +} + +// static +void NativeWidgetPrivate::GetAllChildWidgets(gfx::NativeView native_view, + Widget::Widgets* children) { + NOTIMPLEMENTED(); + if (!native_view) + return; +} + +// static +void NativeWidgetPrivate::ReparentNativeView(gfx::NativeView native_view, + gfx::NativeView new_parent) { + NOTIMPLEMENTED(); + if (!native_view) + return; +} + +// static +bool NativeWidgetPrivate::IsMouseButtonDown() { + NOTIMPLEMENTED(); + return false; +} + +} // namespace internal + +} // namespace views |