Chromium Code Reviews| Index: ui/platform_window/x11/x11_window_base.cc |
| diff --git a/ui/platform_window/x11/x11_window_base.cc b/ui/platform_window/x11/x11_window_base.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..96094d497006ea4dce390da685f3da011f341cd7 |
| --- /dev/null |
| +++ b/ui/platform_window/x11/x11_window_base.cc |
| @@ -0,0 +1,278 @@ |
| +// Copyright 2016 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 "ui/platform_window/x11/x11_window_base.h" |
| + |
| +#include <X11/Xatom.h> |
| +#include <X11/Xlib.h> |
| +#include <X11/Xutil.h> |
| +#include <X11/extensions/XInput2.h> |
| + |
| +#include <string> |
| + |
| +#include "base/strings/utf_string_conversions.h" |
| +#include "ui/events/devices/x11/touch_factory_x11.h" |
| +#include "ui/events/event.h" |
| +#include "ui/events/event_utils.h" |
| +#include "ui/events/platform/platform_event_dispatcher.h" |
| +#include "ui/events/platform/platform_event_source.h" |
| +#include "ui/events/platform/x11/x11_event_source.h" |
| +#include "ui/events/x/events_x_utils.h" |
| +#include "ui/gfx/geometry/rect.h" |
| +#include "ui/gfx/x/x11_atom_cache.h" |
| +#include "ui/gfx/x/x11_types.h" |
| +#include "ui/platform_window/platform_window_delegate.h" |
| + |
| +namespace ui { |
| + |
| +namespace { |
| + |
| +const char* kAtomsToCache[] = {"UTF8_STRING", "WM_DELETE_WINDOW", |
| + "_NET_WM_NAME", "_NET_WM_PID", |
| + "_NET_WM_PING", NULL}; |
| + |
| +bool g_override_redirect = false; |
| + |
| +} // namespace |
| + |
| +X11WindowBase::X11WindowBase(PlatformWindowDelegate* delegate) |
| + : delegate_(delegate), |
| + xdisplay_(gfx::GetXDisplay()), |
| + xwindow_(None), |
| + xroot_window_(DefaultRootWindow(xdisplay_)), |
| + atom_cache_(xdisplay_, kAtomsToCache) { |
| + DCHECK(delegate_); |
| + TouchFactory::SetTouchDeviceListFromCommandLine(); |
|
sadrul
2016/02/09 19:34:40
Note for a separate CL: this should move into X11S
|
| +} |
| + |
| +X11WindowBase::~X11WindowBase() { |
| + Destroy(); |
| +} |
| + |
| +void X11WindowBase::Destroy() { |
| + if (xwindow_ == None) |
| + return; |
| + |
| + // Stop processing events. |
| + XID xwindow = xwindow_; |
| + XDisplay* xdisplay = xdisplay_; |
| + xwindow_ = None; |
| + delegate_->OnClosed(); |
| + // |this| might be deleted because of the above call. |
| + |
| + XDestroyWindow(xdisplay, xwindow); |
| +} |
| + |
| +void X11WindowBase::Create() { |
| + XSetWindowAttributes swa; |
| + memset(&swa, 0, sizeof(swa)); |
| + swa.background_pixmap = None; |
| + swa.bit_gravity = NorthWestGravity; |
| + swa.override_redirect = g_override_redirect; |
| + xwindow_ = XCreateWindow( |
| + xdisplay_, xroot_window_, requested_bounds_.x(), requested_bounds_.y(), |
| + requested_bounds_.width(), requested_bounds_.height(), |
| + 0, // border width |
| + CopyFromParent, // depth |
| + InputOutput, |
| + CopyFromParent, // visual |
| + CWBackPixmap | CWBitGravity | CWOverrideRedirect, &swa); |
| + |
| + long event_mask = ButtonPressMask | ButtonReleaseMask | FocusChangeMask | |
| + KeyPressMask | KeyReleaseMask | EnterWindowMask | |
| + LeaveWindowMask | ExposureMask | VisibilityChangeMask | |
| + StructureNotifyMask | PropertyChangeMask | |
| + PointerMotionMask; |
| + XSelectInput(xdisplay_, xwindow_, event_mask); |
| + |
| + unsigned char mask[XIMaskLen(XI_LASTEVENT)]; |
| + memset(mask, 0, sizeof(mask)); |
| + |
| + XISetMask(mask, XI_TouchBegin); |
| + XISetMask(mask, XI_TouchUpdate); |
| + XISetMask(mask, XI_TouchEnd); |
| + XISetMask(mask, XI_ButtonPress); |
| + XISetMask(mask, XI_ButtonRelease); |
| + XISetMask(mask, XI_Motion); |
| + XISetMask(mask, XI_KeyPress); |
| + XISetMask(mask, XI_KeyRelease); |
| + |
| + XIEventMask evmask; |
| + evmask.deviceid = XIAllDevices; |
| + evmask.mask_len = sizeof(mask); |
| + evmask.mask = mask; |
| + XISelectEvents(xdisplay_, xwindow_, &evmask, 1); |
| + XFlush(xdisplay_); |
| + |
| + ::Atom protocols[2]; |
| + protocols[0] = atom_cache_.GetAtom("WM_DELETE_WINDOW"); |
| + protocols[1] = atom_cache_.GetAtom("_NET_WM_PING"); |
| + XSetWMProtocols(xdisplay_, xwindow_, protocols, 2); |
| + |
| + // We need a WM_CLIENT_MACHINE and WM_LOCALE_NAME value so we integrate with |
| + // the desktop environment. |
| + XSetWMProperties(xdisplay_, xwindow_, NULL, NULL, NULL, 0, NULL, NULL, NULL); |
| + |
| + // Likewise, the X server needs to know this window's pid so it knows which |
| + // program to kill if the window hangs. |
| + // XChangeProperty() expects "pid" to be long. |
| + static_assert(sizeof(long) >= sizeof(pid_t), |
| + "pid_t should not be larger than long"); |
| + long pid = getpid(); |
| + XChangeProperty(xdisplay_, xwindow_, atom_cache_.GetAtom("_NET_WM_PID"), |
| + XA_CARDINAL, 32, PropModeReplace, |
| + reinterpret_cast<unsigned char*>(&pid), 1); |
| + // Before we map the window, set size hints. Otherwise, some window managers |
| + // will ignore toplevel XMoveWindow commands. |
| + XSizeHints size_hints; |
| + size_hints.flags = PPosition | PWinGravity; |
| + size_hints.x = requested_bounds_.x(); |
| + size_hints.y = requested_bounds_.y(); |
| + // Set StaticGravity so that the window position is not affected by the |
| + // frame width when running with window manager. |
| + size_hints.win_gravity = StaticGravity; |
| + XSetWMNormalHints(xdisplay_, xwindow_, &size_hints); |
| + |
| + // TODO(sky): provide real scale factor. |
| + delegate_->OnAcceleratedWidgetAvailable(xwindow_, 1.f); |
| +} |
| + |
| +void X11WindowBase::Show() { |
| + if (window_mapped_) |
| + return; |
| + if (xwindow_ == None) |
| + Create(); |
| + |
| + XMapWindow(xdisplay_, xwindow_); |
| + |
| + // We now block until our window is mapped. Some X11 APIs will crash and |
| + // burn if passed |xwindow_| before the window is mapped, and XMapWindow is |
| + // asynchronous. |
| + if (X11EventSource::GetInstance()) |
| + X11EventSource::GetInstance()->BlockUntilWindowMapped(xwindow_); |
| + window_mapped_ = true; |
| +} |
| + |
| +void X11WindowBase::Hide() { |
| + if (!window_mapped_) |
| + return; |
| + XWithdrawWindow(xdisplay_, xwindow_, 0); |
| + window_mapped_ = false; |
| +} |
| + |
| +void X11WindowBase::Close() { |
| + Destroy(); |
| +} |
| + |
| +void X11WindowBase::SetBounds(const gfx::Rect& bounds) { |
| + requested_bounds_ = bounds; |
| + if (!window_mapped_ || bounds == confirmed_bounds_) |
| + return; |
| + XWindowChanges changes = {0}; |
| + unsigned value_mask = CWX | CWY | CWWidth | CWHeight; |
| + changes.x = bounds.x(); |
| + changes.y = bounds.y(); |
| + changes.width = bounds.width(); |
| + changes.height = bounds.height(); |
| + XConfigureWindow(xdisplay_, xwindow_, value_mask, &changes); |
| +} |
| + |
| +gfx::Rect X11WindowBase::GetBounds() { |
| + return confirmed_bounds_; |
| +} |
| + |
| +void X11WindowBase::SetTitle(const base::string16& title) { |
| + if (window_title_ == title) |
| + return; |
| + window_title_ = title; |
| + std::string utf8str = base::UTF16ToUTF8(title); |
| + XChangeProperty(xdisplay_, xwindow_, atom_cache_.GetAtom("_NET_WM_NAME"), |
| + atom_cache_.GetAtom("UTF8_STRING"), 8, PropModeReplace, |
| + reinterpret_cast<const unsigned char*>(utf8str.c_str()), |
| + utf8str.size()); |
| + XTextProperty xtp; |
| + char* c_utf8_str = const_cast<char*>(utf8str.c_str()); |
| + if (Xutf8TextListToTextProperty(xdisplay_, &c_utf8_str, 1, XUTF8StringStyle, |
| + &xtp) == Success) { |
| + XSetWMName(xdisplay_, xwindow_, &xtp); |
| + XFree(xtp.value); |
| + } |
| +} |
| + |
| +void X11WindowBase::SetCapture() {} |
| + |
| +void X11WindowBase::ReleaseCapture() {} |
| + |
| +void X11WindowBase::ToggleFullscreen() {} |
| + |
| +void X11WindowBase::Maximize() {} |
| + |
| +void X11WindowBase::Minimize() {} |
| + |
| +void X11WindowBase::Restore() {} |
| + |
| +void X11WindowBase::MoveCursorTo(const gfx::Point& location) {} |
| + |
| +void X11WindowBase::ConfineCursorToBounds(const gfx::Rect& bounds) {} |
| + |
| +PlatformImeController* X11WindowBase::GetPlatformImeController() { |
| + return nullptr; |
| +} |
| + |
| +bool X11WindowBase::HasXWindow() { |
| + return xwindow_ != None; |
| +} |
| + |
| +void X11WindowBase::ProcessXWindowEvent(XEvent* xev) { |
| + switch (xev->type) { |
| + case Expose: { |
| + gfx::Rect damage_rect(xev->xexpose.x, xev->xexpose.y, xev->xexpose.width, |
| + xev->xexpose.height); |
| + delegate_->OnDamageRect(damage_rect); |
| + break; |
| + } |
| + |
| + case FocusOut: |
| + if (xev->xfocus.mode != NotifyGrab) |
| + delegate_->OnLostCapture(); |
| + break; |
| + |
| + case ConfigureNotify: { |
| + DCHECK_EQ(xwindow_, xev->xconfigure.event); |
| + DCHECK_EQ(xwindow_, xev->xconfigure.window); |
| + gfx::Rect bounds(xev->xconfigure.x, xev->xconfigure.y, |
| + xev->xconfigure.width, xev->xconfigure.height); |
| + if (confirmed_bounds_ != bounds) { |
| + confirmed_bounds_ = bounds; |
| + delegate_->OnBoundsChanged(confirmed_bounds_); |
| + } |
| + break; |
| + } |
| + |
| + case ClientMessage: { |
| + Atom message = static_cast<Atom>(xev->xclient.data.l[0]); |
| + if (message == atom_cache_.GetAtom("WM_DELETE_WINDOW")) { |
| + delegate_->OnCloseRequest(); |
| + } else if (message == atom_cache_.GetAtom("_NET_WM_PING")) { |
| + XEvent reply_event = *xev; |
| + reply_event.xclient.window = xroot_window_; |
| + |
| + XSendEvent(xdisplay_, reply_event.xclient.window, False, |
| + SubstructureRedirectMask | SubstructureNotifyMask, |
| + &reply_event); |
| + XFlush(xdisplay_); |
| + } |
| + break; |
| + } |
| + } |
| +} |
|
sadrul
2016/02/09 19:34:39
I haven't looked very closely in this block. I ass
kylechar
2016/02/09 21:31:54
Should be identical. There is no fall through betw
|
| + |
| +namespace test { |
| + |
| +void SetUseOverrideRedirectWindowByDefault(bool override_redirect) { |
| + g_override_redirect = override_redirect; |
| +} |
| + |
| +} // namespace test |
| +} // namespace ui |