| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2011 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/desktop_host.h" | |
| 6 | |
| 7 #include <X11/cursorfont.h> | |
| 8 #include <X11/Xlib.h> | |
| 9 | |
| 10 // Get rid of a macro from Xlib.h that conflicts with Aura's RootWindow class. | |
| 11 #undef RootWindow | |
| 12 | |
| 13 #include <algorithm> | |
| 14 | |
| 15 #include "base/message_loop.h" | |
| 16 #include "base/message_pump_x.h" | |
| 17 #include "ui/aura/cursor.h" | |
| 18 #include "ui/aura/desktop.h" | |
| 19 #include "ui/aura/event.h" | |
| 20 #include "ui/base/keycodes/keyboard_codes.h" | |
| 21 #include "ui/base/touch/touch_factory.h" | |
| 22 #include "ui/base/x/x11_util.h" | |
| 23 #include "ui/gfx/compositor/layer.h" | |
| 24 | |
| 25 #include <X11/cursorfont.h> | |
| 26 #include <X11/extensions/XInput2.h> | |
| 27 #include <X11/Xlib.h> | |
| 28 | |
| 29 using std::max; | |
| 30 using std::min; | |
| 31 | |
| 32 namespace aura { | |
| 33 | |
| 34 namespace { | |
| 35 | |
| 36 // The events reported for slave devices can have incorrect information for some | |
| 37 // fields. This utility function is used to check for such inconsistencies. | |
| 38 void CheckXEventForConsistency(XEvent* xevent) { | |
| 39 static bool expect_master_event = false; | |
| 40 static XIDeviceEvent slave_event; | |
| 41 static gfx::Point slave_location; | |
| 42 | |
| 43 // Note: If an event comes from a slave pointer device, then it will be | |
| 44 // followed by the same event, but reported from its master pointer device. | |
| 45 // However, if the event comes from a floating slave device (e.g. a | |
| 46 // touchscreen), then it will not be followed by a duplicate event, since the | |
| 47 // floating slave isn't attached to a master. | |
| 48 | |
| 49 bool was_expecting_master_event = expect_master_event; | |
| 50 expect_master_event = false; | |
| 51 | |
| 52 if (!xevent || xevent->type != GenericEvent) | |
| 53 return; | |
| 54 | |
| 55 XIDeviceEvent* xievent = static_cast<XIDeviceEvent*>(xevent->xcookie.data); | |
| 56 if (xievent->evtype != XI_Motion && | |
| 57 xievent->evtype != XI_ButtonPress && | |
| 58 xievent->evtype != XI_ButtonRelease) { | |
| 59 return; | |
| 60 } | |
| 61 | |
| 62 if (xievent->sourceid == xievent->deviceid) { | |
| 63 slave_event = *xievent; | |
| 64 slave_location = ui::EventLocationFromNative(xevent); | |
| 65 expect_master_event = true; | |
| 66 } else if (was_expecting_master_event) { | |
| 67 CHECK_EQ(slave_location.x(), ui::EventLocationFromNative(xevent).x()); | |
| 68 CHECK_EQ(slave_location.y(), ui::EventLocationFromNative(xevent).y()); | |
| 69 | |
| 70 CHECK_EQ(slave_event.type, xievent->type); | |
| 71 CHECK_EQ(slave_event.evtype, xievent->evtype); | |
| 72 CHECK_EQ(slave_event.detail, xievent->detail); | |
| 73 CHECK_EQ(slave_event.flags, xievent->flags); | |
| 74 CHECK_EQ(slave_event.buttons.mask_len, xievent->buttons.mask_len); | |
| 75 CHECK_EQ(slave_event.valuators.mask_len, xievent->valuators.mask_len); | |
| 76 CHECK_EQ(slave_event.mods.base, xievent->mods.base); | |
| 77 CHECK_EQ(slave_event.mods.latched, xievent->mods.latched); | |
| 78 CHECK_EQ(slave_event.mods.locked, xievent->mods.locked); | |
| 79 CHECK_EQ(slave_event.mods.effective, xievent->mods.effective); | |
| 80 } | |
| 81 } | |
| 82 | |
| 83 // Returns X font cursor shape from an Aura cursor. | |
| 84 int CursorShapeFromNative(gfx::NativeCursor native_cursor) { | |
| 85 switch (native_cursor) { | |
| 86 case aura::kCursorNull: | |
| 87 return XC_left_ptr; | |
| 88 case aura::kCursorPointer: | |
| 89 return XC_left_ptr; | |
| 90 case aura::kCursorCross: | |
| 91 return XC_crosshair; | |
| 92 case aura::kCursorHand: | |
| 93 return XC_hand2; | |
| 94 case aura::kCursorIBeam: | |
| 95 return XC_xterm; | |
| 96 case aura::kCursorWait: | |
| 97 return XC_watch; | |
| 98 case aura::kCursorHelp: | |
| 99 return XC_question_arrow; | |
| 100 case aura::kCursorEastResize: | |
| 101 return XC_right_side; | |
| 102 case aura::kCursorNorthResize: | |
| 103 return XC_top_side; | |
| 104 case aura::kCursorNorthEastResize: | |
| 105 return XC_top_right_corner; | |
| 106 case aura::kCursorNorthWestResize: | |
| 107 return XC_top_left_corner; | |
| 108 case aura::kCursorSouthResize: | |
| 109 return XC_bottom_side; | |
| 110 case aura::kCursorSouthEastResize: | |
| 111 return XC_bottom_right_corner; | |
| 112 case aura::kCursorSouthWestResize: | |
| 113 return XC_bottom_left_corner; | |
| 114 case aura::kCursorWestResize: | |
| 115 return XC_left_side; | |
| 116 case aura::kCursorNorthSouthResize: | |
| 117 return XC_sb_v_double_arrow; | |
| 118 case aura::kCursorEastWestResize: | |
| 119 return XC_sb_h_double_arrow; | |
| 120 case aura::kCursorNorthEastSouthWestResize: | |
| 121 case aura::kCursorNorthWestSouthEastResize: | |
| 122 // There isn't really a useful cursor available for these. | |
| 123 NOTIMPLEMENTED(); | |
| 124 return XC_left_ptr; | |
| 125 case aura::kCursorColumnResize: | |
| 126 return XC_sb_h_double_arrow; | |
| 127 case aura::kCursorRowResize: | |
| 128 return XC_sb_v_double_arrow; | |
| 129 case aura::kCursorMiddlePanning: | |
| 130 return XC_fleur; | |
| 131 case aura::kCursorEastPanning: | |
| 132 return XC_sb_right_arrow; | |
| 133 case aura::kCursorNorthPanning: | |
| 134 return XC_sb_up_arrow; | |
| 135 case aura::kCursorNorthEastPanning: | |
| 136 return XC_top_right_corner; | |
| 137 case aura::kCursorNorthWestPanning: | |
| 138 return XC_top_left_corner; | |
| 139 case aura::kCursorSouthPanning: | |
| 140 return XC_sb_down_arrow; | |
| 141 case aura::kCursorSouthEastPanning: | |
| 142 return XC_bottom_right_corner; | |
| 143 case aura::kCursorSouthWestPanning: | |
| 144 return XC_bottom_left_corner; | |
| 145 case aura::kCursorWestPanning: | |
| 146 return XC_sb_left_arrow; | |
| 147 case aura::kCursorMove: | |
| 148 return XC_fleur; | |
| 149 case aura::kCursorVerticalText: | |
| 150 case aura::kCursorCell: | |
| 151 case aura::kCursorContextMenu: | |
| 152 case aura::kCursorAlias: | |
| 153 case aura::kCursorProgress: | |
| 154 case aura::kCursorNoDrop: | |
| 155 case aura::kCursorCopy: | |
| 156 case aura::kCursorNone: | |
| 157 case aura::kCursorNotAllowed: | |
| 158 case aura::kCursorZoomIn: | |
| 159 case aura::kCursorZoomOut: | |
| 160 case aura::kCursorGrab: | |
| 161 case aura::kCursorGrabbing: | |
| 162 case aura::kCursorCustom: | |
| 163 // TODO(jamescook): Need cursors for these. | |
| 164 NOTIMPLEMENTED(); | |
| 165 return XC_left_ptr; | |
| 166 } | |
| 167 NOTREACHED(); | |
| 168 return XC_left_ptr; | |
| 169 } | |
| 170 | |
| 171 // Coalesce all pending motion events that are at the top of the queue, and | |
| 172 // return the number eliminated, storing the last one in |last_event|. | |
| 173 int CoalescePendingXIMotionEvents(const XEvent* xev, XEvent* last_event) { | |
| 174 XIDeviceEvent* xievent = static_cast<XIDeviceEvent*>(xev->xcookie.data); | |
| 175 int num_coalesed = 0; | |
| 176 Display* display = xev->xany.display; | |
| 177 | |
| 178 while (XPending(display)) { | |
| 179 XEvent next_event; | |
| 180 XPeekEvent(display, &next_event); | |
| 181 | |
| 182 // If we can't get the cookie, abort the check. | |
| 183 if (!XGetEventData(next_event.xgeneric.display, &next_event.xcookie)) | |
| 184 return num_coalesed; | |
| 185 | |
| 186 // If this isn't from a valid device, throw the event away, as | |
| 187 // that's what the message pump would do. Device events come in pairs | |
| 188 // with one from the master and one from the slave so there will | |
| 189 // always be at least one pending. | |
| 190 if (!ui::TouchFactory::GetInstance()->ShouldProcessXI2Event(&next_event)) { | |
| 191 CheckXEventForConsistency(&next_event); | |
| 192 XFreeEventData(display, &next_event.xcookie); | |
| 193 XNextEvent(display, &next_event); | |
| 194 continue; | |
| 195 } | |
| 196 | |
| 197 if (next_event.type == GenericEvent && | |
| 198 next_event.xgeneric.evtype == XI_Motion) { | |
| 199 XIDeviceEvent* next_xievent = | |
| 200 static_cast<XIDeviceEvent*>(next_event.xcookie.data); | |
| 201 // Confirm that the motion event is targeted at the same window | |
| 202 // and that no buttons or modifiers have changed. | |
| 203 if (xievent->event == next_xievent->event && | |
| 204 xievent->child == next_xievent->child && | |
| 205 xievent->buttons.mask_len == next_xievent->buttons.mask_len && | |
| 206 (memcmp(xievent->buttons.mask, | |
| 207 next_xievent->buttons.mask, | |
| 208 xievent->buttons.mask_len) == 0) && | |
| 209 xievent->mods.base == next_xievent->mods.base && | |
| 210 xievent->mods.latched == next_xievent->mods.latched && | |
| 211 xievent->mods.locked == next_xievent->mods.locked && | |
| 212 xievent->mods.effective == next_xievent->mods.effective) { | |
| 213 XFreeEventData(display, &next_event.xcookie); | |
| 214 // Free the previous cookie. | |
| 215 if (num_coalesed > 0) | |
| 216 XFreeEventData(display, &last_event->xcookie); | |
| 217 // Get the event and its cookie data. | |
| 218 XNextEvent(display, last_event); | |
| 219 XGetEventData(display, &last_event->xcookie); | |
| 220 CheckXEventForConsistency(last_event); | |
| 221 ++num_coalesed; | |
| 222 continue; | |
| 223 } else { | |
| 224 // This isn't an event we want so free its cookie data. | |
| 225 XFreeEventData(display, &next_event.xcookie); | |
| 226 } | |
| 227 } | |
| 228 break; | |
| 229 } | |
| 230 return num_coalesed; | |
| 231 } | |
| 232 | |
| 233 // We emulate Windows' WM_KEYDOWN and WM_CHAR messages. WM_CHAR events are only | |
| 234 // generated for certain keys; see | |
| 235 // http://msdn.microsoft.com/en-us/library/windows/desktop/ms646268.aspx. | |
| 236 bool ShouldSendCharEventForKeyboardCode(ui::KeyboardCode keycode) { | |
| 237 if ((keycode >= ui::VKEY_0 && keycode <= ui::VKEY_9) || | |
| 238 (keycode >= ui::VKEY_A && keycode <= ui::VKEY_Z) || | |
| 239 (keycode >= ui::VKEY_NUMPAD0 && keycode <= ui::VKEY_NUMPAD9)) { | |
| 240 return true; | |
| 241 } | |
| 242 | |
| 243 switch (keycode) { | |
| 244 case ui::VKEY_BACK: | |
| 245 case ui::VKEY_RETURN: | |
| 246 case ui::VKEY_ESCAPE: | |
| 247 case ui::VKEY_SPACE: | |
| 248 case ui::VKEY_TAB: | |
| 249 // In addition to the keys listed at MSDN, we include other | |
| 250 // graphic-character and numpad keys. | |
| 251 case ui::VKEY_MULTIPLY: | |
| 252 case ui::VKEY_ADD: | |
| 253 case ui::VKEY_SUBTRACT: | |
| 254 case ui::VKEY_DECIMAL: | |
| 255 case ui::VKEY_DIVIDE: | |
| 256 case ui::VKEY_OEM_1: | |
| 257 case ui::VKEY_OEM_2: | |
| 258 case ui::VKEY_OEM_3: | |
| 259 case ui::VKEY_OEM_4: | |
| 260 case ui::VKEY_OEM_5: | |
| 261 case ui::VKEY_OEM_6: | |
| 262 case ui::VKEY_OEM_7: | |
| 263 case ui::VKEY_OEM_102: | |
| 264 case ui::VKEY_OEM_PLUS: | |
| 265 case ui::VKEY_OEM_COMMA: | |
| 266 case ui::VKEY_OEM_MINUS: | |
| 267 case ui::VKEY_OEM_PERIOD: | |
| 268 return true; | |
| 269 default: | |
| 270 return false; | |
| 271 } | |
| 272 } | |
| 273 | |
| 274 class DesktopHostLinux : public DesktopHost, | |
| 275 public MessageLoop::DestructionObserver { | |
| 276 public: | |
| 277 explicit DesktopHostLinux(const gfx::Rect& bounds); | |
| 278 virtual ~DesktopHostLinux(); | |
| 279 | |
| 280 private: | |
| 281 // MessageLoop::Dispatcher Override. | |
| 282 virtual DispatchStatus Dispatch(XEvent* xev) OVERRIDE; | |
| 283 | |
| 284 // DesktopHost Overrides. | |
| 285 virtual void SetDesktop(Desktop* desktop) OVERRIDE; | |
| 286 virtual gfx::AcceleratedWidget GetAcceleratedWidget() OVERRIDE; | |
| 287 virtual void Show() OVERRIDE; | |
| 288 virtual void ToggleFullScreen() OVERRIDE; | |
| 289 virtual gfx::Size GetSize() const OVERRIDE; | |
| 290 virtual void SetSize(const gfx::Size& size) OVERRIDE; | |
| 291 virtual gfx::Point GetLocationOnNativeScreen() const OVERRIDE; | |
| 292 virtual void SetCursor(gfx::NativeCursor cursor_type) OVERRIDE; | |
| 293 virtual gfx::Point QueryMouseLocation() OVERRIDE; | |
| 294 virtual void PostNativeEvent(const base::NativeEvent& event) OVERRIDE; | |
| 295 | |
| 296 // MessageLoop::DestructionObserver Overrides. | |
| 297 virtual void WillDestroyCurrentMessageLoop() OVERRIDE; | |
| 298 | |
| 299 // Returns true if there's an X window manager present... in most cases. Some | |
| 300 // window managers (notably, ion3) don't implement enough of ICCCM for us to | |
| 301 // detect that they're there. | |
| 302 bool IsWindowManagerPresent(); | |
| 303 | |
| 304 Desktop* desktop_; | |
| 305 | |
| 306 // The display and the native X window hosting the desktop. | |
| 307 Display* xdisplay_; | |
| 308 ::Window xwindow_; | |
| 309 | |
| 310 // The native root window. | |
| 311 ::Window root_window_; | |
| 312 | |
| 313 // Current Aura cursor. | |
| 314 gfx::NativeCursor current_cursor_; | |
| 315 | |
| 316 // The bounds of |xwindow_|. | |
| 317 gfx::Rect bounds_; | |
| 318 | |
| 319 DISALLOW_COPY_AND_ASSIGN(DesktopHostLinux); | |
| 320 }; | |
| 321 | |
| 322 DesktopHostLinux::DesktopHostLinux(const gfx::Rect& bounds) | |
| 323 : desktop_(NULL), | |
| 324 xdisplay_(base::MessagePumpX::GetDefaultXDisplay()), | |
| 325 xwindow_(0), | |
| 326 root_window_(DefaultRootWindow(xdisplay_)), | |
| 327 current_cursor_(aura::kCursorNull), | |
| 328 bounds_(bounds) { | |
| 329 xwindow_ = XCreateSimpleWindow(xdisplay_, root_window_, | |
| 330 bounds.x(), bounds.y(), | |
| 331 bounds.width(), bounds.height(), | |
| 332 0, 0, 0); | |
| 333 | |
| 334 long event_mask = ButtonPressMask | ButtonReleaseMask | | |
| 335 KeyPressMask | KeyReleaseMask | | |
| 336 EnterWindowMask | LeaveWindowMask | | |
| 337 ExposureMask | VisibilityChangeMask | | |
| 338 StructureNotifyMask | PropertyChangeMask | | |
| 339 PointerMotionMask; | |
| 340 XSelectInput(xdisplay_, xwindow_, event_mask); | |
| 341 XSelectInput(xdisplay_, root_window_, StructureNotifyMask); | |
| 342 XFlush(xdisplay_); | |
| 343 | |
| 344 // TODO(sad): Re-enable once crbug.com/106516 is fixed. | |
| 345 #if 0 | |
| 346 if (base::MessagePumpForUI::HasXInput2()) | |
| 347 ui::TouchFactory::GetInstance()->SetupXI2ForXWindow(xwindow_); | |
| 348 #endif | |
| 349 | |
| 350 base::MessagePumpX::SetDefaultDispatcher(this); | |
| 351 MessageLoopForUI::current()->AddDestructionObserver(this); | |
| 352 } | |
| 353 | |
| 354 DesktopHostLinux::~DesktopHostLinux() { | |
| 355 XDestroyWindow(xdisplay_, xwindow_); | |
| 356 | |
| 357 // Clears XCursorCache. | |
| 358 ui::GetXCursor(ui::kCursorClearXCursorCache); | |
| 359 | |
| 360 MessageLoopForUI::current()->RemoveDestructionObserver(this); | |
| 361 base::MessagePumpX::SetDefaultDispatcher(NULL); | |
| 362 } | |
| 363 | |
| 364 base::MessagePumpDispatcher::DispatchStatus DesktopHostLinux::Dispatch( | |
| 365 XEvent* xev) { | |
| 366 bool handled = false; | |
| 367 | |
| 368 CheckXEventForConsistency(xev); | |
| 369 | |
| 370 switch (xev->type) { | |
| 371 case Expose: | |
| 372 desktop_->ScheduleDraw(); | |
| 373 handled = true; | |
| 374 break; | |
| 375 case KeyPress: { | |
| 376 KeyEvent keydown_event(xev, false); | |
| 377 handled = desktop_->DispatchKeyEvent(&keydown_event); | |
| 378 if (ShouldSendCharEventForKeyboardCode(keydown_event.key_code())) { | |
| 379 KeyEvent char_event(xev, true); | |
| 380 handled |= desktop_->DispatchKeyEvent(&char_event); | |
| 381 } | |
| 382 break; | |
| 383 } | |
| 384 case KeyRelease: { | |
| 385 KeyEvent keyup_event(xev, false); | |
| 386 handled = desktop_->DispatchKeyEvent(&keyup_event); | |
| 387 break; | |
| 388 } | |
| 389 case ButtonPress: | |
| 390 case ButtonRelease: { | |
| 391 MouseEvent mouseev(xev); | |
| 392 handled = desktop_->DispatchMouseEvent(&mouseev); | |
| 393 break; | |
| 394 } | |
| 395 case ConfigureNotify: { | |
| 396 if (xev->xconfigure.window == root_window_) { | |
| 397 desktop_->OnNativeScreenResized( | |
| 398 gfx::Size(xev->xconfigure.width, xev->xconfigure.height)); | |
| 399 handled = true; | |
| 400 break; | |
| 401 } | |
| 402 | |
| 403 DCHECK_EQ(xwindow_, xev->xconfigure.window); | |
| 404 DCHECK_EQ(xwindow_, xev->xconfigure.event); | |
| 405 | |
| 406 // It's possible that the X window may be resized by some other means than | |
| 407 // from within aura (e.g. the X window manager can change the size). Make | |
| 408 // sure the desktop size is maintained properly. | |
| 409 gfx::Rect bounds(xev->xconfigure.x, xev->xconfigure.y, | |
| 410 xev->xconfigure.width, xev->xconfigure.height); | |
| 411 bool size_changed = bounds_.size() != bounds.size(); | |
| 412 bounds_ = bounds; | |
| 413 if (size_changed) | |
| 414 desktop_->OnHostResized(bounds.size()); | |
| 415 handled = true; | |
| 416 break; | |
| 417 } | |
| 418 case GenericEvent: { | |
| 419 ui::TouchFactory* factory = ui::TouchFactory::GetInstance(); | |
| 420 if (!factory->ShouldProcessXI2Event(xev)) | |
| 421 break; | |
| 422 | |
| 423 // If this is a motion event we want to coalesce all pending motion | |
| 424 // events that are at the top of the queue. | |
| 425 XEvent last_event; | |
| 426 int num_coalesced = 0; | |
| 427 if (xev->xgeneric.evtype == XI_Motion) { | |
| 428 num_coalesced = CoalescePendingXIMotionEvents(xev, &last_event); | |
| 429 if (num_coalesced > 0) | |
| 430 xev = &last_event; | |
| 431 } | |
| 432 | |
| 433 ui::EventType type = ui::EventTypeFromNative(xev); | |
| 434 switch (type) { | |
| 435 case ui::ET_TOUCH_PRESSED: | |
| 436 case ui::ET_TOUCH_RELEASED: | |
| 437 case ui::ET_TOUCH_MOVED: { | |
| 438 TouchEvent touchev(xev); | |
| 439 handled = desktop_->DispatchTouchEvent(&touchev); | |
| 440 break; | |
| 441 } | |
| 442 case ui::ET_MOUSE_PRESSED: | |
| 443 case ui::ET_MOUSE_RELEASED: | |
| 444 case ui::ET_MOUSE_MOVED: | |
| 445 case ui::ET_MOUSE_DRAGGED: | |
| 446 case ui::ET_MOUSEWHEEL: | |
| 447 case ui::ET_MOUSE_ENTERED: | |
| 448 case ui::ET_MOUSE_EXITED: { | |
| 449 MouseEvent mouseev(xev); | |
| 450 handled = desktop_->DispatchMouseEvent(&mouseev); | |
| 451 break; | |
| 452 } | |
| 453 case ui::ET_UNKNOWN: | |
| 454 handled = false; | |
| 455 break; | |
| 456 default: | |
| 457 NOTREACHED(); | |
| 458 } | |
| 459 | |
| 460 // If we coalesced an event we need to free its cookie. | |
| 461 if (num_coalesced > 0) | |
| 462 XFreeEventData(xev->xgeneric.display, &last_event.xcookie); | |
| 463 break; | |
| 464 } | |
| 465 case MapNotify: { | |
| 466 // If there's no window manager running, we need to assign the X input | |
| 467 // focus to our host window. | |
| 468 if (!IsWindowManagerPresent()) | |
| 469 XSetInputFocus(xdisplay_, xwindow_, RevertToNone, CurrentTime); | |
| 470 handled = true; | |
| 471 break; | |
| 472 } | |
| 473 case MappingNotify: { | |
| 474 if (xev->xmapping.request == MappingModifier || | |
| 475 xev->xmapping.request == MappingKeyboard) | |
| 476 XRefreshKeyboardMapping(&xev->xmapping); | |
| 477 break; | |
| 478 } | |
| 479 case MotionNotify: { | |
| 480 // Discard all but the most recent motion event that targets the same | |
| 481 // window with unchanged state. | |
| 482 XEvent last_event; | |
| 483 while (XPending(xev->xany.display)) { | |
| 484 XEvent next_event; | |
| 485 XPeekEvent(xev->xany.display, &next_event); | |
| 486 if (next_event.type == MotionNotify && | |
| 487 next_event.xmotion.window == xev->xmotion.window && | |
| 488 next_event.xmotion.subwindow == xev->xmotion.subwindow && | |
| 489 next_event.xmotion.state == xev->xmotion.state) { | |
| 490 XNextEvent(xev->xany.display, &last_event); | |
| 491 xev = &last_event; | |
| 492 } else { | |
| 493 break; | |
| 494 } | |
| 495 } | |
| 496 | |
| 497 MouseEvent mouseev(xev); | |
| 498 handled = desktop_->DispatchMouseEvent(&mouseev); | |
| 499 break; | |
| 500 } | |
| 501 } | |
| 502 return handled ? EVENT_PROCESSED : EVENT_IGNORED; | |
| 503 } | |
| 504 | |
| 505 void DesktopHostLinux::SetDesktop(Desktop* desktop) { | |
| 506 desktop_ = desktop; | |
| 507 } | |
| 508 | |
| 509 gfx::AcceleratedWidget DesktopHostLinux::GetAcceleratedWidget() { | |
| 510 return xwindow_; | |
| 511 } | |
| 512 | |
| 513 void DesktopHostLinux::Show() { | |
| 514 XMapWindow(xdisplay_, xwindow_); | |
| 515 } | |
| 516 | |
| 517 void DesktopHostLinux::ToggleFullScreen() { | |
| 518 NOTIMPLEMENTED(); | |
| 519 } | |
| 520 | |
| 521 gfx::Size DesktopHostLinux::GetSize() const { | |
| 522 return bounds_.size(); | |
| 523 } | |
| 524 | |
| 525 void DesktopHostLinux::SetSize(const gfx::Size& size) { | |
| 526 if (size == bounds_.size()) | |
| 527 return; | |
| 528 | |
| 529 XResizeWindow(xdisplay_, xwindow_, size.width(), size.height()); | |
| 530 | |
| 531 // Assume that the resize will go through as requested, which should be the | |
| 532 // case if we're running without a window manager. If there's a window | |
| 533 // manager, it can modify or ignore the request, but (per ICCCM) we'll get a | |
| 534 // (possibly synthetic) ConfigureNotify about the actual size and correct | |
| 535 // |bounds_| later. | |
| 536 bounds_.set_size(size); | |
| 537 desktop_->OnHostResized(size); | |
| 538 } | |
| 539 | |
| 540 gfx::Point DesktopHostLinux::GetLocationOnNativeScreen() const { | |
| 541 return bounds_.origin(); | |
| 542 } | |
| 543 | |
| 544 void DesktopHostLinux::SetCursor(gfx::NativeCursor cursor) { | |
| 545 if (current_cursor_ == cursor) | |
| 546 return; | |
| 547 current_cursor_ = cursor; | |
| 548 // Custom web cursors are handled directly. | |
| 549 if (cursor == kCursorCustom) | |
| 550 return; | |
| 551 int cursor_shape = CursorShapeFromNative(cursor); | |
| 552 ::Cursor xcursor = ui::GetXCursor(cursor_shape); | |
| 553 XDefineCursor(xdisplay_, xwindow_, xcursor); | |
| 554 } | |
| 555 | |
| 556 gfx::Point DesktopHostLinux::QueryMouseLocation() { | |
| 557 ::Window root_return, child_return; | |
| 558 int root_x_return, root_y_return, win_x_return, win_y_return; | |
| 559 unsigned int mask_return; | |
| 560 XQueryPointer(xdisplay_, | |
| 561 xwindow_, | |
| 562 &root_return, | |
| 563 &child_return, | |
| 564 &root_x_return, &root_y_return, | |
| 565 &win_x_return, &win_y_return, | |
| 566 &mask_return); | |
| 567 return gfx::Point(max(0, min(bounds_.width(), win_x_return)), | |
| 568 max(0, min(bounds_.height(), win_y_return))); | |
| 569 } | |
| 570 | |
| 571 void DesktopHostLinux::PostNativeEvent(const base::NativeEvent& native_event) { | |
| 572 DCHECK(xwindow_); | |
| 573 DCHECK(xdisplay_); | |
| 574 XEvent xevent = *native_event; | |
| 575 xevent.xany.display = xdisplay_; | |
| 576 xevent.xany.window = xwindow_; | |
| 577 | |
| 578 switch (xevent.type) { | |
| 579 case EnterNotify: | |
| 580 case LeaveNotify: | |
| 581 case MotionNotify: | |
| 582 case KeyPress: | |
| 583 case KeyRelease: | |
| 584 case ButtonPress: | |
| 585 case ButtonRelease: { | |
| 586 // The fields used below are in the same place for all of events | |
| 587 // above. Using xmotion from XEvent's unions to avoid repeating | |
| 588 // the code. | |
| 589 xevent.xmotion.root = root_window_; | |
| 590 xevent.xmotion.time = CurrentTime; | |
| 591 | |
| 592 gfx::Point point(xevent.xmotion.x, xevent.xmotion.y); | |
| 593 desktop_->ConvertPointToNativeScreen(&point); | |
| 594 xevent.xmotion.x_root = point.x(); | |
| 595 xevent.xmotion.y_root = point.y(); | |
| 596 } | |
| 597 default: | |
| 598 break; | |
| 599 } | |
| 600 XSendEvent(xdisplay_, xwindow_, False, 0, &xevent); | |
| 601 } | |
| 602 | |
| 603 void DesktopHostLinux::WillDestroyCurrentMessageLoop() { | |
| 604 aura::Desktop::DeleteInstance(); | |
| 605 } | |
| 606 | |
| 607 bool DesktopHostLinux::IsWindowManagerPresent() { | |
| 608 // Per ICCCM 2.8, "Manager Selections", window managers should take ownership | |
| 609 // of WM_Sn selections (where n is a screen number). | |
| 610 ::Atom wm_s0_atom = XInternAtom(xdisplay_, "WM_S0", False); | |
| 611 return XGetSelectionOwner(xdisplay_, wm_s0_atom) != None; | |
| 612 } | |
| 613 | |
| 614 } // namespace | |
| 615 | |
| 616 // static | |
| 617 DesktopHost* DesktopHost::Create(const gfx::Rect& bounds) { | |
| 618 return new DesktopHostLinux(bounds); | |
| 619 } | |
| 620 | |
| 621 // static | |
| 622 gfx::Size DesktopHost::GetNativeScreenSize() { | |
| 623 ::Display* xdisplay = base::MessagePumpX::GetDefaultXDisplay(); | |
| 624 return gfx::Size(DisplayWidth(xdisplay, 0), DisplayHeight(xdisplay, 0)); | |
| 625 } | |
| 626 | |
| 627 } // namespace aura | |
| OLD | NEW |