| Index: ui/platform_window/x11/x11_window.cc
|
| diff --git a/ui/platform_window/x11/x11_window.cc b/ui/platform_window/x11/x11_window.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..bfc9ee630aee22a05e52b1c379b6cdbeb7d4807c
|
| --- /dev/null
|
| +++ b/ui/platform_window/x11/x11_window.cc
|
| @@ -0,0 +1,308 @@
|
| +// Copyright 2014 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 <X11/extensions/XInput2.h>
|
| +#include <X11/Xatom.h>
|
| +#include <X11/Xlib.h>
|
| +#include <X11/Xutil.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/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.h"
|
| +#include "ui/platform_window/platform_window_delegate.h"
|
| +
|
| +namespace ui {
|
| +
|
| +namespace {
|
| +
|
| +const char* kAtomsToCache[] = {
|
| + "WM_DELETE_WINDOW",
|
| + "_NET_WM_PING",
|
| + "_NET_WM_PID",
|
| + NULL
|
| +};
|
| +
|
| +XID FindXEventTarget(XEvent* xevent) {
|
| + XID target = xevent->xany.window;
|
| + if (xevent->type == GenericEvent)
|
| + target = static_cast<XIDeviceEvent*>(xevent->xcookie.data)->event;
|
| + return target;
|
| +}
|
| +
|
| +class X11Window : public PlatformWindow,
|
| + public PlatformEventDispatcher {
|
| + public:
|
| + explicit X11Window(PlatformWindowDelegate* delegate)
|
| + : delegate_(delegate),
|
| + xdisplay_(gfx::GetXDisplay()),
|
| + xwindow_(None),
|
| + xroot_window_(DefaultRootWindow(xdisplay_)),
|
| + atom_cache_(xdisplay_, kAtomsToCache),
|
| + window_mapped_(false) {
|
| + CHECK(delegate_);
|
| + }
|
| +
|
| + virtual ~X11Window() {
|
| + Destroy();
|
| + }
|
| +
|
| + private:
|
| + void Destroy() {
|
| + if (xwindow_ == None)
|
| + return;
|
| +
|
| + // Stop processing events.
|
| + PlatformEventSource::GetInstance()->RemovePlatformEventDispatcher(this);
|
| + XDestroyWindow(xdisplay_, xwindow_);
|
| + xwindow_ = None;
|
| + }
|
| +
|
| + // PlatformWindow:
|
| + virtual void Show() OVERRIDE {
|
| + if (window_mapped_)
|
| + return;
|
| +
|
| + CHECK(PlatformEventSource::GetInstance());
|
| + PlatformEventSource::GetInstance()->AddPlatformEventDispatcher(this);
|
| +
|
| + XSetWindowAttributes swa;
|
| + memset(&swa, 0, sizeof(swa));
|
| + swa.background_pixmap = None;
|
| + swa.override_redirect = False;
|
| + 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 | CWOverrideRedirect,
|
| + &swa);
|
| +
|
| + long event_mask = ButtonPressMask | ButtonReleaseMask | FocusChangeMask |
|
| + KeyPressMask | KeyReleaseMask | EnterWindowMask |
|
| + LeaveWindowMask | ExposureMask | VisibilityChangeMask |
|
| + StructureNotifyMask | PropertyChangeMask |
|
| + PointerMotionMask;
|
| + XSelectInput(xdisplay_, xwindow_, event_mask);
|
| +
|
| +#if 0
|
| + unsigned char mask[XIMaskLen(XI_LASTEVENT)];
|
| + memset(mask, 0, sizeof(mask));
|
| +
|
| +#if defined(USE_XI2_MT)
|
| + XISetMask(mask, XI_TouchBegin);
|
| + XISetMask(mask, XI_TouchUpdate);
|
| + XISetMask(mask, XI_TouchEnd);
|
| +#endif
|
| + XISetMask(mask, XI_ButtonPress);
|
| + XISetMask(mask, XI_ButtonRelease);
|
| + XISetMask(mask, XI_Motion);
|
| +
|
| + XIEventMask evmask;
|
| + evmask.deviceid = XIAllDevices;
|
| + evmask.mask_len = sizeof(mask);
|
| + evmask.mask = mask;
|
| + XISelectEvents(xdisplay_, xwindow_, &evmask, 1);
|
| +#endif
|
| + 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.
|
| + COMPILE_ASSERT(sizeof(long) >= sizeof(pid_t), pid_t_bigger_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);
|
| +
|
| + 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;
|
| + }
|
| +
|
| + virtual void Hide() OVERRIDE {
|
| + if (!window_mapped_)
|
| + return;
|
| + XWithdrawWindow(xdisplay_, xwindow_, 0);
|
| + window_mapped_ = false;
|
| + }
|
| +
|
| + virtual void Close() OVERRIDE {
|
| + Destroy();
|
| + }
|
| +
|
| + virtual void SetBounds(const gfx::Rect& bounds) OVERRIDE {
|
| + requested_bounds_ = bounds;
|
| + if (!window_mapped_)
|
| + 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);
|
| + }
|
| +
|
| + virtual void SetCapture() OVERRIDE {}
|
| +
|
| + virtual void ReleaseCapture() OVERRIDE {}
|
| +
|
| + virtual void ToggleFullscreen() OVERRIDE {}
|
| +
|
| + virtual void Maximize() OVERRIDE {}
|
| +
|
| + virtual void Minimize() OVERRIDE {}
|
| +
|
| + virtual void Restore() OVERRIDE {}
|
| +
|
| + // PlatformEventDispatcher:
|
| + virtual bool CanDispatchEvent(const PlatformEvent& event) OVERRIDE {
|
| + return FindXEventTarget(event) == xwindow_;
|
| + }
|
| +
|
| + virtual uint32_t DispatchEvent(const PlatformEvent& event) OVERRIDE {
|
| + XEvent* xev = event;
|
| + switch (xev->type) {
|
| + case EnterNotify: {
|
| + // EnterNotify creates ET_MOUSE_MOVED. Mark as synthesized as this is
|
| + // not real mouse move event.
|
| + MouseEvent mouse_event(xev);
|
| + CHECK_EQ(ET_MOUSE_MOVED, mouse_event.type());
|
| + mouse_event.set_flags(mouse_event.flags() | EF_IS_SYNTHESIZED);
|
| + delegate_->DispatchEvent(&mouse_event);
|
| + break;
|
| + }
|
| + case LeaveNotify: {
|
| + MouseEvent mouse_event(xev);
|
| + delegate_->DispatchEvent(&mouse_event);
|
| + break;
|
| + }
|
| +
|
| + case Expose: {
|
| + gfx::Rect damage_rect(xev->xexpose.x,
|
| + xev->xexpose.y,
|
| + xev->xexpose.width,
|
| + xev->xexpose.height);
|
| + delegate_->OnDamageRect(damage_rect);
|
| + break;
|
| + }
|
| +
|
| + case KeyPress:
|
| + case KeyRelease: {
|
| + ui::KeyEvent key_event(xev, false);
|
| + delegate_->DispatchEvent(&key_event);
|
| + break;
|
| + }
|
| +
|
| + case ButtonPress:
|
| + case ButtonRelease: {
|
| + switch (ui::EventTypeFromNative(xev)) {
|
| + case ui::ET_MOUSEWHEEL: {
|
| + ui::MouseWheelEvent mouseev(xev);
|
| + delegate_->DispatchEvent(&mouseev);
|
| + break;
|
| + }
|
| + case ui::ET_MOUSE_PRESSED:
|
| + case ui::ET_MOUSE_RELEASED: {
|
| + ui::MouseEvent mouseev(xev);
|
| + delegate_->DispatchEvent(&mouseev);
|
| + break;
|
| + }
|
| + case ui::ET_UNKNOWN:
|
| + // No event is created for X11-release events for mouse-wheel
|
| + // buttons.
|
| + break;
|
| + default:
|
| + NOTREACHED();
|
| + }
|
| + break;
|
| + }
|
| +
|
| + case MotionNotify: {
|
| + ui::MouseEvent mouseev(xev);
|
| + delegate_->DispatchEvent(&mouseev);
|
| + 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);
|
| + delegate_->OnBoundsChanged(bounds);
|
| + break;
|
| + }
|
| + }
|
| + return POST_DISPATCH_STOP_PROPAGATION;
|
| + }
|
| +
|
| + PlatformWindowDelegate* delegate_;
|
| +
|
| + XDisplay* xdisplay_;
|
| + XID xwindow_;
|
| + XID xroot_window_;
|
| + X11AtomCache atom_cache_;
|
| +
|
| + gfx::Rect requested_bounds_;
|
| + bool window_mapped_;
|
| +
|
| + DISALLOW_COPY_AND_ASSIGN(X11Window);
|
| +};
|
| +
|
| +} // namespace
|
| +
|
| +scoped_ptr<PlatformWindow>
|
| +CreatePlatformWindow(PlatformWindowDelegate* delegate) {
|
| + return scoped_ptr<PlatformWindow>(new X11Window(delegate));
|
| +}
|
| +
|
| +} // namespace ui
|
|
|