1 // Copyright 2017 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/views/widget/desktop_aura/window_event_filter_linux.h"
6
7 #include "ui/aura/client/aura_constants.h"
8 #include "ui/aura/env.h"
9 #include "ui/aura/window.h"
10 #include "ui/aura/window_delegate.h"
11 #include "ui/aura/window_tree_host.h"
12 #include "ui/base/hit_test.h"
13 #include "ui/display/display.h"
14 #include "ui/display/screen.h"
15 #include "ui/events/event.h"
16 #include "ui/events/event_utils.h"
17 #include "ui/platform_window/platform_window_handler/wm_move_resize_handler.h"
18 #include "ui/views/linux_ui/linux_ui.h"
19 #include "ui/views/widget/desktop_aura/desktop_window_tree_host_linux.h"
20 #include "ui/views/widget/native_widget_aura.h"
21 #include "ui/views/widget/widget.h"
22
23 namespace views {
24
WindowEventFilterLinux(DesktopWindowTreeHostLinux * desktop_window_tree_host,ui::WmMoveResizeHandler * handler)25 WindowEventFilterLinux::WindowEventFilterLinux(
26 DesktopWindowTreeHostLinux* desktop_window_tree_host,
27 ui::WmMoveResizeHandler* handler)
28 : desktop_window_tree_host_(desktop_window_tree_host), handler_(handler) {}
29
30 WindowEventFilterLinux::~WindowEventFilterLinux() = default;
31
HandleMouseEventWithHitTest(int hit_test,ui::MouseEvent * event)32 void WindowEventFilterLinux::HandleMouseEventWithHitTest(
33 int hit_test,
34 ui::MouseEvent* event) {
35 if (event->type() != ui::ET_MOUSE_PRESSED)
36 return;
37
38 int previous_click_component = HTNOWHERE;
39 if (event->IsLeftMouseButton()) {
40 previous_click_component = click_component_;
41 click_component_ = hit_test;
42 }
43
44 if (hit_test == HTCAPTION) {
45 OnClickedCaption(event, previous_click_component);
46 } else if (hit_test == HTMAXBUTTON) {
47 OnClickedMaximizeButton(event);
48 } else {
49 if (desktop_window_tree_host_->GetContentWindow()->GetProperty(
50 aura::client::kResizeBehaviorKey) &
51 aura::client::kResizeBehaviorCanResize) {
52 MaybeDispatchHostWindowDragMovement(hit_test, event);
53 }
54 }
55 }
56
OnClickedCaption(ui::MouseEvent * event,int previous_click_component)57 void WindowEventFilterLinux::OnClickedCaption(ui::MouseEvent* event,
58 int previous_click_component) {
59 LinuxUI* linux_ui = LinuxUI::instance();
60
61 views::LinuxUI::WindowFrameActionSource action_type;
62 views::LinuxUI::WindowFrameAction default_action;
63
64 if (event->IsRightMouseButton()) {
65 action_type = LinuxUI::WindowFrameActionSource::kRightClick;
66 default_action = LinuxUI::WindowFrameAction::kMenu;
67 } else if (event->IsMiddleMouseButton()) {
68 action_type = LinuxUI::WindowFrameActionSource::kMiddleClick;
69 default_action = LinuxUI::WindowFrameAction::kNone;
70 } else if (event->IsLeftMouseButton() &&
71 event->flags() & ui::EF_IS_DOUBLE_CLICK) {
72 click_component_ = HTNOWHERE;
73 if (previous_click_component == HTCAPTION) {
74 action_type = LinuxUI::WindowFrameActionSource::kDoubleClick;
75 default_action = LinuxUI::WindowFrameAction::kToggleMaximize;
76 } else {
77 return;
78 }
79 } else {
80 MaybeDispatchHostWindowDragMovement(HTCAPTION, event);
81 return;
82 }
83
84 auto* content_window = desktop_window_tree_host_->GetContentWindow();
85 LinuxUI::WindowFrameAction action =
86 linux_ui ? linux_ui->GetWindowFrameAction(action_type) : default_action;
87 switch (action) {
88 case LinuxUI::WindowFrameAction::kNone:
89 break;
90 case LinuxUI::WindowFrameAction::kLower:
91 LowerWindow();
92 event->SetHandled();
93 break;
94 case LinuxUI::WindowFrameAction::kMinimize:
95 desktop_window_tree_host_->Minimize();
96 event->SetHandled();
97 break;
98 case LinuxUI::WindowFrameAction::kToggleMaximize:
99
100 if (content_window->GetProperty(aura::client::kResizeBehaviorKey) &
101 aura::client::kResizeBehaviorCanMaximize) {
102 ToggleMaximizedState();
103 }
104 event->SetHandled();
105 break;
106 case LinuxUI::WindowFrameAction::kMenu:
107 views::Widget* widget =
108 views::Widget::GetWidgetForNativeView(content_window);
109 if (!widget)
110 break;
111 views::View* view = widget->GetContentsView();
112 if (!view || !view->context_menu_controller())
113 break;
114 gfx::Point location(event->location());
115 views::View::ConvertPointToScreen(view, &location);
116 view->ShowContextMenu(location, ui::MENU_SOURCE_MOUSE);
117 event->SetHandled();
118 break;
119 }
120 }
121
OnClickedMaximizeButton(ui::MouseEvent * event)122 void WindowEventFilterLinux::OnClickedMaximizeButton(ui::MouseEvent* event) {
123 auto* content_window = desktop_window_tree_host_->GetContentWindow();
124 views::Widget* widget = views::Widget::GetWidgetForNativeView(content_window);
125 if (!widget)
126 return;
127
128 gfx::Rect display_work_area = display::Screen::GetScreen()
129 ->GetDisplayNearestWindow(content_window)
130 .work_area();
131 gfx::Rect bounds = widget->GetWindowBoundsInScreen();
132 if (event->IsMiddleMouseButton()) {
133 bounds.set_y(display_work_area.y());
134 bounds.set_height(display_work_area.height());
135 widget->SetBounds(bounds);
136 event->StopPropagation();
137 } else if (event->IsRightMouseButton()) {
138 bounds.set_x(display_work_area.x());
139 bounds.set_width(display_work_area.width());
140 widget->SetBounds(bounds);
141 event->StopPropagation();
142 }
143 }
144
ToggleMaximizedState()145 void WindowEventFilterLinux::ToggleMaximizedState() {
146 if (desktop_window_tree_host_->IsMaximized())
147 desktop_window_tree_host_->Restore();
148 else
149 desktop_window_tree_host_->Maximize();
150 }
151
LowerWindow()152 void WindowEventFilterLinux::LowerWindow() {
153 desktop_window_tree_host_->LowerXWindow();
154 }
155
MaybeDispatchHostWindowDragMovement(int hittest,ui::MouseEvent * event)156 void WindowEventFilterLinux::MaybeDispatchHostWindowDragMovement(
157 int hittest,
158 ui::MouseEvent* event) {
159 if (handler_ && event->IsLeftMouseButton() &&
160 ui::CanPerformDragOrResize(hittest)) {
161 // Some platforms (eg X11) may require last pointer location not in the
162 // local surface coordinates, but rather in the screen coordinates for
163 // interactive move/resize.
164 auto bounds_in_px =
165 desktop_window_tree_host_->AsWindowTreeHost()->GetBoundsInPixels();
166 auto screen_point_in_px = event->location();
167 screen_point_in_px.Offset(bounds_in_px.x(), bounds_in_px.y());
168 handler_->DispatchHostWindowDragMovement(hittest, screen_point_in_px);
169 event->StopPropagation();
170 return;
171 }
172 }
173
174 } // namespace views
175