1 /*
2  * Copyright (C) 2023 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 "host/libs/config/display.h"
18 
19 #include <unordered_map>
20 #include <vector>
21 
22 #include <android-base/logging.h>
23 #include <android-base/parseint.h>
24 #include <android-base/strings.h>
25 
26 #include "common/libs/utils/contains.h"
27 #include "common/libs/utils/flag_parser.h"
28 #include "host/commands/assemble_cvd/flags_defaults.h"
29 
30 namespace cuttlefish {
31 namespace {
32 
33 static constexpr char kDisplay0FlagName[] = "display0";
34 static constexpr char kDisplay1FlagName[] = "display1";
35 static constexpr char kDisplay2FlagName[] = "display2";
36 static constexpr char kDisplay3FlagName[] = "display3";
37 
38 }  // namespace
39 
ParseDisplayConfig(const std::string & flag)40 Result<std::optional<CuttlefishConfig::DisplayConfig>> ParseDisplayConfig(
41     const std::string& flag) {
42   if (flag.empty()) {
43     return std::nullopt;
44   }
45 
46   std::unordered_map<std::string, std::string> props;
47 
48   const std::vector<std::string> pairs = android::base::Split(flag, ",");
49   for (const std::string& pair : pairs) {
50     const std::vector<std::string> keyvalue = android::base::Split(pair, "=");
51     CF_EXPECT_EQ(keyvalue.size(), 2,
52                  "Invalid display flag key-value: \"" << flag << "\"");
53     const std::string& prop_key = keyvalue[0];
54     const std::string& prop_val = keyvalue[1];
55     props[prop_key] = prop_val;
56   }
57 
58   CF_EXPECT(Contains(props, "width"),
59             "Display configuration missing 'width' in \"" << flag << "\"");
60   CF_EXPECT(Contains(props, "height"),
61             "Display configuration missing 'height' in \"" << flag << "\"");
62 
63   int display_width;
64   CF_EXPECT(android::base::ParseInt(props["width"], &display_width),
65             "Display configuration invalid 'width' in \"" << flag << "\"");
66 
67   int display_height;
68   CF_EXPECT(android::base::ParseInt(props["height"], &display_height),
69             "Display configuration invalid 'height' in \"" << flag << "\"");
70 
71   int display_dpi = CF_DEFAULTS_DISPLAY_DPI;
72   auto display_dpi_it = props.find("dpi");
73   if (display_dpi_it != props.end()) {
74     CF_EXPECT(android::base::ParseInt(display_dpi_it->second, &display_dpi),
75               "Display configuration invalid 'dpi' in \"" << flag << "\"");
76   }
77 
78   int display_refresh_rate_hz = CF_DEFAULTS_DISPLAY_REFRESH_RATE;
79   auto display_refresh_rate_hz_it = props.find("refresh_rate_hz");
80   if (display_refresh_rate_hz_it != props.end()) {
81     CF_EXPECT(android::base::ParseInt(display_refresh_rate_hz_it->second,
82                                       &display_refresh_rate_hz),
83               "Display configuration invalid 'refresh_rate_hz' in \"" << flag
84                                                                       << "\"");
85   }
86 
87   return CuttlefishConfig::DisplayConfig{
88       .width = display_width,
89       .height = display_height,
90       .dpi = display_dpi,
91       .refresh_rate_hz = display_refresh_rate_hz,
92   };
93 }
94 
95 Result<std::vector<CuttlefishConfig::DisplayConfig>>
ParseDisplayConfigsFromArgs(std::vector<std::string> & args)96 ParseDisplayConfigsFromArgs(std::vector<std::string>& args) {
97   std::string display0_flag_value;
98   std::string display1_flag_value;
99   std::string display2_flag_value;
100   std::string display3_flag_value;
101   std::vector<std::string> repeated_display_flag_values;
102 
103   const std::vector<Flag> display_flags = {
104       GflagsCompatFlag(kDisplay0FlagName, display0_flag_value)
105           .Help(kDisplayHelp),
106       GflagsCompatFlag(kDisplay1FlagName, display1_flag_value)
107           .Help(kDisplayHelp),
108       GflagsCompatFlag(kDisplay2FlagName, display2_flag_value)
109           .Help(kDisplayHelp),
110       GflagsCompatFlag(kDisplay3FlagName, display3_flag_value)
111           .Help(kDisplayHelp),
112       GflagsCompatFlag(kDisplayFlag)
113           .Help(kDisplayHelp)
114           .Setter([&](const FlagMatch& match) -> Result<void> {
115             repeated_display_flag_values.push_back(match.value);
116             return {};
117           }),
118   };
119 
120   CF_EXPECT(ConsumeFlags(display_flags, args),
121             "Failed to parse display flags.");
122 
123   std::vector<CuttlefishConfig::DisplayConfig> displays_configs;
124 
125   auto display0 = CF_EXPECT(ParseDisplayConfig(display0_flag_value));
126   if (display0) {
127     displays_configs.push_back(*display0);
128   }
129   auto display1 = CF_EXPECT(ParseDisplayConfig(display1_flag_value));
130   if (display1) {
131     displays_configs.push_back(*display1);
132   }
133   auto display2 = CF_EXPECT(ParseDisplayConfig(display2_flag_value));
134   if (display2) {
135     displays_configs.push_back(*display2);
136   }
137   auto display3 = CF_EXPECT(ParseDisplayConfig(display3_flag_value));
138   if (display3) {
139     displays_configs.push_back(*display3);
140   }
141 
142   for (const std::string& display_params : repeated_display_flag_values) {
143     auto display = CF_EXPECT(ParseDisplayConfig(display_params));
144     if (display) {
145       displays_configs.push_back(*display);
146     }
147   }
148 
149   return displays_configs;
150 }
151 
152 }  // namespace cuttlefish
153