1 #include "output-impl.hpp"
2 #include "wayfire/view.hpp"
3 #include "../core/core-impl.hpp"
4 #include "wayfire/signal-definitions.hpp"
5 #include "wayfire/render-manager.hpp"
6 #include "wayfire/output-layout.hpp"
7 #include "wayfire/workspace-manager.hpp"
8 #include "wayfire/compositor-view.hpp"
9 #include "wayfire-shell.hpp"
10 #include "../core/seat/input-manager.hpp"
11 #include "../view/xdg-shell.hpp"
12 #include <wayfire/util/log.hpp>
13 #include <wayfire/nonstd/wlroots-full.hpp>
14 
15 #include <algorithm>
16 #include <assert.h>
17 
18 wf::output_t::output_t() = default;
19 
output_impl_t(wlr_output * handle,const wf::dimensions_t & effective_size)20 wf::output_impl_t::output_impl_t(wlr_output *handle,
21     const wf::dimensions_t& effective_size)
22 {
23     this->bindings = std::make_unique<bindings_repository_t>(this);
24     this->set_effective_size(effective_size);
25     this->handle = handle;
26     workspace    = std::make_unique<workspace_manager>(this);
27     render = std::make_unique<render_manager>(this);
28 
29     view_disappeared_cb = [=] (wf::signal_data_t *data)
30     {
31         output_t::refocus(get_signaled_view(data));
32     };
33 
34     connect_signal("view-disappeared", &view_disappeared_cb);
35 }
36 
start_plugins()37 void wf::output_impl_t::start_plugins()
38 {
39     plugin = std::make_unique<plugin_manager>(this);
40 }
41 
to_string() const42 std::string wf::output_t::to_string() const
43 {
44     return handle->name;
45 }
46 
refocus(wayfire_view skip_view,uint32_t layers)47 void wf::output_impl_t::refocus(wayfire_view skip_view, uint32_t layers)
48 {
49     wayfire_view next_focus = nullptr;
50     auto views = workspace->get_views_on_workspace(
51         workspace->get_current_workspace(), layers);
52 
53     const auto& suitable_for_focus = [&] (wayfire_view view)
54     {
55         return (view != skip_view) && view->is_mapped() &&
56                view->get_keyboard_focus_surface() && !view->minimized;
57     };
58 
59     const auto& newer_than_candidate = [&] (wayfire_view view)
60     {
61         return (!next_focus ||
62             (next_focus->last_focus_timestamp < view->last_focus_timestamp));
63     };
64 
65     for (auto toplevel : views)
66     {
67         for (auto& v : toplevel->enumerate_views())
68         {
69             if (suitable_for_focus(v) && newer_than_candidate(v))
70             {
71                 next_focus = v;
72             }
73         }
74     }
75 
76     focus_view(next_focus, 0u);
77 }
78 
refocus(wayfire_view skip_view)79 void wf::output_t::refocus(wayfire_view skip_view)
80 {
81     uint32_t focused_layer = wf::get_core().get_focused_layer();
82     uint32_t layers = focused_layer <=
83         LAYER_WORKSPACE ? MIDDLE_LAYERS : focused_layer;
84 
85     auto views = workspace->get_views_on_workspace(
86         workspace->get_current_workspace(), layers);
87 
88     if (views.empty())
89     {
90         if (wf::get_core().get_active_output() == this)
91         {
92             LOGD("warning: no focused views in the focused layer, probably a bug");
93         }
94 
95         /* Usually, we focus a layer so that a particular view has focus, i.e
96          * we expect that there is a view in the focused layer. However we
97          * should try to find reasonable focus in any focuseable layers if
98          * that is not the case, for ex. if there is a focused layer by a
99          * layer surface on another output */
100         layers = all_layers_not_below(focused_layer);
101     }
102 
103     refocus(skip_view, layers);
104 }
105 
~output_t()106 wf::output_t::~output_t()
107 {}
108 
~output_impl_t()109 wf::output_impl_t::~output_impl_t()
110 {
111     // Release plugins before bindings
112     this->plugin.reset();
113     this->bindings.reset();
114 }
115 
set_effective_size(const wf::dimensions_t & size)116 void wf::output_impl_t::set_effective_size(const wf::dimensions_t& size)
117 {
118     this->effective_size = size;
119 }
120 
get_screen_size() const121 wf::dimensions_t wf::output_impl_t::get_screen_size() const
122 {
123     return this->effective_size;
124 }
125 
get_relative_geometry() const126 wf::geometry_t wf::output_t::get_relative_geometry() const
127 {
128     auto size = get_screen_size();
129 
130     return {
131         0, 0, size.width, size.height
132     };
133 }
134 
get_layout_geometry() const135 wf::geometry_t wf::output_t::get_layout_geometry() const
136 {
137     auto box = wlr_output_layout_get_box(
138         wf::get_core().output_layout->get_handle(), handle);
139     if (box)
140     {
141         return *box;
142     } else
143     {
144         LOGE("Get layout geometry for an invalid output!");
145 
146         return {0, 0, 1, 1};
147     }
148 }
149 
ensure_pointer(bool center) const150 void wf::output_t::ensure_pointer(bool center) const
151 {
152     auto ptr = wf::get_core().get_cursor_position();
153     if (!center &&
154         (get_layout_geometry() & wf::point_t{(int)ptr.x, (int)ptr.y}))
155     {
156         return;
157     }
158 
159     auto lg = get_layout_geometry();
160     wf::pointf_t target = {
161         lg.x + lg.width / 2.0,
162         lg.y + lg.height / 2.0,
163     };
164     wf::get_core().warp_cursor(target);
165     wf::get_core().set_cursor("default");
166 }
167 
get_cursor_position() const168 wf::pointf_t wf::output_t::get_cursor_position() const
169 {
170     auto og = get_layout_geometry();
171     auto gc = wf::get_core().get_cursor_position();
172 
173     return {gc.x - og.x, gc.y - og.y};
174 }
175 
ensure_visible(wayfire_view v)176 bool wf::output_t::ensure_visible(wayfire_view v)
177 {
178     auto bbox = v->get_bounding_box();
179     auto g    = this->get_relative_geometry();
180 
181     /* Compute the percentage of the view which is visible */
182     auto intersection = wf::geometry_intersection(bbox, g);
183     double area = 1.0 * intersection.width * intersection.height;
184     area /= 1.0 * bbox.width * bbox.height;
185 
186     if (area >= 0.1) /* View is somewhat visible, no need for anything special */
187     {
188         return false;
189     }
190 
191     /* Otherwise, switch the workspace so the view gets maximum exposure */
192     int dx = bbox.x + bbox.width / 2;
193     int dy = bbox.y + bbox.height / 2;
194 
195     int dvx  = std::floor(1.0 * dx / g.width);
196     int dvy  = std::floor(1.0 * dy / g.height);
197     auto cws = workspace->get_current_workspace();
198     workspace->request_workspace(cws + wf::point_t{dvx, dvy});
199 
200     return true;
201 }
202 
close_popups()203 void wf::output_impl_t::close_popups()
204 {
205     for (auto& v : workspace->get_views_in_layer(wf::ALL_LAYERS))
206     {
207         auto popup = dynamic_cast<wayfire_xdg_popup*>(v.get());
208         if (!popup || (popup->popup_parent == active_view.get()))
209         {
210             continue;
211         }
212 
213         /* Ignore popups which have a popup as their parent. In those cases, we'll
214          * close the topmost popup and this will recursively destroy the others.
215          *
216          * Otherwise we get a race condition with wlroots. */
217         if (dynamic_cast<wayfire_xdg_popup*>(popup->popup_parent))
218         {
219             continue;
220         }
221 
222         popup->close();
223     }
224 }
225 
update_active_view(wayfire_view v,uint32_t flags)226 void wf::output_impl_t::update_active_view(wayfire_view v, uint32_t flags)
227 {
228     this->active_view = v;
229     if (this == wf::get_core().get_active_output())
230     {
231         wf::get_core().set_active_view(v);
232     }
233 
234     if (flags & FOCUS_VIEW_CLOSE_POPUPS)
235     {
236         close_popups();
237     }
238 }
239 
update_focus_timestamp(wayfire_view view)240 static void update_focus_timestamp(wayfire_view view)
241 {
242     if (view)
243     {
244         timespec ts;
245         clock_gettime(CLOCK_MONOTONIC, &ts);
246         view->last_focus_timestamp = ts.tv_sec * 1'000'000'000ll + ts.tv_nsec;
247     }
248 }
249 
focus_view(wayfire_view v,uint32_t flags)250 void wf::output_impl_t::focus_view(wayfire_view v, uint32_t flags)
251 {
252     static wf::option_wrapper_t<bool>
253     all_dialogs_modal{"workarounds/all_dialogs_modal"};
254 
255     const auto& make_view_visible = [this, flags] (wayfire_view view)
256     {
257         if (view->minimized)
258         {
259             view->minimize_request(false);
260         }
261 
262         if (flags & FOCUS_VIEW_RAISE)
263         {
264             while (view->parent)
265             {
266                 view = view->parent;
267             }
268 
269             workspace->bring_to_front(view);
270         }
271     };
272 
273     if (v && (workspace->get_view_layer(v) < wf::get_core().get_focused_layer()))
274     {
275         auto active_view = get_active_view();
276         if (active_view && (active_view->get_app_id().find("$unfocus") == 0))
277         {
278             /* This is the case where for ex. a panel has grabbed input focus,
279              * but user has clicked on another view so we want to dismiss the
280              * grab. We can't do that straight away because the client still
281              * holds the focus layer request.
282              *
283              * Instead, we want to deactive the $unfocus view, so that it can
284              * release the grab. At the same time, we bring the to-be-focused
285              * view on top, so that it gets the focus next. */
286             update_active_view(nullptr, flags);
287             make_view_visible(v);
288             update_focus_timestamp(v);
289         } else
290         {
291             LOGD("Denying focus request for a view from a lower layer than the"
292                  " focused layer");
293         }
294 
295         return;
296     }
297 
298     focus_view_signal data;
299 
300     if (!v || !v->is_mapped())
301     {
302         update_active_view(nullptr, flags);
303         data.view = nullptr;
304         emit_signal("focus-view", &data);
305         return;
306     }
307 
308     while (all_dialogs_modal && v->parent && v->parent->is_mapped())
309     {
310         v = v->parent;
311     }
312 
313     /* If no keyboard focus surface is set, then we don't want to focus the view */
314     if (v->get_keyboard_focus_surface() || interactive_view_from_view(v.get()))
315     {
316         make_view_visible(v);
317         update_focus_timestamp(v);
318         update_active_view(v, flags);
319         data.view = v;
320         emit_signal("view-focused", &data);
321     }
322 }
323 
focus_view(wayfire_view v,bool raise)324 void wf::output_impl_t::focus_view(wayfire_view v, bool raise)
325 {
326     uint32_t flags = FOCUS_VIEW_CLOSE_POPUPS;
327     if (raise)
328     {
329         flags |= FOCUS_VIEW_RAISE;
330     }
331 
332     focus_view(v, flags);
333 }
334 
get_top_view() const335 wayfire_view wf::output_t::get_top_view() const
336 {
337     auto views = workspace->get_views_on_workspace(
338         workspace->get_current_workspace(),
339         LAYER_WORKSPACE);
340 
341     return views.empty() ? nullptr : views[0];
342 }
343 
get_active_view() const344 wayfire_view wf::output_impl_t::get_active_view() const
345 {
346     return active_view;
347 }
348 
can_activate_plugin(uint32_t caps,uint32_t flags)349 bool wf::output_impl_t::can_activate_plugin(uint32_t caps,
350     uint32_t flags)
351 {
352     if (this->inhibited && !(flags & wf::PLUGIN_ACTIVATION_IGNORE_INHIBIT))
353     {
354         return false;
355     }
356 
357     for (auto act_owner : active_plugins)
358     {
359         bool compatible = ((act_owner->capabilities & caps) == 0);
360         if (!compatible)
361         {
362             return false;
363         }
364     }
365 
366     return true;
367 }
368 
can_activate_plugin(const plugin_grab_interface_uptr & owner,uint32_t flags)369 bool wf::output_impl_t::can_activate_plugin(const plugin_grab_interface_uptr& owner,
370     uint32_t flags)
371 {
372     if (!owner)
373     {
374         return false;
375     }
376 
377     if (active_plugins.find(owner.get()) != active_plugins.end())
378     {
379         return flags & wf::PLUGIN_ACTIVATE_ALLOW_MULTIPLE;
380     }
381 
382     return can_activate_plugin(owner->capabilities, flags);
383 }
384 
activate_plugin(const plugin_grab_interface_uptr & owner,uint32_t flags)385 bool wf::output_impl_t::activate_plugin(const plugin_grab_interface_uptr& owner,
386     uint32_t flags)
387 {
388     if (!can_activate_plugin(owner, flags))
389     {
390         return false;
391     }
392 
393     if (active_plugins.find(owner.get()) != active_plugins.end())
394     {
395         LOGD("output ", handle->name,
396             ": activate plugin ", owner->name, " again");
397     } else
398     {
399         LOGD("output ", handle->name, ": activate plugin ", owner->name);
400     }
401 
402     active_plugins.insert(owner.get());
403 
404     return true;
405 }
406 
deactivate_plugin(const plugin_grab_interface_uptr & owner)407 bool wf::output_impl_t::deactivate_plugin(
408     const plugin_grab_interface_uptr& owner)
409 {
410     auto it = active_plugins.find(owner.get());
411     if (it == active_plugins.end())
412     {
413         return true;
414     }
415 
416     active_plugins.erase(it);
417     LOGD("output ", handle->name, ": deactivate plugin ", owner->name);
418 
419     if (active_plugins.count(owner.get()) == 0)
420     {
421         owner->ungrab();
422         active_plugins.erase(owner.get());
423 
424         return true;
425     }
426 
427     return false;
428 }
429 
cancel_active_plugins()430 void wf::output_impl_t::cancel_active_plugins()
431 {
432     std::vector<wf::plugin_grab_interface_t*> ifaces;
433     for (auto p : active_plugins)
434     {
435         if (p->callbacks.cancel)
436         {
437             ifaces.push_back(p);
438         }
439     }
440 
441     for (auto p : ifaces)
442     {
443         p->callbacks.cancel();
444     }
445 }
446 
is_plugin_active(std::string name) const447 bool wf::output_impl_t::is_plugin_active(std::string name) const
448 {
449     for (auto act : active_plugins)
450     {
451         if (act && (act->name == name))
452         {
453             return true;
454         }
455     }
456 
457     return false;
458 }
459 
get_input_grab_interface()460 wf::plugin_grab_interface_t*wf::output_impl_t::get_input_grab_interface()
461 {
462     for (auto p : active_plugins)
463     {
464         if (p && p->is_grabbed())
465         {
466             return p;
467         }
468     }
469 
470     return nullptr;
471 }
472 
inhibit_plugins()473 void wf::output_impl_t::inhibit_plugins()
474 {
475     this->inhibited = true;
476     cancel_active_plugins();
477 }
478 
uninhibit_plugins()479 void wf::output_impl_t::uninhibit_plugins()
480 {
481     this->inhibited = false;
482 }
483 
is_inhibited() const484 bool wf::output_impl_t::is_inhibited() const
485 {
486     return this->inhibited;
487 }
488 
489 namespace wf
490 {
491 template<class Option, class Callback>
push_binding(binding_container_t<Option,Callback> & bindings,option_sptr_t<Option> opt,Callback * callback)492 static wf::binding_t *push_binding(
493     binding_container_t<Option, Callback>& bindings,
494     option_sptr_t<Option> opt,
495     Callback *callback)
496 {
497     auto bnd = std::make_unique<output_binding_t<Option, Callback>>();
498     bnd->activated_by = opt;
499     bnd->callback     = callback;
500     bindings.emplace_back(std::move(bnd));
501 
502     return bindings.back().get();
503 }
504 
add_key(option_sptr_t<keybinding_t> key,wf::key_callback * callback)505 binding_t*output_impl_t::add_key(option_sptr_t<keybinding_t> key,
506     wf::key_callback *callback)
507 {
508     return push_binding(this->bindings->keys, key, callback);
509 }
510 
add_axis(option_sptr_t<keybinding_t> axis,wf::axis_callback * callback)511 binding_t*output_impl_t::add_axis(option_sptr_t<keybinding_t> axis,
512     wf::axis_callback *callback)
513 {
514     return push_binding(this->bindings->axes, axis, callback);
515 }
516 
add_button(option_sptr_t<buttonbinding_t> button,wf::button_callback * callback)517 binding_t*output_impl_t::add_button(option_sptr_t<buttonbinding_t> button,
518     wf::button_callback *callback)
519 {
520     return push_binding(this->bindings->buttons, button, callback);
521 }
522 
add_activator(option_sptr_t<activatorbinding_t> activator,wf::activator_callback * callback)523 binding_t*output_impl_t::add_activator(
524     option_sptr_t<activatorbinding_t> activator, wf::activator_callback *callback)
525 {
526     auto result = push_binding(this->bindings->activators, activator, callback);
527     this->bindings->recreate_hotspots();
528     return result;
529 }
530 
rem_binding(wf::binding_t * binding)531 void wf::output_impl_t::rem_binding(wf::binding_t *binding)
532 {
533     return this->bindings->rem_binding(binding);
534 }
535 
rem_binding(void * callback)536 void wf::output_impl_t::rem_binding(void *callback)
537 {
538     return this->bindings->rem_binding(callback);
539 }
540 
get_bindings()541 bindings_repository_t& output_impl_t::get_bindings()
542 {
543     return *bindings;
544 }
545 
call_plugin(const std::string & activator,const wf::activator_data_t & data) const546 bool output_impl_t::call_plugin(
547     const std::string& activator, const wf::activator_data_t& data) const
548 {
549     return this->bindings->handle_activator(activator, data);
550 }
551 
all_layers_not_below(uint32_t layer)552 uint32_t all_layers_not_below(uint32_t layer)
553 {
554     uint32_t mask = 0;
555     for (int i = 0; i < wf::TOTAL_LAYERS; i++)
556     {
557         if ((1u << i) >= layer)
558         {
559             mask |= (1 << i);
560         }
561     }
562 
563     return mask;
564 }
565 } // namespace wf
566