Skip to content

Commit

Permalink
Implement fullscreening
Browse files Browse the repository at this point in the history
Also wire up maximization in Xwayland
  • Loading branch information
serebit committed Nov 4, 2023
1 parent 6d3c28e commit e1bf1ea
Show file tree
Hide file tree
Showing 9 changed files with 186 additions and 64 deletions.
24 changes: 16 additions & 8 deletions src/foreign_toplevel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,16 @@ static void foreign_toplevel_handle_request_maximize_notify(wl_listener* listene
const ForeignToplevelHandle& handle = magpie_container_of(listener, handle, request_activate);
auto& event = *static_cast<wlr_foreign_toplevel_handle_v1_maximized_event*>(data);

handle.view.set_minimized(false);
handle.view.set_maximized(event.maximized);
auto placement = event.maximized ? VIEW_PLACEMENT_MAXIMIZED : VIEW_PLACEMENT_STACKING;
handle.view.set_placement(placement);
}

static void foreign_toplevel_handle_request_fullscreen_notify(wl_listener* listener, void* data) {
const ForeignToplevelHandle& handle = magpie_container_of(listener, handle, request_activate);
auto& event = *static_cast<wlr_foreign_toplevel_handle_v1_maximized_event*>(data);

auto placement = event.maximized ? VIEW_PLACEMENT_FULLSCREEN : VIEW_PLACEMENT_STACKING;
handle.view.set_placement(placement);
}

