• 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/no_destructor.h>
25 #include <android-base/strings.h>
26 
27 #include "common/libs/utils/flag_parser.h"
28 #include "common/libs/utils/subprocess.h"
29 #include "host/commands/assemble_cvd/flags_defaults.h"
30 #include "host/libs/config/cuttlefish_config.h"
31 #include "host/libs/config/display.h"
32 
33 namespace cuttlefish {
34 namespace {
35 
36 static const char kAddUsage[] =
37     R"(
38 
39 Adds and connects a display to the given virtual device.
40 
41 usage: cvd display add \\
42         --display=width=1280,height=800 \\
43         --display=width=1920,height=1080,refresh_rate_hz=60
44 )";
45 
46 static const char kListUsage[] =
47     R"(
48 
49 Lists all of the displays currently connected to a given virtual device.
50 
51 usage: cvd display list
52 )";
53 
54 static const char kRemoveUsage[] =
55     R"(
56 
57 Disconnects and removes displays from the given virtual device.
58 
59 usage: cvd display remove \\
60         --display=<display id> \\
61         --display=<display id> ...
62 )";
63 
RunCrosvmDisplayCommand(int instance_num,const std::vector<std::string> & args)64 Result<int> RunCrosvmDisplayCommand(int instance_num,
65                                     const std::vector<std::string>& args) {
66   auto config = cuttlefish::CuttlefishConfig::Get();
67   if (!config) {
68     return CF_ERR("Failed to get Cuttlefish config.");
69   }
70   // TODO(b/260649774): Consistent executable API for selecting an instance
71   auto instance = config->ForInstance(instance_num);
72 
73   const std::string crosvm_binary_path = instance.crosvm_binary();
74   const std::string crosvm_control_path = instance.CrosvmSocketPath();
75 
76   cuttlefish::Command command(crosvm_binary_path);
77   command.AddParameter("gpu");
78   for (const std::string& arg : args) {
79     command.AddParameter(arg);
80   }
81   command.AddParameter(crosvm_control_path);
82 
83   std::string out;
84   std::string err;
85   auto ret = RunWithManagedStdio(std::move(command), NULL, &out, &err);
86   if (ret != 0) {
87     std::cerr << "Failed to run crosvm display command: ret code: " << ret
88               << "\n"
89               << out << "\n"
90               << err;
91     return ret;
92   }
93 
94   std::cerr << err << std::endl;
95   std::cout << out << std::endl;
96   return 0;
97 }
98 
GetInstanceNum(std::vector<std::string> & args)99 Result<int> GetInstanceNum(std::vector<std::string>& args) {
100   int instance_num = 1;
101   CF_EXPECT(
102       ConsumeFlags({GflagsCompatFlag("instance_num", instance_num)}, args));
103   return instance_num;
104 }
105 
DoHelp(std::vector<std::string> & args)106 Result<int> DoHelp(std::vector<std::string>& args) {
107   static const android::base::NoDestructor<
108       std::unordered_map<std::string, std::string>>
109       kSubCommandUsages({
110           {"add", kAddUsage},
111           {"list", kListUsage},
112           {"remove", kRemoveUsage},
113       });
114 
115   const std::string& subcommand_str = args[0];
116   auto subcommand_usage = kSubCommandUsages->find(subcommand_str);
117   if (subcommand_usage == kSubCommandUsages->end()) {
118     std::cerr << "Unknown subcommand '" << subcommand_str
119               << "'. See `cvd display help`" << std::endl;
120     return 1;
121   }
122 
123   std::cout << subcommand_usage->second << std::endl;
124   return 0;
125 }
126 
DoAdd(std::vector<std::string> & args)127 Result<int> DoAdd(std::vector<std::string>& args) {
128   const int instance_num = CF_EXPECT(GetInstanceNum(args));
129 
130   const auto display_configs = CF_EXPECT(ParseDisplayConfigsFromArgs(args));
131   if (display_configs.empty()) {
132     std::cerr << "Must provide at least 1 display to add. Usage:" << std::endl;
133     std::cerr << kAddUsage << std::endl;
134     return 1;
135   }
136 
137   std::vector<std::string> add_displays_command_args;
138   add_displays_command_args.push_back("add-displays");
139 
140   for (const auto& display_config : display_configs) {
141     const std::string w = std::to_string(display_config.width);
142     const std::string h = std::to_string(display_config.height);
143     const std::string dpi = std::to_string(display_config.dpi);
144     const std::string rr = std::to_string(display_config.refresh_rate_hz);
145 
146     const std::string add_display_flag =
147         "--gpu-display=" + android::base::Join(
148                                std::vector<std::string>{
149                                    "mode=windowed[" + w + "," + h + "]",
150                                    "dpi=[" + dpi + "," + dpi + "]",
151                                    "refresh-rate=" + rr,
152                                },
153                                ",");
154 
155     add_displays_command_args.push_back(add_display_flag);
156   }
157 
158   return CF_EXPECT(
159       RunCrosvmDisplayCommand(instance_num, add_displays_command_args));
160 }
161 
DoList(std::vector<std::string> & args)162 Result<int> DoList(std::vector<std::string>& args) {
163   const int instance_num = CF_EXPECT(GetInstanceNum(args));
164   return CF_EXPECT(RunCrosvmDisplayCommand(instance_num, {"list-displays"}));
165 }
166 
DoRemove(std::vector<std::string> & args)167 Result<int> DoRemove(std::vector<std::string>& args) {
168   const int instance_num = CF_EXPECT(GetInstanceNum(args));
169 
170   std::vector<std::string> displays;
171   const std::vector<Flag> remove_displays_flags = {
172       GflagsCompatFlag(kDisplayFlag)
173           .Help("Display id of a display to remove.")
174           .Setter([&](const FlagMatch& match) -> Result<void> {
175             displays.push_back(match.value);
176             return {};
177           }),
178   };
179   auto parse_res = ConsumeFlags(remove_displays_flags, args);
180   if (!parse_res.ok()) {
181     std::cerr << parse_res.error().FormatForEnv() << std::endl;
182     std::cerr << "Failed to parse flags. Usage:" << std::endl;
183     std::cerr << kRemoveUsage << std::endl;
184     return 1;
185   }
186 
187   if (displays.empty()) {
188     std::cerr << "Must specify at least one display id to remove. Usage:"
189               << std::endl;
190     std::cerr << kRemoveUsage << std::endl;
191     return 1;
192   }
193 
194   std::vector<std::string> remove_displays_command_args;
195   remove_displays_command_args.push_back("remove-displays");
196   for (const auto& display : displays) {
197     remove_displays_command_args.push_back("--display-id=" + display);
198   }
199 
200   return CF_EXPECT(
201       RunCrosvmDisplayCommand(instance_num, remove_displays_command_args));
202 }
203 
204 using DisplaySubCommand = Result<int> (*)(std::vector<std::string>&);
205 
DisplayMain(int argc,char ** argv)206 int DisplayMain(int argc, char** argv) {
207   ::android::base::InitLogging(argv, android::base::StderrLogger);
208 
209   const std::unordered_map<std::string, DisplaySubCommand> kSubCommands = {
210       {"add", DoAdd},
211       {"list", DoList},
212       {"help", DoHelp},
213       {"remove", DoRemove},
214   };
215 
216   auto args = ArgsToVec(argc - 1, argv + 1);
217   if (args.empty()) {
218     args.push_back("help");
219   }
220 
221   const std::string command_str = args[0];
222   args.erase(args.begin());
223 
224   auto command_func_it = kSubCommands.find(command_str);
225   if (command_func_it == kSubCommands.end()) {
226     std::cerr << "Unknown display command: '" << command_str << "'."
227               << std::endl;
228     return 1;
229   }
230 
231   auto result = command_func_it->second(args);
232   if (!result.ok()) {
233     std::cerr << result.error().FormatForEnv();
234     return 1;
235   }
236   return result.value();
237 }
238 
239 }  // namespace
240 }  // namespace cuttlefish
241 
main(int argc,char ** argv)242 int main(int argc, char** argv) { return cuttlefish::DisplayMain(argc, argv); }
243