Skip to content

Commit 8ee54a0

Browse files
committed
xwayland: implement forced scaling
1 parent e429aaf commit 8ee54a0

18 files changed

Lines changed: 332 additions & 88 deletions

metadata/workarounds.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,5 +105,10 @@
105105
<min>0</min>
106106
<max>500</max>
107107
</option>
108+
<option name="force_xwayland_scaling" type="bool">
109+
<_short>Force Xwayland DPI scaling</_short>
110+
<_long>Disables the default blurry scaling for Xwayland and forces client-side scaling for Xwayland windows dependent on the DPI settings.</_long>
111+
<default>false</default>
112+
</option>
108113
</plugin>
109114
</wayfire>

src/api/wayfire/unstable/wlr-surface-node.hpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ struct surface_state_t
1919
wlr_texture *texture; // The texture of the wlr_client_buffer
2020

2121
wf::region_t accumulated_damage;
22+
wf::region_t opaque_region;
2223
wf::dimensions_t size = {0, 0};
2324
std::optional<wlr_fbox> src_viewport;
2425
wl_output_transform transform = WL_OUTPUT_TRANSFORM_NORMAL;
@@ -70,7 +71,7 @@ class wlr_surface_node_t : public node_t, public zero_copy_texturable_node_t
7071
std::shared_ptr<wf::texture_t> to_texture() const override;
7172

7273
wlr_surface *get_surface() const;
73-
void apply_state(surface_state_t&& state);
74+
virtual void apply_state(surface_state_t&& state);
7475
void apply_current_surface_state();
7576
void send_frame_done(bool delay_until_vblank);
7677

@@ -93,6 +94,8 @@ class wlr_surface_node_t : public node_t, public zero_copy_texturable_node_t
9394
wf::wl_listener_wrapper on_surface_commit;
9495

9596
const bool autocommit;
97+
98+
protected:
9699
surface_state_t current_state;
97100
};
98101
}

src/api/wayfire/unstable/xwl-toplevel-base.hpp

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,17 @@
1010

1111
#include <wayfire/nonstd/wlroots-full.hpp>
1212
#include <wayfire/view.hpp>
13+
#include <wayfire/util.hpp>
1314
#include <wayfire/unstable/wlr-surface-node.hpp>
1415

