1 /********************************************************************
2 Copyright © 2020 Roman Gilg <subdiff@gmail.com>
3 
4 This library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Lesser General Public
6 License as published by the Free Software Foundation; either
7 version 2.1 of the License, or (at your option) version 3, or any
8 later version accepted by the membership of KDE e.V. (or its
9 successor approved by the membership of KDE e.V.), which shall
10 act as a proxy defined in Section 6 of version 3 of the license.
11 
12 This library is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 Lesser General Public License for more details.
16 
17 You should have received a copy of the GNU Lesser General Public
18 License along with this library.  If not, see <http://www.gnu.org/licenses/>.
19 *********************************************************************/
20 #include "wl_output_p.h"
21 
22 #include "display.h"
23 
24 #include "wayland/client.h"
25 #include "wayland/display.h"
26 
27 #include <functional>
28 #include <wayland-server.h>
29 
30 namespace Wrapland::Server
31 {
32 
Private(Output * output,Display * display,WlOutput * q)33 WlOutput::Private::Private(Output* output, Display* display, WlOutput* q)
34     : WlOutputGlobal(q, display, &wl_output_interface, &s_interface)
35     , displayHandle(display)
36     , output(output)
37 {
38     create();
39 }
40 
41 const struct wl_output_interface WlOutput::Private::s_interface = {resourceDestroyCallback};
42 
to_subpixel(Output::Subpixel subpixel)43 int32_t to_subpixel(Output::Subpixel subpixel)
44 {
45     switch (subpixel) {
46     case Output::Subpixel::Unknown:
47         return WL_OUTPUT_SUBPIXEL_UNKNOWN;
48     case Output::Subpixel::None:
49         return WL_OUTPUT_SUBPIXEL_NONE;
50     case Output::Subpixel::HorizontalRGB:
51         return WL_OUTPUT_SUBPIXEL_HORIZONTAL_RGB;
52     case Output::Subpixel::HorizontalBGR:
53         return WL_OUTPUT_SUBPIXEL_HORIZONTAL_BGR;
54     case Output::Subpixel::VerticalRGB:
55         return WL_OUTPUT_SUBPIXEL_VERTICAL_RGB;
56     case Output::Subpixel::VerticalBGR:
57         return WL_OUTPUT_SUBPIXEL_VERTICAL_BGR;
58     }
59     abort();
60 }
61 
62 std::tuple<int32_t, int32_t, int32_t, int32_t, int32_t, const char*, const char*, int32_t>
geometry_args(OutputState const & state)63 WlOutput::Private::geometry_args(OutputState const& state)
64 {
65     auto const position = state.geometry.topLeft();
66 
67     return std::make_tuple(position.x(),
68                            position.y(),
69                            state.info.physical_size.width(),
70                            state.info.physical_size.height(),
71                            to_subpixel(state.subpixel),
72                            state.info.make.c_str(),
73                            state.info.model.c_str(),
74                            Output::Private::to_transform(state.transform));
75 }
76 
bindInit(WlOutputBind * bind)77 void WlOutput::Private::bindInit(WlOutputBind* bind)
78 {
79     auto const state = output->d_ptr->published;
80 
81     send<wl_output_send_geometry>(bind, geometry_args(state));
82 
83     for (auto const& mode : output->d_ptr->modes) {
84         if (mode != output->d_ptr->published.mode) {
85             sendMode(bind, mode);
86         }
87     }
88     sendMode(bind, output->d_ptr->published.mode);
89 
90     send<wl_output_send_scale, WL_OUTPUT_SCALE_SINCE_VERSION>(bind, state.client_scale);
91     done(bind);
92     bind->client()->flush();
93 }
94 
sendMode(WlOutputBind * bind,Output::Mode const & mode)95 void WlOutput::Private::sendMode(WlOutputBind* bind, Output::Mode const& mode)
96 {
97     // Only called on bind. In this case we want to send the currently published mode.
98     auto flags = Output::Private::get_mode_flags(mode, output->d_ptr->published);
99 
100     send<wl_output_send_mode>(
101         bind, flags, mode.size.width(), mode.size.height(), mode.refresh_rate);
102 }
103 
sendMode(Output::Mode const & mode)104 void WlOutput::Private::sendMode(Output::Mode const& mode)
105 {
106     // Only called on update. In this case we want to send the pending mode.
107     auto flags = Output::Private::get_mode_flags(mode, output->d_ptr->pending);
108 
109     send<wl_output_send_mode>(flags, mode.size.width(), mode.size.height(), mode.refresh_rate);
110 }
111 
broadcast()112 bool WlOutput::Private::broadcast()
113 {
114     auto const published = output->d_ptr->published;
115     auto const pending = output->d_ptr->pending;
116 
117     bool changed = false;
118 
119     if (published.geometry.topLeft() != pending.geometry.topLeft()
120         || published.info.physical_size != pending.info.physical_size
121         || published.subpixel != pending.subpixel || published.info.make != pending.info.make
122         || published.info.model != pending.info.model || published.transform != pending.transform) {
123         send<wl_output_send_geometry>(geometry_args(pending));
124         changed = true;
125     }
126 
127     if (published.client_scale != pending.client_scale) {
128         send<wl_output_send_scale, WL_OUTPUT_SCALE_SINCE_VERSION>(pending.client_scale);
129         changed = true;
130     }
131 
132     if (published.mode != pending.mode) {
133         sendMode(pending.mode);
134         changed = true;
135     }
136 
137     return changed;
138 }
139 
done()140 void WlOutput::Private::done()
141 {
142     send<wl_output_send_done, WL_OUTPUT_DONE_SINCE_VERSION>();
143 }
144 
done(WlOutputBind * bind)145 void WlOutput::Private::done(WlOutputBind* bind)
146 {
147     send<wl_output_send_done, WL_OUTPUT_DONE_SINCE_VERSION>(bind);
148 }
149 
WlOutput(Output * output,Display * display)150 WlOutput::WlOutput(Output* output, Display* display)
151     : QObject(nullptr)
152     , d_ptr(new Private(output, display, this))
153 {
154     display->add_wl_output(this);
155 }
156 
~WlOutput()157 WlOutput::~WlOutput()
158 {
159     Q_EMIT removed();
160 
161     if (d_ptr->displayHandle) {
162         d_ptr->displayHandle->removeOutput(this);
163     }
164 }
165 
output() const166 Output* WlOutput::output() const
167 {
168     return d_ptr->output;
169 }
170 
171 }
172