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 |