1516
namespace wf
1617
{
1718
#if WF_HAS_XWAYLAND
19+
namespace xw
20+
{
21+
class xwayland_surface_node_t;
22+
}
23+
1824
/**
1925
* A base class for views which base on a wlr_xwayland surface.
2026
* Contains the implementation of view_interface_t functions used in them.
@@ -50,7 +56,7 @@ class xwayland_view_base_t : public virtual wf::view_interface_t
5056
void handle_title_changed(std::string new_title);
5157

5258
wf::wl_listener_wrapper on_destroy, on_set_title, on_set_app_id, on_ping_timeout;
53-
std::shared_ptr<wf::scene::wlr_surface_node_t> main_surface;
59+
std::shared_ptr<wf::xw::xwayland_surface_node_t> main_surface;
5460
};
5561
#endif
5662
}

src/core/xdg-output-management.cpp

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include <wayfire/geometry.hpp>
1212
#include <wlr/types/wlr_output.h>
1313
#include <wayfire/signal-provider.hpp>
14+
#include "view/view-impl.hpp"
1415
#include "wayfire/debug.hpp"
1516
#include "xdg-output-unstable-v1-protocol.h"
1617

@@ -62,6 +63,11 @@ class xdg_output_v1_resource
6263
}
6364
}
6465

66+
bool is_xwayland() const
67+
{
68+
return wl_resource_get_client(xdg_output) == wf::xwayland_get_client();
69+
}
70+
6571
bool resend_details(wf::geometry_t geometry)
6672
{
6773
if (last_sent_geometry == geometry)
@@ -117,12 +123,14 @@ void xdg_output_manager_v1::update_outputs()
117123
return config.count(output) && (config[output].source & OUTPUT_IMAGE_SOURCE_SELF);
118124
};
119125

120-
const auto& update_output = [&] (wlr_output *output, wf::geometry_t geometry)
126+
const auto& update_output = [&] (wlr_output *output, wf::geometry_t geometry,
127+
wf::geometry_t xwayland_geometry)
121128
{
122129
bool changed = false;
123130
for (auto& resource : output_resources[output])
124131
{
125-
changed |= resource->resend_details(geometry);
132+
wf::geometry_t to_send = resource->is_xwayland() ? xwayland_geometry : geometry;
133+
changed |= resource->resend_details(to_send);
126134
}
127135

128136
if (changed)
@@ -131,6 +139,9 @@ void xdg_output_manager_v1::update_outputs()
131139
}
132140
};
133141

142+
static wf::option_wrapper_t<bool> force_xwayland_scaling{"workarounds/force_xwayland_scaling"};
143+
144+
int xwayland_location_x = 0;
134145
auto it = output_resources.begin();
135146
while (it != output_resources.end())
136147
{
@@ -139,8 +150,22 @@ void xdg_output_manager_v1::update_outputs()
139150
it = output_resources.erase(it);
140151
} else
141152
{
142-
auto geometry = ol->find_output(it->first)->get_layout_geometry();
143-
update_output(it->first, geometry);
153+
auto wo = ol->find_output(it->first);
154+
auto geometry = wo->get_layout_geometry();
155+
if (force_xwayland_scaling)
156+
{
157+
int width, height;
158+
wlr_output_transformed_resolution(it->first, &width, &height);
159+
wf::geometry_t xwayland_geometry = {xwayland_location_x, 0, width, height};
160+
update_output(it->first, geometry, xwayland_geometry);
161+
xwayland_location_x += width;
162+
wo->get_data_safe<wf::xdg_output_xwayland_geometry>()->geometry = xwayland_geometry;
163+
} else
164+
{
165+
update_output(it->first, geometry, geometry);
166+
wo->get_data_safe<wf::xdg_output_xwayland_geometry>()->geometry = geometry;
167+
}
168+
144169
++it;
145170
}
146171
}

src/core/xdg-output-management.hpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,18 @@
33
#include <memory>
44
#include <map>
55
#include <vector>
6+
#include "wayfire/object.hpp"
67
#include "xdg-output-unstable-v1-protocol.h"
78
#include <wayfire/output-layout.hpp>
89

910
namespace wf
1011
{
1112
class xdg_output_v1_resource;
13+
class xdg_output_xwayland_geometry : public wf::custom_data_t
14+
{
15+
public:
16+
std::optional<wf::geometry_t> geometry;
17+
};
1218

1319
class xdg_output_manager_v1
1420
{

src/meson.build

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ wayfire_sources = ['geometry.cpp',
5151
'view/xwayland/xwayland-view-base.cpp',
5252
'view/xwayland/xwayland-toplevel.cpp',
5353
'view/xwayland/xwayland-helpers.cpp',
54+
'view/xwayland/xwayland-surface.cpp',
5455
'view/layer-shell/layer-shell.cpp',
5556
'view/layer-shell/layer-shell-node.cpp',
5657
'view/view-3d.cpp',

src/view/view-impl.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ void xwayland_update_default_cursor();
7676
/* Ensure that the given surface is on top of the Xwayland stack order. */
7777
void xwayland_bring_to_front(wlr_surface *surface);
7878
int xwayland_get_pid();
79+
wl_client *xwayland_get_client();
7980

8081
void init_desktop_apis();
8182
void fini_desktop_apis();

src/view/wlr-surface-node.cpp

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
#include "wayfire/unstable/wlr-surface-node.hpp"
2-
#include "pixman.h"
32
#include "wayfire/geometry.hpp"
43
#include "wayfire/render-manager.hpp"
54
#include "wayfire/scene-render.hpp"
@@ -32,6 +31,7 @@ wf::scene::surface_state_t& wf::scene::surface_state_t::operator =(surface_state
3231
current_buffer = other.current_buffer;
3332
texture = other.texture;
3433
accumulated_damage = other.accumulated_damage;
34+
opaque_region = other.opaque_region;
3535
seq = other.seq;
3636
size = other.size;
3737
src_viewport = other.src_viewport;
@@ -41,6 +41,7 @@ wf::scene::surface_state_t& wf::scene::surface_state_t::operator =(surface_state
4141
other.current_buffer = NULL;
4242
other.texture = NULL;
4343
other.accumulated_damage.clear();
44+
other.opaque_region.clear();
4445
other.src_viewport.reset();
4546
other.color_transform = wf::color_transform_t{};
4647
other.seq.reset();
@@ -117,6 +118,7 @@ void wf::scene::surface_state_t::merge_state(wlr_surface *surface)
117118
wf::region_t current_damage;
118119
wlr_surface_get_effective_damage(surface, current_damage.to_pixman());
119120
this->accumulated_damage |= current_damage;
121+
this->opaque_region = wf::region_t{&surface->opaque_region};
120122
}
121123

122124
wf::scene::surface_state_t::~surface_state_t()
@@ -341,11 +343,7 @@ class wf::scene::wlr_surface_node_t::wlr_surface_render_instance_t : public rend
341343
.damage = std::move(our_damage),
342344
});
343345

