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