• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2022 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include <iostream>
18 #include <ostream>
19 #include <string>
20 #include <unordered_map>
21 #include <vector>
22 
23 #include <android-base/logging.h>
24 #include <android-base/strings.h>
25 #include <gflags/gflags.h>
26 
27 #include "common/libs/utils/subprocess.h"
28 #include "host/commands/assemble_cvd/display_flags.h"
29 #include "host/commands/assemble_cvd/flags_defaults.h"
30 #include "host/libs/config/cuttlefish_config.h"
31 
32 DEFINE_uint32(instance_num, 1, "Which instance to read the configs from");
33 DEFINE_uint32(width, 0,
34               "When adding a display, the width of the display in pixels");
35 DEFINE_uint32(height, 0,
36               "When adding a display, the height of the display in pixels");
37 DEFINE_uint32(dpi, 0,
38               "When adding a display, the pixels per inch of the display");
39 DEFINE_uint32(refresh_rate_hz, 0,
40               "When adding a display, the refresh rate of the display in "
41               "Hertz");
42 
43 DEFINE_string(display0, "", cuttlefish::kDisplayHelp);
44 DEFINE_string(display1, "", cuttlefish::kDisplayHelp);
45 DEFINE_string(display2, "", cuttlefish::kDisplayHelp);
46 DEFINE_string(display3, "", cuttlefish::kDisplayHelp);
47 
48 namespace cuttlefish {
49 namespace {
50 
51 static const std::string kUsage =
52     R"(Cuttlefish Virtual Device (CVD) Display CLI.
53 
54 usage: cvd display <command> <args>
55 
56 Commands:
57     help                Print this message.
58     help <command>      Print help for a command.
59     add                 Adds a new display to a given device.
60     list                Prints the currently connected displays.
61     remove              Removes a display from a given device.
62 )";
63 
64 static const std::string kAddUsage =
65     R"(Cuttlefish Virtual Device (CVD) Display CLI.
66 
67 Adds and connects a display to the given virtual device.
68 
69 usage: cvd display add --width=720 --height=1280
70 
71        cvd display add \\
72         --display0=width=1280,height=800
73         --display1=width=1920,height=1080,refresh_rate_hz=60
74 )";
75 
76 static const std::string kListUsage =
77     R"(Cuttlefish Virtual Device (CVD) Display CLI.
78 
79 Lists all of the displays currently connected to a given virtual device.
80 
81 usage: cvd display list
82 )";
83 
84 static const std::string kRemoveUsage =
85     R"(Cuttlefish Virtual Device (CVD) Display CLI.
86 
87 Disconnects and removes a display from the given virtual device.
88 
89 usage: cvd display remove <display index>
90        cvd display remove <display index> <display index> ...
91 )";
92 
93 static const std::unordered_map<std::string, std::string> kSubCommandUsages = {
94     {"add", kAddUsage},
95     {"list", kListUsage},
96     {"help", kUsage},
97     {"remove", kRemoveUsage},
98 };
99 
RunCrosvmDisplayCommand(const std::vector<std::string> & args)100 Result<int> RunCrosvmDisplayCommand(const std::vector<std::string>& args) {
101   auto config = cuttlefish::CuttlefishConfig::Get();
102   if (!config) {
103     return CF_ERR("Failed to get Cuttlefish config.");
104   }
105   // TODO(b/260649774): Consistent executable API for selecting an instance
106   auto instance = config->ForInstance(FLAGS_instance_num);
107 
108   const std::string crosvm_binary_path = instance.crosvm_binary();
109   const std::string crosvm_control_path =
110       instance.PerInstanceInternalUdsPath("crosvm_control.sock");
111 
112   cuttlefish::Command command(crosvm_binary_path);
113   command.AddParameter("gpu");
114   for (const std::string& arg : args) {
115     command.AddParameter(arg);
116   }
117   command.AddParameter(crosvm_control_path);
118 
119   std::string out;
120   std::string err;
121   auto ret = RunWithManagedStdio(std::move(command), NULL, &out, &err);
122   if (ret != 0) {
123     std::cerr << "Failed to run crosvm display command: ret code: " << ret
124               << "\n"
125               << out << "\n"
126               << err;
127     return ret;
128   }
129 
130   std::cerr << err << std::endl;
131   std::cout << out << std::endl;
132   return 0;
133 }
134 
DoHelp(const std::vector<std::string> & args)135 Result<int> DoHelp(const std::vector<std::string>& args) {
136   if (args.empty()) {
137     std::cout << kUsage << std::endl;
138     return 0;
139   }
140 
141   const std::string& subcommand_str = args[0];
142   auto subcommand_usage = kSubCommandUsages.find(subcommand_str);
143   if (subcommand_usage == kSubCommandUsages.end()) {
144     std::cerr << "Unknown subcommand '" << subcommand_str
145               << "'. See `cvd display help`" << std::endl;
146     return 1;
147   }
148 
149   std::cout << subcommand_usage->second << std::endl;
150   return 0;
151 }
152 
153 Result<std::optional<CuttlefishConfig::DisplayConfig>>
ParseLegacyDisplayFlags()154 ParseLegacyDisplayFlags() {
155   if (FLAGS_width == 0 && FLAGS_height == 0 && FLAGS_dpi == 0 &&
156       FLAGS_refresh_rate_hz == 0) {
157     return std::nullopt;
158   }
159 
160   CF_EXPECT_GT(FLAGS_width, 0,
161                "Must specify valid --width flag. Usage:\n"
162                    << kAddUsage);
163   CF_EXPECT_GT(FLAGS_height, 0,
164                "Must specify valid --height flag. Usage:\n"
165                    << kAddUsage);
166   CF_EXPECT_GT(FLAGS_dpi, 0,
167                "Must specify valid --dpi flag. Usage:\n"
168                    << kAddUsage);
169   CF_EXPECT_GT(FLAGS_refresh_rate_hz, 0,
170                "Must specify valid --refresh_rate_hz flag. Usage:\n"
171                    << kAddUsage);
172 
173   const int display_width = FLAGS_width;
174   const int display_height = FLAGS_height;
175   const int display_dpi = FLAGS_dpi > 0 ? FLAGS_dpi : CF_DEFAULTS_DISPLAY_DPI;
176   const int display_rr = FLAGS_refresh_rate_hz > 0
177                              ? FLAGS_refresh_rate_hz
178                              : CF_DEFAULTS_DISPLAY_REFRESH_RATE;
179 
180   return CuttlefishConfig::DisplayConfig{
181       .width = display_width,
182       .height = display_height,
183       .dpi = display_dpi,
184       .refresh_rate_hz = display_rr,
185   };
186 }
187 
DoAdd(const std::vector<std::string> &)188 Result<int> DoAdd(const std::vector<std::string>&) {
189   std::vector<CuttlefishConfig::DisplayConfig> display_configs;
190 
191   auto display = CF_EXPECT(ParseLegacyDisplayFlags());
192   if (display) {
193     display_configs.push_back(*display);
194   }
195   auto display0 = CF_EXPECT(ParseDisplayConfig(FLAGS_display0));
196   if (display0) {
197     display_configs.push_back(*display0);
198   }
199   auto display1 = CF_EXPECT(ParseDisplayConfig(FLAGS_display1));
200   if (display1) {
201     display_configs.push_back(*display1);
202   }
203   auto display2 = CF_EXPECT(ParseDisplayConfig(FLAGS_display2));
204   if (display2) {
205     display_configs.push_back(*display2);
206   }
207   auto display3 = CF_EXPECT(ParseDisplayConfig(FLAGS_display3));
208   if (display3) {
209     display_configs.push_back(*display3);
210   }
211 
212   if (display_configs.empty()) {
213     return CF_ERR("No displays params provided. Usage:\n" << kAddUsage);
214   }
215 
216   std::vector<std::string> add_displays_command_args;
217   add_displays_command_args.push_back("add-displays");
218 
219   for (const auto& display_config : display_configs) {
220     const std::string w = std::to_string(display_config.width);
221     const std::string h = std::to_string(display_config.height);
222     const std::string dpi = std::to_string(display_config.dpi);
223     const std::string rr = std::to_string(display_config.refresh_rate_hz);
224 
225     const std::string add_display_flag =
226         "--gpu-display=" + android::base::Join(
227                                std::vector<std::string>{
228                                    "mode=windowed[" + w + "," + h + "]",
229                                    "dpi=[" + dpi + "," + dpi + "]",
230                                    "refresh-rate=" + rr,
231                                },
232                                ",");
233 
234     add_displays_command_args.push_back(add_display_flag);
235   }
236 
237   return CF_EXPECT(RunCrosvmDisplayCommand(add_displays_command_args));
238 }
239 
DoList(const std::vector<std::string> &)240 Result<int> DoList(const std::vector<std::string>&) {
241   return CF_EXPECT(RunCrosvmDisplayCommand({"list-displays"}));
242 }
243 
DoRemove(const std::vector<std::string> & args)244 Result<int> DoRemove(const std::vector<std::string>& args) {
245   if (args.empty()) {
246     std::cerr << "Must specify the display id to remove. Usage:" << std::endl;
247     std::cerr << kRemoveUsage << std::endl;
248     return 1;
249   }
250 
251   std::vector<std::string> remove_displays_command_args;
252   remove_displays_command_args.push_back("remove-displays");
253   for (const auto& arg : args) {
254     remove_displays_command_args.push_back("--display-id=" + arg);
255   }
256 
257   return CF_EXPECT(RunCrosvmDisplayCommand(remove_displays_command_args));
258 }
259 
260 using DisplaySubCommand = Result<int> (*)(const std::vector<std::string>&);
261 
DisplayMain(int argc,char ** argv)262 int DisplayMain(int argc, char** argv) {
263   ::android::base::InitLogging(argv, android::base::StderrLogger);
264   ::gflags::SetUsageMessage(kUsage);
265   ::gflags::ParseCommandLineFlags(&argc, &argv, true);
266 
267   std::vector<std::string> args;
268   for (int i = 1; i < argc; i++) {
269     args.push_back(argv[i]);
270   }
271 
272   if (args.empty()) {
273     args.push_back("help");
274   }
275 
276   const std::unordered_map<std::string, DisplaySubCommand> kSubCommands = {
277       {"add", DoAdd},
278       {"list", DoList},
279       {"help", DoHelp},
280       {"remove", DoRemove},
281   };
282 
283   const auto command_str = args[0];
284   args.erase(args.begin());
285 
286   auto command_func_it = kSubCommands.find(command_str);
287   if (command_func_it == kSubCommands.end()) {
288     std::cerr << "Unknown display command: '" << command_str << "'."
289               << std::endl;
290     return 1;
291   }
292 
293   auto result = command_func_it->second(args);
294   if (!result.ok()) {
295     std::cerr << result.error().Message();
296     return 1;
297   }
298   return result.value();
299 }
300 
301 }  // namespace
302 }  // namespace cuttlefish
303 
main(int argc,char ** argv)304 int main(int argc, char** argv) { return cuttlefish::DisplayMain(argc, argv); }
305