static void foreign_toplevel_handle_request_minimize_notify(wl_listener* listener, void* data) {
Expand All @@ -24,12 +32,7 @@ static void foreign_toplevel_handle_request_activate_notify(wl_listener* listene
(void) data;

handle.view.set_minimized(false);
handle.view.get_server().focus_view(&handle.view, handle.view.get_wlr_surface());
}

static void foreign_toplevel_handle_request_fullscreen_notify(wl_listener* listener, void* data) {
(void) listener;
(void) data;
handle.view.get_server().focus_view(&handle.view);
}

static void foreign_toplevel_handle_request_close_notify(wl_listener* listener, void* data) {
Expand Down Expand Up @@ -89,6 +92,11 @@ void ForeignToplevelHandle::set_parent(std::optional<std::reference_wrapper<cons
wlr_foreign_toplevel_handle_v1_set_parent(&handle, (parent.has_value()) ? nullptr : &parent->get().handle);
}

void ForeignToplevelHandle::set_placement(const ViewPlacement placement) {
set_maximized(placement == VIEW_PLACEMENT_MAXIMIZED);
set_fullscreen(placement == VIEW_PLACEMENT_FULLSCREEN);
}

void ForeignToplevelHandle::set_maximized(const bool maximized) {
wlr_foreign_toplevel_handle_v1_set_maximized(&handle, maximized);
}
Expand Down
9 changes: 5 additions & 4 deletions src/foreign_toplevel.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,11 @@ class ForeignToplevelHandle {
void set_title(const char* title);
void set_app_id(const char* app_id);
void set_parent(std::optional<std::reference_wrapper<const ForeignToplevelHandle>> parent);
void set_maximized(bool maximized);
void set_minimized(bool minimized);
void set_activated(bool activated);
void set_fullscreen(bool fullscreen);
void set_placement(const ViewPlacement placement);
void set_maximized(const bool maximized);
void set_fullscreen(const bool fullscreen);
void set_minimized(const bool minimized);
void set_activated(const bool activated);
void output_enter(const Output& output);
void output_leave(const Output& output);
};
Expand Down
10 changes: 10 additions & 0 deletions src/output.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,16 @@ void Output::update_layout() {
}
}

wlr_box Output::full_area_in_layout_coords() const {
double layout_x = 0, layout_y = 0;
wlr_output_layout_output_coords(server.output_layout, &wlr, &layout_x, &layout_y);

wlr_box box = full_area;
box.x += layout_x;
box.y += layout_y;
return box;
}

wlr_box Output::usable_area_in_layout_coords() const {
double layout_x = 0, layout_y = 0;
wlr_output_layout_output_coords(server.output_layout, &wlr, &layout_x, &layout_y);
Expand Down
1 change: 1 addition & 0 deletions src/output.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ class Output {
~Output() noexcept;

void update_layout();
wlr_box full_area_in_layout_coords() const;
wlr_box usable_area_in_layout_coords() const;
};

Expand Down
121 changes: 88 additions & 33 deletions src/surface/view.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,15 @@
#include "output.hpp"
#include "server.hpp"

#include "types.hpp"
#include "wlr-wrap-start.hpp"
#include <wlr/types/wlr_compositor.h>
#include <wlr/types/wlr_cursor.h>
#include <wlr/types/wlr_output_layout.h>
#include <wlr/types/wlr_scene.h>
#include <wlr/types/wlr_seat.h>
#include <wlr/util/edges.h>
#include <wlr/util/log.h>
#include "wlr-wrap-end.hpp"

const std::optional<const Output*> View::find_output_for_maximize() {
Expand Down Expand Up @@ -94,15 +96,23 @@ void View::begin_interactive(const CursorMode mode, const uint32_t edges) {
}

void View::set_position(const int new_x, const int new_y) {
previous.x = current.x;
previous.y = current.y;
if (curr_placement == VIEW_PLACEMENT_STACKING) {
previous.x = current.x;
previous.y = current.y;
}
current.x = new_x;
current.y = new_y;
wlr_scene_node_set_position(scene_node, new_x, new_y);
impl_set_position(new_x, new_y);
}

void View::set_size(const int new_width, const int new_height) {
if (curr_placement == VIEW_PLACEMENT_STACKING) {
previous.width = current.width;
previous.height = current.height;
}
current.width = new_width;
current.height = new_height;
impl_set_size(new_width, new_height);
}

Expand All @@ -114,47 +124,78 @@ void View::set_activated(const bool activated) {
}
}

void View::set_maximized(const bool maximized) {
void View::set_placement(const ViewPlacement new_placement, const bool force) {
Server& server = get_server();
if (this->is_maximized == maximized) {
/* Don't honor request if already maximized. */
return;
}

wlr_surface* focused_surface = server.seat->wlr->pointer_state.focused_surface;
if (get_wlr_surface() != wlr_surface_get_root_surface(focused_surface)) {
/* Deny maximize requests from unfocused clients. */
return;
if (!force) {
if (curr_placement == new_placement) {
return;
}

wlr_surface* focused_surface = server.seat->wlr->pointer_state.focused_surface;
if (focused_surface == nullptr || get_wlr_surface() != wlr_surface_get_root_surface(focused_surface)) {
/* Deny placement requests from unfocused clients. */
return;
}
}

if (this->is_maximized) {
set_size(previous.width, previous.height);
impl_set_maximized(false);
current.x = previous.x;
current.y = previous.y;
wlr_scene_node_set_position(scene_node, current.x, current.y);
} else {
previous = get_geometry();
previous.x = current.x;
previous.y = current.y;
bool res = true;

switch (new_placement) {
case VIEW_PLACEMENT_STACKING:
stack();
break;
case VIEW_PLACEMENT_MAXIMIZED:
res = maximize();
break;
case VIEW_PLACEMENT_FULLSCREEN:
res = fullscreen();
break;
}

auto best_output = find_output_for_maximize();
if (!best_output.has_value()) {
return;
if (res) {
prev_placement = curr_placement;
curr_placement = new_placement;
if (toplevel_handle.has_value()) {
toplevel_handle->set_placement(new_placement);
}
}
}

void View::stack() {
set_size(previous.width, previous.height);
impl_set_maximized(false);
impl_set_fullscreen(false);
set_position(previous.x, previous.y);
}

wlr_box output_box = best_output.value()->usable_area_in_layout_coords();
set_size(output_box.width, output_box.height);
impl_set_maximized(true);
current.x = output_box.x;
current.y = output_box.y;
wlr_scene_node_set_position(scene_node, current.x, current.y);
bool View::maximize() {
auto best_output = find_output_for_maximize();
if (!best_output.has_value()) {
return false;
}

this->is_maximized = maximized;
if (toplevel_handle.has_value()) {
toplevel_handle->set_maximized(maximized);
wlr_box output_box = best_output.value()->usable_area_in_layout_coords();
set_size(output_box.width, output_box.height);
impl_set_fullscreen(false);
impl_set_maximized(true);
set_position(output_box.x, output_box.y);

return true;
}

bool View::fullscreen() {
auto best_output = find_output_for_maximize();
if (!best_output.has_value()) {
return false;
}

wlr_box output_box = best_output.value()->full_area_in_layout_coords();
set_size(output_box.width, output_box.height);
impl_set_fullscreen(true);
set_position(output_box.x, output_box.y);

return true;
}

void View::set_minimized(const bool minimized) {
Expand All @@ -175,3 +216,17 @@ void View::set_minimized(const bool minimized) {
map();
}
}

void View::toggle_maximize() {
if (curr_placement != VIEW_PLACEMENT_FULLSCREEN) {
set_placement(curr_placement != VIEW_PLACEMENT_MAXIMIZED ? VIEW_PLACEMENT_MAXIMIZED : VIEW_PLACEMENT_STACKING);
}
}

void View::toggle_fullscreen() {
if (curr_placement == VIEW_PLACEMENT_FULLSCREEN) {
set_placement(prev_placement);
} else {
set_placement(VIEW_PLACEMENT_FULLSCREEN);
}
}
14 changes: 11 additions & 3 deletions src/surface/view.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,9 @@
#include "wlr-wrap-end.hpp"

struct View : public Surface {
bool is_maximized;
bool is_minimized;
ViewPlacement prev_placement = VIEW_PLACEMENT_STACKING;
ViewPlacement curr_placement = VIEW_PLACEMENT_STACKING;
bool is_minimized = false;
wlr_box current;
wlr_box pending;
wlr_box previous;
Expand All @@ -35,11 +36,16 @@ struct View : public Surface {
void set_position(const int new_x, const int new_y);
void set_size(const int new_width, const int new_height);
void set_activated(const bool activated);
void set_maximized(const bool maximized);
void set_placement(const ViewPlacement placement, const bool force = false);
void set_minimized(const bool minimized);
void toggle_maximize();
void toggle_fullscreen();

private:
const std::optional<const Output*> find_output_for_maximize();
void stack();
bool maximize();
bool fullscreen();

protected:
virtual void impl_set_position(const int new_x, const int new_y) = 0;
Expand Down Expand Up @@ -106,6 +112,8 @@ class XWaylandView : public View {
wl_listener request_configure;
wl_listener request_move;
wl_listener request_resize;
wl_listener request_maximize;
wl_listener request_fullscreen;
wl_listener set_geometry;
wl_listener set_title;
wl_listener set_class;
Expand Down
32 changes: 20 additions & 12 deletions src/surface/xdg_view.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ static void xdg_toplevel_request_move_notify(wl_listener* listener, void* data)
XdgView& view = magpie_container_of(listener, view, request_move);
(void) data;

view.set_maximized(false);
view.set_placement(VIEW_PLACEMENT_STACKING);
view.begin_interactive(MAGPIE_CURSOR_MOVE, 0);
}

Expand All @@ -61,7 +61,7 @@ static void xdg_toplevel_request_resize_notify(wl_listener* listener, void* data
XdgView& view = magpie_container_of(listener, view, request_resize);
auto* event = static_cast<wlr_xdg_toplevel_resize_event*>(data);

view.set_maximized(false);
view.set_placement(VIEW_PLACEMENT_STACKING);
view.begin_interactive(MAGPIE_CURSOR_RESIZE, event->edges);
}

Expand All @@ -72,23 +72,23 @@ static void xdg_toplevel_request_maximize_notify(wl_listener* listener, void* da
XdgView& view = magpie_container_of(listener, view, request_maximize);
(void) data;

view.set_maximized(!view.is_maximized);
view.toggle_maximize();
wlr_xdg_surface_schedule_configure(view.xdg_toplevel.base);
}

static void xdg_toplevel_request_minimize_notify(wl_listener* listener, void* data) {
XdgView& view = magpie_container_of(listener, view, request_minimize);
static void xdg_toplevel_request_fullscreen_notify(wl_listener* listener, void* data) {
XdgView& view = magpie_container_of(listener, view, request_fullscreen);
(void) data;

view.set_minimized(!view.is_minimized);
view.toggle_fullscreen();
wlr_xdg_surface_schedule_configure(view.xdg_toplevel.base);
}

static void xdg_toplevel_request_fullscreen_notify(wl_listener* listener, void* data) {
XdgView& view = magpie_container_of(listener, view, request_fullscreen);
static void xdg_toplevel_request_minimize_notify(wl_listener* listener, void* data) {
XdgView& view = magpie_container_of(listener, view, request_minimize);
(void) data;

/* We must send a configure here, even on a no-op. */
view.set_minimized(!view.is_minimized);
wlr_xdg_surface_schedule_configure(view.xdg_toplevel.base);
}

Expand Down Expand Up @@ -126,8 +126,9 @@ XdgView::XdgView(Server& server, wlr_xdg_toplevel& toplevel) noexcept
auto* scene_tree = wlr_scene_xdg_surface_create(&server.scene->tree, toplevel.base);
scene_node = &scene_tree->node;

wlr_xdg_toplevel_set_wm_capabilities(
&toplevel, WLR_XDG_TOPLEVEL_WM_CAPABILITIES_MAXIMIZE | WLR_XDG_TOPLEVEL_WM_CAPABILITIES_MINIMIZE);
wlr_xdg_toplevel_set_wm_capabilities(&toplevel, WLR_XDG_TOPLEVEL_WM_CAPABILITIES_MAXIMIZE |
WLR_XDG_TOPLEVEL_WM_CAPABILITIES_MINIMIZE |
WLR_XDG_TOPLEVEL_WM_CAPABILITIES_FULLSCREEN);

scene_node->data = this;
toplevel.base->surface->data = this;
Expand Down Expand Up @@ -214,7 +215,14 @@ void XdgView::map() {
}

wlr_scene_node_set_enabled(scene_node, true);
is_maximized = xdg_toplevel.current.maximized;
if (xdg_toplevel.current.fullscreen) {
set_placement(VIEW_PLACEMENT_FULLSCREEN);
} else if (xdg_toplevel.current.maximized) {
set_placement(VIEW_PLACEMENT_MAXIMIZED);
} else {
set_placement(VIEW_PLACEMENT_STACKING);
}

server.focus_view(this);
}

Expand Down
Loading

0 comments on commit e1bf1ea

Please sign in to comment.