344-
if (self->surface)
345-
{
346-
pixman_region32_subtract(damage.to_pixman(), damage.to_pixman(),
347-
&self->surface->opaque_region);
348-
}
346+
damage ^= self->current_state.opaque_region;
349347
}
350348
}
351349

@@ -431,10 +429,9 @@ class wf::scene::wlr_surface_node_t::wlr_surface_render_instance_t : public rend
431429
// We are visible on the given output => send wl_surface.frame on output frame, so that clients
432430
// can draw the next frame.
433431
output->connect(&on_frame_done);
434-
if (use_opaque_optimizations && self->surface)
432+
if (use_opaque_optimizations)
435433
{
436-
pixman_region32_subtract(visible.to_pixman(), visible.to_pixman(),
437-
&self->surface->opaque_region);
434+
visible ^= self->current_state.opaque_region;
438435
}
439436
}
440437
}

src/view/xwayland.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -336,3 +336,14 @@ int wf::xwayland_get_pid()
336336
return -1;
337337
#endif
338338
}
339+
340+
wl_client*wf::xwayland_get_client()
341+
{
342+
#if WF_HAS_XWAYLAND
343+
344+
return (xwayland_handle && xwayland_handle->server) ? xwayland_handle->server->client : NULL;
345+
#else
346+
347+
return nullptr;
348+
#endif
349+
}

src/view/xwayland/xwayland-helpers.cpp

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
#include "xwayland-helpers.hpp"
2+
#include "core/xdg-output-management.hpp"
23

34
#if WF_HAS_XWAYLAND
45

@@ -50,4 +51,58 @@ bool wf::xw::has_type(wlr_xwayland_surface *xw, xcb_atom_t type)
5051
return false;
5152
}
5253

54+
wf::output_t*wf::xw::find_xwayland_surface_output(wlr_xwayland_surface *xw)
55+
{
56+
auto& ol = wf::get_core().output_layout;
57+
double cx = xw->x + xw->width / 2.0;
58+
double cy = xw->y + xw->height / 2.0;
59+
wf::output_t *closest = ol->get_outputs().empty() ? nullptr : ol->get_outputs().front();
60+
double closest_dist = std::numeric_limits<double>::max();
61+
for (auto & wo : ol->get_outputs())
62+
{
63+
auto data = wo->get_data_safe<wf::xdg_output_xwayland_geometry>();
64+
if (!data->geometry.has_value())
65+
{
66+
continue;
67+
}
68+
69+
double dx;
70+
double dy;
71+
wlr_box_closest_point(&data->geometry.value(), cx, cy, &dx, &dy);
72+
const double dist = (dx - cx) * (dx - cx) + (dy - cy) * (dy - cy);
73+
if (dist < closest_dist)
74+
{
75+
closest_dist = dist;
76+
closest = wo;
77+
}
78+
}
79+
80+
return closest;
81+
}
82+
83+
wf::geometry_t wf::xw::calculate_wayfire_geometry(wf::output_t *ref_output, wf::geometry_t geometry)
84+
{
85+
if (!ref_output)
86+
{
87+
return geometry;
88+
}
89+
90+
auto data = ref_output->get_data_safe<wf::xdg_output_xwayland_geometry>();
91+
if (!data->geometry.has_value())
92+
{
93+
LOGW("Xwayland geometry not set for output ", ref_output->to_string(),
94+
", returning original geometry");
95+
return geometry;
96+
}
97+
98+
geometry = geometry - wf::origin(data->geometry.value());
99+
static wf::option_wrapper_t<bool> force_xwayland_scaling{"workarounds/force_xwayland_scaling"};
100+
if (force_xwayland_scaling)
101+
{
102+
geometry = geometry * (1.0 / ref_output->get_scale());
103+
}
104+
105+
return geometry;
106+
}
107+
53108
#endif

0 commit comments

Comments
 (0)