Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / ui / aura / window_tree_host_x11.cc
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "ui/aura/window_tree_host_x11.h"
6
7 #include <strings.h>
8 #include <X11/cursorfont.h>
9 #include <X11/extensions/XInput2.h>
10 #include <X11/extensions/Xrandr.h>
11 #include <X11/Xatom.h>
12 #include <X11/Xcursor/Xcursor.h>
13 #include <X11/Xlib.h>
14
15 #include <algorithm>
16 #include <limits>
17 #include <string>
18
19 #include "base/basictypes.h"
20 #include "base/command_line.h"
21 #include "base/debug/trace_event.h"
22 #include "base/stl_util.h"
23 #include "base/strings/string_number_conversions.h"
24 #include "base/strings/string_util.h"
25 #include "base/strings/stringprintf.h"
26 #include "base/sys_info.h"
27 #include "ui/aura/client/cursor_client.h"
28 #include "ui/aura/env.h"
29 #include "ui/aura/window.h"
30 #include "ui/aura/window_event_dispatcher.h"
31 #include "ui/base/cursor/cursor.h"
32 #include "ui/base/ui_base_switches.h"
33 #include "ui/base/view_prop.h"
34 #include "ui/base/x/x11_util.h"
35 #include "ui/compositor/compositor.h"
36 #include "ui/compositor/dip_util.h"
37 #include "ui/compositor/layer.h"
38 #include "ui/events/event.h"
39 #include "ui/events/event_switches.h"
40 #include "ui/events/event_utils.h"
41 #include "ui/events/keycodes/keyboard_codes.h"
42 #include "ui/events/platform/x11/x11_event_source.h"
43 #include "ui/events/x/device_data_manager.h"
44 #include "ui/events/x/device_list_cache_x.h"
45 #include "ui/events/x/touch_factory_x11.h"
46 #include "ui/gfx/screen.h"
47
48 using std::max;
49 using std::min;
50
51 namespace aura {
52
53 namespace {
54
55 const char* kAtomsToCache[] = {
56   "WM_DELETE_WINDOW",
57   "_NET_WM_PING",
58   "_NET_WM_PID",
59   NULL
60 };
61
62 ::Window FindEventTarget(const base::NativeEvent& xev) {
63   ::Window target = xev->xany.window;
64   if (xev->type == GenericEvent)
65     target = static_cast<XIDeviceEvent*>(xev->xcookie.data)->event;
66   return target;
67 }
68
69 void SelectXInput2EventsForRootWindow(XDisplay* display, ::Window root_window) {
70   CHECK(ui::IsXInput2Available());
71   unsigned char mask[XIMaskLen(XI_LASTEVENT)] = {};
72   memset(mask, 0, sizeof(mask));
73
74   XISetMask(mask, XI_HierarchyChanged);
75   XISetMask(mask, XI_KeyPress);
76   XISetMask(mask, XI_KeyRelease);
77
78   XIEventMask evmask;
79   evmask.deviceid = XIAllDevices;
80   evmask.mask_len = sizeof(mask);
81   evmask.mask = mask;
82   XISelectEvents(display, root_window, &evmask, 1);
83
84 #if defined(OS_CHROMEOS)
85   if (base::SysInfo::IsRunningOnChromeOS()) {
86     // It is necessary to listen for touch events on the root window for proper
87     // touch event calibration on Chrome OS, but this is not currently necessary
88     // on the desktop. This seems to fail in some cases (e.g. when logging
89     // in incognito). So select for non-touch events first, and then select for
90     // touch-events (but keep the other events in the mask, i.e. do not memset
91     // |mask| back to 0).
92     // TODO(sad): Figure out why this happens. http://crbug.com/153976
93     XISetMask(mask, XI_TouchBegin);
94     XISetMask(mask, XI_TouchUpdate);
95     XISetMask(mask, XI_TouchEnd);
96     XISelectEvents(display, root_window, &evmask, 1);
97   }
98 #endif
99 }
100
101 bool default_override_redirect = false;
102
103 }  // namespace
104
105 ////////////////////////////////////////////////////////////////////////////////
106 // WindowTreeHostX11
107
108 WindowTreeHostX11::WindowTreeHostX11(const gfx::Rect& bounds)
109     : xdisplay_(gfx::GetXDisplay()),
110       xwindow_(0),
111       x_root_window_(DefaultRootWindow(xdisplay_)),
112       current_cursor_(ui::kCursorNull),
113       window_mapped_(false),
114       bounds_(bounds),
115       atom_cache_(xdisplay_, kAtomsToCache) {
116   XSetWindowAttributes swa;
117   memset(&swa, 0, sizeof(swa));
118   swa.background_pixmap = None;
119   swa.override_redirect = default_override_redirect;
120   xwindow_ = XCreateWindow(
121       xdisplay_, x_root_window_,
122       bounds.x(), bounds.y(), bounds.width(), bounds.height(),
123       0,               // border width
124       CopyFromParent,  // depth
125       InputOutput,
126       CopyFromParent,  // visual
127       CWBackPixmap | CWOverrideRedirect,
128       &swa);
129   if (ui::PlatformEventSource::GetInstance())
130     ui::PlatformEventSource::GetInstance()->AddPlatformEventDispatcher(this);
131
132   long event_mask = ButtonPressMask | ButtonReleaseMask | FocusChangeMask |
133                     KeyPressMask | KeyReleaseMask |
134                     EnterWindowMask | LeaveWindowMask |
135                     ExposureMask | VisibilityChangeMask |
136                     StructureNotifyMask | PropertyChangeMask |
137                     PointerMotionMask;
138   XSelectInput(xdisplay_, xwindow_, event_mask);
139   XFlush(xdisplay_);
140
141   if (ui::IsXInput2Available()) {
142     ui::TouchFactory::GetInstance()->SetupXI2ForXWindow(xwindow_);
143     SelectXInput2EventsForRootWindow(xdisplay_, x_root_window_);
144   }
145
146   // TODO(erg): We currently only request window deletion events. We also
147   // should listen for activation events and anything else that GTK+ listens
148   // for, and do something useful.
149   ::Atom protocols[2];
150   protocols[0] = atom_cache_.GetAtom("WM_DELETE_WINDOW");
151   protocols[1] = atom_cache_.GetAtom("_NET_WM_PING");
152   XSetWMProtocols(xdisplay_, xwindow_, protocols, 2);
153
154   // We need a WM_CLIENT_MACHINE and WM_LOCALE_NAME value so we integrate with
155   // the desktop environment.
156   XSetWMProperties(xdisplay_, xwindow_, NULL, NULL, NULL, 0, NULL, NULL, NULL);
157
158   // Likewise, the X server needs to know this window's pid so it knows which
159   // program to kill if the window hangs.
160   // XChangeProperty() expects "pid" to be long.
161   COMPILE_ASSERT(sizeof(long) >= sizeof(pid_t), pid_t_bigger_than_long);
162   long pid = getpid();
163   XChangeProperty(xdisplay_,
164                   xwindow_,
165                   atom_cache_.GetAtom("_NET_WM_PID"),
166                   XA_CARDINAL,
167                   32,
168                   PropModeReplace,
169                   reinterpret_cast<unsigned char*>(&pid), 1);
170
171   // Allow subclasses to create and cache additional atoms.
172   atom_cache_.allow_uncached_atoms();
173
174   XRRSelectInput(xdisplay_, x_root_window_,
175                  RRScreenChangeNotifyMask | RROutputChangeNotifyMask);
176   CreateCompositor(GetAcceleratedWidget());
177 }
178
179 WindowTreeHostX11::~WindowTreeHostX11() {
180   if (ui::PlatformEventSource::GetInstance())
181     ui::PlatformEventSource::GetInstance()->RemovePlatformEventDispatcher(this);
182
183   DestroyCompositor();
184   DestroyDispatcher();
185   XDestroyWindow(xdisplay_, xwindow_);
186 }
187
188 bool WindowTreeHostX11::CanDispatchEvent(const ui::PlatformEvent& event) {
189   ::Window target = FindEventTarget(event);
190   return target == xwindow_ || target == x_root_window_;
191 }
192
193 uint32_t WindowTreeHostX11::DispatchEvent(const ui::PlatformEvent& event) {
194   XEvent* xev = event;
195   if (FindEventTarget(xev) == x_root_window_) {
196     if (xev->type == GenericEvent)
197       DispatchXI2Event(xev);
198     return ui::POST_DISPATCH_NONE;
199   }
200
201   switch (xev->type) {
202     case EnterNotify: {
203       aura::Window* root_window = window();
204       client::CursorClient* cursor_client =
205           client::GetCursorClient(root_window);
206       if (cursor_client) {
207         const gfx::Display display = gfx::Screen::GetScreenFor(root_window)->
208             GetDisplayNearestWindow(root_window);
209         cursor_client->SetDisplay(display);
210       }
211       ui::MouseEvent mouse_event(xev);
212       // EnterNotify creates ET_MOUSE_MOVE. Mark as synthesized as this is not
213       // real mouse move event.
214       mouse_event.set_flags(mouse_event.flags() | ui::EF_IS_SYNTHESIZED);
215       TranslateAndDispatchLocatedEvent(&mouse_event);
216       break;
217     }
218     case LeaveNotify: {
219       ui::MouseEvent mouse_event(xev);
220       TranslateAndDispatchLocatedEvent(&mouse_event);
221       break;
222     }
223     case Expose: {
224       gfx::Rect damage_rect(xev->xexpose.x, xev->xexpose.y,
225                             xev->xexpose.width, xev->xexpose.height);
226       compositor()->ScheduleRedrawRect(damage_rect);
227       break;
228     }
229     case KeyPress: {
230       ui::KeyEvent keydown_event(xev, false);
231       SendEventToProcessor(&keydown_event);
232       break;
233     }
234     case KeyRelease: {
235       ui::KeyEvent keyup_event(xev, false);
236       SendEventToProcessor(&keyup_event);
237       break;
238     }
239     case ButtonPress:
240     case ButtonRelease: {
241       switch (ui::EventTypeFromNative(xev)) {
242         case ui::ET_MOUSEWHEEL: {
243           ui::MouseWheelEvent mouseev(xev);
244           TranslateAndDispatchLocatedEvent(&mouseev);
245           break;
246         }
247         case ui::ET_MOUSE_PRESSED:
248         case ui::ET_MOUSE_RELEASED: {
249           ui::MouseEvent mouseev(xev);
250           TranslateAndDispatchLocatedEvent(&mouseev);
251           break;
252         }
253         case ui::ET_UNKNOWN:
254           // No event is created for X11-release events for mouse-wheel buttons.
255           break;
256         default:
257           NOTREACHED();
258       }
259       break;
260     }
261     case FocusOut:
262       if (xev->xfocus.mode != NotifyGrab)
263         OnHostLostWindowCapture();
264       break;
265     case ConfigureNotify: {
266       DCHECK_EQ(xwindow_, xev->xconfigure.event);
267       DCHECK_EQ(xwindow_, xev->xconfigure.window);
268       // It's possible that the X window may be resized by some other means
269       // than from within aura (e.g. the X window manager can change the
270       // size). Make sure the root window size is maintained properly.
271       gfx::Rect bounds(xev->xconfigure.x, xev->xconfigure.y,
272           xev->xconfigure.width, xev->xconfigure.height);
273       bool size_changed = bounds_.size() != bounds.size();
274       bool origin_changed = bounds_.origin() != bounds.origin();
275       bounds_ = bounds;
276       OnConfigureNotify();
277       if (size_changed)
278         OnHostResized(bounds.size());
279       if (origin_changed)
280         OnHostMoved(bounds_.origin());
281       break;
282     }
283     case GenericEvent:
284       DispatchXI2Event(xev);
285       break;
286     case ClientMessage: {
287       Atom message_type = static_cast<Atom>(xev->xclient.data.l[0]);
288       if (message_type == atom_cache_.GetAtom("WM_DELETE_WINDOW")) {
289         // We have received a close message from the window manager.
290         OnHostCloseRequested();
291       } else if (message_type == atom_cache_.GetAtom("_NET_WM_PING")) {
292         XEvent reply_event = *xev;
293         reply_event.xclient.window = x_root_window_;
294
295         XSendEvent(xdisplay_,
296                    reply_event.xclient.window,
297                    False,
298                    SubstructureRedirectMask | SubstructureNotifyMask,
299                    &reply_event);
300         XFlush(xdisplay_);
301       }
302       break;
303     }
304     case MappingNotify: {
305       switch (xev->xmapping.request) {
306         case MappingModifier:
307         case MappingKeyboard:
308           XRefreshKeyboardMapping(&xev->xmapping);
309           break;
310         case MappingPointer:
311           ui::DeviceDataManager::GetInstance()->UpdateButtonMap();
312           break;
313         default:
314           NOTIMPLEMENTED() << " Unknown request: " << xev->xmapping.request;
315           break;
316       }
317       break;
318     }
319     case MotionNotify: {
320       // Discard all but the most recent motion event that targets the same
321       // window with unchanged state.
322       XEvent last_event;
323       while (XPending(xev->xany.display)) {
324         XEvent next_event;
325         XPeekEvent(xev->xany.display, &next_event);
326         if (next_event.type == MotionNotify &&
327             next_event.xmotion.window == xev->xmotion.window &&
328             next_event.xmotion.subwindow == xev->xmotion.subwindow &&
329             next_event.xmotion.state == xev->xmotion.state) {
330           XNextEvent(xev->xany.display, &last_event);
331           xev = &last_event;
332         } else {
333           break;
334         }
335       }
336
337       ui::MouseEvent mouseev(xev);
338       TranslateAndDispatchLocatedEvent(&mouseev);
339       break;
340     }
341   }
342   return ui::POST_DISPATCH_STOP_PROPAGATION;
343 }
344
345 ui::EventSource* WindowTreeHostX11::GetEventSource() {
346   return this;
347 }
348
349 gfx::AcceleratedWidget WindowTreeHostX11::GetAcceleratedWidget() {
350   return xwindow_;
351 }
352
353 void WindowTreeHostX11::Show() {
354   if (!window_mapped_) {
355     // Before we map the window, set size hints. Otherwise, some window managers
356     // will ignore toplevel XMoveWindow commands.
357     XSizeHints size_hints;
358     size_hints.flags = PPosition | PWinGravity;
359     size_hints.x = bounds_.x();
360     size_hints.y = bounds_.y();
361     // Set StaticGravity so that the window position is not affected by the
362     // frame width when running with window manager.
363     size_hints.win_gravity = StaticGravity;
364     XSetWMNormalHints(xdisplay_, xwindow_, &size_hints);
365
366     XMapWindow(xdisplay_, xwindow_);
367
368     // We now block until our window is mapped. Some X11 APIs will crash and
369     // burn if passed |xwindow_| before the window is mapped, and XMapWindow is
370     // asynchronous.
371     if (ui::X11EventSource::GetInstance())
372       ui::X11EventSource::GetInstance()->BlockUntilWindowMapped(xwindow_);
373     window_mapped_ = true;
374   }
375 }
376
377 void WindowTreeHostX11::Hide() {
378   if (window_mapped_) {
379     XWithdrawWindow(xdisplay_, xwindow_, 0);
380     window_mapped_ = false;
381   }
382 }
383
384 gfx::Rect WindowTreeHostX11::GetBounds() const {
385   return bounds_;
386 }
387
388 void WindowTreeHostX11::SetBounds(const gfx::Rect& bounds) {
389   // Even if the host window's size doesn't change, aura's root window
390   // size, which is in DIP, changes when the scale changes.
391   float current_scale = compositor()->device_scale_factor();
392   float new_scale = gfx::Screen::GetScreenFor(window())->
393       GetDisplayNearestWindow(window()).device_scale_factor();
394   bool origin_changed = bounds_.origin() != bounds.origin();
395   bool size_changed = bounds_.size() != bounds.size();
396   XWindowChanges changes = {0};
397   unsigned value_mask = 0;
398
399   if (size_changed) {
400     changes.width = bounds.width();
401     changes.height = bounds.height();
402     value_mask = CWHeight | CWWidth;
403   }
404
405   if (origin_changed) {
406     changes.x = bounds.x();
407     changes.y = bounds.y();
408     value_mask |= CWX | CWY;
409   }
410   if (value_mask)
411     XConfigureWindow(xdisplay_, xwindow_, value_mask, &changes);
412
413   // Assume that the resize will go through as requested, which should be the
414   // case if we're running without a window manager.  If there's a window
415   // manager, it can modify or ignore the request, but (per ICCCM) we'll get a
416   // (possibly synthetic) ConfigureNotify about the actual size and correct
417   // |bounds_| later.
418   bounds_ = bounds;
419   if (origin_changed)
420     OnHostMoved(bounds.origin());
421   if (size_changed || current_scale != new_scale) {
422     OnHostResized(bounds.size());
423   } else {
424     window()->SchedulePaintInRect(window()->bounds());
425   }
426 }
427
428 gfx::Point WindowTreeHostX11::GetLocationOnNativeScreen() const {
429   return bounds_.origin();
430 }
431
432 void WindowTreeHostX11::SetCapture() {
433   // TODO(oshima): Grab x input.
434 }
435
436 void WindowTreeHostX11::ReleaseCapture() {
437   // TODO(oshima): Release x input.
438 }
439
440 void WindowTreeHostX11::PostNativeEvent(
441     const base::NativeEvent& native_event) {
442   DCHECK(xwindow_);
443   DCHECK(xdisplay_);
444   XEvent xevent = *native_event;
445   xevent.xany.display = xdisplay_;
446   xevent.xany.window = xwindow_;
447
448   switch (xevent.type) {
449     case EnterNotify:
450     case LeaveNotify:
451     case MotionNotify:
452     case KeyPress:
453     case KeyRelease:
454     case ButtonPress:
455     case ButtonRelease: {
456       // The fields used below are in the same place for all of events
457       // above. Using xmotion from XEvent's unions to avoid repeating
458       // the code.
459       xevent.xmotion.root = x_root_window_;
460       xevent.xmotion.time = CurrentTime;
461
462       gfx::Point point(xevent.xmotion.x, xevent.xmotion.y);
463       ConvertPointToNativeScreen(&point);
464       xevent.xmotion.x_root = point.x();
465       xevent.xmotion.y_root = point.y();
466     }
467     default:
468       break;
469   }
470   XSendEvent(xdisplay_, xwindow_, False, 0, &xevent);
471   XFlush(xdisplay_);
472 }
473
474 void WindowTreeHostX11::OnDeviceScaleFactorChanged(
475     float device_scale_factor) {
476 }
477
478 void WindowTreeHostX11::SetCursorNative(gfx::NativeCursor cursor) {
479   if (cursor == current_cursor_)
480     return;
481   current_cursor_ = cursor;
482   SetCursorInternal(cursor);
483 }
484
485 void WindowTreeHostX11::MoveCursorToNative(const gfx::Point& location) {
486   XWarpPointer(xdisplay_, None, x_root_window_, 0, 0, 0, 0,
487                bounds_.x() + location.x(),
488                bounds_.y() + location.y());
489 }
490
491 void WindowTreeHostX11::OnCursorVisibilityChangedNative(bool show) {
492 }
493
494 ui::EventProcessor* WindowTreeHostX11::GetEventProcessor() {
495   return dispatcher();
496 }
497
498 void WindowTreeHostX11::DispatchXI2Event(const base::NativeEvent& event) {
499   ui::TouchFactory* factory = ui::TouchFactory::GetInstance();
500   XEvent* xev = event;
501   if (!factory->ShouldProcessXI2Event(xev))
502     return;
503
504   TRACE_EVENT1("input", "WindowTreeHostX11::DispatchXI2Event",
505                "event_latency_us",
506                (ui::EventTimeForNow() - ui::EventTimeFromNative(event)).
507                  InMicroseconds());
508
509   ui::EventType type = ui::EventTypeFromNative(xev);
510   XEvent last_event;
511   int num_coalesced = 0;
512
513   switch (type) {
514     case ui::ET_TOUCH_MOVED:
515     case ui::ET_TOUCH_PRESSED:
516     case ui::ET_TOUCH_CANCELLED:
517     case ui::ET_TOUCH_RELEASED: {
518 #if defined(OS_CHROMEOS)
519       // Bail out early before generating a ui::TouchEvent if this event
520       // is not within the range of this RootWindow. Converting an xevent
521       // to ui::TouchEvent might change the state of the global touch tracking
522       // state, e.g. touch release event can remove the touch id from the
523       // record, and doing this multiple time when there are multiple
524       // RootWindow will cause problem. So only generate the ui::TouchEvent
525       // when we are sure it belongs to this RootWindow.
526       if (base::SysInfo::IsRunningOnChromeOS() &&
527           !bounds().Contains(ui::EventLocationFromNative(xev)))
528         return;
529 #endif
530       ui::TouchEvent touchev(xev);
531       TranslateAndDispatchLocatedEvent(&touchev);
532       break;
533     }
534     case ui::ET_MOUSE_MOVED:
535     case ui::ET_MOUSE_DRAGGED:
536     case ui::ET_MOUSE_PRESSED:
537     case ui::ET_MOUSE_RELEASED:
538     case ui::ET_MOUSE_ENTERED:
539     case ui::ET_MOUSE_EXITED: {
540       if (type == ui::ET_MOUSE_MOVED || type == ui::ET_MOUSE_DRAGGED) {
541         // If this is a motion event, we want to coalesce all pending motion
542         // events that are at the top of the queue.
543         num_coalesced = ui::CoalescePendingMotionEvents(xev, &last_event);
544         if (num_coalesced > 0)
545           xev = &last_event;
546       }
547       ui::MouseEvent mouseev(xev);
548       TranslateAndDispatchLocatedEvent(&mouseev);
549       break;
550     }
551     case ui::ET_MOUSEWHEEL: {
552       ui::MouseWheelEvent mouseev(xev);
553       TranslateAndDispatchLocatedEvent(&mouseev);
554       break;
555     }
556     case ui::ET_SCROLL_FLING_START:
557     case ui::ET_SCROLL_FLING_CANCEL:
558     case ui::ET_SCROLL: {
559       ui::ScrollEvent scrollev(xev);
560       SendEventToProcessor(&scrollev);
561       break;
562     }
563     case ui::ET_UMA_DATA:
564       break;
565     case ui::ET_UNKNOWN:
566       break;
567     default:
568       NOTREACHED();
569   }
570
571   // If we coalesced an event we need to free its cookie.
572   if (num_coalesced > 0)
573     XFreeEventData(xev->xgeneric.display, &last_event.xcookie);
574 }
575
576 void WindowTreeHostX11::SetCursorInternal(gfx::NativeCursor cursor) {
577   XDefineCursor(xdisplay_, xwindow_, cursor.platform());
578 }
579
580 void WindowTreeHostX11::OnConfigureNotify() {}
581
582 void WindowTreeHostX11::TranslateAndDispatchLocatedEvent(
583     ui::LocatedEvent* event) {
584   SendEventToProcessor(event);
585 }
586
587 // static
588 WindowTreeHost* WindowTreeHost::Create(const gfx::Rect& bounds) {
589   return new WindowTreeHostX11(bounds);
590 }
591
592 // static
593 gfx::Size WindowTreeHost::GetNativeScreenSize() {
594   ::XDisplay* xdisplay = gfx::GetXDisplay();
595   return gfx::Size(DisplayWidth(xdisplay, 0), DisplayHeight(xdisplay, 0));
596 }
597
598 namespace test {
599
600 void SetUseOverrideRedirectWindowByDefault(bool override_redirect) {
601   default_override_redirect = override_redirect;
602 }
603
604 }  // namespace test
605 }  // namespace aura