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 <iomanip>
6 #include <iostream>
7 #include <string>
8 #include <vector>
9 
10 #include "base/logging.h"
11 #include "base/strings/string_util.h"
12 #include "base/strings/stringprintf.h"
13 #include "components/exo/wayland/clients/client_helper.h"
14 
15 // Client that retreives output related properties (modes, scales, etc.) from
16 // a compositor and prints them to standard output.
17 
18 namespace {
19 
20 // This struct contains all the fields that will be set by output
21 // interface listener callbacks.
22 struct Info {
23   int32_t connection;
24   int32_t device_scale_factor;
25   struct {
26     int32_t x, y;
27     int32_t physical_width, physical_height;
28     int32_t subpixel;
29     std::string make;
30     std::string model;
31     int32_t transform;
32   } geometry;
33   struct Mode {
34     uint32_t flags;
35     int32_t width, height;
36     int32_t refresh;
37   };
38   // |next_modes| are swapped with |modes| after receiving output done event.
39   std::vector<Mode> modes, next_modes;
40   struct Scale {
41     uint32_t flags;
42     int32_t scale;
43   };
44   // |next_scales| are swapped with |scales| after receiving output done event.
45   std::vector<Scale> scales, next_scales;
46   std::unique_ptr<wl_output> output;
47   std::unique_ptr<zaura_output> aura_output;
48 };
49 
50 // This struct contains globals and all outputs.
51 struct Globals {
52   std::unique_ptr<zaura_shell> aura_shell;
53   std::vector<Info> outputs;
54 };
55 
RegistryHandler(void * data,wl_registry * registry,uint32_t id,const char * interface,uint32_t version)56 void RegistryHandler(void* data,
57                      wl_registry* registry,
58                      uint32_t id,
59                      const char* interface,
60                      uint32_t version) {
61   Globals* globals = static_cast<Globals*>(data);
62 
63   if (strcmp(interface, "wl_output") == 0) {
64     globals->outputs.push_back(
65         {.connection = ZAURA_OUTPUT_CONNECTION_TYPE_UNKNOWN,
66          .device_scale_factor = ZAURA_OUTPUT_SCALE_FACTOR_1000,
67          .geometry = {.subpixel = WL_OUTPUT_SUBPIXEL_UNKNOWN,
68                       .make = "unknown",
69                       .model = "unknown",
70                       .transform = WL_OUTPUT_TRANSFORM_NORMAL}});
71     globals->outputs.back().output.reset(static_cast<wl_output*>(
72         wl_registry_bind(registry, id, &wl_output_interface, 2)));
73   } else if (strcmp(interface, "zaura_shell") == 0) {
74     if (version >= 2) {
75       globals->aura_shell.reset(static_cast<zaura_shell*>(
76           wl_registry_bind(registry, id, &zaura_shell_interface, 5)));
77     }
78   }
79 }
80 
RegistryRemover(void * data,wl_registry * registry,uint32_t id)81 void RegistryRemover(void* data, wl_registry* registry, uint32_t id) {
82   LOG(WARNING) << "Got a registry losing event for " << id;
83 }
84 
OutputGeometry(void * data,wl_output * output,int x,int y,int physical_width,int physical_height,int subpixel,const char * make,const char * model,int transform)85 void OutputGeometry(void* data,
86                     wl_output* output,
87                     int x,
88                     int y,
89                     int physical_width,
90                     int physical_height,
91                     int subpixel,
92                     const char* make,
93                     const char* model,
94                     int transform) {
95   Info* info = static_cast<Info*>(data);
96 
97   info->geometry.x = x;
98   info->geometry.y = y;
99   info->geometry.physical_width = physical_width;
100   info->geometry.physical_height = physical_height;
101   info->geometry.subpixel = subpixel;
102   info->geometry.make = make;
103   info->geometry.model = model;
104   info->geometry.transform = transform;
105 }
106 
OutputMode(void * data,wl_output * output,uint32_t flags,int width,int height,int refresh)107 void OutputMode(void* data,
108                 wl_output* output,
109                 uint32_t flags,
110                 int width,
111                 int height,
112                 int refresh) {
113   Info* info = static_cast<Info*>(data);
114 
115   info->next_modes.push_back({flags, width, height, refresh});
116 }
117 
OutputDone(void * data,wl_output * output)118 void OutputDone(void* data, wl_output* output) {
119   Info* info = static_cast<Info*>(data);
120 
121   std::swap(info->modes, info->next_modes);
122   info->next_modes.clear();
123   std::swap(info->scales, info->next_scales);
124   info->next_scales.clear();
125 }
126 
OutputScale(void * data,wl_output * output,int32_t scale)127 void OutputScale(void* data, wl_output* output, int32_t scale) {
128   Info* info = static_cast<Info*>(data);
129 
130   info->device_scale_factor = scale * 1000;
131 }
132 
AuraOutputScale(void * data,zaura_output * output,uint32_t flags,uint32_t scale)133 void AuraOutputScale(void* data,
134                      zaura_output* output,
135                      uint32_t flags,
136                      uint32_t scale) {
137   Info* info = static_cast<Info*>(data);
138 
139   info->next_scales.push_back({flags, scale});
140 }
141 
AuraOutputConnection(void * data,zaura_output * output,uint32_t connection)142 void AuraOutputConnection(void* data,
143                           zaura_output* output,
144                           uint32_t connection) {
145   Info* info = static_cast<Info*>(data);
146 
147   info->connection = connection;
148 }
149 
AuraOutputDeviceScaleFactor(void * data,zaura_output * output,uint32_t device_scale_factor)150 void AuraOutputDeviceScaleFactor(void* data,
151                                  zaura_output* output,
152                                  uint32_t device_scale_factor) {
153   Info* info = static_cast<Info*>(data);
154 
155   info->device_scale_factor = device_scale_factor;
156 }
157 
OutputSubpixelToString(int32_t subpixel)158 std::string OutputSubpixelToString(int32_t subpixel) {
159   switch (subpixel) {
160     case WL_OUTPUT_SUBPIXEL_UNKNOWN:
161       return "unknown";
162     case WL_OUTPUT_SUBPIXEL_NONE:
163       return "none";
164     case WL_OUTPUT_SUBPIXEL_HORIZONTAL_RGB:
165       return "horizontal rgb";
166     case WL_OUTPUT_SUBPIXEL_HORIZONTAL_BGR:
167       return "horizontal bgr";
168     case WL_OUTPUT_SUBPIXEL_VERTICAL_RGB:
169       return "vertical rgb";
170     case WL_OUTPUT_SUBPIXEL_VERTICAL_BGR:
171       return "vertical bgr";
172     default:
173       return base::StringPrintf("unknown (%d)", subpixel);
174   }
175 }
176 
OutputTransformToString(int32_t transform)177 std::string OutputTransformToString(int32_t transform) {
178   switch (transform) {
179     case WL_OUTPUT_TRANSFORM_NORMAL:
180       return "normal";
181     case WL_OUTPUT_TRANSFORM_90:
182       return "90°";
183     case WL_OUTPUT_TRANSFORM_180:
184       return "180°";
185     case WL_OUTPUT_TRANSFORM_270:
186       return "270°";
187     case WL_OUTPUT_TRANSFORM_FLIPPED:
188       return "flipped";
189     case WL_OUTPUT_TRANSFORM_FLIPPED_90:
190       return "flipped 90°";
191     case WL_OUTPUT_TRANSFORM_FLIPPED_180:
192       return "flipped 180°";
193     case WL_OUTPUT_TRANSFORM_FLIPPED_270:
194       return "flipped 270°";
195     default:
196       return base::StringPrintf("unknown (%d)", transform);
197   }
198 }
199 
OutputModeFlagsToString(uint32_t flags)200 std::string OutputModeFlagsToString(uint32_t flags) {
201   std::string string;
202   if (flags & WL_OUTPUT_MODE_CURRENT)
203     string += "current       ";
204   if (flags & WL_OUTPUT_MODE_PREFERRED)
205     string += "preferred";
206   base::TrimWhitespaceASCII(string, base::TRIM_TRAILING, &string);
207   return string;
208 }
209 
AuraOutputScaleFlagsToString(uint32_t flags)210 std::string AuraOutputScaleFlagsToString(uint32_t flags) {
211   std::string string;
212   if (flags & ZAURA_OUTPUT_SCALE_PROPERTY_CURRENT)
213     string += "current       ";
214   if (flags & ZAURA_OUTPUT_SCALE_PROPERTY_PREFERRED)
215     string += "preferred";
216   base::TrimWhitespaceASCII(string, base::TRIM_TRAILING, &string);
217   return string;
218 }
219 
AuraOutputScaleFactorToString(int32_t scale)220 std::string AuraOutputScaleFactorToString(int32_t scale) {
221   switch (scale) {
222     case ZAURA_OUTPUT_SCALE_FACTOR_0400:
223     case ZAURA_OUTPUT_SCALE_FACTOR_0500:
224     case ZAURA_OUTPUT_SCALE_FACTOR_0550:
225     case ZAURA_OUTPUT_SCALE_FACTOR_0600:
226     case ZAURA_OUTPUT_SCALE_FACTOR_0625:
227     case ZAURA_OUTPUT_SCALE_FACTOR_0650:
228     case ZAURA_OUTPUT_SCALE_FACTOR_0700:
229     case ZAURA_OUTPUT_SCALE_FACTOR_0750:
230     case ZAURA_OUTPUT_SCALE_FACTOR_0800:
231     case ZAURA_OUTPUT_SCALE_FACTOR_0850:
232     case ZAURA_OUTPUT_SCALE_FACTOR_0900:
233     case ZAURA_OUTPUT_SCALE_FACTOR_0950:
234     case ZAURA_OUTPUT_SCALE_FACTOR_1000:
235     case ZAURA_OUTPUT_SCALE_FACTOR_1050:
236     case ZAURA_OUTPUT_SCALE_FACTOR_1100:
237     case ZAURA_OUTPUT_SCALE_FACTOR_1150:
238     case ZAURA_OUTPUT_SCALE_FACTOR_1125:
239     case ZAURA_OUTPUT_SCALE_FACTOR_1200:
240     case ZAURA_OUTPUT_SCALE_FACTOR_1250:
241     case ZAURA_OUTPUT_SCALE_FACTOR_1300:
242     case ZAURA_OUTPUT_SCALE_FACTOR_1400:
243     case ZAURA_OUTPUT_SCALE_FACTOR_1450:
244     case ZAURA_OUTPUT_SCALE_FACTOR_1500:
245     case ZAURA_OUTPUT_SCALE_FACTOR_1600:
246     case ZAURA_OUTPUT_SCALE_FACTOR_1750:
247     case ZAURA_OUTPUT_SCALE_FACTOR_1800:
248     case ZAURA_OUTPUT_SCALE_FACTOR_2000:
249     case ZAURA_OUTPUT_SCALE_FACTOR_2200:
250     case ZAURA_OUTPUT_SCALE_FACTOR_2250:
251     case ZAURA_OUTPUT_SCALE_FACTOR_2500:
252     case ZAURA_OUTPUT_SCALE_FACTOR_2750:
253     case ZAURA_OUTPUT_SCALE_FACTOR_3000:
254     case ZAURA_OUTPUT_SCALE_FACTOR_3500:
255     case ZAURA_OUTPUT_SCALE_FACTOR_4000:
256     case ZAURA_OUTPUT_SCALE_FACTOR_4500:
257     case ZAURA_OUTPUT_SCALE_FACTOR_5000:
258       return base::StringPrintf("%.3f", scale / 1000.0);
259     default:
260       return base::StringPrintf("unknown (%g)", scale / 1000.0);
261   }
262 }
263 
AuraOutputConnectionToString(uint32_t connection_type)264 std::string AuraOutputConnectionToString(uint32_t connection_type) {
265   switch (connection_type) {
266     case ZAURA_OUTPUT_CONNECTION_TYPE_UNKNOWN:
267       return "unknown";
268     case ZAURA_OUTPUT_CONNECTION_TYPE_INTERNAL:
269       return "internal";
270     default:
271       return "invalid";
272   }
273 }
274 
275 }  // namespace
276 
main(int argc,char * argv[])277 int main(int argc, char* argv[]) {
278   std::unique_ptr<wl_display> display(wl_display_connect(nullptr));
279   if (!display) {
280     LOG(ERROR) << "Failed to connect to display";
281     return 1;
282   }
283 
284   Globals globals;
285 
286   wl_registry_listener registry_listener = {RegistryHandler, RegistryRemover};
287   wl_registry* registry = wl_display_get_registry(display.get());
288   wl_registry_add_listener(registry, &registry_listener, &globals);
289 
290   wl_display_roundtrip(display.get());
291 
292   wl_output_listener output_listener = {OutputGeometry, OutputMode, OutputDone,
293                                         OutputScale};
294 
295   zaura_output_listener aura_output_listener = {
296       AuraOutputScale, AuraOutputConnection, AuraOutputDeviceScaleFactor};
297   for (auto& info : globals.outputs) {
298     wl_output_add_listener(info.output.get(), &output_listener, &info);
299     if (globals.aura_shell) {
300       info.aura_output.reset(
301           static_cast<zaura_output*>(zaura_shell_get_aura_output(
302               globals.aura_shell.get(), info.output.get())));
303       zaura_output_add_listener(info.aura_output.get(), &aura_output_listener,
304                                 &info);
305     }
306   }
307 
308   wl_display_roundtrip(display.get());
309 
310   for (auto& info : globals.outputs) {
311     int id = &info - &globals.outputs[0];
312     if (id)
313       std::cout << std::endl;
314     std::cout << "OUTPUT" << id << ":" << std::endl << std::endl;
315     std::cout << "  connection:          "
316               << AuraOutputConnectionToString(info.connection) << std::endl;
317     std::cout << "  device scale factor: "
318               << AuraOutputScaleFactorToString(info.device_scale_factor)
319               << std::endl
320               << std::endl;
321     std::cout << "  geometry:" << std::endl
322               << "    x:                 " << info.geometry.x << std::endl
323               << "    y:                 " << info.geometry.y << std::endl
324               << "    physical width:    " << info.geometry.physical_width
325               << " mm" << std::endl
326               << "    physical height:   " << info.geometry.physical_height
327               << " mm" << std::endl
328               << "    subpixel:          "
329               << OutputSubpixelToString(info.geometry.subpixel) << std::endl
330               << "    make:              " << info.geometry.make << std::endl
331               << "    model:             " << info.geometry.model << std::endl
332               << "    transform:         "
333               << OutputTransformToString(info.geometry.transform) << std::endl
334               << std::endl;
335     std::cout << "  modes:" << std::endl;
336     for (auto& mode : info.modes) {
337       std::cout << "    " << std::left << std::setw(19)
338                 << base::StringPrintf("%dx%d:", mode.width, mode.height)
339                 << std::left << std::setw(14)
340                 << base::StringPrintf("%.2f Hz", mode.refresh / 1000.0)
341                 << OutputModeFlagsToString(mode.flags) << std::endl;
342     }
343     if (!info.scales.empty()) {
344       std::cout << std::endl;
345       std::cout << "  scales:" << std::endl;
346       for (auto& scale : info.scales) {
347         std::cout << "    " << std::left << std::setw(19)
348                   << (AuraOutputScaleFactorToString(scale.scale) + ":")
349                   << AuraOutputScaleFlagsToString(scale.flags) << std::endl;
350       }
351     }
352   }
353 
354   return 0;
355 }
356